diff --git a/backend/src/ee/routes/v1/kmip-operation-router.ts b/backend/src/ee/routes/v1/kmip-operation-router.ts index c8c6d1531..32e3b76ec 100644 --- a/backend/src/ee/routes/v1/kmip-operation-router.ts +++ b/backend/src/ee/routes/v1/kmip-operation-router.ts @@ -153,7 +153,55 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) => server.route({ method: "POST", - url: "/delete", + url: "/get-attributes", + config: { + rateLimit: writeLimit + }, + schema: { + description: "KMIP endpoint for getting attributes of managed object", + body: z.object({ + id: z.string() + }), + response: { + 200: z.object({ + id: z.string(), + algorithm: z.string(), + isActive: z.boolean(), + createdAt: z.date(), + updatedAt: z.date() + }) + } + }, + handler: async (req) => { + const object = await server.services.kmipOperation.getAttributes({ + ...req.kmipUser, + id: req.body.id + }); + + await server.services.auditLog.createAuditLog({ + projectId: req.kmipUser.projectId, + actor: { + type: ActorType.KMIP_CLIENT, + metadata: { + clientId: req.kmipUser.clientId, + name: req.kmipUser.name + } + }, + event: { + type: EventType.KMIP_OPERATION_GET_ATTRIBUTES, + metadata: { + id: object.id + } + } + }); + + return object; + } + }); + + server.route({ + method: "POST", + url: "/destroy", config: { rateLimit: writeLimit }, @@ -194,4 +242,193 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) => return object; } }); + + server.route({ + method: "POST", + url: "/activate", + config: { + rateLimit: writeLimit + }, + schema: { + description: "KMIP endpoint for activating managed object", + body: z.object({ + id: z.string() + }), + response: { + 200: z.object({ + id: z.string(), + isActive: z.boolean() + }) + } + }, + handler: async (req) => { + const object = await server.services.kmipOperation.activate({ + ...req.kmipUser, + id: req.body.id + }); + + await server.services.auditLog.createAuditLog({ + projectId: req.kmipUser.projectId, + actor: { + type: ActorType.KMIP_CLIENT, + metadata: { + clientId: req.kmipUser.clientId, + name: req.kmipUser.name + } + }, + event: { + type: EventType.KMIP_OPERATION_ACTIVATE, + metadata: { + id: object.id + } + } + }); + + return object; + } + }); + + server.route({ + method: "POST", + url: "/revoke", + config: { + rateLimit: writeLimit + }, + schema: { + description: "KMIP endpoint for revoking managed object", + body: z.object({ + id: z.string() + }), + response: { + 200: z.object({ + id: z.string(), + updatedAt: z.date() + }) + } + }, + handler: async (req) => { + const object = await server.services.kmipOperation.revoke({ + ...req.kmipUser, + id: req.body.id + }); + + await server.services.auditLog.createAuditLog({ + projectId: req.kmipUser.projectId, + actor: { + type: ActorType.KMIP_CLIENT, + metadata: { + clientId: req.kmipUser.clientId, + name: req.kmipUser.name + } + }, + event: { + type: EventType.KMIP_OPERATION_REVOKE, + metadata: { + id: object.id + } + } + }); + + return object; + } + }); + + server.route({ + method: "POST", + url: "/locate", + config: { + rateLimit: writeLimit + }, + schema: { + description: "KMIP endpoint for locating managed objects", + response: { + 200: z.object({ + objects: z + .object({ + id: z.string(), + name: z.string(), + isActive: z.boolean(), + algorithm: z.string(), + createdAt: z.date(), + updatedAt: z.date() + }) + .array() + }) + } + }, + handler: async (req) => { + const objects = await server.services.kmipOperation.locate({ + ...req.kmipUser + }); + + await server.services.auditLog.createAuditLog({ + projectId: req.kmipUser.projectId, + actor: { + type: ActorType.KMIP_CLIENT, + metadata: { + clientId: req.kmipUser.clientId, + name: req.kmipUser.name + } + }, + event: { + type: EventType.KMIP_OPERATION_LOCATE, + metadata: { + ids: objects.map((obj) => obj.id) + } + } + }); + + return { + objects + }; + } + }); + + server.route({ + method: "POST", + url: "/register", + config: { + rateLimit: writeLimit + }, + schema: { + description: "KMIP endpoint for registering managed object", + body: z.object({ + key: z.string(), + name: z.string(), + algorithm: z.nativeEnum(SymmetricEncryption) + }), + response: { + 200: z.object({ + id: z.string() + }) + } + }, + handler: async (req) => { + const object = await server.services.kmipOperation.register({ + ...req.kmipUser, + ...req.body + }); + + await server.services.auditLog.createAuditLog({ + projectId: req.kmipUser.projectId, + actor: { + type: ActorType.KMIP_CLIENT, + metadata: { + clientId: req.kmipUser.clientId, + name: req.kmipUser.name + } + }, + event: { + type: EventType.KMIP_OPERATION_REGISTER, + metadata: { + id: object.id, + algorithm: req.body.algorithm, + name: object.name + } + } + }); + + return object; + } + }); }; 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 7ac11fb11..3787e7b03 100644 --- a/backend/src/ee/services/audit-log/audit-log-types.ts +++ b/backend/src/ee/services/audit-log/audit-log-types.ts @@ -269,7 +269,12 @@ export enum EventType { CREATE_KMIP_CLIENT_CERTIFICATE = "create-kmip-client-certificate", KMIP_OPERATION_CREATE = "kmip-operation-create", KMIP_OPERATION_GET = "kmip-operation-get", - KMIP_OPERATION_DELETE = "kmip-operation-delete" + KMIP_OPERATION_DELETE = "kmip-operation-delete", + KMIP_OPERATION_GET_ATTRIBUTES = "kmip-operation-get-attributes", + KMIP_OPERATION_ACTIVATE = "kmip-operation-activate", + KMIP_OPERATION_REVOKE = "kmip-operation-revoke", + KMIP_OPERATION_LOCATE = "kmip-operation-locate", + KMIP_OPERATION_REGISTER = "kmip-operation-register" } interface UserActorMetadata { @@ -2165,6 +2170,43 @@ interface KmipOperationCreateEvent { }; } +interface KmipOperationGetAttributesEvent { + type: EventType.KMIP_OPERATION_GET_ATTRIBUTES; + metadata: { + id: string; + }; +} + +interface KmipOperationActivateEvent { + type: EventType.KMIP_OPERATION_ACTIVATE; + metadata: { + id: string; + }; +} + +interface KmipOperationRevokeEvent { + type: EventType.KMIP_OPERATION_REVOKE; + metadata: { + id: string; + }; +} + +interface KmipOperationLocateEvent { + type: EventType.KMIP_OPERATION_LOCATE; + metadata: { + ids: string[]; + }; +} + +interface KmipOperationRegisterEvent { + type: EventType.KMIP_OPERATION_REGISTER; + metadata: { + id: string; + algorithm: string; + name: string; + }; +} + export type Event = | GetSecretsEvent | GetSecretEvent @@ -2364,4 +2406,9 @@ export type Event = | CreateKmipClientCertificateEvent | KmipOperationGetEvent | KmipOperationDeleteEvent - | KmipOperationCreateEvent; + | KmipOperationCreateEvent + | KmipOperationGetAttributesEvent + | KmipOperationActivateEvent + | KmipOperationRevokeEvent + | KmipOperationLocateEvent + | KmipOperationRegisterEvent; diff --git a/backend/src/ee/services/kmip/kmip-enum.ts b/backend/src/ee/services/kmip/kmip-enum.ts index 065ac2183..0e56feeac 100644 --- a/backend/src/ee/services/kmip/kmip-enum.ts +++ b/backend/src/ee/services/kmip/kmip-enum.ts @@ -3,5 +3,9 @@ export enum KmipPermission { Locate = "locate", Check = "check", Get = "get", - Delete = "delete" + GetAttributes = "get-attributes", + Activate = "activate", + Revoke = "revoke", + Delete = "delete", + Register = "register" } diff --git a/backend/src/ee/services/kmip/kmip-operation-service.ts b/backend/src/ee/services/kmip/kmip-operation-service.ts index c650bea1e..17e19290e 100644 --- a/backend/src/ee/services/kmip/kmip-operation-service.ts +++ b/backend/src/ee/services/kmip/kmip-operation-service.ts @@ -6,7 +6,15 @@ import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TKmipClientDALFactory } from "./kmip-client-dal"; import { KmipPermission } from "./kmip-enum"; -import { TKmipCreateDTO, TKmipDeleteDTO, TKmipGetDTO } from "./kmip-types"; +import { + TKmipCreateDTO, + TKmipDeleteDTO, + TKmipGetAttributesDTO, + TKmipGetDTO, + TKmipLocateDTO, + TKmipRegisterDTO, + TKmipRevokeDTO +} from "./kmip-types"; type TKmipOperationServiceFactoryDep = { kmsService: TKmsServiceFactory; @@ -143,13 +151,211 @@ export const kmipOperationServiceFactory = ({ return { id: key.id, value: kmsKey.toString("base64"), - algorithm: completeKeyDetails.internalKms.encryptionAlgorithm + algorithm: completeKeyDetails.internalKms.encryptionAlgorithm, + isActive: !key.isDisabled, + createdAt: key.createdAt, + updatedAt: key.updatedAt }; }; + const activate = async ({ projectId: preSplitProjectId, id, clientId }: TKmipGetDTO) => { + let projectId = preSplitProjectId; + const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS); + + if (cmekProjectFromSplit) { + projectId = cmekProjectFromSplit.id; + } + + const kmipClient = await kmipClientDAL.findOne({ + id: clientId, + projectId + }); + + if (!kmipClient.permissions?.includes(KmipPermission.Activate)) { + throw new ForbiddenRequestError({ + message: "Client does not have sufficient permission to perform KMIP activate" + }); + } + + const key = await kmsDAL.findOne({ + id, + projectId + }); + + if (!key) { + throw new NotFoundError({ message: `Key with ID ${id} not found` }); + } + + return { + id: key.id, + isActive: !key.isDisabled + }; + }; + + const revoke = async ({ projectId: preSplitProjectId, id, clientId }: TKmipRevokeDTO) => { + let projectId = preSplitProjectId; + const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS); + + if (cmekProjectFromSplit) { + projectId = cmekProjectFromSplit.id; + } + + const kmipClient = await kmipClientDAL.findOne({ + id: clientId, + projectId + }); + + if (!kmipClient.permissions?.includes(KmipPermission.Revoke)) { + throw new ForbiddenRequestError({ + message: "Client does not have sufficient permission to perform KMIP revoke" + }); + } + + const key = await kmsDAL.findOne({ + id, + projectId + }); + + if (!key) { + throw new NotFoundError({ message: `Key with ID ${id} not found` }); + } + + if (key.isReserved) { + throw new BadRequestError({ message: "Cannot revoke reserved keys" }); + } + + const completeKeyDetails = await kmsDAL.findByIdWithAssociatedKms(id); + + if (!completeKeyDetails.internalKms) { + throw new BadRequestError({ + message: "Cannot revoke external keys" + }); + } + + const revokedKey = await kmsDAL.updateById(key.id, { + isDisabled: true + }); + + return { + id: key.id, + updatedAt: revokedKey.updatedAt + }; + }; + + const getAttributes = async ({ projectId: preSplitProjectId, id, clientId }: TKmipGetAttributesDTO) => { + let projectId = preSplitProjectId; + const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS); + + if (cmekProjectFromSplit) { + projectId = cmekProjectFromSplit.id; + } + + const kmipClient = await kmipClientDAL.findOne({ + id: clientId, + projectId + }); + + if (!kmipClient.permissions?.includes(KmipPermission.GetAttributes)) { + throw new ForbiddenRequestError({ + message: "Client does not have sufficient permission to perform KMIP get attributes" + }); + } + + const key = await kmsDAL.findOne({ + id, + projectId + }); + + if (!key) { + throw new NotFoundError({ message: `Key with ID ${id} not found` }); + } + + if (key.isReserved) { + throw new BadRequestError({ message: "Cannot get reserved keys" }); + } + + const completeKeyDetails = await kmsDAL.findByIdWithAssociatedKms(id); + + if (!completeKeyDetails.internalKms) { + throw new BadRequestError({ + message: "Cannot get external keys" + }); + } + + return { + id: key.id, + algorithm: completeKeyDetails.internalKms.encryptionAlgorithm, + isActive: !key.isDisabled, + createdAt: key.createdAt, + updatedAt: key.updatedAt + }; + }; + + const locate = async ({ projectId: preSplitProjectId, clientId }: TKmipLocateDTO) => { + let projectId = preSplitProjectId; + const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS); + + if (cmekProjectFromSplit) { + projectId = cmekProjectFromSplit.id; + } + + const kmipClient = await kmipClientDAL.findOne({ + id: clientId, + projectId + }); + + if (!kmipClient.permissions?.includes(KmipPermission.Locate)) { + throw new ForbiddenRequestError({ + message: "Client does not have sufficient permission to perform KMIP locate" + }); + } + + const keys = await kmsDAL.findProjectCmeks(projectId); + + return keys; + }; + + const register = async ({ projectId: preSplitProjectId, clientId, key, algorithm, name }: TKmipRegisterDTO) => { + let projectId = preSplitProjectId; + const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS); + + if (cmekProjectFromSplit) { + projectId = cmekProjectFromSplit.id; + } + + const kmipClient = await kmipClientDAL.findOne({ + id: clientId, + projectId + }); + + if (!kmipClient.permissions?.includes(KmipPermission.Register)) { + throw new ForbiddenRequestError({ + message: "Client does not have sufficient permission to perform KMIP register" + }); + } + + const project = await projectDAL.findById(projectId); + + const kmsKey = await kmsService.importKeyMaterial({ + name, + key: Buffer.from(key, "base64"), + algorithm, + isReserved: false, + projectId, + orgId: project.orgId + }); + + return kmsKey; + }; + return { create, get, - deleteOp + activate, + getAttributes, + deleteOp, + revoke, + locate, + register }; }; diff --git a/backend/src/ee/services/kmip/kmip-types.ts b/backend/src/ee/services/kmip/kmip-types.ts index 265a5e61a..aafce7454 100644 --- a/backend/src/ee/services/kmip/kmip-types.ts +++ b/backend/src/ee/services/kmip/kmip-types.ts @@ -56,6 +56,26 @@ export type TKmipGetDTO = { id: string; } & KmipOperationBaseDTO; +export type TKmipGetAttributesDTO = { + id: string; +} & KmipOperationBaseDTO; + export type TKmipDeleteDTO = { id: string; } & KmipOperationBaseDTO; + +export type TKmipActivateDTO = { + id: string; +} & KmipOperationBaseDTO; + +export type TKmipRevokeDTO = { + id: string; +} & KmipOperationBaseDTO; + +export type TKmipLocateDTO = KmipOperationBaseDTO; + +export type TKmipRegisterDTO = { + name: string; + key: string; + algorithm: SymmetricEncryption; +} & KmipOperationBaseDTO; diff --git a/backend/src/services/kms/kms-key-dal.ts b/backend/src/services/kms/kms-key-dal.ts index e0246c096..3b21d1e3e 100644 --- a/backend/src/services/kms/kms-key-dal.ts +++ b/backend/src/services/kms/kms-key-dal.ts @@ -73,6 +73,34 @@ export const kmskeyDALFactory = (db: TDbClient) => { } }; + const findProjectCmeks = async (projectId: string, tx?: Knex) => { + try { + const result = await (tx || db.replicaNode())(TableName.KmsKey) + .where({ + [`${TableName.KmsKey}.projectId` as "projectId"]: projectId, + [`${TableName.KmsKey}.isReserved` as "isReserved"]: false + }) + .join(TableName.Organization, `${TableName.KmsKey}.orgId`, `${TableName.Organization}.id`) + .join(TableName.InternalKms, `${TableName.KmsKey}.id`, `${TableName.InternalKms}.kmsKeyId`) + .select(selectAllTableCols(TableName.KmsKey)) + .select( + db.ref("encryptedKey").withSchema(TableName.InternalKms).as("internalKmsEncryptedKey"), + db.ref("encryptionAlgorithm").withSchema(TableName.InternalKms).as("internalKmsEncryptionAlgorithm"), + db.ref("version").withSchema(TableName.InternalKms).as("internalKmsVersion") + ); + + return result.map((entry) => ({ + ...KmsKeysSchema.parse(entry), + isActive: !entry.isDisabled, + encryptedKey: entry.internalKmsEncryptedKey, + algorithm: entry.internalKmsEncryptionAlgorithm, + version: entry.internalKmsVersion + })); + } catch (error) { + throw new DatabaseError({ error, name: "Find project cmeks" }); + } + }; + const findKmsKeysByProjectId = async ( { projectId, @@ -118,5 +146,5 @@ export const kmskeyDALFactory = (db: TDbClient) => { } }; - return { ...kmsOrm, findByIdWithAssociatedKms, findKmsKeysByProjectId }; + return { ...kmsOrm, findByIdWithAssociatedKms, findKmsKeysByProjectId, findProjectCmeks }; }; diff --git a/backend/src/services/kms/kms-service.ts b/backend/src/services/kms/kms-service.ts index 3ca3f849c..9cc960c8c 100644 --- a/backend/src/services/kms/kms-service.ts +++ b/backend/src/services/kms/kms-service.ts @@ -38,6 +38,7 @@ import { TEncryptWithKmsDTO, TGenerateKMSDTO, TGetKeyMaterialDTO, + TImportKeyMaterialDTO, TUpdateProjectSecretManagerKmsKeyDTO } from "./kms-types"; @@ -351,6 +352,48 @@ export const kmsServiceFactory = ({ return kmsKey; }; + const importKeyMaterial = async ( + { key, algorithm, name, isReserved, projectId, orgId }: TImportKeyMaterialDTO, + tx?: Knex + ) => { + const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256); + + const expectedByteLength = getByteLengthForAlgorithm(algorithm); + if (key.byteLength !== expectedByteLength) { + throw new BadRequestError({ + message: `Invalid key length for ${algorithm}. Expected ${expectedByteLength} bytes but got ${key.byteLength} bytes` + }); + } + + const encryptedKeyMaterial = cipher.encrypt(key, ROOT_ENCRYPTION_KEY); + const sanitizedName = name ? slugify(name) : slugify(alphaNumericNanoId(8).toLowerCase()); + const dbQuery = async (db: Knex) => { + const kmsDoc = await kmsDAL.create( + { + name: sanitizedName, + orgId, + isReserved, + projectId + }, + db + ); + + await internalKmsDAL.create( + { + version: 1, + encryptedKey: encryptedKeyMaterial, + encryptionAlgorithm: algorithm, + kmsKeyId: kmsDoc.id + }, + db + ); + return kmsDoc; + }; + if (tx) return dbQuery(tx); + const doc = await kmsDAL.transaction(async (tx2) => dbQuery(tx2)); + return doc; + }; + const encryptWithKmsKey = async ({ kmsId }: Omit, tx?: Knex) => { const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId, tx); if (!kmsDoc) { @@ -993,6 +1036,7 @@ export const kmsServiceFactory = ({ loadProjectKeyBackup, getKmsById, createCipherPairWithDataKey, - getKeyMaterial + getKeyMaterial, + importKeyMaterial }; }; diff --git a/backend/src/services/kms/kms-types.ts b/backend/src/services/kms/kms-types.ts index d4c192132..8be0b29fc 100644 --- a/backend/src/services/kms/kms-types.ts +++ b/backend/src/services/kms/kms-types.ts @@ -64,3 +64,12 @@ export enum RootKeyEncryptionStrategy { export type TGetKeyMaterialDTO = { kmsId: string; }; + +export type TImportKeyMaterialDTO = { + key: Buffer; + algorithm: SymmetricEncryption; + name?: string; + isReserved: boolean; + projectId: string; + orgId: string; +}; diff --git a/frontend/src/hooks/api/auditLogs/constants.tsx b/frontend/src/hooks/api/auditLogs/constants.tsx index bc8cae1d0..e750d0a5b 100644 --- a/frontend/src/hooks/api/auditLogs/constants.tsx +++ b/frontend/src/hooks/api/auditLogs/constants.tsx @@ -127,7 +127,12 @@ export const eventToNameMap: { [K in EventType]: string } = { [EventType.CREATE_KMIP_CLIENT_CERTIFICATE]: "Create KMIP client certificate", [EventType.KMIP_OPERATION_CREATE]: "KMIP operation create", [EventType.KMIP_OPERATION_GET]: "KMIP operation get", - [EventType.KMIP_OPERATION_DELETE]: "KMIP operation delete" + [EventType.KMIP_OPERATION_DELETE]: "KMIP operation delete", + [EventType.KMIP_OPERATION_GET_ATTRIBUTES]: "KMIP operation get attributes", + [EventType.KMIP_OPERATION_ACTIVATE]: "KMIP operation activate", + [EventType.KMIP_OPERATION_REVOKE]: "KMIP operation revoke", + [EventType.KMIP_OPERATION_LOCATE]: "KMIP operation locate", + [EventType.KMIP_OPERATION_REGISTER]: "KMIP operation register" }; export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = { diff --git a/frontend/src/hooks/api/auditLogs/enums.tsx b/frontend/src/hooks/api/auditLogs/enums.tsx index e0a42f0bc..1294414d5 100644 --- a/frontend/src/hooks/api/auditLogs/enums.tsx +++ b/frontend/src/hooks/api/auditLogs/enums.tsx @@ -139,5 +139,10 @@ export enum EventType { CREATE_KMIP_CLIENT_CERTIFICATE = "create-kmip-client-certificate", KMIP_OPERATION_CREATE = "kmip-operation-create", KMIP_OPERATION_GET = "kmip-operation-get", - KMIP_OPERATION_DELETE = "kmip-operation-delete" + KMIP_OPERATION_DELETE = "kmip-operation-delete", + KMIP_OPERATION_GET_ATTRIBUTES = "kmip-operation-get-attributes", + KMIP_OPERATION_ACTIVATE = "kmip-operation-activate", + KMIP_OPERATION_REVOKE = "kmip-operation-revoke", + KMIP_OPERATION_LOCATE = "kmip-operation-locate", + KMIP_OPERATION_REGISTER = "kmip-operation-register" } diff --git a/frontend/src/hooks/api/kmip/types.ts b/frontend/src/hooks/api/kmip/types.ts index 036a2caef..c090baa20 100644 --- a/frontend/src/hooks/api/kmip/types.ts +++ b/frontend/src/hooks/api/kmip/types.ts @@ -6,7 +6,11 @@ export enum KmipPermission { Locate = "locate", Check = "check", Get = "get", - Delete = "delete" + GetAttributes = "get-attributes", + Activate = "activate", + Revoke = "revoke", + Delete = "delete", + Register = "register" } export type TKmipClient = { diff --git a/frontend/src/pages/kms/KmipPage/components/KmipClientModal.tsx b/frontend/src/pages/kms/KmipPage/components/KmipClientModal.tsx index 7f8b37158..58a845253 100644 --- a/frontend/src/pages/kms/KmipPage/components/KmipClientModal.tsx +++ b/frontend/src/pages/kms/KmipPage/components/KmipClientModal.tsx @@ -22,7 +22,11 @@ const KMIP_PERMISSIONS_OPTIONS = [ { value: KmipPermission.Create, label: "Create" }, { value: KmipPermission.Get, label: "Get" }, { value: KmipPermission.Locate, label: "Locate" }, - { value: KmipPermission.Delete, label: "Delete" } + { value: KmipPermission.Delete, label: "Delete" }, + { value: KmipPermission.Activate, label: "Activate" }, + { value: KmipPermission.Revoke, label: "Revoke" }, + { value: KmipPermission.GetAttributes, label: "Get Attributes" }, + { value: KmipPermission.Register, label: "Register" } ] as const; const formSchema = z.object({ @@ -33,7 +37,11 @@ const formSchema = z.object({ [KmipPermission.Create]: z.boolean().optional(), [KmipPermission.Get]: z.boolean().optional(), [KmipPermission.Locate]: z.boolean().optional(), - [KmipPermission.Delete]: z.boolean().optional() + [KmipPermission.Delete]: z.boolean().optional(), + [KmipPermission.Activate]: z.boolean().optional(), + [KmipPermission.GetAttributes]: z.boolean().optional(), + [KmipPermission.Revoke]: z.boolean().optional(), + [KmipPermission.Register]: z.boolean().optional() }) });