diff --git a/backend/src/helpers/organization.ts b/backend/src/helpers/organization.ts index 3e8598de3..2e5ee6ebb 100644 --- a/backend/src/helpers/organization.ts +++ b/backend/src/helpers/organization.ts @@ -1,4 +1,4 @@ -import mongoose, { Types } from "mongoose"; +import mongoose, { Types, mongo } from "mongoose"; import { Bot, BotKey, @@ -111,48 +111,78 @@ export const createOrganization = async ({ * @returns */ export const deleteOrganization = async ({ - organizationId + organizationId, + existingSession }: { organizationId: Types.ObjectId; + existingSession?: mongo.ClientSession; }) => { - const session = await mongoose.startSession(); - session.startTransaction(); + + let session; + + if (existingSession) { + session = existingSession; + } else { + session = await mongoose.startSession(); + session.startTransaction(); + } try { - const organization = await Organization.findByIdAndDelete(organizationId); + const organization = await Organization.findByIdAndDelete( + organizationId, + { + session + } + ); if (!organization) throw ResourceNotFoundError(); await MembershipOrg.deleteMany({ organization: organization._id + }, { + session }); await BotOrg.deleteMany({ organization: organization._id + }, { + session }); await SSOConfig.deleteMany({ organization: organization._id + }, { + session }); await Role.deleteMany({ organization: organization._id + }, { + session }); await IncidentContactOrg.deleteMany({ organization: organization._id + }, { + session }); await GitRisks.deleteMany({ organization: organization._id + }, { + session }); await GitAppInstallationSession.deleteMany({ organization: organization._id + }, { + session }); await GitAppOrganizationInstallation.deleteMany({ organization: organization._id + }, { + session }); const workspaceIds = await Workspace.distinct("_id", { @@ -161,167 +191,230 @@ export const deleteOrganization = async ({ await Workspace.deleteMany({ organization: organization._id + }, { + session }); await Membership.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Key.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Bot.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await BotKey.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await SecretBlindIndexData.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Secret.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await SecretVersion.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await SecretSnapshot.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); - await SecretImport.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Folder.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await FolderVersion.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Webhook.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await TrustedIP.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Tag.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await IntegrationAuth.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Integration.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await ServiceToken.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await ServiceTokenData.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await ServiceTokenDataV3.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await ServiceTokenDataV3Key.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await AuditLog.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Log.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await Action.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await SecretApprovalPolicy.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); await SecretApprovalRequest.deleteMany({ workspace: { $in: workspaceIds } + }, { + session }); + if (organization.customerId) { + // delete from stripe here + await licenseServerKeyRequest.delete( + `${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}` + ); + } + return organization; } catch (err) { - await session.abortTransaction(); + if (!existingSession) { + await session.abortTransaction(); + } throw InternalServerError({ message: "Failed to delete organization" }); } finally { - session.endSession(); + if (!existingSession) { + await session.commitTransaction(); + session.endSession(); + } } } diff --git a/backend/src/helpers/user.ts b/backend/src/helpers/user.ts index 263c5c736..b78f471f1 100644 --- a/backend/src/helpers/user.ts +++ b/backend/src/helpers/user.ts @@ -1,4 +1,4 @@ -import mongoose, { Types } from "mongoose"; +import mongoose, { Types, mongo } from "mongoose"; import { APIKeyData, BackupPrivateKey, @@ -222,15 +222,26 @@ const checkDeleteUserConditions = async ({ * @returns {User} user - deleted user */ export const deleteUser = async ({ - userId + userId, + existingSession }: { - userId: Types.ObjectId + userId: Types.ObjectId; + existingSession?: mongo.ClientSession; }) => { - const session = await mongoose.startSession(); - session.startTransaction(); + + let session; + + if (existingSession) { + session = existingSession; + } else { + session = await mongoose.startSession(); + session.startTransaction(); + } try { - const user = await User.findByIdAndDelete(userId); + const user = await User.findByIdAndDelete(userId, { + session + }); if (!user) throw ResourceNotFoundError(); @@ -240,22 +251,32 @@ export const deleteUser = async ({ await UserAction.deleteMany({ user: user._id + }, { + session }); await BackupPrivateKey.deleteMany({ user: user._id + }, { + session }); await APIKeyData.deleteMany({ user: user._id + }, { + session }); await Action.deleteMany({ user: user._id + }, { + session }); await Log.deleteMany({ user: user._id + }, { + session }); await TokenVersion.deleteMany({ @@ -264,10 +285,14 @@ export const deleteUser = async ({ await Key.deleteMany({ receiver: user._id + }, { + session }); const membershipOrgs = await MembershipOrg.find({ user: userId + }, null, { + session }); // delete organizations where user is only member @@ -280,13 +305,16 @@ export const deleteUser = async ({ // organization only has 1 member (the current user) await deleteOrganization({ - organizationId: membershipOrg.organization + organizationId: membershipOrg.organization, + existingSession: session }); } } const memberships = await Membership.find({ user: userId + }, null, { + session }); // delete workspaces where user is only member @@ -299,26 +327,36 @@ export const deleteUser = async ({ // workspace only has 1 member (the current user) -> delete workspace await deleteWorkspace({ - workspaceId: membership.workspace + workspaceId: membership.workspace, + existingSession: session }); } } await MembershipOrg.deleteMany({ user: userId + }, { + session }); await Membership.deleteMany({ user: userId + }, { + session }); return user; } catch (err) { - await session.abortTransaction(); + if (!existingSession) { + await session.abortTransaction(); + } throw InternalServerError({ message: "Failed to delete account" }) } finally { - session.endSession(); + if (!existingSession) { + await session.commitTransaction(); + session.endSession(); + } } } \ No newline at end of file diff --git a/backend/src/helpers/workspace.ts b/backend/src/helpers/workspace.ts index 73f83b061..71d945e78 100644 --- a/backend/src/helpers/workspace.ts +++ b/backend/src/helpers/workspace.ts @@ -1,4 +1,4 @@ -import mongoose, { Types } from "mongoose"; +import mongoose, { Types, mongo } from "mongoose"; import { Bot, BotKey, @@ -102,125 +102,189 @@ export const createWorkspace = async ({ * @param {String} obj.id - id of workspace to delete */ export const deleteWorkspace = async ({ - workspaceId + workspaceId, + existingSession }: { workspaceId: Types.ObjectId; + existingSession?: mongo.ClientSession; }) => { - const session = await mongoose.startSession(); - session.startTransaction(); + + let session; + + if (existingSession) { + session = existingSession; + } else { + session = await mongoose.startSession(); + session.startTransaction(); + } try { - const workspace = await Workspace.findByIdAndDelete(workspaceId); + const workspace = await Workspace.findByIdAndDelete(workspaceId, { session }); if (!workspace) throw ResourceNotFoundError(); await Membership.deleteMany({ workspace: workspace._id + }, { + session }); await Key.deleteMany({ workspace: workspace._id + }, { + session }); await Bot.deleteMany({ workspace: workspace._id + }, { + session }); await BotKey.deleteMany({ workspace: workspace._id + }, { + session }); await SecretBlindIndexData.deleteMany({ workspace: workspace._id + }, { + session }); await Secret.deleteMany({ workspace: workspace._id + }, { + session }); await SecretVersion.deleteMany({ workspace: workspace._id + }, { + session }); await SecretSnapshot.deleteMany({ workspace: workspace._id + }, { + session }); await SecretImport.deleteMany({ workspace: workspace._id + }, { + session }); await Folder.deleteMany({ workspace: workspace._id + }, { + session }); await FolderVersion.deleteMany({ workspace: workspace._id + }, { + session }); await Webhook.deleteMany({ workspace: workspace._id + }, { + session }); await TrustedIP.deleteMany({ workspace: workspace._id + }, { + session }); await Tag.deleteMany({ workspace: workspace._id + }, { + session }); await IntegrationAuth.deleteMany({ workspace: workspace._id + }, { + session }); await Integration.deleteMany({ workspace: workspace._id + }, { + session }); await ServiceToken.deleteMany({ workspace: workspace._id + }, { + session }); await ServiceTokenData.deleteMany({ workspace: workspace._id + }, { + session }); await ServiceTokenDataV3.deleteMany({ workspace: workspace._id + }, { + session }); await ServiceTokenDataV3Key.deleteMany({ workspace: workspace._id + }, { + session }); await AuditLog.deleteMany({ workspace: workspace._id + }, { + session }); await Log.deleteMany({ workspace: workspace._id + }, { + session }); await Action.deleteMany({ workspace: workspace._id + }, { + session }); await SecretApprovalPolicy.deleteMany({ workspace: workspace._id + }, { + session }); await SecretApprovalRequest.deleteMany({ workspace: workspace._id + }, { + session }); return workspace; } catch (err) { - await session.abortTransaction(); + if (!existingSession) { + await session.abortTransaction(); + } throw InternalServerError({ message: "Failed to delete organization" }); } finally { - session.endSession(); + if (!existingSession) { + await session.commitTransaction(); + session.endSession(); + } } }; diff --git a/frontend/src/hooks/api/users/queries.tsx b/frontend/src/hooks/api/users/queries.tsx index 7949a59e5..adf0ccd09 100644 --- a/frontend/src/hooks/api/users/queries.tsx +++ b/frontend/src/hooks/api/users/queries.tsx @@ -51,6 +51,17 @@ export const useDeleteUser = () => { return user; }, onSuccess: () => { + localStorage.removeItem("protectedKey"); + localStorage.removeItem("protectedKeyIV"); + localStorage.removeItem("protectedKeyTag"); + localStorage.removeItem("publicKey"); + localStorage.removeItem("encryptedPrivateKey"); + localStorage.removeItem("iv"); + localStorage.removeItem("tag"); + localStorage.removeItem("PRIVATE_KEY"); + localStorage.removeItem("orgData.id"); + localStorage.removeItem("projectData.id"); + queryClient.clear(); } }); diff --git a/frontend/src/layouts/AppLayout/AppLayout.tsx b/frontend/src/layouts/AppLayout/AppLayout.tsx index 4f0cba998..ba3aa8758 100644 --- a/frontend/src/layouts/AppLayout/AppLayout.tsx +++ b/frontend/src/layouts/AppLayout/AppLayout.tsx @@ -34,7 +34,6 @@ import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { OrgPermissionCan } from "@app/components/permissions"; -import onboardingCheck from "@app/components/utilities/checks/OnboardingCheck"; import { tempLocalStorage } from "@app/components/utilities/checks/tempLocalStorage"; import { encryptAssymmetric } from "@app/components/utilities/cryptography/crypto"; import { @@ -214,7 +213,6 @@ export const AppLayout = ({ children }: LayoutProps) => { // } }; putUserInOrg(); - onboardingCheck({}); }, [router.query.id]); const onCreateProject = async ({ name, addMembers }: TAddProjectFormData) => { diff --git a/frontend/src/views/Login/Login.utils.tsx b/frontend/src/views/Login/Login.utils.tsx index 44bf24ce8..c8bf2f6c1 100644 --- a/frontend/src/views/Login/Login.utils.tsx +++ b/frontend/src/views/Login/Login.utils.tsx @@ -12,6 +12,7 @@ export const navigateUserToOrg = async (router: NextRouter) => { router.push(`/org/${userOrg}/overview`); } else { // user is not part of any org + localStorage.removeItem("orgData.id"); router.push("/org/none"); } } diff --git a/frontend/src/views/Org/NonePage/NonePage.tsx b/frontend/src/views/Org/NonePage/NonePage.tsx index ec7a768a3..6e21c302f 100644 --- a/frontend/src/views/Org/NonePage/NonePage.tsx +++ b/frontend/src/views/Org/NonePage/NonePage.tsx @@ -49,6 +49,8 @@ export const NonePage = () => { const organization = await mutateAsync({ name }); + + localStorage.setItem("orgData.id", organization._id); createNotification({ text: "Successfully created organization", diff --git a/frontend/src/views/Settings/BillingSettingsPage/components/BillingCloudTab/PreviewSection.tsx b/frontend/src/views/Settings/BillingSettingsPage/components/BillingCloudTab/PreviewSection.tsx index 04d878b67..579730ec3 100644 --- a/frontend/src/views/Settings/BillingSettingsPage/components/BillingCloudTab/PreviewSection.tsx +++ b/frontend/src/views/Settings/BillingSettingsPage/components/BillingCloudTab/PreviewSection.tsx @@ -71,7 +71,7 @@ export const PreviewSection = () => { console.error(err); } }; - + return (