Spaces:
Sleeping
Sleeping
import React, { useState, useEffect } from "react"; | |
import { | |
Card, | |
Title, | |
Text, | |
Grid, | |
Col, | |
Button as TremorButton, | |
Callout, | |
TextInput, | |
Divider, | |
} from "@tremor/react"; | |
import { message, Form } from "antd"; | |
import { keyCreateCall } from "./networking"; | |
import { CopyToClipboard } from "react-copy-to-clipboard"; | |
import { | |
LinkOutlined, | |
KeyOutlined, | |
CopyOutlined, | |
ExclamationCircleOutlined, | |
PlusCircleOutlined | |
} from "@ant-design/icons"; | |
interface SCIMConfigProps { | |
accessToken: string | null; | |
userID: string | null; | |
proxySettings: any; | |
} | |
const SCIMConfig: React.FC<SCIMConfigProps> = ({ accessToken, userID, proxySettings }) => { | |
const [form] = Form.useForm(); | |
const [isCreatingToken, setIsCreatingToken] = useState(false); | |
const [tokenData, setTokenData] = useState<any>(null); | |
const [baseUrl, setBaseUrl] = useState("<your_proxy_base_url>"); | |
useEffect(() => { | |
let url = "<your_proxy_base_url>"; | |
if (proxySettings && proxySettings.PROXY_BASE_URL && proxySettings.PROXY_BASE_URL !== undefined) { | |
url = proxySettings.PROXY_BASE_URL; | |
} else if (typeof window !== 'undefined') { | |
// Use the current origin as the base URL if no proxy URL is set | |
url = window.location.origin; | |
} | |
setBaseUrl(url); | |
}, [proxySettings]); | |
const scimBaseUrl = `${baseUrl}/scim/v2`; | |
const handleCreateSCIMToken = async (values: any) => { | |
if (!accessToken || !userID) { | |
message.error("You need to be logged in to create a SCIM token"); | |
return; | |
} | |
try { | |
setIsCreatingToken(true); | |
const formData = { | |
key_alias: values.key_alias || "SCIM Access Token", | |
team_id: null, | |
models: [], | |
allowed_routes: ["/scim/*"], | |
}; | |
const response = await keyCreateCall(accessToken, userID, formData); | |
setTokenData(response); | |
message.success("SCIM token created successfully"); | |
} catch (error) { | |
console.error("Error creating SCIM token:", error); | |
message.error("Failed to create SCIM token"); | |
} finally { | |
setIsCreatingToken(false); | |
} | |
}; | |
return ( | |
<Grid numItems={1}> | |
<Card> | |
<div className="flex items-center mb-4"> | |
<Title>SCIM Configuration</Title> | |
</div> | |
<Text className="text-gray-600"> | |
System for Cross-domain Identity Management (SCIM) allows you to automatically provision and manage users and groups in LiteLLM. | |
</Text> | |
<Divider /> | |
<div className="space-y-8"> | |
{/* Step 1: SCIM URL */} | |
<div> | |
<div className="flex items-center mb-2"> | |
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-blue-100 text-blue-700 mr-2"> | |
1 | |
</div> | |
<Title className="text-lg flex items-center"> | |
<LinkOutlined className="h-5 w-5 mr-2" /> | |
SCIM Tenant URL | |
</Title> | |
</div> | |
<Text className="text-gray-600 mb-3"> | |
Use this URL in your identity provider SCIM integration settings. | |
</Text> | |
<div className="flex items-center"> | |
<TextInput | |
value={scimBaseUrl} | |
disabled={true} | |
className="flex-grow" | |
/> | |
<CopyToClipboard | |
text={scimBaseUrl} | |
onCopy={() => message.success("URL copied to clipboard")} | |
> | |
<TremorButton variant="primary" className="ml-2 flex items-center"> | |
<CopyOutlined className="h-4 w-4 mr-1" /> | |
Copy | |
</TremorButton> | |
</CopyToClipboard> | |
</div> | |
</div> | |
{/* Step 2: SCIM Token */} | |
<div> | |
<div className="flex items-center mb-2"> | |
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-blue-100 text-blue-700 mr-2"> | |
2 | |
</div> | |
<Title className="text-lg flex items-center"> | |
<KeyOutlined className="h-5 w-5 mr-2" /> | |
Authentication Token | |
</Title> | |
</div> | |
<Callout title="Using SCIM" color="blue" className="mb-4"> | |
You need a SCIM token to authenticate with the SCIM API. Create one below and use it in your SCIM provider configuration. | |
</Callout> | |
{!tokenData ? ( | |
<div className="bg-gray-50 p-4 rounded-lg"> | |
<Form | |
form={form} | |
onFinish={handleCreateSCIMToken} | |
layout="vertical" | |
> | |
<Form.Item | |
name="key_alias" | |
label="Token Name" | |
rules={[{ required: true, message: "Please enter a name for your token" }]} | |
> | |
<TextInput placeholder="SCIM Access Token" /> | |
</Form.Item> | |
<Form.Item> | |
<TremorButton | |
variant="primary" | |
type="submit" | |
loading={isCreatingToken} | |
className="flex items-center" | |
> | |
<KeyOutlined className="h-4 w-4 mr-1" /> | |
Create SCIM Token | |
</TremorButton> | |
</Form.Item> | |
</Form> | |
</div> | |
) : ( | |
<Card className="border border-yellow-300 bg-yellow-50"> | |
<div className="flex items-center mb-2 text-yellow-800"> | |
<ExclamationCircleOutlined className="h-5 w-5 mr-2" /> | |
<Title className="text-lg text-yellow-800">Your SCIM Token</Title> | |
</div> | |
<Text className="text-yellow-800 mb-4 font-medium"> | |
Make sure to copy this token now. You will not be able to see it again. | |
</Text> | |
<div className="flex items-center"> | |
<TextInput | |
value={tokenData.token} | |
className="flex-grow mr-2 bg-white" | |
type="password" | |
disabled={true} | |
/> | |
<CopyToClipboard | |
text={tokenData.token} | |
onCopy={() => message.success("Token copied to clipboard")} | |
> | |
<TremorButton variant="primary" className="flex items-center"> | |
<CopyOutlined className="h-4 w-4 mr-1" /> | |
Copy | |
</TremorButton> | |
</CopyToClipboard> | |
</div> | |
<TremorButton | |
className="mt-4 flex items-center" | |
variant="secondary" | |
onClick={() => setTokenData(null)} | |
> | |
<PlusCircleOutlined className="h-4 w-4 mr-1" /> | |
Create Another Token | |
</TremorButton> | |
</Card> | |
)} | |
</div> | |
</div> | |
</Card> | |
</Grid> | |
); | |
}; | |
export default SCIMConfig; |