From b163a6c5ad7405ba98190ebc0e180e80508820a2 Mon Sep 17 00:00:00 2001 From: Sheen Capadngan Date: Thu, 5 Sep 2024 00:42:21 +0800 Subject: [PATCH] feat: integration to access request approval --- .../access-approval-policy-fns.ts | 71 +++++++++++++++++++ .../access-approval-request-service.ts | 37 ++++++++-- backend/src/server/routes/index.ts | 4 +- 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/backend/src/ee/services/access-approval-policy/access-approval-policy-fns.ts b/backend/src/ee/services/access-approval-policy/access-approval-policy-fns.ts index 7b0a2681f..c47dbd238 100644 --- a/backend/src/ee/services/access-approval-policy/access-approval-policy-fns.ts +++ b/backend/src/ee/services/access-approval-policy/access-approval-policy-fns.ts @@ -2,6 +2,11 @@ import { ForbiddenError, subject } from "@casl/ability"; import { BadRequestError } from "@app/lib/errors"; import { ActorType } from "@app/services/auth/auth-type"; +import { TKmsServiceFactory } from "@app/services/kms/kms-service"; +import { TProjectDALFactory } from "@app/services/project/project-dal"; +import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal"; +import { triggerSlackNotification } from "@app/services/slack/slack-fns"; +import { SlackTriggerFeature } from "@app/services/slack/slack-types"; import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission"; import { TVerifyApprovers } from "./access-approval-policy-types"; @@ -34,3 +39,69 @@ export const verifyApprovers = async ({ } } }; + +type TTriggerAccessRequestSlackNotif = { + projectId: string; + projectName: string; + requesterFullName: string; + isTemporary: boolean; + requesterEmail: string; + secretPath: string; + environment: string; + permissions: string[]; + approvalUrl: string; + projectDAL: Pick; + kmsService: Pick; + projectSlackConfigDAL: Pick; +}; + +export const triggerAccessRequestSlackNotif = async ({ + projectId, + projectName, + requesterFullName, + isTemporary, + requesterEmail, + secretPath, + environment, + permissions, + approvalUrl, + projectDAL, + kmsService, + projectSlackConfigDAL +}: TTriggerAccessRequestSlackNotif) => { + const messageBody = `${requesterFullName} (${requesterEmail}) has requested ${ + isTemporary ? "temporary" : "permanent" + } access to ${secretPath} in the ${environment} environment of ${projectName}. + + The following permissions are requested: ${permissions.join(", ")} + + View the request and approve or deny it <${approvalUrl}|here>.`; + + const payloadBlocks = [ + { + type: "header", + text: { + type: "plain_text", + text: "New access approval request pending for review", + emoji: true + } + }, + { + type: "section", + text: { + type: "mrkdwn", + text: messageBody + } + } + ]; + + await triggerSlackNotification({ + projectId, + projectDAL, + kmsService, + payloadMessage: messageBody, + projectSlackConfigDAL, + payloadBlocks, + feature: SlackTriggerFeature.ACCESS_REQUEST + }); +}; diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-service.ts b/backend/src/ee/services/access-approval-request/access-approval-request-service.ts index 544229609..1e0447d0d 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-service.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-service.ts @@ -5,15 +5,17 @@ import { ProjectMembershipRole } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { alphaNumericNanoId } from "@app/lib/nanoid"; +import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal"; +import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal"; import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service"; import { TUserDALFactory } from "@app/services/user/user-dal"; import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-policy/access-approval-policy-approver-dal"; import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal"; -import { verifyApprovers } from "../access-approval-policy/access-approval-policy-fns"; +import { triggerAccessRequestSlackNotif, verifyApprovers } from "../access-approval-policy/access-approval-policy-fns"; import { TPermissionServiceFactory } from "../permission/permission-service"; import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal"; import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types"; @@ -33,7 +35,10 @@ type TSecretApprovalRequestServiceFactoryDep = { permissionService: Pick; accessApprovalPolicyApproverDAL: Pick; projectEnvDAL: Pick; - projectDAL: Pick; + projectDAL: Pick< + TProjectDALFactory, + "checkProjectUpgradeStatus" | "findProjectBySlug" | "findProjectWithOrg" | "findById" + >; accessApprovalRequestDAL: Pick< TAccessApprovalRequestDALFactory, | "create" @@ -56,6 +61,8 @@ type TSecretApprovalRequestServiceFactoryDep = { TUserDALFactory, "findUserByProjectMembershipId" | "findUsersByProjectMembershipIds" | "find" | "findById" >; + kmsService: Pick; + projectSlackConfigDAL: Pick; }; export type TAccessApprovalRequestServiceFactory = ReturnType; @@ -71,7 +78,9 @@ export const accessApprovalRequestServiceFactory = ({ accessApprovalPolicyApproverDAL, additionalPrivilegeDAL, smtpService, - userDAL + userDAL, + kmsService, + projectSlackConfigDAL }: TSecretApprovalRequestServiceFactoryDep) => { const createAccessApprovalRequest = async ({ isTemporary, @@ -166,13 +175,31 @@ export const accessApprovalRequestServiceFactory = ({ tx ); + const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`; + const approvalUrl = `${cfg.SITE_URL}/project/${project.id}/approval`; + + await triggerAccessRequestSlackNotif({ + projectId: project.id, + projectName: project.name, + requesterFullName, + isTemporary, + requesterEmail: requestedByUser.email as string, + secretPath, + environment: envSlug, + permissions: accessTypes, + approvalUrl, + projectDAL, + kmsService, + projectSlackConfigDAL + }); + await smtpService.sendMail({ recipients: approverUsers.filter((approver) => approver.email).map((approver) => approver.email!), subjectLine: "Access Approval Request", substitutions: { projectName: project.name, - requesterFullName: `${requestedByUser.firstName} ${requestedByUser.lastName}`, + requesterFullName, requesterEmail: requestedByUser.email, isTemporary, ...(isTemporary && { @@ -181,7 +208,7 @@ export const accessApprovalRequestServiceFactory = ({ secretPath, environment: envSlug, permissions: accessTypes, - approvalUrl: `${cfg.SITE_URL}/project/${project.id}/approval` + approvalUrl }, template: SmtpTemplates.AccessApprovalRequest }); diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index f4c59ccdb..18aaa302f 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -936,7 +936,9 @@ export const registerRoutes = async ( projectEnvDAL, userDAL, smtpService, - accessApprovalPolicyApproverDAL + accessApprovalPolicyApproverDAL, + projectSlackConfigDAL, + kmsService }); const secretReplicationService = secretReplicationServiceFactory({