From 906de67f43a316d8f7beb8a58b8a6867ad167bb8 Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Fri, 7 Nov 2025 11:29:16 -0300 Subject: [PATCH] Refactor approval URL construction in access and secret approval request services - Updated the construction of approval URLs to use a consistent project path format. - Modified notification payloads to include the new approval URL structure. - Enhanced Slack message formatting to include buttons for viewing requests directly. --- .../access-approval-request-service.ts | 6 +- .../secret-approval-request-fns.ts | 4 +- .../secret-approval-request-service.ts | 16 +++- .../src/lib/workflow-integrations/types.ts | 2 + backend/src/services/slack/slack-fns.ts | 81 +++++++++++++------ 5 files changed, 77 insertions(+), 32 deletions(-) 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 83d3a23a7..4b2608c24 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 @@ -399,7 +399,8 @@ export const accessApprovalRequestServiceFactory = ({ const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`; const editorFullName = `${editedByUser.firstName} ${editedByUser.lastName}`; - const approvalPath = `/projects/secret-management/${project.id}/approval`; + const projectPath = `/projects/secret-management/${project.id}`; + const approvalPath = `${projectPath}/approval`; const approvalUrl = `${cfg.SITE_URL}${approvalPath}`; await triggerWorkflowIntegrationNotification({ @@ -417,7 +418,8 @@ export const accessApprovalRequestServiceFactory = ({ approvalUrl, editNote, editorEmail: editedByUser.email as string, - editorFullName + editorFullName, + projectPath } }, projectId: project.id diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts index d96fb2e53..8f1c3d060 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-fns.ts @@ -37,7 +37,7 @@ export const sendApprovalEmailsFn = async ({ 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}` + link: `/projects/secret-management/${project.id}/approval` })) ); @@ -51,7 +51,7 @@ export const sendApprovalEmailsFn = async ({ firstName: reviewerUser.firstName, projectName: project.name, organizationName: project.organization.name, - approvalUrl: `${cfg.SITE_URL}/projects/secret-management/${project.id}/approval?requestId=${secretApprovalRequest.id}` + approvalUrl: `${cfg.SITE_URL}/projects/secret-management/${project.id}/approval}` }, template: SmtpTemplates.SecretApprovalRequestNeedsReview }); diff --git a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts index a10a3f568..e6455c113 100644 --- a/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts +++ b/backend/src/ee/services/secret-approval-request/secret-approval-request-service.ts @@ -1416,6 +1416,11 @@ export const secretApprovalRequestServiceFactory = ({ const env = await projectEnvDAL.findOne({ id: policy.envId }); const user = await userDAL.findById(actorId); + const projectPath = `/projects/secret-management/${projectId}`; + const approvalPath = `${projectPath}/approval`; + const cfg = getConfig(); + const approvalUrl = `${cfg.SITE_URL}${approvalPath}`; + await triggerWorkflowIntegrationNotification({ input: { projectId, @@ -1427,7 +1432,8 @@ export const secretApprovalRequestServiceFactory = ({ secretPath, projectId, requestId: secretApprovalRequest.id, - secretKeys: [...new Set(Object.values(data).flatMap((arr) => arr?.map((item) => item.secretName) ?? []))] + secretKeys: [...new Set(Object.values(data).flatMap((arr) => arr?.map((item) => item.secretName) ?? []))], + approvalUrl } } }, @@ -1786,6 +1792,11 @@ export const secretApprovalRequestServiceFactory = ({ const user = await userDAL.findById(actorId); const env = await projectEnvDAL.findOne({ id: policy.envId }); + const projectPath = `/projects/secret-management/${project.id}`; + const approvalPath = `${projectPath}/approval`; + const cfg = getConfig(); + const approvalUrl = `${cfg.SITE_URL}${approvalPath}`; + await triggerWorkflowIntegrationNotification({ input: { projectId, @@ -1797,7 +1808,8 @@ export const secretApprovalRequestServiceFactory = ({ secretPath, projectId, requestId: secretApprovalRequest.id, - secretKeys: [...new Set(Object.values(data).flatMap((arr) => arr?.map((item) => item.secretKey) ?? []))] + secretKeys: [...new Set(Object.values(data).flatMap((arr) => arr?.map((item) => item.secretKey) ?? []))], + approvalUrl } } }, diff --git a/backend/src/lib/workflow-integrations/types.ts b/backend/src/lib/workflow-integrations/types.ts index db0725475..6d81c9174 100644 --- a/backend/src/lib/workflow-integrations/types.ts +++ b/backend/src/lib/workflow-integrations/types.ts @@ -21,6 +21,7 @@ export type TNotification = requestId: string; projectId: string; secretKeys: string[]; + approvalUrl: string; }; } | { @@ -52,6 +53,7 @@ export type TNotification = editNote?: string; editorFullName?: string; editorEmail?: string; + projectPath: string; }; } | { diff --git a/backend/src/services/slack/slack-fns.ts b/backend/src/services/slack/slack-fns.ts index d813cb9ae..aaeb28916 100644 --- a/backend/src/services/slack/slack-fns.ts +++ b/backend/src/services/slack/slack-fns.ts @@ -51,13 +51,9 @@ const buildSlackPayload = (notification: TNotification) => { const messageBody = `A secret approval request has been opened by ${payload.userEmail}. *Environment*: ${payload.environment} *Secret path*: ${payload.secretPath || "/"} -*Secret Key${payload.secretKeys.length > 1 ? "s" : ""}*: ${payload.secretKeys.join(", ")} +*Secret Key${payload.secretKeys.length > 1 ? "s" : ""}*: ${payload.secretKeys.join(", ")}`; -View the complete details <${appCfg.SITE_URL}/projects/secret-management/${payload.projectId}/approval?requestId=${ - payload.requestId - }|here>.`; - - const payloadBlocks = [ + const headerBlocks = [ { type: "header", text: { @@ -65,18 +61,36 @@ View the complete details <${appCfg.SITE_URL}/projects/secret-management/${paylo text: "Secret approval request", emoji: true } - }, + } + ]; + + const payloadBlocks = [ { type: "section", text: { type: "mrkdwn", text: messageBody } + }, + { + type: "actions", + elements: [ + { + type: "button", + text: { + type: "plain_text", + text: "View request", + emoji: true + }, + style: "primary", + url: payload.approvalUrl + } + ] } ]; return { - headerBlocks: [], + headerBlocks, payloadMessage: messageBody, payloadBlocks, color: COMPANY_BRAND_COLOR @@ -84,12 +98,12 @@ View the complete details <${appCfg.SITE_URL}/projects/secret-management/${paylo } case TriggerFeature.ACCESS_REQUEST: { const { payload } = notification; - const projectUrl = `${appCfg.SITE_URL}${payload.projectPath}`; + const projectUrl = `${appCfg.SITE_URL}${payload.projectPath}/overview`; const accessType = payload.isTemporary ? "temporary" : "permanent"; const permissionsFormatted = payload.permissions.map((p) => `*${p}*`).join(", "); const messageBody = `${payload.requesterFullName} (${payload.requesterEmail}) has requested ${accessType} access to ${payload.secretPath} in the ${payload.environment} environment of ${payload.projectName}.\n\nThe following permissions are requested: ${payload.permissions.join(", ")}${ - payload.note ? `\n\nUser note\n${payload.note}` : "" + payload.note ? `\n\nUser note: ${payload.note}` : "" }`; const headerBlocks = [ @@ -109,7 +123,7 @@ View the complete details <${appCfg.SITE_URL}/projects/secret-management/${paylo text: { type: "mrkdwn", text: `*${payload.requesterFullName}* (${payload.requesterEmail}) has requested *${accessType}* access to *${payload.secretPath}* in the *${payload.environment}* environment of *<${projectUrl}|${payload.projectName}>*.\n\nThe following permissions are requested: ${permissionsFormatted}${ - payload.note ? `\n\n*User note*\n${payload.note}` : "" + payload.note ? `\n\n*User note:* ${payload.note}` : "" }` } }, @@ -139,20 +153,15 @@ View the complete details <${appCfg.SITE_URL}/projects/secret-management/${paylo } case TriggerFeature.ACCESS_REQUEST_UPDATED: { const { payload } = notification; - const messageBody = `${payload.editorFullName} (${payload.editorEmail}) has updated the ${ - payload.isTemporary ? "temporary" : "permanent" - } access request from ${payload.requesterFullName} (${payload.requesterEmail}) to ${payload.secretPath} in the ${payload.environment} environment of ${payload.projectName}. + const projectUrl = `${appCfg.SITE_URL}${payload.projectPath}/overview`; + const accessType = payload.isTemporary ? "temporary" : "permanent"; + const permissionsFormatted = payload.permissions.map((p) => `*${p}*`).join(", "); -The following permissions are requested: ${payload.permissions.join(", ")} - -View the request and approve or deny it <${payload.approvalUrl}|here>.${ - payload.editNote - ? ` -Editor Note: ${payload.editNote}` - : "" + const messageBody = `${payload.editorFullName} (${payload.editorEmail}) has updated the ${accessType} access request from ${payload.requesterFullName} (${payload.requesterEmail}) to ${payload.secretPath} in the ${payload.environment} environment of ${payload.projectName}.\n\nThe following permissions are requested: ${payload.permissions.join(", ")}${ + payload.editNote ? `\n\nEditor Note: ${payload.editNote}` : "" }`; - const payloadBlocks = [ + const headerBlocks = [ { type: "header", text: { @@ -160,18 +169,38 @@ Editor Note: ${payload.editNote}` text: "Updated access approval request pending for review", emoji: true } - }, + } + ]; + + const payloadBlocks = [ { type: "section", text: { type: "mrkdwn", - text: messageBody + text: `*${payload.editorFullName}* (${payload.editorEmail}) has updated the *${accessType}* access request from *${payload.requesterFullName}* (${payload.requesterEmail}) to *${payload.secretPath}* in the *${payload.environment}* environment of *<${projectUrl}|${payload.projectName}>*.\n\nThe following permissions are requested: ${permissionsFormatted}${ + payload.editNote ? `\n\n*Editor Note:* ${payload.editNote}` : "" + }` } + }, + { + type: "actions", + elements: [ + { + type: "button", + text: { + type: "plain_text", + text: "View request", + emoji: true + }, + style: "primary", + url: payload.approvalUrl + } + ] } ]; return { - headerBlocks: [], + headerBlocks, payloadMessage: messageBody, payloadBlocks, color: COMPANY_BRAND_COLOR @@ -198,7 +227,7 @@ Editor Note: ${payload.editNote}` type: "section", text: { type: "mrkdwn", - text: `*Environment*\n${payload.environment}\n\n\n*Secret Path*\n${payload.secretPath}\n\n\n*Project*\n<${projectUrl}|${payload.projectName}>\n\n\n*Reason*\n${payload.failureMessage}` + text: `*Environment:* ${payload.environment}\n\n*Secret Path:* ${payload.secretPath}\n\n*Project:* <${projectUrl}|${payload.projectName}>\n\n*Reason:* ${payload.failureMessage}` } }, {