Merge pull request #4588 from Infisical/feat/ENG-3757

Add SAML group mapping
This commit is contained in:
carlosmonastyrski
2025-09-30 22:47:38 -03:00
committed by GitHub
12 changed files with 445 additions and 22 deletions

View File

@@ -34,7 +34,8 @@ export const useCreateSSOConfig = () => {
isActive,
entryPoint,
issuer,
cert
cert,
enableGroupSync
}: {
organizationId: string;
authProvider: string;
@@ -42,6 +43,7 @@ export const useCreateSSOConfig = () => {
entryPoint: string;
issuer: string;
cert: string;
enableGroupSync?: boolean;
}) => {
const { data } = await apiRequest.post("/api/v1/sso/config", {
organizationId,
@@ -49,7 +51,8 @@ export const useCreateSSOConfig = () => {
isActive,
entryPoint,
issuer,
cert
cert,
...(enableGroupSync !== undefined ? { enableGroupSync } : {})
});
return data;
@@ -69,7 +72,8 @@ export const useUpdateSSOConfig = () => {
isActive,
entryPoint,
issuer,
cert
cert,
enableGroupSync
}: {
organizationId: string;
authProvider?: string;
@@ -77,6 +81,7 @@ export const useUpdateSSOConfig = () => {
entryPoint?: string;
issuer?: string;
cert?: string;
enableGroupSync?: boolean;
}) => {
const { data } = await apiRequest.patch("/api/v1/sso/config", {
organizationId,
@@ -84,7 +89,8 @@ export const useUpdateSSOConfig = () => {
...(isActive !== undefined ? { isActive } : {}),
...(entryPoint !== undefined ? { entryPoint } : {}),
...(issuer !== undefined ? { issuer } : {}),
...(cert !== undefined ? { cert } : {})
...(cert !== undefined ? { cert } : {}),
...(enableGroupSync !== undefined ? { enableGroupSync } : {})
});
return data;

View File

@@ -1,7 +1,10 @@
import { faInfoCircle, faWarning } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import { Button, Switch } from "@app/components/v2";
import { Button, Switch, Tooltip } from "@app/components/v2";
import {
OrgPermissionActions,
OrgPermissionSubjects,
@@ -13,6 +16,9 @@ import { usePopUp } from "@app/hooks/usePopUp";
import { SSOModal } from "./SSOModal";
// Auth providers that support group sync
const GROUP_SYNC_SUPPORTED_PROVIDERS = ["google-saml"] as const;
export const OrgSSOSection = (): JSX.Element => {
const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
@@ -21,6 +27,7 @@ export const OrgSSOSection = (): JSX.Element => {
const { mutateAsync } = useUpdateSSOConfig();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"upgradePlan",
"upgradeEnterprisePlan",
"addSSO"
] as const);
@@ -53,6 +60,33 @@ export const OrgSSOSection = (): JSX.Element => {
}
};
const handleSamlGroupManagement = async (value: boolean) => {
try {
if (!currentOrg?.id) return;
if (!subscription?.samlSSO || !subscription?.groups) {
handlePopUpOpen("upgradeEnterprisePlan");
return;
}
await mutateAsync({
organizationId: currentOrg?.id,
enableGroupSync: value
});
createNotification({
text: `Successfully ${value ? "enabled" : "disabled"} SAML group membership mapping`,
type: "success"
});
} catch (err) {
console.error(err);
createNotification({
text: `Failed to ${value ? "enable" : "disable"} SAML group membership mapping`,
type: "error"
});
}
};
const addSSOBtnClick = async () => {
try {
if (subscription?.samlSSO && currentOrg) {
@@ -132,6 +166,66 @@ export const OrgSSOSection = (): JSX.Element => {
Allow members to authenticate into Infisical with SAML
</p>
</div>
{data && GROUP_SYNC_SUPPORTED_PROVIDERS.includes(data.authProvider) && (
<div className="py-4">
<div className="mb-2 flex justify-between">
<div className="text-md flex items-center text-mineshaft-100">
<span>SAML Group Membership Mapping</span>
<Tooltip
className="max-w-lg"
content={
<>
<p>
When this feature is enabled, Infisical will automatically sync group
memberships between the SAML provider and Infisical. Users will be added to
Infisical groups that match their SAML group names.
</p>
<p className="mt-4">
To use this feature you must include group claims in the SAML response as a
&quot;groups&quot; attribute.
</p>
<a
target="_blank"
rel="noopener noreferrer"
className="underline underline-offset-2 hover:text-mineshaft-300"
href="https://infisical.com/docs/documentation/platform/sso/overview"
>
See your SAML provider docs for details.
</a>
<p className="mt-4 text-yellow">
<FontAwesomeIcon className="mr-1" icon={faWarning} />
Group membership changes in the SAML provider only sync with Infisical when a
user logs in via SAML. For example, if you remove a user from a group in the
SAML provider, this change will not be reflected in Infisical until their next
SAML login. To ensure this behavior, Infisical recommends enabling Enforce
SAML SSO.
</p>
</>
}
>
<FontAwesomeIcon
icon={faInfoCircle}
size="sm"
className="ml-1 mt-0.5 inline-block text-mineshaft-400"
/>
</Tooltip>
</div>
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Sso}>
{(isAllowed) => (
<Switch
id="enable-saml-group-sync"
isChecked={data?.enableGroupSync ?? false}
onCheckedChange={(value) => handleSamlGroupManagement(value)}
isDisabled={!isAllowed}
/>
)}
</OrgPermissionCan>
</div>
<p className="text-sm text-mineshaft-300">
Infisical will manage user group memberships based on the SAML provider
</p>
</div>
)}
<SSOModal
popUp={popUp}
handlePopUpClose={handlePopUpClose}
@@ -142,6 +236,11 @@ export const OrgSSOSection = (): JSX.Element => {
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can use SAML SSO if you switch to Infisical's Pro plan."
/>
<UpgradePlanModal
isOpen={popUp.upgradeEnterprisePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradeEnterprisePlan", isOpen)}
text="You can use SAML group mapping if you switch to Infisical's Enterprise plan."
/>
</div>
);
};