misc: moved cert issuance to job

This commit is contained in:
Sheen Capadngan
2025-05-16 20:38:08 +08:00
parent 6d10afc9d2
commit 04d553f052
17 changed files with 373 additions and 169 deletions
+2
View File
@@ -37,6 +37,8 @@ export const KeyStorePrefixes = {
`sync-integration-last-run-${projectId}-${environmentSlug}-${secretPath}` as const, `sync-integration-last-run-${projectId}-${environmentSlug}-${secretPath}` as const,
SecretSyncLock: (syncId: string) => `secret-sync-mutex-${syncId}` as const, SecretSyncLock: (syncId: string) => `secret-sync-mutex-${syncId}` as const,
SecretRotationLock: (rotationId: string) => `secret-rotation-v2-mutex-${rotationId}` as const, SecretRotationLock: (rotationId: string) => `secret-rotation-v2-mutex-${rotationId}` as const,
CaOrderCertificateForSubscriberLock: (subscriberId: string) =>
`ca-order-certificate-for-subscriber-lock-${subscriberId}` as const,
SecretSyncLastRunTimestamp: (syncId: string) => `secret-sync-last-run-${syncId}` as const, SecretSyncLastRunTimestamp: (syncId: string) => `secret-sync-last-run-${syncId}` as const,
IdentityAccessTokenStatusUpdate: (identityAccessTokenId: string) => IdentityAccessTokenStatusUpdate: (identityAccessTokenId: string) =>
`identity-access-token-status:${identityAccessTokenId}`, `identity-access-token-status:${identityAccessTokenId}`,
+11 -1
View File
@@ -14,6 +14,7 @@ import {
} from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-queue-types"; } from "@app/ee/services/secret-scanning/secret-scanning-queue/secret-scanning-queue-types";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { CaType } from "@app/services/certificate-authority/certificate-authority-enums";
import { import {
TFailedIntegrationSyncEmailsPayload, TFailedIntegrationSyncEmailsPayload,
TIntegrationSyncPayload, TIntegrationSyncPayload,
@@ -44,6 +45,7 @@ export enum QueueName {
UpgradeProjectToGhost = "upgrade-project-to-ghost", UpgradeProjectToGhost = "upgrade-project-to-ghost",
DynamicSecretRevocation = "dynamic-secret-revocation", DynamicSecretRevocation = "dynamic-secret-revocation",
CaCrlRotation = "ca-crl-rotation", CaCrlRotation = "ca-crl-rotation",
CaLifecycle = "ca-lifecycle", // parent queue to ca-order-certificate-for-subscriber
SecretReplication = "secret-replication", SecretReplication = "secret-replication",
SecretSync = "secret-sync", // parent queue to push integration sync, webhook, and secret replication SecretSync = "secret-sync", // parent queue to push integration sync, webhook, and secret replication
ProjectV3Migration = "project-v3-migration", ProjectV3Migration = "project-v3-migration",
@@ -84,7 +86,8 @@ export enum QueueJobs {
SecretRotationV2QueueRotations = "secret-rotation-v2-queue-rotations", SecretRotationV2QueueRotations = "secret-rotation-v2-queue-rotations",
SecretRotationV2RotateSecrets = "secret-rotation-v2-rotate-secrets", SecretRotationV2RotateSecrets = "secret-rotation-v2-rotate-secrets",
SecretRotationV2SendNotification = "secret-rotation-v2-send-notification", SecretRotationV2SendNotification = "secret-rotation-v2-send-notification",
InvalidateCache = "invalidate-cache" InvalidateCache = "invalidate-cache",
CaOrderCertificateForSubscriber = "ca-order-certificate-for-subscriber"
} }
export type TQueueJobTypes = { export type TQueueJobTypes = {
@@ -245,6 +248,13 @@ export type TQueueJobTypes = {
}; };
}; };
}; };
[QueueName.CaLifecycle]: {
name: QueueJobs.CaOrderCertificateForSubscriber;
payload: {
subscriberId: string;
caType: CaType;
};
};
}; };
export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>; export type TQueueServiceFactory = ReturnType<typeof queueServiceFactory>;
+53 -43
View File
@@ -850,16 +850,6 @@ export const registerRoutes = async (
permissionService permissionService
}); });
const certificateAuthorityQueue = certificateAuthorityQueueFactory({
certificateAuthorityCrlDAL,
certificateAuthorityDAL,
certificateAuthoritySecretDAL,
certificateDAL,
projectDAL,
kmsService,
queueService
});
const sshCertificateAuthorityService = sshCertificateAuthorityServiceFactory({ const sshCertificateAuthorityService = sshCertificateAuthorityServiceFactory({
sshCertificateAuthorityDAL, sshCertificateAuthorityDAL,
sshCertificateAuthoritySecretDAL, sshCertificateAuthoritySecretDAL,
@@ -906,24 +896,6 @@ export const registerRoutes = async (
groupDAL groupDAL
}); });
const internalCertificateAuthorityService = internalCertificateAuthorityServiceFactory({
certificateAuthorityDAL,
certificateAuthorityCertDAL,
certificateAuthoritySecretDAL,
certificateAuthorityCrlDAL,
certificateTemplateDAL,
certificateAuthorityQueue,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
pkiCollectionDAL,
pkiCollectionItemDAL,
projectDAL,
internalCertificateAuthorityDAL,
kmsService,
permissionService
});
const certificateAuthorityCrlService = certificateAuthorityCrlServiceFactory({ const certificateAuthorityCrlService = certificateAuthorityCrlServiceFactory({
certificateAuthorityDAL, certificateAuthorityDAL,
certificateAuthorityCrlDAL, certificateAuthorityCrlDAL,
@@ -943,17 +915,6 @@ export const registerRoutes = async (
licenseService licenseService
}); });
const certificateEstService = certificateEstServiceFactory({
internalCertificateAuthorityService,
certificateTemplateService,
certificateTemplateDAL,
certificateAuthorityCertDAL,
certificateAuthorityDAL,
projectDAL,
kmsService,
licenseService
});
const pkiAlertService = pkiAlertServiceFactory({ const pkiAlertService = pkiAlertServiceFactory({
pkiAlertDAL, pkiAlertDAL,
pkiCollectionDAL, pkiCollectionDAL,
@@ -1627,6 +1588,52 @@ export const registerRoutes = async (
keyStore keyStore
}); });
const certificateAuthorityQueue = certificateAuthorityQueueFactory({
certificateAuthorityCrlDAL,
certificateAuthorityDAL,
certificateAuthoritySecretDAL,
certificateDAL,
projectDAL,
kmsService,
queueService,
pkiSubscriberDAL,
certificateBodyDAL,
certificateSecretDAL,
externalCertificateAuthorityDAL,
keyStore,
appConnectionDAL,
appConnectionService
});
const internalCertificateAuthorityService = internalCertificateAuthorityServiceFactory({
certificateAuthorityDAL,
certificateAuthorityCertDAL,
certificateAuthoritySecretDAL,
certificateAuthorityCrlDAL,
certificateTemplateDAL,
certificateAuthorityQueue,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
pkiCollectionDAL,
pkiCollectionItemDAL,
projectDAL,
internalCertificateAuthorityDAL,
kmsService,
permissionService
});
const certificateEstService = certificateEstServiceFactory({
internalCertificateAuthorityService,
certificateTemplateService,
certificateTemplateDAL,
certificateAuthorityCertDAL,
certificateAuthorityDAL,
projectDAL,
kmsService,
licenseService
});
const kmipService = kmipServiceFactory({ const kmipService = kmipServiceFactory({
kmipClientDAL, kmipClientDAL,
permissionService, permissionService,
@@ -1673,7 +1680,12 @@ export const registerRoutes = async (
appConnectionDAL, appConnectionDAL,
appConnectionService, appConnectionService,
externalCertificateAuthorityDAL, externalCertificateAuthorityDAL,
internalCertificateAuthorityService internalCertificateAuthorityService,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
kmsService,
pkiSubscriberDAL
}); });
const pkiSubscriberService = pkiSubscriberServiceFactory({ const pkiSubscriberService = pkiSubscriberServiceFactory({
@@ -1688,9 +1700,7 @@ export const registerRoutes = async (
projectDAL, projectDAL,
kmsService, kmsService,
permissionService, permissionService,
appConnectionDAL, certificateAuthorityQueue
appConnectionService,
externalCertificateAuthorityDAL
}); });
await secretRotationV2QueueServiceFactory({ await secretRotationV2QueueServiceFactory({
@@ -288,7 +288,7 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
schema: { schema: {
hide: false, hide: false,
tags: [ApiDocsTags.PkiSubscribers], tags: [ApiDocsTags.PkiSubscribers],
description: "Issue certificate", description: "Order certificate",
params: z.object({ params: z.object({
subscriberName: z.string().describe(PKI_SUBSCRIBERS.ISSUE_CERT.subscriberName) subscriberName: z.string().describe(PKI_SUBSCRIBERS.ISSUE_CERT.subscriberName)
}), }),
@@ -297,16 +297,12 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
}), }),
response: { response: {
200: z.object({ 200: z.object({
certificate: z.string().trim().describe(PKI_SUBSCRIBERS.ISSUE_CERT.certificate), message: z.string().trim()
issuingCaCertificate: z.string().trim().describe(PKI_SUBSCRIBERS.ISSUE_CERT.issuingCaCertificate),
certificateChain: z.string().trim().describe(PKI_SUBSCRIBERS.ISSUE_CERT.certificateChain),
privateKey: z.string().trim().describe(PKI_SUBSCRIBERS.ISSUE_CERT.privateKey),
serialNumber: z.string().trim().describe(PKI_SUBSCRIBERS.ISSUE_CERT.serialNumber)
}) })
} }
}, },
handler: async (req) => { handler: async (req) => {
await server.services.pkiSubscriber.issueSubscriberCert({ const subscriber = await server.services.pkiSubscriber.orderSubscriberCert({
subscriberName: req.params.subscriberName, subscriberName: req.params.subscriberName,
projectId: req.body.projectId, projectId: req.body.projectId,
actor: req.permission.type, actor: req.permission.type,
@@ -315,35 +311,31 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId actorOrgId: req.permission.orgId
}); });
await server.services.auditLog.createAuditLog({ // await server.services.auditLog.createAuditLog({
...req.auditLogInfo, // ...req.auditLogInfo,
projectId: subscriber.projectId, // projectId: subscriber.projectId,
event: { // event: {
type: EventType.ISSUE_PKI_SUBSCRIBER_CERT, // type: EventType.ISSUE_PKI_SUBSCRIBER_CERT,
metadata: { // metadata: {
subscriberId: subscriber.id, // subscriberId: subscriber.id,
name: subscriber.name, // name: subscriber.name,
serialNumber // serialNumber
} // }
} // }
}); // });
await server.services.telemetry.sendPostHogEvents({ // await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.IssueCert, // event: PostHogEventTypes.IssueCert,
distinctId: getTelemetryDistinctId(req), // distinctId: getTelemetryDistinctId(req),
properties: { // properties: {
subscriberId: subscriber.id, // subscriberId: subscriber.id,
commonName: subscriber.commonName, // commonName: subscriber.commonName,
...req.auditLogInfo // ...req.auditLogInfo
} // }
}); // });
return { return {
certificate, message: "Successfully placed order for certificate"
certificateChain,
issuingCaCertificate,
privateKey,
serialNumber
}; };
} }
}); });
@@ -3,11 +3,12 @@ import * as x509 from "@peculiar/x509";
import acme from "acme-client"; import acme from "acme-client";
import { KeyObject } from "crypto"; import { KeyObject } from "crypto";
import { TableName, TPkiSubscribers } from "@app/db/schemas"; import { TableName } from "@app/db/schemas";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types"; import { OrgServiceActor } from "@app/lib/types";
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal"; import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
import { AppConnection, AWSRegion } from "@app/services/app-connection/app-connection-enums"; import { AppConnection, AWSRegion } from "@app/services/app-connection/app-connection-enums";
import { decryptAppConnection } from "@app/services/app-connection/app-connection-fns";
import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service"; import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service";
import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns"; import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns";
import { TAwsConnection, TAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-types"; import { TAwsConnection, TAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-types";
@@ -21,6 +22,7 @@ import {
CertStatus CertStatus
} from "@app/services/certificate/certificate-types"; } from "@app/services/certificate/certificate-types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns"; import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
@@ -30,6 +32,7 @@ import { keyAlgorithmToAlgCfg } from "../certificate-authority-fns";
import { TCertificateAuthority } from "../certificate-authority-types"; import { TCertificateAuthority } from "../certificate-authority-types";
import { TExternalCertificateAuthorityDALFactory } from "../external-certificate-authority-dal"; import { TExternalCertificateAuthorityDALFactory } from "../external-certificate-authority-dal";
import { AcmeDnsProvider } from "./acme-certificate-authority-enums"; import { AcmeDnsProvider } from "./acme-certificate-authority-enums";
import { AcmeCertificateAuthorityCredentialsSchema } from "./acme-certificate-authority-schemas";
import { import {
TAcmeCertificateAuthority, TAcmeCertificateAuthority,
TCreateAcmeCertificateAuthorityDTO, TCreateAcmeCertificateAuthorityDTO,
@@ -47,7 +50,11 @@ type TAcmeCertificateAuthorityFnsDeps = {
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">; certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">; certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">; certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
kmsService: Pick<TKmsServiceFactory, "encryptWithKmsKey" | "generateKmsKey">; kmsService: Pick<
TKmsServiceFactory,
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
>;
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
projectDAL: Pick<TProjectDALFactory, "findById" | "findOne" | "updateById" | "transaction">; projectDAL: Pick<TProjectDALFactory, "findById" | "findOne" | "updateById" | "transaction">;
}; };
@@ -59,7 +66,7 @@ type DBConfigurationColumn = {
export const castDbEntryToAcmeCertificateAuthority = ( export const castDbEntryToAcmeCertificateAuthority = (
ca: Awaited<ReturnType<TCertificateAuthorityDALFactory["findByIdWithAssociatedCa"]>> ca: Awaited<ReturnType<TCertificateAuthorityDALFactory["findByIdWithAssociatedCa"]>>
): TAcmeCertificateAuthority => { ): TAcmeCertificateAuthority & { credentials: unknown } => {
if (!ca.externalCa) { if (!ca.externalCa) {
throw new BadRequestError({ message: "Malformed ACME certificate authority" }); throw new BadRequestError({ message: "Malformed ACME certificate authority" });
} }
@@ -72,6 +79,7 @@ export const castDbEntryToAcmeCertificateAuthority = (
disableDirectIssuance: ca.disableDirectIssuance, disableDirectIssuance: ca.disableDirectIssuance,
name: ca.externalCa.name, name: ca.externalCa.name,
projectId: ca.projectId, projectId: ca.projectId,
credentials: ca.externalCa.credentials,
configuration: { configuration: {
dnsAppConnectionId: ca.externalCa.dnsAppConnectionId as string, dnsAppConnectionId: ca.externalCa.dnsAppConnectionId as string,
dnsProvider: dbConfigurationCol.dnsProvider as AcmeDnsProvider, dnsProvider: dbConfigurationCol.dnsProvider as AcmeDnsProvider,
@@ -147,7 +155,8 @@ export const AcmeCertificateAuthorityFns = ({
certificateBodyDAL, certificateBodyDAL,
certificateSecretDAL, certificateSecretDAL,
kmsService, kmsService,
projectDAL projectDAL,
pkiSubscriberDAL
}: TAcmeCertificateAuthorityFnsDeps) => { }: TAcmeCertificateAuthorityFnsDeps) => {
const createCertificateAuthority = async ({ const createCertificateAuthority = async ({
name, name,
@@ -320,20 +329,66 @@ export const AcmeCertificateAuthorityFns = ({
return cas.map(castDbEntryToAcmeCertificateAuthority); return cas.map(castDbEntryToAcmeCertificateAuthority);
}; };
// SHEEN TODO: need to execute this from a job const orderCertificate = async (subscriberId: string) => {
const orderCertificate = async ( const subscriber = await pkiSubscriberDAL.findById(subscriberId);
subscriber: TPkiSubscribers, if (!subscriber.caId) {
ca: Awaited<ReturnType<TCertificateAuthorityDALFactory["findByIdWithAssociatedCa"]>>, throw new BadRequestError({ message: "Subscriber does not have a CA" });
actor: OrgServiceActor }
) => {
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(subscriber.caId);
if (!ca.externalCa || ca.externalCa.type !== CaType.ACME) {
throw new BadRequestError({ message: "CA is not an ACME CA" });
}
const acmeCa = castDbEntryToAcmeCertificateAuthority(ca); const acmeCa = castDbEntryToAcmeCertificateAuthority(ca);
// SHEEN TODO: need to save this in credentials field and reuse const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
const privateRsaKey = await acme.crypto.createPrivateRsaKey(); projectId: ca.projectId,
projectDAL,
kmsService
});
const kmsEncryptor = await kmsService.encryptWithKmsKey({
kmsId: certificateManagerKmsId
});
const kmsDecryptor = await kmsService.decryptWithKmsKey({
kmsId: certificateManagerKmsId
});
let accountKey: Buffer | undefined;
if (acmeCa.credentials) {
const decryptedCredentials = await kmsDecryptor({
cipherTextBlob: acmeCa.credentials as Buffer
});
const parsedCredentials = await AcmeCertificateAuthorityCredentialsSchema.parseAsync(
JSON.parse(decryptedCredentials.toString("utf8"))
);
accountKey = Buffer.from(parsedCredentials.accountKey, "base64");
}
if (!accountKey) {
accountKey = await acme.crypto.createPrivateRsaKey();
const newCredentials = {
accountKey: accountKey.toString("base64")
};
const { cipherTextBlob: encryptedNewCredentials } = await kmsEncryptor({
plainText: Buffer.from(JSON.stringify(newCredentials))
});
await externalCertificateAuthorityDAL.update(
{
certificateAuthorityId: acmeCa.id
},
{
credentials: encryptedNewCredentials
}
);
}
const acmeClient = new acme.Client({ const acmeClient = new acme.Client({
directoryUrl: acmeCa.configuration.directoryUrl, directoryUrl: acmeCa.configuration.directoryUrl,
accountKey: privateRsaKey accountKey
}); });
const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048); const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048);
@@ -349,13 +404,8 @@ export const AcmeCertificateAuthorityFns = ({
skLeaf skLeaf
); );
// SHEEN TODO: need to update this to remove dependence on ACTOR
const appConnection = await appConnectionDAL.findById(acmeCa.configuration.dnsAppConnectionId); const appConnection = await appConnectionDAL.findById(acmeCa.configuration.dnsAppConnectionId);
const connection = await appConnectionService.connectAppConnectionById( const connection = await decryptAppConnection(appConnection, kmsService);
appConnection.app as AppConnection,
acmeCa.configuration.dnsAppConnectionId,
actor
);
const pem = await acmeClient.auto({ const pem = await acmeClient.auto({
csr: certificateCsr, csr: certificateCsr,
@@ -385,20 +435,9 @@ export const AcmeCertificateAuthorityFns = ({
} }
}); });
console.log("PEM IS", pem);
const [leafCert, parentCert] = acme.crypto.splitPemChain(pem); const [leafCert, parentCert] = acme.crypto.splitPemChain(pem);
const certObj = new x509.X509Certificate(leafCert); const certObj = new x509.X509Certificate(leafCert);
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
projectId: ca.projectId,
projectDAL,
kmsService
});
const kmsEncryptor = await kmsService.encryptWithKmsKey({
kmsId: certificateManagerKmsId
});
const { cipherTextBlob: encryptedCertificate } = await kmsEncryptor({ const { cipherTextBlob: encryptedCertificate } = await kmsEncryptor({
plainText: Buffer.from(new Uint8Array(certObj.rawData)) plainText: Buffer.from(new Uint8Array(certObj.rawData))
}); });
@@ -15,6 +15,10 @@ export const AcmeCertificateAuthorityConfigurationSchema = z.object({
accountEmail: z.string().trim() accountEmail: z.string().trim()
}); });
export const AcmeCertificateAuthorityCredentialsSchema = z.object({
accountKey: z.string()
});
export const AcmeCertificateAuthoritySchema = BaseCertificateAuthoritySchema(CaType.ACME).extend({ export const AcmeCertificateAuthoritySchema = BaseCertificateAuthoritySchema(CaType.ACME).extend({
type: z.literal(CaType.ACME), type: z.literal(CaType.ACME),
configuration: AcmeCertificateAuthorityConfigurationSchema configuration: AcmeCertificateAuthorityConfigurationSchema
@@ -58,6 +58,7 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
db.ref("type").withSchema(TableName.ExternalCertificateAuthority).as("externalType"), db.ref("type").withSchema(TableName.ExternalCertificateAuthority).as("externalType"),
db.ref("status").withSchema(TableName.ExternalCertificateAuthority).as("externalStatus"), db.ref("status").withSchema(TableName.ExternalCertificateAuthority).as("externalStatus"),
db.ref("configuration").withSchema(TableName.ExternalCertificateAuthority).as("externalConfiguration"), db.ref("configuration").withSchema(TableName.ExternalCertificateAuthority).as("externalConfiguration"),
db.ref("credentials").withSchema(TableName.ExternalCertificateAuthority).as("externalCredentials"),
db db
.ref("dnsAppConnectionId") .ref("dnsAppConnectionId")
.withSchema(TableName.ExternalCertificateAuthority) .withSchema(TableName.ExternalCertificateAuthority)
@@ -99,7 +100,8 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
status: result.externalStatus, status: result.externalStatus,
configuration: result.externalConfiguration, configuration: result.externalConfiguration,
dnsAppConnectionId: result.externalDnsAppConnectionId, dnsAppConnectionId: result.externalDnsAppConnectionId,
appConnectionId: result.externalAppConnectionId appConnectionId: result.externalAppConnectionId,
credentials: result.externalCredentials
} }
: undefined : undefined
}; };
@@ -195,6 +197,7 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
.ref("dnsAppConnectionId") .ref("dnsAppConnectionId")
.withSchema(TableName.ExternalCertificateAuthority) .withSchema(TableName.ExternalCertificateAuthority)
.as("externalDnsAppConnectionId"), .as("externalDnsAppConnectionId"),
db.ref("credentials").withSchema(TableName.ExternalCertificateAuthority).as("externalCredentials"),
db.ref("appConnectionId").withSchema(TableName.ExternalCertificateAuthority).as("externalAppConnectionId") db.ref("appConnectionId").withSchema(TableName.ExternalCertificateAuthority).as("externalAppConnectionId")
); );
@@ -243,7 +246,8 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
status: ca.externalStatus, status: ca.externalStatus,
configuration: ca.externalConfiguration, configuration: ca.externalConfiguration,
dnsAppConnectionId: ca.externalDnsAppConnectionId, dnsAppConnectionId: ca.externalDnsAppConnectionId,
appConnectionId: ca.externalAppConnectionId appConnectionId: ca.externalAppConnectionId,
credentials: ca.externalCredentials
} }
: undefined : undefined
})); }));
@@ -1,6 +1,7 @@
import * as x509 from "@peculiar/x509"; import * as x509 from "@peculiar/x509";
import crypto from "crypto"; import crypto from "crypto";
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates"; import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
import { NotFoundError } from "@app/lib/errors"; import { NotFoundError } from "@app/lib/errors";
@@ -13,21 +14,42 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns"; import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal"; import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
import { TAppConnectionServiceFactory } from "../app-connection/app-connection-service";
import { TCertificateBodyDALFactory } from "../certificate/certificate-body-dal";
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
import { TPkiSubscriberDALFactory } from "../pki-subscriber/pki-subscriber-dal";
import { AcmeCertificateAuthorityFns } from "./acme/acme-certificate-authority-fns";
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal"; import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
import { CaType } from "./certificate-authority-enums";
import { keyAlgorithmToAlgCfg } from "./certificate-authority-fns"; import { keyAlgorithmToAlgCfg } from "./certificate-authority-fns";
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal"; import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
import { TRotateCaCrlTriggerDTO } from "./internal/internal-certificate-authority-types"; import { TExternalCertificateAuthorityDALFactory } from "./external-certificate-authority-dal";
import {
TOrderCertificateForSubscriberDTO,
TRotateCaCrlTriggerDTO
} from "./internal/internal-certificate-authority-types";
type TCertificateAuthorityQueueFactoryDep = { type TCertificateAuthorityQueueFactoryDep = {
// TODO: Pick
certificateAuthorityDAL: TCertificateAuthorityDALFactory; certificateAuthorityDAL: TCertificateAuthorityDALFactory;
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update">;
appConnectionService: Pick<TAppConnectionServiceFactory, "connectAppConnectionById">;
externalCertificateAuthorityDAL: Pick<TExternalCertificateAuthorityDALFactory, "create" | "update">;
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "setItemWithExpiry" | "getItem">;
certificateAuthorityCrlDAL: TCertificateAuthorityCrlDALFactory; certificateAuthorityCrlDAL: TCertificateAuthorityCrlDALFactory;
certificateAuthoritySecretDAL: TCertificateAuthoritySecretDALFactory; certificateAuthoritySecretDAL: TCertificateAuthoritySecretDALFactory;
certificateDAL: TCertificateDALFactory; certificateDAL: TCertificateDALFactory;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">; projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey">; kmsService: Pick<
TKmsServiceFactory,
"generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey" | "createCipherPairWithDataKey"
>;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
queueService: TQueueServiceFactory; queueService: TQueueServiceFactory;
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
}; };
export type TCertificateAuthorityQueueFactory = ReturnType<typeof certificateAuthorityQueueFactory>; export type TCertificateAuthorityQueueFactory = ReturnType<typeof certificateAuthorityQueueFactory>;
export const certificateAuthorityQueueFactory = ({ export const certificateAuthorityQueueFactory = ({
@@ -37,8 +59,28 @@ export const certificateAuthorityQueueFactory = ({
certificateDAL, certificateDAL,
projectDAL, projectDAL,
kmsService, kmsService,
queueService queueService,
keyStore,
appConnectionDAL,
appConnectionService,
externalCertificateAuthorityDAL,
certificateBodyDAL,
certificateSecretDAL,
pkiSubscriberDAL
}: TCertificateAuthorityQueueFactoryDep) => { }: TCertificateAuthorityQueueFactoryDep) => {
const acmeFns = AcmeCertificateAuthorityFns({
appConnectionDAL,
appConnectionService,
certificateAuthorityDAL,
externalCertificateAuthorityDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
kmsService,
pkiSubscriberDAL,
projectDAL
});
// TODO 1: auto-periodic rotation // TODO 1: auto-periodic rotation
// TODO 2: manual rotation // TODO 2: manual rotation
@@ -71,6 +113,51 @@ export const certificateAuthorityQueueFactory = ({
); );
}; };
const orderCertificateForSubscriber = async ({ subscriberId, caType }: TOrderCertificateForSubscriberDTO) => {
await queueService.queue(
QueueName.CaLifecycle,
QueueJobs.CaOrderCertificateForSubscriber,
{
subscriberId,
caType
},
{
attempts: 1,
removeOnComplete: true,
removeOnFail: true
}
);
};
queueService.start(QueueName.CaLifecycle, async (job) => {
if (job.name === QueueJobs.CaOrderCertificateForSubscriber) {
const { subscriberId, caType } = job.data;
let lock: Awaited<ReturnType<typeof keyStore.acquireLock>>;
try {
lock = await keyStore.acquireLock(
[KeyStorePrefixes.CaOrderCertificateForSubscriberLock(subscriberId)],
// scott: not sure on this duration; syncs can take excessive amounts of time so we need to keep it locked,
// but should always release below...
5 * 60 * 1000
);
} catch (e) {
logger.info(`CaOrderCertificate Failed to acquire lock [subscriberId=${subscriberId}] [job=${job.name}]`);
return;
}
try {
if (caType === CaType.ACME) {
await acmeFns.orderCertificate(subscriberId);
}
} catch (e) {
logger.error(e, `CaOrderCertificate Failed [subscriberId=${subscriberId}] [job=${job.name}]`);
} finally {
await lock.release();
}
}
});
queueService.start(QueueName.CaCrlRotation, async (job) => { queueService.start(QueueName.CaCrlRotation, async (job) => {
const { caId } = job.data; const { caId } = job.data;
logger.info(`secretReminderQueue.process: [secretDocument=${caId}]`); logger.info(`secretReminderQueue.process: [secretDocument=${caId}]`);
@@ -144,6 +231,7 @@ export const certificateAuthorityQueueFactory = ({
}); });
return { return {
setCaCrlRotationInterval setCaCrlRotationInterval,
orderCertificateForSubscriber
}; };
}; };
@@ -8,6 +8,11 @@ import { OrgServiceActor } from "@app/lib/types";
import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal"; import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
import { TAppConnectionServiceFactory } from "../app-connection/app-connection-service"; import { TAppConnectionServiceFactory } from "../app-connection/app-connection-service";
import { TCertificateBodyDALFactory } from "../certificate/certificate-body-dal";
import { TCertificateDALFactory } from "../certificate/certificate-dal";
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TPkiSubscriberDALFactory } from "../pki-subscriber/pki-subscriber-dal";
import { TProjectDALFactory } from "../project/project-dal"; import { TProjectDALFactory } from "../project/project-dal";
import { AcmeCertificateAuthorityFns } from "./acme/acme-certificate-authority-fns"; import { AcmeCertificateAuthorityFns } from "./acme/acme-certificate-authority-fns";
import { import {
@@ -46,6 +51,14 @@ type TCertificateAuthorityServiceFactoryDep = {
"findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId" "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId"
>; >;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
kmsService: Pick<
TKmsServiceFactory,
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
>;
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
}; };
export type TCertificateAuthorityServiceFactory = ReturnType<typeof certificateAuthorityServiceFactory>; export type TCertificateAuthorityServiceFactory = ReturnType<typeof certificateAuthorityServiceFactory>;
@@ -57,13 +70,24 @@ export const certificateAuthorityServiceFactory = ({
internalCertificateAuthorityService, internalCertificateAuthorityService,
appConnectionDAL, appConnectionDAL,
appConnectionService, appConnectionService,
externalCertificateAuthorityDAL externalCertificateAuthorityDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
kmsService,
pkiSubscriberDAL
}: TCertificateAuthorityServiceFactoryDep) => { }: TCertificateAuthorityServiceFactoryDep) => {
const acmeFns = AcmeCertificateAuthorityFns({ const acmeFns = AcmeCertificateAuthorityFns({
appConnectionDAL, appConnectionDAL,
appConnectionService, appConnectionService,
certificateAuthorityDAL, certificateAuthorityDAL,
externalCertificateAuthorityDAL externalCertificateAuthorityDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
kmsService,
pkiSubscriberDAL,
projectDAL
}); });
const createCertificateAuthority = async ( const createCertificateAuthority = async (
@@ -9,7 +9,7 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TCertificateAuthorityCertDALFactory } from "../certificate-authority-cert-dal"; import { TCertificateAuthorityCertDALFactory } from "../certificate-authority-cert-dal";
import { TCertificateAuthorityDALFactory } from "../certificate-authority-dal"; import { TCertificateAuthorityDALFactory } from "../certificate-authority-dal";
import { CaRenewalType, CaStatus, InternalCaType } from "../certificate-authority-enums"; import { CaRenewalType, CaStatus, CaType, InternalCaType } from "../certificate-authority-enums";
import { TCertificateAuthoritySecretDALFactory } from "../certificate-authority-secret-dal"; import { TCertificateAuthoritySecretDALFactory } from "../certificate-authority-secret-dal";
import { import {
CreateInternalCertificateAuthoritySchema, CreateInternalCertificateAuthoritySchema,
@@ -212,3 +212,8 @@ export type TRotateCaCrlTriggerDTO = {
caId: string; caId: string;
rotationIntervalDays: number; rotationIntervalDays: number;
}; };
export type TOrderCertificateForSubscriberDTO = {
subscriberId: string;
caType: CaType;
};
@@ -25,6 +25,7 @@ export const pkiAlertDALFactory = (db: TDbClient) => {
recipientEmails: string; recipientEmails: string;
}; };
// SHEEN TODO: FIX REGRESION HERE
// gets CAs and certificates as part of PKI collection items // gets CAs and certificates as part of PKI collection items
const combinedQuery = db const combinedQuery = db
.replicaNode() .replicaNode()
@@ -1,3 +1,5 @@
import { z } from "zod";
import { PkiSubscribersSchema } from "@app/db/schemas"; import { PkiSubscribersSchema } from "@app/db/schemas";
export const sanitizedPkiSubscriber = PkiSubscribersSchema.pick({ export const sanitizedPkiSubscriber = PkiSubscribersSchema.pick({
@@ -11,4 +13,6 @@ export const sanitizedPkiSubscriber = PkiSubscribersSchema.pick({
ttl: true, ttl: true,
keyUsages: true, keyUsages: true,
extendedKeyUsages: true extendedKeyUsages: true
}).extend({
supportsImmediateCertIssuance: z.boolean().optional()
}); });
@@ -38,11 +38,8 @@ import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subsc
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns"; import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
import { TAppConnectionServiceFactory } from "../app-connection/app-connection-service";
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal"; import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
import { AcmeCertificateAuthorityFns } from "../certificate-authority/acme/acme-certificate-authority-fns"; import { TCertificateAuthorityQueueFactory } from "../certificate-authority/certificate-authority-queue";
import { TExternalCertificateAuthorityDALFactory } from "../certificate-authority/external-certificate-authority-dal";
import { InternalCertificateAuthorityFns } from "../certificate-authority/internal/internal-certificate-authority-fns"; import { InternalCertificateAuthorityFns } from "../certificate-authority/internal/internal-certificate-authority-fns";
import { import {
PkiSubscriberStatus, PkiSubscriberStatus,
@@ -57,9 +54,6 @@ import {
} from "./pki-subscriber-types"; } from "./pki-subscriber-types";
type TPkiSubscriberServiceFactoryDep = { type TPkiSubscriberServiceFactoryDep = {
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById">;
appConnectionService: Pick<TAppConnectionServiceFactory, "connectAppConnectionById">;
externalCertificateAuthorityDAL: Pick<TExternalCertificateAuthorityDALFactory, "create" | "update">;
pkiSubscriberDAL: Pick< pkiSubscriberDAL: Pick<
TPkiSubscriberDALFactory, TPkiSubscriberDALFactory,
"create" | "findById" | "updateById" | "deleteById" | "transaction" | "find" | "findOne" "create" | "findById" | "updateById" | "deleteById" | "transaction" | "find" | "findOne"
@@ -70,6 +64,7 @@ type TPkiSubscriberServiceFactoryDep = {
>; >;
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">; certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">; certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
certificateAuthorityQueue: Pick<TCertificateAuthorityQueueFactory, "orderCertificateForSubscriber">;
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "findOne">; certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "findOne">;
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction" | "countCertificatesForPkiSubscriber" | "find">; certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction" | "countCertificatesForPkiSubscriber" | "find">;
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">; certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
@@ -93,9 +88,7 @@ export const pkiSubscriberServiceFactory = ({
projectDAL, projectDAL,
kmsService, kmsService,
permissionService, permissionService,
appConnectionDAL, certificateAuthorityQueue
appConnectionService,
externalCertificateAuthorityDAL
}: TPkiSubscriberServiceFactoryDep) => { }: TPkiSubscriberServiceFactoryDep) => {
const internalCaFns = InternalCertificateAuthorityFns({ const internalCaFns = InternalCertificateAuthorityFns({
certificateAuthorityDAL, certificateAuthorityDAL,
@@ -109,18 +102,6 @@ export const pkiSubscriberServiceFactory = ({
kmsService kmsService
}); });
const acmeCaFns = AcmeCertificateAuthorityFns({
appConnectionDAL,
appConnectionService,
certificateAuthorityDAL,
externalCertificateAuthorityDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
kmsService,
projectDAL
});
const createSubscriber = async ({ const createSubscriber = async ({
name, name,
commonName, commonName,
@@ -198,7 +179,18 @@ export const pkiSubscriberServiceFactory = ({
}) })
); );
return subscriber; let supportsImmediateCertIssuance = false;
if (subscriber.caId) {
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(subscriber.caId);
if (ca.internalCa?.id) {
supportsImmediateCertIssuance = true;
}
}
return {
...subscriber,
supportsImmediateCertIssuance
};
}; };
const updateSubscriber = async ({ const updateSubscriber = async ({
@@ -325,16 +317,16 @@ export const pkiSubscriberServiceFactory = ({
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(subscriber.caId); const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(subscriber.caId);
if (ca.internalCa?.id) { if (ca.internalCa?.id) {
throw new BadRequestError({ message: "CA does not support ordering certificates" }); throw new BadRequestError({ message: "CA does not support ordering of certificates" });
} }
if (ca.externalCa?.id && ca.externalCa.type === CaType.ACME) { if (ca.externalCa?.id && ca.externalCa.type === CaType.ACME) {
return acmeCaFns.orderCertificate(subscriber, ca, { await certificateAuthorityQueue.orderCertificateForSubscriber({
type: actor, subscriberId: subscriber.id,
id: actorId, caType: ca.externalCa.type
authMethod: actorAuthMethod,
orgId: actorOrgId
}); });
return subscriber;
} }
throw new BadRequestError({ message: "Unsupported CA type" }); throw new BadRequestError({ message: "Unsupported CA type" });
@@ -380,15 +372,6 @@ export const pkiSubscriberServiceFactory = ({
return internalCaFns.issueCertificate(subscriber, ca); return internalCaFns.issueCertificate(subscriber, ca);
} }
if (ca.externalCa?.id && ca.externalCa.type === CaType.ACME) {
return acmeCaFns.orderCertificate(subscriber, ca, {
type: actor,
id: actorId,
authMethod: actorAuthMethod,
orgId: actorOrgId
});
}
throw new BadRequestError({ message: "CA does not support immediate issuance of certificates" }); throw new BadRequestError({ message: "CA does not support immediate issuance of certificates" });
}; };
@@ -2,6 +2,7 @@ export {
useCreatePkiSubscriber, useCreatePkiSubscriber,
useDeletePkiSubscriber, useDeletePkiSubscriber,
useIssuePkiSubscriberCert, useIssuePkiSubscriberCert,
useOrderPkiSubscriberCert,
useUpdatePkiSubscriber useUpdatePkiSubscriber
} from "./mutations"; } from "./mutations";
export { useGetPkiSubscriber, useGetPkiSubscriberCertificates } from "./queries"; export { useGetPkiSubscriber, useGetPkiSubscriberCertificates } from "./queries";
@@ -108,3 +108,17 @@ export const useIssuePkiSubscriberCert = () => {
} }
}); });
}; };
export const useOrderPkiSubscriberCert = () => {
return useMutation<{ message: string }, object, TIssuePkiSubscriberCertDTO>({
mutationFn: async ({ subscriberName, projectId }) => {
const { data } = await apiRequest.post(
`/api/v1/pki/subscribers/${subscriberName}/order-certificate`,
{
projectId
}
);
return data;
}
});
};
@@ -16,6 +16,7 @@ export type TPkiSubscriber = {
subjectAlternativeNames: string[]; subjectAlternativeNames: string[];
keyUsages: CertKeyUsage[]; keyUsages: CertKeyUsage[];
extendedKeyUsages: CertExtendedKeyUsage[]; extendedKeyUsages: CertExtendedKeyUsage[];
supportsImmediateCertIssuance?: boolean;
}; };
export type TCreatePkiSubscriberDTO = { export type TCreatePkiSubscriberDTO = {
@@ -51,3 +52,8 @@ export type TIssuePkiSubscriberCertDTO = {
subscriberName: string; subscriberName: string;
projectId: string; projectId: string;
}; };
export type TOrderPkiSubscriberCertDTO = {
subscriberName: string;
projectId: string;
};
@@ -13,7 +13,11 @@ import {
useWorkspace useWorkspace
} from "@app/context"; } from "@app/context";
import { useTimedReset } from "@app/hooks"; import { useTimedReset } from "@app/hooks";
import { useGetPkiSubscriber, useIssuePkiSubscriberCert } from "@app/hooks/api"; import {
useGetPkiSubscriber,
useIssuePkiSubscriberCert,
useOrderPkiSubscriberCert
} from "@app/hooks/api";
import { pkiSubscriberStatusToNameMap } from "@app/hooks/api/pkiSubscriber/constants"; import { pkiSubscriberStatusToNameMap } from "@app/hooks/api/pkiSubscriber/constants";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -49,23 +53,34 @@ export const PkiSubscriberDetailsSection = ({ subscriberName, handlePopUpOpen }:
const { mutateAsync: issuePkiSubscriberCert, isPending: isIssuingCert } = const { mutateAsync: issuePkiSubscriberCert, isPending: isIssuingCert } =
useIssuePkiSubscriberCert(); useIssuePkiSubscriberCert();
const { mutateAsync: orderPkiSubscriberCert } = useOrderPkiSubscriberCert();
const onIssuePkiSubscriberCert = async () => { const onIssuePkiSubscriberCert = async () => {
try { try {
const response = await issuePkiSubscriberCert({ subscriberName, projectId }); if (pkiSubscriber?.supportsImmediateCertIssuance) {
const response = await issuePkiSubscriberCert({ subscriberName, projectId });
setCertificateDetails({ setCertificateDetails({
serialNumber: response.serialNumber, serialNumber: response.serialNumber,
certificate: response.certificate, certificate: response.certificate,
certificateChain: response.certificateChain, certificateChain: response.certificateChain,
privateKey: response.privateKey privateKey: response.privateKey
}); });
setIsModalOpen(true); setIsModalOpen(true);
createNotification({ createNotification({
text: "Successfully issued certificate", text: "Successfully issued certificate",
type: "success" type: "success"
}); });
} else {
await orderPkiSubscriberCert({ subscriberName, projectId });
createNotification({
text: "Successfully ordered certificate. It will be issued after CA processing.",
type: "success"
});
}
} catch (err) { } catch (err) {
console.error(err); console.error(err);
createNotification({ createNotification({
@@ -158,7 +173,9 @@ export const PkiSubscriberDetailsSection = ({ subscriberName, handlePopUpOpen }:
onIssuePkiSubscriberCert(); onIssuePkiSubscriberCert();
}} }}
> >
Issue Certificate {pkiSubscriber?.supportsImmediateCertIssuance
? "Issue Certificate"
: "Order Certificate"}
</Button> </Button>
)} )}
</div> </div>