From 230b44fca1a0e856dd204ab3716ef50d9609d096 Mon Sep 17 00:00:00 2001 From: carlosmonastyrski Date: Thu, 10 Apr 2025 20:10:38 -0300 Subject: [PATCH] Add access request note and change secret request to change request --- ...410203010_add-comment-to-access-request.ts | 21 +++++++++++++++++++ .../db/schemas/access-approval-requests.ts | 3 ++- .../v1/access-approval-request-router.ts | 6 ++++-- .../access-approval-request-service.ts | 12 +++++++---- .../access-approval-request-types.ts | 1 + backend/src/services/slack/slack-fns.ts | 7 ++++++- backend/src/services/slack/slack-types.ts | 1 + .../accessApprovalRequest.handlebars | 3 +++ .../src/hooks/api/accessApproval/types.ts | 3 +++ .../SpecificPrivilegeSection.tsx | 18 ++++++++++++++-- .../SecretApprovalsPage.tsx | 2 +- .../components/ReviewAccessModal.tsx | 7 +++++++ 12 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 backend/src/db/migrations/20250410203010_add-comment-to-access-request.ts diff --git a/backend/src/db/migrations/20250410203010_add-comment-to-access-request.ts b/backend/src/db/migrations/20250410203010_add-comment-to-access-request.ts new file mode 100644 index 000000000..0e0b46fd8 --- /dev/null +++ b/backend/src/db/migrations/20250410203010_add-comment-to-access-request.ts @@ -0,0 +1,21 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const hasCol = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "note"); + if (!hasCol) { + await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => { + t.string("note").nullable(); + }); + } +} + +export async function down(knex: Knex): Promise { + const hasCol = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "note"); + if (hasCol) { + await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => { + t.dropColumn("note"); + }); + } +} diff --git a/backend/src/db/schemas/access-approval-requests.ts b/backend/src/db/schemas/access-approval-requests.ts index 0b20202f5..bfe990b3a 100644 --- a/backend/src/db/schemas/access-approval-requests.ts +++ b/backend/src/db/schemas/access-approval-requests.ts @@ -17,7 +17,8 @@ export const AccessApprovalRequestsSchema = z.object({ permissions: z.unknown(), createdAt: z.date(), updatedAt: z.date(), - requestedByUserId: z.string().uuid() + requestedByUserId: z.string().uuid(), + note: z.string().nullable().optional() }); export type TAccessApprovalRequests = z.infer; diff --git a/backend/src/ee/routes/v1/access-approval-request-router.ts b/backend/src/ee/routes/v1/access-approval-request-router.ts index 6a6ec3c07..f735b4363 100644 --- a/backend/src/ee/routes/v1/access-approval-request-router.ts +++ b/backend/src/ee/routes/v1/access-approval-request-router.ts @@ -22,7 +22,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv body: z.object({ permissions: z.any().array(), isTemporary: z.boolean(), - temporaryRange: z.string().optional() + temporaryRange: z.string().optional(), + note: z.string().optional() }), querystring: z.object({ projectSlug: z.string().trim() @@ -43,7 +44,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv actorOrgId: req.permission.orgId, projectSlug: req.query.projectSlug, temporaryRange: req.body.temporaryRange, - isTemporary: req.body.isTemporary + isTemporary: req.body.isTemporary, + note: req.body.note }); return { approval: 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 50670cb49..3606b4bdc 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 @@ -94,7 +94,8 @@ export const accessApprovalRequestServiceFactory = ({ actor, actorOrgId, actorAuthMethod, - projectSlug + projectSlug, + note }: TCreateAccessApprovalRequestDTO) => { const cfg = getConfig(); const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId); @@ -209,7 +210,8 @@ export const accessApprovalRequestServiceFactory = ({ requestedByUserId: actorId, temporaryRange: temporaryRange || null, permissions: JSON.stringify(requestedPermissions), - isTemporary + isTemporary, + note: note || null }, tx ); @@ -232,7 +234,8 @@ export const accessApprovalRequestServiceFactory = ({ secretPath, environment: envSlug, permissions: accessTypes, - approvalUrl + approvalUrl, + note } } }); @@ -252,7 +255,8 @@ export const accessApprovalRequestServiceFactory = ({ secretPath, environment: envSlug, permissions: accessTypes, - approvalUrl + approvalUrl, + note }, template: SmtpTemplates.AccessApprovalRequest }); diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-types.ts b/backend/src/ee/services/access-approval-request/access-approval-request-types.ts index e11ca58d5..51a5e0ca2 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-types.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-types.ts @@ -24,6 +24,7 @@ export type TCreateAccessApprovalRequestDTO = { permissions: unknown; isTemporary: boolean; temporaryRange?: string; + note?: string; } & Omit; export type TListApprovalRequestsDTO = { diff --git a/backend/src/services/slack/slack-fns.ts b/backend/src/services/slack/slack-fns.ts index f92f96a24..6c84c0e76 100644 --- a/backend/src/services/slack/slack-fns.ts +++ b/backend/src/services/slack/slack-fns.ts @@ -87,7 +87,12 @@ View the complete details <${appCfg.SITE_URL}/secret-manager/${payload.projectId The following permissions are requested: ${payload.permissions.join(", ")} -View the request and approve or deny it <${payload.approvalUrl}|here>.`; +View the request and approve or deny it <${payload.approvalUrl}|here>.${ + payload.note + ? ` +User Note: ${payload.note}` + : "" + }`; const payloadBlocks = [ { diff --git a/backend/src/services/slack/slack-types.ts b/backend/src/services/slack/slack-types.ts index a92ba4e8b..3e8354adf 100644 --- a/backend/src/services/slack/slack-types.ts +++ b/backend/src/services/slack/slack-types.ts @@ -76,5 +76,6 @@ export type TSlackNotification = projectName: string; permissions: string[]; approvalUrl: string; + note?: string; }; }; diff --git a/backend/src/services/smtp/templates/accessApprovalRequest.handlebars b/backend/src/services/smtp/templates/accessApprovalRequest.handlebars index ef11957a7..6813c1200 100644 --- a/backend/src/services/smtp/templates/accessApprovalRequest.handlebars +++ b/backend/src/services/smtp/templates/accessApprovalRequest.handlebars @@ -40,6 +40,9 @@ {{/each}}

+ {{#if note}} +

User Note: "{{note}}"

+ {{/if}}

View the request and approve or deny it diff --git a/frontend/src/hooks/api/accessApproval/types.ts b/frontend/src/hooks/api/accessApproval/types.ts index 40da0207a..e2d620fe2 100644 --- a/frontend/src/hooks/api/accessApproval/types.ts +++ b/frontend/src/hooks/api/accessApproval/types.ts @@ -79,6 +79,8 @@ export type TAccessApprovalRequest = { member: string; status: string; }[]; + + note?: string; }; export type TAccessApproval = { @@ -119,6 +121,7 @@ export type TProjectUserPrivilege = { export type TCreateAccessRequestDTO = { projectSlug: string; + note?: string; } & Omit; export type TGetAccessApprovalRequestsDTO = { diff --git a/frontend/src/pages/project/AccessControlPage/components/MembersTab/components/MemberRoleForm/SpecificPrivilegeSection.tsx b/frontend/src/pages/project/AccessControlPage/components/MembersTab/components/MemberRoleForm/SpecificPrivilegeSection.tsx index 06641be7a..e9c9d15fc 100644 --- a/frontend/src/pages/project/AccessControlPage/components/MembersTab/components/MemberRoleForm/SpecificPrivilegeSection.tsx +++ b/frontend/src/pages/project/AccessControlPage/components/MembersTab/components/MemberRoleForm/SpecificPrivilegeSection.tsx @@ -67,7 +67,8 @@ const secretPermissionSchema = z.object({ z.object({ isTemporary: z.literal(false) }) - ]) + ]), + note: z.string().optional() }); type TSecretPermissionForm = z.infer; export const SpecificPrivilegeSecretForm = ({ @@ -231,7 +232,8 @@ export const SpecificPrivilegeSecretForm = ({ action, subject: [ProjectPermissionSub.Secrets], conditions - })) + })), + note: data.note }); createNotification({ @@ -541,6 +543,18 @@ export const SpecificPrivilegeSecretForm = ({ )} +

+ ( +
+ + +
+ )} + /> +
{!!policies && (