diff --git a/backend/src/db/migrations/20250205045509_increase-gcp-auth-limit.ts b/backend/src/db/migrations/20250205045509_increase-gcp-auth-limit.ts index 02f3904d5..6d5e7cc4a 100644 --- a/backend/src/db/migrations/20250205045509_increase-gcp-auth-limit.ts +++ b/backend/src/db/migrations/20250205045509_increase-gcp-auth-limit.ts @@ -1,4 +1,5 @@ import { Knex } from "knex"; + import { TableName } from "../schemas"; export async function up(knex: Knex): Promise { diff --git a/backend/src/db/migrations/20250205220952_kms-keys-drop-slug-col.ts b/backend/src/db/migrations/20250205220952_kms-keys-drop-slug-col.ts new file mode 100644 index 000000000..525e459b3 --- /dev/null +++ b/backend/src/db/migrations/20250205220952_kms-keys-drop-slug-col.ts @@ -0,0 +1,27 @@ +import { Knex } from "knex"; + +import { TableName } from "@app/db/schemas"; + +export async function up(knex: Knex): Promise { + if (await knex.schema.hasTable(TableName.KmsKey)) { + const hasSlugCol = await knex.schema.hasColumn(TableName.KmsKey, "slug"); + + if (hasSlugCol) { + await knex.schema.alterTable(TableName.KmsKey, (t) => { + t.dropColumn("slug"); + }); + } + } +} + +export async function down(knex: Knex): Promise { + if (await knex.schema.hasTable(TableName.KmsKey)) { + const hasSlugCol = await knex.schema.hasColumn(TableName.KmsKey, "slug"); + + if (!hasSlugCol) { + await knex.schema.alterTable(TableName.KmsKey, (t) => { + t.string("slug", 32); + }); + } + } +} diff --git a/backend/src/db/schemas/kms-keys.ts b/backend/src/db/schemas/kms-keys.ts index dffaeec24..b56fab7bf 100644 --- a/backend/src/db/schemas/kms-keys.ts +++ b/backend/src/db/schemas/kms-keys.ts @@ -16,8 +16,7 @@ export const KmsKeysSchema = z.object({ name: z.string(), createdAt: z.date(), updatedAt: z.date(), - projectId: z.string().nullable().optional(), - slug: z.string().nullable().optional() + projectId: z.string().nullable().optional() }); export type TKmsKeys = z.infer; diff --git a/backend/src/ee/services/audit-log/audit-log-types.ts b/backend/src/ee/services/audit-log/audit-log-types.ts index eed743476..1bb58764c 100644 --- a/backend/src/ee/services/audit-log/audit-log-types.ts +++ b/backend/src/ee/services/audit-log/audit-log-types.ts @@ -223,6 +223,7 @@ export enum EventType { UPDATE_CMEK = "update-cmek", DELETE_CMEK = "delete-cmek", GET_CMEKS = "get-cmeks", + GET_CMEK = "get-cmek", CMEK_ENCRYPT = "cmek-encrypt", CMEK_DECRYPT = "cmek-decrypt", UPDATE_EXTERNAL_GROUP_ORG_ROLE_MAPPINGS = "update-external-group-org-role-mapping", @@ -1847,6 +1848,13 @@ interface GetCmeksEvent { }; } +interface GetCmekEvent { + type: EventType.GET_CMEK; + metadata: { + keyId: string; + }; +} + interface CmekEncryptEvent { type: EventType.CMEK_ENCRYPT; metadata: { @@ -2237,6 +2245,7 @@ export type Event = | CreateCmekEvent | UpdateCmekEvent | DeleteCmekEvent + | GetCmekEvent | GetCmeksEvent | CmekEncryptEvent | CmekDecryptEvent diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index f77a5049b..5f6f243b9 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -1593,6 +1593,13 @@ export const KMS = { orderDirection: "The direction to order keys in.", search: "The text string to filter key names by." }, + GET_KEY_BY_ID: { + keyId: "The ID of the KMS key to retrieve." + }, + GET_KEY_BY_NAME: { + keyName: "The name of the KMS key to retrieve.", + projectId: "The ID of the project the key belongs to." + }, ENCRYPT: { keyId: "The ID of the key to encrypt the data with.", plaintext: "The plaintext to be encrypted (base64 encoded)." diff --git a/backend/src/lib/knex/index.ts b/backend/src/lib/knex/index.ts index 0022ee8ea..d43d2af8e 100644 --- a/backend/src/lib/knex/index.ts +++ b/backend/src/lib/knex/index.ts @@ -7,6 +7,7 @@ import { buildDynamicKnexQuery, TKnexDynamicOperator } from "./dynamic"; export * from "./connection"; export * from "./join"; +export * from "./prependTableNameToFindFilter"; export * from "./select"; export const withTransaction = (db: Knex, dal: K) => ({ diff --git a/backend/src/lib/knex/prependTableNameToFindFilter.ts b/backend/src/lib/knex/prependTableNameToFindFilter.ts new file mode 100644 index 000000000..ee48dce5a --- /dev/null +++ b/backend/src/lib/knex/prependTableNameToFindFilter.ts @@ -0,0 +1,13 @@ +import { TableName } from "@app/db/schemas"; +import { buildFindFilter } from "@app/lib/knex/index"; + +type TFindFilterParameters = Parameters>[0]; + +export const prependTableNameToFindFilter = (tableName: TableName, filterObj: object): TFindFilterParameters => + Object.fromEntries( + Object.entries(filterObj).map(([key, value]) => + key.startsWith("$") + ? [key, prependTableNameToFindFilter(tableName, value as object)] + : [`${tableName}.${key}`, value] + ) + ); diff --git a/backend/src/server/routes/v1/cmek-router.ts b/backend/src/server/routes/v1/cmek-router.ts index e3982f3d6..7aecaee37 100644 --- a/backend/src/server/routes/v1/cmek-router.ts +++ b/backend/src/server/routes/v1/cmek-router.ts @@ -15,6 +15,10 @@ import { CmekOrderBy } from "@app/services/cmek/cmek-types"; const keyNameSchema = slugSchema({ min: 1, max: 32, field: "Name" }); const keyDescriptionSchema = z.string().trim().max(500).optional(); +const CmekSchema = KmsKeysSchema.merge(InternalKmsSchema.pick({ version: true, encryptionAlgorithm: true })).omit({ + isReserved: true +}); + const base64Schema = z.string().superRefine((val, ctx) => { if (!isBase64(val)) { ctx.addIssue({ @@ -53,7 +57,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - key: KmsKeysSchema + key: CmekSchema }) } }, @@ -106,7 +110,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - key: KmsKeysSchema + key: CmekSchema }) } }, @@ -150,7 +154,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - key: KmsKeysSchema + key: CmekSchema }) } }, @@ -201,7 +205,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - keys: KmsKeysSchema.merge(InternalKmsSchema.pick({ version: true, encryptionAlgorithm: true })).array(), + keys: CmekSchema.array(), totalCount: z.number() }) } @@ -230,6 +234,92 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => { } }); + server.route({ + method: "GET", + url: "/keys/:keyId", + config: { + rateLimit: readLimit + }, + schema: { + description: "Get KMS key by ID", + params: z.object({ + keyId: z.string().uuid().describe(KMS.GET_KEY_BY_ID.keyId) + }), + response: { + 200: z.object({ + key: CmekSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const { + params: { keyId }, + permission + } = req; + + const key = await server.services.cmek.findCmekById(keyId, permission); + + await server.services.auditLog.createAuditLog({ + ...req.auditLogInfo, + projectId: key.projectId!, + event: { + type: EventType.GET_CMEK, + metadata: { + keyId: key.id + } + } + }); + + return { key }; + } + }); + + server.route({ + method: "GET", + url: "/keys/key-name/:keyName", + config: { + rateLimit: readLimit + }, + schema: { + description: "Get KMS key by Name", + params: z.object({ + keyName: slugSchema({ field: "Key name" }).describe(KMS.GET_KEY_BY_NAME.keyName) + }), + querystring: z.object({ + projectId: z.string().min(1, "Project ID is required").describe(KMS.GET_KEY_BY_NAME.projectId) + }), + response: { + 200: z.object({ + key: CmekSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const { + params: { keyName }, + query: { projectId }, + permission + } = req; + + const key = await server.services.cmek.findCmekByName(keyName, projectId, permission); + + await server.services.auditLog.createAuditLog({ + ...req.auditLogInfo, + projectId: key.projectId!, + event: { + type: EventType.GET_CMEK, + metadata: { + keyId: key.id + } + } + }); + + return { key }; + } + }); + // encrypt data server.route({ method: "POST", diff --git a/backend/src/services/cmek/cmek-service.ts b/backend/src/services/cmek/cmek-service.ts index 775367e6e..5e74a5bac 100644 --- a/backend/src/services/cmek/cmek-service.ts +++ b/backend/src/services/cmek/cmek-service.ts @@ -3,7 +3,8 @@ import { ForbiddenError } from "@casl/ability"; import { ActionProjectType, ProjectType } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionCmekActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; -import { BadRequestError, NotFoundError } from "@app/lib/errors"; +import { DatabaseErrorCode } from "@app/lib/error-codes"; +import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors"; import { OrgServiceActor } from "@app/lib/types"; import { TCmekDecryptDTO, @@ -44,17 +45,31 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj }); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Create, ProjectPermissionSub.Cmek); - const cmek = await kmsService.generateKmsKey({ - ...dto, - projectId, - isReserved: false - }); + try { + const cmek = await kmsService.generateKmsKey({ + ...dto, + projectId, + isReserved: false + }); - return cmek; + return { + ...cmek, + version: 1, + encryptionAlgorithm: dto.encryptionAlgorithm + }; + } catch (err) { + if (err instanceof DatabaseError && (err.error as { code: string })?.code === DatabaseErrorCode.UniqueViolation) { + throw new BadRequestError({ + message: `A KMS key with the name "${dto.name}" already exists for the project with ID "${projectId}"` + }); + } + + throw err; + } }; const updateCmekById = async ({ keyId, ...data }: TUpdabteCmekByIdDTO, actor: OrgServiceActor) => { - const key = await kmsDAL.findById(keyId); + const key = await kmsDAL.findCmekById(keyId); if (!key) throw new NotFoundError({ message: `Key with ID ${keyId} not found` }); @@ -71,13 +86,27 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Edit, ProjectPermissionSub.Cmek); - const cmek = await kmsDAL.updateById(keyId, data); + try { + const cmek = await kmsDAL.updateById(keyId, data); - return cmek; + return { + ...cmek, + version: key.version, + encryptionAlgorithm: key.encryptionAlgorithm + }; + } catch (err) { + if (err instanceof DatabaseError && (err.error as { code: string })?.code === DatabaseErrorCode.UniqueViolation) { + throw new BadRequestError({ + message: `A KMS key with the name "${data.name!}" already exists for the project with ID "${key.projectId}"` + }); + } + + throw err; + } }; const deleteCmekById = async (keyId: string, actor: OrgServiceActor) => { - const key = await kmsDAL.findById(keyId); + const key = await kmsDAL.findCmekById(keyId); if (!key) throw new NotFoundError({ message: `Key with ID ${keyId} not found` }); @@ -94,9 +123,9 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Delete, ProjectPermissionSub.Cmek); - const cmek = kmsDAL.deleteById(keyId); + await kmsDAL.deleteById(keyId); - return cmek; + return key; }; const listCmeksByProjectId = async ( @@ -120,15 +149,58 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); - const { keys: cmeks, totalCount } = await kmsDAL.findKmsKeysByProjectId({ projectId, ...filters }); + const { keys: cmeks, totalCount } = await kmsDAL.listCmeksByProjectId({ projectId, ...filters }); return { cmeks, totalCount }; }; + const findCmekById = async (keyId: string, actor: OrgServiceActor) => { + const key = await kmsDAL.findCmekById(keyId); + + if (!key) throw new NotFoundError({ message: `Key with ID "${keyId}" not found` }); + + if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" }); + + const { permission } = await permissionService.getProjectPermission({ + actor: actor.type, + actorId: actor.id, + projectId: key.projectId, + actorAuthMethod: actor.authMethod, + actorOrgId: actor.orgId, + actionProjectType: ActionProjectType.KMS + }); + + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); + + return key; + }; + + const findCmekByName = async (keyName: string, projectId: string, actor: OrgServiceActor) => { + const key = await kmsDAL.findCmekByName(keyName, projectId); + + if (!key) + throw new NotFoundError({ message: `Key with name "${keyName}" not found for project with ID "${projectId}"` }); + + if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" }); + + const { permission } = await permissionService.getProjectPermission({ + actor: actor.type, + actorId: actor.id, + projectId: key.projectId, + actorAuthMethod: actor.authMethod, + actorOrgId: actor.orgId, + actionProjectType: ActionProjectType.KMS + }); + + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); + + return key; + }; + const cmekEncrypt = async ({ keyId, plaintext }: TCmekEncryptDTO, actor: OrgServiceActor) => { const key = await kmsDAL.findById(keyId); - if (!key) throw new NotFoundError({ message: `Key with ID ${keyId} not found` }); + if (!key) throw new NotFoundError({ message: `Key with ID "${keyId}" not found` }); if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" }); @@ -155,7 +227,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj const cmekDecrypt = async ({ keyId, ciphertext }: TCmekDecryptDTO, actor: OrgServiceActor) => { const key = await kmsDAL.findById(keyId); - if (!key) throw new NotFoundError({ message: `Key with ID ${keyId} not found` }); + if (!key) throw new NotFoundError({ message: `Key with ID "${keyId}" not found` }); if (!key.projectId || key.isReserved) throw new BadRequestError({ message: "Key is not customer managed" }); @@ -185,6 +257,8 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj deleteCmekById, listCmeksByProjectId, cmekEncrypt, - cmekDecrypt + cmekDecrypt, + findCmekById, + findCmekByName }; }; diff --git a/backend/src/services/kms/kms-key-dal.ts b/backend/src/services/kms/kms-key-dal.ts index e0246c096..69a175471 100644 --- a/backend/src/services/kms/kms-key-dal.ts +++ b/backend/src/services/kms/kms-key-dal.ts @@ -3,12 +3,32 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { KmsKeysSchema, TableName, TInternalKms, TKmsKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { ormify, selectAllTableCols } from "@app/lib/knex"; +import { buildFindFilter, ormify, prependTableNameToFindFilter, selectAllTableCols } from "@app/lib/knex"; import { OrderByDirection } from "@app/lib/types"; import { CmekOrderBy, TListCmeksByProjectIdDTO } from "@app/services/cmek/cmek-types"; export type TKmsKeyDALFactory = ReturnType; +type TCmekFindFilter = Parameters>[0]; + +const baseCmekQuery = ({ filter, db, tx }: { db: TDbClient; filter?: TCmekFindFilter; tx?: Knex }) => { + const query = (tx || db.replicaNode())(TableName.KmsKey) + .where(`${TableName.KmsKey}.isReserved`, false) + .join(TableName.InternalKms, `${TableName.InternalKms}.kmsKeyId`, `${TableName.KmsKey}.id`) + .select( + selectAllTableCols(TableName.KmsKey), + db.ref("encryptionAlgorithm").withSchema(TableName.InternalKms), + db.ref("version").withSchema(TableName.InternalKms) + ); + + if (filter) { + /* eslint-disable @typescript-eslint/no-misused-promises */ + void query.where(buildFindFilter(prependTableNameToFindFilter(TableName.KmsKey, filter))); + } + + return query; +}; + export const kmskeyDALFactory = (db: TDbClient) => { const kmsOrm = ormify(db, TableName.KmsKey); @@ -73,7 +93,7 @@ export const kmskeyDALFactory = (db: TDbClient) => { } }; - const findKmsKeysByProjectId = async ( + const listCmeksByProjectId = async ( { projectId, offset = 0, @@ -92,6 +112,7 @@ export const kmskeyDALFactory = (db: TDbClient) => { void qb.whereILike("name", `%${search}%`); } }) + .where(`${TableName.KmsKey}.isReserved`, false) .join(TableName.InternalKms, `${TableName.InternalKms}.kmsKeyId`, `${TableName.KmsKey}.id`) .select< (TKmsKeys & @@ -118,5 +139,33 @@ export const kmskeyDALFactory = (db: TDbClient) => { } }; - return { ...kmsOrm, findByIdWithAssociatedKms, findKmsKeysByProjectId }; + const findCmekById = async (id: string, tx?: Knex) => { + try { + const key = await baseCmekQuery({ + filter: { id }, + db, + tx + }).first(); + + return key; + } catch (error) { + throw new DatabaseError({ error, name: "Find by ID - KMS Key" }); + } + }; + + const findCmekByName = async (keyName: string, projectId: string, tx?: Knex) => { + try { + const key = await baseCmekQuery({ + filter: { name: keyName, projectId }, + db, + tx + }).first(); + + return key; + } catch (error) { + throw new DatabaseError({ error, name: "Find by Name - KMS Key" }); + } + }; + + return { ...kmsOrm, findByIdWithAssociatedKms, listCmeksByProjectId, findCmekById, findCmekByName }; }; diff --git a/backend/src/services/secret-sync/secret-sync-dal.ts b/backend/src/services/secret-sync/secret-sync-dal.ts index 0f5733a3a..cc2cd1fcf 100644 --- a/backend/src/services/secret-sync/secret-sync-dal.ts +++ b/backend/src/services/secret-sync/secret-sync-dal.ts @@ -4,7 +4,7 @@ import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; import { TSecretSyncs } from "@app/db/schemas/secret-syncs"; import { DatabaseError } from "@app/lib/errors"; -import { buildFindFilter, ormify, selectAllTableCols } from "@app/lib/knex"; +import { buildFindFilter, ormify, prependTableNameToFindFilter, selectAllTableCols } from "@app/lib/knex"; import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-folder-dal"; export type TSecretSyncDALFactory = ReturnType; @@ -34,17 +34,9 @@ const baseSecretSyncQuery = ({ filter, db, tx }: { db: TDbClient; filter?: Secre db.ref("updatedAt").withSchema(TableName.AppConnection).as("connectionUpdatedAt") ); - // prepends table name to filter keys to avoid ambiguous col references, skipping utility filters like $in, etc. - const prependTableName = (filterObj: object): SecretSyncFindFilter => - Object.fromEntries( - Object.entries(filterObj).map(([key, value]) => - key.startsWith("$") ? [key, prependTableName(value as object)] : [`${TableName.SecretSync}.${key}`, value] - ) - ); - if (filter) { /* eslint-disable @typescript-eslint/no-misused-promises */ - void query.where(buildFindFilter(prependTableName(filter))); + void query.where(buildFindFilter(prependTableNameToFindFilter(TableName.SecretSync, filter))); } return query; diff --git a/docs/api-reference/endpoints/kms/keys/get-by-id.mdx b/docs/api-reference/endpoints/kms/keys/get-by-id.mdx new file mode 100644 index 000000000..a896c640e --- /dev/null +++ b/docs/api-reference/endpoints/kms/keys/get-by-id.mdx @@ -0,0 +1,4 @@ +--- +title: "Get Key by ID" +openapi: "Get /api/v1/kms/keys/{keyId}" +--- diff --git a/docs/api-reference/endpoints/kms/keys/get-by-name.mdx b/docs/api-reference/endpoints/kms/keys/get-by-name.mdx new file mode 100644 index 000000000..fe94da19b --- /dev/null +++ b/docs/api-reference/endpoints/kms/keys/get-by-name.mdx @@ -0,0 +1,4 @@ +--- +title: "Get Key by Name" +openapi: "Get /api/v1/kms/keys/key-name/{keyName}" +--- diff --git a/docs/mint.json b/docs/mint.json index 43a46f74b..55498832e 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -1039,6 +1039,8 @@ "group": "Keys", "pages": [ "api-reference/endpoints/kms/keys/list", + "api-reference/endpoints/kms/keys/get-by-id", + "api-reference/endpoints/kms/keys/get-by-name", "api-reference/endpoints/kms/keys/create", "api-reference/endpoints/kms/keys/update", "api-reference/endpoints/kms/keys/delete", diff --git a/frontend/src/hooks/api/auditLogs/constants.tsx b/frontend/src/hooks/api/auditLogs/constants.tsx index 7308f2fbf..a51ca7902 100644 --- a/frontend/src/hooks/api/auditLogs/constants.tsx +++ b/frontend/src/hooks/api/auditLogs/constants.tsx @@ -92,6 +92,7 @@ export const eventToNameMap: { [K in EventType]: string } = { [EventType.UPDATE_CMEK]: "Update KMS key", [EventType.DELETE_CMEK]: "Delete KMS key", [EventType.GET_CMEKS]: "List KMS keys", + [EventType.GET_CMEK]: "Get KMS key", [EventType.CMEK_ENCRYPT]: "Encrypt with KMS key", [EventType.CMEK_DECRYPT]: "Decrypt with KMS key", [EventType.UPDATE_EXTERNAL_GROUP_ORG_ROLE_MAPPINGS]: diff --git a/frontend/src/hooks/api/auditLogs/enums.tsx b/frontend/src/hooks/api/auditLogs/enums.tsx index 0da1eac02..a83a8a429 100644 --- a/frontend/src/hooks/api/auditLogs/enums.tsx +++ b/frontend/src/hooks/api/auditLogs/enums.tsx @@ -106,6 +106,7 @@ export enum EventType { UPDATE_CMEK = "update-cmek", DELETE_CMEK = "delete-cmek", GET_CMEKS = "get-cmeks", + GET_CMEK = "get-cmek", CMEK_ENCRYPT = "cmek-encrypt", CMEK_DECRYPT = "cmek-decrypt", UPDATE_EXTERNAL_GROUP_ORG_ROLE_MAPPINGS = "update-external-group-org-role-mapping", diff --git a/frontend/src/pages/kms/OverviewPage/components/CmekTable.tsx b/frontend/src/pages/kms/OverviewPage/components/CmekTable.tsx index 23e5b98a3..532d5afc6 100644 --- a/frontend/src/pages/kms/OverviewPage/components/CmekTable.tsx +++ b/frontend/src/pages/kms/OverviewPage/components/CmekTable.tsx @@ -284,7 +284,8 @@ export const CmekTable = () => { { navigator.clipboard.writeText(id); setCopyCipherText("Copied"); @@ -299,112 +300,116 @@ export const CmekTable = () => { {label} {version} - - - - - - - - - -
- handlePopUpOpen("encryptData", cmek)} - icon={} - iconPos="left" - isDisabled={cannotEncryptData || isDisabled} - > - Encrypt Data - -
-
- -
- handlePopUpOpen("decryptData", cmek)} - icon={} - iconPos="left" - isDisabled={cannotDecryptData || isDisabled} - > - Decrypt Data - -
-
- -
- handlePopUpOpen("upsertKey", cmek)} - icon={} - iconPos="left" - isDisabled={cannotEditKey} - > - Edit Key - -
-
- -
- handleDisableCmek(cmek)} - icon={ - - } - iconPos="left" - isDisabled={cannotEditKey} - > - {isDisabled ? "Enable" : "Disable"} Key - -
-
- -
- handlePopUpOpen("deleteKey", cmek)} - icon={} - iconPos="left" - isDisabled={cannotDeleteKey} - > - Delete Key - -
-
-
-
+ +
+ + + + + + + + +
+ handlePopUpOpen("encryptData", cmek)} + icon={} + iconPos="left" + isDisabled={cannotEncryptData || isDisabled} + > + Encrypt Data + +
+
+ +
+ handlePopUpOpen("decryptData", cmek)} + icon={} + iconPos="left" + isDisabled={cannotDecryptData || isDisabled} + > + Decrypt Data + +
+
+ +
+ handlePopUpOpen("upsertKey", cmek)} + icon={} + iconPos="left" + isDisabled={cannotEditKey} + > + Edit Key + +
+
+ +
+ handleDisableCmek(cmek)} + icon={ + + } + iconPos="left" + isDisabled={cannotEditKey} + > + {isDisabled ? "Enable" : "Disable"} Key + +
+
+ +
+ handlePopUpOpen("deleteKey", cmek)} + icon={} + iconPos="left" + isDisabled={cannotDeleteKey} + > + Delete Key + +
+
+
+
+
);