diff --git a/frontend/src/components/signup/InitialSignupStep.tsx b/frontend/src/components/signup/InitialSignupStep.tsx
index 7953ff0df..e5c23f333 100644
--- a/frontend/src/components/signup/InitialSignupStep.tsx
+++ b/frontend/src/components/signup/InitialSignupStep.tsx
@@ -1,9 +1,7 @@
import { useTranslation } from "react-i18next";
import Link from "next/link";
-import { useRouter } from "next/router";
import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons";
import { faEnvelope } from "@fortawesome/free-regular-svg-icons";
-import { faLock } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Button } from "../v2";
@@ -14,7 +12,6 @@ export default function InitialSignupStep({
setIsSignupWithEmail: (value: boolean) => void;
}) {
const { t } = useTranslation();
- const router = useRouter();
return (
@@ -76,17 +73,6 @@ export default function InitialSignupStep({
Continue with Email
-
- router.push("/saml-sso")}
- leftIcon={ }
- className="mx-0 h-12 w-full"
- >
- Continue with SSO
-
-
{t("signup.create-policy")}
diff --git a/frontend/src/context/OrganizationContext/OrganizationContext.tsx b/frontend/src/context/OrganizationContext/OrganizationContext.tsx
index a4562ef82..e73578d13 100644
--- a/frontend/src/context/OrganizationContext/OrganizationContext.tsx
+++ b/frontend/src/context/OrganizationContext/OrganizationContext.tsx
@@ -25,7 +25,7 @@ export const OrgProvider = ({ children }: Props): JSX.Element => {
const value = useMemo
(
() => ({
orgs: userOrgs,
- currentOrg: (userOrgs || []).find(({ id }) => id === currentWsOrgID) || (userOrgs || [])[0],
+ currentOrg: (userOrgs || []).find(({ id }) => id === currentWsOrgID),
isLoading
}),
[currentWsOrgID, userOrgs, isLoading]
diff --git a/frontend/src/hooks/api/organization/index.ts b/frontend/src/hooks/api/organization/index.ts
index c8a284836..6a8af3b67 100644
--- a/frontend/src/hooks/api/organization/index.ts
+++ b/frontend/src/hooks/api/organization/index.ts
@@ -17,6 +17,6 @@ export {
useGetOrgPmtMethods,
useGetOrgTaxIds,
useGetOrgTrialUrl,
- useRenameOrg,
+ useUpdateOrg,
useUpdateOrgBillingDetails
} from "./queries";
diff --git a/frontend/src/hooks/api/organization/queries.tsx b/frontend/src/hooks/api/organization/queries.tsx
index 0edaf2c2f..018fa1edb 100644
--- a/frontend/src/hooks/api/organization/queries.tsx
+++ b/frontend/src/hooks/api/organization/queries.tsx
@@ -12,8 +12,8 @@ import {
PlanBillingInfo,
PmtMethod,
ProductsTable,
- RenameOrgDTO,
- TaxID
+ TaxID,
+ UpdateOrgDTO
} from "./types";
export const organizationKeys = {
@@ -65,12 +65,20 @@ export const useCreateOrg = () => {
});
};
-export const useRenameOrg = () => {
+export const useUpdateOrg = () => {
const queryClient = useQueryClient();
-
- return useMutation<{}, {}, RenameOrgDTO>({
- mutationFn: ({ newOrgName, orgId }) => {
- return apiRequest.patch(`/api/v1/organization/${orgId}/name`, { name: newOrgName });
+ return useMutation<{}, {}, UpdateOrgDTO>({
+ mutationFn: ({
+ name,
+ authEnforced,
+ slug,
+ orgId
+ }) => {
+ return apiRequest.patch(`/api/v1/organization/${orgId}`, {
+ name,
+ authEnforced,
+ slug
+ });
},
onSuccess: () => {
queryClient.invalidateQueries(organizationKeys.getUserOrganizations);
diff --git a/frontend/src/hooks/api/organization/types.ts b/frontend/src/hooks/api/organization/types.ts
index 3bfbd7419..6492e3515 100644
--- a/frontend/src/hooks/api/organization/types.ts
+++ b/frontend/src/hooks/api/organization/types.ts
@@ -3,11 +3,15 @@ export type Organization = {
name: string;
createAt: string;
updatedAt: string;
+ authEnforced: boolean;
+ slug: string;
};
-export type RenameOrgDTO = {
+export type UpdateOrgDTO = {
orgId: string;
- newOrgName: string;
+ name?: string;
+ authEnforced?: boolean;
+ slug?: string;
};
export type BillingDetails = {
diff --git a/frontend/src/layouts/AppLayout/AppLayout.tsx b/frontend/src/layouts/AppLayout/AppLayout.tsx
index 3009c0726..d175afe48 100644
--- a/frontend/src/layouts/AppLayout/AppLayout.tsx
+++ b/frontend/src/layouts/AppLayout/AppLayout.tsx
@@ -272,7 +272,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
createNotification({ text: "Failed to create workspace", type: "error" });
}
};
-
+
return (
<>
@@ -310,10 +310,25 @@ export const AppLayout = ({ children }: LayoutProps) => {
{user?.email}
- {orgs?.map((org) => (
+ {orgs?.map((org) => {
+ return (
changeOrg(org?.id)}
+ onClick={async () => {
+ if (currentOrg?.id === org.id) return;
+
+ if (org.authEnforced) {
+ // org has an org-level auth method enabled (e.g. SAML)
+ // -> logout + redirect to SAML SSO
+
+ await logout.mutateAsync();
+ window.open(`/api/v1/sso/redirect/saml2/organizations/${org.slug}`);
+ window.close();
+ return;
+ }
+
+ changeOrg(org?.id)
+ }}
variant="plain"
colorSchema="secondary"
size="xs"
@@ -329,7 +344,8 @@ export const AppLayout = ({ children }: LayoutProps) => {
- ))}
+ )
+ })}
{/*
handlePopUpOpen("createOrg")}
@@ -604,16 +620,18 @@ export const AppLayout = ({ children }: LayoutProps) => {
-
-
-
- Usage & Billing
-
-
-
+ {(window.location.origin.includes("https://app.infisical.com")) && (
+
+
+
+ Usage & Billing
+
+
+
+ )}
{
diff --git a/frontend/src/views/Login/Login.utils.tsx b/frontend/src/views/Login/Login.utils.tsx
index 8df93e3b9..0aa063268 100644
--- a/frontend/src/views/Login/Login.utils.tsx
+++ b/frontend/src/views/Login/Login.utils.tsx
@@ -4,6 +4,8 @@ import { fetchOrganizations } from "@app/hooks/api/organization/queries";
export const navigateUserToOrg = async (router: NextRouter, organizationId?: string) => {
const userOrgs = await fetchOrganizations();
+
+ const nonAuthEnforcedOrgs = userOrgs.filter((org) => !org.authEnforced);
if (organizationId) {
localStorage.setItem("orgData.id", organizationId);
@@ -11,13 +13,13 @@ export const navigateUserToOrg = async (router: NextRouter, organizationId?: str
return;
}
- if (userOrgs.length > 0) {
- // user is part of at least 1 org
- const userOrg = userOrgs[0] && userOrgs[0].id;
+ if (nonAuthEnforcedOrgs.length > 0) {
+ // user is part of at least 1 non-auth enforced org
+ const userOrg = nonAuthEnforcedOrgs[0] && nonAuthEnforcedOrgs[0].id;
localStorage.setItem("orgData.id", userOrg);
router.push(`/org/${userOrg}/overview`);
} else {
- // user is not part of any org
+ // user is not part of any non-auth enforced orgs
localStorage.removeItem("orgData.id");
router.push("/org/none");
}
diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
index 73abbb56e..bbef55751 100644
--- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
+++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx
@@ -180,7 +180,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
leftIcon={ }
className="mx-0 h-10 w-full"
>
- Continue with SSO
+ Continue with SAML SSO
diff --git a/frontend/src/views/Login/components/SAMLSSOStep/SAMLSSOStep.tsx b/frontend/src/views/Login/components/SAMLSSOStep/SAMLSSOStep.tsx
index b4da2ce92..4a4545ac8 100644
--- a/frontend/src/views/Login/components/SAMLSSOStep/SAMLSSOStep.tsx
+++ b/frontend/src/views/Login/components/SAMLSSOStep/SAMLSSOStep.tsx
@@ -18,14 +18,14 @@ export const SAMLSSOStep = ({
const handleSubmission = (e:React.FormEvent) => {
e.preventDefault()
const callbackPort = queryParams.get("callback_port");
- window.open(`/api/v1/sso/redirect/saml2/${ssoIdentifier}${callbackPort ? `?callback_port=${callbackPort}` : ""}`);
+ window.open(`/api/v1/sso/redirect/saml2/organizations/${ssoIdentifier}${callbackPort ? `?callback_port=${callbackPort}` : ""}`);
window.close();
}
return (
- What's your SSO Identifier?
+ What's your organization slug?
diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersSection.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersSection.tsx
index 3f2a8cc15..246922b58 100644
--- a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersSection.tsx
+++ b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersSection.tsx
@@ -16,7 +16,7 @@ import {
useOrganization,
useSubscription
} from "@app/context";
-import { useDeleteOrgMembership, useGetSSOConfig } from "@app/hooks/api";
+import { useDeleteOrgMembership } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp";
import { AddOrgMemberModal } from "./AddOrgMemberModal";
@@ -27,10 +27,9 @@ export const OrgMembersSection = () => {
const { subscription } = useSubscription();
const { currentOrg } = useOrganization();
const orgId = currentOrg?.id ?? "";
-
+
const [completeInviteLink, setCompleteInviteLink] = useState
("");
- const { data: ssoConfig, isLoading: isLoadingSSOConfig } = useGetSSOConfig(orgId);
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"addMember",
"removeMember",
@@ -45,9 +44,9 @@ export const OrgMembersSection = () => {
: false;
const handleAddMemberModal = () => {
- if (!isLoadingSSOConfig && ssoConfig && ssoConfig.isActive) {
+ if (currentOrg?.authEnforced) {
createNotification({
- text: "You cannot invite users when SAML SSO is configured for your organization",
+ text: "You cannot invite users when org-level auth is configured for your organization",
type: "error"
});
return;
diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTable/OrgMembersTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTable/OrgMembersTable.tsx
deleted file mode 100644
index f163bd530..000000000
--- a/frontend/src/views/Org/MembersPage/components/OrgMembersTable/OrgMembersTable.tsx
+++ /dev/null
@@ -1,577 +0,0 @@
-import { useCallback, useEffect, useMemo, useState } from "react";
-import { Controller, useForm } from "react-hook-form";
-import { useRouter } from "next/router";
-import {
- faCheck,
- faCopy,
- faMagnifyingGlass,
- faPlus,
- faTrash,
- faUsers
-} from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { yupResolver } from "@hookform/resolvers/yup";
-import * as yup from "yup";
-
-import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
-import { OrgPermissionCan } from "@app/components/permissions";
-import {
- decryptAssymmetric,
- encryptAssymmetric
-} from "@app/components/utilities/cryptography/crypto";
-import {
- Button,
- DeleteActionModal,
- EmailServiceSetupModal,
- EmptyState,
- FormControl,
- IconButton,
- Input,
- Modal,
- ModalContent,
- Select,
- SelectItem,
- Table,
- TableContainer,
- TableSkeleton,
- Tag,
- TBody,
- Td,
- Th,
- THead,
- Tr,
- UpgradePlanModal
-} from "@app/components/v2";
-import {
- OrgPermissionActions,
- OrgPermissionSubjects,
- useOrganization,
- useSubscription,
- useUser,
- useWorkspace
-} from "@app/context";
-import { usePopUp, useToggle } from "@app/hooks";
-import {
- useAddUserToOrg,
- useDeleteOrgMembership,
- useGetOrgUsers,
- useGetSSOConfig,
- useGetUserWorkspaceMemberships,
- useGetUserWsKey,
- useUpdateOrgUserRole,
- useUploadWsKey
-} from "@app/hooks/api";
-import { TProjectRole } from "@app/hooks/api/roles/types";
-import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
-
-type Props = {
- roles?: TProjectRole[];
- isRolesLoading?: boolean;
-};
-
-const addMemberFormSchema = yup.object({
- email: yup.string().email().required().label("Email").trim().lowercase()
-});
-
-type TAddMemberForm = yup.InferType;
-
-export const OrgMembersTable = ({ roles = [], isRolesLoading }: Props) => {
- const router = useRouter();
- const { createNotification } = useNotificationContext();
-
- const { currentOrg } = useOrganization();
- const { workspaces, currentWorkspace } = useWorkspace();
- const { user } = useUser();
- const userId = user?.id || "";
- const orgId = currentOrg?.id || "";
- const workspaceId = currentWorkspace?.id || "";
-
- const { data: ssoConfig, isLoading: isLoadingSSOConfig } = useGetSSOConfig(orgId);
- const [searchMemberFilter, setSearchMemberFilter] = useState("");
- const { data: serverDetails } = useFetchServerStatus();
-
- const [isInviteLinkCopied, setInviteLinkCopied] = useToggle(false);
- const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
- "addMember",
- "removeMember",
- "upgradePlan",
- "setUpEmail"
- ] as const);
- const { subscription } = useSubscription();
-
- const { data: members, isLoading: isMembersLoading } = useGetOrgUsers(orgId);
- const { data: workspaceMemberships, isLoading: IsWsMembershipLoading } =
- useGetUserWorkspaceMemberships(orgId);
- const { data: wsKey } = useGetUserWsKey(workspaceId);
-
- const removeUserOrgMembership = useDeleteOrgMembership();
- const addUserToOrg = useAddUserToOrg();
- const updateOrgUserRole = useUpdateOrgUserRole();
- const uploadWsKey = useUploadWsKey();
-
- const [completeInviteLink, setCompleteInviteLink] = useState("");
-
- const isMoreUsersNotAllowed = subscription?.memberLimit
- ? subscription.membersUsed >= subscription.memberLimit
- : false;
-
- useEffect(() => {
- if (router.query.action === "invite") {
- handlePopUpOpen("addMember");
- }
- }, []);
-
- const {
- control,
- handleSubmit,
- reset,
- formState: { isSubmitting }
- } = useForm({ resolver: yupResolver(addMemberFormSchema) });
-
- const onAddMember = async ({ email }: TAddMemberForm) => {
- if (!currentOrg?.id) return;
-
- try {
- const { data } = await addUserToOrg.mutateAsync({
- organizationId: currentOrg?.id,
- inviteeEmail: email
- });
- setCompleteInviteLink(data?.completeInviteLink);
- // only show this notification when email is configured.
- // A [completeInviteLink] will not be sent if smtp is configured
- if (!data.completeInviteLink) {
- createNotification({
- text: "Successfully invited user to the organization.",
- type: "success"
- });
- }
- } catch (error) {
- console.error(error);
- createNotification({
- text: "Failed to invite user to org",
- type: "error"
- });
- }
- if (serverDetails?.emailConfigured) {
- handlePopUpClose("addMember");
- }
- reset();
- };
-
- const onAddUserToOrg = async (email: string) => {
- if (!currentOrg?.id) return;
-
- try {
- const { data } = await addUserToOrg.mutateAsync({
- organizationId: currentOrg?.id,
- inviteeEmail: email
- });
- setCompleteInviteLink(data?.completeInviteLink);
-
- // only show this notification when email is configured. A [completeInviteLink] will not be sent if smtp is configured
- if (!data.completeInviteLink) {
- createNotification({
- text: "Successfully invited user to the organization.",
- type: "success"
- });
- }
- } catch (error) {
- console.error(error);
- createNotification({
- text: "Failed to invite user to org",
- type: "error"
- });
- }
- };
-
- const onRemoveOrgMemberApproved = async () => {
- const membershipId = (popUp?.removeMember?.data as { id: string })?.id;
- if (!currentOrg?.id) return;
-
- try {
- await removeUserOrgMembership.mutateAsync({ orgId: currentOrg?.id, membershipId });
- createNotification({
- text: "Successfully removed user from org",
- type: "success"
- });
- } catch (error) {
- console.error(error);
- createNotification({
- text: "Failed to remove user from the organization",
- type: "error"
- });
- }
- handlePopUpClose("removeMember");
- };
-
- const isIamOwner = useMemo(
- () => members?.find(({ user: u }) => userId === u?.id)?.role === "owner",
- [userId, members]
- );
-
- const findRoleFromId = useCallback(
- (roleId: string) => {
- return roles.find(({ id }) => id === roleId);
- },
- [roles]
- );
-
- const filterdUser = useMemo(
- () =>
- members?.filter(
- ({ user: u, inviteEmail }) =>
- u?.firstName?.toLowerCase().includes(searchMemberFilter) ||
- u?.lastName?.toLowerCase().includes(searchMemberFilter) ||
- u?.email?.toLowerCase().includes(searchMemberFilter) ||
- inviteEmail?.includes(searchMemberFilter)
- ),
- [members, searchMemberFilter]
- );
-
- useEffect(() => {
- let timer: NodeJS.Timeout;
- if (isInviteLinkCopied) {
- timer = setTimeout(() => setInviteLinkCopied.off(), 2000);
- }
- return () => clearTimeout(timer);
- }, [isInviteLinkCopied]);
-
- const onRoleChange = async (membershipId: string, role: string) => {
- if (!currentOrg?.id) return;
-
- try {
- await updateOrgUserRole.mutateAsync({ organizationId: currentOrg?.id, membershipId, role });
- createNotification({
- text: "Successfully updated user role",
- type: "success"
- });
- } catch (error) {
- console.error(error);
- createNotification({
- text: "Failed to update user role",
- type: "error"
- });
- }
- };
-
- 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.mutateAsync({
- userId: grantedUserId,
- nonce,
- encryptedKey: ciphertext,
- workspaceId: currentWorkspace?.id || ""
- });
- } catch (err) {
- console.error(err);
- createNotification({
- text: "Failed to grant access to user",
- type: "error"
- });
- }
- };
-
- const copyTokenToClipboard = () => {
- navigator.clipboard.writeText(completeInviteLink as string);
- setInviteLinkCopied.on();
- };
-
- const isLoading = isMembersLoading || IsWsMembershipLoading || isRolesLoading;
-
- return (
-
-
-
- setSearchMemberFilter(e.target.value)}
- leftIcon={ }
- placeholder="Search members..."
- />
-
-
- {(isAllowed) => (
- }
- onClick={() => {
- if (!isLoadingSSOConfig && ssoConfig && ssoConfig.isActive) {
- createNotification({
- text: "You cannot invite users when SAML SSO is configured for your organization",
- type: "error"
- });
-
- return;
- }
-
- if (isMoreUsersNotAllowed) {
- handlePopUpOpen("upgradePlan");
- } else {
- handlePopUpOpen("addMember");
- }
- }}
- >
- Add Member
-
- )}
-
-
-
-
-
-
-
- Name
- Email
- Role
- Projects
-
-
-
-
- {isLoading && }
- {!isLoading &&
- filterdUser?.map(
- ({ user: u, inviteEmail, role, roleId, id: orgMembershipId, status }) => {
- const name = u ? `${u.firstName || "-"} ${u.lastName || ""}` : "-";
- const email = u?.email || inviteEmail;
- const userWs = workspaceMemberships?.[u?.id];
-
- return (
-
- {name}
- {email}
-
-
- {(isAllowed) => (
- <>
- {status === "accepted" && (
-
- onRoleChange(orgMembershipId, selectedRole)
- }
- >
- {roles
- .filter(({ slug }) =>
- slug === "owner" ? isIamOwner || role === "owner" : true
- )
- .map(({ slug, name: roleName }) => (
-
- {roleName}
-
- ))}
-
- )}
- {(status === "invited" || status === "verified") &&
- serverDetails?.emailConfigured && (
- onAddUserToOrg(email)}
- >
- Resend Invite
-
- )}
- {status === "completed" && (
- onGrantAccess(u?.id, u?.publicKey)}
- >
- Grant Access
-
- )}
- >
- )}
-
-
-
- {userWs ? (
- userWs?.map(({ name: wsName, id }) => (
-
- {wsName}
-
- ))
- ) : (
-
- {(status === "invited" || status === "verified") &&
- serverDetails?.emailConfigured ? (
-
- This user hasn't accepted the invite yet
-
- ) : (
-
- This user isn't part of any projects yet
-
- )}
- {router.query.id !== "undefined" &&
- !(
- (status === "invited" || status === "verified") &&
- serverDetails?.emailConfigured
- ) && (
-
- router.push(`/project/${workspaces[0]?.id}/members`)
- }
- className="w-max cursor-pointer rounded-sm bg-mineshaft px-1.5 py-0.5 text-sm duration-200 hover:bg-primary hover:text-black"
- >
-
- Add to projects
-
- )}
-
- )}
-
-
- {userId !== u?.id && (
-
- {(isAllowed) => (
-
- handlePopUpOpen("removeMember", { id: orgMembershipId })
- }
- >
-
-
- )}
-
- )}
-
-
- );
- }
- )}
-
-
- {!isLoading && filterdUser?.length === 0 && (
-
- )}
-
-
-
{
- handlePopUpToggle("addMember", isOpen);
- setCompleteInviteLink(undefined);
- }}
- >
-
- {!completeInviteLink && (
-
- An invite is specific to an email address and expires after 1 day.
-
- For security reasons, you will need to separately add members to projects.
-
- )}
- {completeInviteLink &&
- "This Infisical instance does not have a email provider setup. Please share this invite link with the invitee manually"}
-
- }
- >
- {!completeInviteLink && (
-
- )}
- {completeInviteLink && (
-
-
{completeInviteLink}
-
-
-
- click to copy
-
-
-
- )}
-
-
- handlePopUpToggle("removeMember", isOpen)}
- onDeleteApproved={onRemoveOrgMemberApproved}
- />
- handlePopUpToggle("upgradePlan", isOpen)}
- text="You can add custom environments if you switch to Infisical's Team plan."
- />
- handlePopUpToggle("setUpEmail", isOpen)}
- />
-
- );
-};
diff --git a/frontend/src/views/Org/NonePage/NonePage.tsx b/frontend/src/views/Org/NonePage/NonePage.tsx
index 9d6b4a65c..531ef823b 100644
--- a/frontend/src/views/Org/NonePage/NonePage.tsx
+++ b/frontend/src/views/Org/NonePage/NonePage.tsx
@@ -1,9 +1,15 @@
+import { useEffect } from "react";
+
import { usePopUp } from "@app/hooks/usePopUp";
import { CreateOrgModal } from "../components";
export const NonePage = () => {
const { popUp, handlePopUpToggle } = usePopUp(["createOrg"] as const);
+
+ useEffect(() => {
+ handlePopUpToggle("createOrg", true);
+ }, []);
return (
@@ -13,4 +19,4 @@ export const NonePage = () => {
/>
);
-};
+};
\ No newline at end of file
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
index c29c948fc..c4d78b6f0 100644
--- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
+++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgAuthTab.tsx
@@ -1,12 +1,14 @@
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { withPermission } from "@app/hoc";
+import { OrgGeneralAuthSection } from "./OrgGeneralAuthSection";
import { OrgSSOSection } from "./OrgSSOSection";
export const OrgAuthTab = withPermission(
() => {
return (
+
);
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgGeneralAuthSection.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgGeneralAuthSection.tsx
new file mode 100644
index 000000000..9bc026cf8
--- /dev/null
+++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgGeneralAuthSection.tsx
@@ -0,0 +1,65 @@
+import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
+import { OrgPermissionCan } from "@app/components/permissions";
+import { Switch } from "@app/components/v2";
+import {
+ OrgPermissionActions,
+ OrgPermissionSubjects,
+ useOrganization
+} from "@app/context";
+import { useLogoutUser,useUpdateOrg } from "@app/hooks/api";
+
+export const OrgGeneralAuthSection = () => {
+ const { createNotification } = useNotificationContext();
+ const { currentOrg } = useOrganization();
+
+ const { mutateAsync } = useUpdateOrg();
+
+ const logout = useLogoutUser();
+
+ const handleEnforceOrgAuthToggle = async (value: boolean) => {
+ try {
+ if (!currentOrg?.id) return;
+
+ await mutateAsync({
+ orgId: currentOrg?.id,
+ authEnforced: value
+ });
+
+ createNotification({
+ text: `Successfully ${value ? "enforced" : "un-enforced"} org-level auth`,
+ type: "success"
+ });
+
+ if (value) {
+ await logout.mutateAsync();
+ window.open(`/api/v1/sso/redirect/saml2/organizations/${currentOrg.slug}`);
+ window.close();
+ }
+
+ } catch (err) {
+ console.error(err);
+ createNotification({
+ text: `Failed to ${value ? "enforce" : "un-enforce"} org-level auth`,
+ type: "error"
+ });
+ }
+ }
+
+ return (
+
+
Settings
+
+ {(isAllowed) => (
+ handleEnforceOrgAuthToggle(value)}
+ isChecked={currentOrg?.authEnforced ?? false}
+ isDisabled={!isAllowed}
+ >
+ Enforce SAML SSO
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSSOSection.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSSOSection.tsx
index ab28ec127..23900708d 100644
--- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSSOSection.tsx
+++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/OrgSSOSection.tsx
@@ -1,5 +1,6 @@
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";
@@ -136,6 +137,10 @@ export const OrgSSOSection = (): JSX.Element => {
Issuer
{data && data.issuer !== "" ? data.issuer : "-"}