misc: addressed first set of review comments

This commit is contained in:
Sheen Capadngan
2025-05-22 00:22:49 +08:00
parent 77b42836e7
commit 77de085ffc
32 changed files with 339 additions and 252 deletions
@@ -24,6 +24,7 @@ export async function up(knex: Knex): Promise<void> {
t.dropColumn("requireTemplateForIssuance"); t.dropColumn("requireTemplateForIssuance");
t.dropColumn("createdAt"); t.dropColumn("createdAt");
t.dropColumn("updatedAt"); t.dropColumn("updatedAt");
t.dropColumn("status");
t.uuid("parentCaId") t.uuid("parentCaId")
.nullable() .nullable()
.references("id") .references("id")
@@ -44,22 +45,18 @@ export async function up(knex: Knex): Promise<void> {
await Promise.all( await Promise.all(
cas.map((ca) => { cas.map((ca) => {
const slugifiedName = ca.friendlyName const slugifiedName = ca.friendlyName
? slugify(`${ca.friendlyName}-${alphaNumericNanoId(8)}`) ? slugify(`${ca.friendlyName.slice(0, 16)}-${alphaNumericNanoId(8)}`)
: slugify(alphaNumericNanoId(12)); : slugify(alphaNumericNanoId(12));
return ( return knex(TableName.CertificateAuthority)
knex(TableName.CertificateAuthority) .where({ id: ca.id })
.where({ id: ca.id }) .update({ name: slugifiedName, enableDirectIssuance: !ca.enableDirectIssuance });
// @ts-expect-error intentional: migration
.update({ name: slugifiedName, enableDirectIssuance: !ca.enableDirectIssuance })
);
}) })
); );
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => { await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.dropColumn("parentCaId"); t.dropColumn("parentCaId");
t.dropColumn("type"); t.dropColumn("type");
t.dropColumn("status");
t.dropColumn("friendlyName"); t.dropColumn("friendlyName");
t.dropColumn("organization"); t.dropColumn("organization");
t.dropColumn("ou"); t.dropColumn("ou");
@@ -74,6 +71,7 @@ export async function up(knex: Knex): Promise<void> {
t.dropColumn("notBefore"); t.dropColumn("notBefore");
t.dropColumn("notAfter"); t.dropColumn("notAfter");
t.dropColumn("activeCaCertId"); t.dropColumn("activeCaCertId");
t.boolean("enableDirectIssuance").notNullable().defaultTo(true).alter();
t.string("name").notNullable().alter(); t.string("name").notNullable().alter();
t.unique(["name", "projectId"]); t.unique(["name", "projectId"]);
}); });
@@ -90,7 +88,6 @@ export async function up(knex: Knex): Promise<void> {
t.uuid("caId").notNullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE"); t.uuid("caId").notNullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
t.binary("credentials"); t.binary("credentials");
t.json("configuration"); t.json("configuration");
t.string("status").notNullable();
}); });
} }
@@ -114,7 +111,6 @@ export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => { await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.uuid("parentCaId").nullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE"); t.uuid("parentCaId").nullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
t.string("type").nullable(); t.string("type").nullable();
t.string("status").nullable();
t.string("friendlyName").nullable(); t.string("friendlyName").nullable();
t.string("organization").nullable(); t.string("organization").nullable();
t.string("ou").nullable(); t.string("ou").nullable();
@@ -150,7 +146,6 @@ export async function down(knex: Knex): Promise<void> {
UPDATE ${TableName.CertificateAuthority} ca UPDATE ${TableName.CertificateAuthority} ca
SET SET
type = ica.type, type = ica.type,
status = ica.status,
"friendlyName" = ica."friendlyName", "friendlyName" = ica."friendlyName",
organization = ica.organization, organization = ica.organization,
ou = ica.ou, ou = ica.ou,
@@ -172,7 +167,6 @@ export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => { await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.string("type").notNullable().alter(); t.string("type").notNullable().alter();
t.string("status").notNullable().alter();
t.string("friendlyName").notNullable().alter(); t.string("friendlyName").notNullable().alter();
t.string("organization").notNullable().alter(); t.string("organization").notNullable().alter();
t.string("ou").notNullable().alter(); t.string("ou").notNullable().alter();
@@ -182,6 +176,7 @@ export async function down(knex: Knex): Promise<void> {
t.string("commonName").notNullable().alter(); t.string("commonName").notNullable().alter();
t.string("dn").notNullable().alter(); t.string("dn").notNullable().alter();
t.string("keyAlgorithm").notNullable().alter(); t.string("keyAlgorithm").notNullable().alter();
t.boolean("requireTemplateForIssuance").notNullable().defaultTo(false).alter();
}); });
await knex.schema.dropTable(TableName.InternalCertificateAuthority); await knex.schema.dropTable(TableName.InternalCertificateAuthority);
@@ -12,7 +12,9 @@ export const CertificateAuthoritiesSchema = z.object({
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date(), updatedAt: z.date(),
projectId: z.string(), projectId: z.string(),
disableDirectIssuance: z.boolean().default(false) enableDirectIssuance: z.boolean().default(true),
status: z.string(),
name: z.string()
}); });
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>; export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;
@@ -12,14 +12,11 @@ import { TImmutableDBKeys } from "./models";
export const ExternalCertificateAuthoritiesSchema = z.object({ export const ExternalCertificateAuthoritiesSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
type: z.string(), type: z.string(),
name: z.string(),
projectId: z.string(),
appConnectionId: z.string().uuid().nullable().optional(), appConnectionId: z.string().uuid().nullable().optional(),
dnsAppConnectionId: z.string().uuid().nullable().optional(), dnsAppConnectionId: z.string().uuid().nullable().optional(),
certificateAuthorityId: z.string().uuid(), caId: z.string().uuid(),
credentials: zodBuffer.nullable().optional(), credentials: zodBuffer.nullable().optional(),
configuration: z.unknown().nullable().optional(), configuration: z.unknown().nullable().optional()
status: z.string()
}); });
export type TExternalCertificateAuthorities = z.infer<typeof ExternalCertificateAuthoritiesSchema>; export type TExternalCertificateAuthorities = z.infer<typeof ExternalCertificateAuthoritiesSchema>;
@@ -11,7 +11,6 @@ export const InternalCertificateAuthoritiesSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
parentCaId: z.string().uuid().nullable().optional(), parentCaId: z.string().uuid().nullable().optional(),
type: z.string(), type: z.string(),
status: z.string(),
friendlyName: z.string(), friendlyName: z.string(),
organization: z.string(), organization: z.string(),
ou: z.string(), ou: z.string(),
@@ -26,7 +25,7 @@ export const InternalCertificateAuthoritiesSchema = z.object({
notBefore: z.date().nullable().optional(), notBefore: z.date().nullable().optional(),
notAfter: z.date().nullable().optional(), notAfter: z.date().nullable().optional(),
activeCaCertId: z.string().uuid().nullable().optional(), activeCaCertId: z.string().uuid().nullable().optional(),
certificateAuthorityId: z.string().uuid() caId: z.string().uuid()
}); });
export type TInternalCertificateAuthorities = z.infer<typeof InternalCertificateAuthoritiesSchema>; export type TInternalCertificateAuthorities = z.infer<typeof InternalCertificateAuthoritiesSchema>;
+4 -3
View File
@@ -1793,7 +1793,7 @@ export const PKI_SUBSCRIBERS = {
subscriberName: "The name of the PKI subscriber to get.", subscriberName: "The name of the PKI subscriber to get.",
projectId: "The ID of the project to get the PKI subscriber for." projectId: "The ID of the project to get the PKI subscriber for."
}, },
GET_ACTIVE_CERT_BUNDLE: { GET_LATEST_CERT_BUNDLE: {
subscriberName: "The name of the PKI subscriber to get the active certificate bundle for.", subscriberName: "The name of the PKI subscriber to get the active certificate bundle for.",
projectId: "The ID of the project to get the active certificate bundle for.", projectId: "The ID of the project to get the active certificate bundle for.",
certificate: "The active certificate for the subscriber.", certificate: "The active certificate for the subscriber.",
@@ -2018,13 +2018,14 @@ export const CertificateAuthorities = {
CREATE: (type: CaType) => ({ CREATE: (type: CaType) => ({
name: `The name of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority to create. Must be slug-friendly.`, name: `The name of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority to create. Must be slug-friendly.`,
projectId: `The ID of the project to create the Certificate Authority in.`, projectId: `The ID of the project to create the Certificate Authority in.`,
disableDirectIssuance: `Whether or not to disable direct issuance of certificates for the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority.`, enableDirectIssuance: `Whether or not to enable direct issuance of certificates for the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority.`,
status: `The status of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority.` status: `The status of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority.`
}), }),
UPDATE: (type: CaType) => ({ UPDATE: (type: CaType) => ({
caId: `The ID of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority to update.`, caId: `The ID of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority to update.`,
projectId: `The ID of the project to update the Certificate Authority in.`,
name: `The updated name of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority. Must be slug-friendly.`, name: `The updated name of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority. Must be slug-friendly.`,
disableDirectIssuance: `Whether or not to disable direct issuance of certificates for the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority.`, enableDirectIssuance: `Whether or not to enable direct issuance of certificates for the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority.`,
status: `The updated status of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority.` status: `The updated status of the ${CERTIFICATE_AUTHORITIES_TYPE_MAP[type]} Certificate Authority.`
}), }),
CONFIGURATIONS: { CONFIGURATIONS: {
@@ -277,6 +277,7 @@ export const SanitizedTagSchema = SecretTagsSchema.pick({
export const InternalCertificateAuthorityResponseSchema = CertificateAuthoritiesSchema.merge( export const InternalCertificateAuthorityResponseSchema = CertificateAuthoritiesSchema.merge(
InternalCertificateAuthoritiesSchema.omit({ InternalCertificateAuthoritiesSchema.omit({
caId: true,
notAfter: true, notAfter: true,
notBefore: true notBefore: true
}) })
@@ -28,13 +28,14 @@ export const registerCertificateAuthorityEndpoints = <
projectId: string; projectId: string;
status: CaStatus; status: CaStatus;
configuration: I["configuration"]; configuration: I["configuration"];
disableDirectIssuance: boolean; enableDirectIssuance: boolean;
}>; }>;
updateSchema: z.ZodType<{ updateSchema: z.ZodType<{
projectId: string;
name?: string; name?: string;
status?: CaStatus; status?: CaStatus;
configuration?: I["configuration"]; configuration?: I["configuration"];
disableDirectIssuance?: boolean; enableDirectIssuance?: boolean;
}>; }>;
responseSchema: z.ZodTypeAny; responseSchema: z.ZodTypeAny;
}) => { }) => {
@@ -51,7 +52,7 @@ export const registerCertificateAuthorityEndpoints = <
projectId: z.string().trim().min(1, "Project ID required") projectId: z.string().trim().min(1, "Project ID required")
}), }),
response: { response: {
200: z.object({ certificateAuthorities: responseSchema.array() }) 200: responseSchema.array()
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
@@ -76,13 +77,13 @@ export const registerCertificateAuthorityEndpoints = <
} }
}); });
return { certificateAuthorities }; return certificateAuthorities;
} }
}); });
server.route({ server.route({
method: "GET", method: "GET",
url: "/:certificateAuthorityId", url: "/:caName",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
@@ -90,20 +91,25 @@ export const registerCertificateAuthorityEndpoints = <
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
params: z.object({ params: z.object({
certificateAuthorityId: z.string().uuid() caName: z.string()
}),
querystring: z.object({
projectId: z.string().uuid()
}), }),
response: { response: {
200: z.object({ certificateAuthority: responseSchema }) 200: responseSchema
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const { certificateAuthorityId } = req.params; const { caName } = req.params;
const { projectId } = req.query;
const certificateAuthority = (await server.services.certificateAuthority.findCertificateAuthorityById( const certificateAuthority =
{ certificateAuthorityId, type: caType }, (await server.services.certificateAuthority.findCertificateAuthorityByNameAndProjectId(
req.permission { caName, type: caType, projectId },
)) as T; req.permission
)) as T;
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
@@ -116,7 +122,7 @@ export const registerCertificateAuthorityEndpoints = <
} }
}); });
return { certificateAuthority }; return certificateAuthority;
} }
}); });
@@ -131,7 +137,7 @@ export const registerCertificateAuthorityEndpoints = <
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
body: createSchema, body: createSchema,
response: { response: {
200: z.object({ certificateAuthority: responseSchema }) 200: responseSchema
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
@@ -152,13 +158,13 @@ export const registerCertificateAuthorityEndpoints = <
} }
}); });
return { certificateAuthority }; return certificateAuthority;
} }
}); });
server.route({ server.route({
method: "PATCH", method: "PATCH",
url: "/:certificateAuthorityId", url: "/:caName",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -166,22 +172,22 @@ export const registerCertificateAuthorityEndpoints = <
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
params: z.object({ params: z.object({
certificateAuthorityId: z.string().uuid() caName: z.string()
}), }),
body: updateSchema, body: updateSchema,
response: { response: {
200: z.object({ certificateAuthority: responseSchema }) 200: responseSchema
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const { certificateAuthorityId } = req.params; const { caName } = req.params;
const certificateAuthority = (await server.services.certificateAuthority.updateCertificateAuthority( const certificateAuthority = (await server.services.certificateAuthority.updateCertificateAuthority(
{ {
...req.body, ...req.body,
id: certificateAuthorityId, type: caType,
type: caType caName
}, },
req.permission req.permission
)) as T; )) as T;
@@ -198,13 +204,13 @@ export const registerCertificateAuthorityEndpoints = <
} }
}); });
return { certificateAuthority }; return certificateAuthority;
} }
}); });
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:certificateAuthorityId", url: "/:caName",
config: { config: {
rateLimit: writeLimit rateLimit: writeLimit
}, },
@@ -212,18 +218,22 @@ export const registerCertificateAuthorityEndpoints = <
hide: false, hide: false,
tags: [ApiDocsTags.PkiCertificateAuthorities], tags: [ApiDocsTags.PkiCertificateAuthorities],
params: z.object({ params: z.object({
certificateAuthorityId: z.string().uuid() caName: z.string()
}),
body: z.object({
projectId: z.string().uuid()
}), }),
response: { response: {
200: z.object({ certificateAuthority: responseSchema }) 200: responseSchema
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const { certificateAuthorityId } = req.params; const { caName } = req.params;
const { projectId } = req.body;
const certificateAuthority = (await server.services.certificateAuthority.deleteCertificateAuthority( const certificateAuthority = (await server.services.certificateAuthority.deleteCertificateAuthority(
{ id: certificateAuthorityId, type: caType }, { caName, type: caType, projectId },
req.permission req.permission
)) as T; )) as T;
@@ -238,7 +248,7 @@ export const registerCertificateAuthorityEndpoints = <
} }
}); });
return { certificateAuthority }; return certificateAuthority;
} }
}); });
}; };
@@ -485,30 +485,30 @@ export const registerPkiSubscriberRouter = async (server: FastifyZodProvider) =>
server.route({ server.route({
method: "GET", method: "GET",
url: "/:subscriberName/active-certificate/bundle", url: "/:subscriberName/latest-certificate-bundle",
config: { config: {
rateLimit: readLimit rateLimit: readLimit
}, },
schema: { schema: {
hide: false, hide: false,
tags: [ApiDocsTags.PkiSubscribers], tags: [ApiDocsTags.PkiSubscribers],
description: "Get active certificate bundle of a subscriber", description: "Get latest certificate bundle of a subscriber",
params: z.object({ params: z.object({
subscriberName: z.string().describe(PKI_SUBSCRIBERS.GET_ACTIVE_CERT_BUNDLE.subscriberName) subscriberName: z.string().describe(PKI_SUBSCRIBERS.GET_LATEST_CERT_BUNDLE.subscriberName)
}), }),
querystring: z.object({ querystring: z.object({
projectId: z.string().trim().describe(PKI_SUBSCRIBERS.GET_ACTIVE_CERT_BUNDLE.projectId) projectId: z.string().trim().describe(PKI_SUBSCRIBERS.GET_LATEST_CERT_BUNDLE.projectId)
}), }),
response: { response: {
200: z.object({ 200: z.object({
certificate: z.string().trim().describe(PKI_SUBSCRIBERS.GET_ACTIVE_CERT_BUNDLE.certificate), certificate: z.string().trim().describe(PKI_SUBSCRIBERS.GET_LATEST_CERT_BUNDLE.certificate),
certificateChain: z certificateChain: z
.string() .string()
.trim() .trim()
.nullable() .nullable()
.describe(PKI_SUBSCRIBERS.GET_ACTIVE_CERT_BUNDLE.certificateChain), .describe(PKI_SUBSCRIBERS.GET_LATEST_CERT_BUNDLE.certificateChain),
privateKey: z.string().trim().describe(PKI_SUBSCRIBERS.GET_ACTIVE_CERT_BUNDLE.privateKey), privateKey: z.string().trim().describe(PKI_SUBSCRIBERS.GET_LATEST_CERT_BUNDLE.privateKey),
serialNumber: z.string().trim().describe(PKI_SUBSCRIBERS.GET_ACTIVE_CERT_BUNDLE.serialNumber) serialNumber: z.string().trim().describe(PKI_SUBSCRIBERS.GET_LATEST_CERT_BUNDLE.serialNumber)
}) })
} }
}, },
@@ -77,8 +77,8 @@ export const castDbEntryToAcmeCertificateAuthority = (
return { return {
id: ca.id, id: ca.id,
type: CaType.ACME, type: CaType.ACME,
disableDirectIssuance: ca.disableDirectIssuance, enableDirectIssuance: ca.enableDirectIssuance,
name: ca.externalCa.name, name: ca.name,
projectId: ca.projectId, projectId: ca.projectId,
credentials: ca.externalCa.credentials, credentials: ca.externalCa.credentials,
configuration: { configuration: {
@@ -90,7 +90,7 @@ export const castDbEntryToAcmeCertificateAuthority = (
directoryUrl: dbConfigurationCol.directoryUrl, directoryUrl: dbConfigurationCol.directoryUrl,
accountEmail: dbConfigurationCol.accountEmail accountEmail: dbConfigurationCol.accountEmail
}, },
status: ca.externalCa.status as CaStatus status: ca.status as CaStatus
}; };
}; };
@@ -176,7 +176,7 @@ export const AcmeCertificateAuthorityFns = ({
name, name,
projectId, projectId,
configuration, configuration,
disableDirectIssuance, enableDirectIssuance,
actor, actor,
status status
}: { }: {
@@ -184,7 +184,7 @@ export const AcmeCertificateAuthorityFns = ({
name: string; name: string;
projectId: string; projectId: string;
configuration: TCreateAcmeCertificateAuthorityDTO["configuration"]; configuration: TCreateAcmeCertificateAuthorityDTO["configuration"];
disableDirectIssuance: boolean; enableDirectIssuance: boolean;
actor: OrgServiceActor; actor: OrgServiceActor;
}) => { }) => {
const { dnsAppConnectionId, directoryUrl, accountEmail, dnsProviderConfig } = configuration; const { dnsAppConnectionId, directoryUrl, accountEmail, dnsProviderConfig } = configuration;
@@ -208,25 +208,24 @@ export const AcmeCertificateAuthorityFns = ({
const ca = await certificateAuthorityDAL.create( const ca = await certificateAuthorityDAL.create(
{ {
projectId, projectId,
disableDirectIssuance enableDirectIssuance,
name,
status
}, },
tx tx
); );
await externalCertificateAuthorityDAL.create( await externalCertificateAuthorityDAL.create(
{ {
certificateAuthorityId: ca.id, caId: ca.id,
dnsAppConnectionId, dnsAppConnectionId,
type: CaType.ACME, type: CaType.ACME,
name,
projectId,
configuration: { configuration: {
directoryUrl, directoryUrl,
accountEmail, accountEmail,
dnsProvider: dnsProviderConfig.provider, dnsProvider: dnsProviderConfig.provider,
hostedZoneId: dnsProviderConfig.hostedZoneId hostedZoneId: dnsProviderConfig.hostedZoneId
}, }
status
}, },
tx tx
); );
@@ -255,14 +254,14 @@ export const AcmeCertificateAuthorityFns = ({
id, id,
status, status,
configuration, configuration,
disableDirectIssuance, enableDirectIssuance,
actor, actor,
name name
}: { }: {
id: string; id: string;
status?: CaStatus; status?: CaStatus;
configuration: TUpdateAcmeCertificateAuthorityDTO["configuration"]; configuration: TUpdateAcmeCertificateAuthorityDTO["configuration"];
disableDirectIssuance?: boolean; enableDirectIssuance?: boolean;
actor: OrgServiceActor; actor: OrgServiceActor;
name?: string; name?: string;
}) => { }) => {
@@ -290,7 +289,7 @@ export const AcmeCertificateAuthorityFns = ({
await externalCertificateAuthorityDAL.update( await externalCertificateAuthorityDAL.update(
{ {
certificateAuthorityId: id, caId: id,
type: CaType.ACME type: CaType.ACME
}, },
{ {
@@ -306,23 +305,13 @@ export const AcmeCertificateAuthorityFns = ({
); );
} }
await externalCertificateAuthorityDAL.update( if (name || status || enableDirectIssuance) {
{
certificateAuthorityId: id,
type: CaType.ACME
},
{
name,
status
},
tx
);
if (disableDirectIssuance !== undefined) {
await certificateAuthorityDAL.updateById( await certificateAuthorityDAL.updateById(
id, id,
{ {
disableDirectIssuance name,
status,
enableDirectIssuance
}, },
tx tx
); );
@@ -399,7 +388,7 @@ export const AcmeCertificateAuthorityFns = ({
}); });
await externalCertificateAuthorityDAL.update( await externalCertificateAuthorityDAL.update(
{ {
certificateAuthorityId: acmeCa.id caId: acmeCa.id
}, },
{ {
credentials: encryptedNewCredentials credentials: encryptedNewCredentials
@@ -14,25 +14,25 @@ export type TCertificateAuthorityWithAssociatedCa = Awaited<
export const certificateAuthorityDALFactory = (db: TDbClient) => { export const certificateAuthorityDALFactory = (db: TDbClient) => {
const caOrm = ormify(db, TableName.CertificateAuthority); const caOrm = ormify(db, TableName.CertificateAuthority);
const findByIdWithAssociatedCa = async (caId: string, tx?: Knex) => { const findByNameAndProjectIdWithAssociatedCa = async (caName: string, projectId: string, tx?: Knex) => {
const result = await (tx || db.replicaNode())(TableName.CertificateAuthority) const result = await (tx || db.replicaNode())(TableName.CertificateAuthority)
.leftJoin( .leftJoin(
TableName.InternalCertificateAuthority, TableName.InternalCertificateAuthority,
`${TableName.CertificateAuthority}.id`, `${TableName.CertificateAuthority}.id`,
`${TableName.InternalCertificateAuthority}.certificateAuthorityId` `${TableName.InternalCertificateAuthority}.caId`
) )
.leftJoin( .leftJoin(
TableName.ExternalCertificateAuthority, TableName.ExternalCertificateAuthority,
`${TableName.CertificateAuthority}.id`, `${TableName.CertificateAuthority}.id`,
`${TableName.ExternalCertificateAuthority}.certificateAuthorityId` `${TableName.ExternalCertificateAuthority}.caId`
) )
.where(`${TableName.CertificateAuthority}.id`, caId) .where(`${TableName.CertificateAuthority}.name`, caName)
.where(`${TableName.CertificateAuthority}.projectId`, projectId)
.select(selectAllTableCols(TableName.CertificateAuthority)) .select(selectAllTableCols(TableName.CertificateAuthority))
.select( .select(
db.ref("id").withSchema(TableName.InternalCertificateAuthority).as("internalCaId"), db.ref("id").withSchema(TableName.InternalCertificateAuthority).as("internalCaId"),
db.ref("parentCaId").withSchema(TableName.InternalCertificateAuthority).as("internalParentCaId"), db.ref("parentCaId").withSchema(TableName.InternalCertificateAuthority).as("internalParentCaId"),
db.ref("type").withSchema(TableName.InternalCertificateAuthority).as("internalType"), db.ref("type").withSchema(TableName.InternalCertificateAuthority).as("internalType"),
db.ref("status").withSchema(TableName.InternalCertificateAuthority).as("internalStatus"),
db.ref("friendlyName").withSchema(TableName.InternalCertificateAuthority).as("internalFriendlyName"), db.ref("friendlyName").withSchema(TableName.InternalCertificateAuthority).as("internalFriendlyName"),
db.ref("organization").withSchema(TableName.InternalCertificateAuthority).as("internalOrganization"), db.ref("organization").withSchema(TableName.InternalCertificateAuthority).as("internalOrganization"),
db.ref("ou").withSchema(TableName.InternalCertificateAuthority).as("internalOu"), db.ref("ou").withSchema(TableName.InternalCertificateAuthority).as("internalOu"),
@@ -46,17 +46,11 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
db.ref("keyAlgorithm").withSchema(TableName.InternalCertificateAuthority).as("internalKeyAlgorithm"), db.ref("keyAlgorithm").withSchema(TableName.InternalCertificateAuthority).as("internalKeyAlgorithm"),
db.ref("notBefore").withSchema(TableName.InternalCertificateAuthority).as("internalNotBefore"), db.ref("notBefore").withSchema(TableName.InternalCertificateAuthority).as("internalNotBefore"),
db.ref("notAfter").withSchema(TableName.InternalCertificateAuthority).as("internalNotAfter"), db.ref("notAfter").withSchema(TableName.InternalCertificateAuthority).as("internalNotAfter"),
db.ref("activeCaCertId").withSchema(TableName.InternalCertificateAuthority).as("internalActiveCaCertId"), db.ref("activeCaCertId").withSchema(TableName.InternalCertificateAuthority).as("internalActiveCaCertId")
db
.ref("certificateAuthorityId")
.withSchema(TableName.InternalCertificateAuthority)
.as("internalCertificateAuthorityId")
) )
.select( .select(
db.ref("id").withSchema(TableName.ExternalCertificateAuthority).as("externalCaId"), db.ref("id").withSchema(TableName.ExternalCertificateAuthority).as("externalCaId"),
db.ref("name").withSchema(TableName.ExternalCertificateAuthority).as("externalName"),
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("configuration").withSchema(TableName.ExternalCertificateAuthority).as("externalConfiguration"), db.ref("configuration").withSchema(TableName.ExternalCertificateAuthority).as("externalConfiguration"),
db.ref("credentials").withSchema(TableName.ExternalCertificateAuthority).as("externalCredentials"), db.ref("credentials").withSchema(TableName.ExternalCertificateAuthority).as("externalCredentials"),
db db
@@ -74,7 +68,6 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
id: result.internalCaId, id: result.internalCaId,
parentCaId: result.internalParentCaId, parentCaId: result.internalParentCaId,
type: result.internalType, type: result.internalType,
status: result.internalStatus,
friendlyName: result.internalFriendlyName, friendlyName: result.internalFriendlyName,
organization: result.internalOrganization, organization: result.internalOrganization,
ou: result.internalOu, ou: result.internalOu,
@@ -88,16 +81,97 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
keyAlgorithm: result.internalKeyAlgorithm, keyAlgorithm: result.internalKeyAlgorithm,
notBefore: result.internalNotBefore?.toISOString(), notBefore: result.internalNotBefore?.toISOString(),
notAfter: result.internalNotAfter?.toISOString(), notAfter: result.internalNotAfter?.toISOString(),
activeCaCertId: result.internalActiveCaCertId, activeCaCertId: result.internalActiveCaCertId
certificateAuthorityId: result.internalCertificateAuthorityId }
: undefined,
externalCa: result
? {
id: result.externalCaId,
type: result.externalType,
configuration: result.externalConfiguration,
dnsAppConnectionId: result.externalDnsAppConnectionId,
appConnectionId: result.externalAppConnectionId,
credentials: result.externalCredentials
}
: undefined
};
return data;
};
const findByIdWithAssociatedCa = async (caId: string, tx?: Knex) => {
const result = await (tx || db.replicaNode())(TableName.CertificateAuthority)
.leftJoin(
TableName.InternalCertificateAuthority,
`${TableName.CertificateAuthority}.id`,
`${TableName.InternalCertificateAuthority}.caId`
)
.leftJoin(
TableName.ExternalCertificateAuthority,
`${TableName.CertificateAuthority}.id`,
`${TableName.ExternalCertificateAuthority}.caId`
)
.where(`${TableName.CertificateAuthority}.id`, caId)
.select(selectAllTableCols(TableName.CertificateAuthority))
.select(
db.ref("id").withSchema(TableName.InternalCertificateAuthority).as("internalCaId"),
db.ref("parentCaId").withSchema(TableName.InternalCertificateAuthority).as("internalParentCaId"),
db.ref("type").withSchema(TableName.InternalCertificateAuthority).as("internalType"),
db.ref("friendlyName").withSchema(TableName.InternalCertificateAuthority).as("internalFriendlyName"),
db.ref("organization").withSchema(TableName.InternalCertificateAuthority).as("internalOrganization"),
db.ref("ou").withSchema(TableName.InternalCertificateAuthority).as("internalOu"),
db.ref("country").withSchema(TableName.InternalCertificateAuthority).as("internalCountry"),
db.ref("province").withSchema(TableName.InternalCertificateAuthority).as("internalProvince"),
db.ref("locality").withSchema(TableName.InternalCertificateAuthority).as("internalLocality"),
db.ref("commonName").withSchema(TableName.InternalCertificateAuthority).as("internalCommonName"),
db.ref("dn").withSchema(TableName.InternalCertificateAuthority).as("internalDn"),
db.ref("serialNumber").withSchema(TableName.InternalCertificateAuthority).as("internalSerialNumber"),
db.ref("maxPathLength").withSchema(TableName.InternalCertificateAuthority).as("internalMaxPathLength"),
db.ref("keyAlgorithm").withSchema(TableName.InternalCertificateAuthority).as("internalKeyAlgorithm"),
db.ref("notBefore").withSchema(TableName.InternalCertificateAuthority).as("internalNotBefore"),
db.ref("notAfter").withSchema(TableName.InternalCertificateAuthority).as("internalNotAfter"),
db.ref("activeCaCertId").withSchema(TableName.InternalCertificateAuthority).as("internalActiveCaCertId")
)
.select(
db.ref("id").withSchema(TableName.ExternalCertificateAuthority).as("externalCaId"),
db.ref("type").withSchema(TableName.ExternalCertificateAuthority).as("externalType"),
db.ref("configuration").withSchema(TableName.ExternalCertificateAuthority).as("externalConfiguration"),
db.ref("credentials").withSchema(TableName.ExternalCertificateAuthority).as("externalCredentials"),
db
.ref("dnsAppConnectionId")
.withSchema(TableName.ExternalCertificateAuthority)
.as("externalDnsAppConnectionId"),
db.ref("appConnectionId").withSchema(TableName.ExternalCertificateAuthority).as("externalAppConnectionId")
)
.first();
const data = {
...CertificateAuthoritiesSchema.parse(result),
internalCa: result
? {
id: result.internalCaId,
parentCaId: result.internalParentCaId,
type: result.internalType,
friendlyName: result.internalFriendlyName,
organization: result.internalOrganization,
ou: result.internalOu,
country: result.internalCountry,
province: result.internalProvince,
locality: result.internalLocality,
commonName: result.internalCommonName,
dn: result.internalDn,
serialNumber: result.internalSerialNumber,
maxPathLength: result.internalMaxPathLength,
keyAlgorithm: result.internalKeyAlgorithm,
notBefore: result.internalNotBefore?.toISOString(),
notAfter: result.internalNotAfter?.toISOString(),
activeCaCertId: result.internalActiveCaCertId
} }
: undefined, : undefined,
externalCa: result externalCa: result
? { ? {
id: result.externalCaId, id: result.externalCaId,
name: result.externalName,
type: result.externalType, type: result.externalType,
status: result.externalStatus,
configuration: result.externalConfiguration, configuration: result.externalConfiguration,
dnsAppConnectionId: result.externalDnsAppConnectionId, dnsAppConnectionId: result.externalDnsAppConnectionId,
appConnectionId: result.externalAppConnectionId, appConnectionId: result.externalAppConnectionId,
@@ -153,12 +227,12 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
.leftJoin( .leftJoin(
TableName.InternalCertificateAuthority, TableName.InternalCertificateAuthority,
`${TableName.CertificateAuthority}.id`, `${TableName.CertificateAuthority}.id`,
`${TableName.InternalCertificateAuthority}.certificateAuthorityId` `${TableName.InternalCertificateAuthority}.caId`
) )
.leftJoin( .leftJoin(
TableName.ExternalCertificateAuthority, TableName.ExternalCertificateAuthority,
`${TableName.CertificateAuthority}.id`, `${TableName.CertificateAuthority}.id`,
`${TableName.ExternalCertificateAuthority}.certificateAuthorityId` `${TableName.ExternalCertificateAuthority}.caId`
) )
// eslint-disable-next-line @typescript-eslint/no-misused-promises // eslint-disable-next-line @typescript-eslint/no-misused-promises
.where(buildFindFilter(filter)) .where(buildFindFilter(filter))
@@ -167,7 +241,6 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
db.ref("id").withSchema(TableName.InternalCertificateAuthority).as("internalCaId"), db.ref("id").withSchema(TableName.InternalCertificateAuthority).as("internalCaId"),
db.ref("parentCaId").withSchema(TableName.InternalCertificateAuthority).as("internalParentCaId"), db.ref("parentCaId").withSchema(TableName.InternalCertificateAuthority).as("internalParentCaId"),
db.ref("type").withSchema(TableName.InternalCertificateAuthority).as("internalType"), db.ref("type").withSchema(TableName.InternalCertificateAuthority).as("internalType"),
db.ref("status").withSchema(TableName.InternalCertificateAuthority).as("internalStatus"),
db.ref("friendlyName").withSchema(TableName.InternalCertificateAuthority).as("internalFriendlyName"), db.ref("friendlyName").withSchema(TableName.InternalCertificateAuthority).as("internalFriendlyName"),
db.ref("organization").withSchema(TableName.InternalCertificateAuthority).as("internalOrganization"), db.ref("organization").withSchema(TableName.InternalCertificateAuthority).as("internalOrganization"),
db.ref("ou").withSchema(TableName.InternalCertificateAuthority).as("internalOu"), db.ref("ou").withSchema(TableName.InternalCertificateAuthority).as("internalOu"),
@@ -181,17 +254,11 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
db.ref("keyAlgorithm").withSchema(TableName.InternalCertificateAuthority).as("internalKeyAlgorithm"), db.ref("keyAlgorithm").withSchema(TableName.InternalCertificateAuthority).as("internalKeyAlgorithm"),
db.ref("notBefore").withSchema(TableName.InternalCertificateAuthority).as("internalNotBefore"), db.ref("notBefore").withSchema(TableName.InternalCertificateAuthority).as("internalNotBefore"),
db.ref("notAfter").withSchema(TableName.InternalCertificateAuthority).as("internalNotAfter"), db.ref("notAfter").withSchema(TableName.InternalCertificateAuthority).as("internalNotAfter"),
db.ref("activeCaCertId").withSchema(TableName.InternalCertificateAuthority).as("internalActiveCaCertId"), db.ref("activeCaCertId").withSchema(TableName.InternalCertificateAuthority).as("internalActiveCaCertId")
db
.ref("certificateAuthorityId")
.withSchema(TableName.InternalCertificateAuthority)
.as("internalCertificateAuthorityId")
) )
.select( .select(
db.ref("id").withSchema(TableName.ExternalCertificateAuthority).as("externalCaId"), db.ref("id").withSchema(TableName.ExternalCertificateAuthority).as("externalCaId"),
db.ref("name").withSchema(TableName.ExternalCertificateAuthority).as("externalName"),
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("configuration").withSchema(TableName.ExternalCertificateAuthority).as("externalConfiguration"), db.ref("configuration").withSchema(TableName.ExternalCertificateAuthority).as("externalConfiguration"),
db db
.ref("dnsAppConnectionId") .ref("dnsAppConnectionId")
@@ -220,7 +287,6 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
id: ca.internalCaId, id: ca.internalCaId,
parentCaId: ca.internalParentCaId, parentCaId: ca.internalParentCaId,
type: ca.internalType, type: ca.internalType,
status: ca.internalStatus,
friendlyName: ca.internalFriendlyName, friendlyName: ca.internalFriendlyName,
organization: ca.internalOrganization, organization: ca.internalOrganization,
ou: ca.internalOu, ou: ca.internalOu,
@@ -234,16 +300,13 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
keyAlgorithm: ca.internalKeyAlgorithm, keyAlgorithm: ca.internalKeyAlgorithm,
notBefore: ca.internalNotBefore?.toISOString(), notBefore: ca.internalNotBefore?.toISOString(),
notAfter: ca.internalNotAfter?.toISOString(), notAfter: ca.internalNotAfter?.toISOString(),
activeCaCertId: ca.internalActiveCaCertId, activeCaCertId: ca.internalActiveCaCertId
certificateAuthorityId: ca.internalCertificateAuthorityId
} }
: undefined, : undefined,
externalCa: ca externalCa: ca
? { ? {
id: ca.externalCaId, id: ca.externalCaId,
name: ca.externalName,
type: ca.externalType, type: ca.externalType,
status: ca.externalStatus,
configuration: ca.externalConfiguration, configuration: ca.externalConfiguration,
dnsAppConnectionId: ca.externalDnsAppConnectionId, dnsAppConnectionId: ca.externalDnsAppConnectionId,
appConnectionId: ca.externalAppConnectionId, appConnectionId: ca.externalAppConnectionId,
@@ -260,6 +323,7 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
...caOrm, ...caOrm,
findWithAssociatedCa, findWithAssociatedCa,
buildCertificateChain, buildCertificateChain,
findByIdWithAssociatedCa findByIdWithAssociatedCa,
findByNameAndProjectIdWithAssociatedCa
}; };
}; };
@@ -329,6 +329,6 @@ export const expandInternalCa = (
return { return {
...ca.internalCa, ...ca.internalCa,
...ca, ...ca,
requireTemplateForIssuance: ca.disableDirectIssuance requireTemplateForIssuance: !ca.enableDirectIssuance
} as const; } as const;
}; };
@@ -8,10 +8,10 @@ import { CaStatus, CaType } from "./certificate-authority-enums";
export const BaseCertificateAuthoritySchema = CertificateAuthoritiesSchema.pick({ export const BaseCertificateAuthoritySchema = CertificateAuthoritiesSchema.pick({
projectId: true, projectId: true,
disableDirectIssuance: true, enableDirectIssuance: true,
name: true,
id: true id: true
}).extend({ }).extend({
name: z.string(),
status: z.nativeEnum(CaStatus) status: z.nativeEnum(CaStatus)
}); });
@@ -19,13 +19,14 @@ export const GenericCreateCertificateAuthorityFieldsSchema = (type: CaType) =>
z.object({ z.object({
name: slugSchema({ field: "name" }).describe(CertificateAuthorities.CREATE(type).name), name: slugSchema({ field: "name" }).describe(CertificateAuthorities.CREATE(type).name),
projectId: z.string().trim().min(1, "Project ID required").describe(CertificateAuthorities.CREATE(type).projectId), projectId: z.string().trim().min(1, "Project ID required").describe(CertificateAuthorities.CREATE(type).projectId),
disableDirectIssuance: z.boolean().describe(CertificateAuthorities.CREATE(type).disableDirectIssuance), enableDirectIssuance: z.boolean().describe(CertificateAuthorities.CREATE(type).enableDirectIssuance),
status: z.nativeEnum(CaStatus).describe(CertificateAuthorities.CREATE(type).status) status: z.nativeEnum(CaStatus).describe(CertificateAuthorities.CREATE(type).status)
}); });
export const GenericUpdateCertificateAuthorityFieldsSchema = (type: CaType) => export const GenericUpdateCertificateAuthorityFieldsSchema = (type: CaType) =>
z.object({ z.object({
name: slugSchema({ field: "name" }).optional().describe(CertificateAuthorities.UPDATE(type).name), name: slugSchema({ field: "name" }).optional().describe(CertificateAuthorities.UPDATE(type).name),
disableDirectIssuance: z.boolean().optional().describe(CertificateAuthorities.UPDATE(type).disableDirectIssuance), projectId: z.string().trim().min(1, "Project ID required").describe(CertificateAuthorities.UPDATE(type).projectId),
enableDirectIssuance: z.boolean().optional().describe(CertificateAuthorities.UPDATE(type).enableDirectIssuance),
status: z.nativeEnum(CaStatus).optional().describe(CertificateAuthorities.UPDATE(type).status) status: z.nativeEnum(CaStatus).optional().describe(CertificateAuthorities.UPDATE(type).status)
}); });
@@ -46,6 +46,7 @@ type TCertificateAuthorityServiceFactoryDep = {
| "findOne" | "findOne"
| "findByIdWithAssociatedCa" | "findByIdWithAssociatedCa"
| "findWithAssociatedCa" | "findWithAssociatedCa"
| "findByNameAndProjectIdWithAssociatedCa"
>; >;
externalCertificateAuthorityDAL: Pick<TExternalCertificateAuthorityDALFactory, "create" | "update">; externalCertificateAuthorityDAL: Pick<TExternalCertificateAuthorityDALFactory, "create" | "update">;
internalCertificateAuthorityService: TInternalCertificateAuthorityServiceFactory; internalCertificateAuthorityService: TInternalCertificateAuthorityServiceFactory;
@@ -94,7 +95,7 @@ export const certificateAuthorityServiceFactory = ({
}); });
const createCertificateAuthority = async ( const createCertificateAuthority = async (
{ type, projectId, name, disableDirectIssuance, configuration, status }: TCreateCertificateAuthorityDTO, { type, projectId, name, enableDirectIssuance, configuration, status }: TCreateCertificateAuthorityDTO,
actor: OrgServiceActor actor: OrgServiceActor
) => { ) => {
let finalProjectId: string = projectId; let finalProjectId: string = projectId;
@@ -126,7 +127,7 @@ export const certificateAuthorityServiceFactory = ({
...(configuration as TCreateInternalCertificateAuthorityDTO["configuration"]), ...(configuration as TCreateInternalCertificateAuthorityDTO["configuration"]),
isInternal: true, isInternal: true,
projectId: finalProjectId, projectId: finalProjectId,
requireTemplateForIssuance: disableDirectIssuance requireTemplateForIssuance: !enableDirectIssuance
}); });
if (!ca.internalCa) { if (!ca.internalCa) {
@@ -138,8 +139,8 @@ export const certificateAuthorityServiceFactory = ({
return { return {
id: ca.id, id: ca.id,
type, type,
disableDirectIssuance: ca.disableDirectIssuance, enableDirectIssuance: ca.enableDirectIssuance,
name: ca.internalCa?.friendlyName, name: ca.name,
projectId: finalProjectId, projectId: finalProjectId,
status, status,
configuration: ca.internalCa configuration: ca.internalCa
@@ -151,7 +152,7 @@ export const certificateAuthorityServiceFactory = ({
name, name,
projectId: finalProjectId, projectId: finalProjectId,
configuration: configuration as TCreateAcmeCertificateAuthorityDTO["configuration"], configuration: configuration as TCreateAcmeCertificateAuthorityDTO["configuration"],
disableDirectIssuance, enableDirectIssuance,
status, status,
actor actor
}); });
@@ -160,15 +161,18 @@ export const certificateAuthorityServiceFactory = ({
throw new BadRequestError({ message: "Invalid certificate authority type" }); throw new BadRequestError({ message: "Invalid certificate authority type" });
}; };
const findCertificateAuthorityById = async ( const findCertificateAuthorityByNameAndProjectId = async (
{ certificateAuthorityId, type }: { certificateAuthorityId: string; type: CaType }, { caName, type, projectId }: { caName: string; type: CaType; projectId: string },
actor: OrgServiceActor actor: OrgServiceActor
) => { ) => {
const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(certificateAuthorityId); const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa(
caName,
projectId
);
if (!certificateAuthority) if (!certificateAuthority)
throw new NotFoundError({ throw new NotFoundError({
message: `Could not find certificate authority with ID "${certificateAuthorityId}"` message: `Could not find certificate authority with name "${caName}" in project "${projectId}"`
}); });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
@@ -188,24 +192,24 @@ export const certificateAuthorityServiceFactory = ({
if (type === CaType.INTERNAL) { if (type === CaType.INTERNAL) {
if (!certificateAuthority.internalCa?.id) { if (!certificateAuthority.internalCa?.id) {
throw new NotFoundError({ throw new NotFoundError({
message: `Internal certificate authority with ID "${certificateAuthorityId}" not found` message: `Internal certificate authority with name "${caName}" in project "${projectId}" not found`
}); });
} }
return { return {
id: certificateAuthority.id, id: certificateAuthority.id,
type, type,
disableDirectIssuance: certificateAuthority.disableDirectIssuance, enableDirectIssuance: certificateAuthority.enableDirectIssuance,
name: certificateAuthority.internalCa.friendlyName, name: certificateAuthority.name,
projectId: certificateAuthority.projectId, projectId: certificateAuthority.projectId,
configuration: certificateAuthority.internalCa, configuration: certificateAuthority.internalCa,
status: certificateAuthority.internalCa.status status: certificateAuthority.status
} as TCertificateAuthority; } as TCertificateAuthority;
} }
if (certificateAuthority.externalCa?.type !== type) { if (certificateAuthority.externalCa?.type !== type) {
throw new NotFoundError({ throw new NotFoundError({
message: `Could not find external certificate authority with ID "${certificateAuthorityId}" and type "${type}"` message: `Could not find external certificate authority with name "${caName}" in project "${projectId}" and type "${type}"`
}); });
} }
@@ -255,11 +259,11 @@ export const certificateAuthorityServiceFactory = ({
.map((ca) => ({ .map((ca) => ({
id: ca.id, id: ca.id,
type, type,
disableDirectIssuance: ca.disableDirectIssuance, enableDirectIssuance: ca.enableDirectIssuance,
name: ca.internalCa.friendlyName, name: ca.name,
projectId: ca.projectId, projectId: ca.projectId,
configuration: ca.internalCa, configuration: ca.internalCa,
status: ca.internalCa.status status: ca.status
})) as TCertificateAuthority[]; })) as TCertificateAuthority[];
} }
@@ -271,14 +275,17 @@ export const certificateAuthorityServiceFactory = ({
}; };
const updateCertificateAuthority = async ( const updateCertificateAuthority = async (
{ id, type, configuration, disableDirectIssuance, status, name }: TUpdateCertificateAuthorityDTO, { caName, type, configuration, enableDirectIssuance, status, name, projectId }: TUpdateCertificateAuthorityDTO,
actor: OrgServiceActor actor: OrgServiceActor
) => { ) => {
const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(id); const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa(
caName,
projectId
);
if (!certificateAuthority) if (!certificateAuthority)
throw new NotFoundError({ throw new NotFoundError({
message: `Could not find certificate authority with ID "${id}"` message: `Could not find certificate authority with name "${caName}" in project "${projectId}"`
}); });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
@@ -298,15 +305,16 @@ export const certificateAuthorityServiceFactory = ({
if (type === CaType.INTERNAL) { if (type === CaType.INTERNAL) {
if (!certificateAuthority.internalCa?.id) { if (!certificateAuthority.internalCa?.id) {
throw new NotFoundError({ throw new NotFoundError({
message: `Internal certificate authority with ID "${id}" not found` message: `Internal certificate authority with name "${caName}" in project "${projectId}" not found`
}); });
} }
const updatedCa = await internalCertificateAuthorityService.updateCaById({ const updatedCa = await internalCertificateAuthorityService.updateCaById({
...configuration, ...configuration,
isInternal: true, isInternal: true,
requireTemplateForIssuance: disableDirectIssuance, requireTemplateForIssuance: !enableDirectIssuance,
caId: id caId: certificateAuthority.id,
name
}); });
if (!updatedCa.internalCa) { if (!updatedCa.internalCa) {
@@ -318,19 +326,19 @@ export const certificateAuthorityServiceFactory = ({
return { return {
id: updatedCa.id, id: updatedCa.id,
type, type,
disableDirectIssuance: updatedCa.disableDirectIssuance, enableDirectIssuance: updatedCa.enableDirectIssuance,
name: updatedCa.internalCa?.friendlyName, name: updatedCa.name,
projectId: updatedCa.projectId, projectId: updatedCa.projectId,
configuration: updatedCa.internalCa, configuration: updatedCa.internalCa,
status: updatedCa.internalCa?.status status: updatedCa.status
} as TCertificateAuthority; } as TCertificateAuthority;
} }
if (type === CaType.ACME) { if (type === CaType.ACME) {
return acmeFns.updateCertificateAuthority({ return acmeFns.updateCertificateAuthority({
id, id: certificateAuthority.id,
configuration: configuration as TUpdateAcmeCertificateAuthorityDTO["configuration"], configuration: configuration as TUpdateAcmeCertificateAuthorityDTO["configuration"],
disableDirectIssuance, enableDirectIssuance,
actor, actor,
status, status,
name name
@@ -340,12 +348,18 @@ export const certificateAuthorityServiceFactory = ({
throw new BadRequestError({ message: "Invalid certificate authority type" }); throw new BadRequestError({ message: "Invalid certificate authority type" });
}; };
const deleteCertificateAuthority = async ({ id, type }: { id: string; type: CaType }, actor: OrgServiceActor) => { const deleteCertificateAuthority = async (
const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(id); { caName, type, projectId }: { caName: string; type: CaType; projectId: string },
actor: OrgServiceActor
) => {
const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa(
caName,
projectId
);
if (!certificateAuthority) if (!certificateAuthority)
throw new NotFoundError({ throw new NotFoundError({
message: `Could not find certificate authority with ID "${id}"` message: `Could not find certificate authority with name "${caName}" in project "${projectId}"`
}); });
const { permission } = await permissionService.getProjectPermission({ const { permission } = await permissionService.getProjectPermission({
@@ -374,17 +388,17 @@ export const certificateAuthorityServiceFactory = ({
}); });
} }
await certificateAuthorityDAL.deleteById(id); await certificateAuthorityDAL.deleteById(certificateAuthority.id);
if (type === CaType.INTERNAL) { if (type === CaType.INTERNAL) {
return { return {
id: certificateAuthority.id, id: certificateAuthority.id,
type, type,
disableDirectIssuance: certificateAuthority.disableDirectIssuance, enableDirectIssuance: certificateAuthority.enableDirectIssuance,
name: certificateAuthority.internalCa?.friendlyName, name: certificateAuthority.name,
projectId: certificateAuthority.projectId, projectId: certificateAuthority.projectId,
configuration: certificateAuthority.internalCa, configuration: certificateAuthority.internalCa,
status: certificateAuthority.internalCa?.status status: certificateAuthority.status
} as TCertificateAuthority; } as TCertificateAuthority;
} }
@@ -397,7 +411,7 @@ export const certificateAuthorityServiceFactory = ({
return { return {
createCertificateAuthority, createCertificateAuthority,
findCertificateAuthorityById, findCertificateAuthorityByNameAndProjectId,
listCertificateAuthoritiesByProjectId, listCertificateAuthoritiesByProjectId,
updateCertificateAuthority, updateCertificateAuthority,
deleteCertificateAuthority deleteCertificateAuthority
@@ -13,5 +13,6 @@ export type TCreateCertificateAuthorityDTO = Omit<TCertificateAuthority, "id">;
export type TUpdateCertificateAuthorityDTO = Partial<Omit<TCreateCertificateAuthorityDTO, "projectId">> & { export type TUpdateCertificateAuthorityDTO = Partial<Omit<TCreateCertificateAuthorityDTO, "projectId">> & {
type: CaType; type: CaType;
id: string; caName: string;
projectId: string;
}; };
@@ -1,6 +1,7 @@
/* eslint-disable no-bitwise */ /* eslint-disable no-bitwise */
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509"; import * as x509 from "@peculiar/x509";
import slugify from "@sindresorhus/slugify";
import crypto, { KeyObject } from "crypto"; import crypto, { KeyObject } from "crypto";
import { z } from "zod"; import { z } from "zod";
@@ -21,6 +22,7 @@ import { extractX509CertFromChain } from "@app/lib/certificates/extract-certific
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { ms } from "@app/lib/ms"; import { ms } from "@app/lib/ms";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { isFQDN } from "@app/lib/validator/validate-url"; import { isFQDN } from "@app/lib/validator/validate-url";
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal"; import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal"; import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
@@ -200,14 +202,16 @@ export const internalCertificateAuthorityServiceFactory = ({
const ca = await certificateAuthorityDAL.create( const ca = await certificateAuthorityDAL.create(
{ {
projectId, projectId,
disableDirectIssuance: requireTemplateForIssuance enableDirectIssuance: !requireTemplateForIssuance,
name: slugify(`${friendlyName || dn}-${alphaNumericNanoId(8)}`),
status: type === InternalCaType.ROOT ? CaStatus.ACTIVE : CaStatus.PENDING_CERTIFICATE
}, },
tx tx
); );
const internalCa = await internalCertificateAuthorityDAL.create( const internalCa = await internalCertificateAuthorityDAL.create(
{ {
certificateAuthorityId: ca.id, caId: ca.id,
type, type,
organization, organization,
ou, ou,
@@ -216,7 +220,6 @@ export const internalCertificateAuthorityServiceFactory = ({
locality, locality,
friendlyName: friendlyName || dn, friendlyName: friendlyName || dn,
commonName, commonName,
status: type === InternalCaType.ROOT ? CaStatus.ACTIVE : CaStatus.PENDING_CERTIFICATE,
dn, dn,
keyAlgorithm, keyAlgorithm,
...(type === InternalCaType.ROOT && { ...(type === InternalCaType.ROOT && {
@@ -357,7 +360,7 @@ export const internalCertificateAuthorityServiceFactory = ({
* Update CA with id [caId]. * Update CA with id [caId].
* Note: Used to enable/disable CA * Note: Used to enable/disable CA
*/ */
const updateCaById = async ({ caId, status, requireTemplateForIssuance, ...dto }: TUpdateCaDTO) => { const updateCaById = async ({ caId, status, requireTemplateForIssuance, name, ...dto }: TUpdateCaDTO) => {
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId); const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
if (!ca.internalCa) throw new NotFoundError({ message: `CA with ID '${caId}' not found` }); if (!ca.internalCa) throw new NotFoundError({ message: `CA with ID '${caId}' not found` });
@@ -378,20 +381,14 @@ export const internalCertificateAuthorityServiceFactory = ({
} }
const updatedCa = await certificateAuthorityDAL.transaction(async (tx) => { const updatedCa = await certificateAuthorityDAL.transaction(async (tx) => {
if (status !== undefined) { if (requireTemplateForIssuance !== undefined || status !== undefined || name !== undefined) {
await internalCertificateAuthorityDAL.update( await certificateAuthorityDAL.updateById(
{ ca.id,
certificateAuthorityId: ca.id { enableDirectIssuance: !requireTemplateForIssuance, status, name },
},
{ status },
tx tx
); );
} }
if (requireTemplateForIssuance !== undefined) {
await certificateAuthorityDAL.updateById(ca.id, { disableDirectIssuance: requireTemplateForIssuance }, tx);
}
return certificateAuthorityDAL.findByIdWithAssociatedCa(caId, tx); return certificateAuthorityDAL.findByIdWithAssociatedCa(caId, tx);
}); });
@@ -509,7 +506,7 @@ export const internalCertificateAuthorityServiceFactory = ({
ProjectPermissionSub.CertificateAuthorities ProjectPermissionSub.CertificateAuthorities
); );
if (ca.internalCa.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" }); if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
// get latest CA certificate // get latest CA certificate
const caCert = await certificateAuthorityCertDAL.findById(ca.internalCa.activeCaCertId); const caCert = await certificateAuthorityCertDAL.findById(ca.internalCa.activeCaCertId);
@@ -604,7 +601,7 @@ export const internalCertificateAuthorityServiceFactory = ({
await internalCertificateAuthorityDAL.update( await internalCertificateAuthorityDAL.update(
{ {
certificateAuthorityId: ca.id caId: ca.id
}, },
{ {
activeCaCertId: newCaCert.id, activeCaCertId: newCaCert.id,
@@ -746,7 +743,7 @@ export const internalCertificateAuthorityServiceFactory = ({
await internalCertificateAuthorityDAL.update( await internalCertificateAuthorityDAL.update(
{ {
certificateAuthorityId: ca.id caId: ca.id
}, },
{ {
activeCaCertId: newCaCert.id, activeCaCertId: newCaCert.id,
@@ -914,7 +911,7 @@ export const internalCertificateAuthorityServiceFactory = ({
ProjectPermissionSub.CertificateAuthorities ProjectPermissionSub.CertificateAuthorities
); );
if (ca.internalCa.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" }); if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
if (!ca.internalCa.activeCaCertId) if (!ca.internalCa.activeCaCertId)
throw new BadRequestError({ message: "CA does not have a certificate installed" }); throw new BadRequestError({ message: "CA does not have a certificate installed" });
@@ -1150,12 +1147,15 @@ export const internalCertificateAuthorityServiceFactory = ({
tx tx
); );
await certificateAuthorityDAL.updateById(ca.id, {
status: CaStatus.ACTIVE
});
await internalCertificateAuthorityDAL.update( await internalCertificateAuthorityDAL.update(
{ {
certificateAuthorityId: ca.id caId: ca.id
}, },
{ {
status: CaStatus.ACTIVE,
maxPathLength: maxPathLength === undefined ? -1 : maxPathLength, maxPathLength: maxPathLength === undefined ? -1 : maxPathLength,
notBefore: new Date(certObj.notBefore), notBefore: new Date(certObj.notBefore),
notAfter: new Date(certObj.notAfter), notAfter: new Date(certObj.notAfter),
@@ -1231,10 +1231,10 @@ export const internalCertificateAuthorityServiceFactory = ({
ProjectPermissionSub.Certificates ProjectPermissionSub.Certificates
); );
if (ca.internalCa.status !== CaStatus.ACTIVE) throw new BadRequestError({ message: "CA is not active" }); if (ca.status !== CaStatus.ACTIVE) throw new BadRequestError({ message: "CA is not active" });
if (!ca.internalCa.activeCaCertId) if (!ca.internalCa.activeCaCertId)
throw new BadRequestError({ message: "CA does not have a certificate installed" }); throw new BadRequestError({ message: "CA does not have a certificate installed" });
if (ca.disableDirectIssuance && !certificateTemplate) { if (!ca.enableDirectIssuance && !certificateTemplate) {
throw new BadRequestError({ message: "Certificate template or subscriber is required for issuance" }); throw new BadRequestError({ message: "Certificate template or subscriber is required for issuance" });
} }
@@ -1589,10 +1589,10 @@ export const internalCertificateAuthorityServiceFactory = ({
); );
} }
if (ca.internalCa.status !== CaStatus.ACTIVE) throw new BadRequestError({ message: "CA is not active" }); if (ca.status !== CaStatus.ACTIVE) throw new BadRequestError({ message: "CA is not active" });
if (!ca.internalCa.activeCaCertId) if (!ca.internalCa.activeCaCertId)
throw new BadRequestError({ message: "CA does not have a certificate installed" }); throw new BadRequestError({ message: "CA does not have a certificate installed" });
if (ca.disableDirectIssuance && !certificateTemplate) { if (!ca.enableDirectIssuance && !certificateTemplate) {
throw new BadRequestError({ message: "Certificate template or subscriber is required for issuance" }); throw new BadRequestError({ message: "Certificate template or subscriber is required for issuance" });
} }
@@ -69,12 +69,14 @@ export type TUpdateCaDTO =
| { | {
isInternal: true; isInternal: true;
caId: string; caId: string;
name?: string;
status?: CaStatus; status?: CaStatus;
requireTemplateForIssuance?: boolean; requireTemplateForIssuance?: boolean;
} }
| ({ | ({
isInternal: false; isInternal: false;
caId: string; caId: string;
name?: string;
status?: CaStatus; status?: CaStatus;
requireTemplateForIssuance?: boolean; requireTemplateForIssuance?: boolean;
} & Omit<TProjectPermission, "projectId">); } & Omit<TProjectPermission, "projectId">);
@@ -21,7 +21,7 @@ export const certificateTemplateDALFactory = (db: TDbClient) => {
) )
.join( .join(
TableName.InternalCertificateAuthority, TableName.InternalCertificateAuthority,
`${TableName.InternalCertificateAuthority}.certificateAuthorityId`, `${TableName.InternalCertificateAuthority}.caId`,
`${TableName.CertificateAuthority}.id` `${TableName.CertificateAuthority}.id`
) )
.where(`${TableName.CertificateAuthority}.projectId`, "=", projectId) .where(`${TableName.CertificateAuthority}.projectId`, "=", projectId)
@@ -48,7 +48,7 @@ export const certificateTemplateDALFactory = (db: TDbClient) => {
.join(TableName.Project, `${TableName.Project}.id`, `${TableName.CertificateAuthority}.projectId`) .join(TableName.Project, `${TableName.Project}.id`, `${TableName.CertificateAuthority}.projectId`)
.join( .join(
TableName.InternalCertificateAuthority, TableName.InternalCertificateAuthority,
`${TableName.InternalCertificateAuthority}.certificateAuthorityId`, `${TableName.InternalCertificateAuthority}.caId`,
`${TableName.CertificateAuthority}.id` `${TableName.CertificateAuthority}.id`
) )
.where(`${TableName.CertificateTemplate}.id`, "=", id) .where(`${TableName.CertificateTemplate}.id`, "=", id)
@@ -37,7 +37,7 @@ export const pkiAlertDALFactory = (db: TDbClient) => {
"pci.pkiCollectionId" "pci.pkiCollectionId"
) )
.from(`${TableName.CertificateAuthority} as ${PkiItemType.CA}`) .from(`${TableName.CertificateAuthority} as ${PkiItemType.CA}`)
.join(`${TableName.InternalCertificateAuthority} as ic`, `${PkiItemType.CA}.id`, "ic.certificateAuthorityId") .join(`${TableName.InternalCertificateAuthority} as ic`, `${PkiItemType.CA}.id`, "ic.caId")
.join(`${TableName.PkiCollectionItem} as pci`, `${PkiItemType.CA}.id`, "pci.caId") .join(`${TableName.PkiCollectionItem} as pci`, `${PkiItemType.CA}.id`, "pci.caId")
.unionAll((qb) => { .unionAll((qb) => {
void qb void qb
@@ -44,7 +44,7 @@ export const pkiCollectionItemDALFactory = (db: TDbClient) => {
.leftJoin( .leftJoin(
TableName.InternalCertificateAuthority, TableName.InternalCertificateAuthority,
`${TableName.PkiCollectionItem}.caId`, `${TableName.PkiCollectionItem}.caId`,
`${TableName.InternalCertificateAuthority}.certificateAuthorityId` `${TableName.InternalCertificateAuthority}.caId`
) )
.leftJoin(TableName.Certificate, `${TableName.PkiCollectionItem}.certId`, `${TableName.Certificate}.id`) .leftJoin(TableName.Certificate, `${TableName.PkiCollectionItem}.certId`, `${TableName.Certificate}.id`)
.where((builder) => { .where((builder) => {
@@ -1,4 +1,4 @@
--- ---
title: "Delete" title: "Delete"
openapi: "DELETE /api/v1/pki/ca/acme/{certificateAuthorityId}" openapi: "DELETE /api/v1/pki/ca/acme/{caName}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Read" title: "Read"
openapi: "GET /api/v1/pki/ca/acme/{certificateAuthorityId}" openapi: "GET /api/v1/pki/ca/acme/{caName}"
--- ---
@@ -1,4 +1,4 @@
--- ---
title: "Update" title: "Update"
openapi: "PATCH /api/v1/pki/ca/acme/{certificateAuthorityId}" openapi: "PATCH /api/v1/pki/ca/acme/{caName}"
--- ---
@@ -1,4 +0,0 @@
---
title: "Retrieve active certificate bundle"
openapi: "GET /api/v1/pki/subscribers/{subscriberName}/active-certificate/bundle"
---
@@ -0,0 +1,4 @@
---
title: "Retrieve latest certificate bundle"
openapi: "GET /api/v1/pki/subscribers/{subscriberName}/latest-certificate-bundle"
---
+1 -1
View File
@@ -1522,7 +1522,7 @@
"api-reference/endpoints/pki/subscribers/issue-cert", "api-reference/endpoints/pki/subscribers/issue-cert",
"api-reference/endpoints/pki/subscribers/sign-cert", "api-reference/endpoints/pki/subscribers/sign-cert",
"api-reference/endpoints/pki/subscribers/order-cert", "api-reference/endpoints/pki/subscribers/order-cert",
"api-reference/endpoints/pki/subscribers/get-active-cert-bundle" "api-reference/endpoints/pki/subscribers/get-latest-cert-bundle"
] ]
}, },
{ {
+1 -1
View File
@@ -12,8 +12,8 @@ export {
useUpdateUnifiedCa useUpdateUnifiedCa
} from "./mutations"; } from "./mutations";
export { export {
useGetCa,
useGetCaById, useGetCaById,
useGetCaByTypeAndId,
useGetCaCert, useGetCaCert,
useGetCaCerts, useGetCaCerts,
useGetCaCertTemplates, useGetCaCertTemplates,
+11 -8
View File
@@ -26,15 +26,13 @@ import {
export const useUpdateUnifiedCa = () => { export const useUpdateUnifiedCa = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TUnifiedCertificateAuthority, object, TUpdateUnifiedCertificateAuthorityDTO>({ return useMutation<TUnifiedCertificateAuthority, object, TUpdateUnifiedCertificateAuthorityDTO>({
mutationFn: async ({ id, ...body }) => { mutationFn: async ({ caName, ...body }) => {
const { const { data } = await apiRequest.patch<TUnifiedCertificateAuthority>(
data: { certificateAuthority } `/api/v1/pki/ca/${body.type}/${caName}`,
} = await apiRequest.patch<{ certificateAuthority: TUnifiedCertificateAuthority }>(
`/api/v1/pki/ca/${body.type}/${id}`,
body body
); );
return certificateAuthority; return data;
}, },
onSuccess: ({ projectId, type }) => { onSuccess: ({ projectId, type }) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
@@ -65,11 +63,16 @@ export const useCreateUnifiedCa = () => {
export const useDeleteUnifiedCa = () => { export const useDeleteUnifiedCa = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<TUnifiedCertificateAuthority, object, TDeleteUnifiedCertificateAuthorityDTO>({ return useMutation<TUnifiedCertificateAuthority, object, TDeleteUnifiedCertificateAuthorityDTO>({
mutationFn: async ({ caId, type }) => { mutationFn: async ({ caName, type, projectId }) => {
const { const {
data: { certificateAuthority } data: { certificateAuthority }
} = await apiRequest.delete<{ certificateAuthority: TUnifiedCertificateAuthority }>( } = await apiRequest.delete<{ certificateAuthority: TUnifiedCertificateAuthority }>(
`/api/v1/pki/ca/${type}/${caId}` `/api/v1/pki/ca/${type}/${caName}`,
{
data: {
projectId
}
}
); );
return certificateAuthority; return certificateAuthority;
}, },
+19 -13
View File
@@ -8,7 +8,7 @@ import { TCertificateAuthority, TUnifiedCertificateAuthority } from "./types";
export const caKeys = { export const caKeys = {
getCaById: (caId: string) => [{ caId }, "ca"], getCaById: (caId: string) => [{ caId }, "ca"],
getCaByTypeAndId: (type: CaType, caId: string) => [{ type, caId }, "ca"], getCaByNameAndProjectId: (caName: string, projectId: string) => [{ caName, projectId }, "ca"],
listCasByTypeAndProjectId: (type: CaType, projectId: string) => [{ type, projectId }, "cas"], listCasByTypeAndProjectId: (type: CaType, projectId: string) => [{ type, projectId }, "cas"],
listCasByProjectId: (projectId: string) => [{ projectId }, "cas"], listCasByProjectId: (projectId: string) => [{ projectId }, "cas"],
getCaCerts: (caId: string) => [{ caId }, "ca-cert"], getCaCerts: (caId: string) => [{ caId }, "ca-cert"],
@@ -20,18 +20,24 @@ export const caKeys = {
getCaEstConfig: (caId: string) => [{ caId }, "ca-est-config"] getCaEstConfig: (caId: string) => [{ caId }, "ca-est-config"]
}; };
export const useGetCaByTypeAndId = (type: CaType, caId: string) => { export const useGetCa = ({
caName,
projectId,
type
}: {
caName: string;
projectId: string;
type: CaType;
}) => {
return useQuery({ return useQuery({
queryKey: caKeys.getCaByTypeAndId(type, caId), queryKey: caKeys.getCaByNameAndProjectId(caName, projectId),
queryFn: async () => { queryFn: async () => {
const { const { data } = await apiRequest.get<TUnifiedCertificateAuthority>(
data: { certificateAuthority } `/api/v1/pki/ca/${type}/${caName}?projectId=${projectId}`
} = await apiRequest.get<{ certificateAuthority: TUnifiedCertificateAuthority }>(
`/api/v1/pki/ca/${type}/${caId}`
); );
return certificateAuthority; return data;
}, },
enabled: Boolean(caId) enabled: Boolean(caName && projectId && type)
}); });
}; };
@@ -39,11 +45,11 @@ export const useListCasByTypeAndProjectId = (type: CaType, projectId: string) =>
return useQuery({ return useQuery({
queryKey: caKeys.listCasByTypeAndProjectId(type, projectId), queryKey: caKeys.listCasByTypeAndProjectId(type, projectId),
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ const { data } = await apiRequest.get<TUnifiedCertificateAuthority[]>(
certificateAuthorities: TUnifiedCertificateAuthority[]; `/api/v1/pki/ca/${type}?projectId=${projectId}`
}>(`/api/v1/pki/ca/${type}?projectId=${projectId}`); );
return data.certificateAuthorities; return data;
} }
}); });
}; };
+5 -4
View File
@@ -7,7 +7,7 @@ export type TAcmeCertificateAuthority = {
type: CaType.ACME; type: CaType.ACME;
status: CaStatus; status: CaStatus;
name: string; name: string;
disableDirectIssuance: boolean; enableDirectIssuance: boolean;
configuration: { configuration: {
dnsAppConnectionId: string; dnsAppConnectionId: string;
dnsProviderConfig: { dnsProviderConfig: {
@@ -25,7 +25,7 @@ export type TInternalCertificateAuthority = {
type: CaType.INTERNAL; type: CaType.INTERNAL;
status: CaStatus; status: CaStatus;
name: string; name: string;
disableDirectIssuance: boolean; enableDirectIssuance: boolean;
configuration: { configuration: {
type: InternalCaType; type: InternalCaType;
friendlyName?: string; friendlyName?: string;
@@ -52,12 +52,13 @@ export type TUnifiedCertificateAuthority =
export type TCreateUnifiedCertificateAuthorityDTO = Omit<TUnifiedCertificateAuthority, "id">; export type TCreateUnifiedCertificateAuthorityDTO = Omit<TUnifiedCertificateAuthority, "id">;
export type TUpdateUnifiedCertificateAuthorityDTO = Partial<TUnifiedCertificateAuthority> & { export type TUpdateUnifiedCertificateAuthorityDTO = Partial<TUnifiedCertificateAuthority> & {
id: string; caName: string;
projectId: string;
type: CaType; type: CaType;
}; };
export type TDeleteUnifiedCertificateAuthorityDTO = { export type TDeleteUnifiedCertificateAuthorityDTO = {
caId: string; caName: string;
type: CaType; type: CaType;
projectId: string; projectId: string;
}; };
@@ -23,7 +23,7 @@ import {
CaStatus, CaStatus,
CaType, CaType,
useCreateUnifiedCa, useCreateUnifiedCa,
useGetCaByTypeAndId, useGetCa,
useUpdateUnifiedCa useUpdateUnifiedCa
} from "@app/hooks/api/ca"; } from "@app/hooks/api/ca";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -35,7 +35,7 @@ const schema = z
name: slugSchema({ name: slugSchema({
field: "Name" field: "Name"
}), }),
disableDirectIssuance: z.boolean(), enableDirectIssuance: z.boolean(),
status: z.nativeEnum(CaStatus), status: z.nativeEnum(CaStatus),
configuration: z.object({ configuration: z.object({
dnsAppConnection: z.object({ dnsAppConnection: z.object({
@@ -65,10 +65,11 @@ const caTypes = [{ label: "ACME", value: CaType.ACME }];
export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => { export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const { data: ca } = useGetCaByTypeAndId( const { data: ca } = useGetCa({
(popUp?.ca?.data as { type: CaType })?.type || "", caName: (popUp?.ca?.data as { name: string })?.name || "",
(popUp?.ca?.data as { caId: string })?.caId || "" projectId: currentWorkspace?.id || "",
); type: (popUp?.ca?.data as { type: CaType })?.type || ""
});
const { mutateAsync: createMutateAsync } = useCreateUnifiedCa(); const { mutateAsync: createMutateAsync } = useCreateUnifiedCa();
const { mutateAsync: updateMutateAsync } = useUpdateUnifiedCa(); const { mutateAsync: updateMutateAsync } = useUpdateUnifiedCa();
@@ -85,7 +86,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: CaType.ACME, type: CaType.ACME,
name: "", name: "",
status: CaStatus.ACTIVE, status: CaStatus.ACTIVE,
disableDirectIssuance: false, enableDirectIssuance: true,
configuration: { configuration: {
dnsAppConnection: { dnsAppConnection: {
id: "", id: "",
@@ -122,7 +123,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: ca.type, type: ca.type,
name: ca.name, name: ca.name,
status: ca.status, status: ca.status,
disableDirectIssuance: ca.disableDirectIssuance, enableDirectIssuance: ca.enableDirectIssuance,
configuration: { configuration: {
dnsAppConnection: { dnsAppConnection: {
id: ca.configuration.dnsAppConnectionId, id: ca.configuration.dnsAppConnectionId,
@@ -142,7 +143,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
type: CaType.ACME, type: CaType.ACME,
name: "", name: "",
status: CaStatus.ACTIVE, status: CaStatus.ACTIVE,
disableDirectIssuance: false, enableDirectIssuance: true,
configuration: { configuration: {
dnsAppConnection: { dnsAppConnection: {
id: "", id: "",
@@ -162,7 +163,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const onFormSubmit = async ({ const onFormSubmit = async ({
type, type,
name, name,
disableDirectIssuance, enableDirectIssuance,
status, status,
configuration configuration
}: FormData) => { }: FormData) => {
@@ -171,12 +172,12 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
if (ca && type !== CaType.INTERNAL) { if (ca && type !== CaType.INTERNAL) {
await updateMutateAsync({ await updateMutateAsync({
id: ca.id, caName: ca.name,
projectId: currentWorkspace.id, projectId: currentWorkspace.id,
name, name,
type, type,
status, status,
disableDirectIssuance, enableDirectIssuance,
configuration: { configuration: {
...configuration, ...configuration,
dnsAppConnectionId: configuration.dnsAppConnection.id dnsAppConnectionId: configuration.dnsAppConnection.id
@@ -188,7 +189,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
name, name,
type, type,
status, status,
disableDirectIssuance, enableDirectIssuance,
configuration: { configuration: {
...configuration, ...configuration,
dnsAppConnectionId: configuration.dnsAppConnection.id dnsAppConnectionId: configuration.dnsAppConnection.id
@@ -369,7 +370,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
)} )}
<Controller <Controller
control={control} control={control}
name="disableDirectIssuance" name="enableDirectIssuance"
render={({ field, fieldState: { error } }) => { render={({ field, fieldState: { error } }) => {
return ( return (
<FormControl isError={Boolean(error)} errorText={error?.message} className="my-8"> <FormControl isError={Boolean(error)} errorText={error?.message} className="my-8">
@@ -378,7 +379,7 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
onCheckedChange={(value) => field.onChange(value)} onCheckedChange={(value) => field.onChange(value)}
isChecked={field.value} isChecked={field.value}
> >
<p className="w-full">Disable Direct Issuance</p> <p className="w-full">Enable Direct Issuance</p>
</Switch> </Switch>
</FormControl> </FormControl>
); );
@@ -24,11 +24,11 @@ export const ExternalCaSection = () => {
"upgradePlan" "upgradePlan"
] as const); ] as const);
const onRemoveCaSubmit = async (caId: string, type: CaType) => { const onRemoveCaSubmit = async (caName: string, type: CaType) => {
try { try {
if (!currentWorkspace?.id) return; if (!currentWorkspace?.id) return;
await deleteCa({ caId, type, projectId: currentWorkspace.id }); await deleteCa({ caName, type, projectId: currentWorkspace.id });
createNotification({ createNotification({
text: "Successfully deleted CA", text: "Successfully deleted CA",
@@ -45,18 +45,18 @@ export const ExternalCaSection = () => {
}; };
const onUpdateCaStatus = async ({ const onUpdateCaStatus = async ({
caId, name,
type, type,
status status
}: { }: {
caId: string; name: string;
type: CaType; type: CaType;
status: CaStatus; status: CaStatus;
}) => { }) => {
try { try {
if (!currentWorkspace?.slug) return; if (!currentWorkspace?.slug) return;
await updateCa({ id: caId, type, status }); await updateCa({ caName: name, type, status, projectId: currentWorkspace.id });
createNotification({ createNotification({
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`, text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
@@ -105,7 +105,7 @@ export const ExternalCaSection = () => {
deleteKey="confirm" deleteKey="confirm"
onDeleteApproved={() => onDeleteApproved={() =>
onRemoveCaSubmit( onRemoveCaSubmit(
(popUp?.deleteCa?.data as { caId: string })?.caId, (popUp?.deleteCa?.data as { name: string })?.name,
(popUp?.deleteCa?.data as { type: CaType })?.type (popUp?.deleteCa?.data as { type: CaType })?.type
) )
} }
@@ -127,7 +127,7 @@ export const ExternalCaSection = () => {
deleteKey="confirm" deleteKey="confirm"
onDeleteApproved={() => onDeleteApproved={() =>
onUpdateCaStatus( onUpdateCaStatus(
popUp?.caStatus?.data as { caId: string; type: CaType; status: CaStatus } popUp?.caStatus?.data as { name: string; type: CaType; status: CaStatus }
) )
} }
/> />
@@ -35,7 +35,7 @@ type Props = {
handlePopUpOpen: ( handlePopUpOpen: (
popUpName: keyof UsePopUpState<["ca", "deleteCa", "caStatus", "upgradePlan"]>, popUpName: keyof UsePopUpState<["ca", "deleteCa", "caStatus", "upgradePlan"]>,
data?: { data?: {
caId?: string; name?: string;
type?: CaType; type?: CaType;
status?: CaStatus; status?: CaStatus;
description?: string; description?: string;
@@ -71,7 +71,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
key={`ca-${ca.id}`} key={`ca-${ca.id}`}
onClick={() => { onClick={() => {
handlePopUpOpen("ca", { handlePopUpOpen("ca", {
caId: ca.id, name: ca.name,
type: ca.type type: ca.type
}); });
}} }}
@@ -105,7 +105,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("ca", { handlePopUpOpen("ca", {
caId: ca.id, name: ca.name,
type: ca.type type: ca.type
}); });
}} }}
@@ -130,7 +130,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("caStatus", { handlePopUpOpen("caStatus", {
caId: ca.id, name: ca.name,
type: ca.type, type: ca.type,
status: status:
ca.status === CaStatus.ACTIVE ca.status === CaStatus.ACTIVE
@@ -158,7 +158,7 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handlePopUpOpen("deleteCa", { handlePopUpOpen("deleteCa", {
caId: ca.id, name: ca.name,
type: ca.type type: ca.type
}); });
}} }}