mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 16:52:38 +00:00
Finish docs for pki alerting + expose endpoints
This commit is contained in:
@@ -1,12 +1,84 @@
|
||||
import { TDbClient } from "@app/db";
|
||||
import { TableName } from "@app/db/schemas";
|
||||
import { DatabaseError } from "@app/lib/errors";
|
||||
import { ormify } from "@app/lib/knex";
|
||||
|
||||
import { PkiItemType } from "../pki-collection/pki-collection-types";
|
||||
|
||||
export type TPkiAlertDALFactory = ReturnType<typeof pkiAlertDALFactory>;
|
||||
|
||||
export const pkiAlertDALFactory = (db: TDbClient) => {
|
||||
const pkiAlertOrm = ormify(db, TableName.PkiAlert);
|
||||
|
||||
const getExpiringPkiCollectionItemsForAlerting = async () => {
|
||||
try {
|
||||
type AlertItem = {
|
||||
type: PkiItemType;
|
||||
id: string; // id of the CA or certificate
|
||||
expiryDate: Date;
|
||||
serialNumber: string;
|
||||
friendlyName: string;
|
||||
pkiCollectionId: string;
|
||||
alertId: string;
|
||||
alertName: string;
|
||||
alertBeforeDays: number;
|
||||
recipientEmails: string;
|
||||
};
|
||||
|
||||
// gets CAs and certificates as part of PKI collection items
|
||||
const combinedQuery = db
|
||||
.replicaNode()
|
||||
.select(
|
||||
db.raw("? as type", [PkiItemType.CA]),
|
||||
`${PkiItemType.CA}.id`,
|
||||
`${PkiItemType.CA}.notAfter as expiryDate`,
|
||||
`${PkiItemType.CA}.serialNumber`,
|
||||
`${PkiItemType.CA}.friendlyName`,
|
||||
"pci.pkiCollectionId"
|
||||
)
|
||||
.from(`${TableName.CertificateAuthority} as ${PkiItemType.CA}`)
|
||||
.join(`${TableName.PkiCollectionItem} as pci`, `${PkiItemType.CA}.id`, "pci.caId")
|
||||
.unionAll((qb) => {
|
||||
void qb
|
||||
.select(
|
||||
db.raw("? as type", [PkiItemType.CERTIFICATE]),
|
||||
`${PkiItemType.CERTIFICATE}.id`,
|
||||
`${PkiItemType.CERTIFICATE}.notAfter as expiryDate`,
|
||||
`${PkiItemType.CERTIFICATE}.serialNumber`,
|
||||
`${PkiItemType.CERTIFICATE}.friendlyName`,
|
||||
"pci.pkiCollectionId"
|
||||
)
|
||||
.from(`${TableName.Certificate} as ${PkiItemType.CERTIFICATE}`)
|
||||
.join(`${TableName.PkiCollectionItem} as pci`, `${PkiItemType.CERTIFICATE}.id`, "pci.certId");
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets alerts to send based on alertBeforeDays on PKI alerts connected to PKI collection items
|
||||
* Note: Results are clamped to 1-day window to avoid sending multiple alerts for the same item
|
||||
*/
|
||||
const alertQuery = db
|
||||
.replicaNode()
|
||||
.select("combined.*", "pa.id as alertId", "pa.name as alertName", "pa.alertBeforeDays", "pa.recipientEmails")
|
||||
.from(db.raw("(?) as combined", [combinedQuery]))
|
||||
.join(`${TableName.PkiAlert} as pa`, "combined.pkiCollectionId", "pa.pkiCollectionId")
|
||||
.whereRaw(
|
||||
`
|
||||
combined."expiryDate" <= CURRENT_TIMESTAMP + (pa."alertBeforeDays" * INTERVAL '1 day')
|
||||
AND combined."expiryDate" > CURRENT_TIMESTAMP + ((pa."alertBeforeDays" - 1) * INTERVAL '1 day')
|
||||
`
|
||||
)
|
||||
.orderBy("combined.expiryDate");
|
||||
|
||||
const results = (await alertQuery) as AlertItem[];
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
throw new DatabaseError({ error, name: "Get expiring PKI collection items for alerting" });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
getExpiringPkiCollectionItemsForAlerting,
|
||||
...pkiAlertOrm
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,7 +3,10 @@ import { ForbiddenError } from "@casl/ability";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||
import { groupBy } from "@app/lib/fn";
|
||||
import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-collection-dal";
|
||||
import { pkiItemTypeToNameMap } from "@app/services/pki-collection/pki-collection-types";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
|
||||
import { TPkiAlertDALFactory } from "./pki-alert-dal";
|
||||
import { TCreateAlertDTO, TDeleteAlertDTO, TGetAlertByIdDTO, TUpdateAlertDTO } from "./pki-alert-types";
|
||||
@@ -12,6 +15,7 @@ type TPkiAlertServiceFactoryDep = {
|
||||
pkiAlertDAL: TPkiAlertDALFactory;
|
||||
pkiCollectionDAL: TPkiCollectionDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||
smtpService: Pick<TSmtpService, "sendMail">;
|
||||
};
|
||||
|
||||
export type TPkiAlertServiceFactory = ReturnType<typeof pkiAlertServiceFactory>;
|
||||
@@ -19,8 +23,42 @@ export type TPkiAlertServiceFactory = ReturnType<typeof pkiAlertServiceFactory>;
|
||||
export const pkiAlertServiceFactory = ({
|
||||
pkiAlertDAL,
|
||||
pkiCollectionDAL,
|
||||
permissionService
|
||||
permissionService,
|
||||
smtpService
|
||||
}: TPkiAlertServiceFactoryDep) => {
|
||||
const sendPkiItemExpiryNotices = async () => {
|
||||
const allAlertItems = await pkiAlertDAL.getExpiringPkiCollectionItemsForAlerting();
|
||||
|
||||
const flattenedResults = allAlertItems.flatMap(({ recipientEmails, ...item }) =>
|
||||
recipientEmails.split(",").map((email) => ({
|
||||
...item,
|
||||
recipientEmail: email.trim()
|
||||
}))
|
||||
);
|
||||
|
||||
const groupedByEmail = groupBy(flattenedResults, (item) => item.recipientEmail);
|
||||
|
||||
for await (const [email, items] of Object.entries(groupedByEmail)) {
|
||||
const groupedByAlert = groupBy(items, (item) => item.alertId);
|
||||
for await (const [, alertItems] of Object.entries(groupedByAlert)) {
|
||||
await smtpService.sendMail({
|
||||
recipients: [email],
|
||||
subjectLine: `Infisical CA/Certificate expiration notice: ${alertItems[0].alertName}`,
|
||||
substitutions: {
|
||||
alertName: alertItems[0].alertName,
|
||||
alertBeforeDays: items[0].alertBeforeDays,
|
||||
items: alertItems.map((alertItem) => ({
|
||||
...alertItem,
|
||||
type: pkiItemTypeToNameMap[alertItem.type],
|
||||
expiryDate: new Date(alertItem.expiryDate).toString()
|
||||
}))
|
||||
},
|
||||
template: SmtpTemplates.PkiExpirationAlert
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const createPkiAlert = async ({
|
||||
projectId,
|
||||
name,
|
||||
@@ -108,7 +146,7 @@ export const pkiAlertServiceFactory = ({
|
||||
name,
|
||||
alertBeforeDays,
|
||||
...(pkiCollectionId && { pkiCollectionId }),
|
||||
...(emails && { recipientEmails: emails.join(",") }) // TODO: standardize recipient emails
|
||||
...(emails && { recipientEmails: emails.join(",") })
|
||||
});
|
||||
|
||||
return alert;
|
||||
@@ -132,6 +170,7 @@ export const pkiAlertServiceFactory = ({
|
||||
};
|
||||
|
||||
return {
|
||||
sendPkiItemExpiryNotices,
|
||||
createPkiAlert,
|
||||
getPkiAlertById,
|
||||
updatePkiAlert,
|
||||
|
||||
Reference in New Issue
Block a user