Add PKI collection to issue cert modal

This commit is contained in:
Tuan Dang
2024-08-15 10:41:37 -07:00
parent ca3d8c5594
commit 7baa3b4cbe
7 changed files with 99 additions and 3 deletions
+2
View File
@@ -1088,6 +1088,7 @@ export const CERTIFICATE_AUTHORITIES = {
},
ISSUE_CERT: {
caId: "The ID of the CA to issue the certificate from",
pkiCollectionId: "The ID of the PKI collection to add the certificate to",
friendlyName: "A friendly name for the certificate",
commonName: "The common name (CN) for the certificate",
altNames:
@@ -1103,6 +1104,7 @@ export const CERTIFICATE_AUTHORITIES = {
},
SIGN_CERT: {
caId: "The ID of the CA to issue the certificate from",
pkiCollectionId: "The ID of the PKI collection to add the certificate to",
csr: "The pem-encoded CSR to sign with the CA to be used for certificate issuance",
friendlyName: "A friendly name for the certificate",
commonName: "The common name (CN) for the certificate",
+2
View File
@@ -622,6 +622,8 @@ export const registerRoutes = async (
certificateAuthorityQueue,
certificateDAL,
certificateBodyDAL,
pkiCollectionDAL,
pkiCollectionItemDAL,
projectDAL,
kmsService,
permissionService
@@ -556,6 +556,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
}),
body: z
.object({
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.pkiCollectionId),
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.friendlyName),
commonName: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.commonName),
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.altNames),
@@ -635,6 +636,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
body: z
.object({
csr: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.csr),
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.pkiCollectionId),
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.friendlyName),
commonName: z.string().trim().min(1).optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.commonName),
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.altNames),
@@ -7,10 +7,12 @@ import { z } from "zod";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError } from "@app/lib/errors";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
import { TPkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-collection-item-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
@@ -60,6 +62,8 @@ type TCertificateAuthorityServiceFactoryDep = {
certificateAuthorityQueue: TCertificateAuthorityQueueFactory; // TODO: Pick
certificateDAL: Pick<TCertificateDALFactory, "transaction" | "create" | "find">;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "findById">;
pkiCollectionItemDAL: Pick<TPkiCollectionItemDALFactory, "create">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
@@ -74,6 +78,8 @@ export const certificateAuthorityServiceFactory = ({
certificateAuthorityCrlDAL,
certificateDAL,
certificateBodyDAL,
pkiCollectionDAL,
pkiCollectionItemDAL,
projectDAL,
kmsService,
permissionService
@@ -1007,6 +1013,7 @@ export const certificateAuthorityServiceFactory = ({
*/
const issueCertFromCa = async ({
caId,
pkiCollectionId,
friendlyName,
commonName,
altNames,
@@ -1039,6 +1046,13 @@ export const certificateAuthorityServiceFactory = ({
throw new BadRequestError({ message: "CA is expired" });
}
// check PKI collection
if (pkiCollectionId) {
const pkiCollection = await pkiCollectionDAL.findById(pkiCollectionId);
if (!pkiCollection) throw new NotFoundError({ message: "PKI collection not found" });
if (pkiCollection.projectId !== ca.projectId) throw new BadRequestError({ message: "Invalid PKI collection" });
}
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
projectId: ca.projectId,
projectDAL,
@@ -1186,6 +1200,16 @@ export const certificateAuthorityServiceFactory = ({
tx
);
if (pkiCollectionId) {
await pkiCollectionItemDAL.create(
{
pkiCollectionId,
certId: cert.id
},
tx
);
}
return cert;
});
@@ -1214,6 +1238,7 @@ export const certificateAuthorityServiceFactory = ({
const signCertFromCa = async ({
caId,
csr,
pkiCollectionId,
friendlyName,
commonName,
altNames,
@@ -1247,6 +1272,13 @@ export const certificateAuthorityServiceFactory = ({
throw new BadRequestError({ message: "CA is expired" });
}
// check PKI collection
if (pkiCollectionId) {
const pkiCollection = await pkiCollectionDAL.findById(pkiCollectionId);
if (!pkiCollection) throw new NotFoundError({ message: "PKI collection not found" });
if (pkiCollection.projectId !== ca.projectId) throw new BadRequestError({ message: "Invalid PKI collection" });
}
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
projectId: ca.projectId,
projectDAL,
@@ -1390,6 +1422,16 @@ export const certificateAuthorityServiceFactory = ({
tx
);
if (pkiCollectionId) {
await pkiCollectionItemDAL.create(
{
pkiCollectionId,
certId: cert.id
},
tx
);
}
return cert;
});
@@ -87,6 +87,7 @@ export type TImportCertToCaDTO = {
export type TIssueCertFromCaDTO = {
caId: string;
pkiCollectionId?: string;
friendlyName?: string;
commonName: string;
altNames: string;
@@ -98,6 +99,7 @@ export type TIssueCertFromCaDTO = {
export type TSignCertFromCaDTO = {
caId: string;
csr: string;
pkiCollectionId?: string;
friendlyName?: string;
commonName?: string;
altNames: string;
+1
View File
@@ -80,6 +80,7 @@ export type TImportCaCertificateResponse = {
export type TCreateCertificateDTO = {
projectSlug: string;
caId: string;
pkiCollectionId?: string;
friendlyName?: string;
commonName: string;
altNames: string; // sans
@@ -14,7 +14,13 @@ import {
SelectItem
} from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { CaStatus, useCreateCertificate, useGetCert, useListWorkspaceCas } from "@app/hooks/api";
import {
CaStatus,
useCreateCertificate,
useGetCert,
useListWorkspaceCas,
useListWorkspacePkiCollections
} from "@app/hooks/api";
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -22,6 +28,7 @@ import { CertificateContent } from "./CertificateContent";
const schema = z.object({
caId: z.string(),
collectionId: z.string().optional(),
friendlyName: z.string(),
commonName: z.string().trim().min(1),
altNames: z.string(),
@@ -54,6 +61,10 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
status: CaStatus.ACTIVE
});
const { data } = useListWorkspacePkiCollections({
workspaceId: currentWorkspace?.id || ""
});
const { mutateAsync: createCertificate } = useCreateCertificate();
const {
@@ -86,13 +97,21 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
}
}, [cert]);
const onFormSubmit = async ({ caId, friendlyName, commonName, altNames, ttl }: FormData) => {
const onFormSubmit = async ({
caId,
collectionId,
friendlyName,
commonName,
altNames,
ttl
}: FormData) => {
try {
if (!currentWorkspace?.slug) return;
const { serialNumber, certificate, certificateChain, privateKey } = await createCertificate({
projectSlug: currentWorkspace.slug,
caId,
pkiCollectionId: collectionId,
friendlyName,
commonName,
altNames,
@@ -167,6 +186,32 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
</FormControl>
)}
/>
<Controller
control={control}
name="collectionId"
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
<FormControl
label="Certificate Collection (Optional)"
errorText={error?.message}
isError={Boolean(error)}
className="mt-4"
>
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
isDisabled={Boolean(cert)}
>
{(data?.collections || []).map(({ id, name }) => (
<SelectItem value={id} key={`pki-collection-${id}`}>
{name}
</SelectItem>
))}
</Select>
</FormControl>
)}
/>
<Controller
control={control}
defaultValue=""