Rename pki alerting structures

This commit is contained in:
Tuan Dang
2024-08-09 08:27:53 -07:00
parent 556e4d62c4
commit fc4a20caf2
54 changed files with 1443 additions and 161 deletions
-12
View File
@@ -1,12 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TAlertDALFactory = ReturnType<typeof alertDALFactory>;
export const alertDALFactory = (db: TDbClient) => {
const alertOrm = ormify(db, TableName.Alert);
return {
...alertOrm
};
};
@@ -0,0 +1,12 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TPkiAlertDALFactory = ReturnType<typeof pkiAlertDALFactory>;
export const pkiAlertDALFactory = (db: TDbClient) => {
const pkiAlertOrm = ormify(db, TableName.PkiAlert);
return {
...pkiAlertOrm
};
};
@@ -5,18 +5,22 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
import { NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
import { TAlertDALFactory } from "./alert-dal";
import { TCreateAlertDTO, TDeleteAlertDTO, TGetAlertByIdDTO, TUpdateAlertDTO } from "./alert-types";
import { TPkiAlertDALFactory } from "./pki-alert-dal";
import { TCreateAlertDTO, TDeleteAlertDTO, TGetAlertByIdDTO, TUpdateAlertDTO } from "./pki-alert-types";
type TAlertServiceFactoryDep = {
alertDAL: TAlertDALFactory;
type TPkiAlertServiceFactoryDep = {
pkiAlertDAL: TPkiAlertDALFactory;
pkiCollectionDAL: TPkiCollectionDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
export type TAlertServiceFactory = ReturnType<typeof alertServiceFactory>;
export type TPkiAlertServiceFactory = ReturnType<typeof pkiAlertServiceFactory>;
export const alertServiceFactory = ({ alertDAL, pkiCollectionDAL, permissionService }: TAlertServiceFactoryDep) => {
export const pkiAlertServiceFactory = ({
pkiAlertDAL,
pkiCollectionDAL,
permissionService
}: TPkiAlertServiceFactoryDep) => {
const createPkiAlert = async ({
projectId,
name,
@@ -43,7 +47,7 @@ export const alertServiceFactory = ({ alertDAL, pkiCollectionDAL, permissionServ
if (pkiCollection.projectId !== projectId)
throw new UnauthorizedError({ message: "PKI collection not found in project" });
const alert = await alertDAL.create({
const alert = await pkiAlertDAL.create({
projectId,
pkiCollectionId,
name,
@@ -54,7 +58,7 @@ export const alertServiceFactory = ({ alertDAL, pkiCollectionDAL, permissionServ
};
const getPkiAlertById = async ({ alertId, actorId, actorAuthMethod, actor, actorOrgId }: TGetAlertByIdDTO) => {
const alert = await alertDAL.findById(alertId);
const alert = await pkiAlertDAL.findById(alertId);
if (!alert) throw new NotFoundError({ message: "Alert not found" });
const { permission } = await permissionService.getProjectPermission(
@@ -80,7 +84,7 @@ export const alertServiceFactory = ({ alertDAL, pkiCollectionDAL, permissionServ
actor,
actorOrgId
}: TUpdateAlertDTO) => {
let alert = await alertDAL.findById(alertId);
let alert = await pkiAlertDAL.findById(alertId);
if (!alert) throw new NotFoundError({ message: "Alert not found" });
const { permission } = await permissionService.getProjectPermission(
@@ -100,7 +104,7 @@ export const alertServiceFactory = ({ alertDAL, pkiCollectionDAL, permissionServ
throw new UnauthorizedError({ message: "PKI collection not found in project" });
}
alert = await alertDAL.updateById(alertId, {
alert = await pkiAlertDAL.updateById(alertId, {
name,
alertBeforeDays,
...(pkiCollectionId && { pkiCollectionId }),
@@ -111,7 +115,7 @@ export const alertServiceFactory = ({ alertDAL, pkiCollectionDAL, permissionServ
};
const deletePkiAlert = async ({ alertId, actorId, actorAuthMethod, actor, actorOrgId }: TDeleteAlertDTO) => {
let alert = await alertDAL.findById(alertId);
let alert = await pkiAlertDAL.findById(alertId);
if (!alert) throw new NotFoundError({ message: "Alert not found" });
const { permission } = await permissionService.getProjectPermission(
@@ -123,7 +127,7 @@ export const alertServiceFactory = ({ alertDAL, pkiCollectionDAL, permissionServ
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.PkiAlerts);
alert = await alertDAL.deleteById(alertId);
alert = await pkiAlertDAL.deleteById(alertId);
return alert;
};
@@ -0,0 +1,30 @@
import { TPkiCollectionItems } from "@app/db/schemas";
import { PkiItemType } from "./pki-collection-types";
/**
* Transforms a PKI Collection Item from the database to the expected API response format
*/
export const transformPkiCollectionItem = (pkiCollectionItem: TPkiCollectionItems) => {
let type: PkiItemType;
let itemId: string;
if (pkiCollectionItem.caId) {
type = PkiItemType.CA;
itemId = pkiCollectionItem.caId;
} else if (pkiCollectionItem.certId) {
type = PkiItemType.CERTIFICATE;
itemId = pkiCollectionItem.certId;
} else {
throw new Error("Invalid PKI Collection Item: must have either caId or certId");
}
return {
id: pkiCollectionItem.id,
pkiCollectionId: pkiCollectionItem.pkiCollectionId,
type,
itemId,
createdAt: pkiCollectionItem.createdAt,
updatedAt: pkiCollectionItem.updatedAt
};
};
@@ -0,0 +1,33 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex";
export type TPkiCollectionItemDALFactory = ReturnType<typeof pkiCollectionItemDALFactory>;
export const pkiCollectionItemDALFactory = (db: TDbClient) => {
const pkiCollectionItemOrm = ormify(db, TableName.PkiCollectionItem);
const countItemsInPkiCollection = async (collectionId: string) => {
try {
interface CountResult {
count: string;
}
const query = db
.replicaNode()(TableName.PkiCollectionItem)
.where(`${TableName.PkiCollectionItem}.pkiCollectionId`, collectionId);
const count = await query.count("*").first();
return parseInt((count as unknown as CountResult).count || "0", 10);
} catch (error) {
throw new DatabaseError({ error, name: "Count all project certificates" });
}
};
return {
...pkiCollectionItemOrm,
countItemsInPkiCollection
};
};
@@ -1,19 +1,31 @@
import { ForbiddenError } from "@casl/ability";
import { TPkiCollectionItems } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { NotFoundError } from "@app/lib/errors";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
import { TPkiCollectionDALFactory } from "./pki-collection-dal";
import { transformPkiCollectionItem } from "./pki-collection-fns";
import { TPkiCollectionItemDALFactory } from "./pki-collection-item-dal";
import {
PkiItemType,
TAddItemToPkiCollectionDTO,
TCreatePkiCollectionDTO,
TDeletePkiCollectionDTO,
TGetPkiCollectionByIdDTO,
TGetPkiCollectionItems,
TRemoveItemFromPkiCollectionDTO,
TUpdatePkiCollectionDTO
} from "./pki-collection-types";
type TPkiCollectionServiceFactoryDep = {
pkiCollectionDAL: TPkiCollectionDALFactory;
pkiCollectionDAL: TPkiCollectionDALFactory; // TODO: Pick
pkiCollectionItemDAL: TPkiCollectionItemDALFactory;
certificateAuthorityDAL: TCertificateAuthorityDALFactory;
certificateDAL: TCertificateDALFactory;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
@@ -21,6 +33,9 @@ export type TPkiCollectionServiceFactory = ReturnType<typeof pkiCollectionServic
export const pkiCollectionServiceFactory = ({
pkiCollectionDAL,
pkiCollectionItemDAL,
certificateAuthorityDAL,
certificateDAL,
permissionService
}: TPkiCollectionServiceFactoryDep) => {
const createPkiCollection = async ({
@@ -127,12 +142,168 @@ export const pkiCollectionServiceFactory = ({
return pkiCollection;
};
// TODO: add/remove pki collection items
const getPkiCollectionItems = async ({
collectionId,
offset = 0,
limit = 25,
actorId,
actorAuthMethod,
actor,
actorOrgId
}: TGetPkiCollectionItems) => {
const pkiCollection = await pkiCollectionDAL.findById(collectionId);
if (!pkiCollection) throw new NotFoundError({ message: "PKI collection not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
pkiCollection.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiCollections);
const pkiCollectionItems = await pkiCollectionItemDAL.find(
{ pkiCollectionId: collectionId },
{ offset, limit, sort: [["createdAt", "desc"]] }
);
const count = await pkiCollectionItemDAL.countItemsInPkiCollection(collectionId);
return {
pkiCollectionItems: pkiCollectionItems.map(transformPkiCollectionItem),
totalCount: count
};
};
const addItemToPkiCollection = async ({
collectionId,
actorId,
actorAuthMethod,
actor,
actorOrgId,
type,
itemId
}: TAddItemToPkiCollectionDTO) => {
const pkiCollection = await pkiCollectionDAL.findById(collectionId);
if (!pkiCollection) throw new NotFoundError({ message: "PKI collection not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
pkiCollection.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.PkiCollections
);
let pkiCollectionItem: TPkiCollectionItems;
switch (type) {
case PkiItemType.CA: {
// validate that CA has not already been added to PKI collection
const isCaAdded = await pkiCollectionItemDAL.findOne({
pkiCollectionId: collectionId,
caId: itemId
});
if (isCaAdded) throw new BadRequestError({ message: "CA is already part of the PKI collection" });
// validate that there exists a CA in same project as PKI collection
const ca = await certificateAuthorityDAL.findOne({
id: itemId,
projectId: pkiCollection.projectId
});
if (!ca) throw new NotFoundError({ message: "CA not found" });
pkiCollectionItem = await pkiCollectionItemDAL.create({
pkiCollectionId: collectionId,
caId: itemId
});
break;
}
case PkiItemType.CERTIFICATE: {
// validate that certificate has not already been added to PKI collection
const isCertAdded = await pkiCollectionItemDAL.findOne({
pkiCollectionId: collectionId,
certId: itemId
});
if (isCertAdded) throw new BadRequestError({ message: "Certificate already part of the PKI collection" });
// validate that there exists a certificate in same project as PKI collection
const cas = await certificateAuthorityDAL.find({ projectId: pkiCollection.projectId });
// TODO: consider making this more efficient
const [certificate] = await certificateDAL.find({
$in: {
caId: cas.map((ca) => ca.id)
},
id: itemId
});
if (!certificate) throw new NotFoundError({ message: "Certificate not found" });
pkiCollectionItem = await pkiCollectionItemDAL.create({
pkiCollectionId: collectionId,
certId: itemId
});
break;
}
default: {
throw new BadRequestError({ message: "Invalid PKI item type" });
}
}
return transformPkiCollectionItem(pkiCollectionItem);
};
const removeItemFromPkiCollection = async ({
collectionId,
actorId,
actorAuthMethod,
actor,
actorOrgId,
itemId
}: TRemoveItemFromPkiCollectionDTO) => {
const pkiCollection = await pkiCollectionDAL.findById(collectionId);
if (!pkiCollection) throw new NotFoundError({ message: "PKI collection not found" });
let pkiCollectionItem = await pkiCollectionItemDAL.findOne({
pkiCollectionId: collectionId,
id: itemId
});
if (!pkiCollectionItem) throw new NotFoundError({ message: "PKI collection item not found" });
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
pkiCollection.projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
ProjectPermissionSub.PkiCollections
);
pkiCollectionItem = await pkiCollectionItemDAL.deleteById(itemId);
return transformPkiCollectionItem(pkiCollectionItem);
};
return {
createPkiCollection,
getPkiCollectionById,
updatePkiCollection,
deletePkiCollection
deletePkiCollection,
getPkiCollectionItems,
addItemToPkiCollection,
removeItemFromPkiCollection
};
};
@@ -16,3 +16,25 @@ export type TUpdatePkiCollectionDTO = {
export type TDeletePkiCollectionDTO = {
collectionId: string;
} & Omit<TProjectPermission, "projectId">;
export enum PkiItemType {
CERTIFICATE = "certificate",
CA = "ca"
}
export type TGetPkiCollectionItems = {
collectionId: string;
offset: number;
limit: number;
} & Omit<TProjectPermission, "projectId">;
export type TAddItemToPkiCollectionDTO = {
collectionId: string;
type: PkiItemType;
itemId: string;
} & Omit<TProjectPermission, "projectId">;
export type TRemoveItemFromPkiCollectionDTO = {
collectionId: string;
itemId: string;
} & Omit<TProjectPermission, "projectId">;
@@ -13,7 +13,6 @@ import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TProjectPermission } from "@app/lib/types";
import { TAlertDALFactory } from "../alert/alert-dal";
import { ActorType } from "../auth/auth-type";
import { TCertificateDALFactory } from "../certificate/certificate-dal";
import { TCertificateAuthorityDALFactory } from "../certificate-authority/certificate-authority-dal";
@@ -23,6 +22,7 @@ import { TIdentityProjectMembershipRoleDALFactory } from "../identity-project/id
import { TKmsServiceFactory } from "../kms/kms-service";
import { TOrgDALFactory } from "../org/org-dal";
import { TOrgServiceFactory } from "../org/org-service";
import { TPkiAlertDALFactory } from "../pki-alert/pki-alert-dal";
import { TPkiCollectionDALFactory } from "../pki-collection/pki-collection-dal";
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
@@ -73,7 +73,7 @@ type TProjectServiceFactoryDep = {
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "find">;
certificateDAL: Pick<TCertificateDALFactory, "find" | "countCertificatesInProject">;
alertDAL: Pick<TAlertDALFactory, "find">;
pkiAlertDAL: Pick<TPkiAlertDALFactory, "find">;
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "find">;
permissionService: TPermissionServiceFactory;
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
@@ -113,7 +113,7 @@ export const projectServiceFactory = ({
certificateAuthorityDAL,
certificateDAL,
pkiCollectionDAL,
alertDAL,
pkiAlertDAL,
keyStore,
kmsService,
projectBotDAL
@@ -684,7 +684,7 @@ export const projectServiceFactory = ({
};
/**
* Return list of alerts configured for project
* Return list of (PKI) alerts configured for project
*/
const listProjectAlerts = async ({
projectId,
@@ -703,7 +703,7 @@ export const projectServiceFactory = ({
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiAlerts);
const alerts = await alertDAL.find({ projectId });
const alerts = await pkiAlertDAL.find({ projectId });
return {
alerts