Fixing Edit SSO Settings Modal

This commit is contained in:
yuneng-jiang 2026-01-05 18:08:41 -08:00
parent 4cdcdc98e8
commit 2d464032c5
4 changed files with 83 additions and 84 deletions

View file

@ -189,11 +189,16 @@ const BaseSSOSettingsForm: React.FC<BaseSSOSettingsFormProps> = ({ form, onFormS
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) => prevValues.use_role_mappings !== currentValues.use_role_mappings}
shouldUpdate={(prevValues, currentValues) =>
prevValues.use_role_mappings !== currentValues.use_role_mappings ||
prevValues.sso_provider !== currentValues.sso_provider
}
>
{({ getFieldValue }) => {
const useRoleMappings = getFieldValue("use_role_mappings");
return useRoleMappings ? (
const provider = getFieldValue("sso_provider");
const supportsRoleMappings = provider === "okta" || provider === "generic";
return useRoleMappings && supportsRoleMappings ? (
<Form.Item
label="Group Claim"
name="group_claim"
@ -207,11 +212,16 @@ const BaseSSOSettingsForm: React.FC<BaseSSOSettingsFormProps> = ({ form, onFormS
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) => prevValues.use_role_mappings !== currentValues.use_role_mappings}
shouldUpdate={(prevValues, currentValues) =>
prevValues.use_role_mappings !== currentValues.use_role_mappings ||
prevValues.sso_provider !== currentValues.sso_provider
}
>
{({ getFieldValue }) => {
const useRoleMappings = getFieldValue("use_role_mappings");
return useRoleMappings ? (
const provider = getFieldValue("sso_provider");
const supportsRoleMappings = provider === "okta" || provider === "generic";
return useRoleMappings && supportsRoleMappings ? (
<>
<Form.Item label="Default Role" name="default_role" initialValue="Internal User">
<Select>

View file

@ -1,79 +1,66 @@
import { Modal } from "antd";
import { useEditSSOSettings } from "@/app/(dashboard)/hooks/sso/useEditSSOSettings";
import { useSSOSettings } from "@/app/(dashboard)/hooks/sso/useSSOSettings";
import React from "react";
import DeleteResourceModal from "../../../../common_components/DeleteResourceModal";
import NotificationsManager from "../../../../molecules/notifications_manager";
import { updateSSOSettings } from "../../../../networking";
import { parseErrorMessage } from "../../../../shared/errorUtils";
import { detectSSOProvider } from "../utils";
interface DeleteSSOSettingsModalProps {
isVisible: boolean;
onCancel: () => void;
onSuccess: () => void;
accessToken: string | null;
}
const DeleteSSOSettingsModal: React.FC<DeleteSSOSettingsModalProps> = ({
isVisible,
onCancel,
onSuccess,
accessToken,
}) => {
const DeleteSSOSettingsModal: React.FC<DeleteSSOSettingsModalProps> = ({ isVisible, onCancel, onSuccess }) => {
const { data: ssoSettings } = useSSOSettings();
const { mutateAsync: editSSOSettings, isPending: isEditingSSOSettings } = useEditSSOSettings();
// Handle clearing SSO settings
const handleClearSSO = async () => {
if (!accessToken) {
NotificationsManager.fromBackend("No access token available");
return;
}
const clearSettings = {
google_client_id: null,
google_client_secret: null,
microsoft_client_id: null,
microsoft_client_secret: null,
microsoft_tenant: null,
generic_client_id: null,
generic_client_secret: null,
generic_authorization_endpoint: null,
generic_token_endpoint: null,
generic_userinfo_endpoint: null,
proxy_base_url: null,
user_email: null,
sso_provider: null,
role_mappings: null,
};
try {
// Clear all SSO settings
const clearSettings = {
google_client_id: null,
google_client_secret: null,
microsoft_client_id: null,
microsoft_client_secret: null,
microsoft_tenant: null,
generic_client_id: null,
generic_client_secret: null,
generic_authorization_endpoint: null,
generic_token_endpoint: null,
generic_userinfo_endpoint: null,
proxy_base_url: null,
user_email: null,
sso_provider: null,
};
await updateSSOSettings(accessToken, clearSettings);
NotificationsManager.success("SSO settings cleared successfully");
// Close modal and trigger success callback
onCancel();
onSuccess();
} catch (error) {
console.error("Failed to clear SSO settings:", error);
NotificationsManager.fromBackend("Failed to clear SSO settings: " + parseErrorMessage(error));
}
await editSSOSettings(clearSettings, {
onSuccess: () => {
NotificationsManager.success("SSO settings cleared successfully");
onCancel();
onSuccess();
},
onError: (error) => {
NotificationsManager.fromBackend("Failed to clear SSO settings: " + parseErrorMessage(error));
},
});
};
return (
<Modal
<DeleteResourceModal
isOpen={isVisible}
title="Confirm Clear SSO Settings"
visible={isVisible}
onOk={handleClearSSO}
alertMessage="This action cannot be undone."
message="Are you sure you want to clear all SSO settings? Users will no longer be able to login using SSO after this change."
resourceInformationTitle="SSO Settings"
resourceInformation={[
{ label: "Provider", value: (ssoSettings?.values && detectSSOProvider(ssoSettings?.values)) || "Generic" },
]}
onCancel={onCancel}
okText="Yes, Clear"
cancelText="Cancel"
okButtonProps={{
danger: true,
style: {
backgroundColor: "#dc2626",
borderColor: "#dc2626",
},
}}
>
<p>Are you sure you want to clear all SSO settings? This action cannot be undone.</p>
<p>Users will no longer be able to login using SSO after this change.</p>
</Modal>
onOk={handleClearSSO}
confirmLoading={isEditingSSOSettings}
/>
);
};

View file

@ -1,7 +1,6 @@
"use client";
import { useSSOSettings, type SSOSettingsValues } from "@/app/(dashboard)/hooks/sso/useSSOSettings";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { Button, Card, Descriptions, Space, Typography } from "antd";
import { Edit, Shield, Trash2 } from "lucide-react";
import { useState } from "react";
@ -13,12 +12,12 @@ import RedactableField from "./RedactableField";
import RoleMappings from "./RoleMappings";
import SSOSettingsEmptyPlaceholder from "./SSOSettingsEmptyPlaceholder";
import SSOSettingsLoadingSkeleton from "./SSOSettingsLoadingSkeleton";
import { detectSSOProvider } from "./utils";
const { Title, Text } = Typography;
export default function SSOSettings() {
const { data: ssoSettings, refetch, isLoading } = useSSOSettings();
const { accessToken } = useAuthorized();
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const [isAddModalVisible, setIsAddModalVisible] = useState(false);
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
@ -27,23 +26,6 @@ export default function SSOSettings() {
Boolean(ssoSettings?.values.microsoft_client_id) ||
Boolean(ssoSettings?.values.generic_client_id);
// Determine the SSO provider based on the configuration
const detectSSOProvider = (values: SSOSettingsValues): string | null => {
if (values.google_client_id) return "google";
if (values.microsoft_client_id) return "microsoft";
if (values.generic_client_id) {
// Check if it looks like Okta/Auth0 based on endpoints
if (
values.generic_authorization_endpoint?.includes("okta") ||
values.generic_authorization_endpoint?.includes("auth0")
) {
return "okta";
}
return "generic";
}
return null;
};
const selectedProvider = ssoSettings?.values ? detectSSOProvider(ssoSettings.values) : null;
const isRoleMappingsEnabled = Boolean(ssoSettings?.values.role_mappings);
@ -233,7 +215,6 @@ export default function SSOSettings() {
isVisible={isDeleteModalVisible}
onCancel={() => setIsDeleteModalVisible(false)}
onSuccess={() => refetch()}
accessToken={accessToken}
/>
<AddSSOSettingsModal

View file

@ -1,3 +1,5 @@
import { SSOSettingsValues } from "@/app/(dashboard)/hooks/sso/useSSOSettings";
/**
* Processes SSO settings form values and transforms them into the payload format expected by the API
* Handles role mappings transformation and field extraction
@ -11,6 +13,7 @@ export const processSSOSettingsPayload = (formValues: Record<string, any>): Reco
default_role,
group_claim,
use_role_mappings,
sso_provider,
...rest
} = formValues;
@ -18,8 +21,9 @@ export const processSSOSettingsPayload = (formValues: Record<string, any>): Reco
...rest,
};
// Add role mappings if use_role_mappings is checked
if (use_role_mappings) {
// Add role mappings only if use_role_mappings is checked AND provider supports role mappings
const supportsRoleMappings = sso_provider === "okta" || sso_provider === "generic";
if (use_role_mappings && supportsRoleMappings) {
// Helper function to split comma-separated string into array
const splitTeams = (teams: string | undefined): string[] => {
if (!teams || teams.trim() === "") return [];
@ -52,3 +56,20 @@ export const processSSOSettingsPayload = (formValues: Record<string, any>): Reco
return payload;
};
// Determine the SSO provider based on the configuration
export const detectSSOProvider = (values: SSOSettingsValues): string | null => {
if (values.google_client_id) return "google";
if (values.microsoft_client_id) return "microsoft";
if (values.generic_client_id) {
// Check if it looks like Okta/Auth0 based on endpoints
if (
values.generic_authorization_endpoint?.includes("okta") ||
values.generic_authorization_endpoint?.includes("auth0")
) {
return "okta";
}
return "generic";
}
return null;
};