diff --git a/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts b/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts index b293a38d6..48e2d88bf 100644 --- a/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts +++ b/backend/src/ee/services/access-approval-request/access-approval-request-dal.ts @@ -1,7 +1,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; -import { AccessApprovalRequestsSchema, TableName, TAccessApprovalRequests } from "@app/db/schemas"; +import { AccessApprovalRequestsSchema, TableName, TAccessApprovalRequests, TUsers } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex"; @@ -40,6 +40,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { `${TableName.AccessApprovalPolicyApprover}.policyId` ) + .join( + db(TableName.Users).as("requestedByUser"), + `${TableName.AccessApprovalRequest}.requestedByUserId`, + `requestedByUser.id` + ) + .leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`) .select(selectAllTableCols(TableName.AccessApprovalRequest)) @@ -52,7 +58,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId") ) - .select(db.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover)) + .select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover)) .select( db.ref("projectId").withSchema(TableName.Environment), @@ -61,15 +67,20 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { ) .select( - db.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"), + db.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId"), db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus") ) + // TODO: ADD SUPPORT FOR GROUPS!!!! .select( - db - .ref("projectMembershipId") - .withSchema(TableName.ProjectUserAdditionalPrivilege) - .as("privilegeMembershipId"), + db.ref("email").withSchema("requestedByUser").as("requestedByUserEmail"), + db.ref("username").withSchema("requestedByUser").as("requestedByUserUsername"), + db.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"), + db.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"), + + db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeUserId"), + db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeMembershipId"), + db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeIsTemporary"), db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryMode"), db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryRange"), @@ -102,9 +113,18 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { enforcementLevel: doc.policyEnforcementLevel, envId: doc.policyEnvId }, + requestedByUser: { + userId: doc.requestedByUserId, + email: doc.requestedByUserEmail, + firstName: doc.requestedByUserFirstName, + lastName: doc.requestedByUserLastName, + username: doc.requestedByUserUsername + }, privilege: doc.privilegeId ? { membershipId: doc.privilegeMembershipId, + userId: doc.privilegeUserId, + projectId: doc.projectId, isTemporary: doc.privilegeIsTemporary, temporaryMode: doc.privilegeTemporaryMode, temporaryRange: doc.privilegeTemporaryRange, @@ -118,11 +138,11 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { }), childrenMapper: [ { - key: "reviewerMemberId", + key: "reviewerUserId", label: "reviewers" as const, - mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined) + mapper: ({ reviewerUserId: userId, reviewerStatus: status }) => (userId ? { userId, status } : undefined) }, - { key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId } + { key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId } ] }); @@ -146,30 +166,65 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { `${TableName.AccessApprovalPolicy}.id` ) + .join( + db(TableName.Users).as("requestedByUser"), + `${TableName.AccessApprovalRequest}.requestedByUserId`, + `requestedByUser.id` + ) + .join( TableName.AccessApprovalPolicyApprover, `${TableName.AccessApprovalPolicy}.id`, `${TableName.AccessApprovalPolicyApprover}.policyId` ) + + .join( + db(TableName.Users).as("accessApprovalPolicyApproverUser"), + `${TableName.AccessApprovalPolicyApprover}.approverUserId`, + "accessApprovalPolicyApproverUser.id" + ) + .leftJoin( TableName.AccessApprovalRequestReviewer, `${TableName.AccessApprovalRequest}.id`, `${TableName.AccessApprovalRequestReviewer}.requestId` ) + .leftJoin( + db(TableName.Users).as("accessApprovalReviewerUser"), + `${TableName.AccessApprovalRequestReviewer}.reviewerUserId`, + `accessApprovalReviewerUser.id` + ) + .leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`) .select(selectAllTableCols(TableName.AccessApprovalRequest)) .select( - tx.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"), + tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover), + tx.ref("email").withSchema("accessApprovalPolicyApproverUser").as("approverEmail"), + tx.ref("username").withSchema("accessApprovalPolicyApproverUser").as("approverUsername"), + tx.ref("firstName").withSchema("accessApprovalPolicyApproverUser").as("approverFirstName"), + tx.ref("lastName").withSchema("accessApprovalPolicyApproverUser").as("approverLastName"), + tx.ref("email").withSchema("requestedByUser").as("requestedByUserEmail"), + tx.ref("username").withSchema("requestedByUser").as("requestedByUserUsername"), + tx.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"), + tx.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"), + + tx.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer), + tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"), + + tx.ref("email").withSchema("accessApprovalReviewerUser").as("reviewerEmail"), + tx.ref("username").withSchema("accessApprovalReviewerUser").as("reviewerUsername"), + tx.ref("firstName").withSchema("accessApprovalReviewerUser").as("reviewerFirstName"), + tx.ref("lastName").withSchema("accessApprovalReviewerUser").as("reviewerLastName"), + tx.ref("id").withSchema(TableName.AccessApprovalPolicy).as("policyId"), tx.ref("name").withSchema(TableName.AccessApprovalPolicy).as("policyName"), tx.ref("projectId").withSchema(TableName.Environment), tx.ref("slug").withSchema(TableName.Environment).as("environment"), tx.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"), tx.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"), - tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"), - tx.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover) + tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals") ); const findById = async (id: string, tx?: Knex) => { @@ -189,15 +244,45 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { approvals: el.policyApprovals, secretPath: el.policySecretPath, enforcementLevel: el.policyEnforcementLevel + }, + requestedByUser: { + userId: el.requestedByUserId, + email: el.requestedByUserEmail, + firstName: el.requestedByUserFirstName, + lastName: el.requestedByUserLastName, + username: el.requestedByUserUsername } }), childrenMapper: [ { - key: "reviewerMemberId", + key: "reviewerUserId", label: "reviewers" as const, - mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined) + mapper: ({ + reviewerUserId: userId, + reviewerStatus: status, + reviewerEmail: email, + reviewerLastName: lastName, + reviewerUsername: username, + reviewerFirstName: firstName + }) => (userId ? { userId, status, email, firstName, lastName, username } : undefined) }, - { key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId } + { + key: "approverUserId", + label: "approvers" as const, + mapper: ({ + approverUserId, + approverEmail: email, + approverUsername: username, + approverLastName: lastName, + approverFirstName: firstName + }) => ({ + userId: approverUserId, + email, + firstName, + lastName, + username + }) + } ] }); if (!formatedDoc?.[0]) return; @@ -235,7 +320,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { .where(`${TableName.Environment}.projectId`, projectId) .select(selectAllTableCols(TableName.AccessApprovalRequest)) .select(db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus")) - .select(db.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId")); + .select(db.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId")); const formattedRequests = sqlNestRelationships({ data: accessRequests, @@ -245,9 +330,10 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => { }), childrenMapper: [ { - key: "reviewerMemberId", + key: "reviewerUserId", label: "reviewers" as const, - mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined) + mapper: ({ reviewerUserId: reviewer, reviewerStatus: status }) => + reviewer ? { reviewer, status } : undefined } ] }); 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 becdb78da..544229609 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 @@ -52,7 +52,10 @@ type TSecretApprovalRequestServiceFactoryDep = { >; projectMembershipDAL: Pick; smtpService: Pick; - userDAL: Pick; + userDAL: Pick< + TUserDALFactory, + "findUserByProjectMembershipId" | "findUsersByProjectMembershipIds" | "find" | "findById" + >; }; export type TAccessApprovalRequestServiceFactory = ReturnType; @@ -94,7 +97,7 @@ export const accessApprovalRequestServiceFactory = ({ ); if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" }); - const requestedByUser = await userDAL.findUserByProjectMembershipId(membership.id); + const requestedByUser = await userDAL.findById(actorId); if (!requestedByUser) throw new UnauthorizedError({ message: "User not found" }); await projectDAL.checkProjectUpgradeStatus(project.id); @@ -114,13 +117,15 @@ export const accessApprovalRequestServiceFactory = ({ policyId: policy.id }); - const approverUsers = await userDAL.findUsersByProjectMembershipIds( - approvers.map((approver) => approver.approverId) - ); + const approverUsers = await userDAL.find({ + $in: { + id: approvers.map((approver) => approver.approverUserId) + } + }); const duplicateRequests = await accessApprovalRequestDAL.find({ policyId: policy.id, - requestedBy: membership.id, + requestedByUserId: actorId, permissions: JSON.stringify(requestedPermissions), isTemporary }); @@ -153,7 +158,7 @@ export const accessApprovalRequestServiceFactory = ({ const approvalRequest = await accessApprovalRequestDAL.create( { policyId: policy.id, - requestedBy: membership.id, + requestedByUserId: actorId, temporaryRange: temporaryRange || null, permissions: JSON.stringify(requestedPermissions), isTemporary @@ -212,7 +217,7 @@ export const accessApprovalRequestServiceFactory = ({ let requests = await accessApprovalRequestDAL.findRequestsWithPrivilegeByPolicyIds(policies.map((p) => p.id)); if (authorProjectMembershipId) { - requests = requests.filter((request) => request.requestedBy === authorProjectMembershipId); + requests = requests.filter((request) => request.requestedByUserId === actorId); } if (envSlug) { @@ -246,8 +251,8 @@ export const accessApprovalRequestServiceFactory = ({ if ( !hasRole(ProjectMembershipRole.Admin) && - accessApprovalRequest.requestedBy !== membership.id && // The request wasn't made by the current user - !policy.approvers.find((approverId) => approverId === membership.id) // The request isn't performed by an assigned approver + accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user + !policy.approvers.find((approver) => approver.userId === actorId) // The request isn't performed by an assigned approver ) { throw new UnauthorizedError({ message: "You are not authorized to approve this request" }); } @@ -273,7 +278,7 @@ export const accessApprovalRequestServiceFactory = ({ const review = await accessApprovalRequestReviewerDAL.findOne( { requestId: accessApprovalRequest.id, - member: membership.id + reviewerUserId: actorId }, tx ); @@ -282,7 +287,7 @@ export const accessApprovalRequestServiceFactory = ({ { status, requestId: accessApprovalRequest.id, - member: membership.id + reviewerUserId: actorId }, tx ); @@ -303,7 +308,8 @@ export const accessApprovalRequestServiceFactory = ({ // Permanent access const privilege = await additionalPrivilegeDAL.create( { - projectMembershipId: accessApprovalRequest.requestedBy, + userId: accessApprovalRequest.requestedByUserId, + projectId: accessApprovalRequest.projectId, slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`, permissions: JSON.stringify(accessApprovalRequest.permissions) }, @@ -317,7 +323,8 @@ export const accessApprovalRequestServiceFactory = ({ const privilege = await additionalPrivilegeDAL.create( { - projectMembershipId: accessApprovalRequest.requestedBy, + userId: accessApprovalRequest.requestedByUserId, + projectId: accessApprovalRequest.projectId, slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`, permissions: JSON.stringify(accessApprovalRequest.permissions), isTemporary: true,