mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-17 06:34:57 +00:00
448 lines
15 KiB
TypeScript
448 lines
15 KiB
TypeScript
import { ForbiddenError, subject } from "@casl/ability";
|
|
|
|
import { ActionProjectType } from "@app/db/schemas";
|
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
|
import { ProjectPermissionPkiSyncActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
|
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
|
|
import { OrgServiceActor } from "@app/lib/types";
|
|
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
|
import { TAppConnectionServiceFactory } from "@app/services/app-connection/app-connection-service";
|
|
import { TPkiSubscriberDALFactory } from "@app/services/pki-subscriber/pki-subscriber-dal";
|
|
|
|
import { TPkiSyncDALFactory } from "./pki-sync-dal";
|
|
import { PkiSync, PkiSyncStatus } from "./pki-sync-enums";
|
|
import { enterprisePkiSyncCheck, getPkiSyncProviderCapabilities, listPkiSyncOptions } from "./pki-sync-fns";
|
|
import { PKI_SYNC_CONNECTION_MAP, PKI_SYNC_NAME_MAP } from "./pki-sync-maps";
|
|
import { TPkiSyncQueueFactory } from "./pki-sync-queue";
|
|
import {
|
|
TCreatePkiSyncDTO,
|
|
TDeletePkiSyncDTO,
|
|
TFindPkiSyncByIdDTO,
|
|
TListPkiSyncsByProjectId,
|
|
TPkiSync,
|
|
TTriggerPkiSyncImportCertificatesByIdDTO,
|
|
TTriggerPkiSyncRemoveCertificatesByIdDTO,
|
|
TTriggerPkiSyncSyncCertificatesByIdDTO,
|
|
TUpdatePkiSyncDTO
|
|
} from "./pki-sync-types";
|
|
|
|
const getDestinationAppType = (destination: PkiSync): AppConnection => {
|
|
const appConnection = PKI_SYNC_CONNECTION_MAP[destination];
|
|
if (!appConnection) {
|
|
throw new BadRequestError({
|
|
message: `Unsupported PKI sync destination: ${destination}`
|
|
});
|
|
}
|
|
return appConnection;
|
|
};
|
|
|
|
type TPkiSyncServiceFactoryDep = {
|
|
pkiSyncDAL: Pick<
|
|
TPkiSyncDALFactory,
|
|
"findById" | "findByProjectIdWithSubscribers" | "findByNameAndProjectId" | "create" | "updateById" | "deleteById"
|
|
>;
|
|
pkiSubscriberDAL: Pick<TPkiSubscriberDALFactory, "findById">;
|
|
appConnectionService: Pick<TAppConnectionServiceFactory, "connectAppConnectionById">;
|
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
|
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
|
pkiSyncQueue: Pick<
|
|
TPkiSyncQueueFactory,
|
|
"queuePkiSyncSyncCertificatesById" | "queuePkiSyncImportCertificatesById" | "queuePkiSyncRemoveCertificatesById"
|
|
>;
|
|
};
|
|
|
|
export type TPkiSyncServiceFactory = ReturnType<typeof pkiSyncServiceFactory>;
|
|
|
|
export const pkiSyncServiceFactory = ({
|
|
pkiSyncDAL,
|
|
pkiSubscriberDAL,
|
|
appConnectionService,
|
|
permissionService,
|
|
licenseService,
|
|
pkiSyncQueue
|
|
}: TPkiSyncServiceFactoryDep) => {
|
|
const createPkiSync = async (
|
|
{
|
|
name,
|
|
description,
|
|
destination,
|
|
isAutoSyncEnabled = true,
|
|
destinationConfig,
|
|
syncOptions = {},
|
|
subscriberId,
|
|
connectionId,
|
|
projectId
|
|
}: Omit<TCreatePkiSyncDTO, "auditLogInfo">,
|
|
actor: OrgServiceActor
|
|
): Promise<TPkiSync> => {
|
|
await enterprisePkiSyncCheck(licenseService, actor.orgId, destination);
|
|
|
|
const { permission } = await permissionService.getProjectPermission({
|
|
actor: actor.type,
|
|
actorId: actor.id,
|
|
actorAuthMethod: actor.authMethod,
|
|
actorOrgId: actor.orgId,
|
|
actionProjectType: ActionProjectType.CertificateManager,
|
|
projectId
|
|
});
|
|
|
|
let subscriber;
|
|
if (subscriberId) {
|
|
subscriber = await pkiSubscriberDAL.findById(subscriberId);
|
|
if (!subscriber || subscriber.projectId !== projectId) {
|
|
throw new NotFoundError({ message: "PKI subscriber not found" });
|
|
}
|
|
}
|
|
|
|
ForbiddenError.from(permission).throwUnlessCan(
|
|
ProjectPermissionPkiSyncActions.Create,
|
|
subscriber
|
|
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: subscriber.name })
|
|
: ProjectPermissionSub.PkiSyncs
|
|
);
|
|
|
|
// Get the destination app type based on PKI sync destination
|
|
const destinationApp = getDestinationAppType(destination);
|
|
|
|
// Validates permission to connect and app is valid for sync destination
|
|
await appConnectionService.connectAppConnectionById(destinationApp, connectionId, actor);
|
|
|
|
const providerCapabilities = getPkiSyncProviderCapabilities(destination);
|
|
const resolvedSyncOptions = {
|
|
...providerCapabilities,
|
|
...syncOptions
|
|
};
|
|
|
|
try {
|
|
const pkiSync = await pkiSyncDAL.create({
|
|
name,
|
|
description,
|
|
destination,
|
|
isAutoSyncEnabled,
|
|
destinationConfig,
|
|
syncOptions: resolvedSyncOptions,
|
|
subscriberId,
|
|
connectionId,
|
|
projectId,
|
|
...(isAutoSyncEnabled && { syncStatus: PkiSyncStatus.Pending })
|
|
});
|
|
|
|
if (pkiSync.isAutoSyncEnabled) {
|
|
await pkiSyncQueue.queuePkiSyncSyncCertificatesById({ syncId: pkiSync.id });
|
|
}
|
|
|
|
return pkiSync as TPkiSync;
|
|
} catch (err) {
|
|
if (err instanceof DatabaseError && (err.error as { code: string })?.code === "23505") {
|
|
throw new BadRequestError({
|
|
message: `A PKI Sync with the name "${name}" already exists for the project with ID "${projectId}"`
|
|
});
|
|
}
|
|
throw err;
|
|
}
|
|
};
|
|
|
|
const updatePkiSync = async (
|
|
{
|
|
id,
|
|
name,
|
|
description,
|
|
isAutoSyncEnabled,
|
|
destinationConfig,
|
|
syncOptions,
|
|
subscriberId,
|
|
connectionId
|
|
}: Omit<TUpdatePkiSyncDTO, "auditLogInfo" | "projectId">,
|
|
actor: OrgServiceActor
|
|
): Promise<TPkiSync> => {
|
|
const pkiSync = await pkiSyncDAL.findById(id);
|
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
|
|
|
const { permission } = await permissionService.getProjectPermission({
|
|
actor: actor.type,
|
|
actorId: actor.id,
|
|
actorAuthMethod: actor.authMethod,
|
|
actorOrgId: actor.orgId,
|
|
actionProjectType: ActionProjectType.CertificateManager,
|
|
projectId: pkiSync.projectId
|
|
});
|
|
|
|
let currentSubscriber;
|
|
if (pkiSync.subscriberId) {
|
|
currentSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
|
}
|
|
|
|
ForbiddenError.from(permission).throwUnlessCan(
|
|
ProjectPermissionPkiSyncActions.Edit,
|
|
currentSubscriber
|
|
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: currentSubscriber.name })
|
|
: ProjectPermissionSub.PkiSyncs
|
|
);
|
|
|
|
if (name && name !== pkiSync.name) {
|
|
const existingPkiSync = await pkiSyncDAL.findByNameAndProjectId(name, pkiSync.projectId);
|
|
if (existingPkiSync) {
|
|
throw new BadRequestError({ message: "PKI sync with this name already exists" });
|
|
}
|
|
}
|
|
|
|
if (subscriberId) {
|
|
const subscriber = await pkiSubscriberDAL.findById(subscriberId);
|
|
if (!subscriber || subscriber.projectId !== pkiSync.projectId) {
|
|
throw new NotFoundError({ message: "PKI subscriber not found" });
|
|
}
|
|
}
|
|
|
|
if (connectionId && connectionId !== pkiSync.connectionId) {
|
|
const destinationApp = getDestinationAppType(pkiSync.destination);
|
|
await appConnectionService.connectAppConnectionById(destinationApp, connectionId, actor);
|
|
}
|
|
|
|
let resolvedSyncOptions = syncOptions;
|
|
if (syncOptions) {
|
|
const providerCapabilities = getPkiSyncProviderCapabilities(pkiSync.destination);
|
|
|
|
if (syncOptions.canImportCertificates && !providerCapabilities.canImportCertificates) {
|
|
throw new BadRequestError({
|
|
message: `Certificate import is not supported for ${PKI_SYNC_NAME_MAP[pkiSync.destination]} PKI sync destination`
|
|
});
|
|
}
|
|
|
|
if (syncOptions.canRemoveCertificates && !providerCapabilities.canRemoveCertificates) {
|
|
throw new BadRequestError({
|
|
message: `Certificate removal cannot be enabled for ${PKI_SYNC_NAME_MAP[pkiSync.destination]} PKI sync destination`
|
|
});
|
|
}
|
|
|
|
resolvedSyncOptions = {
|
|
...providerCapabilities,
|
|
...syncOptions
|
|
};
|
|
}
|
|
|
|
const updatedPkiSync = await pkiSyncDAL.updateById(id, {
|
|
name,
|
|
description,
|
|
isAutoSyncEnabled,
|
|
destinationConfig,
|
|
syncOptions: resolvedSyncOptions,
|
|
subscriberId,
|
|
connectionId
|
|
});
|
|
|
|
return updatedPkiSync as TPkiSync;
|
|
};
|
|
|
|
const deletePkiSync = async (
|
|
{ id }: Omit<TDeletePkiSyncDTO, "auditLogInfo" | "projectId">,
|
|
actor: OrgServiceActor
|
|
) => {
|
|
const pkiSync = await pkiSyncDAL.findById(id);
|
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
|
|
|
const { permission } = await permissionService.getProjectPermission({
|
|
actor: actor.type,
|
|
actorId: actor.id,
|
|
actorAuthMethod: actor.authMethod,
|
|
actorOrgId: actor.orgId,
|
|
actionProjectType: ActionProjectType.CertificateManager,
|
|
projectId: pkiSync.projectId
|
|
});
|
|
|
|
let pkiSyncSubscriber;
|
|
if (pkiSync.subscriberId) {
|
|
pkiSyncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
|
}
|
|
|
|
ForbiddenError.from(permission).throwUnlessCan(
|
|
ProjectPermissionPkiSyncActions.Delete,
|
|
pkiSyncSubscriber
|
|
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: pkiSyncSubscriber.name })
|
|
: ProjectPermissionSub.PkiSyncs
|
|
);
|
|
|
|
return pkiSyncDAL.deleteById(id);
|
|
};
|
|
|
|
const listPkiSyncsByProjectId = async (
|
|
{ projectId }: TListPkiSyncsByProjectId,
|
|
actor: OrgServiceActor
|
|
): Promise<TPkiSync[]> => {
|
|
const { permission } = await permissionService.getProjectPermission({
|
|
actor: actor.type,
|
|
actorId: actor.id,
|
|
actorAuthMethod: actor.authMethod,
|
|
actorOrgId: actor.orgId,
|
|
actionProjectType: ActionProjectType.CertificateManager,
|
|
projectId
|
|
});
|
|
|
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Read, ProjectPermissionSub.PkiSyncs);
|
|
|
|
const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId);
|
|
|
|
return pkiSyncsWithSubscribers as TPkiSync[];
|
|
};
|
|
|
|
const findPkiSyncById = async ({ id, projectId }: TFindPkiSyncByIdDTO, actor: OrgServiceActor) => {
|
|
const pkiSync = await pkiSyncDAL.findById(id);
|
|
if (!pkiSync || (projectId && pkiSync.projectId !== projectId)) {
|
|
throw new NotFoundError({
|
|
message: `Could not find PKI Sync with ID "${id}"`
|
|
});
|
|
}
|
|
|
|
const { permission } = await permissionService.getProjectPermission({
|
|
actor: actor.type,
|
|
actorId: actor.id,
|
|
actorAuthMethod: actor.authMethod,
|
|
actorOrgId: actor.orgId,
|
|
actionProjectType: ActionProjectType.CertificateManager,
|
|
projectId: pkiSync.projectId
|
|
});
|
|
|
|
let findSubscriber;
|
|
if (pkiSync.subscriberId) {
|
|
findSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
|
}
|
|
|
|
ForbiddenError.from(permission).throwUnlessCan(
|
|
ProjectPermissionPkiSyncActions.Read,
|
|
findSubscriber
|
|
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: findSubscriber.name })
|
|
: ProjectPermissionSub.PkiSyncs
|
|
);
|
|
|
|
const result = {
|
|
...pkiSync,
|
|
subscriber: findSubscriber ? { id: findSubscriber.id, name: findSubscriber.name } : null
|
|
} as TPkiSync;
|
|
|
|
return result;
|
|
};
|
|
|
|
const triggerPkiSyncSyncCertificatesById = async (
|
|
{ id }: Omit<TTriggerPkiSyncSyncCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
|
actor: OrgServiceActor
|
|
) => {
|
|
const pkiSync = await pkiSyncDAL.findById(id);
|
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
|
|
|
const { permission } = await permissionService.getProjectPermission({
|
|
actor: actor.type,
|
|
actorId: actor.id,
|
|
actorAuthMethod: actor.authMethod,
|
|
actorOrgId: actor.orgId,
|
|
actionProjectType: ActionProjectType.CertificateManager,
|
|
projectId: pkiSync.projectId
|
|
});
|
|
|
|
let syncSubscriber;
|
|
if (pkiSync.subscriberId) {
|
|
syncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
|
}
|
|
|
|
ForbiddenError.from(permission).throwUnlessCan(
|
|
ProjectPermissionPkiSyncActions.SyncCertificates,
|
|
syncSubscriber
|
|
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: syncSubscriber.name })
|
|
: ProjectPermissionSub.PkiSyncs
|
|
);
|
|
|
|
await pkiSyncQueue.queuePkiSyncSyncCertificatesById({ syncId: id });
|
|
|
|
return { message: "PKI sync job added to queue successfully" };
|
|
};
|
|
|
|
const triggerPkiSyncImportCertificatesById = async (
|
|
{ id }: Omit<TTriggerPkiSyncImportCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
|
actor: OrgServiceActor
|
|
) => {
|
|
const pkiSync = await pkiSyncDAL.findById(id);
|
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
|
|
|
const { permission } = await permissionService.getProjectPermission({
|
|
actor: actor.type,
|
|
actorId: actor.id,
|
|
actorAuthMethod: actor.authMethod,
|
|
actorOrgId: actor.orgId,
|
|
actionProjectType: ActionProjectType.CertificateManager,
|
|
projectId: pkiSync.projectId
|
|
});
|
|
|
|
// Check if the PKI sync destination supports importing certificates
|
|
const syncOptions = listPkiSyncOptions().find((option) => option.destination === pkiSync.destination);
|
|
if (!syncOptions?.canImportCertificates) {
|
|
throw new BadRequestError({
|
|
message: `Certificate import is not supported for ${pkiSync.destination} PKI sync destination`
|
|
});
|
|
}
|
|
|
|
let importSubscriber;
|
|
if (pkiSync.subscriberId) {
|
|
importSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
|
}
|
|
|
|
ForbiddenError.from(permission).throwUnlessCan(
|
|
ProjectPermissionPkiSyncActions.ImportCertificates,
|
|
importSubscriber
|
|
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: importSubscriber.name })
|
|
: ProjectPermissionSub.PkiSyncs
|
|
);
|
|
|
|
await pkiSyncQueue.queuePkiSyncImportCertificatesById({ syncId: id });
|
|
|
|
return { message: "PKI sync import job added to queue successfully" };
|
|
};
|
|
|
|
const triggerPkiSyncRemoveCertificatesById = async (
|
|
{ id }: Omit<TTriggerPkiSyncRemoveCertificatesByIdDTO, "auditLogInfo" | "projectId">,
|
|
actor: OrgServiceActor
|
|
) => {
|
|
const pkiSync = await pkiSyncDAL.findById(id);
|
|
if (!pkiSync) throw new NotFoundError({ message: "PKI sync not found" });
|
|
|
|
const { permission } = await permissionService.getProjectPermission({
|
|
actor: actor.type,
|
|
actorId: actor.id,
|
|
actorAuthMethod: actor.authMethod,
|
|
actorOrgId: actor.orgId,
|
|
actionProjectType: ActionProjectType.CertificateManager,
|
|
projectId: pkiSync.projectId
|
|
});
|
|
|
|
let removeSubscriber;
|
|
if (pkiSync.subscriberId) {
|
|
removeSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
|
}
|
|
|
|
ForbiddenError.from(permission).throwUnlessCan(
|
|
ProjectPermissionPkiSyncActions.RemoveCertificates,
|
|
removeSubscriber
|
|
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: removeSubscriber.name })
|
|
: ProjectPermissionSub.PkiSyncs
|
|
);
|
|
|
|
await pkiSyncQueue.queuePkiSyncRemoveCertificatesById({ syncId: id });
|
|
|
|
return { message: "PKI sync remove job added to queue successfully" };
|
|
};
|
|
|
|
const getPkiSyncOptions = () => {
|
|
return listPkiSyncOptions();
|
|
};
|
|
|
|
return {
|
|
createPkiSync,
|
|
updatePkiSync,
|
|
deletePkiSync,
|
|
listPkiSyncsByProjectId,
|
|
findPkiSyncById,
|
|
triggerPkiSyncSyncCertificatesById,
|
|
triggerPkiSyncImportCertificatesById,
|
|
triggerPkiSyncRemoveCertificatesById,
|
|
getPkiSyncOptions
|
|
};
|
|
};
|