mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 22:46:24 +00:00
Merge pull request #4535 from Infisical/expand-notification-coverage
feat(notifications): expand notification coverage
This commit is contained in:
@@ -777,6 +777,20 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
.map((appUser) => appUser.email)
|
||||
.filter((email): email is string => !!email);
|
||||
|
||||
const approvalPath = `/projects/secret-management/${project.id}/approval`;
|
||||
const approvalUrl = `${cfg.SITE_URL}${approvalPath}`;
|
||||
|
||||
await notificationService.createUserNotifications(
|
||||
approverUsersForEmail.map((approver) => ({
|
||||
userId: approver.id,
|
||||
orgId: actorOrgId,
|
||||
type: NotificationType.ACCESS_POLICY_BYPASSED,
|
||||
title: "Secret Access Policy Bypassed",
|
||||
body: `**${actingUser.firstName} ${actingUser.lastName}** (${actingUser.email}) has accessed a secret in **${policy.secretPath || "/"}** in the **${environment?.name || permissionEnvironment}** environment for project **${project.name}** without obtaining the required approval.`,
|
||||
link: approvalPath
|
||||
}))
|
||||
);
|
||||
|
||||
if (recipientEmails.length > 0) {
|
||||
await smtpService.sendMail({
|
||||
recipients: recipientEmails,
|
||||
@@ -788,7 +802,7 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
bypassReason: bypassReason || "No reason provided",
|
||||
secretPath: policy.secretPath || "/",
|
||||
environment: environment?.name || permissionEnvironment,
|
||||
approvalUrl: `${cfg.SITE_URL}/projects/secret-management/${project.id}/approval`,
|
||||
approvalUrl,
|
||||
requestType: "access"
|
||||
},
|
||||
template: SmtpTemplates.AccessSecretRequestBypassed
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { TSecretApprovalRequests } from "@app/db/schemas";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
|
||||
import { NotificationType } from "@app/services/notification/notification-types";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
|
||||
@@ -11,6 +13,7 @@ type TSendApprovalEmails = {
|
||||
smtpService: Pick<TSmtpService, "sendMail">;
|
||||
projectId: string;
|
||||
secretApprovalRequest: TSecretApprovalRequests;
|
||||
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||
};
|
||||
|
||||
export const sendApprovalEmailsFn = async ({
|
||||
@@ -18,7 +21,8 @@ export const sendApprovalEmailsFn = async ({
|
||||
projectDAL,
|
||||
smtpService,
|
||||
projectId,
|
||||
secretApprovalRequest
|
||||
secretApprovalRequest,
|
||||
notificationService
|
||||
}: TSendApprovalEmails) => {
|
||||
const cfg = getConfig();
|
||||
|
||||
@@ -26,6 +30,17 @@ export const sendApprovalEmailsFn = async ({
|
||||
|
||||
const project = await projectDAL.findProjectWithOrg(projectId);
|
||||
|
||||
await notificationService.createUserNotifications(
|
||||
policy.userApprovers.map((approver) => ({
|
||||
userId: approver.userId,
|
||||
orgId: project.orgId,
|
||||
type: NotificationType.SECRET_CHANGE_REQUEST,
|
||||
title: "Secret Change Request",
|
||||
body: `You have a new secret change request pending your review for the project **${project.name}** in the organization **${project.organization.name}**.`,
|
||||
link: `/projects/secret-management/${project.id}/approval?requestId=${secretApprovalRequest.id}`
|
||||
}))
|
||||
);
|
||||
|
||||
// now we need to go through each of the reviewers and print out all the commits that they need to approve
|
||||
for await (const reviewerUser of policy.userApprovers) {
|
||||
await smtpService.sendMail({
|
||||
|
||||
@@ -28,6 +28,8 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||
import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
||||
import { TProjectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal";
|
||||
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
|
||||
import { NotificationType } from "@app/services/notification/notification-types";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||
@@ -140,6 +142,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
projectMicrosoftTeamsConfigDAL: Pick<TProjectMicrosoftTeamsConfigDALFactory, "getIntegrationDetailsByProject">;
|
||||
microsoftTeamsService: Pick<TMicrosoftTeamsServiceFactory, "sendNotification">;
|
||||
folderCommitService: Pick<TFolderCommitServiceFactory, "createCommit">;
|
||||
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||
};
|
||||
|
||||
export type TSecretApprovalRequestServiceFactory = ReturnType<typeof secretApprovalRequestServiceFactory>;
|
||||
@@ -172,7 +175,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
resourceMetadataDAL,
|
||||
projectMicrosoftTeamsConfigDAL,
|
||||
microsoftTeamsService,
|
||||
folderCommitService
|
||||
folderCommitService,
|
||||
notificationService
|
||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
||||
const requestCount = async ({
|
||||
projectId,
|
||||
@@ -1035,6 +1039,17 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
}
|
||||
});
|
||||
|
||||
await notificationService.createUserNotifications(
|
||||
approverUsers.map((approver) => ({
|
||||
userId: approver.id,
|
||||
orgId: project.orgId,
|
||||
type: NotificationType.SECRET_CHANGE_POLICY_BYPASSED,
|
||||
title: "Secret Change Policy Bypassed",
|
||||
body: `**${requestedByUser.firstName} ${requestedByUser.lastName}** (${requestedByUser.email}) has merged a secret to **${policy.secretPath}** in the **${env.name}** environment for project **${project.name}** without obtaining the required approval.`,
|
||||
link: `/projects/secret-management/${project.id}/approval`
|
||||
}))
|
||||
);
|
||||
|
||||
await smtpService.sendMail({
|
||||
recipients: approverUsers.filter((approver) => approver.email).map((approver) => approver.email!),
|
||||
subjectLine: "Infisical Secret Change Policy Bypassed",
|
||||
@@ -1446,7 +1461,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
secretApprovalPolicyDAL,
|
||||
secretApprovalRequest,
|
||||
smtpService,
|
||||
projectId
|
||||
projectId,
|
||||
notificationService
|
||||
});
|
||||
|
||||
return secretApprovalRequest;
|
||||
@@ -1813,7 +1829,8 @@ export const secretApprovalRequestServiceFactory = ({
|
||||
secretApprovalPolicyDAL,
|
||||
secretApprovalRequest,
|
||||
smtpService,
|
||||
projectId
|
||||
projectId,
|
||||
notificationService
|
||||
});
|
||||
return secretApprovalRequest;
|
||||
};
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
|
||||
import { NotificationType } from "@app/services/notification/notification-types";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
@@ -28,6 +30,7 @@ type TSecretRotationV2QueueServiceFactoryDep = {
|
||||
smtpService: Pick<TSmtpService, "sendMail">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findAllProjectMembers">;
|
||||
projectDAL: Pick<TProjectDALFactory, "findById">;
|
||||
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||
};
|
||||
|
||||
export const secretRotationV2QueueServiceFactory = async ({
|
||||
@@ -36,7 +39,8 @@ export const secretRotationV2QueueServiceFactory = async ({
|
||||
secretRotationV2Service,
|
||||
projectMembershipDAL,
|
||||
projectDAL,
|
||||
smtpService
|
||||
smtpService,
|
||||
notificationService
|
||||
}: TSecretRotationV2QueueServiceFactoryDep) => {
|
||||
const appCfg = getConfig();
|
||||
|
||||
@@ -152,6 +156,19 @@ export const secretRotationV2QueueServiceFactory = async ({
|
||||
|
||||
const rotationType = SECRET_ROTATION_NAME_MAP[type as SecretRotation];
|
||||
|
||||
const rotationPath = `/projects/secret-management/${projectId}/secrets/${environment.slug}`;
|
||||
|
||||
await notificationService.createUserNotifications(
|
||||
projectAdmins.map((admin) => ({
|
||||
userId: admin.userId,
|
||||
orgId: project.orgId,
|
||||
type: NotificationType.SECRET_ROTATION_FAILED,
|
||||
title: "Secret Rotation Failed",
|
||||
body: `Your **${rotationType}** rotation **${rotationName}** failed to rotate.`,
|
||||
link: rotationPath
|
||||
}))
|
||||
);
|
||||
|
||||
await smtpService.sendMail({
|
||||
recipients: projectAdmins.map((member) => member.user.email!).filter(Boolean),
|
||||
template: SmtpTemplates.SecretRotationFailed,
|
||||
@@ -165,9 +182,7 @@ export const secretRotationV2QueueServiceFactory = async ({
|
||||
secretPath: folder.path,
|
||||
environment: environment.name,
|
||||
projectName: project.name,
|
||||
rotationUrl: encodeURI(
|
||||
`${appCfg.SITE_URL}/projects/secret-management/${projectId}/secrets/${environment.slug}`
|
||||
)
|
||||
rotationUrl: encodeURI(`${appCfg.SITE_URL}${rotationPath}`)
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -21,6 +21,8 @@ import { decryptAppConnection } from "@app/services/app-connection/app-connectio
|
||||
import { TAppConnection } from "@app/services/app-connection/app-connection-types";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TNotificationServiceFactory } from "@app/services/notification/notification-service";
|
||||
import { NotificationType } from "@app/services/notification/notification-types";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
@@ -52,6 +54,7 @@ type TSecretRotationV2QueueServiceFactoryDep = {
|
||||
appConnectionDAL: Pick<TAppConnectionDALFactory, "updateById">;
|
||||
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "getItem">;
|
||||
notificationService: Pick<TNotificationServiceFactory, "createUserNotifications">;
|
||||
};
|
||||
|
||||
export type TSecretScanningV2QueueServiceFactory = Awaited<ReturnType<typeof secretScanningV2QueueServiceFactory>>;
|
||||
@@ -65,7 +68,8 @@ export const secretScanningV2QueueServiceFactory = async ({
|
||||
kmsService,
|
||||
auditLogService,
|
||||
keyStore,
|
||||
appConnectionDAL
|
||||
appConnectionDAL,
|
||||
notificationService
|
||||
}: TSecretRotationV2QueueServiceFactoryDep) => {
|
||||
const queueDataSourceFullScan = async (
|
||||
dataSource: TSecretScanningDataSourceWithConnection,
|
||||
@@ -592,16 +596,38 @@ export const secretScanningV2QueueServiceFactory = async ({
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
const subjectLine =
|
||||
payload.status === SecretScanningScanStatus.Completed
|
||||
? "Incident Alert: Secret(s) Leaked"
|
||||
: `Secret Scanning Failed`;
|
||||
|
||||
await notificationService.createUserNotifications(
|
||||
recipients.map((member) => ({
|
||||
userId: member.userId,
|
||||
orgId: project.orgId,
|
||||
type:
|
||||
payload.status === SecretScanningScanStatus.Completed
|
||||
? NotificationType.SECRET_SCANNING_SECRETS_DETECTED
|
||||
: NotificationType.SECRET_SCANNING_SCAN_FAILED,
|
||||
title: subjectLine,
|
||||
body:
|
||||
payload.status === SecretScanningScanStatus.Completed
|
||||
? `Uncovered **${payload.numberOfSecrets}** secret(s) ${payload.isDiffScan ? " from a recent commit to" : " in"} **${resourceName}**.`
|
||||
: `Encountered an error while attempting to scan the resource **${resourceName}**: ${payload.errorMessage}`,
|
||||
link:
|
||||
payload.status === SecretScanningScanStatus.Completed
|
||||
? `/projects/secret-scanning/${projectId}/findings?search=scanId:${payload.scanId}`
|
||||
: `/projects/secret-scanning/${projectId}/data-sources/${dataSource.type}/${dataSource.id}`
|
||||
}))
|
||||
);
|
||||
|
||||
await smtpService.sendMail({
|
||||
recipients: recipients.map((member) => member.user.email!).filter(Boolean),
|
||||
template:
|
||||
payload.status === SecretScanningScanStatus.Completed
|
||||
? SmtpTemplates.SecretScanningV2SecretsDetected
|
||||
: SmtpTemplates.SecretScanningV2ScanFailed,
|
||||
subjectLine:
|
||||
payload.status === SecretScanningScanStatus.Completed
|
||||
? "Incident Alert: Secret(s) Leaked"
|
||||
: `Secret Scanning Failed`,
|
||||
subjectLine,
|
||||
substitutions:
|
||||
payload.status === SecretScanningScanStatus.Completed
|
||||
? {
|
||||
|
||||
Reference in New Issue
Block a user