mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 16:27:46 +00:00
misc: added internal CA route
This commit is contained in:
Vendored
+1
-1
@@ -53,7 +53,7 @@ import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
|||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
import { TCertificateServiceFactory } from "@app/services/certificate/certificate-service";
|
import { TCertificateServiceFactory } from "@app/services/certificate/certificate-service";
|
||||||
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
|
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
|
||||||
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal-certificate-authority-service";
|
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
|
||||||
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
||||||
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
|
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
|
||||||
import { TExternalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
import { TExternalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
||||||
|
|||||||
Vendored
+8
@@ -68,6 +68,9 @@ import {
|
|||||||
TDynamicSecrets,
|
TDynamicSecrets,
|
||||||
TDynamicSecretsInsert,
|
TDynamicSecretsInsert,
|
||||||
TDynamicSecretsUpdate,
|
TDynamicSecretsUpdate,
|
||||||
|
TExternalCertificateAuthorities,
|
||||||
|
TExternalCertificateAuthoritiesInsert,
|
||||||
|
TExternalCertificateAuthoritiesUpdate,
|
||||||
TExternalGroupOrgRoleMappings,
|
TExternalGroupOrgRoleMappings,
|
||||||
TExternalGroupOrgRoleMappingsInsert,
|
TExternalGroupOrgRoleMappingsInsert,
|
||||||
TExternalGroupOrgRoleMappingsUpdate,
|
TExternalGroupOrgRoleMappingsUpdate,
|
||||||
@@ -543,6 +546,11 @@ declare module "knex/types/tables" {
|
|||||||
TInternalCertificateAuthoritiesInsert,
|
TInternalCertificateAuthoritiesInsert,
|
||||||
TInternalCertificateAuthoritiesUpdate
|
TInternalCertificateAuthoritiesUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.ExternalCertificateAuthority]: KnexOriginal.CompositeTableType<
|
||||||
|
TExternalCertificateAuthorities,
|
||||||
|
TExternalCertificateAuthoritiesInsert,
|
||||||
|
TExternalCertificateAuthoritiesUpdate
|
||||||
|
>;
|
||||||
[TableName.Certificate]: KnexOriginal.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
|
[TableName.Certificate]: KnexOriginal.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
|
||||||
[TableName.CertificateTemplate]: KnexOriginal.CompositeTableType<
|
[TableName.CertificateTemplate]: KnexOriginal.CompositeTableType<
|
||||||
TCertificateTemplates,
|
TCertificateTemplates,
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.uuid("certificateAuthorityId").nullable();
|
t.uuid("certificateAuthorityId").nullable();
|
||||||
});
|
});
|
||||||
|
|
||||||
await knex(TableName.InternalCertificateAuthority).insert(knex(TableName.CertificateAuthority).select("*"));
|
const caRows = await knex(TableName.CertificateAuthority).select("*");
|
||||||
|
if (caRows.length > 0) {
|
||||||
|
// @ts-expect-error intentional: migration
|
||||||
|
await knex(TableName.InternalCertificateAuthority).insert(caRows);
|
||||||
|
}
|
||||||
|
|
||||||
await knex(TableName.InternalCertificateAuthority).update("certificateAuthorityId", knex.ref("id"));
|
await knex(TableName.InternalCertificateAuthority).update("certificateAuthorityId", knex.ref("id"));
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.InternalCertificateAuthority, (t) => {
|
await knex.schema.alterTable(TableName.InternalCertificateAuthority, (t) => {
|
||||||
@@ -59,7 +64,20 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
|
|
||||||
if (!hasExternalCATable) {
|
if (!hasExternalCATable) {
|
||||||
await knex.schema.createTable(TableName.ExternalCertificateAuthority, (t) => {
|
await knex.schema.createTable(TableName.ExternalCertificateAuthority, (t) => {
|
||||||
//
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("type").notNullable();
|
||||||
|
t.string("name").notNullable();
|
||||||
|
t.uuid("appConnectionId").nullable();
|
||||||
|
t.foreign("appConnectionId").references("id").inTable(TableName.AppConnection);
|
||||||
|
t.uuid("dnsAppConnectionId").nullable();
|
||||||
|
t.foreign("dnsAppConnectionId").references("id").inTable(TableName.AppConnection);
|
||||||
|
t.uuid("certificateAuthorityId")
|
||||||
|
.notNullable()
|
||||||
|
.references("id")
|
||||||
|
.inTable(TableName.CertificateAuthority)
|
||||||
|
.onDelete("CASCADE");
|
||||||
|
t.binary("credentials");
|
||||||
|
t.json("configuration");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +131,7 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
"notAfter" = ica."notAfter",
|
"notAfter" = ica."notAfter",
|
||||||
"activeCaCertId" = ica."activeCaCertId"
|
"activeCaCertId" = ica."activeCaCertId"
|
||||||
FROM ${TableName.InternalCertificateAuthority} ica
|
FROM ${TableName.InternalCertificateAuthority} ica
|
||||||
WHERE ca.id = ica.id
|
WHERE ca.id = ica."certificateAuthorityId"
|
||||||
`);
|
`);
|
||||||
|
|
||||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { zodBuffer } from "@app/lib/zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const ExternalCertificateAuthoritiesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
type: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
appConnectionId: z.string().uuid().nullable().optional(),
|
||||||
|
dnsAppConnectionId: z.string().uuid().nullable().optional(),
|
||||||
|
certificateAuthorityId: z.string().uuid(),
|
||||||
|
credentials: zodBuffer.nullable().optional(),
|
||||||
|
configuration: z.unknown().nullable().optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TExternalCertificateAuthorities = z.infer<typeof ExternalCertificateAuthoritiesSchema>;
|
||||||
|
export type TExternalCertificateAuthoritiesInsert = Omit<
|
||||||
|
z.input<typeof ExternalCertificateAuthoritiesSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TExternalCertificateAuthoritiesUpdate = Partial<
|
||||||
|
Omit<z.input<typeof ExternalCertificateAuthoritiesSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
||||||
@@ -20,6 +20,7 @@ export * from "./certificate-templates";
|
|||||||
export * from "./certificates";
|
export * from "./certificates";
|
||||||
export * from "./dynamic-secret-leases";
|
export * from "./dynamic-secret-leases";
|
||||||
export * from "./dynamic-secrets";
|
export * from "./dynamic-secrets";
|
||||||
|
export * from "./external-certificate-authorities";
|
||||||
export * from "./external-group-org-role-mappings";
|
export * from "./external-group-org-role-mappings";
|
||||||
export * from "./external-kms";
|
export * from "./external-kms";
|
||||||
export * from "./gateways";
|
export * from "./gateways";
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
|
|||||||
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
|
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
|
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||||
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
|
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
|
||||||
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { isCertChainValid } from "@app/services/certificate/certificate-fns";
|
|||||||
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
||||||
import { getCaCertChain, getCaCertChains } from "@app/services/certificate-authority/certificate-authority-fns";
|
import { getCaCertChain, getCaCertChains } from "@app/services/certificate-authority/certificate-authority-fns";
|
||||||
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal-certificate-authority-service";
|
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
|
||||||
import { TCertificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal";
|
import { TCertificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal";
|
||||||
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
|||||||
@@ -133,8 +133,8 @@ import { certificateAuthorityDALFactory } from "@app/services/certificate-author
|
|||||||
import { certificateAuthorityQueueFactory } from "@app/services/certificate-authority/certificate-authority-queue";
|
import { certificateAuthorityQueueFactory } from "@app/services/certificate-authority/certificate-authority-queue";
|
||||||
import { certificateAuthoritySecretDALFactory } from "@app/services/certificate-authority/certificate-authority-secret-dal";
|
import { certificateAuthoritySecretDALFactory } from "@app/services/certificate-authority/certificate-authority-secret-dal";
|
||||||
import { certificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
|
import { certificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
|
||||||
import { internalCertificateAuthorityDALFactory } from "@app/services/certificate-authority/internal-certificate-authority-dal";
|
import { internalCertificateAuthorityDALFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-dal";
|
||||||
import { internalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal-certificate-authority-service";
|
import { internalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
|
||||||
import { certificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal";
|
import { certificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal";
|
||||||
import { certificateTemplateEstConfigDALFactory } from "@app/services/certificate-template/certificate-template-est-config-dal";
|
import { certificateTemplateEstConfigDALFactory } from "@app/services/certificate-template/certificate-template-est-config-dal";
|
||||||
import { certificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
import { certificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
|||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import { CaRenewalType, CaStatus, CaType } from "@app/services/certificate-authority/certificate-authority-types";
|
import {
|
||||||
|
CaRenewalType,
|
||||||
|
CaStatus,
|
||||||
|
InternalCaType
|
||||||
|
} from "@app/services/certificate-authority/certificate-authority-enums";
|
||||||
import {
|
import {
|
||||||
validateAltNamesField,
|
validateAltNamesField,
|
||||||
validateCaDateField
|
validateCaDateField
|
||||||
@@ -34,7 +38,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
|||||||
body: z
|
body: z
|
||||||
.object({
|
.object({
|
||||||
projectSlug: z.string().trim().describe(CERTIFICATE_AUTHORITIES.CREATE.projectSlug),
|
projectSlug: z.string().trim().describe(CERTIFICATE_AUTHORITIES.CREATE.projectSlug),
|
||||||
type: z.nativeEnum(CaType).describe(CERTIFICATE_AUTHORITIES.CREATE.type),
|
type: z.nativeEnum(InternalCaType).describe(CERTIFICATE_AUTHORITIES.CREATE.type),
|
||||||
friendlyName: z.string().optional().describe(CERTIFICATE_AUTHORITIES.CREATE.friendlyName),
|
friendlyName: z.string().optional().describe(CERTIFICATE_AUTHORITIES.CREATE.friendlyName),
|
||||||
commonName: z.string().trim().describe(CERTIFICATE_AUTHORITIES.CREATE.commonName),
|
commonName: z.string().trim().describe(CERTIFICATE_AUTHORITIES.CREATE.commonName),
|
||||||
organization: z.string().trim().describe(CERTIFICATE_AUTHORITIES.CREATE.organization),
|
organization: z.string().trim().describe(CERTIFICATE_AUTHORITIES.CREATE.organization),
|
||||||
@@ -79,6 +83,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
|||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
isInternal: false,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.body
|
...req.body
|
||||||
});
|
});
|
||||||
@@ -209,6 +214,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
|||||||
caId: req.params.caId,
|
caId: req.params.caId,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
|
isInternal: false,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.body
|
...req.body
|
||||||
|
|||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { ApiDocsTags } from "@app/lib/api-docs";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import { CaType } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||||
|
import {
|
||||||
|
TCertificateAuthority,
|
||||||
|
TCertificateAuthorityInput
|
||||||
|
} from "@app/services/certificate-authority/certificate-authority-types";
|
||||||
|
|
||||||
|
export const registerCertificateAuthorityEndpoints = <
|
||||||
|
T extends TCertificateAuthority,
|
||||||
|
I extends TCertificateAuthorityInput
|
||||||
|
>({
|
||||||
|
server,
|
||||||
|
caType,
|
||||||
|
createSchema,
|
||||||
|
updateSchema,
|
||||||
|
responseSchema
|
||||||
|
}: {
|
||||||
|
caType: CaType;
|
||||||
|
server: FastifyZodProvider;
|
||||||
|
createSchema: z.ZodType<{
|
||||||
|
name: string;
|
||||||
|
projectId: string;
|
||||||
|
configuration: I["configuration"];
|
||||||
|
disableDirectIssuance: boolean;
|
||||||
|
}>;
|
||||||
|
updateSchema: z.ZodType<{
|
||||||
|
name?: string;
|
||||||
|
configuration?: I["configuration"];
|
||||||
|
disableDirectIssuance?: boolean;
|
||||||
|
}>;
|
||||||
|
responseSchema: z.ZodTypeAny;
|
||||||
|
}) => {
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
hide: false,
|
||||||
|
tags: [ApiDocsTags.PkiCertificateAuthorities],
|
||||||
|
querystring: z.object({
|
||||||
|
projectId: z.string().trim().min(1, "Project ID required")
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ certificateAuthorities: responseSchema.array() })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const {
|
||||||
|
query: { projectId }
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
const certificateAuthorities = (await server.services.certificateAuthority.listCertificateAuthoritiesByProjectId(
|
||||||
|
{ projectId, type: caType },
|
||||||
|
req.permission
|
||||||
|
)) as T[];
|
||||||
|
|
||||||
|
// await server.services.auditLog.createAuditLog({
|
||||||
|
// ...req.auditLogInfo,
|
||||||
|
// projectId,
|
||||||
|
// event: {
|
||||||
|
// type: EventType.GET_SECRET_SYNCS,
|
||||||
|
// metadata: {
|
||||||
|
// destination,
|
||||||
|
// count: secretSyncs.length,
|
||||||
|
// syncIds: secretSyncs.map((connection) => connection.id)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
return { certificateAuthorities };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:certificateAuthorityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
hide: false,
|
||||||
|
tags: [ApiDocsTags.PkiCertificateAuthorities],
|
||||||
|
params: z.object({
|
||||||
|
certificateAuthorityId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ certificateAuthority: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { certificateAuthorityId } = req.params;
|
||||||
|
|
||||||
|
const certificateAuthority = (await server.services.certificateAuthority.findCertificateAuthorityById(
|
||||||
|
{ certificateAuthorityId, type: caType },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
// await server.services.auditLog.createAuditLog({
|
||||||
|
// ...req.auditLogInfo,
|
||||||
|
// projectId: secretSync.projectId,
|
||||||
|
// event: {
|
||||||
|
// type: EventType.GET_SECRET_SYNC,
|
||||||
|
// metadata: {
|
||||||
|
// syncId,
|
||||||
|
// destination
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
return { certificateAuthority };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
hide: false,
|
||||||
|
tags: [ApiDocsTags.PkiCertificateAuthorities],
|
||||||
|
body: createSchema,
|
||||||
|
response: {
|
||||||
|
200: z.object({ certificateAuthority: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const certificateAuthority = (await server.services.certificateAuthority.createCertificateAuthority(
|
||||||
|
{ ...req.body, type: caType },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
// await server.services.auditLog.createAuditLog({
|
||||||
|
// ...req.auditLogInfo,
|
||||||
|
// projectId: secretSync.projectId,
|
||||||
|
// event: {
|
||||||
|
// type: EventType.CREATE_SECRET_SYNC,
|
||||||
|
// metadata: {
|
||||||
|
// syncId: secretSync.id,
|
||||||
|
// destination,
|
||||||
|
// ...req.body
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
return { certificateAuthority };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/:certificateAuthorityId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
hide: false,
|
||||||
|
tags: [ApiDocsTags.PkiCertificateAuthorities],
|
||||||
|
params: z.object({
|
||||||
|
certificateAuthorityId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
body: updateSchema,
|
||||||
|
response: {
|
||||||
|
200: z.object({ certificateAuthority: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { certificateAuthorityId } = req.params;
|
||||||
|
|
||||||
|
const certificateAuthority = (await server.services.certificateAuthority.updateCertificateAuthority(
|
||||||
|
{ ...req.body, id: certificateAuthorityId, type: caType },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
// await server.services.auditLog.createAuditLog({
|
||||||
|
// ...req.auditLogInfo,
|
||||||
|
// projectId: certificateAuthority.projectId,
|
||||||
|
// event: {
|
||||||
|
// type: EventType.UPDATE_SECRET_SYNC,
|
||||||
|
// metadata: {
|
||||||
|
// syncId,
|
||||||
|
// destination,
|
||||||
|
// ...req.body
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
return { certificateAuthority };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/:certificateAuthorityId`,
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
hide: false,
|
||||||
|
tags: [ApiDocsTags.PkiCertificateAuthorities],
|
||||||
|
params: z.object({
|
||||||
|
certificateAuthorityId: z.string().uuid()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ certificateAuthority: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { certificateAuthorityId } = req.params;
|
||||||
|
|
||||||
|
const certificateAuthority = (await server.services.certificateAuthority.deleteCertificateAuthority(
|
||||||
|
{ id: certificateAuthorityId, type: caType },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
// await server.services.auditLog.createAuditLog({
|
||||||
|
// ...req.auditLogInfo,
|
||||||
|
// orgId: req.permission.orgId,
|
||||||
|
// event: {
|
||||||
|
// type: EventType.DELETE_SECRET_SYNC,
|
||||||
|
// metadata: {
|
||||||
|
// destination,
|
||||||
|
// syncId,
|
||||||
|
// removeSecrets
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
return { certificateAuthority };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { CaType } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||||
|
|
||||||
|
import { registerInternalCertificateAuthorityRouter } from "./internal-certificate-authority-router";
|
||||||
|
|
||||||
|
export * from "./internal-certificate-authority-router";
|
||||||
|
|
||||||
|
export const CERTIFICATE_AUTHORITY_REGISTER_ROUTER_MAP: Record<CaType, (server: FastifyZodProvider) => Promise<void>> =
|
||||||
|
{
|
||||||
|
[CaType.INTERNAL]: registerInternalCertificateAuthorityRouter,
|
||||||
|
[CaType.ACME]: registerInternalCertificateAuthorityRouter
|
||||||
|
};
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
import { CaType } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||||
|
import {
|
||||||
|
CreateInternalCertificateAuthoritySchema,
|
||||||
|
InternalCertificateAuthoritySchema,
|
||||||
|
UpdateInternalCertificateAuthoritySchema
|
||||||
|
} from "@app/services/certificate-authority/internal/internal-certificate-authority-schemas";
|
||||||
|
|
||||||
|
import { registerCertificateAuthorityEndpoints } from "./certificate-authority-endpoints";
|
||||||
|
|
||||||
|
export const registerInternalCertificateAuthorityRouter = async (server: FastifyZodProvider) => {
|
||||||
|
registerCertificateAuthorityEndpoints({
|
||||||
|
caType: CaType.INTERNAL,
|
||||||
|
server,
|
||||||
|
responseSchema: InternalCertificateAuthoritySchema,
|
||||||
|
createSchema: CreateInternalCertificateAuthoritySchema,
|
||||||
|
updateSchema: UpdateInternalCertificateAuthoritySchema
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -10,6 +10,7 @@ import { registerAdminRouter } from "./admin-router";
|
|||||||
import { registerAuthRoutes } from "./auth-router";
|
import { registerAuthRoutes } from "./auth-router";
|
||||||
import { registerProjectBotRouter } from "./bot-router";
|
import { registerProjectBotRouter } from "./bot-router";
|
||||||
import { registerCaRouter } from "./certificate-authority-router";
|
import { registerCaRouter } from "./certificate-authority-router";
|
||||||
|
import { CERTIFICATE_AUTHORITY_REGISTER_ROUTER_MAP } from "./certificate-authority-routers";
|
||||||
import { registerCertRouter } from "./certificate-router";
|
import { registerCertRouter } from "./certificate-router";
|
||||||
import { registerCertificateTemplateRouter } from "./certificate-template-router";
|
import { registerCertificateTemplateRouter } from "./certificate-template-router";
|
||||||
import { registerExternalGroupOrgRoleMappingRouter } from "./external-group-org-role-mapping-router";
|
import { registerExternalGroupOrgRoleMappingRouter } from "./external-group-org-role-mapping-router";
|
||||||
@@ -102,6 +103,16 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(
|
await server.register(
|
||||||
async (pkiRouter) => {
|
async (pkiRouter) => {
|
||||||
await pkiRouter.register(registerCaRouter, { prefix: "/ca" });
|
await pkiRouter.register(registerCaRouter, { prefix: "/ca" });
|
||||||
|
await pkiRouter.register(
|
||||||
|
async (caRouter) => {
|
||||||
|
for await (const [caType, router] of Object.entries(CERTIFICATE_AUTHORITY_REGISTER_ROUTER_MAP)) {
|
||||||
|
await caRouter.register(router, { prefix: `/${caType}` });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prefix: "/ca"
|
||||||
|
}
|
||||||
|
);
|
||||||
await pkiRouter.register(registerCertRouter, { prefix: "/certificates" });
|
await pkiRouter.register(registerCertRouter, { prefix: "/certificates" });
|
||||||
await pkiRouter.register(registerCertificateTemplateRouter, { prefix: "/certificate-templates" });
|
await pkiRouter.register(registerCertificateTemplateRouter, { prefix: "/certificate-templates" });
|
||||||
await pkiRouter.register(registerPkiAlertRouter, { prefix: "/alerts" });
|
await pkiRouter.register(registerPkiAlertRouter, { prefix: "/alerts" });
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { slugSchema } from "@app/server/lib/schemas";
|
|||||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
|
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||||
import { sanitizedCertificateTemplate } from "@app/services/certificate-template/certificate-template-schema";
|
import { sanitizedCertificateTemplate } from "@app/services/certificate-template/certificate-template-schema";
|
||||||
import { sanitizedPkiSubscriber } from "@app/services/pki-subscriber/pki-subscriber-schema";
|
import { sanitizedPkiSubscriber } from "@app/services/pki-subscriber/pki-subscriber-schema";
|
||||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
import { ProjectFilterType } from "@app/services/project/project-types";
|
||||||
|
|||||||
@@ -125,6 +125,11 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.CertificateAuthority}.id`,
|
`${TableName.CertificateAuthority}.id`,
|
||||||
`${TableName.InternalCertificateAuthority}.certificateAuthorityId`
|
`${TableName.InternalCertificateAuthority}.certificateAuthorityId`
|
||||||
)
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.ExternalCertificateAuthority,
|
||||||
|
`${TableName.CertificateAuthority}.id`,
|
||||||
|
`${TableName.ExternalCertificateAuthority}.certificateAuthorityId`
|
||||||
|
)
|
||||||
.where(filter)
|
.where(filter)
|
||||||
.select(selectAllTableCols(TableName.CertificateAuthority))
|
.select(selectAllTableCols(TableName.CertificateAuthority))
|
||||||
.select(
|
.select(
|
||||||
@@ -150,6 +155,14 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
|
|||||||
.ref("certificateAuthorityId")
|
.ref("certificateAuthorityId")
|
||||||
.withSchema(TableName.InternalCertificateAuthority)
|
.withSchema(TableName.InternalCertificateAuthority)
|
||||||
.as("internalCertificateAuthorityId")
|
.as("internalCertificateAuthorityId")
|
||||||
|
)
|
||||||
|
.select(
|
||||||
|
db.ref("id").withSchema(TableName.ExternalCertificateAuthority).as("externalCaId"),
|
||||||
|
db.ref("type").withSchema(TableName.ExternalCertificateAuthority).as("externalType"),
|
||||||
|
db
|
||||||
|
.ref("certificateAuthorityId")
|
||||||
|
.withSchema(TableName.ExternalCertificateAuthority)
|
||||||
|
.as("externalCertificateAuthorityId")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (limit) void query.limit(limit);
|
if (limit) void query.limit(limit);
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
export enum CaType {
|
||||||
|
INTERNAL = "internal",
|
||||||
|
ACME = "acme"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum InternalCaType {
|
||||||
|
ROOT = "root",
|
||||||
|
INTERMEDIATE = "intermediate"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CaStatus {
|
||||||
|
ACTIVE = "active",
|
||||||
|
DISABLED = "disabled",
|
||||||
|
PENDING_CERTIFICATE = "pending-certificate"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum CaRenewalType {
|
||||||
|
EXISTING = "existing"
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
TGetCaCertChainsDTO,
|
TGetCaCertChainsDTO,
|
||||||
TGetCaCredentialsDTO,
|
TGetCaCredentialsDTO,
|
||||||
TRebuildCaCrlDTO
|
TRebuildCaCrlDTO
|
||||||
} from "./certificate-authority-types";
|
} from "./internal/internal-certificate-authority-types";
|
||||||
|
|
||||||
/* eslint-disable no-bitwise */
|
/* eslint-disable no-bitwise */
|
||||||
export const createSerialNumber = () => {
|
export const createSerialNumber = () => {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificat
|
|||||||
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
||||||
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 "./certificate-authority-types";
|
import { TRotateCaCrlTriggerDTO } from "./internal/internal-certificate-authority-types";
|
||||||
|
|
||||||
type TCertificateAuthorityQueueFactoryDep = {
|
type TCertificateAuthorityQueueFactoryDep = {
|
||||||
// TODO: Pick
|
// TODO: Pick
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import z from "zod";
|
||||||
|
|
||||||
|
import { CertificateAuthoritiesSchema } from "@app/db/schemas";
|
||||||
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
|
|
||||||
|
import { CaType } from "./certificate-authority-enums";
|
||||||
|
|
||||||
|
// SHEEN TODO: add description mapping using type
|
||||||
|
export const BaseCertificateAuthoritySchema = (type: CaType) =>
|
||||||
|
CertificateAuthoritiesSchema.pick({
|
||||||
|
projectId: true,
|
||||||
|
disableDirectIssuance: true,
|
||||||
|
id: true
|
||||||
|
}).extend({
|
||||||
|
name: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GenericCreateCertificateAuthorityFieldsSchema = (type: CaType) =>
|
||||||
|
z.object({
|
||||||
|
name: slugSchema({ field: "name" }),
|
||||||
|
projectId: z.string().trim().min(1, "Project ID required"),
|
||||||
|
disableDirectIssuance: z.boolean()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GenericUpdateCertificateAuthorityFieldsSchema = (type: CaType) =>
|
||||||
|
z.object({
|
||||||
|
name: slugSchema({ field: "name" }).optional(),
|
||||||
|
disableDirectIssuance: z.boolean().optional()
|
||||||
|
});
|
||||||
@@ -1,5 +1,300 @@
|
|||||||
type TCertificateAuthorityServiceFactoryDep = {};
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { ActionProjectType, ProjectType, TableName } from "@app/db/schemas";
|
||||||
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
|
|
||||||
|
import { TProjectDALFactory } from "../project/project-dal";
|
||||||
|
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
||||||
|
import { CaType } from "./certificate-authority-enums";
|
||||||
|
import {
|
||||||
|
TCertificateAuthority,
|
||||||
|
TCreateCertificateAuthorityDTO,
|
||||||
|
TUpdateCertificateAuthorityDTO
|
||||||
|
} from "./certificate-authority-types";
|
||||||
|
import { TInternalCertificateAuthorityServiceFactory } from "./internal/internal-certificate-authority-service";
|
||||||
|
|
||||||
|
type TCertificateAuthorityServiceFactoryDep = {
|
||||||
|
certificateAuthorityDAL: Pick<
|
||||||
|
TCertificateAuthorityDALFactory,
|
||||||
|
| "transaction"
|
||||||
|
| "create"
|
||||||
|
| "findById"
|
||||||
|
| "updateById"
|
||||||
|
| "deleteById"
|
||||||
|
| "findOne"
|
||||||
|
| "findByIdWithAssociatedCa"
|
||||||
|
| "findWithAssociatedCa"
|
||||||
|
>;
|
||||||
|
internalCertificateAuthorityService: TInternalCertificateAuthorityServiceFactory;
|
||||||
|
projectDAL: Pick<
|
||||||
|
TProjectDALFactory,
|
||||||
|
"findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId"
|
||||||
|
>;
|
||||||
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
|
};
|
||||||
|
|
||||||
export type TCertificateAuthorityServiceFactory = ReturnType<typeof certificateAuthorityServiceFactory>;
|
export type TCertificateAuthorityServiceFactory = ReturnType<typeof certificateAuthorityServiceFactory>;
|
||||||
|
|
||||||
export const certificateAuthorityServiceFactory = ({}: TCertificateAuthorityServiceFactoryDep) => {};
|
export const certificateAuthorityServiceFactory = ({
|
||||||
|
certificateAuthorityDAL,
|
||||||
|
projectDAL,
|
||||||
|
permissionService,
|
||||||
|
internalCertificateAuthorityService
|
||||||
|
}: TCertificateAuthorityServiceFactoryDep) => {
|
||||||
|
const createCertificateAuthority = async (
|
||||||
|
{ type, projectId, configuration, disableDirectIssuance }: TCreateCertificateAuthorityDTO,
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
|
let finalProjectId: string = projectId;
|
||||||
|
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
|
||||||
|
projectId,
|
||||||
|
ProjectType.CertificateManager
|
||||||
|
);
|
||||||
|
|
||||||
|
if (certManagerProjectFromSplit) {
|
||||||
|
finalProjectId = certManagerProjectFromSplit.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor: actor.type,
|
||||||
|
actorId: actor.id,
|
||||||
|
projectId: finalProjectId,
|
||||||
|
actorAuthMethod: actor.authMethod,
|
||||||
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
|
);
|
||||||
|
|
||||||
|
if (type === CaType.INTERNAL) {
|
||||||
|
const ca = await internalCertificateAuthorityService.createCa({
|
||||||
|
...configuration,
|
||||||
|
isInternal: true,
|
||||||
|
projectId: finalProjectId,
|
||||||
|
requireTemplateForIssuance: disableDirectIssuance
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ca.internalCa) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to create internal certificate authority"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: ca.id,
|
||||||
|
type,
|
||||||
|
disableDirectIssuance: ca.disableDirectIssuance,
|
||||||
|
name: ca.internalCa?.friendlyName,
|
||||||
|
projectId,
|
||||||
|
configuration: ca.internalCa
|
||||||
|
} as TCertificateAuthority;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const findCertificateAuthorityById = async (
|
||||||
|
{ certificateAuthorityId, type }: { certificateAuthorityId: string; type: CaType },
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
|
const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(certificateAuthorityId);
|
||||||
|
|
||||||
|
if (!certificateAuthority)
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Could not find certificate authority with ID "${certificateAuthorityId}"`
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor: actor.type,
|
||||||
|
actorId: actor.id,
|
||||||
|
projectId: certificateAuthority.projectId,
|
||||||
|
actorAuthMethod: actor.authMethod,
|
||||||
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
|
);
|
||||||
|
|
||||||
|
if (type === CaType.INTERNAL) {
|
||||||
|
if (!certificateAuthority.internalCa) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Could not find internal certificate authority with ID "${certificateAuthorityId}"`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: certificateAuthority.id,
|
||||||
|
type,
|
||||||
|
disableDirectIssuance: certificateAuthority.disableDirectIssuance,
|
||||||
|
name: certificateAuthority.internalCa.friendlyName,
|
||||||
|
projectId: certificateAuthority.projectId,
|
||||||
|
configuration: certificateAuthority.internalCa
|
||||||
|
} as TCertificateAuthority;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const listCertificateAuthoritiesByProjectId = async (
|
||||||
|
{ projectId, type }: { projectId: string; type: CaType },
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
|
let finalProjectId: string = projectId;
|
||||||
|
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
|
||||||
|
projectId,
|
||||||
|
ProjectType.CertificateManager
|
||||||
|
);
|
||||||
|
|
||||||
|
if (certManagerProjectFromSplit) {
|
||||||
|
finalProjectId = certManagerProjectFromSplit.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor: actor.type,
|
||||||
|
actorId: actor.id,
|
||||||
|
projectId: finalProjectId,
|
||||||
|
actorAuthMethod: actor.authMethod,
|
||||||
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
|
);
|
||||||
|
|
||||||
|
const cas = await certificateAuthorityDAL.findWithAssociatedCa({
|
||||||
|
[`${TableName.CertificateAuthority}.projectId` as "projectId"]: finalProjectId,
|
||||||
|
...(type === CaType.INTERNAL && {
|
||||||
|
$notNull: [`${TableName.InternalCertificateAuthority}.id` as "id"]
|
||||||
|
}),
|
||||||
|
...(type !== CaType.INTERNAL && {
|
||||||
|
[`${TableName.ExternalCertificateAuthority}.type` as "type"]: type
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (type === CaType.INTERNAL) {
|
||||||
|
return cas
|
||||||
|
.filter((ca): ca is typeof ca & { internalCa: NonNullable<typeof ca.internalCa> } => Boolean(ca.internalCa))
|
||||||
|
.map((ca) => ({
|
||||||
|
id: ca.id,
|
||||||
|
type,
|
||||||
|
disableDirectIssuance: ca.disableDirectIssuance,
|
||||||
|
name: ca.internalCa.friendlyName,
|
||||||
|
projectId: ca.projectId,
|
||||||
|
configuration: ca.internalCa
|
||||||
|
})) as TCertificateAuthority[];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateCertificateAuthority = async (
|
||||||
|
{ id, type, configuration, disableDirectIssuance }: TUpdateCertificateAuthorityDTO,
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
|
const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(id);
|
||||||
|
|
||||||
|
if (!certificateAuthority)
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Could not find certificate authority with ID "${id}"`
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor: actor.type,
|
||||||
|
actorId: actor.id,
|
||||||
|
projectId: certificateAuthority.projectId,
|
||||||
|
actorAuthMethod: actor.authMethod,
|
||||||
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Edit,
|
||||||
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
|
);
|
||||||
|
|
||||||
|
if (type === CaType.INTERNAL) {
|
||||||
|
if (!certificateAuthority.internalCa) {
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Could not find internal certificate authority with ID "${id}"`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedCa = await internalCertificateAuthorityService.updateCaById({
|
||||||
|
...configuration,
|
||||||
|
isInternal: true,
|
||||||
|
requireTemplateForIssuance: disableDirectIssuance,
|
||||||
|
caId: id
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!updatedCa.internalCa) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to update internal certificate authority"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: updatedCa.id,
|
||||||
|
type,
|
||||||
|
disableDirectIssuance: updatedCa.disableDirectIssuance,
|
||||||
|
name: updatedCa.internalCa?.friendlyName,
|
||||||
|
projectId: updatedCa.projectId,
|
||||||
|
configuration: updatedCa.internalCa
|
||||||
|
} as TCertificateAuthority;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteCertificateAuthority = async ({ id, type }: { id: string; type: CaType }, actor: OrgServiceActor) => {
|
||||||
|
const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(id);
|
||||||
|
|
||||||
|
if (!certificateAuthority)
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Could not find certificate authority with ID "${id}"`
|
||||||
|
});
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor: actor.type,
|
||||||
|
actorId: actor.id,
|
||||||
|
projectId: certificateAuthority.projectId,
|
||||||
|
actorAuthMethod: actor.authMethod,
|
||||||
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!certificateAuthority.internalCa && type === CaType.INTERNAL) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Certificate authority cannot be deleted due to mismatching type"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await certificateAuthorityDAL.deleteById(id);
|
||||||
|
|
||||||
|
if (type === CaType.INTERNAL) {
|
||||||
|
return {
|
||||||
|
id: certificateAuthority.id,
|
||||||
|
type,
|
||||||
|
disableDirectIssuance: certificateAuthority.disableDirectIssuance,
|
||||||
|
name: certificateAuthority.internalCa?.friendlyName,
|
||||||
|
projectId: certificateAuthority.projectId,
|
||||||
|
configuration: certificateAuthority.internalCa
|
||||||
|
} as TCertificateAuthority;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
createCertificateAuthority,
|
||||||
|
findCertificateAuthorityById,
|
||||||
|
listCertificateAuthoritiesByProjectId,
|
||||||
|
updateCertificateAuthority,
|
||||||
|
deleteCertificateAuthority
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,186 +1,18 @@
|
|||||||
import { TProjectPermission } from "@app/lib/types";
|
import { CaType } from "./certificate-authority-enums";
|
||||||
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
|
import {
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
TInternalCertificateAuthority,
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
TInternalCertificateAuthorityInput
|
||||||
|
} from "./internal/internal-certificate-authority-types";
|
||||||
|
|
||||||
import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
export type TCertificateAuthority = TInternalCertificateAuthority;
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "../certificate/certificate-types";
|
|
||||||
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
|
||||||
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
|
|
||||||
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
|
||||||
|
|
||||||
export enum CaType {
|
export type TCertificateAuthorityInput = TInternalCertificateAuthorityInput;
|
||||||
ROOT = "root",
|
|
||||||
INTERMEDIATE = "intermediate"
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CaStatus {
|
export type TCreateCertificateAuthorityDTO = Omit<TCertificateAuthority, "type" | "id"> & {
|
||||||
ACTIVE = "active",
|
|
||||||
DISABLED = "disabled",
|
|
||||||
PENDING_CERTIFICATE = "pending-certificate"
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CaRenewalType {
|
|
||||||
EXISTING = "existing"
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TCreateCaDTO = {
|
|
||||||
projectSlug: string;
|
|
||||||
type: CaType;
|
type: CaType;
|
||||||
friendlyName?: string;
|
|
||||||
commonName: string;
|
|
||||||
organization: string;
|
|
||||||
ou: string;
|
|
||||||
country: string;
|
|
||||||
province: string;
|
|
||||||
locality: string;
|
|
||||||
notBefore?: string;
|
|
||||||
notAfter?: string;
|
|
||||||
maxPathLength: number;
|
|
||||||
keyAlgorithm: CertKeyAlgorithm;
|
|
||||||
requireTemplateForIssuance: boolean;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TGetCaDTO = {
|
|
||||||
caId: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TUpdateCaDTO = {
|
|
||||||
caId: string;
|
|
||||||
status?: CaStatus;
|
|
||||||
requireTemplateForIssuance?: boolean;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TDeleteCaDTO = {
|
|
||||||
caId: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TGetCaCsrDTO = {
|
|
||||||
caId: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TRenewCaCertDTO = {
|
|
||||||
caId: string;
|
|
||||||
notAfter: string;
|
|
||||||
type: CaRenewalType;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TGetCaCertsDTO = {
|
|
||||||
caId: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TGetCaCertDTO = {
|
|
||||||
caId: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TSignIntermediateDTO = {
|
|
||||||
caId: string;
|
|
||||||
csr: string;
|
|
||||||
notBefore?: string;
|
|
||||||
notAfter: string;
|
|
||||||
maxPathLength: number;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TImportCertToCaDTO = {
|
|
||||||
caId: string;
|
|
||||||
certificate: string;
|
|
||||||
certificateChain: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TIssueCertFromCaDTO = {
|
|
||||||
caId?: string;
|
|
||||||
certificateTemplateId?: string;
|
|
||||||
pkiCollectionId?: string;
|
|
||||||
friendlyName?: string;
|
|
||||||
commonName: string;
|
|
||||||
altNames: string;
|
|
||||||
ttl: string;
|
|
||||||
notBefore?: string;
|
|
||||||
notAfter?: string;
|
|
||||||
keyUsages?: CertKeyUsage[];
|
|
||||||
extendedKeyUsages?: CertExtendedKeyUsage[];
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TSignCertFromCaDTO =
|
|
||||||
| {
|
|
||||||
isInternal: true;
|
|
||||||
caId?: string;
|
|
||||||
csr: string;
|
|
||||||
certificateTemplateId?: string;
|
|
||||||
pkiCollectionId?: string;
|
|
||||||
friendlyName?: string;
|
|
||||||
commonName?: string;
|
|
||||||
altNames?: string;
|
|
||||||
ttl?: string;
|
|
||||||
notBefore?: string;
|
|
||||||
notAfter?: string;
|
|
||||||
keyUsages?: CertKeyUsage[];
|
|
||||||
extendedKeyUsages?: CertExtendedKeyUsage[];
|
|
||||||
}
|
|
||||||
| ({
|
|
||||||
isInternal: false;
|
|
||||||
caId?: string;
|
|
||||||
csr: string;
|
|
||||||
certificateTemplateId?: string;
|
|
||||||
pkiCollectionId?: string;
|
|
||||||
friendlyName?: string;
|
|
||||||
commonName?: string;
|
|
||||||
altNames: string;
|
|
||||||
ttl: string;
|
|
||||||
notBefore?: string;
|
|
||||||
notAfter?: string;
|
|
||||||
keyUsages?: CertKeyUsage[];
|
|
||||||
extendedKeyUsages?: CertExtendedKeyUsage[];
|
|
||||||
} & Omit<TProjectPermission, "projectId">);
|
|
||||||
|
|
||||||
export type TGetCaCertificateTemplatesDTO = {
|
|
||||||
caId: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TDNParts = {
|
|
||||||
commonName?: string;
|
|
||||||
organization?: string;
|
|
||||||
ou?: string;
|
|
||||||
country?: string;
|
|
||||||
province?: string;
|
|
||||||
locality?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetCaCredentialsDTO = {
|
export type TUpdateCertificateAuthorityDTO = Partial<Omit<TCreateCertificateAuthorityDTO, "projectId">> & {
|
||||||
caId: string;
|
type: CaType;
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findByIdWithAssociatedCa">;
|
id: string;
|
||||||
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
|
||||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetCaCertChainsDTO = {
|
|
||||||
caId: string;
|
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
|
||||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
|
||||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TGetCaCertChainDTO = {
|
|
||||||
caCertId: string;
|
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
|
||||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
|
||||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TRebuildCaCrlDTO = {
|
|
||||||
caId: string;
|
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findByIdWithAssociatedCa">;
|
|
||||||
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "update">;
|
|
||||||
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
|
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
|
||||||
certificateDAL: Pick<TCertificateDALFactory, "find">;
|
|
||||||
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "decryptWithKmsKey" | "encryptWithKmsKey">;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TRotateCaCrlTriggerDTO = {
|
|
||||||
caId: string;
|
|
||||||
rotationIntervalDays: number;
|
|
||||||
};
|
};
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
||||||
|
|
||||||
|
import { CaType, InternalCaType } from "../certificate-authority-enums";
|
||||||
|
import {
|
||||||
|
BaseCertificateAuthoritySchema,
|
||||||
|
GenericCreateCertificateAuthorityFieldsSchema,
|
||||||
|
GenericUpdateCertificateAuthorityFieldsSchema
|
||||||
|
} from "../certificate-authority-schemas";
|
||||||
|
import { validateCaDateField } from "../certificate-authority-validators";
|
||||||
|
|
||||||
|
const InternalCertificateAuthorityConfigurationSchema = z
|
||||||
|
.object({
|
||||||
|
type: z.nativeEnum(InternalCaType),
|
||||||
|
friendlyName: z.string().optional(),
|
||||||
|
commonName: z.string().trim(),
|
||||||
|
organization: z.string().trim(),
|
||||||
|
ou: z.string().trim(),
|
||||||
|
country: z.string().trim(),
|
||||||
|
province: z.string().trim(),
|
||||||
|
locality: z.string().trim(),
|
||||||
|
// format: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format
|
||||||
|
notBefore: validateCaDateField.optional(),
|
||||||
|
notAfter: validateCaDateField.optional(),
|
||||||
|
maxPathLength: z.number().min(-1),
|
||||||
|
keyAlgorithm: z.nativeEnum(CertKeyAlgorithm)
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
(data) => {
|
||||||
|
// Check that at least one of the specified fields is non-empty
|
||||||
|
return [data.commonName, data.organization, data.ou, data.country, data.province, data.locality].some(
|
||||||
|
(field) => field !== ""
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message:
|
||||||
|
"At least one of the fields commonName, organization, ou, country, province, or locality must be non-empty",
|
||||||
|
path: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const InternalCertificateAuthoritySchema = BaseCertificateAuthoritySchema(CaType.INTERNAL).extend({
|
||||||
|
type: z.literal(CaType.INTERNAL),
|
||||||
|
configuration: InternalCertificateAuthorityConfigurationSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateInternalCertificateAuthoritySchema = GenericCreateCertificateAuthorityFieldsSchema(
|
||||||
|
CaType.INTERNAL
|
||||||
|
).extend({
|
||||||
|
configuration: InternalCertificateAuthorityConfigurationSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
export const UpdateInternalCertificateAuthoritySchema = GenericUpdateCertificateAuthorityFieldsSchema(
|
||||||
|
CaType.INTERNAL
|
||||||
|
).extend({
|
||||||
|
configuration: InternalCertificateAuthorityConfigurationSchema.optional()
|
||||||
|
});
|
||||||
+64
-69
@@ -30,19 +30,20 @@ import { TPkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-c
|
|||||||
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 { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
import { TCertificateAuthorityCrlDALFactory } from "../../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
||||||
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
|
import { TCertificateSecretDALFactory } from "../../certificate/certificate-secret-dal";
|
||||||
import {
|
import {
|
||||||
CertExtendedKeyUsage,
|
CertExtendedKeyUsage,
|
||||||
CertExtendedKeyUsageOIDToName,
|
CertExtendedKeyUsageOIDToName,
|
||||||
CertKeyAlgorithm,
|
CertKeyAlgorithm,
|
||||||
CertKeyUsage,
|
CertKeyUsage,
|
||||||
CertStatus
|
CertStatus
|
||||||
} from "../certificate/certificate-types";
|
} from "../../certificate/certificate-types";
|
||||||
import { TCertificateTemplateDALFactory } from "../certificate-template/certificate-template-dal";
|
import { TCertificateTemplateDALFactory } from "../../certificate-template/certificate-template-dal";
|
||||||
import { validateCertificateDetailsAgainstTemplate } from "../certificate-template/certificate-template-fns";
|
import { validateCertificateDetailsAgainstTemplate } from "../../certificate-template/certificate-template-fns";
|
||||||
import { TCertificateAuthorityCertDALFactory } from "./certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "../certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityDALFactory, TCertificateAuthorityWithAssociatedCa } from "./certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory, TCertificateAuthorityWithAssociatedCa } from "../certificate-authority-dal";
|
||||||
|
import { CaStatus, InternalCaType } from "../certificate-authority-enums";
|
||||||
import {
|
import {
|
||||||
createDistinguishedName,
|
createDistinguishedName,
|
||||||
createSerialNumber,
|
createSerialNumber,
|
||||||
@@ -52,12 +53,11 @@ import {
|
|||||||
getCaCredentials,
|
getCaCredentials,
|
||||||
keyAlgorithmToAlgCfg,
|
keyAlgorithmToAlgCfg,
|
||||||
parseDistinguishedName
|
parseDistinguishedName
|
||||||
} from "./certificate-authority-fns";
|
} from "../certificate-authority-fns";
|
||||||
import { TCertificateAuthorityQueueFactory } from "./certificate-authority-queue";
|
import { TCertificateAuthorityQueueFactory } from "../certificate-authority-queue";
|
||||||
import { TCertificateAuthoritySecretDALFactory } from "./certificate-authority-secret-dal";
|
import { TCertificateAuthoritySecretDALFactory } from "../certificate-authority-secret-dal";
|
||||||
|
import { TInternalCertificateAuthorityDALFactory } from "./internal-certificate-authority-dal";
|
||||||
import {
|
import {
|
||||||
CaStatus,
|
|
||||||
CaType,
|
|
||||||
TCreateCaDTO,
|
TCreateCaDTO,
|
||||||
TDeleteCaDTO,
|
TDeleteCaDTO,
|
||||||
TGetCaCertDTO,
|
TGetCaCertDTO,
|
||||||
@@ -71,8 +71,7 @@ import {
|
|||||||
TSignCertFromCaDTO,
|
TSignCertFromCaDTO,
|
||||||
TSignIntermediateDTO,
|
TSignIntermediateDTO,
|
||||||
TUpdateCaDTO
|
TUpdateCaDTO
|
||||||
} from "./certificate-authority-types";
|
} from "./internal-certificate-authority-types";
|
||||||
import { TInternalCertificateAuthorityDALFactory } from "./internal-certificate-authority-dal";
|
|
||||||
|
|
||||||
type TInternalCertificateAuthorityServiceFactoryDep = {
|
type TInternalCertificateAuthorityServiceFactoryDep = {
|
||||||
certificateAuthorityDAL: Pick<
|
certificateAuthorityDAL: Pick<
|
||||||
@@ -130,7 +129,6 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
permissionService
|
permissionService
|
||||||
}: TInternalCertificateAuthorityServiceFactoryDep) => {
|
}: TInternalCertificateAuthorityServiceFactoryDep) => {
|
||||||
const createCa = async ({
|
const createCa = async ({
|
||||||
projectSlug,
|
|
||||||
type,
|
type,
|
||||||
friendlyName,
|
friendlyName,
|
||||||
commonName,
|
commonName,
|
||||||
@@ -144,37 +142,39 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
maxPathLength,
|
maxPathLength,
|
||||||
keyAlgorithm,
|
keyAlgorithm,
|
||||||
requireTemplateForIssuance,
|
requireTemplateForIssuance,
|
||||||
actorId,
|
...dto
|
||||||
actorAuthMethod,
|
|
||||||
actor,
|
|
||||||
actorOrgId
|
|
||||||
}: TCreateCaDTO) => {
|
}: TCreateCaDTO) => {
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
let projectId: string;
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!dto.isInternal) {
|
||||||
let projectId = project.id;
|
const project = await projectDAL.findProjectBySlug(dto.projectSlug, dto.actorOrgId);
|
||||||
|
if (!project) throw new NotFoundError({ message: `Project with slug '${dto.projectSlug}' not found` });
|
||||||
|
projectId = project.id;
|
||||||
|
|
||||||
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
|
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
|
||||||
projectId,
|
projectId,
|
||||||
ProjectType.CertificateManager
|
ProjectType.CertificateManager
|
||||||
);
|
);
|
||||||
if (certManagerProjectFromSplit) {
|
if (certManagerProjectFromSplit) {
|
||||||
projectId = certManagerProjectFromSplit.id;
|
projectId = certManagerProjectFromSplit.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor: dto.actor,
|
||||||
|
actorId: dto.actorId,
|
||||||
|
projectId,
|
||||||
|
actorAuthMethod: dto.actorAuthMethod,
|
||||||
|
actorOrgId: dto.actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
projectId = dto.projectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId,
|
|
||||||
actionProjectType: ActionProjectType.CertificateManager
|
|
||||||
});
|
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
|
||||||
ProjectPermissionActions.Create,
|
|
||||||
ProjectPermissionSub.CertificateAuthorities
|
|
||||||
);
|
|
||||||
|
|
||||||
const dn = createDistinguishedName({
|
const dn = createDistinguishedName({
|
||||||
commonName,
|
commonName,
|
||||||
organization,
|
organization,
|
||||||
@@ -216,10 +216,10 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
locality,
|
locality,
|
||||||
friendlyName: friendlyName || dn,
|
friendlyName: friendlyName || dn,
|
||||||
commonName,
|
commonName,
|
||||||
status: type === CaType.ROOT ? CaStatus.ACTIVE : CaStatus.PENDING_CERTIFICATE,
|
status: type === InternalCaType.ROOT ? CaStatus.ACTIVE : CaStatus.PENDING_CERTIFICATE,
|
||||||
dn,
|
dn,
|
||||||
keyAlgorithm,
|
keyAlgorithm,
|
||||||
...(type === CaType.ROOT && {
|
...(type === InternalCaType.ROOT && {
|
||||||
maxPathLength,
|
maxPathLength,
|
||||||
notBefore: notBeforeDate,
|
notBefore: notBeforeDate,
|
||||||
notAfter: notAfterDate,
|
notAfter: notAfterDate,
|
||||||
@@ -256,7 +256,7 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
if (type === CaType.ROOT) {
|
if (type === InternalCaType.ROOT) {
|
||||||
// note: create self-signed cert only applicable for root CA
|
// note: create self-signed cert only applicable for root CA
|
||||||
const cert = await x509.X509CertificateGenerator.createSelfSigned({
|
const cert = await x509.X509CertificateGenerator.createSelfSigned({
|
||||||
name: dn,
|
name: dn,
|
||||||
@@ -357,31 +357,25 @@ 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 ({
|
const updateCaById = async ({ caId, status, requireTemplateForIssuance, ...dto }: TUpdateCaDTO) => {
|
||||||
caId,
|
|
||||||
status,
|
|
||||||
requireTemplateForIssuance,
|
|
||||||
actorId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actor,
|
|
||||||
actorOrgId
|
|
||||||
}: 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` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
if (!dto.isInternal) {
|
||||||
actor,
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actorId,
|
actor: dto.actor,
|
||||||
projectId: ca.projectId,
|
actorId: dto.actorId,
|
||||||
actorAuthMethod,
|
projectId: ca.projectId,
|
||||||
actorOrgId,
|
actorAuthMethod: dto.actorAuthMethod,
|
||||||
actionProjectType: ActionProjectType.CertificateManager
|
actorOrgId: dto.actorOrgId,
|
||||||
});
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionActions.Edit,
|
||||||
ProjectPermissionSub.CertificateAuthorities
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const updatedCa = await certificateAuthorityDAL.transaction(async (tx) => {
|
const updatedCa = await certificateAuthorityDAL.transaction(async (tx) => {
|
||||||
if (status !== undefined) {
|
if (status !== undefined) {
|
||||||
@@ -451,7 +445,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
ProjectPermissionSub.CertificateAuthorities
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ca.internalCa.type === CaType.ROOT) throw new BadRequestError({ message: "Root CA cannot generate CSR" });
|
if (ca.internalCa.type === InternalCaType.ROOT)
|
||||||
|
throw new BadRequestError({ message: "Root CA cannot generate CSR" });
|
||||||
|
|
||||||
const { caPrivateKey, caPublicKey } = await getCaCredentials({
|
const { caPrivateKey, caPublicKey } = await getCaCredentials({
|
||||||
caId,
|
caId,
|
||||||
@@ -554,7 +549,7 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
let certificateChain = "";
|
let certificateChain = "";
|
||||||
|
|
||||||
switch (ca.internalCa.type) {
|
switch (ca.internalCa.type) {
|
||||||
case CaType.ROOT: {
|
case InternalCaType.ROOT: {
|
||||||
if (new Date(notAfter) <= new Date(caCertObj.notAfter)) {
|
if (new Date(notAfter) <= new Date(caCertObj.notAfter)) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message:
|
message:
|
||||||
@@ -623,7 +618,7 @@ export const internalCertificateAuthorityServiceFactory = ({
|
|||||||
certificate = cert.toString("pem");
|
certificate = cert.toString("pem");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CaType.INTERMEDIATE: {
|
case InternalCaType.INTERMEDIATE: {
|
||||||
if (!ca.internalCa.parentCaId) {
|
if (!ca.internalCa.parentCaId) {
|
||||||
// TODO: look into optimal way to support renewal of intermediate CA with external parent CA
|
// TODO: look into optimal way to support renewal of intermediate CA with external parent CA
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
||||||
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
|
||||||
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
|
import { TCertificateAuthorityCertDALFactory } from "../certificate-authority-cert-dal";
|
||||||
|
import { TCertificateAuthorityDALFactory } from "../certificate-authority-dal";
|
||||||
|
import { CaRenewalType, CaStatus, InternalCaType } from "../certificate-authority-enums";
|
||||||
|
import { TCertificateAuthoritySecretDALFactory } from "../certificate-authority-secret-dal";
|
||||||
|
import {
|
||||||
|
CreateInternalCertificateAuthoritySchema,
|
||||||
|
InternalCertificateAuthoritySchema
|
||||||
|
} from "./internal-certificate-authority-schemas";
|
||||||
|
|
||||||
|
export type TInternalCertificateAuthority = z.infer<typeof InternalCertificateAuthoritySchema>;
|
||||||
|
|
||||||
|
export type TInternalCertificateAuthorityInput = z.infer<typeof CreateInternalCertificateAuthoritySchema>;
|
||||||
|
|
||||||
|
export type TCreateCaDTO =
|
||||||
|
| {
|
||||||
|
isInternal: true;
|
||||||
|
projectId: string;
|
||||||
|
type: InternalCaType;
|
||||||
|
friendlyName?: string;
|
||||||
|
commonName: string;
|
||||||
|
organization: string;
|
||||||
|
ou: string;
|
||||||
|
country: string;
|
||||||
|
province: string;
|
||||||
|
locality: string;
|
||||||
|
notBefore?: string;
|
||||||
|
notAfter?: string;
|
||||||
|
maxPathLength: number;
|
||||||
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
|
requireTemplateForIssuance: boolean;
|
||||||
|
}
|
||||||
|
| ({
|
||||||
|
isInternal: false;
|
||||||
|
projectSlug: string;
|
||||||
|
type: InternalCaType;
|
||||||
|
friendlyName?: string;
|
||||||
|
commonName: string;
|
||||||
|
organization: string;
|
||||||
|
ou: string;
|
||||||
|
country: string;
|
||||||
|
province: string;
|
||||||
|
locality: string;
|
||||||
|
notBefore?: string;
|
||||||
|
notAfter?: string;
|
||||||
|
maxPathLength: number;
|
||||||
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
|
requireTemplateForIssuance: boolean;
|
||||||
|
} & Omit<TProjectPermission, "projectId">);
|
||||||
|
|
||||||
|
export type TGetCaDTO = {
|
||||||
|
caId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TUpdateCaDTO =
|
||||||
|
| {
|
||||||
|
isInternal: true;
|
||||||
|
caId: string;
|
||||||
|
status?: CaStatus;
|
||||||
|
requireTemplateForIssuance?: boolean;
|
||||||
|
}
|
||||||
|
| ({
|
||||||
|
isInternal: false;
|
||||||
|
caId: string;
|
||||||
|
status?: CaStatus;
|
||||||
|
requireTemplateForIssuance?: boolean;
|
||||||
|
} & Omit<TProjectPermission, "projectId">);
|
||||||
|
|
||||||
|
export type TDeleteCaDTO = {
|
||||||
|
caId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetCaCsrDTO = {
|
||||||
|
caId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TRenewCaCertDTO = {
|
||||||
|
caId: string;
|
||||||
|
notAfter: string;
|
||||||
|
type: CaRenewalType;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetCaCertsDTO = {
|
||||||
|
caId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetCaCertDTO = {
|
||||||
|
caId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TSignIntermediateDTO = {
|
||||||
|
caId: string;
|
||||||
|
csr: string;
|
||||||
|
notBefore?: string;
|
||||||
|
notAfter: string;
|
||||||
|
maxPathLength: number;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TImportCertToCaDTO = {
|
||||||
|
caId: string;
|
||||||
|
certificate: string;
|
||||||
|
certificateChain: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TIssueCertFromCaDTO = {
|
||||||
|
caId?: string;
|
||||||
|
certificateTemplateId?: string;
|
||||||
|
pkiCollectionId?: string;
|
||||||
|
friendlyName?: string;
|
||||||
|
commonName: string;
|
||||||
|
altNames: string;
|
||||||
|
ttl: string;
|
||||||
|
notBefore?: string;
|
||||||
|
notAfter?: string;
|
||||||
|
keyUsages?: CertKeyUsage[];
|
||||||
|
extendedKeyUsages?: CertExtendedKeyUsage[];
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TSignCertFromCaDTO =
|
||||||
|
| {
|
||||||
|
isInternal: true;
|
||||||
|
caId?: string;
|
||||||
|
csr: string;
|
||||||
|
certificateTemplateId?: string;
|
||||||
|
pkiCollectionId?: string;
|
||||||
|
friendlyName?: string;
|
||||||
|
commonName?: string;
|
||||||
|
altNames?: string;
|
||||||
|
ttl?: string;
|
||||||
|
notBefore?: string;
|
||||||
|
notAfter?: string;
|
||||||
|
keyUsages?: CertKeyUsage[];
|
||||||
|
extendedKeyUsages?: CertExtendedKeyUsage[];
|
||||||
|
}
|
||||||
|
| ({
|
||||||
|
isInternal: false;
|
||||||
|
caId?: string;
|
||||||
|
csr: string;
|
||||||
|
certificateTemplateId?: string;
|
||||||
|
pkiCollectionId?: string;
|
||||||
|
friendlyName?: string;
|
||||||
|
commonName?: string;
|
||||||
|
altNames: string;
|
||||||
|
ttl: string;
|
||||||
|
notBefore?: string;
|
||||||
|
notAfter?: string;
|
||||||
|
keyUsages?: CertKeyUsage[];
|
||||||
|
extendedKeyUsages?: CertExtendedKeyUsage[];
|
||||||
|
} & Omit<TProjectPermission, "projectId">);
|
||||||
|
|
||||||
|
export type TGetCaCertificateTemplatesDTO = {
|
||||||
|
caId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TDNParts = {
|
||||||
|
commonName?: string;
|
||||||
|
organization?: string;
|
||||||
|
ou?: string;
|
||||||
|
country?: string;
|
||||||
|
province?: string;
|
||||||
|
locality?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetCaCredentialsDTO = {
|
||||||
|
caId: string;
|
||||||
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findByIdWithAssociatedCa">;
|
||||||
|
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetCaCertChainsDTO = {
|
||||||
|
caId: string;
|
||||||
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||||
|
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find">;
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TGetCaCertChainDTO = {
|
||||||
|
caCertId: string;
|
||||||
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||||
|
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TRebuildCaCrlDTO = {
|
||||||
|
caId: string;
|
||||||
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findByIdWithAssociatedCa">;
|
||||||
|
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "update">;
|
||||||
|
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
|
||||||
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
|
certificateDAL: Pick<TCertificateDALFactory, "find">;
|
||||||
|
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "decryptWithKmsKey" | "encryptWithKmsKey">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TRotateCaCrlTriggerDTO = {
|
||||||
|
caId: string;
|
||||||
|
rotationIntervalDays: number;
|
||||||
|
};
|
||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
} from "@app/services/certificate/certificate-types";
|
} from "@app/services/certificate/certificate-types";
|
||||||
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
||||||
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
||||||
|
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-enums";
|
||||||
import {
|
import {
|
||||||
createSerialNumber,
|
createSerialNumber,
|
||||||
expandInternalCa,
|
expandInternalCa,
|
||||||
@@ -36,7 +37,6 @@ import {
|
|||||||
parseDistinguishedName
|
parseDistinguishedName
|
||||||
} from "@app/services/certificate-authority/certificate-authority-fns";
|
} from "@app/services/certificate-authority/certificate-authority-fns";
|
||||||
import { TCertificateAuthoritySecretDALFactory } from "@app/services/certificate-authority/certificate-authority-secret-dal";
|
import { TCertificateAuthoritySecretDALFactory } from "@app/services/certificate-authority/certificate-authority-secret-dal";
|
||||||
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-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 { 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";
|
||||||
|
|||||||
Reference in New Issue
Block a user