mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 16:52:38 +00:00
merging PKI PRs
This commit is contained in:
@@ -6,7 +6,11 @@ import { z } from "zod";
|
||||
|
||||
import { ActionProjectType, ProjectType, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
@@ -21,6 +25,7 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||
|
||||
import { TCertificateAuthorityCrlDALFactory } from "../../ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
||||
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
|
||||
import {
|
||||
CertExtendedKeyUsage,
|
||||
CertExtendedKeyUsageOIDToName,
|
||||
@@ -75,6 +80,7 @@ type TCertificateAuthorityServiceFactoryDep = {
|
||||
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "getById" | "find">;
|
||||
certificateAuthorityQueue: TCertificateAuthorityQueueFactory; // TODO: Pick
|
||||
certificateDAL: Pick<TCertificateDALFactory, "transaction" | "create" | "find">;
|
||||
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
|
||||
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
|
||||
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "findById">;
|
||||
pkiCollectionItemDAL: Pick<TPkiCollectionItemDALFactory, "create">;
|
||||
@@ -96,6 +102,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
certificateTemplateDAL,
|
||||
certificateDAL,
|
||||
certificateBodyDAL,
|
||||
certificateSecretDAL,
|
||||
pkiCollectionDAL,
|
||||
pkiCollectionItemDAL,
|
||||
projectDAL,
|
||||
@@ -1157,7 +1164,10 @@ export const certificateAuthorityServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Create,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
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" });
|
||||
@@ -1373,6 +1383,23 @@ export const certificateAuthorityServiceFactory = ({
|
||||
const { cipherTextBlob: encryptedCertificate } = await kmsEncryptor({
|
||||
plainText: Buffer.from(new Uint8Array(leafCert.rawData))
|
||||
});
|
||||
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
|
||||
plainText: Buffer.from(skLeaf)
|
||||
});
|
||||
|
||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||
caCertId: caCert.id,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const certificateChainPem = `${issuingCaCertificate}\n${caCertChain}`.trim();
|
||||
|
||||
const { cipherTextBlob: encryptedCertificateChain } = await kmsEncryptor({
|
||||
plainText: Buffer.from(certificateChainPem)
|
||||
});
|
||||
|
||||
await certificateDAL.transaction(async (tx) => {
|
||||
const cert = await certificateDAL.create(
|
||||
@@ -1397,7 +1424,16 @@ export const certificateAuthorityServiceFactory = ({
|
||||
await certificateBodyDAL.create(
|
||||
{
|
||||
certId: cert.id,
|
||||
encryptedCertificate
|
||||
encryptedCertificate,
|
||||
encryptedCertificateChain
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await certificateSecretDAL.create(
|
||||
{
|
||||
certId: cert.id,
|
||||
encryptedPrivateKey
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -1415,17 +1451,9 @@ export const certificateAuthorityServiceFactory = ({
|
||||
return cert;
|
||||
});
|
||||
|
||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||
caCertId: caCert.id,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
return {
|
||||
certificate: leafCert.toString("pem"),
|
||||
certificateChain: `${issuingCaCertificate}\n${caCertChain}`.trim(),
|
||||
certificateChain: certificateChainPem,
|
||||
issuingCaCertificate,
|
||||
privateKey: skLeaf,
|
||||
serialNumber,
|
||||
@@ -1488,7 +1516,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionCertificateActions.Create,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
}
|
||||
@@ -1766,6 +1794,20 @@ export const certificateAuthorityServiceFactory = ({
|
||||
plainText: Buffer.from(new Uint8Array(leafCert.rawData))
|
||||
});
|
||||
|
||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||
caCertId: ca.activeCaCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const certificateChainPem = `${issuingCaCertificate}\n${caCertChain}`.trim();
|
||||
|
||||
const { cipherTextBlob: encryptedCertificateChain } = await kmsEncryptor({
|
||||
plainText: Buffer.from(certificateChainPem)
|
||||
});
|
||||
|
||||
await certificateDAL.transaction(async (tx) => {
|
||||
const cert = await certificateDAL.create(
|
||||
{
|
||||
@@ -1789,7 +1831,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
await certificateBodyDAL.create(
|
||||
{
|
||||
certId: cert.id,
|
||||
encryptedCertificate
|
||||
encryptedCertificate,
|
||||
encryptedCertificateChain
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -1807,17 +1850,9 @@ export const certificateAuthorityServiceFactory = ({
|
||||
return cert;
|
||||
});
|
||||
|
||||
const { caCert: issuingCaCertificate, caCertChain } = await getCaCertChain({
|
||||
caCertId: ca.activeCaCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
return {
|
||||
certificate: leafCert,
|
||||
certificateChain: `${issuingCaCertificate}\n${caCertChain}`.trim(),
|
||||
certificateChain: certificateChainPem,
|
||||
issuingCaCertificate,
|
||||
serialNumber,
|
||||
ca,
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import crypto from "node:crypto";
|
||||
|
||||
import * as x509 from "@peculiar/x509";
|
||||
|
||||
import { CrlReason } from "./certificate-types";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
|
||||
import { getProjectKmsCertificateKeyId } from "../project/project-fns";
|
||||
import { CrlReason, TGetCertificateCredentialsDTO } from "./certificate-types";
|
||||
|
||||
export const revocationReasonToCrlCode = (crlReason: CrlReason) => {
|
||||
switch (crlReason) {
|
||||
@@ -49,3 +54,47 @@ export const constructPemChainFromCerts = (certificates: x509.X509Certificate[])
|
||||
|
||||
export const splitPemChain = (pemText: string) =>
|
||||
pemText.match(/-----BEGIN CERTIFICATE-----[^-]+-----END CERTIFICATE-----/g) || [];
|
||||
|
||||
/**
|
||||
* Return the public and private key of certificate
|
||||
* Note: credentials are returned as PEM strings
|
||||
*/
|
||||
export const getCertificateCredentials = async ({
|
||||
certId,
|
||||
projectId,
|
||||
certificateSecretDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
}: TGetCertificateCredentialsDTO) => {
|
||||
const certificateSecret = await certificateSecretDAL.findOne({ certId });
|
||||
if (!certificateSecret)
|
||||
throw new NotFoundError({ message: `Certificate secret for certificate with ID '${certId}' not found` });
|
||||
|
||||
const keyId = await getProjectKmsCertificateKeyId({
|
||||
projectId,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
const kmsDecryptor = await kmsService.decryptWithKmsKey({
|
||||
kmsId: keyId
|
||||
});
|
||||
const decryptedPrivateKey = await kmsDecryptor({
|
||||
cipherTextBlob: certificateSecret.encryptedPrivateKey
|
||||
});
|
||||
|
||||
try {
|
||||
const skObj = crypto.createPrivateKey({ key: decryptedPrivateKey, format: "pem", type: "pkcs8" });
|
||||
const certPrivateKey = skObj.export({ format: "pem", type: "pkcs8" }).toString();
|
||||
|
||||
const pkObj = crypto.createPublicKey(skObj);
|
||||
const certPublicKey = pkObj.export({ format: "pem", type: "spki" }).toString();
|
||||
|
||||
return {
|
||||
certificateSecret,
|
||||
certPrivateKey,
|
||||
certPublicKey
|
||||
};
|
||||
} catch (error) {
|
||||
throw new BadRequestError({ message: `Failed to process private key for certificate with ID '${certId}'` });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
export type TCertificateSecretDALFactory = ReturnType<typeof certificateSecretDALFactory>;
|
||||
|
||||
export const certificateSecretDALFactory = (db: TDbClient) => {
|
||||
const certSecretOrm = ormify(db, TableName.CertificateSecret);
|
||||
return certSecretOrm;
|
||||
};
|
||||
@@ -5,7 +5,10 @@ import { createPrivateKey, createPublicKey, sign, verify } from "crypto";
|
||||
import { ActionProjectType, ProjectType } from "@app/db/schemas";
|
||||
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import {
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
|
||||
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
|
||||
@@ -19,7 +22,8 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
|
||||
|
||||
import { getCaCertChain, rebuildCaCrl } from "../certificate-authority/certificate-authority-fns";
|
||||
import { revocationReasonToCrlCode, splitPemChain } from "./certificate-fns";
|
||||
import { getCertificateCredentials, revocationReasonToCrlCode, splitPemChain } from "./certificate-fns";
|
||||
import { TCertificateSecretDALFactory } from "./certificate-secret-dal";
|
||||
import {
|
||||
CertExtendedKeyUsage,
|
||||
CertExtendedKeyUsageOIDToName,
|
||||
@@ -27,13 +31,16 @@ import {
|
||||
CertStatus,
|
||||
TDeleteCertDTO,
|
||||
TGetCertBodyDTO,
|
||||
TGetCertBundleDTO,
|
||||
TGetCertDTO,
|
||||
TGetCertPrivateKeyDTO,
|
||||
TImportCertDTO,
|
||||
TRevokeCertDTO
|
||||
} from "./certificate-types";
|
||||
|
||||
type TCertificateServiceFactoryDep = {
|
||||
certificateDAL: Pick<TCertificateDALFactory, "findOne" | "deleteById" | "update" | "find" | "transaction" | "create">;
|
||||
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "findOne" | "create">;
|
||||
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "findOne" | "create">;
|
||||
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
|
||||
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "findById">;
|
||||
@@ -53,6 +60,7 @@ export type TCertificateServiceFactory = ReturnType<typeof certificateServiceFac
|
||||
|
||||
export const certificateServiceFactory = ({
|
||||
certificateDAL,
|
||||
certificateSecretDAL,
|
||||
certificateBodyDAL,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
@@ -79,13 +87,56 @@ export const certificateServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
return {
|
||||
cert
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get certificate private key.
|
||||
*/
|
||||
const getCertPrivateKey = async ({
|
||||
serialNumber,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actor,
|
||||
actorOrgId
|
||||
}: TGetCertPrivateKeyDTO) => {
|
||||
const cert = await certificateDAL.findOne({ serialNumber });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId: cert.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
const { certPrivateKey } = await getCertificateCredentials({
|
||||
certId: cert.id,
|
||||
projectId: cert.projectId,
|
||||
certificateSecretDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
return {
|
||||
cert,
|
||||
certPrivateKey
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete certificate with serial number [serialNumber]
|
||||
*/
|
||||
@@ -101,7 +152,10 @@ export const certificateServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Certificates);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Delete,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
const deletedCert = await certificateDAL.deleteById(cert.id);
|
||||
|
||||
@@ -140,7 +194,10 @@ export const certificateServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Certificates);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Delete,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
if (cert.status === CertStatus.REVOKED) throw new Error("Certificate already revoked");
|
||||
|
||||
@@ -186,7 +243,10 @@ export const certificateServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
const certBody = await certificateBodyDAL.findOne({ certId: cert.id });
|
||||
|
||||
@@ -207,8 +267,14 @@ export const certificateServiceFactory = ({
|
||||
|
||||
let certificateChain = null;
|
||||
|
||||
// TODO(andrey): Update this to get certificateChain straight from the certificate body after the "store cert chain on cert body" PR gets merged
|
||||
if (cert.caCertId) {
|
||||
// On newer certs the certBody.encryptedCertificateChain column will always exist.
|
||||
// Older certs will have a caCertId which will be used as a fallback mechanism for structuring the chain.
|
||||
if (certBody.encryptedCertificateChain) {
|
||||
const decryptedCertChain = await kmsDecryptor({
|
||||
cipherTextBlob: certBody.encryptedCertificateChain
|
||||
});
|
||||
certificateChain = decryptedCertChain.toString();
|
||||
} else if (cert.caCertId) {
|
||||
const { caCert, caCertChain } = await getCaCertChain({
|
||||
caCertId: cert.caCertId,
|
||||
certificateAuthorityDAL,
|
||||
@@ -266,7 +332,10 @@ export const certificateServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Certificates);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Create,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
// Check PKI collection
|
||||
if (collectionId) {
|
||||
@@ -361,6 +430,10 @@ export const certificateServiceFactory = ({
|
||||
plainText: Buffer.from(certificatePem)
|
||||
});
|
||||
|
||||
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
|
||||
plainText: Buffer.from(privateKeyPem)
|
||||
});
|
||||
|
||||
// Extract Key Usage
|
||||
const keyUsagesExt = leafCert.getExtension("2.5.29.15") as x509.KeyUsagesExtension;
|
||||
|
||||
@@ -379,6 +452,10 @@ export const certificateServiceFactory = ({
|
||||
extendedKeyUsages = extKeyUsageExt.usages.map((ekuOid) => CertExtendedKeyUsageOIDToName[ekuOid as string]);
|
||||
}
|
||||
|
||||
const { cipherTextBlob: encryptedCertificateChain } = await kmsEncryptor({
|
||||
plainText: Buffer.from(chainPem)
|
||||
});
|
||||
|
||||
const cert = await certificateDAL.transaction(async (tx) => {
|
||||
try {
|
||||
const txCert = await certificateDAL.create(
|
||||
@@ -400,7 +477,16 @@ export const certificateServiceFactory = ({
|
||||
await certificateBodyDAL.create(
|
||||
{
|
||||
certId: txCert.id,
|
||||
encryptedCertificate
|
||||
encryptedCertificate,
|
||||
encryptedCertificateChain
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
await certificateSecretDAL.create(
|
||||
{
|
||||
certId: cert.id,
|
||||
encryptedPrivateKey
|
||||
},
|
||||
tx
|
||||
);
|
||||
@@ -435,11 +521,94 @@ export const certificateServiceFactory = ({
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Return certificate body and certificate chain for certificate with
|
||||
* serial number [serialNumber]
|
||||
*/
|
||||
const getCertBundle = async ({ serialNumber, actorId, actorAuthMethod, actor, actorOrgId }: TGetCertBundleDTO) => {
|
||||
const cert = await certificateDAL.findOne({ serialNumber });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId: cert.projectId,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
const certBody = await certificateBodyDAL.findOne({ certId: cert.id });
|
||||
|
||||
const certificateManagerKeyId = await getProjectKmsCertificateKeyId({
|
||||
projectId: cert.projectId,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const kmsDecryptor = await kmsService.decryptWithKmsKey({
|
||||
kmsId: certificateManagerKeyId
|
||||
});
|
||||
const decryptedCert = await kmsDecryptor({
|
||||
cipherTextBlob: certBody.encryptedCertificate
|
||||
});
|
||||
|
||||
const certObj = new x509.X509Certificate(decryptedCert);
|
||||
const certificate = certObj.toString("pem");
|
||||
|
||||
let certificateChain = null;
|
||||
|
||||
// On newer certs the certBody.encryptedCertificateChain column will always exist.
|
||||
// Older certs will have a caCertId which will be used as a fallback mechanism for structuring the chain.
|
||||
if (certBody.encryptedCertificateChain) {
|
||||
const decryptedCertChain = await kmsDecryptor({
|
||||
cipherTextBlob: certBody.encryptedCertificateChain
|
||||
});
|
||||
certificateChain = decryptedCertChain.toString();
|
||||
} else if (cert.caCertId) {
|
||||
const { caCert, caCertChain } = await getCaCertChain({
|
||||
caCertId: cert.caCertId,
|
||||
certificateAuthorityDAL,
|
||||
certificateAuthorityCertDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
certificateChain = `${caCert}\n${caCertChain}`.trim();
|
||||
}
|
||||
|
||||
const { certPrivateKey } = await getCertificateCredentials({
|
||||
certId: cert.id,
|
||||
projectId: cert.projectId,
|
||||
certificateSecretDAL,
|
||||
projectDAL,
|
||||
kmsService
|
||||
});
|
||||
|
||||
return {
|
||||
certificate,
|
||||
certificateChain,
|
||||
privateKey: certPrivateKey,
|
||||
serialNumber,
|
||||
cert
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
getCert,
|
||||
getCertPrivateKey,
|
||||
deleteCert,
|
||||
revokeCert,
|
||||
getCertBody,
|
||||
importCert
|
||||
importCert,
|
||||
getCertBundle
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,6 +2,10 @@ import * as x509 from "@peculiar/x509";
|
||||
|
||||
import { TProjectPermission } from "@app/lib/types";
|
||||
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { TCertificateSecretDALFactory } from "./certificate-secret-dal";
|
||||
|
||||
export enum CertStatus {
|
||||
ACTIVE = "active",
|
||||
REVOKED = "revoked"
|
||||
@@ -84,3 +88,19 @@ export type TImportCertDTO = {
|
||||
privateKeyPem: string;
|
||||
chainPem: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetCertPrivateKeyDTO = {
|
||||
serialNumber: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetCertBundleDTO = {
|
||||
serialNumber: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TGetCertificateCredentialsDTO = {
|
||||
certId: string;
|
||||
projectId: string;
|
||||
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "findOne">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
|
||||
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/servi
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionSecretActions,
|
||||
ProjectPermissionSshHostActions,
|
||||
ProjectPermissionSub
|
||||
@@ -948,7 +949,10 @@ export const projectServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
const certificates = await certificateDAL.find(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user