mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 12:27:28 +00:00
feat: integration to access request approval
This commit is contained in:
@@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -936,7 +936,9 @@ export const registerRoutes = async (
|
|||||||
projectEnvDAL,
|
projectEnvDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
smtpService,
|
smtpService,
|
||||||
accessApprovalPolicyApproverDAL
|
accessApprovalPolicyApproverDAL,
|
||||||
|
projectSlackConfigDAL,
|
||||||
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretReplicationService = secretReplicationServiceFactory({
|
const secretReplicationService = secretReplicationServiceFactory({
|
||||||
|
|||||||
Reference in New Issue
Block a user