diff --git a/backend/src/services/project-membership/project-membership-fns.ts b/backend/src/services/project-membership/project-membership-fns.ts deleted file mode 100644 index baa62628c..000000000 --- a/backend/src/services/project-membership/project-membership-fns.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { Knex } from "knex"; - -import { ProjectMembershipRole, SecretKeyEncoding, TProjectMemberships } from "@app/db/schemas"; -import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal"; -import { getConfig } from "@app/lib/config/env"; -import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption"; -import { BadRequestError } from "@app/lib/errors"; -import { groupBy } from "@app/lib/fn"; - -import { TOrgDALFactory } from "../org/org-dal"; -import { TProjectDALFactory } from "../project/project-dal"; -import { assignWorkspaceKeysToMembers } from "../project/project-fns"; -import { TProjectBotDALFactory } from "../project-bot/project-bot-dal"; -import { TProjectKeyDALFactory } from "../project-key/project-key-dal"; -import { TProjectRoleDALFactory } from "../project-role/project-role-dal"; -import { getPredefinedRoles } from "../project-role/project-role-fns"; -import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service"; -import { TProjectMembershipDALFactory } from "./project-membership-dal"; -import { TProjectUserMembershipRoleDALFactory } from "./project-user-membership-role-dal"; - -type TAddMembersToProjectArg = { - orgDAL: Pick; - projectMembershipDAL: Pick; - projectDAL: Pick; - projectKeyDAL: Pick; - projectBotDAL: Pick; - userGroupMembershipDAL: Pick; - projectUserMembershipRoleDAL: Pick; - projectRoleDAL: Pick; - smtpService: Pick; -}; - -type AddMembersToNonE2EEProjectDTO = { - emails: string[]; - usernames: string[]; - projectId: string; - projectMembershipRoles: string[]; - sendEmails?: boolean; -}; - -type AddMembersToNonE2EEProjectOptions = { - tx?: Knex; - throwOnProjectNotFound?: boolean; -}; - -export const addMembersToProject = ({ - orgDAL, - projectDAL, - projectMembershipDAL, - projectKeyDAL, - projectBotDAL, - userGroupMembershipDAL, - projectUserMembershipRoleDAL, - projectRoleDAL, - smtpService -}: TAddMembersToProjectArg) => { - // Can create multiple memberships for a singular project, based on user email / username - const addMembersToNonE2EEProject = async ( - { emails, usernames, projectId, projectMembershipRoles, sendEmails }: AddMembersToNonE2EEProjectDTO, - options: AddMembersToNonE2EEProjectOptions = { throwOnProjectNotFound: true } - ) => { - const processTransaction = async (tx: Knex) => { - const usernamesAndEmails = [...emails, ...usernames]; - - const project = await projectDAL.findProjectById(projectId); - if (!project) { - if (options.throwOnProjectNotFound) { - throw new BadRequestError({ message: "Project not found when attempting to add user to project" }); - } - - return []; - } - - const orgMembers = await orgDAL.findOrgMembersByUsername( - project.orgId, - [...new Set(usernamesAndEmails.map((element) => element.toLowerCase()))], - tx - ); - - if (orgMembers.length !== usernamesAndEmails.length) - throw new BadRequestError({ message: "Some users are not part of org" }); - - if (!orgMembers.length) return []; - - const existingMembers = await projectMembershipDAL.find({ - projectId, - $in: { userId: orgMembers.map(({ user }) => user.id).filter(Boolean) } - }); - if (existingMembers.length) throw new BadRequestError({ message: "Some users are already part of project" }); - - const ghostUser = await projectDAL.findProjectGhostUser(projectId); - - if (!ghostUser) { - throw new BadRequestError({ - message: "Failed to find sudo user" - }); - } - - const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, projectId); - - if (!ghostUserLatestKey) { - throw new BadRequestError({ - message: "Failed to find sudo user latest key" - }); - } - - const bot = await projectBotDAL.findOne({ projectId }); - 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 newWsMembers = assignWorkspaceKeysToMembers({ - decryptKey: ghostUserLatestKey, - userPrivateKey: botPrivateKey, - members: orgMembers.map((membership) => ({ - orgMembershipId: membership.id, - userPublicKey: membership.user.publicKey - })) - }); - - const members: TProjectMemberships[] = []; - - const userIdsToExcludeForProjectKeyAddition = new Set( - await userGroupMembershipDAL.findUserGroupMembershipsInProject(usernamesAndEmails, projectId) - ); - const projectMemberships = await projectMembershipDAL.insertMany( - orgMembers.map(({ user }) => ({ - projectId, - userId: user.id - })), - tx - ); - - const defaultRoles = getPredefinedRoles(projectId).map((i) => i.slug) as string[]; - for await (const projectMembershipRole of projectMembershipRoles) { - // Custom role - if (!defaultRoles.includes(projectMembershipRole)) { - const role = await projectRoleDAL.findOne({ - projectId, - slug: projectMembershipRole - }); - - if (!role) { - throw new BadRequestError({ message: `Custom role "${projectMembershipRole}" not found` }); - } - - await projectUserMembershipRoleDAL.insertMany( - projectMemberships.map(({ id }) => ({ - projectMembershipId: id, - role: ProjectMembershipRole.Custom, - customRoleId: role.id - })), - tx - ); - } - // Predefined role - else { - await projectUserMembershipRoleDAL.insertMany( - projectMemberships.map(({ id }) => ({ projectMembershipId: id, role: projectMembershipRole })), - tx - ); - } - } - - members.push(...projectMemberships); - - const encKeyGroupByOrgMembId = groupBy(newWsMembers, (i) => i.orgMembershipId); - await projectKeyDAL.insertMany( - orgMembers - .filter(({ user }) => !userIdsToExcludeForProjectKeyAddition.has(user.id)) - .map(({ user, id }) => ({ - encryptedKey: encKeyGroupByOrgMembId[id][0].workspaceEncryptedKey, - nonce: encKeyGroupByOrgMembId[id][0].workspaceEncryptedNonce, - senderId: ghostUser.id, - receiverId: user.id, - projectId - })), - tx - ); - - if (sendEmails) { - const recipients = orgMembers.filter((i) => i.user.email).map((i) => i.user.email as string); - - const appCfg = getConfig(); - - if (recipients.length) { - await smtpService.sendMail({ - template: SmtpTemplates.WorkspaceInvite, - subjectLine: "Infisical project invitation", - recipients: orgMembers.filter((i) => i.user.email).map((i) => i.user.email as string), - substitutions: { - workspaceName: project.name, - callback_url: `${appCfg.SITE_URL}/login` - } - }); - } - } - - return members; - }; - - if (options.tx) { - return processTransaction(options.tx); - } - return projectMembershipDAL.transaction(processTransaction); - }; - - return { - addMembersToNonE2EEProject - }; -};