From 07e4bc8eed9f68a2afa3872c9777702d143d3e40 Mon Sep 17 00:00:00 2001 From: x Date: Wed, 30 Apr 2025 17:46:05 -0400 Subject: [PATCH] review fixes --- .../services/permission/project-permission.ts | 41 +++++++++------ backend/src/server/lib/caching.ts | 8 +++ .../server/routes/v1/certificate-router.ts | 17 ++----- .../certificate-authority-service.ts | 13 +++-- .../certificate/certificate-service.ts | 38 ++++++++++---- .../src/services/project/project-service.ts | 6 ++- .../ProjectPermissionContext/index.tsx | 1 + .../context/ProjectPermissionContext/types.ts | 12 +++-- frontend/src/context/index.tsx | 1 + .../CertificatesPage/CertificatesPage.tsx | 11 ++-- .../components/CertificatesSection.tsx | 8 ++- .../components/CertificatesTable.tsx | 14 +++-- .../ProjectRoleModifySection.utils.tsx | 51 ++++++++++++------- 13 files changed, 149 insertions(+), 72 deletions(-) create mode 100644 backend/src/server/lib/caching.ts diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index 7d659e99b..1dbc5ee2a 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -17,6 +17,14 @@ export enum ProjectPermissionActions { Delete = "delete" } +export enum ProjectPermissionCertificateActions { + Read = "read", + Create = "create", + Edit = "edit", + Delete = "delete", + ReadPrivateKey = "read-private-key" +} + export enum ProjectPermissionSecretActions { DescribeAndReadValue = "read", DescribeSecret = "describeSecret", @@ -129,7 +137,6 @@ export enum ProjectPermissionSub { Identity = "identity", CertificateAuthorities = "certificate-authorities", Certificates = "certificates", - CertificatePrivateKey = "certificate-private-key", CertificateTemplates = "certificate-templates", SshCertificateAuthorities = "ssh-certificate-authorities", SshCertificates = "ssh-certificates", @@ -232,8 +239,7 @@ export type ProjectPermissionSet = ProjectPermissionSub.Identity | (ForcedSubject & IdentityManagementSubjectFields) ] | [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities] - | [ProjectPermissionActions, ProjectPermissionSub.Certificates] - | [ProjectPermissionActions, ProjectPermissionSub.CertificatePrivateKey] + | [ProjectPermissionCertificateActions, ProjectPermissionSub.Certificates] | [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificates] @@ -482,12 +488,6 @@ const GeneralPermissionSchema = [ "Describe what action an entity can take." ) }), - z.object({ - subject: z.literal(ProjectPermissionSub.CertificatePrivateKey).describe("The entity this permission pertains to."), - action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe( - "Describe what action an entity can take." - ) - }), z.object({ subject: z.literal(ProjectPermissionSub.CertificateTemplates).describe("The entity this permission pertains to."), action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe( @@ -688,8 +688,6 @@ const buildAdminPermissionRules = () => { ProjectPermissionSub.AuditLogs, ProjectPermissionSub.IpAllowList, ProjectPermissionSub.CertificateAuthorities, - ProjectPermissionSub.Certificates, - ProjectPermissionSub.CertificatePrivateKey, ProjectPermissionSub.CertificateTemplates, ProjectPermissionSub.PkiAlerts, ProjectPermissionSub.PkiCollections, @@ -708,6 +706,17 @@ const buildAdminPermissionRules = () => { ); }); + can( + [ + ProjectPermissionCertificateActions.Read, + ProjectPermissionCertificateActions.Edit, + ProjectPermissionCertificateActions.Create, + ProjectPermissionCertificateActions.Delete, + ProjectPermissionCertificateActions.ReadPrivateKey + ], + ProjectPermissionSub.Certificates + ); + can( [ ProjectPermissionSshHostActions.Edit, @@ -965,10 +974,10 @@ const buildMemberPermissionRules = () => { can( [ - ProjectPermissionActions.Read, - ProjectPermissionActions.Edit, - ProjectPermissionActions.Create, - ProjectPermissionActions.Delete + ProjectPermissionCertificateActions.Read, + ProjectPermissionCertificateActions.Edit, + ProjectPermissionCertificateActions.Create, + ProjectPermissionCertificateActions.Delete ], ProjectPermissionSub.Certificates ); @@ -1041,7 +1050,7 @@ const buildViewerPermissionRules = () => { can(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities); - can(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates); + can(ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates); can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates); can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates); diff --git a/backend/src/server/lib/caching.ts b/backend/src/server/lib/caching.ts new file mode 100644 index 000000000..513f2f635 --- /dev/null +++ b/backend/src/server/lib/caching.ts @@ -0,0 +1,8 @@ +import { FastifyReply } from "fastify"; + +export const addNoCacheHeaders = (reply: FastifyReply) => { + void reply.header("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); + void reply.header("Pragma", "no-cache"); + void reply.header("Expires", "0"); + void reply.header("Surrogate-Control", "no-store"); +}; diff --git a/backend/src/server/routes/v1/certificate-router.ts b/backend/src/server/routes/v1/certificate-router.ts index ecbb02734..dad1d9a80 100644 --- a/backend/src/server/routes/v1/certificate-router.ts +++ b/backend/src/server/routes/v1/certificate-router.ts @@ -6,6 +6,7 @@ import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { ApiDocsTags, CERTIFICATE_AUTHORITIES, CERTIFICATES } from "@app/lib/api-docs"; import { ms } from "@app/lib/ms"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; +import { addNoCacheHeaders } from "@app/server/lib/caching"; import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -106,11 +107,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => { } }); - // Prevent proxies from caching sensitive data (private key) - reply.header("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); - reply.header("Pragma", "no-cache"); - reply.header("Expires", "0"); - reply.header("Surrogate-Control", "no-store"); + addNoCacheHeaders(reply); return certPrivateKey; } @@ -134,7 +131,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => { response: { 200: z.object({ certificate: z.string().trim().describe(CERTIFICATES.GET_CERT.certificate), - certificateChain: z.string().trim().describe(CERTIFICATES.GET_CERT.certificateChain), + certificateChain: z.string().trim().nullish().describe(CERTIFICATES.GET_CERT.certificateChain), privateKey: z.string().trim().describe(CERTIFICATES.GET_CERT.privateKey), serialNumber: z.string().trim().describe(CERTIFICATES.GET_CERT.serialNumberRes) }) @@ -163,11 +160,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => { } }); - // Prevent proxies from caching sensitive data (private key) - reply.header("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); - reply.header("Pragma", "no-cache"); - reply.header("Expires", "0"); - reply.header("Surrogate-Control", "no-store"); + addNoCacheHeaders(reply); return { certificate, @@ -525,7 +518,7 @@ export const registerCertRouter = async (server: FastifyZodProvider) => { response: { 200: z.object({ certificate: z.string().trim().describe(CERTIFICATES.GET_CERT.certificate), - certificateChain: z.string().trim().describe(CERTIFICATES.GET_CERT.certificateChain), + certificateChain: z.string().trim().nullish().describe(CERTIFICATES.GET_CERT.certificateChain), serialNumber: z.string().trim().describe(CERTIFICATES.GET_CERT.serialNumberRes) }) } diff --git a/backend/src/services/certificate-authority/certificate-authority-service.ts b/backend/src/services/certificate-authority/certificate-authority-service.ts index 4c40d9ff4..e1d7ce5cb 100644 --- a/backend/src/services/certificate-authority/certificate-authority-service.ts +++ b/backend/src/services/certificate-authority/certificate-authority-service.ts @@ -6,7 +6,11 @@ import { z } from "zod"; import { ActionProjectType, ProjectType, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { + ProjectPermissionActions, + ProjectPermissionCertificateActions, + ProjectPermissionSub +} from "@app/ee/services/permission/project-permission"; import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; @@ -1160,7 +1164,10 @@ export const certificateAuthorityServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.Create, + ProjectPermissionSub.Certificates + ); if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" }); if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" }); @@ -1508,7 +1515,7 @@ export const certificateAuthorityServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Create, + ProjectPermissionCertificateActions.Create, ProjectPermissionSub.Certificates ); } diff --git a/backend/src/services/certificate/certificate-service.ts b/backend/src/services/certificate/certificate-service.ts index d07027fd8..73a8caed7 100644 --- a/backend/src/services/certificate/certificate-service.ts +++ b/backend/src/services/certificate/certificate-service.ts @@ -4,7 +4,10 @@ import * as x509 from "@peculiar/x509"; import { ActionProjectType } from "@app/db/schemas"; import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; -import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { + ProjectPermissionCertificateActions, + ProjectPermissionSub +} from "@app/ee/services/permission/project-permission"; import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal"; import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal"; import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal"; @@ -70,7 +73,10 @@ export const certificateServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.Read, + ProjectPermissionSub.Certificates + ); return { cert, @@ -101,8 +107,8 @@ export const certificateServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.CertificatePrivateKey + ProjectPermissionCertificateActions.ReadPrivateKey, + ProjectPermissionSub.Certificates ); const { certPrivateKey } = await getCertificateCredentials({ @@ -136,7 +142,10 @@ export const certificateServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Certificates); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.Delete, + ProjectPermissionSub.Certificates + ); const deletedCert = await certificateDAL.deleteById(cert.id); @@ -171,7 +180,10 @@ export const certificateServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Certificates); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.Delete, + ProjectPermissionSub.Certificates + ); if (cert.status === CertStatus.REVOKED) throw new Error("Certificate already revoked"); @@ -218,7 +230,10 @@ export const certificateServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.Read, + ProjectPermissionSub.Certificates + ); const certBody = await certificateBodyDAL.findOne({ certId: cert.id }); @@ -279,10 +294,13 @@ export const certificateServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionActions.Read, - ProjectPermissionSub.CertificatePrivateKey + ProjectPermissionCertificateActions.Read, + ProjectPermissionSub.Certificates + ); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.ReadPrivateKey, + ProjectPermissionSub.Certificates ); const certBody = await certificateBodyDAL.findOne({ certId: cert.id }); diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index 9f2de8a85..db67f49d3 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -14,6 +14,7 @@ import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/servi import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, + ProjectPermissionCertificateActions, ProjectPermissionSecretActions, ProjectPermissionSshHostActions, ProjectPermissionSub @@ -927,7 +928,10 @@ export const projectServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateActions.Read, + ProjectPermissionSub.Certificates + ); const cas = await certificateAuthorityDAL.find({ projectId }); diff --git a/frontend/src/context/ProjectPermissionContext/index.tsx b/frontend/src/context/ProjectPermissionContext/index.tsx index 5bc163817..b195571f8 100644 --- a/frontend/src/context/ProjectPermissionContext/index.tsx +++ b/frontend/src/context/ProjectPermissionContext/index.tsx @@ -2,6 +2,7 @@ export { useProjectPermission } from "./ProjectPermissionContext"; export type { ProjectPermissionSet, TProjectPermission } from "./types"; export { ProjectPermissionActions, + ProjectPermissionCertificateActions, ProjectPermissionCmekActions, ProjectPermissionDynamicSecretActions, ProjectPermissionGroupActions, diff --git a/frontend/src/context/ProjectPermissionContext/types.ts b/frontend/src/context/ProjectPermissionContext/types.ts index 67ea65df4..5aee9c483 100644 --- a/frontend/src/context/ProjectPermissionContext/types.ts +++ b/frontend/src/context/ProjectPermissionContext/types.ts @@ -7,6 +7,14 @@ export enum ProjectPermissionActions { Delete = "delete" } +export enum ProjectPermissionCertificateActions { + Read = "read", + Create = "create", + Edit = "edit", + Delete = "delete", + ReadPrivateKey = "read-private-key" +} + export enum ProjectPermissionSecretActions { DescribeAndReadValue = "read", DescribeSecret = "describeSecret", @@ -170,7 +178,6 @@ export enum ProjectPermissionSub { Identity = "identity", CertificateAuthorities = "certificate-authorities", Certificates = "certificates", - CertificatePrivateKey = "certificate-private-key", CertificateTemplates = "certificate-templates", SshCertificateAuthorities = "ssh-certificate-authorities", SshCertificateTemplates = "ssh-certificate-templates", @@ -268,9 +275,8 @@ export type ProjectPermissionSet = ) ] | [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities] - | [ProjectPermissionActions, ProjectPermissionSub.Certificates] + | [ProjectPermissionCertificateActions, ProjectPermissionSub.Certificates] | [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates] - | [ProjectPermissionActions, ProjectPermissionSub.CertificatePrivateKey] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificates] diff --git a/frontend/src/context/index.tsx b/frontend/src/context/index.tsx index 51f2797d0..04af3c8a4 100644 --- a/frontend/src/context/index.tsx +++ b/frontend/src/context/index.tsx @@ -10,6 +10,7 @@ export { export type { TProjectPermission } from "./ProjectPermissionContext"; export { ProjectPermissionActions, + ProjectPermissionCertificateActions, ProjectPermissionCmekActions, ProjectPermissionDynamicSecretActions, ProjectPermissionGroupActions, diff --git a/frontend/src/pages/cert-manager/CertificatesPage/CertificatesPage.tsx b/frontend/src/pages/cert-manager/CertificatesPage/CertificatesPage.tsx index c985f313f..4a4a22093 100644 --- a/frontend/src/pages/cert-manager/CertificatesPage/CertificatesPage.tsx +++ b/frontend/src/pages/cert-manager/CertificatesPage/CertificatesPage.tsx @@ -3,7 +3,12 @@ import { useTranslation } from "react-i18next"; import { ProjectPermissionCan } from "@app/components/permissions"; import { PageHeader } from "@app/components/v2"; -import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context"; +import { + ProjectPermissionActions, + ProjectPermissionCertificateActions, + ProjectPermissionSub, + useProjectPermission +} from "@app/context"; import { PkiCollectionSection } from "../AlertingPage/components"; import { CertificatesSection } from "./components"; @@ -17,7 +22,7 @@ export const CertificatesPage = () => { ProjectPermissionSub.PkiCollections ); const canAccessCerts = permission.can( - ProjectPermissionActions.Read, + ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates ); @@ -40,7 +45,7 @@ export const CertificatesPage = () => { )} diff --git a/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesSection.tsx b/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesSection.tsx index d5f94e7b7..ef8b09dac 100644 --- a/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesSection.tsx +++ b/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesSection.tsx @@ -4,7 +4,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { createNotification } from "@app/components/notifications"; import { ProjectPermissionCan } from "@app/components/permissions"; import { Button, DeleteActionModal } from "@app/components/v2"; -import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; +import { + ProjectPermissionCertificateActions, + ProjectPermissionSub, + useWorkspace +} from "@app/context"; import { useDeleteCert } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; @@ -50,7 +54,7 @@ export const CertificatesSection = () => {

Certificates

{(isAllowed) => ( diff --git a/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesTable.tsx b/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesTable.tsx index dcc832bfb..8cf7dda24 100644 --- a/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesTable.tsx +++ b/frontend/src/pages/cert-manager/CertificatesPage/components/CertificatesTable.tsx @@ -30,7 +30,11 @@ import { Tooltip, Tr } from "@app/components/v2"; -import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; +import { + ProjectPermissionCertificateActions, + ProjectPermissionSub, + useWorkspace +} from "@app/context"; import { useListWorkspaceCertificates } from "@app/hooks/api"; import { CertStatus } from "@app/hooks/api/certificates/enums"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -110,7 +114,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => { {(isAllowed) => ( @@ -131,7 +135,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => { )} {(isAllowed) => ( @@ -152,7 +156,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => { )} {(isAllowed) => ( @@ -173,7 +177,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => { )} {(isAllowed) => ( diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx index b68ee332f..6b90b791d 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx @@ -6,6 +6,7 @@ import { z } from "zod"; import { Tooltip } from "@app/components/v2"; import { ProjectPermissionActions, + ProjectPermissionCertificateActions, ProjectPermissionCmekActions, ProjectPermissionSub } from "@app/context"; @@ -32,6 +33,14 @@ const GeneralPolicyActionSchema = z.object({ create: z.boolean().optional() }); +const CertificatePolicyActionSchema = z.object({ + [ProjectPermissionCertificateActions.Create]: z.boolean().optional(), + [ProjectPermissionCertificateActions.Delete]: z.boolean().optional(), + [ProjectPermissionCertificateActions.Edit]: z.boolean().optional(), + [ProjectPermissionCertificateActions.Read]: z.boolean().optional(), + [ProjectPermissionCertificateActions.ReadPrivateKey]: z.boolean().optional() +}); + const SecretPolicyActionSchema = z.object({ [ProjectPermissionSecretActions.DescribeAndReadValue]: z.boolean().optional(), // existing read, gives both describe and read value [ProjectPermissionSecretActions.DescribeSecret]: z.boolean().optional(), @@ -219,11 +228,10 @@ export const projectRoleFormSchema = z.object({ [ProjectPermissionSub.AuditLogs]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.IpAllowList]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.CertificateAuthorities]: GeneralPolicyActionSchema.array().default([]), - [ProjectPermissionSub.Certificates]: GeneralPolicyActionSchema.array().default([]), + [ProjectPermissionSub.Certificates]: CertificatePolicyActionSchema.array().default([]), [ProjectPermissionSub.PkiAlerts]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.PkiCollections]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.CertificateTemplates]: GeneralPolicyActionSchema.array().default([]), - [ProjectPermissionSub.CertificatePrivateKey]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.SshCertificateAuthorities]: GeneralPolicyActionSchema.array().default( [] ), @@ -371,11 +379,9 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { ProjectPermissionSub.AuditLogs, ProjectPermissionSub.IpAllowList, ProjectPermissionSub.CertificateAuthorities, - ProjectPermissionSub.Certificates, ProjectPermissionSub.PkiAlerts, ProjectPermissionSub.PkiCollections, ProjectPermissionSub.CertificateTemplates, - ProjectPermissionSub.CertificatePrivateKey, ProjectPermissionSub.SecretApproval, ProjectPermissionSub.Tags, ProjectPermissionSub.SecretRotation, @@ -507,6 +513,25 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { return; } + if (subject === ProjectPermissionSub.Certificates) { + const canRead = action.includes(ProjectPermissionCertificateActions.Read); + const canEdit = action.includes(ProjectPermissionCertificateActions.Edit); + const canDelete = action.includes(ProjectPermissionCertificateActions.Delete); + const canCreate = action.includes(ProjectPermissionCertificateActions.Create); + const canReadPrivateKey = action.includes(ProjectPermissionCertificateActions.ReadPrivateKey); + + if (!formVal[subject]) formVal[subject] = [{}]; + + // from above statement we are sure it won't be undefined + if (canRead) formVal[subject]![0].read = true; + if (canEdit) formVal[subject]![0].edit = true; + if (canCreate) formVal[subject]![0].create = true; + if (canDelete) formVal[subject]![0].delete = true; + if (canReadPrivateKey) + formVal[subject]![0][ProjectPermissionCertificateActions.ReadPrivateKey] = true; + return; + } + if (subject === ProjectPermissionSub.Project) { const canEdit = action.includes(ProjectPermissionActions.Edit); const canDelete = action.includes(ProjectPermissionActions.Delete); @@ -1014,19 +1039,11 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = { [ProjectPermissionSub.Certificates]: { title: "Certificates", actions: [ - { label: "Read", value: "read" }, - { label: "Create", value: "create" }, - { label: "Modify", value: "edit" }, - { label: "Remove", value: "delete" } - ] - }, - [ProjectPermissionSub.CertificatePrivateKey]: { - title: "Certificate Private Key", - actions: [ - { label: "Read", value: "read" }, - { label: "Create", value: "create" }, - { label: "Modify", value: "edit" }, - { label: "Remove", value: "delete" } + { label: "Read", value: ProjectPermissionCertificateActions.Read }, + { label: "Read Private Key", value: ProjectPermissionCertificateActions.ReadPrivateKey }, + { label: "Create", value: ProjectPermissionCertificateActions.Create }, + { label: "Modify", value: ProjectPermissionCertificateActions.Edit }, + { label: "Remove", value: ProjectPermissionCertificateActions.Delete } ] }, [ProjectPermissionSub.CertificateTemplates]: {