mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 15:31:19 +00:00
Merge branch 'heads/main' into overview-page-enchanced
This commit is contained in:
@@ -7,9 +7,9 @@ import { getAuthToken, isLoggedIn } from "@app/reactQuery";
|
||||
|
||||
import {
|
||||
InitialStep,
|
||||
LDAPStep,
|
||||
MFAStep,
|
||||
SAMLSSOStep
|
||||
} from "./components";
|
||||
SAMLSSOStep} from "./components";
|
||||
import { navigateUserToOrg } from "./Login.utils";
|
||||
|
||||
export const Login = () => {
|
||||
@@ -72,7 +72,10 @@ export const Login = () => {
|
||||
return (
|
||||
<SAMLSSOStep setStep={setStep} />
|
||||
);
|
||||
|
||||
case 3:
|
||||
return (
|
||||
<LDAPStep setStep={setStep} />
|
||||
);
|
||||
default:
|
||||
return <div />;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export const LoginSSO = ({ providerAuthToken }: Props) => {
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const {
|
||||
email,
|
||||
username,
|
||||
isUserCompleted
|
||||
} = jwt_decode(providerAuthToken) as any;
|
||||
|
||||
@@ -35,7 +35,7 @@ export const LoginSSO = ({ providerAuthToken }: Props) => {
|
||||
return (
|
||||
<PasswordStep
|
||||
providerAuthToken={providerAuthToken}
|
||||
email={email}
|
||||
email={username}
|
||||
password={password}
|
||||
setPassword={setPassword}
|
||||
setStep={setStep}
|
||||
@@ -45,7 +45,7 @@ export const LoginSSO = ({ providerAuthToken }: Props) => {
|
||||
return (
|
||||
<MFAStep
|
||||
providerAuthToken={providerAuthToken}
|
||||
email={email}
|
||||
email={username}
|
||||
password={password}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -180,7 +180,20 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
|
||||
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
||||
className="mx-0 h-10 w-full"
|
||||
>
|
||||
Continue with SAML SSO
|
||||
Continue with SAML
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
onClick={() => {
|
||||
setStep(3);
|
||||
}}
|
||||
leftIcon={<FontAwesomeIcon icon={faLock} className="mr-2" />}
|
||||
className="mx-0 h-10 w-full"
|
||||
>
|
||||
Continue with LDAP
|
||||
</Button>
|
||||
</div>
|
||||
<div className="my-4 flex w-1/4 min-w-[20rem] flex-row items-center py-2 lg:w-1/6">
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { Button, Input } from "@app/components/v2";
|
||||
import { loginLDAPRedirect } from "@app/hooks/api/auth/queries";
|
||||
|
||||
type Props = {
|
||||
setStep: (step: number) => void;
|
||||
}
|
||||
|
||||
export const LDAPStep = ({
|
||||
setStep
|
||||
}: Props) => {
|
||||
const { createNotification } = useNotificationContext();
|
||||
const [organizationSlug, setOrganizationSlug] = useState("");
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
// const queryParams = new URLSearchParams(window.location.search);
|
||||
|
||||
const handleSubmission = async (e:React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
const { nextUrl } = await loginLDAPRedirect({
|
||||
organizationSlug,
|
||||
username,
|
||||
password
|
||||
});
|
||||
|
||||
if (!nextUrl) {
|
||||
createNotification({
|
||||
text: "Login unsuccessful. Double-check your credentials and try again.",
|
||||
type: "error"
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
createNotification({
|
||||
text: "Successfully logged in",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
// redirects either to /login/sso or /signup/sso
|
||||
window.open(nextUrl);
|
||||
window.close();
|
||||
} catch (err) {
|
||||
createNotification({
|
||||
text: "Login unsuccessful. Double-check your credentials and try again.",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: add callback port support
|
||||
|
||||
// const callbackPort = queryParams.get("callback_port");
|
||||
// window.open(`/api/v1/ldap/redirect/saml2/${ssoIdentifier}${callbackPort ? `?callback_port=${callbackPort}` : ""}`);
|
||||
// window.close();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-md md:px-6">
|
||||
<p className="mx-auto mb-6 flex w-max justify-center text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8">
|
||||
What's your LDAP Login?
|
||||
</p>
|
||||
<form onSubmit={handleSubmission}>
|
||||
<div className="relative flex items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] md:min-w-[22rem] mx-auto w-full rounded-lg max-h-24 md:max-h-28">
|
||||
<div className="flex items-center justify-center w-full rounded-lg max-h-24 md:max-h-28">
|
||||
<Input
|
||||
value={organizationSlug}
|
||||
onChange={(e) => setOrganizationSlug(e.target.value)}
|
||||
type="text"
|
||||
placeholder="Enter your organization slug..."
|
||||
isRequired
|
||||
autoComplete="email"
|
||||
id="email"
|
||||
className="h-12"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 relative flex items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] md:min-w-[22rem] mx-auto w-full rounded-lg max-h-24 md:max-h-28">
|
||||
<div className="flex items-center justify-center w-full rounded-lg max-h-24 md:max-h-28">
|
||||
<Input
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
type="text"
|
||||
placeholder="Enter your LDAP username..."
|
||||
isRequired
|
||||
autoComplete="email"
|
||||
id="email"
|
||||
className="h-12"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 relative flex items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] md:min-w-[22rem] mx-auto w-full rounded-lg max-h-24 md:max-h-28">
|
||||
<div className="flex items-center justify-center w-full rounded-lg max-h-24 md:max-h-28">
|
||||
<Input
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
type="password"
|
||||
placeholder="Enter your LDAP password..."
|
||||
isRequired
|
||||
autoComplete="current-password"
|
||||
id="current-password"
|
||||
className="select:-webkit-autofill:focus h-10"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='lg:w-1/6 w-1/4 w-full mx-auto flex items-center justify-center min-w-[20rem] md:min-w-[22rem] text-center rounded-md mt-4'>
|
||||
<Button
|
||||
type="submit"
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
isFullWidth
|
||||
className="h-14"
|
||||
>
|
||||
{t("login.login")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="flex flex-row items-center justify-center mt-4">
|
||||
<button
|
||||
onClick={() => {
|
||||
setStep(0);
|
||||
}}
|
||||
type="button"
|
||||
className="text-bunker-300 text-sm hover:underline mt-2 hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer"
|
||||
>
|
||||
{t("login.other-option")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { LDAPStep } from "./LDAPStep";
|
||||
@@ -1,4 +1,5 @@
|
||||
export { InitialStep } from "./InitialStep";
|
||||
export { LDAPStep } from "./LDAPStep";
|
||||
export { MFAStep } from "./MFAStep";
|
||||
export { SAMLSSOStep } from "./SAMLSSOStep";
|
||||
|
||||
|
||||
+2
-2
@@ -113,8 +113,8 @@ export const OrgMembersSection = () => {
|
||||
/>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.removeMember.isOpen}
|
||||
title={`Are you sure want to remove member with email ${
|
||||
(popUp?.removeMember?.data as { email: string })?.email || ""
|
||||
title={`Are you sure want to remove member with username ${
|
||||
(popUp?.removeMember?.data as { username: string })?.username || ""
|
||||
}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle("removeMember", isOpen)}
|
||||
deleteKey="confirm"
|
||||
|
||||
+7
-5
@@ -41,7 +41,7 @@ type Props = {
|
||||
popUpName: keyof UsePopUpState<["removeMember", "upgradePlan"]>,
|
||||
data?: {
|
||||
orgMembershipId?: string;
|
||||
email?: string;
|
||||
username?: string;
|
||||
description?: string;
|
||||
}
|
||||
) => void;
|
||||
@@ -143,6 +143,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
||||
({ user: u, inviteEmail }) =>
|
||||
u?.firstName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||
u?.lastName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||
u?.username?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||
u?.email?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||
inviteEmail?.includes(searchMemberFilter.toLowerCase())
|
||||
),
|
||||
@@ -162,7 +163,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Name</Th>
|
||||
<Th>Email</Th>
|
||||
<Th>Username</Th>
|
||||
<Th>Role</Th>
|
||||
<Th className="w-5" />
|
||||
</Tr>
|
||||
@@ -174,10 +175,11 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
||||
({ user: u, inviteEmail, role, roleId, id: orgMembershipId, status }) => {
|
||||
const name = u && u.firstName ? `${u.firstName} ${u.lastName}` : "-";
|
||||
const email = u?.email || inviteEmail;
|
||||
const username = u?.username ?? inviteEmail ?? "-";
|
||||
return (
|
||||
<Tr key={`org-membership-${orgMembershipId}`} className="w-full">
|
||||
<Td>{name}</Td>
|
||||
<Td>{email}</Td>
|
||||
<Td>{username}</Td>
|
||||
<Td>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Edit}
|
||||
@@ -206,7 +208,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
{(status === "invited" || status === "verified") &&
|
||||
{(status === "invited" || status === "verified") && email &&
|
||||
serverDetails?.emailConfigured && (
|
||||
<Button
|
||||
isDisabled={!isAllowed}
|
||||
@@ -239,7 +241,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
||||
return;
|
||||
}
|
||||
|
||||
handlePopUpOpen("removeMember", { orgMembershipId, email });
|
||||
handlePopUpOpen("removeMember", { orgMembershipId, username });
|
||||
}}
|
||||
size="lg"
|
||||
colorSchema="danger"
|
||||
|
||||
+6
@@ -82,6 +82,12 @@ const SIMPLE_PERMISSION_OPTIONS = [
|
||||
icon: faSignIn,
|
||||
formName: "sso"
|
||||
},
|
||||
{
|
||||
title: "LDAP",
|
||||
subtitle: "Define organization level LDAP requirements",
|
||||
icon: faSignIn,
|
||||
formName: "ldap"
|
||||
},
|
||||
{
|
||||
title: "SCIM",
|
||||
subtitle: "Define organization level SCIM requirements",
|
||||
|
||||
+1
@@ -35,6 +35,7 @@ export const formSchema = z.object({
|
||||
"secret-scanning": generalPermissionSchema,
|
||||
sso: generalPermissionSchema,
|
||||
scim: generalPermissionSchema,
|
||||
ldap: generalPermissionSchema,
|
||||
billing: generalPermissionSchema,
|
||||
identity: generalPermissionSchema
|
||||
})
|
||||
|
||||
@@ -9,10 +9,6 @@ import { z } from "zod";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
decryptAssymmetric,
|
||||
encryptAssymmetric
|
||||
} from "@app/components/utilities/cryptography/crypto";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
@@ -51,8 +47,7 @@ import {
|
||||
useGetProjectRoles,
|
||||
useGetUserWsKey,
|
||||
useGetWorkspaceUsers,
|
||||
useUpdateUserWorkspaceRole,
|
||||
useUploadWsKey
|
||||
useUpdateUserWorkspaceRole
|
||||
} from "@app/hooks/api";
|
||||
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||
import { ProjectVersion } from "@app/hooks/api/workspace/types";
|
||||
@@ -81,7 +76,7 @@ export const MemberListTab = () => {
|
||||
const { data: wsKey } = useGetUserWsKey(workspaceId);
|
||||
const { data: members, isLoading: isMembersLoading } = useGetWorkspaceUsers(workspaceId);
|
||||
const { data: orgUsers } = useGetOrgUsers(orgId);
|
||||
|
||||
|
||||
const [searchMemberFilter, setSearchMemberFilter] = useState("");
|
||||
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
@@ -99,7 +94,6 @@ export const MemberListTab = () => {
|
||||
|
||||
const { mutateAsync: addUserToWorkspace } = useAddUserToWsE2EE();
|
||||
const { mutateAsync: addUserToWorkspaceNonE2EE } = useAddUserToWsNonE2EE();
|
||||
const { mutateAsync: uploadWsKey } = useUploadWsKey();
|
||||
const { mutateAsync: removeUserFromWorkspace } = useDeleteUserFromWorkspace();
|
||||
const { mutateAsync: updateUserWorkspaceRole } = useUpdateUserWorkspaceRole();
|
||||
|
||||
@@ -117,7 +111,7 @@ export const MemberListTab = () => {
|
||||
const orgUser = (orgUsers || []).find(({ id }) => id === orgMembershipId);
|
||||
if (!orgUser) return;
|
||||
|
||||
try {
|
||||
try { // TODO: update
|
||||
if (currentWorkspace.version === ProjectVersion.V1) {
|
||||
await addUserToWorkspace({
|
||||
workspaceId,
|
||||
@@ -128,7 +122,7 @@ export const MemberListTab = () => {
|
||||
} else if (currentWorkspace.version === ProjectVersion.V2) {
|
||||
await addUserToWorkspaceNonE2EE({
|
||||
projectId: workspaceId,
|
||||
emails: [orgUser.user.email]
|
||||
usernames: [orgUser.user.username]
|
||||
});
|
||||
} else {
|
||||
createNotification({
|
||||
@@ -154,11 +148,11 @@ export const MemberListTab = () => {
|
||||
};
|
||||
|
||||
const handleRemoveUser = async () => {
|
||||
const email = (popUp?.removeMember?.data as { email: string })?.email;
|
||||
const username = (popUp?.removeMember?.data as { username: string })?.username;
|
||||
if (!currentOrg?.id) return;
|
||||
|
||||
try {
|
||||
await removeUserFromWorkspace({ workspaceId, emails: [email] });
|
||||
await removeUserFromWorkspace({ workspaceId, usernames: [username] });
|
||||
createNotification({
|
||||
text: "Successfully removed user from project",
|
||||
type: "success"
|
||||
@@ -220,6 +214,7 @@ export const MemberListTab = () => {
|
||||
({ user: u, inviteEmail }) =>
|
||||
u?.firstName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||
u?.lastName?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||
u?.username?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||
u?.email?.toLowerCase().includes(searchMemberFilter.toLowerCase()) ||
|
||||
inviteEmail?.includes(searchMemberFilter.toLowerCase())
|
||||
),
|
||||
@@ -227,49 +222,15 @@ export const MemberListTab = () => {
|
||||
);
|
||||
|
||||
const filteredOrgUsers = useMemo(() => {
|
||||
const wsUserEmails = new Map();
|
||||
const wsUserUsernames = new Map();
|
||||
members?.forEach((member) => {
|
||||
wsUserEmails.set(member.user.email, true);
|
||||
wsUserUsernames.set(member.user.username, true);
|
||||
});
|
||||
return (orgUsers || []).filter(
|
||||
({ status, user: u }) => status === "accepted" && !wsUserEmails.has(u.email)
|
||||
({ status, user: u }) => status === "accepted" && !wsUserUsernames.has(u.username)
|
||||
);
|
||||
}, [orgUsers, members]);
|
||||
|
||||
const onGrantAccess = async (grantedUserId: string, publicKey: string) => {
|
||||
try {
|
||||
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
|
||||
if (!PRIVATE_KEY || !wsKey) return;
|
||||
|
||||
// assymmetrically decrypt symmetric key with local private key
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: wsKey.encryptedKey,
|
||||
nonce: wsKey.nonce,
|
||||
publicKey: wsKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
const { ciphertext, nonce } = encryptAssymmetric({
|
||||
plaintext: key,
|
||||
publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
await uploadWsKey({
|
||||
userId: grantedUserId,
|
||||
nonce,
|
||||
encryptedKey: ciphertext,
|
||||
workspaceId: currentWorkspace?.id || ""
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to grant access to user",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const isLoading = isMembersLoading || isRolesLoading;
|
||||
|
||||
return (
|
||||
@@ -302,7 +263,7 @@ export const MemberListTab = () => {
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Name</Th>
|
||||
<Th>Email</Th>
|
||||
<Th>Username</Th>
|
||||
<Th>Role</Th>
|
||||
<Th className="w-5" />
|
||||
</Tr>
|
||||
@@ -311,22 +272,20 @@ export const MemberListTab = () => {
|
||||
{isLoading && <TableSkeleton columns={4} innerKey="project-members" />}
|
||||
{!isLoading &&
|
||||
filterdUsers?.map(
|
||||
({ user: u, inviteEmail, id: membershipId, status, roleId, role }) => {
|
||||
({ user: u, id: membershipId, roleId, role }) => {
|
||||
const name = u ? `${u.firstName} ${u.lastName}` : "-";
|
||||
const email = u?.email || inviteEmail;
|
||||
|
||||
const username = u?.username ?? "-";
|
||||
return (
|
||||
<Tr key={`membership-${membershipId}`} className="w-full">
|
||||
<Td>{name}</Td>
|
||||
<Td>{email}</Td>
|
||||
<Td>{username}</Td>
|
||||
<Td>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.Member}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<>
|
||||
<Select
|
||||
<Select
|
||||
value={role === "custom" ? findRoleFromId(roleId)?.slug : role}
|
||||
isDisabled={userId === u?.id || !isAllowed}
|
||||
className="w-40 bg-mineshaft-600"
|
||||
@@ -345,18 +304,6 @@ export const MemberListTab = () => {
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
{status === "completed" && user.email !== email && (
|
||||
<div className="rounded-md border border-mineshaft-700 bg-white/5 text-white duration-200 hover:bg-primary hover:text-black">
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
onClick={() => onGrantAccess(u?.id, u?.publicKey)}
|
||||
>
|
||||
Grant Access
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</Td>
|
||||
@@ -375,7 +322,7 @@ export const MemberListTab = () => {
|
||||
className="ml-4"
|
||||
isDisabled={userId === u?.id || !isAllowed}
|
||||
onClick={() =>
|
||||
handlePopUpOpen("removeMember", { email: u.email })
|
||||
handlePopUpOpen("removeMember", { username: u.username })
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
@@ -407,20 +354,20 @@ export const MemberListTab = () => {
|
||||
<form onSubmit={handleSubmit(onAddMember)}>
|
||||
<Controller
|
||||
control={control}
|
||||
defaultValue={filteredOrgUsers?.[0]?.user?.email}
|
||||
defaultValue={filteredOrgUsers?.[0]?.user?.username}
|
||||
name="orgMembershipId"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl label="Email" isError={Boolean(error)} errorText={error?.message}>
|
||||
<FormControl label="Username" isError={Boolean(error)} errorText={error?.message}>
|
||||
<Select
|
||||
position="popper"
|
||||
className="w-full"
|
||||
defaultValue={filteredOrgUsers?.[0]?.user?.email}
|
||||
defaultValue={filteredOrgUsers?.[0]?.user?.username}
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
{filteredOrgUsers.map(({ id: orgUserId, user: u }) => (
|
||||
<SelectItem value={orgUserId} key={`org-membership-join-${orgUserId}`}>
|
||||
{u?.email}
|
||||
{u?.username}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { subject } from "@casl/ability";
|
||||
import { faArrowDown, faArrowUp } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import NavHeader from "@app/components/navigation/NavHeader";
|
||||
import { PermissionDeniedBanner } from "@app/components/permissions";
|
||||
import { ContentLoader } from "@app/components/v2";
|
||||
@@ -47,9 +48,10 @@ const LOADER_TEXT = [
|
||||
|
||||
export const SecretMainPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentWorkspace, isLoading: isWorkspaceLoading } = useWorkspace();
|
||||
const router = useRouter();
|
||||
const { permission } = useProjectPermission();
|
||||
const { createNotification } = useNotificationContext();
|
||||
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [sortDir, setSortDir] = useState<SortDir>(SortDir.ASC);
|
||||
@@ -75,6 +77,20 @@ export const SecretMainPage = () => {
|
||||
ProjectPermissionSub.SecretRollback
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isWorkspaceLoading &&
|
||||
!currentWorkspace?.environments.find((env) => env.slug === environment) &&
|
||||
router.isReady
|
||||
) {
|
||||
router.push(`/project/${workspaceId}/secrets/overview`);
|
||||
createNotification({
|
||||
text: "No envronment found with given slug",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}, [isWorkspaceLoading, currentWorkspace, environment, router.isReady]);
|
||||
|
||||
const { data: decryptFileKey } = useGetUserWsKey(workspaceId);
|
||||
|
||||
// fetch secrets
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
Input,
|
||||
Modal,
|
||||
ModalContent,
|
||||
TextArea
|
||||
} from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import {
|
||||
useCreateLDAPConfig,
|
||||
useGetLDAPConfig,
|
||||
useUpdateLDAPConfig
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
const LDAPFormSchema = z.object({
|
||||
url: z.string().min(1, "URL is requiredx"),
|
||||
bindDN: z.string().min(1, "Bind DN is requiredx"),
|
||||
bindPass: z.string().min(1, "Bind Pass is required"),
|
||||
searchBase: z.string().min(1, "Search Base is required"),
|
||||
caCert: z.string().optional()
|
||||
});
|
||||
|
||||
export type TLDAPFormData = z.infer<typeof LDAPFormSchema>;
|
||||
|
||||
type Props = {
|
||||
popUp: UsePopUpState<["addLDAP"]>;
|
||||
handlePopUpClose: (popUpName: keyof UsePopUpState<["addLDAP"]>) => void;
|
||||
handlePopUpToggle: (popUpName: keyof UsePopUpState<["addLDAP"]>, state?: boolean) => void;
|
||||
};
|
||||
|
||||
export const LDAPModal = ({
|
||||
popUp,
|
||||
handlePopUpClose,
|
||||
handlePopUpToggle
|
||||
}: Props) => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { mutateAsync: createMutateAsync, isLoading: createIsLoading } = useCreateLDAPConfig();
|
||||
const { mutateAsync: updateMutateAsync, isLoading: updateIsLoading } = useUpdateLDAPConfig();
|
||||
const { data } = useGetLDAPConfig(currentOrg?.id?? "");
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
reset,
|
||||
} = useForm<TLDAPFormData>({
|
||||
resolver: zodResolver(LDAPFormSchema)
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
reset({
|
||||
url: data?.url ?? "",
|
||||
bindDN: data?.bindDN ?? "",
|
||||
bindPass: data?.bindPass ?? "",
|
||||
searchBase: data?.searchBase ?? "",
|
||||
caCert: data?.caCert ?? ""
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
const onSSOModalSubmit = async ({
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}: TLDAPFormData) => {
|
||||
try {
|
||||
if (!currentOrg) return;
|
||||
|
||||
if (!data) {
|
||||
await createMutateAsync({
|
||||
organizationId: currentOrg.id,
|
||||
isActive: false,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
});
|
||||
} else {
|
||||
await updateMutateAsync({
|
||||
organizationId: currentOrg.id,
|
||||
isActive: false,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpClose("addLDAP");
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${!data ? "added" : "updated"} LDAP configuration`,
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to ${!data ? "add" : "update"} LDAP configuration`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={popUp?.addLDAP?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle("addLDAP", isOpen);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalContent title="Add LDAP">
|
||||
<form onSubmit={handleSubmit(onSSOModalSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="url"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="URL"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="ldaps://ldap.myorg.com:636"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="bindDN"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Bind DN"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="cn=infisical,ou=Users,dc=example,dc=com"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="bindPass"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Bind Pass"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
type="password"
|
||||
placeholder="********"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="searchBase"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Search Base / User DN"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="ou=people,dc=acme,dc=com"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="caCert"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="CA Certificate"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<TextArea
|
||||
{...field}
|
||||
placeholder="-----BEGIN CERTIFICATE----- ..."
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
type="submit"
|
||||
isLoading={createIsLoading || updateIsLoading}
|
||||
>
|
||||
{!data ? "Add" : "Update"}
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpClose("addLDAP")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
|
||||
import { OrgGeneralAuthSection } from "./OrgGeneralAuthSection";
|
||||
import { OrgLDAPSection } from "./OrgLDAPSection";
|
||||
import { OrgScimSection } from "./OrgSCIMSection";
|
||||
import { OrgSSOSection } from "./OrgSSOSection";
|
||||
|
||||
@@ -11,6 +12,7 @@ export const OrgAuthTab = withPermission(
|
||||
<div>
|
||||
<OrgGeneralAuthSection />
|
||||
<OrgSSOSection />
|
||||
<OrgLDAPSection />
|
||||
<OrgScimSection />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
Switch,
|
||||
UpgradePlanModal
|
||||
} from "@app/components/v2";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization,
|
||||
useSubscription
|
||||
} from "@app/context";
|
||||
import {
|
||||
useCreateLDAPConfig,
|
||||
useGetLDAPConfig,
|
||||
useUpdateLDAPConfig
|
||||
} from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { LDAPModal } from "./LDAPModal";
|
||||
|
||||
export const OrgLDAPSection = (): JSX.Element => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const { subscription } = useSubscription();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { data, isLoading } = useGetLDAPConfig(currentOrg?.id ?? "");
|
||||
const { mutateAsync } = useUpdateLDAPConfig();
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"addLDAP",
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
const { mutateAsync: createMutateAsync } = useCreateLDAPConfig();
|
||||
|
||||
const handleLDAPToggle = async (value: boolean) => {
|
||||
try {
|
||||
if (!currentOrg?.id) return;
|
||||
if (!subscription?.ldap) {
|
||||
handlePopUpOpen("upgradePlan");
|
||||
return;
|
||||
}
|
||||
|
||||
await mutateAsync({
|
||||
organizationId: currentOrg?.id,
|
||||
isActive: value
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${value ? "enabled" : "disabled"} LDAP`,
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to ${value ? "enable" : "disable"} LDAP`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const addLDAPBtnClick = async () => {
|
||||
try {
|
||||
if (subscription?.ldap && currentOrg) {
|
||||
if (!data) {
|
||||
// case: LDAP is not configured
|
||||
// -> initialize empty LDAP configuration
|
||||
await createMutateAsync({
|
||||
organizationId: currentOrg.id,
|
||||
isActive: false,
|
||||
url: "",
|
||||
bindDN: "",
|
||||
bindPass: "",
|
||||
searchBase: "",
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpOpen("addLDAP");
|
||||
} else {
|
||||
handlePopUpOpen("upgradePlan");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600">
|
||||
<div className="flex items-center mb-8">
|
||||
<h2 className="text-xl font-semibold flex-1 text-white">LDAP</h2>
|
||||
{!isLoading && (
|
||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Ldap}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
onClick={addLDAPBtnClick}
|
||||
colorSchema="secondary"
|
||||
isDisabled={!isAllowed}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
Configure
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
</div>
|
||||
{data && (
|
||||
<div className="mb-4">
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Ldap}>
|
||||
{(isAllowed) => (
|
||||
<Switch
|
||||
id="enable-saml-sso"
|
||||
onCheckedChange={(value) => handleLDAPToggle(value)}
|
||||
isChecked={data ? data.isActive : false}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Enable
|
||||
</Switch>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
)}
|
||||
<LDAPModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You can use LDAP authentication if you switch to Infisical's Enterprise plan."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+3
-3
@@ -66,7 +66,7 @@ export const OrgScimSection = () => {
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-8 flex items-center">
|
||||
<h2 className="flex-1 text-xl font-semibold text-white">SCIM Configuration</h2>
|
||||
<h2 className="flex-1 text-xl font-semibold text-white">SCIM</h2>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Read} a={OrgPermissionSubjects.Scim}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
@@ -75,7 +75,7 @@ export const OrgScimSection = () => {
|
||||
isDisabled={!isAllowed}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
Manage SCIM Tokens
|
||||
Configure
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
@@ -94,7 +94,7 @@ export const OrgScimSection = () => {
|
||||
isChecked={currentOrg?.scimEnabled ?? false}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Enable SCIM Provisioning
|
||||
Enable
|
||||
</Switch>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
|
||||
+9
-37
@@ -1,6 +1,5 @@
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { format } from "date-fns";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
@@ -16,13 +15,6 @@ import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { SSOModal } from "./SSOModal";
|
||||
|
||||
const ssoAuthProviderMap: { [key: string]: string } = {
|
||||
"okta-saml": "Okta SAML",
|
||||
"azure-saml": "Azure SAML",
|
||||
"jumpcloud-saml": "JumpCloud SAML",
|
||||
"google-saml": "Google SAML"
|
||||
};
|
||||
|
||||
export const OrgSSOSection = (): JSX.Element => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const { subscription } = useSubscription();
|
||||
@@ -39,6 +31,11 @@ export const OrgSSOSection = (): JSX.Element => {
|
||||
const handleSamlSSOToggle = async (value: boolean) => {
|
||||
try {
|
||||
if (!currentOrg?.id) return;
|
||||
|
||||
if (!subscription?.samlSSO) {
|
||||
handlePopUpOpen("upgradePlan");
|
||||
return;
|
||||
}
|
||||
|
||||
await mutateAsync({
|
||||
organizationId: currentOrg?.id,
|
||||
@@ -86,7 +83,7 @@ export const OrgSSOSection = (): JSX.Element => {
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-8 flex items-center">
|
||||
<h2 className="flex-1 text-xl font-semibold text-white">SAML SSO Configuration</h2>
|
||||
<h2 className="flex-1 text-xl font-semibold text-white">SAML</h2>
|
||||
{!isLoading && (
|
||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
@@ -96,13 +93,13 @@ export const OrgSSOSection = (): JSX.Element => {
|
||||
isDisabled={!isAllowed}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
{data ? "Update SAML SSO" : "Set up SAML SSO"}
|
||||
Configure
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
)}
|
||||
</div>
|
||||
{data && (
|
||||
{/* {data && ( */}
|
||||
<div className="mb-4">
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Sso}>
|
||||
{(isAllowed) => (
|
||||
@@ -112,36 +109,11 @@ export const OrgSSOSection = (): JSX.Element => {
|
||||
isChecked={data ? data.isActive : false}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Enable SAML SSO
|
||||
Enable
|
||||
</Switch>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm text-mineshaft-400">SSO identifier</h3>
|
||||
<p className="text-md text-gray-400">{data && data.id !== "" ? data.id : "-"}</p>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm text-mineshaft-400">Type</h3>
|
||||
<p className="text-md text-gray-400">
|
||||
{data && data.authProvider !== "" ? ssoAuthProviderMap[data.authProvider] : "-"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm text-mineshaft-400">Entrypoint</h3>
|
||||
<p className="text-md text-gray-400">
|
||||
{data && data.entryPoint !== "" ? data.entryPoint : "-"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm text-mineshaft-400">Issuer</h3>
|
||||
<p className="text-md text-gray-400">{data && data.issuer !== "" ? data.issuer : "-"}</p>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-sm text-mineshaft-400">Last Logged In</h3>
|
||||
<p className="text-md text-gray-400">{data?.lastUsed ? format(new Date(data?.lastUsed), "yyyy-MM-dd HH:mm:ss") : "-"}</p>
|
||||
</div>
|
||||
<SSOModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
|
||||
+1
-3
@@ -48,8 +48,6 @@ export const ChangePasswordSection = () => {
|
||||
|
||||
const onFormSubmit = async ({ oldPassword, newPassword }: FormData) => {
|
||||
try {
|
||||
if (!user?.email) return;
|
||||
|
||||
const errorCheck = await checkPassword({
|
||||
password: newPassword,
|
||||
setErrors
|
||||
@@ -59,7 +57,7 @@ export const ChangePasswordSection = () => {
|
||||
|
||||
setIsLoading(true);
|
||||
await attemptChangePassword({
|
||||
email: user.email,
|
||||
email: user.username,
|
||||
currentPassword: oldPassword,
|
||||
newPassword
|
||||
});
|
||||
|
||||
+12
-2
@@ -1,12 +1,22 @@
|
||||
import {
|
||||
useGetUser
|
||||
} from "@app/hooks/api";
|
||||
import { AuthMethod } from "@app/hooks/api/users/types";
|
||||
|
||||
import { AuthMethodSection } from "../AuthMethodSection";
|
||||
import { ChangePasswordSection } from "../ChangePasswordSection";
|
||||
import { MFASection } from "../SecuritySection";
|
||||
|
||||
export const PersonalAuthTab = () => {
|
||||
const { data: user } = useGetUser();
|
||||
return (
|
||||
<div>
|
||||
<MFASection />
|
||||
<AuthMethodSection />
|
||||
{user && !user.authMethods.includes(AuthMethod.LDAP) && (
|
||||
<>
|
||||
<MFASection />
|
||||
<AuthMethodSection />
|
||||
</>
|
||||
)}
|
||||
<ChangePasswordSection />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
useGetUser,
|
||||
useUpdateMfaEnabled} from "@app/hooks/api";
|
||||
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
||||
import { AuthMethod } from "@app/hooks/api/users/types";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
export const MFASection = () => {
|
||||
@@ -18,6 +19,15 @@ export const MFASection = () => {
|
||||
|
||||
const toggleMfa = async (state: boolean) => {
|
||||
try {
|
||||
if (!user) return;
|
||||
if (user.authMethods.includes(AuthMethod.LDAP)) {
|
||||
createNotification({
|
||||
text: "Two-factor authentication is not available for LDAP users.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const newUser = await mutateAsync({
|
||||
isMfaEnabled: state
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ export const SignupSSO = ({
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const {
|
||||
email,
|
||||
username,
|
||||
organizationName,
|
||||
firstName,
|
||||
lastName,
|
||||
@@ -27,7 +27,7 @@ export const SignupSSO = ({
|
||||
case 0:
|
||||
return (
|
||||
<UserInfoSSOStep
|
||||
email={email}
|
||||
username={username}
|
||||
name={`${firstName} ${lastName}`}
|
||||
providerOrganizationName={organizationName}
|
||||
password={password}
|
||||
@@ -39,7 +39,7 @@ export const SignupSSO = ({
|
||||
case 1:
|
||||
return (
|
||||
<BackupPDFStep
|
||||
email={email}
|
||||
email={username}
|
||||
password={password}
|
||||
name={`${firstName} ${lastName}`}
|
||||
/>
|
||||
|
||||
@@ -24,7 +24,7 @@ const client = new jsrp.client();
|
||||
|
||||
type Props = {
|
||||
setStep: (step: number) => void;
|
||||
email: string;
|
||||
username: string;
|
||||
password: string;
|
||||
setPassword: (value: string) => void;
|
||||
name: string;
|
||||
@@ -57,7 +57,7 @@ type Errors = {
|
||||
* @param {string} obj.setLastName - function managing the state of user's last name
|
||||
*/
|
||||
export const UserInfoSSOStep = ({
|
||||
email,
|
||||
username,
|
||||
name,
|
||||
providerOrganizationName,
|
||||
password,
|
||||
@@ -113,7 +113,7 @@ export const UserInfoSSOStep = ({
|
||||
|
||||
client.init(
|
||||
{
|
||||
username: email,
|
||||
username,
|
||||
password
|
||||
},
|
||||
async () => {
|
||||
@@ -156,7 +156,7 @@ export const UserInfoSSOStep = ({
|
||||
});
|
||||
|
||||
const response = await completeAccountSignup({
|
||||
email,
|
||||
email: username,
|
||||
firstName: name.split(" ")[0],
|
||||
lastName: name.split(" ").slice(1).join(" "),
|
||||
protectedKey,
|
||||
|
||||
Reference in New Issue
Block a user