mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 15:27:27 +00:00
Merge branch 'certificate-alerting' into feature/certificate-template
This commit is contained in:
@@ -4,16 +4,19 @@ import { TableName } from "../schemas";
|
|||||||
|
|
||||||
export async function up(knex: Knex): Promise<void> {
|
export async function up(knex: Knex): Promise<void> {
|
||||||
if (await knex.schema.hasTable(TableName.CertificateAuthority)) {
|
if (await knex.schema.hasTable(TableName.CertificateAuthority)) {
|
||||||
const hasActiveCaCertVersionColumn = await knex.schema.hasColumn(
|
const hasActiveCaCertIdColumn = await knex.schema.hasColumn(TableName.CertificateAuthority, "activeCaCertId");
|
||||||
TableName.CertificateAuthority,
|
if (!hasActiveCaCertIdColumn) {
|
||||||
"activeCaCertVersion"
|
|
||||||
);
|
|
||||||
if (!hasActiveCaCertVersionColumn) {
|
|
||||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||||
t.integer("activeCaCertVersion").nullable();
|
t.uuid("activeCaCertId").nullable();
|
||||||
|
t.foreign("activeCaCertId").references("id").inTable(TableName.CertificateAuthorityCert);
|
||||||
});
|
});
|
||||||
|
|
||||||
await knex(TableName.CertificateAuthority).where("status", "active").update({ activeCaCertVersion: 1 });
|
await knex.raw(`
|
||||||
|
UPDATE "${TableName.CertificateAuthority}" ca
|
||||||
|
SET "activeCaCertId" = cac.id
|
||||||
|
FROM "${TableName.CertificateAuthorityCert}" cac
|
||||||
|
WHERE ca.id = cac."caId"
|
||||||
|
`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,18 +57,38 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (await knex.schema.hasTable(TableName.CertificateAuthoritySecret)) {
|
if (await knex.schema.hasTable(TableName.CertificateAuthoritySecret)) {
|
||||||
// await knex.schema.alterTable(TableName.CertificateAuthoritySecret, (t) => {
|
await knex.schema.alterTable(TableName.CertificateAuthoritySecret, (t) => {
|
||||||
// t.dropUnique(["caId"]);
|
t.dropUnique(["caId"]);
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.Certificate)) {
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.uuid("caCertId").nullable();
|
||||||
|
t.foreign("caCertId").references("id").inTable(TableName.CertificateAuthorityCert);
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.raw(`
|
||||||
|
UPDATE "${TableName.Certificate}" cert
|
||||||
|
SET "caCertId" = (
|
||||||
|
SELECT caCert.id
|
||||||
|
FROM "${TableName.CertificateAuthorityCert}" caCert
|
||||||
|
WHERE caCert."caId" = cert."caId"
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.uuid("caCertId").notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function down(knex: Knex): Promise<void> {
|
export async function down(knex: Knex): Promise<void> {
|
||||||
if (await knex.schema.hasTable(TableName.CertificateAuthority)) {
|
if (await knex.schema.hasTable(TableName.CertificateAuthority)) {
|
||||||
if (await knex.schema.hasColumn(TableName.CertificateAuthority, "activeCaCertVersion")) {
|
if (await knex.schema.hasColumn(TableName.CertificateAuthority, "activeCaCertId")) {
|
||||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||||
t.dropColumn("activeCaCertVersion");
|
t.dropColumn("activeCaCertId");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,4 +106,12 @@ export async function down(knex: Knex): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.Certificate)) {
|
||||||
|
if (await knex.schema.hasColumn(TableName.Certificate, "caCertId")) {
|
||||||
|
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||||
|
t.dropColumn("caCertId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
t.string("projectId").notNullable();
|
t.string("projectId").notNullable();
|
||||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||||
t.string("name").notNullable();
|
t.string("name").notNullable();
|
||||||
|
t.string("description").notNullable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const CertificateAuthoritiesSchema = z.object({
|
|||||||
keyAlgorithm: z.string(),
|
keyAlgorithm: z.string(),
|
||||||
notBefore: z.date().nullable().optional(),
|
notBefore: z.date().nullable().optional(),
|
||||||
notAfter: z.date().nullable().optional(),
|
notAfter: z.date().nullable().optional(),
|
||||||
activeCaCertVersion: z.number().nullable().optional()
|
activeCaCertId: z.string().uuid().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;
|
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ export const CertificatesSchema = z.object({
|
|||||||
notAfter: z.date(),
|
notAfter: z.date(),
|
||||||
revokedAt: z.date().nullable().optional(),
|
revokedAt: z.date().nullable().optional(),
|
||||||
revocationReason: z.number().nullable().optional(),
|
revocationReason: z.number().nullable().optional(),
|
||||||
altNames: z.string().default("").nullable().optional()
|
altNames: z.string().default("").nullable().optional(),
|
||||||
|
caCertId: z.string().uuid()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ export const PkiCollectionsSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
name: z.string()
|
name: z.string(),
|
||||||
|
description: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TPkiCollections = z.infer<typeof PkiCollectionsSchema>;
|
export type TPkiCollections = z.infer<typeof PkiCollectionsSchema>;
|
||||||
|
|||||||
@@ -1089,6 +1089,7 @@ export const CERTIFICATE_AUTHORITIES = {
|
|||||||
ISSUE_CERT: {
|
ISSUE_CERT: {
|
||||||
caId: "The ID of the CA to issue the certificate from",
|
caId: "The ID of the CA to issue the certificate from",
|
||||||
certificateTemplateId: "The ID of the certificate template to issue the certificate from",
|
certificateTemplateId: "The ID of the certificate template to issue the certificate from",
|
||||||
|
pkiCollectionId: "The ID of the PKI collection to add the certificate to",
|
||||||
friendlyName: "A friendly name for the certificate",
|
friendlyName: "A friendly name for the certificate",
|
||||||
commonName: "The common name (CN) for the certificate",
|
commonName: "The common name (CN) for the certificate",
|
||||||
altNames:
|
altNames:
|
||||||
@@ -1104,6 +1105,7 @@ export const CERTIFICATE_AUTHORITIES = {
|
|||||||
},
|
},
|
||||||
SIGN_CERT: {
|
SIGN_CERT: {
|
||||||
caId: "The ID of the CA to issue the certificate from",
|
caId: "The ID of the CA to issue the certificate from",
|
||||||
|
pkiCollectionId: "The ID of the PKI collection to add the certificate to",
|
||||||
csr: "The pem-encoded CSR to sign with the CA to be used for certificate issuance",
|
csr: "The pem-encoded CSR to sign with the CA to be used for certificate issuance",
|
||||||
friendlyName: "A friendly name for the certificate",
|
friendlyName: "A friendly name for the certificate",
|
||||||
commonName: "The common name (CN) for the certificate",
|
commonName: "The common name (CN) for the certificate",
|
||||||
@@ -1171,14 +1173,16 @@ export const ALERTS = {
|
|||||||
export const PKI_COLLECTIONS = {
|
export const PKI_COLLECTIONS = {
|
||||||
CREATE: {
|
CREATE: {
|
||||||
projectId: "The ID of the project to create the PKI collection in",
|
projectId: "The ID of the project to create the PKI collection in",
|
||||||
name: "The name of the PKI collection"
|
name: "The name of the PKI collection",
|
||||||
|
description: "A description for the PKI collection"
|
||||||
},
|
},
|
||||||
GET: {
|
GET: {
|
||||||
collectionId: "The ID of the PKI collection to get"
|
collectionId: "The ID of the PKI collection to get"
|
||||||
},
|
},
|
||||||
UPDATE: {
|
UPDATE: {
|
||||||
collectionId: "The ID of the PKI collection to update",
|
collectionId: "The ID of the PKI collection to update",
|
||||||
name: "The name of the PKI collection to update to"
|
name: "The name of the PKI collection to update to",
|
||||||
|
description: "The description for the PKI collection to update to"
|
||||||
},
|
},
|
||||||
DELETE: {
|
DELETE: {
|
||||||
collectionId: "The ID of the PKI collection to delete"
|
collectionId: "The ID of the PKI collection to delete"
|
||||||
|
|||||||
@@ -626,6 +626,8 @@ export const registerRoutes = async (
|
|||||||
certificateAuthorityQueue,
|
certificateAuthorityQueue,
|
||||||
certificateDAL,
|
certificateDAL,
|
||||||
certificateBodyDAL,
|
certificateBodyDAL,
|
||||||
|
pkiCollectionDAL,
|
||||||
|
pkiCollectionItemDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
permissionService
|
permissionService
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
schema: {
|
schema: {
|
||||||
description: "Renew CA certificate for CA",
|
description: "Perform CA certificate renewal",
|
||||||
params: z.object({
|
params: z.object({
|
||||||
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.caId)
|
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.RENEW_CA_CERT.caId)
|
||||||
}),
|
}),
|
||||||
@@ -556,6 +556,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}),
|
}),
|
||||||
body: z
|
body: z
|
||||||
.object({
|
.object({
|
||||||
|
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.pkiCollectionId),
|
||||||
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.friendlyName),
|
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.friendlyName),
|
||||||
commonName: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.commonName),
|
commonName: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.commonName),
|
||||||
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.altNames),
|
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.ISSUE_CERT.altNames),
|
||||||
@@ -635,6 +636,7 @@ export const registerCaRouter = async (server: FastifyZodProvider) => {
|
|||||||
body: z
|
body: z
|
||||||
.object({
|
.object({
|
||||||
csr: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.csr),
|
csr: z.string().trim().min(1).describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.csr),
|
||||||
|
pkiCollectionId: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.pkiCollectionId),
|
||||||
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.friendlyName),
|
friendlyName: z.string().trim().optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.friendlyName),
|
||||||
commonName: z.string().trim().min(1).optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.commonName),
|
commonName: z.string().trim().min(1).optional().describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.commonName),
|
||||||
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.altNames),
|
altNames: validateAltNamesField.describe(CERTIFICATE_AUTHORITIES.SIGN_CERT.altNames),
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ export const registerPkiCollectionRouter = async (server: FastifyZodProvider) =>
|
|||||||
description: "Create PKI collection",
|
description: "Create PKI collection",
|
||||||
body: z.object({
|
body: z.object({
|
||||||
projectId: z.string().trim().describe(PKI_COLLECTIONS.CREATE.projectId),
|
projectId: z.string().trim().describe(PKI_COLLECTIONS.CREATE.projectId),
|
||||||
name: z.string().trim().describe(PKI_COLLECTIONS.CREATE.name)
|
name: z.string().trim().describe(PKI_COLLECTIONS.CREATE.name),
|
||||||
|
description: z.string().trim().default("").describe(PKI_COLLECTIONS.CREATE.description)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: PkiCollectionsSchema
|
200: PkiCollectionsSchema
|
||||||
@@ -104,7 +105,8 @@ export const registerPkiCollectionRouter = async (server: FastifyZodProvider) =>
|
|||||||
collectionId: z.string().trim().describe(PKI_COLLECTIONS.UPDATE.collectionId)
|
collectionId: z.string().trim().describe(PKI_COLLECTIONS.UPDATE.collectionId)
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
name: z.string().trim().optional().describe(PKI_COLLECTIONS.UPDATE.name)
|
name: z.string().trim().optional().describe(PKI_COLLECTIONS.UPDATE.name),
|
||||||
|
description: z.string().trim().optional().describe(PKI_COLLECTIONS.UPDATE.description)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: PkiCollectionsSchema
|
200: PkiCollectionsSchema
|
||||||
|
|||||||
@@ -195,19 +195,18 @@ export const getCaCertChains = async ({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the decrypted pem-encoded certificate and certificate chain
|
* Return the decrypted pem-encoded certificate and certificate chain
|
||||||
* for CA with id [caId].
|
* corresponding to CA certificate with id [caCertId].
|
||||||
*/
|
*/
|
||||||
export const getCaCertChain = async ({
|
export const getCaCertChain = async ({
|
||||||
caId,
|
caCertId,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmsService
|
kmsService
|
||||||
}: TGetCaCertChainDTO) => {
|
}: TGetCaCertChainDTO) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const caCert = await certificateAuthorityCertDAL.findById(caCertId);
|
||||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
if (!caCert) throw new BadRequestError({ message: "CA certificate not found" });
|
||||||
|
const ca = await certificateAuthorityDAL.findById(caCert.caId);
|
||||||
const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id });
|
|
||||||
|
|
||||||
const keyId = await getProjectKmsCertificateKeyId({
|
const keyId = await getProjectKmsCertificateKeyId({
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
|||||||
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";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
|
import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
|
||||||
|
import { TPkiCollectionItemDALFactory } from "@app/services/pki-collection/pki-collection-item-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||||
|
|
||||||
@@ -54,13 +56,18 @@ type TCertificateAuthorityServiceFactoryDep = {
|
|||||||
TCertificateAuthorityDALFactory,
|
TCertificateAuthorityDALFactory,
|
||||||
"transaction" | "create" | "findById" | "updateById" | "deleteById" | "findOne"
|
"transaction" | "create" | "findById" | "updateById" | "deleteById" | "findOne"
|
||||||
>;
|
>;
|
||||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "create" | "findOne" | "transaction" | "find">;
|
certificateAuthorityCertDAL: Pick<
|
||||||
|
TCertificateAuthorityCertDALFactory,
|
||||||
|
"create" | "findOne" | "transaction" | "find" | "findById"
|
||||||
|
>;
|
||||||
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "create" | "findOne">;
|
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "create" | "findOne">;
|
||||||
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "create" | "findOne" | "update">;
|
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "create" | "findOne" | "update">;
|
||||||
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "getById">;
|
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "getById">;
|
||||||
certificateAuthorityQueue: TCertificateAuthorityQueueFactory; // TODO: Pick
|
certificateAuthorityQueue: TCertificateAuthorityQueueFactory; // TODO: Pick
|
||||||
certificateDAL: Pick<TCertificateDALFactory, "transaction" | "create" | "find">;
|
certificateDAL: Pick<TCertificateDALFactory, "transaction" | "create" | "find">;
|
||||||
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
|
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
|
||||||
|
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "findById">;
|
||||||
|
pkiCollectionItemDAL: Pick<TPkiCollectionItemDALFactory, "create">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey">;
|
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
@@ -76,6 +83,8 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
certificateTemplateDAL,
|
certificateTemplateDAL,
|
||||||
certificateDAL,
|
certificateDAL,
|
||||||
certificateBodyDAL,
|
certificateBodyDAL,
|
||||||
|
pkiCollectionDAL,
|
||||||
|
pkiCollectionItemDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
kmsService,
|
kmsService,
|
||||||
permissionService
|
permissionService
|
||||||
@@ -158,8 +167,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
maxPathLength,
|
maxPathLength,
|
||||||
notBefore: notBeforeDate,
|
notBefore: notBeforeDate,
|
||||||
notAfter: notAfterDate,
|
notAfter: notAfterDate,
|
||||||
serialNumber,
|
serialNumber
|
||||||
activeCaCertVersion: 1
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@@ -218,7 +226,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
plainText: Buffer.alloc(0)
|
plainText: Buffer.alloc(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
await certificateAuthorityCertDAL.create(
|
const caCert = await certificateAuthorityCertDAL.create(
|
||||||
{
|
{
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
encryptedCertificate,
|
encryptedCertificate,
|
||||||
@@ -228,6 +236,14 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await certificateAuthorityDAL.updateById(
|
||||||
|
ca.id,
|
||||||
|
{
|
||||||
|
activeCaCertId: caCert.id
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create empty CRL
|
// create empty CRL
|
||||||
@@ -352,9 +368,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (ca.type === CaType.ROOT) throw new BadRequestError({ message: "Root CA cannot generate CSR" });
|
if (ca.type === CaType.ROOT) throw new BadRequestError({ message: "Root CA cannot generate CSR" });
|
||||||
|
if (ca.activeCaCertId) throw new BadRequestError({ message: "CA already has a certificate installed" });
|
||||||
const [caCert] = await certificateAuthorityCertDAL.find({ caId: ca.id }, { sort: [["version", "desc"]] });
|
|
||||||
if (caCert) throw new BadRequestError({ message: "CA already has a certificate installed" });
|
|
||||||
|
|
||||||
const { caPrivateKey, caPublicKey } = await getCaCredentials({
|
const { caPrivateKey, caPublicKey } = await getCaCredentials({
|
||||||
caId,
|
caId,
|
||||||
@@ -399,6 +413,8 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||||
|
|
||||||
|
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
@@ -415,8 +431,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
if (ca.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.find({ caId: ca.id }, { sort: [["version", "desc"]] });
|
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
|
||||||
if (!caCert) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
|
||||||
|
|
||||||
const serialNumber = crypto.randomBytes(32).toString("hex");
|
const serialNumber = crypto.randomBytes(32).toString("hex");
|
||||||
|
|
||||||
@@ -494,13 +509,12 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
await certificateAuthorityDAL.transaction(async (tx) => {
|
await certificateAuthorityDAL.transaction(async (tx) => {
|
||||||
const newActiveCaCertVersion = caCert.version + 1;
|
const newCaCert = await certificateAuthorityCertDAL.create(
|
||||||
await certificateAuthorityCertDAL.create(
|
|
||||||
{
|
{
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
encryptedCertificate,
|
encryptedCertificate,
|
||||||
encryptedCertificateChain,
|
encryptedCertificateChain,
|
||||||
version: newActiveCaCertVersion,
|
version: caCert.version + 1,
|
||||||
caSecretId: caSecret.id
|
caSecretId: caSecret.id
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@@ -509,7 +523,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
await certificateAuthorityDAL.updateById(
|
await certificateAuthorityDAL.updateById(
|
||||||
ca.id,
|
ca.id,
|
||||||
{
|
{
|
||||||
activeCaCertVersion: newActiveCaCertVersion,
|
activeCaCertId: newCaCert.id,
|
||||||
notBefore: notBeforeDate,
|
notBefore: notBeforeDate,
|
||||||
notAfter: new Date(notAfter)
|
notAfter: new Date(notAfter)
|
||||||
},
|
},
|
||||||
@@ -538,10 +552,9 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// get latest parent CA certificate
|
// get latest parent CA certificate
|
||||||
const [parentCaCert] = await certificateAuthorityCertDAL.find(
|
if (!parentCa.activeCaCertId)
|
||||||
{ caId: parentCa.id },
|
throw new BadRequestError({ message: "Parent CA does not have a certificate installed" });
|
||||||
{ sort: [["version", "desc"]] }
|
const parentCaCert = await certificateAuthorityCertDAL.findById(parentCa.activeCaCertId);
|
||||||
);
|
|
||||||
|
|
||||||
const decryptedParentCaCert = await kmsDecryptor({
|
const decryptedParentCaCert = await kmsDecryptor({
|
||||||
cipherTextBlob: parentCaCert.encryptedCertificate
|
cipherTextBlob: parentCaCert.encryptedCertificate
|
||||||
@@ -586,7 +599,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const intermediateCert = await x509.X509CertificateGenerator.create({
|
const intermediateCert = await x509.X509CertificateGenerator.create({
|
||||||
serialNumber,
|
serialNumber,
|
||||||
subject: csrObj.subject,
|
subject: csrObj.subject,
|
||||||
issuer: caCertObj.subject,
|
issuer: parentCaCertObj.subject,
|
||||||
notBefore: notBeforeDate,
|
notBefore: notBeforeDate,
|
||||||
notAfter: new Date(notAfter),
|
notAfter: new Date(notAfter),
|
||||||
signingKey: parentCaPrivateKey,
|
signingKey: parentCaPrivateKey,
|
||||||
@@ -605,7 +618,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
ca.maxPathLength === -1 || !ca.maxPathLength ? undefined : ca.maxPathLength,
|
ca.maxPathLength === -1 || !ca.maxPathLength ? undefined : ca.maxPathLength,
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
await x509.AuthorityKeyIdentifierExtension.create(caCertObj, false),
|
await x509.AuthorityKeyIdentifierExtension.create(parentCaCertObj, false),
|
||||||
await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey)
|
await x509.SubjectKeyIdentifierExtension.create(csrObj.publicKey)
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -615,7 +628,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { caCert: parentCaCertificate, caCertChain: parentCaCertChain } = await getCaCertChain({
|
const { caCert: parentCaCertificate, caCertChain: parentCaCertChain } = await getCaCertChain({
|
||||||
caId: parentCa.id,
|
caCertId: parentCa.activeCaCertId,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -629,13 +642,12 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
await certificateAuthorityDAL.transaction(async (tx) => {
|
await certificateAuthorityDAL.transaction(async (tx) => {
|
||||||
const newActiveCaCertVersion = caCert.version + 1;
|
const newCaCert = await certificateAuthorityCertDAL.create(
|
||||||
await certificateAuthorityCertDAL.create(
|
|
||||||
{
|
{
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
encryptedCertificate,
|
encryptedCertificate,
|
||||||
encryptedCertificateChain,
|
encryptedCertificateChain,
|
||||||
version: newActiveCaCertVersion,
|
version: caCert.version + 1,
|
||||||
caSecretId: caSecret.id
|
caSecretId: caSecret.id
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
@@ -644,7 +656,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
await certificateAuthorityDAL.updateById(
|
await certificateAuthorityDAL.updateById(
|
||||||
ca.id,
|
ca.id,
|
||||||
{
|
{
|
||||||
activeCaCertVersion: newActiveCaCertVersion,
|
activeCaCertId: newCaCert.id,
|
||||||
notBefore: notBeforeDate,
|
notBefore: notBeforeDate,
|
||||||
notAfter: new Date(notAfter)
|
notAfter: new Date(notAfter)
|
||||||
},
|
},
|
||||||
@@ -703,11 +715,11 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return current certificate and certificate chain for CA
|
* Return current certificate and certificate chain for CA
|
||||||
* get latest?? ca cert
|
|
||||||
*/
|
*/
|
||||||
const getCaCert = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCertDTO) => {
|
const getCaCert = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCertDTO) => {
|
||||||
const ca = await certificateAuthorityDAL.findById(caId);
|
const ca = await certificateAuthorityDAL.findById(caId);
|
||||||
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
if (!ca) throw new BadRequestError({ message: "CA not found" });
|
||||||
|
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
actor,
|
actor,
|
||||||
@@ -723,7 +735,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { caCert, caCertChain, serialNumber } = await getCaCertChain({
|
const { caCert, caCertChain, serialNumber } = await getCaCertChain({
|
||||||
caId,
|
caCertId: ca.activeCaCertId,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -769,9 +781,9 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||||
|
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||||
|
|
||||||
const [caCert] = await certificateAuthorityCertDAL.find({ caId: ca.id }, { sort: [["version", "desc"]] });
|
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
|
||||||
if (!caCert) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
|
||||||
|
|
||||||
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
||||||
throw new BadRequestError({ message: "CA is expired" });
|
throw new BadRequestError({ message: "CA is expired" });
|
||||||
@@ -859,7 +871,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||||
caId,
|
caCertId: ca.activeCaCertId,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -905,8 +917,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
ProjectPermissionSub.CertificateAuthorities
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
);
|
);
|
||||||
|
|
||||||
const [caCert] = await certificateAuthorityCertDAL.find({ caId: ca.id }, { sort: [["version", "desc"]] });
|
if (ca.activeCaCertId) throw new BadRequestError({ message: "CA has already imported a certificate" });
|
||||||
if (caCert) throw new BadRequestError({ message: "CA has already imported a certificate" });
|
|
||||||
|
|
||||||
const certObj = new x509.X509Certificate(certificate);
|
const certObj = new x509.X509Certificate(certificate);
|
||||||
const maxPathLength = certObj.getExtension(x509.BasicConstraintsExtension)?.pathLength;
|
const maxPathLength = certObj.getExtension(x509.BasicConstraintsExtension)?.pathLength;
|
||||||
@@ -972,7 +983,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
await certificateAuthorityCertDAL.transaction(async (tx) => {
|
await certificateAuthorityCertDAL.transaction(async (tx) => {
|
||||||
await certificateAuthorityCertDAL.create(
|
const newCaCert = await certificateAuthorityCertDAL.create(
|
||||||
{
|
{
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
encryptedCertificate,
|
encryptedCertificate,
|
||||||
@@ -991,7 +1002,8 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
notBefore: new Date(certObj.notBefore),
|
notBefore: new Date(certObj.notBefore),
|
||||||
notAfter: new Date(certObj.notAfter),
|
notAfter: new Date(certObj.notAfter),
|
||||||
serialNumber: certObj.serialNumber,
|
serialNumber: certObj.serialNumber,
|
||||||
parentCaId: parentCa?.id
|
parentCaId: parentCa?.id,
|
||||||
|
activeCaCertId: newCaCert.id
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -1007,6 +1019,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const issueCertFromCa = async ({
|
const issueCertFromCa = async ({
|
||||||
caId,
|
caId,
|
||||||
certificateTemplateId,
|
certificateTemplateId,
|
||||||
|
pkiCollectionId,
|
||||||
friendlyName,
|
friendlyName,
|
||||||
commonName,
|
commonName,
|
||||||
altNames,
|
altNames,
|
||||||
@@ -1049,14 +1062,20 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
||||||
|
|
||||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||||
|
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||||
const [caCert] = await certificateAuthorityCertDAL.find({ caId: ca.id }, { sort: [["version", "desc"]] });
|
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
|
||||||
if (!caCert) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
|
||||||
|
|
||||||
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
||||||
throw new BadRequestError({ message: "CA is expired" });
|
throw new BadRequestError({ message: "CA is expired" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check PKI collection
|
||||||
|
if (pkiCollectionId) {
|
||||||
|
const pkiCollection = await pkiCollectionDAL.findById(pkiCollectionId);
|
||||||
|
if (!pkiCollection) throw new NotFoundError({ message: "PKI collection not found" });
|
||||||
|
if (pkiCollection.projectId !== ca.projectId) throw new BadRequestError({ message: "Invalid PKI collection" });
|
||||||
|
}
|
||||||
|
|
||||||
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
|
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -1198,6 +1217,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const cert = await certificateDAL.create(
|
const cert = await certificateDAL.create(
|
||||||
{
|
{
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
|
caCertId: caCert.id,
|
||||||
status: CertStatus.ACTIVE,
|
status: CertStatus.ACTIVE,
|
||||||
friendlyName: friendlyName || commonName,
|
friendlyName: friendlyName || commonName,
|
||||||
commonName,
|
commonName,
|
||||||
@@ -1217,11 +1237,21 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (pkiCollectionId) {
|
||||||
|
await pkiCollectionItemDAL.create(
|
||||||
|
{
|
||||||
|
pkiCollectionId,
|
||||||
|
certId: cert.id
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return cert;
|
return cert;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||||
caId: ca.id,
|
caCertId: caCert.id,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -1246,6 +1276,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
caId,
|
caId,
|
||||||
certificateTemplateId,
|
certificateTemplateId,
|
||||||
csr,
|
csr,
|
||||||
|
pkiCollectionId,
|
||||||
friendlyName,
|
friendlyName,
|
||||||
commonName,
|
commonName,
|
||||||
altNames,
|
altNames,
|
||||||
@@ -1288,14 +1319,21 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
||||||
|
|
||||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||||
|
if (!ca.activeCaCertId) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
||||||
|
|
||||||
const [caCert] = await certificateAuthorityCertDAL.find({ caId: ca.id }, { sort: [["version", "desc"]] });
|
const caCert = await certificateAuthorityCertDAL.findById(ca.activeCaCertId);
|
||||||
if (!caCert) throw new BadRequestError({ message: "CA does not have a certificate installed" });
|
|
||||||
|
|
||||||
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
if (ca.notAfter && new Date() > new Date(ca.notAfter)) {
|
||||||
throw new BadRequestError({ message: "CA is expired" });
|
throw new BadRequestError({ message: "CA is expired" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check PKI collection
|
||||||
|
if (pkiCollectionId) {
|
||||||
|
const pkiCollection = await pkiCollectionDAL.findById(pkiCollectionId);
|
||||||
|
if (!pkiCollection) throw new NotFoundError({ message: "PKI collection not found" });
|
||||||
|
if (pkiCollection.projectId !== ca.projectId) throw new BadRequestError({ message: "Invalid PKI collection" });
|
||||||
|
}
|
||||||
|
|
||||||
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
|
const certificateManagerKmsId = await getProjectKmsCertificateKeyId({
|
||||||
projectId: ca.projectId,
|
projectId: ca.projectId,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
@@ -1432,6 +1470,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
const cert = await certificateDAL.create(
|
const cert = await certificateDAL.create(
|
||||||
{
|
{
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
|
caCertId: caCert.id,
|
||||||
status: CertStatus.ACTIVE,
|
status: CertStatus.ACTIVE,
|
||||||
friendlyName: friendlyName || csrObj.subject,
|
friendlyName: friendlyName || csrObj.subject,
|
||||||
commonName: cn,
|
commonName: cn,
|
||||||
@@ -1451,11 +1490,21 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (pkiCollectionId) {
|
||||||
|
await pkiCollectionItemDAL.create(
|
||||||
|
{
|
||||||
|
pkiCollectionId,
|
||||||
|
certId: cert.id
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return cert;
|
return cert;
|
||||||
});
|
});
|
||||||
|
|
||||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||||
caId: ca.id,
|
caCertId: ca.activeCaCertId,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ export type TImportCertToCaDTO = {
|
|||||||
export type TIssueCertFromCaDTO = {
|
export type TIssueCertFromCaDTO = {
|
||||||
caId?: string;
|
caId?: string;
|
||||||
certificateTemplateId?: string;
|
certificateTemplateId?: string;
|
||||||
|
pkiCollectionId?: string;
|
||||||
friendlyName?: string;
|
friendlyName?: string;
|
||||||
commonName: string;
|
commonName: string;
|
||||||
altNames: string;
|
altNames: string;
|
||||||
@@ -100,6 +101,7 @@ export type TSignCertFromCaDTO = {
|
|||||||
caId?: string;
|
caId?: string;
|
||||||
csr: string;
|
csr: string;
|
||||||
certificateTemplateId?: string;
|
certificateTemplateId?: string;
|
||||||
|
pkiCollectionId?: string;
|
||||||
friendlyName?: string;
|
friendlyName?: string;
|
||||||
commonName?: string;
|
commonName?: string;
|
||||||
altNames: string;
|
altNames: string;
|
||||||
@@ -134,9 +136,9 @@ export type TGetCaCertChainsDTO = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TGetCaCertChainDTO = {
|
export type TGetCaCertChainDTO = {
|
||||||
caId: string;
|
caCertId: string;
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findOne">;
|
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ type TCertificateServiceFactoryDep = {
|
|||||||
certificateDAL: Pick<TCertificateDALFactory, "findOne" | "deleteById" | "update" | "find">;
|
certificateDAL: Pick<TCertificateDALFactory, "findOne" | "deleteById" | "update" | "find">;
|
||||||
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "findOne">;
|
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "findOne">;
|
||||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findOne">;
|
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
|
||||||
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "update">;
|
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "update">;
|
||||||
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
|
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
|
||||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "findById" | "transaction">;
|
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "findById" | "transaction">;
|
||||||
@@ -180,7 +180,7 @@ export const certificateServiceFactory = ({
|
|||||||
const certObj = new x509.X509Certificate(decryptedCert);
|
const certObj = new x509.X509Certificate(decryptedCert);
|
||||||
|
|
||||||
const { caCert, caCertChain } = await getCaCertChain({
|
const { caCert, caCertChain } = await getCaCertChain({
|
||||||
caId: ca.id,
|
caCertId: cert.caCertId,
|
||||||
certificateAuthorityDAL,
|
certificateAuthorityDAL,
|
||||||
certificateAuthorityCertDAL,
|
certificateAuthorityCertDAL,
|
||||||
projectDAL,
|
projectDAL,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const pkiCollectionServiceFactory = ({
|
|||||||
}: TPkiCollectionServiceFactoryDep) => {
|
}: TPkiCollectionServiceFactoryDep) => {
|
||||||
const createPkiCollection = async ({
|
const createPkiCollection = async ({
|
||||||
name,
|
name,
|
||||||
|
description,
|
||||||
projectId,
|
projectId,
|
||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
@@ -61,7 +62,8 @@ export const pkiCollectionServiceFactory = ({
|
|||||||
|
|
||||||
const pkiCollection = await pkiCollectionDAL.create({
|
const pkiCollection = await pkiCollectionDAL.create({
|
||||||
projectId,
|
projectId,
|
||||||
name
|
name,
|
||||||
|
description
|
||||||
});
|
});
|
||||||
|
|
||||||
return pkiCollection;
|
return pkiCollection;
|
||||||
@@ -92,6 +94,7 @@ export const pkiCollectionServiceFactory = ({
|
|||||||
const updatePkiCollection = async ({
|
const updatePkiCollection = async ({
|
||||||
collectionId,
|
collectionId,
|
||||||
name,
|
name,
|
||||||
|
description,
|
||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actor,
|
actor,
|
||||||
@@ -110,7 +113,8 @@ export const pkiCollectionServiceFactory = ({
|
|||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PkiCollections);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PkiCollections);
|
||||||
pkiCollection = await pkiCollectionDAL.updateById(collectionId, {
|
pkiCollection = await pkiCollectionDAL.updateById(collectionId, {
|
||||||
name
|
name,
|
||||||
|
description
|
||||||
});
|
});
|
||||||
|
|
||||||
return pkiCollection;
|
return pkiCollection;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { TProjectPermission } from "@app/lib/types";
|
|||||||
|
|
||||||
export type TCreatePkiCollectionDTO = {
|
export type TCreatePkiCollectionDTO = {
|
||||||
name: string;
|
name: string;
|
||||||
|
description: string;
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TGetPkiCollectionByIdDTO = {
|
export type TGetPkiCollectionByIdDTO = {
|
||||||
@@ -11,6 +12,7 @@ export type TGetPkiCollectionByIdDTO = {
|
|||||||
export type TUpdatePkiCollectionDTO = {
|
export type TUpdatePkiCollectionDTO = {
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
description?: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TDeletePkiCollectionDTO = {
|
export type TDeletePkiCollectionDTO = {
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ export const useRenewCa = () => {
|
|||||||
},
|
},
|
||||||
onSuccess: (_, { caId, projectSlug }) => {
|
onSuccess: (_, { caId, projectSlug }) => {
|
||||||
queryClient.invalidateQueries(workspaceKeys.getWorkspaceCas({ projectSlug }));
|
queryClient.invalidateQueries(workspaceKeys.getWorkspaceCas({ projectSlug }));
|
||||||
|
queryClient.invalidateQueries(caKeys.getCaById(caId));
|
||||||
queryClient.invalidateQueries(caKeys.getCaCert(caId));
|
queryClient.invalidateQueries(caKeys.getCaCert(caId));
|
||||||
queryClient.invalidateQueries(caKeys.getCaCerts(caId));
|
queryClient.invalidateQueries(caKeys.getCaCerts(caId));
|
||||||
queryClient.invalidateQueries(caKeys.getCaCsr(caId));
|
queryClient.invalidateQueries(caKeys.getCaCsr(caId));
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export type TCertificateAuthority = {
|
|||||||
notAfter?: string;
|
notAfter?: string;
|
||||||
notBefore?: string;
|
notBefore?: string;
|
||||||
keyAlgorithm: CertKeyAlgorithm;
|
keyAlgorithm: CertKeyAlgorithm;
|
||||||
|
activeCaCertId?: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
};
|
};
|
||||||
@@ -80,6 +81,7 @@ export type TCreateCertificateDTO = {
|
|||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
caId?: string;
|
caId?: string;
|
||||||
certificateTemplateId?: string;
|
certificateTemplateId?: string;
|
||||||
|
pkiCollectionId?: string;
|
||||||
friendlyName?: string;
|
friendlyName?: string;
|
||||||
commonName: string;
|
commonName: string;
|
||||||
altNames: string; // sans
|
altNames: string; // sans
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export type TPkiCollection = {
|
export type TPkiCollection = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
description: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
@@ -9,12 +10,14 @@ export type TPkiCollection = {
|
|||||||
export type TCreatePkiCollectionDTO = {
|
export type TCreatePkiCollectionDTO = {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
description: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TUpdatePkiCollectionTO = {
|
export type TUpdatePkiCollectionTO = {
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TDeletePkiCollectionDTO = {
|
export type TDeletePkiCollectionDTO = {
|
||||||
|
|||||||
+2
@@ -1,5 +1,6 @@
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
import { PkiCollectionSection } from "../PkiAlertsTab/components";
|
||||||
import { CertificateTemplatesSection } from "./components/CertificateTemplatesSection";
|
import { CertificateTemplatesSection } from "./components/CertificateTemplatesSection";
|
||||||
import { CertificatesSection } from "./components";
|
import { CertificatesSection } from "./components";
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ export const CertificatesTab = () => {
|
|||||||
animate={{ opacity: 1, translateX: 0 }}
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
exit={{ opacity: 0, translateX: 30 }}
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
>
|
>
|
||||||
|
<PkiCollectionSection />
|
||||||
<CertificateTemplatesSection />
|
<CertificateTemplatesSection />
|
||||||
<CertificatesSection />
|
<CertificatesSection />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
+42
-2
@@ -20,7 +20,8 @@ import {
|
|||||||
useGetCert,
|
useGetCert,
|
||||||
useGetCertTemplate,
|
useGetCertTemplate,
|
||||||
useListWorkspaceCas,
|
useListWorkspaceCas,
|
||||||
useListWorkspaceCertificateTemplates
|
useListWorkspaceCertificateTemplates,
|
||||||
|
useListWorkspacePkiCollections
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
import { caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
@@ -30,6 +31,7 @@ import { CertificateContent } from "./CertificateContent";
|
|||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
certificateTemplateId: z.string().optional(),
|
certificateTemplateId: z.string().optional(),
|
||||||
caId: z.string(),
|
caId: z.string(),
|
||||||
|
collectionId: z.string().optional(),
|
||||||
friendlyName: z.string(),
|
friendlyName: z.string(),
|
||||||
commonName: z.string().trim().min(1),
|
commonName: z.string().trim().min(1),
|
||||||
altNames: z.string(),
|
altNames: z.string(),
|
||||||
@@ -64,6 +66,10 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
status: CaStatus.ACTIVE
|
status: CaStatus.ACTIVE
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { data } = useListWorkspacePkiCollections({
|
||||||
|
workspaceId: currentWorkspace?.id || ""
|
||||||
|
});
|
||||||
|
|
||||||
const { data: templatesData } = useListWorkspaceCertificateTemplates({
|
const { data: templatesData } = useListWorkspaceCertificateTemplates({
|
||||||
workspaceId: currentWorkspace?.id || ""
|
workspaceId: currentWorkspace?.id || ""
|
||||||
});
|
});
|
||||||
@@ -116,7 +122,14 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
}
|
}
|
||||||
}, [selectedCertTemplate, cert]);
|
}, [selectedCertTemplate, cert]);
|
||||||
|
|
||||||
const onFormSubmit = async ({ caId, friendlyName, commonName, altNames, ttl }: FormData) => {
|
const onFormSubmit = async ({
|
||||||
|
caId,
|
||||||
|
friendlyName,
|
||||||
|
collectionId,
|
||||||
|
commonName,
|
||||||
|
altNames,
|
||||||
|
ttl
|
||||||
|
}: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!currentWorkspace?.slug) return;
|
if (!currentWorkspace?.slug) return;
|
||||||
|
|
||||||
@@ -124,6 +137,7 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
caId: !selectedCertTemplate ? caId : undefined,
|
caId: !selectedCertTemplate ? caId : undefined,
|
||||||
certificateTemplateId: selectedCertTemplate ? selectedCertTemplateId : undefined,
|
certificateTemplateId: selectedCertTemplate ? selectedCertTemplateId : undefined,
|
||||||
projectSlug: currentWorkspace.slug,
|
projectSlug: currentWorkspace.slug,
|
||||||
|
pkiCollectionId: collectionId,
|
||||||
friendlyName,
|
friendlyName,
|
||||||
commonName,
|
commonName,
|
||||||
altNames,
|
altNames,
|
||||||
@@ -230,6 +244,32 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="collectionId"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Certificate Collection (Optional)"
|
||||||
|
errorText={error?.message}
|
||||||
|
isError={Boolean(error)}
|
||||||
|
className="mt-4"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
defaultValue={field.value}
|
||||||
|
{...field}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
className="w-full"
|
||||||
|
isDisabled={Boolean(cert)}
|
||||||
|
>
|
||||||
|
{(data?.collections || []).map(({ id, name }) => (
|
||||||
|
<SelectItem value={id} key={`pki-collection-${id}`}>
|
||||||
|
{name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
|
|||||||
+22
-4
@@ -15,7 +15,8 @@ import {
|
|||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
name: z.string().trim().min(1)
|
name: z.string().trim().min(1),
|
||||||
|
description: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type FormData = z.infer<typeof schema>;
|
export type FormData = z.infer<typeof schema>;
|
||||||
@@ -49,16 +50,18 @@ export const PkiCollectionModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (pkiCollection) {
|
if (pkiCollection) {
|
||||||
reset({
|
reset({
|
||||||
name: pkiCollection.name
|
name: pkiCollection.name,
|
||||||
|
description: pkiCollection.description
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
reset({
|
reset({
|
||||||
name: ""
|
name: "",
|
||||||
|
description: ""
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [pkiCollection]);
|
}, [pkiCollection]);
|
||||||
|
|
||||||
const onFormSubmit = async ({ name }: FormData) => {
|
const onFormSubmit = async ({ name, description }: FormData) => {
|
||||||
try {
|
try {
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
|
|
||||||
@@ -67,12 +70,14 @@ export const PkiCollectionModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
await updatePkiCollection({
|
await updatePkiCollection({
|
||||||
collectionId: pkiCollection.id,
|
collectionId: pkiCollection.id,
|
||||||
name,
|
name,
|
||||||
|
description,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// create
|
// create
|
||||||
const { id: createdId } = await createPkiCollection({
|
const { id: createdId } = await createPkiCollection({
|
||||||
name,
|
name,
|
||||||
|
description,
|
||||||
projectId
|
projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -121,6 +126,19 @@ export const PkiCollectionModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="description"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Description" isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="A collection to house certificates for ACME device"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Button
|
<Button
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ export const PkiCollectionSection = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<div className="mb-4 flex justify-between">
|
<div className="mb-4 flex justify-between">
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">Certificate Collection</p>
|
<p className="text-xl font-semibold text-mineshaft-100">Certificate Collections</p>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionActions.Create}
|
I={ProjectPermissionActions.Create}
|
||||||
a={ProjectPermissionSub.PkiCollections}
|
a={ProjectPermissionSub.PkiCollections}
|
||||||
|
|||||||
+3
-1
@@ -46,11 +46,12 @@ export const PkiCollectionTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Name</Th>
|
<Th>Name</Th>
|
||||||
|
<Th>Description</Th>
|
||||||
<Th className="w-5" />
|
<Th className="w-5" />
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
<TBody>
|
<TBody>
|
||||||
{isLoading && <TableSkeleton columns={2} innerKey="pki-collections" />}
|
{isLoading && <TableSkeleton columns={3} innerKey="pki-collections" />}
|
||||||
{!isLoading &&
|
{!isLoading &&
|
||||||
data?.collections.map((pkiCollection) => {
|
data?.collections.map((pkiCollection) => {
|
||||||
return (
|
return (
|
||||||
@@ -62,6 +63,7 @@ export const PkiCollectionTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Td>{pkiCollection.name}</Td>
|
<Td>{pkiCollection.name}</Td>
|
||||||
|
<Td>{pkiCollection.description}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||||
|
|||||||
+5
-1
@@ -71,10 +71,14 @@ export const PkiCollectionDetailsSection = ({ collectionId, handlePopUpOpen }: P
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="mb-4">
|
||||||
<p className="text-sm font-semibold text-mineshaft-300">Name</p>
|
<p className="text-sm font-semibold text-mineshaft-300">Name</p>
|
||||||
<p className="text-sm text-mineshaft-300">{pkiCollection.name}</p>
|
<p className="text-sm text-mineshaft-300">{pkiCollection.name}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-mineshaft-300">Description</p>
|
||||||
|
<p className="text-sm text-mineshaft-300">{pkiCollection.description}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
Reference in New Issue
Block a user