From 6734ce50a540c02f49156c420b46e0faaa17957d Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Thu, 16 Nov 2023 13:28:30 +0530 Subject: [PATCH] fix: resolved backup key generation in signup, removed owner check in custom role and login race condition --- .../controllers/v2/organizationsController.ts | 32 +-- .../signup/DonwloadBackupPDFStep.tsx | 55 +++-- .../src/components/utilities/attemptLogin.ts | 205 ++++++++---------- 3 files changed, 144 insertions(+), 148 deletions(-) diff --git a/backend/src/controllers/v2/organizationsController.ts b/backend/src/controllers/v2/organizationsController.ts index 9d08cce3d..5a14b5838 100644 --- a/backend/src/controllers/v2/organizationsController.ts +++ b/backend/src/controllers/v2/organizationsController.ts @@ -1,24 +1,16 @@ import { Request, Response } from "express"; import { Types } from "mongoose"; -import { - Membership, - MembershipOrg, - Workspace -} from "../../models"; +import { Membership, MembershipOrg, Workspace } from "../../models"; import { Role } from "../../ee/models"; import { deleteMembershipOrg } from "../../helpers/membershipOrg"; -import { +import { createOrganization as create, deleteOrganization, updateSubscriptionOrgQuantity } from "../../helpers/organization"; import { addMembershipsOrg } from "../../helpers/membershipOrg"; import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; -import { - ACCEPTED, - ADMIN, - CUSTOM -} from "../../variables"; +import { ACCEPTED, ADMIN, CUSTOM } from "../../variables"; import * as reqValidator from "../../validation/organization"; import { validateRequest } from "../../helpers/validation"; import { @@ -155,7 +147,7 @@ export const updateOrganizationMembership = async (req: Request, res: Response) OrgPermissionSubjects.Member ); - const isCustomRole = !["admin", "member", "owner"].includes(role); + const isCustomRole = !["admin", "member"].includes(role); if (isCustomRole) { const orgRole = await Role.findOne({ slug: role, isOrgRole: true }); if (!orgRole) throw BadRequestError({ message: "Role not found" }); @@ -336,7 +328,7 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) => * @param res * @returns */ - export const createOrganization = async (req: Request, res: Response) => { +export const createOrganization = async (req: Request, res: Response) => { const { body: { name } } = await validateRequest(reqValidator.CreateOrgv2, req); @@ -361,27 +353,27 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) => /** * Delete organization with id [organizationId] - * @param req - * @param res + * @param req + * @param res */ export const deleteOrganizationById = async (req: Request, res: Response) => { const { params: { organizationId } } = await validateRequest(reqValidator.DeleteOrgv2, req); - + const membershipOrg = await MembershipOrg.findOne({ user: req.user._id, organization: new Types.ObjectId(organizationId), role: ADMIN }); - + if (!membershipOrg) throw UnauthorizedRequestError(); - + const organization = await deleteOrganization({ organizationId: new Types.ObjectId(organizationId) }); - + return res.status(200).send({ organization }); -} \ No newline at end of file +}; diff --git a/frontend/src/components/signup/DonwloadBackupPDFStep.tsx b/frontend/src/components/signup/DonwloadBackupPDFStep.tsx index 9b170cf1d..d928c1473 100644 --- a/frontend/src/components/signup/DonwloadBackupPDFStep.tsx +++ b/frontend/src/components/signup/DonwloadBackupPDFStep.tsx @@ -2,7 +2,11 @@ import { useTranslation } from "react-i18next"; import { faWarning } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import issueBackupKey from "../utilities/cryptography/issueBackupKey"; +import { useToggle } from "@app/hooks"; +import { generateUserBackupKey } from "@app/lib/crypto"; + +import { useNotificationContext } from "../context/Notifications/NotificationProvider"; +import { generateBackupPDFAsync } from "../utilities/generateBackupPDF"; import { Button } from "../v2"; interface DownloadBackupPDFStepProps { @@ -28,35 +32,56 @@ export default function DonwloadBackupPDFStep({ name }: DownloadBackupPDFStepProps): JSX.Element { const { t } = useTranslation(); + const { createNotification } = useNotificationContext(); + const [isLoading, setIsLoading] = useToggle(); + + const handleBackupKeyGenerate = async () => { + try { + setIsLoading.on(); + const generatedKey = await generateUserBackupKey(email, password); + await generateBackupPDFAsync({ + generatedKey, + personalEmail: email, + personalName: name + }); + incrementStep(); + } catch (err) { + console.log(err); + createNotification({ + type: "error", + text: "Faield to generate backup key" + }); + } finally { + setIsLoading.off(); + } + }; return (

- {t("signup.step4-message")} + + {t("signup.step4-message")}

- {t("signup.step4-description1")} {t("signup.step4-description3")} + + {t("signup.step4-description1")} {t("signup.step4-description3")} +
+ > + Download PDF +
diff --git a/frontend/src/components/utilities/attemptLogin.ts b/frontend/src/components/utilities/attemptLogin.ts index c9cedbf68..195cf9b9a 100644 --- a/frontend/src/components/utilities/attemptLogin.ts +++ b/frontend/src/components/utilities/attemptLogin.ts @@ -8,9 +8,6 @@ import Telemetry from "./telemetry/Telemetry"; import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage"; import SecurityClient from "./SecurityClient"; -// eslint-disable-next-line new-cap -const client = new jsrp.client(); - interface IsLoginSuccessful { mfaEnabled: boolean; success: boolean; @@ -22,118 +19,100 @@ interface IsLoginSuccessful { * @param {string} email - email of user to log in * @param {string} password - password of user to log in */ -const attemptLogin = async ( - { - email, - password, - providerAuthToken, - }: { - email: string; - password: string; - providerAuthToken?: string; - } -): Promise => { +const attemptLogin = async ({ + email, + password, + providerAuthToken +}: { + email: string; + password: string; + providerAuthToken?: string; +}): Promise => { const telemetry = new Telemetry().getInstance(); - return new Promise((resolve, reject) => { - client.init( - { - username: email, - password - }, - async () => { - try { - const clientPublicKey = client.getPublicKey(); - - const { serverPublicKey, salt } = await login1({ - email, - clientPublicKey, - providerAuthToken, - }); - - client.setSalt(salt); - client.setServerPublicKey(serverPublicKey); - const clientProof = client.getProof(); // called M1 - - const { - mfaEnabled, - encryptionVersion, - protectedKey, - protectedKeyIV, - protectedKeyTag, - token, - publicKey, - encryptedPrivateKey, - iv, - tag - } = await login2( - { - email, - clientProof, - providerAuthToken, - } - ); - - if (mfaEnabled) { - // case: MFA is enabled - - // set temporary (MFA) JWT token - SecurityClient.setMfaToken(token); - - resolve({ - mfaEnabled, - success: true - }); - } else if ( - !mfaEnabled && - encryptionVersion && - encryptedPrivateKey && - iv && - tag && - token - ) { - // case: MFA is not enabled - - // unset provider auth token in case it was used - SecurityClient.setProviderAuthToken(""); - // set JWT token - SecurityClient.setToken(token); - - const privateKey = await KeyService.decryptPrivateKey({ - encryptionVersion, - encryptedPrivateKey, - iv, - tag, - password, - salt, - protectedKey, - protectedKeyIV, - protectedKeyTag - }); - - saveTokenToLocalStorage({ - publicKey, - encryptedPrivateKey, - iv, - tag, - privateKey - }); - - if (email) { - telemetry.identify(email, email); - telemetry.capture("User Logged In"); - } - - resolve({ - mfaEnabled: false, - success: true - }); - } - } catch (err) { - reject(err); - } - } - ); + // eslint-disable-next-line new-cap + const client = new jsrp.client(); + await new Promise((resolve) => { + client.init({ username: email, password }, () => resolve(null)); }); + const clientPublicKey = client.getPublicKey(); + + const { serverPublicKey, salt } = await login1({ + email, + clientPublicKey, + providerAuthToken + }); + + client.setSalt(salt); + client.setServerPublicKey(serverPublicKey); + const clientProof = client.getProof(); // called M1 + + const { + mfaEnabled, + encryptionVersion, + protectedKey, + protectedKeyIV, + protectedKeyTag, + token, + publicKey, + encryptedPrivateKey, + iv, + tag + } = await login2({ + email, + clientProof, + providerAuthToken + }); + + if (mfaEnabled) { + // case: MFA is enabled + + // set temporary (MFA) JWT token + SecurityClient.setMfaToken(token); + + return { + mfaEnabled, + success: true + }; + } + if (!mfaEnabled && encryptionVersion && encryptedPrivateKey && iv && tag && token) { + // case: MFA is not enabled + + // unset provider auth token in case it was used + SecurityClient.setProviderAuthToken(""); + // set JWT token + SecurityClient.setToken(token); + + const privateKey = await KeyService.decryptPrivateKey({ + encryptionVersion, + encryptedPrivateKey, + iv, + tag, + password, + salt, + protectedKey, + protectedKeyIV, + protectedKeyTag + }); + + saveTokenToLocalStorage({ + publicKey, + encryptedPrivateKey, + iv, + tag, + privateKey + }); + + if (email) { + telemetry.identify(email, email); + telemetry.capture("User Logged In"); + } + + return { + mfaEnabled: false, + success: true + }; + } + return { success: false, mfaEnabled: false }; }; export default attemptLogin;