mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 08:27:22 +00:00
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:
@@ -1,24 +1,16 @@
|
|||||||
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 {
|
||||||
createOrganization as create,
|
createOrganization as create,
|
||||||
deleteOrganization,
|
deleteOrganization,
|
||||||
updateSubscriptionOrgQuantity
|
updateSubscriptionOrgQuantity
|
||||||
} 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" });
|
||||||
@@ -336,7 +328,7 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
|||||||
* @param res
|
* @param res
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const createOrganization = async (req: Request, res: Response) => {
|
export const createOrganization = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
body: { name }
|
body: { name }
|
||||||
} = await validateRequest(reqValidator.CreateOrgv2, req);
|
} = await validateRequest(reqValidator.CreateOrgv2, req);
|
||||||
@@ -361,27 +353,27 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete organization with id [organizationId]
|
* Delete organization with id [organizationId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteOrganizationById = async (req: Request, res: Response) => {
|
export const deleteOrganizationById = async (req: Request, res: Response) => {
|
||||||
const {
|
const {
|
||||||
params: { organizationId }
|
params: { organizationId }
|
||||||
} = await validateRequest(reqValidator.DeleteOrgv2, req);
|
} = await validateRequest(reqValidator.DeleteOrgv2, req);
|
||||||
|
|
||||||
const membershipOrg = await MembershipOrg.findOne({
|
const membershipOrg = await MembershipOrg.findOne({
|
||||||
user: req.user._id,
|
user: req.user._id,
|
||||||
organization: new Types.ObjectId(organizationId),
|
organization: new Types.ObjectId(organizationId),
|
||||||
role: ADMIN
|
role: ADMIN
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!membershipOrg) throw UnauthorizedRequestError();
|
if (!membershipOrg) throw UnauthorizedRequestError();
|
||||||
|
|
||||||
const organization = await deleteOrganization({
|
const organization = await deleteOrganization({
|
||||||
organizationId: new Types.ObjectId(organizationId)
|
organizationId: new Types.ObjectId(organizationId)
|
||||||
});
|
});
|
||||||
|
|
||||||
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,118 +19,100 @@ 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 { 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
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;
|
export default attemptLogin;
|
||||||
|
|||||||
Reference in New Issue
Block a user