feat: integration to access request approval

This commit is contained in:
Sheen Capadngan
2024-09-05 00:42:21 +08:00
parent ddc119ceb6
commit b163a6c5ad
3 changed files with 106 additions and 6 deletions
@@ -2,6 +2,11 @@ import { ForbiddenError, subject } from "@casl/ability";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { ActorType } from "@app/services/auth/auth-type"; 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 { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
import { TVerifyApprovers } from "./access-approval-policy-types"; 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<TProjectDALFactory, "findById" | "findProjectWithOrg">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "getIntegrationDetailsByProject">;
};
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
});
};
@@ -5,15 +5,17 @@ import { ProjectMembershipRole } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors"; import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid"; import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TProjectDALFactory } from "@app/services/project/project-dal"; import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal"; import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-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 { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
import { TUserDALFactory } from "@app/services/user/user-dal"; import { TUserDALFactory } from "@app/services/user/user-dal";
import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-policy/access-approval-policy-approver-dal"; import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-policy/access-approval-policy-approver-dal";
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-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 { TPermissionServiceFactory } from "../permission/permission-service";
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal"; import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types"; import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types";
@@ -33,7 +35,10 @@ type TSecretApprovalRequestServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">; accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">; projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus" | "findProjectBySlug">; projectDAL: Pick<
TProjectDALFactory,
"checkProjectUpgradeStatus" | "findProjectBySlug" | "findProjectWithOrg" | "findById"
>;
accessApprovalRequestDAL: Pick< accessApprovalRequestDAL: Pick<
TAccessApprovalRequestDALFactory, TAccessApprovalRequestDALFactory,
| "create" | "create"
@@ -56,6 +61,8 @@ type TSecretApprovalRequestServiceFactoryDep = {
TUserDALFactory, TUserDALFactory,
"findUserByProjectMembershipId" | "findUsersByProjectMembershipIds" | "find" | "findById" "findUserByProjectMembershipId" | "findUsersByProjectMembershipIds" | "find" | "findById"
>; >;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "getIntegrationDetailsByProject">;
}; };
export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>; export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>;
@@ -71,7 +78,9 @@ export const accessApprovalRequestServiceFactory = ({
accessApprovalPolicyApproverDAL, accessApprovalPolicyApproverDAL,
additionalPrivilegeDAL, additionalPrivilegeDAL,
smtpService, smtpService,
userDAL userDAL,
kmsService,
projectSlackConfigDAL
}: TSecretApprovalRequestServiceFactoryDep) => { }: TSecretApprovalRequestServiceFactoryDep) => {
const createAccessApprovalRequest = async ({ const createAccessApprovalRequest = async ({
isTemporary, isTemporary,
@@ -166,13 +175,31 @@ export const accessApprovalRequestServiceFactory = ({
tx 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({ await smtpService.sendMail({
recipients: approverUsers.filter((approver) => approver.email).map((approver) => approver.email!), recipients: approverUsers.filter((approver) => approver.email).map((approver) => approver.email!),
subjectLine: "Access Approval Request", subjectLine: "Access Approval Request",
substitutions: { substitutions: {
projectName: project.name, projectName: project.name,
requesterFullName: `${requestedByUser.firstName} ${requestedByUser.lastName}`, requesterFullName,
requesterEmail: requestedByUser.email, requesterEmail: requestedByUser.email,
isTemporary, isTemporary,
...(isTemporary && { ...(isTemporary && {
@@ -181,7 +208,7 @@ export const accessApprovalRequestServiceFactory = ({
secretPath, secretPath,
environment: envSlug, environment: envSlug,
permissions: accessTypes, permissions: accessTypes,
approvalUrl: `${cfg.SITE_URL}/project/${project.id}/approval` approvalUrl
}, },
template: SmtpTemplates.AccessApprovalRequest template: SmtpTemplates.AccessApprovalRequest
}); });
+3 -1
View File
@@ -936,7 +936,9 @@ export const registerRoutes = async (
projectEnvDAL, projectEnvDAL,
userDAL, userDAL,
smtpService, smtpService,
accessApprovalPolicyApproverDAL accessApprovalPolicyApproverDAL,
projectSlackConfigDAL,
kmsService
}); });
const secretReplicationService = secretReplicationServiceFactory({ const secretReplicationService = secretReplicationServiceFactory({