diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index daa2f5ca2..e552e5c91 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -126,6 +126,7 @@ declare module "fastify" { kmipUser: { projectId: string; clientId: string; + name: string; }; auditLogInfo: Pick; ssoConfig: Awaited>; diff --git a/backend/src/ee/routes/v1/kmip-operation-router.ts b/backend/src/ee/routes/v1/kmip-operation-router.ts index b90e097a7..c8c6d1531 100644 --- a/backend/src/ee/routes/v1/kmip-operation-router.ts +++ b/backend/src/ee/routes/v1/kmip-operation-router.ts @@ -3,9 +3,11 @@ import jwt, { JwtPayload } from "jsonwebtoken"; import z from "zod"; import { KmsKeysSchema } from "@app/db/schemas"; +import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { SymmetricEncryption } from "@app/lib/crypto/cipher"; import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { writeLimit } from "@app/server/config/rateLimiter"; +import { ActorType } from "@app/services/auth/auth-type"; import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types"; export const registerKmipOperationRouter = async (server: FastifyZodProvider) => { @@ -55,7 +57,8 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) => req.kmipUser = { projectId: decodedToken.projectId, - clientId: decodedToken.clientId + clientId: decodedToken.clientId, + name: kmipClient.name }; }); @@ -68,7 +71,7 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) => schema: { description: "KMIP endpoint for creating managed objects", body: z.object({ - encryptionAlgorithm: z.nativeEnum(SymmetricEncryption) + algorithm: z.nativeEnum(SymmetricEncryption) }), response: { 200: KmsKeysSchema @@ -76,9 +79,26 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) => }, handler: async (req) => { const object = await server.services.kmipOperation.create({ + ...req.kmipUser, + algorithm: req.body.algorithm + }); + + await server.services.auditLog.createAuditLog({ projectId: req.kmipUser.projectId, - clientId: req.kmipUser.clientId, - encryptionAlgorithm: req.body.encryptionAlgorithm + actor: { + type: ActorType.KMIP_CLIENT, + metadata: { + clientId: req.kmipUser.clientId, + name: req.kmipUser.name + } + }, + event: { + type: EventType.KMIP_OPERATION_CREATE, + metadata: { + id: object.id, + algorithm: req.body.algorithm + } + } }); return object; @@ -106,11 +126,71 @@ export const registerKmipOperationRouter = async (server: FastifyZodProvider) => }, handler: async (req) => { const object = await server.services.kmipOperation.get({ - projectId: req.kmipUser.projectId, - clientId: req.kmipUser.clientId, + ...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, + metadata: { + id: object.id + } + } + }); + + return object; + } + }); + + server.route({ + method: "POST", + url: "/delete", + config: { + rateLimit: writeLimit + }, + schema: { + description: "KMIP endpoint for destroying managed objects", + body: z.object({ + id: z.string() + }), + response: { + 200: z.object({ + id: z.string() + }) + } + }, + handler: async (req) => { + const object = await server.services.kmipOperation.deleteOp({ + ...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_DELETE, + metadata: { + id: object.id + } + } + }); + 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 5c1fabacb..7ac11fb11 100644 --- a/backend/src/ee/services/audit-log/audit-log-types.ts +++ b/backend/src/ee/services/audit-log/audit-log-types.ts @@ -41,7 +41,14 @@ export type TListProjectAuditLogDTO = { export type TCreateAuditLogDTO = { event: Event; - actor: UserActor | IdentityActor | ServiceActor | ScimClientActor | PlatformActor | UnknownUserActor; + actor: + | UserActor + | IdentityActor + | ServiceActor + | ScimClientActor + | PlatformActor + | UnknownUserActor + | KmipClientActor; orgId?: string; projectId?: string; } & BaseAuthData; @@ -259,7 +266,10 @@ export enum EventType { DELETE_KMIP_CLIENT = "delete-kmip-client", GET_KMIP_CLIENT = "get-kmip-client", GET_KMIP_CLIENTS = "get-kmip-clients", - CREATE_KMIP_CLIENT_CERTIFICATE = "create-kmip-client-certificate" + 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" } interface UserActorMetadata { @@ -282,6 +292,11 @@ interface ScimClientActorMetadata {} interface PlatformActorMetadata {} +interface KmipClientActorMetadata { + clientId: string; + name: string; +} + interface UnknownUserActorMetadata {} export interface UserActor { @@ -299,6 +314,11 @@ export interface PlatformActor { metadata: PlatformActorMetadata; } +export interface KmipClientActor { + type: ActorType.KMIP_CLIENT; + metadata: KmipClientActorMetadata; +} + export interface UnknownUserActor { type: ActorType.UNKNOWN_USER; metadata: UnknownUserActorMetadata; @@ -314,7 +334,7 @@ export interface ScimClientActor { metadata: ScimClientActorMetadata; } -export type Actor = UserActor | ServiceActor | IdentityActor | ScimClientActor | PlatformActor; +export type Actor = UserActor | ServiceActor | IdentityActor | ScimClientActor | PlatformActor | KmipClientActor; interface GetSecretsEvent { type: EventType.GET_SECRETS; @@ -2123,6 +2143,28 @@ interface CreateKmipClientCertificateEvent { }; } +interface KmipOperationGetEvent { + type: EventType.KMIP_OPERATION_GET; + metadata: { + id: string; + }; +} + +interface KmipOperationDeleteEvent { + type: EventType.KMIP_OPERATION_DELETE; + metadata: { + id: string; + }; +} + +interface KmipOperationCreateEvent { + type: EventType.KMIP_OPERATION_CREATE; + metadata: { + id: string; + algorithm: string; + }; +} + export type Event = | GetSecretsEvent | GetSecretEvent @@ -2319,4 +2361,7 @@ export type Event = | DeleteKmipClientEvent | GetKmipClientEvent | GetKmipClientsEvent - | CreateKmipClientCertificateEvent; + | CreateKmipClientCertificateEvent + | KmipOperationGetEvent + | KmipOperationDeleteEvent + | KmipOperationCreateEvent; diff --git a/backend/src/ee/services/kmip/kmip-enum.ts b/backend/src/ee/services/kmip/kmip-enum.ts index aca3421bb..065ac2183 100644 --- a/backend/src/ee/services/kmip/kmip-enum.ts +++ b/backend/src/ee/services/kmip/kmip-enum.ts @@ -2,5 +2,6 @@ export enum KmipPermission { Create = "create", Locate = "locate", Check = "check", - Get = "get" + Get = "get", + Delete = "delete" } diff --git a/backend/src/ee/services/kmip/kmip-operation-service.ts b/backend/src/ee/services/kmip/kmip-operation-service.ts index bafecefa3..c650bea1e 100644 --- a/backend/src/ee/services/kmip/kmip-operation-service.ts +++ b/backend/src/ee/services/kmip/kmip-operation-service.ts @@ -6,7 +6,7 @@ import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TKmipClientDALFactory } from "./kmip-client-dal"; import { KmipPermission } from "./kmip-enum"; -import { TKmipCreateDTO, TKmipGetDTO } from "./kmip-types"; +import { TKmipCreateDTO, TKmipDeleteDTO, TKmipGetDTO } from "./kmip-types"; type TKmipOperationServiceFactoryDep = { kmsService: TKmsServiceFactory; @@ -23,7 +23,7 @@ export const kmipOperationServiceFactory = ({ projectDAL, kmipClientDAL }: TKmipOperationServiceFactoryDep) => { - const create = async ({ projectId: preSplitProjectId, clientId, encryptionAlgorithm }: TKmipCreateDTO) => { + const create = async ({ projectId: preSplitProjectId, clientId, algorithm }: TKmipCreateDTO) => { let projectId = preSplitProjectId; const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS); if (cmekProjectFromSplit) { @@ -43,7 +43,7 @@ export const kmipOperationServiceFactory = ({ } const kmsKey = await kmsService.generateKmsKey({ - encryptionAlgorithm, + encryptionAlgorithm: algorithm, orgId: project.orgId, projectId, isReserved: false @@ -52,6 +52,50 @@ export const kmipOperationServiceFactory = ({ return kmsKey; }; + const deleteOp = async ({ projectId: preSplitProjectId, id, clientId }: TKmipDeleteDTO) => { + 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.Delete)) { + throw new ForbiddenRequestError({ + message: "Client does not have sufficient permission to perform KMIP delete" + }); + } + + 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 delete reserved keys" }); + } + + const completeKeyDetails = await kmsDAL.findByIdWithAssociatedKms(id); + if (!completeKeyDetails.internalKms) { + throw new BadRequestError({ + message: "Cannot delete external keys" + }); + } + + const kms = kmsDAL.deleteById(id); + + return kms; + }; + const get = async ({ projectId: preSplitProjectId, id, clientId }: TKmipGetDTO) => { let projectId = preSplitProjectId; const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS); @@ -88,7 +132,7 @@ export const kmipOperationServiceFactory = ({ if (!completeKeyDetails.internalKms) { throw new BadRequestError({ - message: "Cannot get external key" + message: "Cannot get external keys" }); } @@ -105,6 +149,7 @@ export const kmipOperationServiceFactory = ({ return { create, - get + get, + deleteOp }; }; diff --git a/backend/src/ee/services/kmip/kmip-types.ts b/backend/src/ee/services/kmip/kmip-types.ts index 39dc4438c..265a5e61a 100644 --- a/backend/src/ee/services/kmip/kmip-types.ts +++ b/backend/src/ee/services/kmip/kmip-types.ts @@ -43,14 +43,19 @@ export type TListKmipClientsByProjectIdDTO = { search?: string; } & TProjectPermission; -export type TKmipCreateDTO = { +type KmipOperationBaseDTO = { clientId: string; projectId: string; - encryptionAlgorithm: SymmetricEncryption; }; +export type TKmipCreateDTO = { + algorithm: SymmetricEncryption; +} & KmipOperationBaseDTO; + export type TKmipGetDTO = { - clientId: string; - projectId: string; id: string; -}; +} & KmipOperationBaseDTO; + +export type TKmipDeleteDTO = { + id: string; +} & KmipOperationBaseDTO; diff --git a/backend/src/services/auth/auth-type.ts b/backend/src/services/auth/auth-type.ts index 05412a73a..497414a60 100644 --- a/backend/src/services/auth/auth-type.ts +++ b/backend/src/services/auth/auth-type.ts @@ -35,6 +35,7 @@ export enum AuthMode { export enum ActorType { // would extend to AWS, Azure, ... PLATFORM = "platform", // Useful for when we want to perform logging on automated actions such as integration syncs. + KMIP_CLIENT = "kmipClient", USER = "user", // userIdentity SERVICE = "service", IDENTITY = "identity", diff --git a/frontend/src/hooks/api/auditLogs/constants.tsx b/frontend/src/hooks/api/auditLogs/constants.tsx index 6a990f80e..bc8cae1d0 100644 --- a/frontend/src/hooks/api/auditLogs/constants.tsx +++ b/frontend/src/hooks/api/auditLogs/constants.tsx @@ -118,7 +118,16 @@ export const eventToNameMap: { [K in EventType]: string } = { [EventType.OIDC_GROUP_MEMBERSHIP_MAPPING_ASSIGN_USER]: "OIDC group membership mapping assigned user to groups", [EventType.OIDC_GROUP_MEMBERSHIP_MAPPING_REMOVE_USER]: - "OIDC group membership mapping removed user from groups" + "OIDC group membership mapping removed user from groups", + [EventType.CREATE_KMIP_CLIENT]: "Create KMIP client", + [EventType.UPDATE_KMIP_CLIENT]: "Update KMIP client", + [EventType.DELETE_KMIP_CLIENT]: "Delete KMIP client", + [EventType.GET_KMIP_CLIENT]: "Get KMIP client", + [EventType.GET_KMIP_CLIENTS]: "Get KMIP clients", + [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" }; 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 349811180..e0a42f0bc 100644 --- a/frontend/src/hooks/api/auditLogs/enums.tsx +++ b/frontend/src/hooks/api/auditLogs/enums.tsx @@ -1,5 +1,6 @@ export enum ActorType { PLATFORM = "platform", + KMIP_CLIENT = "kmipClient", USER = "user", SERVICE = "service", IDENTITY = "identity", @@ -129,5 +130,14 @@ export enum EventType { SECRET_SYNC_IMPORT_SECRETS = "secret-sync-import-secrets", SECRET_SYNC_REMOVE_SECRETS = "secret-sync-remove-secrets", OIDC_GROUP_MEMBERSHIP_MAPPING_ASSIGN_USER = "oidc-group-membership-mapping-assign-user", - OIDC_GROUP_MEMBERSHIP_MAPPING_REMOVE_USER = "oidc-group-membership-mapping-remove-user" + OIDC_GROUP_MEMBERSHIP_MAPPING_REMOVE_USER = "oidc-group-membership-mapping-remove-user", + CREATE_KMIP_CLIENT = "create-kmip-client", + UPDATE_KMIP_CLIENT = "update-kmip-client", + DELETE_KMIP_CLIENT = "delete-kmip-client", + GET_KMIP_CLIENT = "get-kmip-client", + GET_KMIP_CLIENTS = "get-kmip-clients", + 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" } diff --git a/frontend/src/hooks/api/auditLogs/types.tsx b/frontend/src/hooks/api/auditLogs/types.tsx index 338671cab..8acaa215a 100644 --- a/frontend/src/hooks/api/auditLogs/types.tsx +++ b/frontend/src/hooks/api/auditLogs/types.tsx @@ -30,6 +30,10 @@ interface IdentityActorMetadata { identityId: string; name: string; } +interface KmipClientActorMetadata { + clientId: string; + name: string; +} interface UserActor { type: ActorType.USER; @@ -51,11 +55,22 @@ export interface PlatformActor { metadata: object; } +export interface KmipClientActor { + type: ActorType.KMIP_CLIENT; + metadata: KmipClientActorMetadata; +} + export interface UnknownUserActor { type: ActorType.UNKNOWN_USER; } -export type Actor = UserActor | ServiceActor | IdentityActor | PlatformActor | UnknownUserActor; +export type Actor = + | UserActor + | ServiceActor + | IdentityActor + | PlatformActor + | UnknownUserActor + | KmipClientActor; interface GetSecretsEvent { type: EventType.GET_SECRETS; diff --git a/frontend/src/hooks/api/kmip/types.ts b/frontend/src/hooks/api/kmip/types.ts index f1b9fc88d..036a2caef 100644 --- a/frontend/src/hooks/api/kmip/types.ts +++ b/frontend/src/hooks/api/kmip/types.ts @@ -5,7 +5,8 @@ export enum KmipPermission { Create = "create", Locate = "locate", Check = "check", - Get = "get" + Get = "get", + Delete = "delete" } export type TKmipClient = { diff --git a/frontend/src/pages/kms/KmipPage/components/KmipClientModal.tsx b/frontend/src/pages/kms/KmipPage/components/KmipClientModal.tsx index 92b6d3769..7f8b37158 100644 --- a/frontend/src/pages/kms/KmipPage/components/KmipClientModal.tsx +++ b/frontend/src/pages/kms/KmipPage/components/KmipClientModal.tsx @@ -21,7 +21,8 @@ const KMIP_PERMISSIONS_OPTIONS = [ { value: KmipPermission.Check, label: "Check" }, { value: KmipPermission.Create, label: "Create" }, { value: KmipPermission.Get, label: "Get" }, - { value: KmipPermission.Locate, label: "Locate" } + { value: KmipPermission.Locate, label: "Locate" }, + { value: KmipPermission.Delete, label: "Delete" } ] as const; const formSchema = z.object({ @@ -31,7 +32,8 @@ const formSchema = z.object({ [KmipPermission.Check]: z.boolean().optional(), [KmipPermission.Create]: z.boolean().optional(), [KmipPermission.Get]: z.boolean().optional(), - [KmipPermission.Locate]: z.boolean().optional() + [KmipPermission.Locate]: z.boolean().optional(), + [KmipPermission.Delete]: z.boolean().optional() }) }); diff --git a/frontend/src/pages/organization/AuditLogsPage/components/LogsFilter.tsx b/frontend/src/pages/organization/AuditLogsPage/components/LogsFilter.tsx index 5c7e020b7..c32819cde 100644 --- a/frontend/src/pages/organization/AuditLogsPage/components/LogsFilter.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/components/LogsFilter.tsx @@ -94,6 +94,15 @@ export const LogsFilter = ({ {actor.metadata.name} ); + case ActorType.KMIP_CLIENT: + return ( + + {actor.metadata.name} + + ); default: return ( diff --git a/frontend/src/pages/organization/AuditLogsPage/components/LogsTableRow.tsx b/frontend/src/pages/organization/AuditLogsPage/components/LogsTableRow.tsx index f7c7398ee..5c3337938 100644 --- a/frontend/src/pages/organization/AuditLogsPage/components/LogsTableRow.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/components/LogsTableRow.tsx @@ -46,6 +46,13 @@ export const LogsTableRow = ({ auditLog, isOrgAuditLogs, showActorColumn }: Prop

Platform

); + case ActorType.KMIP_CLIENT: + return ( + +

{actor.metadata.name}

+

KMIP Client

+ + ); case ActorType.UNKNOWN_USER: return (