DesertWolf's picture
Upload folder using huggingface_hub
447ebeb verified
import React, { useEffect, useState } from "react";
import { Modal, Form, Input, Button as Button2, Select, message } from "antd";
import { Text, TextInput } from "@tremor/react";
import { getSSOSettings, updateSSOSettings } from "./networking";
interface SSOModalsProps {
isAddSSOModalVisible: boolean;
isInstructionsModalVisible: boolean;
handleAddSSOOk: () => void;
handleAddSSOCancel: () => void;
handleShowInstructions: (formValues: Record<string, any>) => void;
handleInstructionsOk: () => void;
handleInstructionsCancel: () => void;
form: any; // Replace with proper Form type if available
accessToken: string | null;
ssoConfigured?: boolean; // Add optional prop to indicate if SSO is configured
}
const ssoProviderLogoMap: Record<string, string> = {
google: "https://artificialanalysis.ai/img/logos/google_small.svg",
microsoft: "https://upload.wikimedia.org/wikipedia/commons/a/a8/Microsoft_Azure_Logo.svg",
okta: "https://www.okta.com/sites/default/files/Okta_Logo_BrightBlue_Medium.png",
generic: "",
};
// Define the SSO provider configuration type
interface SSOProviderConfig {
envVarMap: Record<string, string>;
fields: Array<{
label: string;
name: string;
placeholder?: string;
}>;
}
// Define configurations for each SSO provider
const ssoProviderConfigs: Record<string, SSOProviderConfig> = {
google: {
envVarMap: {
google_client_id: 'GOOGLE_CLIENT_ID',
google_client_secret: 'GOOGLE_CLIENT_SECRET',
},
fields: [
{ label: 'GOOGLE CLIENT ID', name: 'google_client_id' },
{ label: 'GOOGLE CLIENT SECRET', name: 'google_client_secret' },
],
},
microsoft: {
envVarMap: {
microsoft_client_id: 'MICROSOFT_CLIENT_ID',
microsoft_client_secret: 'MICROSOFT_CLIENT_SECRET',
microsoft_tenant: 'MICROSOFT_TENANT',
},
fields: [
{ label: 'MICROSOFT CLIENT ID', name: 'microsoft_client_id' },
{ label: 'MICROSOFT CLIENT SECRET', name: 'microsoft_client_secret' },
{ label: 'MICROSOFT TENANT', name: 'microsoft_tenant' },
],
},
okta: {
envVarMap: {
generic_client_id: 'GENERIC_CLIENT_ID',
generic_client_secret: 'GENERIC_CLIENT_SECRET',
generic_authorization_endpoint: 'GENERIC_AUTHORIZATION_ENDPOINT',
generic_token_endpoint: 'GENERIC_TOKEN_ENDPOINT',
generic_userinfo_endpoint: 'GENERIC_USERINFO_ENDPOINT',
},
fields: [
{ label: 'GENERIC CLIENT ID', name: 'generic_client_id' },
{ label: 'GENERIC CLIENT SECRET', name: 'generic_client_secret' },
{ label: 'AUTHORIZATION ENDPOINT', name: 'generic_authorization_endpoint', placeholder: 'https://your-okta-domain/authorize' },
{ label: 'TOKEN ENDPOINT', name: 'generic_token_endpoint', placeholder: 'https://your-okta-domain/token' },
{ label: 'USERINFO ENDPOINT', name: 'generic_userinfo_endpoint', placeholder: 'https://your-okta-domain/userinfo' },
],
},
generic: {
envVarMap: {
generic_client_id: 'GENERIC_CLIENT_ID',
generic_client_secret: 'GENERIC_CLIENT_SECRET',
generic_authorization_endpoint: 'GENERIC_AUTHORIZATION_ENDPOINT',
generic_token_endpoint: 'GENERIC_TOKEN_ENDPOINT',
generic_userinfo_endpoint: 'GENERIC_USERINFO_ENDPOINT',
},
fields: [
{ label: 'GENERIC CLIENT ID', name: 'generic_client_id' },
{ label: 'GENERIC CLIENT SECRET', name: 'generic_client_secret' },
{ label: 'AUTHORIZATION ENDPOINT', name: 'generic_authorization_endpoint' },
{ label: 'TOKEN ENDPOINT', name: 'generic_token_endpoint' },
{ label: 'USERINFO ENDPOINT', name: 'generic_userinfo_endpoint' },
],
},
};
const SSOModals: React.FC<SSOModalsProps> = ({
isAddSSOModalVisible,
isInstructionsModalVisible,
handleAddSSOOk,
handleAddSSOCancel,
handleShowInstructions,
handleInstructionsOk,
handleInstructionsCancel,
form,
accessToken,
ssoConfigured = false, // Default to false if not provided
}) => {
const [isClearConfirmModalVisible, setIsClearConfirmModalVisible] = useState(false);
// Load existing SSO settings when modal opens
useEffect(() => {
const loadSSOSettings = async () => {
if (isAddSSOModalVisible && accessToken) {
try {
const ssoData = await getSSOSettings(accessToken);
console.log("Raw SSO data received:", ssoData); // Debug log
if (ssoData && ssoData.values) {
console.log("SSO values:", ssoData.values); // Debug log
console.log("user_email from API:", ssoData.values.user_email); // Debug log
// Determine which SSO provider is configured
let selectedProvider = null;
if (ssoData.values.google_client_id) {
selectedProvider = 'google';
} else if (ssoData.values.microsoft_client_id) {
selectedProvider = 'microsoft';
} else if (ssoData.values.generic_client_id) {
// Check if it looks like Okta based on endpoints
if (ssoData.values.generic_authorization_endpoint?.includes('okta') ||
ssoData.values.generic_authorization_endpoint?.includes('auth0')) {
selectedProvider = 'okta';
} else {
selectedProvider = 'generic';
}
}
// Set form values with existing data
const formValues = {
sso_provider: selectedProvider,
proxy_base_url: ssoData.values.proxy_base_url,
user_email: ssoData.values.user_email,
...ssoData.values,
};
console.log("Setting form values:", formValues); // Debug log
// Clear form first, then set values with a small delay to ensure proper initialization
form.resetFields();
setTimeout(() => {
form.setFieldsValue(formValues);
console.log("Form values set, current form values:", form.getFieldsValue()); // Debug log
}, 100);
}
} catch (error) {
console.error("Failed to load SSO settings:", error);
}
}
};
loadSSOSettings();
}, [isAddSSOModalVisible, accessToken, form]);
// Enhanced form submission handler
const handleFormSubmit = async (formValues: Record<string, any>) => {
if (!accessToken) {
message.error("No access token available");
return;
}
try {
// Save SSO settings using the new API
await updateSSOSettings(accessToken, formValues);
// Continue with the original flow (show instructions)
handleShowInstructions(formValues);
} catch (error) {
console.error("Failed to save SSO settings:", error);
message.error("Failed to save SSO settings");
}
};
// Handle clearing SSO settings
const handleClearSSO = async () => {
if (!accessToken) {
message.error("No access token available");
return;
}
try {
// Clear all SSO settings by sending empty values
const clearSettings = {
google_client_id: '',
google_client_secret: '',
microsoft_client_id: '',
microsoft_client_secret: '',
microsoft_tenant: '',
generic_client_id: '',
generic_client_secret: '',
generic_authorization_endpoint: '',
generic_token_endpoint: '',
generic_userinfo_endpoint: '',
proxy_base_url: '',
user_email: '',
sso_provider: '',
};
await updateSSOSettings(accessToken, clearSettings);
// Clear the form
form.resetFields();
// Close the confirmation modal
setIsClearConfirmModalVisible(false);
// Close the main SSO modal and trigger refresh
handleAddSSOOk();
message.success("SSO settings cleared successfully");
} catch (error) {
console.error("Failed to clear SSO settings:", error);
message.error("Failed to clear SSO settings");
}
};
// Helper function to render provider fields
const renderProviderFields = (provider: string) => {
const config = ssoProviderConfigs[provider];
if (!config) return null;
return config.fields.map((field) => (
<Form.Item
key={field.name}
label={field.label}
name={field.name}
rules={[{ required: true, message: `Please enter the ${field.label.toLowerCase()}` }]}
>
{field.name.includes('client') ? (
<Input.Password />
) : (
<TextInput placeholder={field.placeholder} />
)}
</Form.Item>
));
};
return (
<>
<Modal
title={ssoConfigured ? "Edit SSO Settings" : "Add SSO"}
visible={isAddSSOModalVisible}
width={800}
footer={null}
onOk={handleAddSSOOk}
onCancel={handleAddSSOCancel}
>
<Form
form={form}
onFinish={handleFormSubmit}
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
labelAlign="left"
>
<>
<Form.Item
label="SSO Provider"
name="sso_provider"
rules={[{ required: true, message: "Please select an SSO provider" }]}
>
<Select>
{Object.entries(ssoProviderLogoMap).map(([value, logo]) => (
<Select.Option key={value} value={value}>
<div style={{ display: 'flex', alignItems: 'center', padding: '4px 0' }}>
{logo && <img src={logo} alt={value} style={{ height: 24, width: 24, marginRight: 12, objectFit: 'contain' }} />}
<span>{value.charAt(0).toUpperCase() + value.slice(1)} SSO</span>
</div>
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) => prevValues.sso_provider !== currentValues.sso_provider}
>
{({ getFieldValue }) => {
const provider = getFieldValue('sso_provider');
return provider ? renderProviderFields(provider) : null;
}}
</Form.Item>
<Form.Item
label="Proxy Admin Email"
name="user_email"
rules={[{ required: true, message: "Please enter the email of the proxy admin" }]}
>
<TextInput />
</Form.Item>
<Form.Item
label="PROXY BASE URL"
name="proxy_base_url"
rules={[{ required: true, message: "Please enter the proxy base url" }]}
>
<TextInput />
</Form.Item>
</>
<div style={{ textAlign: "right", marginTop: "10px", display: "flex", justifyContent: "flex-end", alignItems: "center", gap: "8px" }}>
{ssoConfigured && (
<Button2
onClick={() => setIsClearConfirmModalVisible(true)}
style={{
backgroundColor: '#6366f1',
borderColor: '#6366f1',
color: 'white'
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = '#5558eb';
e.currentTarget.style.borderColor = '#5558eb';
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = '#6366f1';
e.currentTarget.style.borderColor = '#6366f1';
}}
>
Clear
</Button2>
)}
<Button2 htmlType="submit">Save</Button2>
</div>
</Form>
</Modal>
{/* Clear Confirmation Modal */}
<Modal
title="Confirm Clear SSO Settings"
visible={isClearConfirmModalVisible}
onOk={handleClearSSO}
onCancel={() => setIsClearConfirmModalVisible(false)}
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>
<Modal
title="SSO Setup Instructions"
visible={isInstructionsModalVisible}
width={800}
footer={null}
onOk={handleInstructionsOk}
onCancel={handleInstructionsCancel}
>
<p>Follow these steps to complete the SSO setup:</p>
<Text className="mt-2">1. DO NOT Exit this TAB</Text>
<Text className="mt-2">2. Open a new tab, visit your proxy base url</Text>
<Text className="mt-2">
3. Confirm your SSO is configured correctly and you can login on the new
Tab
</Text>
<Text className="mt-2">
4. If Step 3 is successful, you can close this tab
</Text>
<div style={{ textAlign: "right", marginTop: "10px" }}>
<Button2 onClick={handleInstructionsOk}>Done</Button2>
</div>
</Modal>
</>
);
};
export { ssoProviderConfigs }; // Export for use in other components
export default SSOModals;