From 0a124093d677a0dc930131de89c110fd6b51b3b8 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Mon, 22 Apr 2024 11:59:17 -0700 Subject: [PATCH] Patch adding groups to project for invited users, add transactions for adding/removing groups to/from projects --- .../group/user-group-membership-dal.ts | 9 +- .../group-project/group-project-service.ts | 149 +++++++++--------- 2 files changed, 83 insertions(+), 75 deletions(-) diff --git a/backend/src/ee/services/group/user-group-membership-dal.ts b/backend/src/ee/services/group/user-group-membership-dal.ts index 122c58561..1ab1839c5 100644 --- a/backend/src/ee/services/group/user-group-membership-dal.ts +++ b/backend/src/ee/services/group/user-group-membership-dal.ts @@ -61,7 +61,7 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => { }; /** - * Return list of users that are part of the group with id [groupId] + * Return list of completed/accepted users that are part of the group with id [groupId] * that have not yet been added individually to project with id [projectId]. * * Note: Filters out users that are part of other groups in the project. @@ -69,18 +69,19 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => { * @param projectId * @returns */ - const findGroupMembersNotInProject = async (groupId: string, projectId: string) => { + const findGroupMembersNotInProject = async (groupId: string, projectId: string, tx?: Knex) => { try { // get list of groups in the project with id [projectId] // that that are not the group with id [groupId] - const groups: string[] = await db(TableName.GroupProjectMembership) + const groups: string[] = await (tx || db)(TableName.GroupProjectMembership) .where(`${TableName.GroupProjectMembership}.projectId`, projectId) .whereNot(`${TableName.GroupProjectMembership}.groupId`, groupId) .pluck(`${TableName.GroupProjectMembership}.groupId`); // main query - const members = await db(TableName.UserGroupMembership) + const members = await (tx || db)(TableName.UserGroupMembership) .where(`${TableName.UserGroupMembership}.groupId`, groupId) + .where(`${TableName.UserGroupMembership}.isPending`, false) .join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`) .leftJoin(TableName.ProjectMembership, function () { this.on(`${TableName.Users}.id`, "=", `${TableName.ProjectMembership}.userId`).andOn( diff --git a/backend/src/services/group-project/group-project-service.ts b/backend/src/services/group-project/group-project-service.ts index 589d0e474..17862dd6f 100644 --- a/backend/src/services/group-project/group-project-service.ts +++ b/backend/src/services/group-project/group-project-service.ts @@ -32,7 +32,7 @@ type TGroupProjectServiceFactoryDep = { TGroupProjectMembershipRoleDALFactory, "create" | "transaction" | "insertMany" | "delete" >; - userGroupMembershipDAL: TUserGroupMembershipDALFactory; + userGroupMembershipDAL: Pick; projectDAL: Pick; projectKeyDAL: Pick; projectRoleDAL: Pick; @@ -116,68 +116,69 @@ export const groupProjectServiceFactory = ({ }, tx ); + + // share project key with users in group that have not + // individually been added to the project and that are not part of + // other groups that are in the project + const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id, tx); + + if (groupMembers.length) { + const ghostUser = await projectDAL.findProjectGhostUser(project.id, tx); + + if (!ghostUser) { + throw new BadRequestError({ + message: "Failed to find sudo user" + }); + } + + const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, project.id, tx); + + if (!ghostUserLatestKey) { + throw new BadRequestError({ + message: "Failed to find sudo user latest key" + }); + } + + const bot = await projectBotDAL.findOne({ projectId: project.id }, tx); + + if (!bot) { + throw new BadRequestError({ + message: "Failed to find bot" + }); + } + + const botPrivateKey = infisicalSymmetricDecrypt({ + keyEncoding: bot.keyEncoding as SecretKeyEncoding, + iv: bot.iv, + tag: bot.tag, + ciphertext: bot.encryptedPrivateKey + }); + + const plaintextProjectKey = decryptAsymmetric({ + ciphertext: ghostUserLatestKey.encryptedKey, + nonce: ghostUserLatestKey.nonce, + publicKey: ghostUserLatestKey.sender.publicKey, + privateKey: botPrivateKey + }); + + const projectKeyData = groupMembers.map(({ user: { publicKey, id } }) => { + const { ciphertext: encryptedKey, nonce } = encryptAsymmetric(plaintextProjectKey, publicKey, botPrivateKey); + + return { + encryptedKey, + nonce, + senderId: ghostUser.id, + receiverId: id, + projectId: project.id + }; + }); + + await projectKeyDAL.insertMany(projectKeyData, tx); + } + return groupProjectMembership; }); - // share project key with users in group that have not - // individually been added to the project and that are not part of - // other groups that are in the project - const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id); - - if (groupMembers.length) { - const ghostUser = await projectDAL.findProjectGhostUser(project.id); - - if (!ghostUser) { - throw new BadRequestError({ - message: "Failed to find sudo user" - }); - } - - const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, project.id); - - if (!ghostUserLatestKey) { - throw new BadRequestError({ - message: "Failed to find sudo user latest key" - }); - } - - const bot = await projectBotDAL.findOne({ projectId: project.id }); - - if (!bot) { - throw new BadRequestError({ - message: "Failed to find bot" - }); - } - - const botPrivateKey = infisicalSymmetricDecrypt({ - keyEncoding: bot.keyEncoding as SecretKeyEncoding, - iv: bot.iv, - tag: bot.tag, - ciphertext: bot.encryptedPrivateKey - }); - - const plaintextProjectKey = decryptAsymmetric({ - ciphertext: ghostUserLatestKey.encryptedKey, - nonce: ghostUserLatestKey.nonce, - publicKey: ghostUserLatestKey.sender.publicKey, - privateKey: botPrivateKey - }); - - const projectKeyData = groupMembers.map(({ user: { publicKey, id } }) => { - const { ciphertext: encryptedKey, nonce } = encryptAsymmetric(plaintextProjectKey, publicKey, botPrivateKey); - - return { - encryptedKey, - nonce, - senderId: ghostUser.id, - receiverId: id, - projectId: project.id - }; - }); - - await projectKeyDAL.insertMany(projectKeyData); - } - return projectGroup; }; @@ -287,20 +288,26 @@ export const groupProjectServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Groups); - const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id); + const deletedProjectGroup = await groupProjectDAL.transaction(async (tx) => { + const groupMembers = await userGroupMembershipDAL.findGroupMembersNotInProject(group.id, project.id, tx); - if (groupMembers.length) { - await projectKeyDAL.delete({ - projectId: project.id, - $in: { - receiverId: groupMembers.map(({ user: { id } }) => id) - } - }); - } + if (groupMembers.length) { + await projectKeyDAL.delete( + { + projectId: project.id, + $in: { + receiverId: groupMembers.map(({ user: { id } }) => id) + } + }, + tx + ); + } - const [deletedGroup] = await groupProjectDAL.delete({ groupId: group.id, projectId: project.id }); + const [projectGroup] = await groupProjectDAL.delete({ groupId: group.id, projectId: project.id }, tx); + return projectGroup; + }); - return deletedGroup; + return deletedProjectGroup; }; const listGroupsInProject = async ({