Files
infisical/backend/src/services/certificate-authority/certificate-authority-service.ts
T

499 lines
16 KiB
TypeScript

import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, TableName } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types";
import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
import { TAppConnectionServiceFactory } from "../app-connection/app-connection-service";
import { TCertificateBodyDALFactory } from "../certificate/certificate-body-dal";
import { TCertificateDALFactory } from "../certificate/certificate-dal";
import { TCertificateSecretDALFactory } from "../certificate/certificate-secret-dal";
import { TKmsServiceFactory } from "../kms/kms-service";
import { TPkiSubscriberDALFactory } from "../pki-subscriber/pki-subscriber-dal";
import { TPkiSyncDALFactory } from "../pki-sync/pki-sync-dal";
import { TPkiSyncQueueFactory } from "../pki-sync/pki-sync-queue";
import { TProjectDALFactory } from "../project/project-dal";
import {
AcmeCertificateAuthorityFns,
castDbEntryToAcmeCertificateAuthority
} from "./acme/acme-certificate-authority-fns";
import {
TCreateAcmeCertificateAuthorityDTO,
TUpdateAcmeCertificateAuthorityDTO
} from "./acme/acme-certificate-authority-types";
import {
AzureAdCsCertificateAuthorityFns,
castDbEntryToAzureAdCsCertificateAuthority
} from "./azure-ad-cs/azure-ad-cs-certificate-authority-fns";
import {
TCreateAzureAdCsCertificateAuthorityDTO,
TUpdateAzureAdCsCertificateAuthorityDTO
} from "./azure-ad-cs/azure-ad-cs-certificate-authority-types";
import { TCertificateAuthorityDALFactory } from "./certificate-authority-dal";
import { CaType } from "./certificate-authority-enums";
import {
TCertificateAuthority,
TCreateCertificateAuthorityDTO,
TUpdateCertificateAuthorityDTO
} from "./certificate-authority-types";
import { TExternalCertificateAuthorityDALFactory } from "./external-certificate-authority-dal";
import { TInternalCertificateAuthorityServiceFactory } from "./internal/internal-certificate-authority-service";
import { TCreateInternalCertificateAuthorityDTO } from "./internal/internal-certificate-authority-types";
type TCertificateAuthorityServiceFactoryDep = {
appConnectionDAL: Pick<TAppConnectionDALFactory, "findById" | "update" | "updateById">;
appConnectionService: Pick<TAppConnectionServiceFactory, "validateAppConnectionUsageById">;
certificateAuthorityDAL: Pick<
TCertificateAuthorityDALFactory,
| "transaction"
| "create"
| "findById"
| "updateById"
| "deleteById"
| "findOne"
| "findByIdWithAssociatedCa"
| "findWithAssociatedCa"
| "findByNameAndProjectIdWithAssociatedCa"
>;
externalCertificateAuthorityDAL: Pick<TExternalCertificateAuthorityDALFactory, "create" | "update">;
internalCertificateAuthorityService: TInternalCertificateAuthorityServiceFactory;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
certificateSecretDAL: Pick<TCertificateSecretDALFactory, "create">;
kmsService: Pick<
TKmsServiceFactory,
"encryptWithKmsKey" | "generateKmsKey" | "createCipherPairWithDataKey" | "decryptWithKmsKey"
>;
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
pkiSyncDAL: Pick<TPkiSyncDALFactory, "find">;
pkiSyncQueue: Pick<TPkiSyncQueueFactory, "queuePkiSyncSyncCertificatesById">;
};
export type TCertificateAuthorityServiceFactory = ReturnType<typeof certificateAuthorityServiceFactory>;
export const certificateAuthorityServiceFactory = ({
certificateAuthorityDAL,
projectDAL,
permissionService,
internalCertificateAuthorityService,
appConnectionDAL,
appConnectionService,
externalCertificateAuthorityDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
kmsService,
pkiSubscriberDAL,
pkiSyncDAL,
pkiSyncQueue
}: TCertificateAuthorityServiceFactoryDep) => {
const acmeFns = AcmeCertificateAuthorityFns({
appConnectionDAL,
appConnectionService,
certificateAuthorityDAL,
externalCertificateAuthorityDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
kmsService,
pkiSubscriberDAL,
projectDAL,
pkiSyncDAL,
pkiSyncQueue
});
const azureAdCsFns = AzureAdCsCertificateAuthorityFns({
appConnectionDAL,
appConnectionService,
certificateAuthorityDAL,
externalCertificateAuthorityDAL,
certificateDAL,
certificateBodyDAL,
certificateSecretDAL,
kmsService,
pkiSubscriberDAL,
projectDAL,
pkiSyncDAL,
pkiSyncQueue
});
const createCertificateAuthority = async (
{ type, projectId, name, enableDirectIssuance, configuration, status }: TCreateCertificateAuthorityDTO,
actor: OrgServiceActor
) => {
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorId: actor.id,
projectId,
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 as TCreateInternalCertificateAuthorityDTO["configuration"]),
isInternal: true,
projectId,
enableDirectIssuance,
name
});
if (!ca.internalCa) {
throw new BadRequestError({
message: "Failed to create internal certificate authority"
});
}
return {
id: ca.id,
type,
enableDirectIssuance: ca.enableDirectIssuance,
name: ca.name,
projectId,
status,
configuration: ca.internalCa
} as TCertificateAuthority;
}
if (type === CaType.ACME) {
return acmeFns.createCertificateAuthority({
name,
projectId,
configuration: configuration as TCreateAcmeCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
status,
actor
});
}
if (type === CaType.AZURE_AD_CS) {
return azureAdCsFns.createCertificateAuthority({
name,
projectId,
configuration: configuration as TCreateAzureAdCsCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
status,
actor
});
}
throw new BadRequestError({ message: "Invalid certificate authority type" });
};
const findCertificateAuthorityByNameAndProjectId = async (
{ caName, type, projectId }: { caName: string; type: CaType; projectId: string },
actor: OrgServiceActor
) => {
const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa(
caName,
projectId
);
if (!certificateAuthority)
throw new NotFoundError({
message: `Could not find certificate authority with name "${caName}" in project "${projectId}"`
});
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?.id) {
throw new NotFoundError({
message: `Internal certificate authority with name "${caName}" in project "${projectId}" not found`
});
}
return {
id: certificateAuthority.id,
type,
enableDirectIssuance: certificateAuthority.enableDirectIssuance,
name: certificateAuthority.name,
projectId: certificateAuthority.projectId,
configuration: certificateAuthority.internalCa,
status: certificateAuthority.status
} as TCertificateAuthority;
}
if (certificateAuthority.externalCa?.type !== type) {
throw new NotFoundError({
message: `Could not find external certificate authority with name "${caName}" in project "${projectId}" and type "${type}"`
});
}
if (type === CaType.ACME) {
return castDbEntryToAcmeCertificateAuthority(certificateAuthority);
}
if (type === CaType.AZURE_AD_CS) {
return castDbEntryToAzureAdCsCertificateAuthority(certificateAuthority);
}
throw new BadRequestError({ message: "Invalid certificate authority type" });
};
const listCertificateAuthoritiesByProjectId = async (
{ projectId, type }: { projectId: string; type: CaType },
actor: OrgServiceActor
) => {
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorId: actor.id,
projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.CertificateAuthorities
);
if (type === CaType.INTERNAL) {
const cas = await certificateAuthorityDAL.findWithAssociatedCa({
[`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId,
$notNull: [`${TableName.InternalCertificateAuthority}.id` as "id"]
});
return cas
.filter((ca): ca is typeof ca & { internalCa: NonNullable<typeof ca.internalCa> } => Boolean(ca.internalCa))
.map((ca) => ({
id: ca.id,
type,
enableDirectIssuance: ca.enableDirectIssuance,
name: ca.name,
projectId: ca.projectId,
configuration: ca.internalCa,
status: ca.status
})) as TCertificateAuthority[];
}
if (type === CaType.ACME) {
return acmeFns.listCertificateAuthorities({ projectId });
}
if (type === CaType.AZURE_AD_CS) {
return azureAdCsFns.listCertificateAuthorities({ projectId });
}
throw new BadRequestError({ message: "Invalid certificate authority type" });
};
const updateCertificateAuthority = async (
{ caName, type, configuration, enableDirectIssuance, status, name, projectId }: TUpdateCertificateAuthorityDTO,
actor: OrgServiceActor
) => {
const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa(
caName,
projectId
);
if (!certificateAuthority)
throw new NotFoundError({
message: `Could not find certificate authority with name "${caName}" in project "${projectId}"`
});
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?.id) {
throw new NotFoundError({
message: `Internal certificate authority with name "${caName}" in project "${projectId}" not found`
});
}
const updatedCa = await internalCertificateAuthorityService.updateCaById({
isInternal: true,
enableDirectIssuance,
caId: certificateAuthority.id,
status,
name
});
if (!updatedCa.internalCa) {
throw new BadRequestError({
message: "Failed to update internal certificate authority"
});
}
return {
id: updatedCa.id,
type,
enableDirectIssuance: updatedCa.enableDirectIssuance,
name: updatedCa.name,
projectId: updatedCa.projectId,
configuration: updatedCa.internalCa,
status: updatedCa.status
} as TCertificateAuthority;
}
if (type === CaType.ACME) {
return acmeFns.updateCertificateAuthority({
id: certificateAuthority.id,
configuration: configuration as TUpdateAcmeCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
actor,
status,
name
});
}
if (type === CaType.AZURE_AD_CS) {
return azureAdCsFns.updateCertificateAuthority({
id: certificateAuthority.id,
configuration: configuration as TUpdateAzureAdCsCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
actor,
status,
name
});
}
throw new BadRequestError({ message: "Invalid certificate authority type" });
};
const deleteCertificateAuthority = async (
{ caName, type, projectId }: { caName: string; type: CaType; projectId: string },
actor: OrgServiceActor
) => {
const certificateAuthority = await certificateAuthorityDAL.findByNameAndProjectIdWithAssociatedCa(
caName,
projectId
);
if (!certificateAuthority)
throw new NotFoundError({
message: `Could not find certificate authority with name "${caName}" in project "${projectId}"`
});
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?.id && type === CaType.INTERNAL) {
throw new BadRequestError({
message: "Internal certificate authority cannot be deleted"
});
}
if (certificateAuthority.externalCa?.id && certificateAuthority.externalCa.type !== type) {
throw new BadRequestError({
message: "External certificate authority cannot be deleted"
});
}
await certificateAuthorityDAL.deleteById(certificateAuthority.id);
if (type === CaType.INTERNAL) {
return {
id: certificateAuthority.id,
type,
enableDirectIssuance: certificateAuthority.enableDirectIssuance,
name: certificateAuthority.name,
projectId: certificateAuthority.projectId,
configuration: certificateAuthority.internalCa,
status: certificateAuthority.status
} as TCertificateAuthority;
}
if (type === CaType.ACME) {
return castDbEntryToAcmeCertificateAuthority(certificateAuthority);
}
if (type === CaType.AZURE_AD_CS) {
return castDbEntryToAzureAdCsCertificateAuthority(certificateAuthority);
}
throw new BadRequestError({ message: "Invalid certificate authority type" });
};
const getAzureAdcsTemplates = async ({
caId,
projectId,
actor,
actorId,
actorAuthMethod,
actorOrgId
}: {
caId: string;
projectId: string;
actor: OrgServiceActor["type"];
actorId: string;
actorAuthMethod: OrgServiceActor["authMethod"];
actorOrgId?: string;
}) => {
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.CertificateAuthorities
);
return azureAdCsFns.getTemplates({
caId,
projectId
});
};
return {
createCertificateAuthority,
findCertificateAuthorityByNameAndProjectId,
listCertificateAuthoritiesByProjectId,
updateCertificateAuthority,
deleteCertificateAuthority,
getAzureAdcsTemplates
};
};