Merge pull request #1178 from akhilmhdh/patch/sign-up

fix: resolved backup key generation in signup, removed owner check and logic race condition error
This commit is contained in:
Maidul Islam
2023-11-17 10:52:24 -05:00
committed by GitHub
3 changed files with 144 additions and 148 deletions
@@ -1,10 +1,6 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import { Types } from "mongoose"; import { Types } from "mongoose";
import { import { Membership, MembershipOrg, Workspace } from "../../models";
Membership,
MembershipOrg,
Workspace
} from "../../models";
import { Role } from "../../ee/models"; import { Role } from "../../ee/models";
import { deleteMembershipOrg } from "../../helpers/membershipOrg"; import { deleteMembershipOrg } from "../../helpers/membershipOrg";
import { import {
@@ -14,11 +10,7 @@ import {
} from "../../helpers/organization"; } from "../../helpers/organization";
import { addMembershipsOrg } from "../../helpers/membershipOrg"; import { addMembershipsOrg } from "../../helpers/membershipOrg";
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors"; import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
import { import { ACCEPTED, ADMIN, CUSTOM } from "../../variables";
ACCEPTED,
ADMIN,
CUSTOM
} from "../../variables";
import * as reqValidator from "../../validation/organization"; import * as reqValidator from "../../validation/organization";
import { validateRequest } from "../../helpers/validation"; import { validateRequest } from "../../helpers/validation";
import { import {
@@ -155,7 +147,7 @@ export const updateOrganizationMembership = async (req: Request, res: Response)
OrgPermissionSubjects.Member OrgPermissionSubjects.Member
); );
const isCustomRole = !["admin", "member", "owner"].includes(role); const isCustomRole = !["admin", "member"].includes(role);
if (isCustomRole) { if (isCustomRole) {
const orgRole = await Role.findOne({ slug: role, isOrgRole: true }); const orgRole = await Role.findOne({ slug: role, isOrgRole: true });
if (!orgRole) throw BadRequestError({ message: "Role not found" }); if (!orgRole) throw BadRequestError({ message: "Role not found" });
@@ -384,4 +376,4 @@ export const deleteOrganizationById = async (req: Request, res: Response) => {
return res.status(200).send({ return res.status(200).send({
organization organization
}); });
} };
@@ -2,7 +2,11 @@ import { useTranslation } from "react-i18next";
import { faWarning } from "@fortawesome/free-solid-svg-icons"; import { faWarning } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; 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"; import { Button } from "../v2";
interface DownloadBackupPDFStepProps { interface DownloadBackupPDFStepProps {
@@ -28,35 +32,56 @@ export default function DonwloadBackupPDFStep({
name name
}: DownloadBackupPDFStepProps): JSX.Element { }: DownloadBackupPDFStepProps): JSX.Element {
const { t } = useTranslation(); 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 ( return (
<div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16"> <div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16">
<p className="text-xl text-center font-medium flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200"> <p className="text-xl text-center font-medium flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 text-2xl text-bunker-200" />{t("signup.step4-message")} <FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 text-2xl text-bunker-200" />
{t("signup.step4-message")}
</p> </p>
<div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md"> <div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md">
<div className="w-full mt-4 md:mt-8 flex flex-row text-center items-center m-2 text-bunker-300 rounded-md lg:w-1/6 lg:w-1/6 w-full md:min-w-[23rem] px-3 mx-auto"> <div className="w-full mt-4 md:mt-8 flex flex-row text-center items-center m-2 text-bunker-300 rounded-md lg:w-1/6 lg:w-1/6 w-full md:min-w-[23rem] px-3 mx-auto">
<span className='mb-2'>{t("signup.step4-description1")} {t("signup.step4-description3")}</span> <span className="mb-2">
{t("signup.step4-description1")} {t("signup.step4-description3")}
</span>
</div> </div>
<div className="flex flex-col items-center px-3 justify-center mt-0 md:mt-4 mb-2 md:mb-4 lg:w-1/6 w-full md:min-w-[20rem] mt-2 md:max-w-md mx-auto text-sm text-center md:text-left"> <div className="flex flex-col items-center px-3 justify-center mt-0 md:mt-4 mb-2 md:mb-4 lg:w-1/6 w-full md:min-w-[20rem] mt-2 md:max-w-md mx-auto text-sm text-center md:text-left">
<div className="text-l py-1 text-lg w-full"> <div className="text-l py-1 text-lg w-full">
<Button <Button
onClick={async () => { onClick={handleBackupKeyGenerate}
await issueBackupKey({
email,
password,
personalName: name,
setBackupKeyError: () => { },
setBackupKeyIssued: () => { }
});
incrementStep();
}}
size="sm" size="sm"
isFullWidth isFullWidth
className='h-12' isLoading={isLoading}
isDisabled={isLoading}
className="h-12"
colorSchema="primary" colorSchema="primary"
variant="outline_bg" variant="outline_bg"
> Download PDF </Button> >
Download PDF
</Button>
</div> </div>
</div> </div>
</div> </div>
@@ -8,9 +8,6 @@ import Telemetry from "./telemetry/Telemetry";
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage"; import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
import SecurityClient from "./SecurityClient"; import SecurityClient from "./SecurityClient";
// eslint-disable-next-line new-cap
const client = new jsrp.client();
interface IsLoginSuccessful { interface IsLoginSuccessful {
mfaEnabled: boolean; mfaEnabled: boolean;
success: boolean; success: boolean;
@@ -22,32 +19,27 @@ interface IsLoginSuccessful {
* @param {string} email - email of user to log in * @param {string} email - email of user to log in
* @param {string} password - password of user to log in * @param {string} password - password of user to log in
*/ */
const attemptLogin = async ( const attemptLogin = async ({
{
email, email,
password, password,
providerAuthToken, providerAuthToken
}: { }: {
email: string; email: string;
password: string; password: string;
providerAuthToken?: string; providerAuthToken?: string;
} }): Promise<IsLoginSuccessful> => {
): Promise<IsLoginSuccessful> => {
const telemetry = new Telemetry().getInstance(); const telemetry = new Telemetry().getInstance();
return new Promise((resolve, reject) => { // eslint-disable-next-line new-cap
client.init( const client = new jsrp.client();
{ await new Promise((resolve) => {
username: email, client.init({ username: email, password }, () => resolve(null));
password });
},
async () => {
try {
const clientPublicKey = client.getPublicKey(); const clientPublicKey = client.getPublicKey();
const { serverPublicKey, salt } = await login1({ const { serverPublicKey, salt } = await login1({
email, email,
clientPublicKey, clientPublicKey,
providerAuthToken, providerAuthToken
}); });
client.setSalt(salt); client.setSalt(salt);
@@ -65,13 +57,11 @@ const attemptLogin = async (
encryptedPrivateKey, encryptedPrivateKey,
iv, iv,
tag tag
} = await login2( } = await login2({
{
email, email,
clientProof, clientProof,
providerAuthToken, providerAuthToken
} });
);
if (mfaEnabled) { if (mfaEnabled) {
// case: MFA is enabled // case: MFA is enabled
@@ -79,18 +69,12 @@ const attemptLogin = async (
// set temporary (MFA) JWT token // set temporary (MFA) JWT token
SecurityClient.setMfaToken(token); SecurityClient.setMfaToken(token);
resolve({ return {
mfaEnabled, mfaEnabled,
success: true success: true
}); };
} else if ( }
!mfaEnabled && if (!mfaEnabled && encryptionVersion && encryptedPrivateKey && iv && tag && token) {
encryptionVersion &&
encryptedPrivateKey &&
iv &&
tag &&
token
) {
// case: MFA is not enabled // case: MFA is not enabled
// unset provider auth token in case it was used // unset provider auth token in case it was used
@@ -123,17 +107,12 @@ const attemptLogin = async (
telemetry.capture("User Logged In"); telemetry.capture("User Logged In");
} }
resolve({ return {
mfaEnabled: false, mfaEnabled: false,
success: true success: true
}); };
} }
} catch (err) { return { success: false, mfaEnabled: false };
reject(err);
}
}
);
});
}; };
export default attemptLogin; export default attemptLogin;