(proxy ui sso flow) - fix invite user sso flow (#6093)

* return if sso setup on ui_settings

* use helper to get invite link
This commit is contained in:
Ishaan Jaff 2024-10-07 12:32:08 +05:30 committed by GitHub
parent a7628317cd
commit 51af0d5d94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 58 additions and 5 deletions

View file

@ -23,6 +23,7 @@ from litellm.proxy._types import (
SSOUserDefinedValues, SSOUserDefinedValues,
UserAPIKeyAuth, UserAPIKeyAuth,
) )
from litellm.proxy.auth.auth_utils import _has_user_setup_sso
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
from litellm.proxy.common_utils.admin_ui_utils import ( from litellm.proxy.common_utils.admin_ui_utils import (
admin_ui_disabled, admin_ui_disabled,
@ -640,6 +641,7 @@ async def get_ui_settings(request: Request):
_proxy_base_url = os.getenv("PROXY_BASE_URL", None) _proxy_base_url = os.getenv("PROXY_BASE_URL", None)
_logout_url = os.getenv("PROXY_LOGOUT_URL", None) _logout_url = os.getenv("PROXY_LOGOUT_URL", None)
_is_sso_enabled = _has_user_setup_sso()
default_team_disabled = general_settings.get("default_team_disabled", False) default_team_disabled = general_settings.get("default_team_disabled", False)
if "PROXY_DEFAULT_TEAM_DISABLED" in os.environ: if "PROXY_DEFAULT_TEAM_DISABLED" in os.environ:
@ -650,4 +652,5 @@ async def get_ui_settings(request: Request):
"PROXY_BASE_URL": _proxy_base_url, "PROXY_BASE_URL": _proxy_base_url,
"PROXY_LOGOUT_URL": _logout_url, "PROXY_LOGOUT_URL": _logout_url,
"DEFAULT_TEAM_DISABLED": default_team_disabled, "DEFAULT_TEAM_DISABLED": default_team_disabled,
"SSO_ENABLED": _is_sso_enabled,
} }

View file

@ -17,6 +17,7 @@ import {
userCreateCall, userCreateCall,
modelAvailableCall, modelAvailableCall,
invitationCreateCall, invitationCreateCall,
getProxyBaseUrlAndLogoutUrl,
} from "./networking"; } from "./networking";
const { Option } = Select; const { Option } = Select;
@ -27,12 +28,21 @@ interface CreateuserProps {
possibleUIRoles: null | Record<string, Record<string, string>>; possibleUIRoles: null | Record<string, Record<string, string>>;
} }
// Define an interface for the UI settings
interface UISettings {
PROXY_BASE_URL: string | null;
PROXY_LOGOUT_URL: string | null;
DEFAULT_TEAM_DISABLED: boolean;
SSO_ENABLED: boolean;
}
const Createuser: React.FC<CreateuserProps> = ({ const Createuser: React.FC<CreateuserProps> = ({
userID, userID,
accessToken, accessToken,
teams, teams,
possibleUIRoles, possibleUIRoles,
}) => { }) => {
const [uiSettings, setUISettings] = useState<UISettings | null>(null);
const [form] = Form.useForm(); const [form] = Form.useForm();
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const [apiuser, setApiuser] = useState<string | null>(null); const [apiuser, setApiuser] = useState<string | null>(null);
@ -70,11 +80,19 @@ const Createuser: React.FC<CreateuserProps> = ({
// Assuming modelDataResponse.data contains an array of model names // Assuming modelDataResponse.data contains an array of model names
setUserModels(availableModels); setUserModels(availableModels);
// get ui settings
const uiSettingsResponse = await getProxyBaseUrlAndLogoutUrl(accessToken);
console.log("uiSettingsResponse:", uiSettingsResponse);
setUISettings(uiSettingsResponse);
} catch (error) { } catch (error) {
console.error("Error fetching model data:", error); console.error("Error fetching model data:", error);
} }
}; };
fetchData(); // Call the function to fetch model data when the component mounts fetchData(); // Call the function to fetch model data when the component mounts
}, []); // Empty dependency array to run only once }, []); // Empty dependency array to run only once
@ -105,10 +123,33 @@ const Createuser: React.FC<CreateuserProps> = ({
console.log("user create Response:", response); console.log("user create Response:", response);
setApiuser(response["key"]); setApiuser(response["key"]);
const user_id = response.data?.user_id || response.user_id; const user_id = response.data?.user_id || response.user_id;
invitationCreateCall(accessToken, user_id).then((data) => {
setInvitationLinkData(data); // only do invite link flow if sso is not enabled
if (!uiSettings?.SSO_ENABLED) {
invitationCreateCall(accessToken, user_id).then((data) => {
data.has_user_setup_sso = false;
setInvitationLinkData(data);
setIsInvitationLinkModalVisible(true);
});
} else {
// create an InvitationLink Object for this user for the SSO flow
// for SSO the invite link is the proxy base url since the User just needs to login
const invitationLink: InvitationLink = {
id: crypto.randomUUID(), // Generate a unique ID
user_id: user_id,
is_accepted: false,
accepted_at: null,
expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // Set expiry to 7 days from now
created_at: new Date(),
created_by: userID, // Assuming userID is the current user creating the invitation
updated_at: new Date(),
updated_by: userID,
has_user_setup_sso: true,
};
setInvitationLinkData(invitationLink);
setIsInvitationLinkModalVisible(true); setIsInvitationLinkModalVisible(true);
}); }
message.success("API user Created"); message.success("API user Created");
form.resetFields(); form.resetFields();
localStorage.removeItem("userData" + userID); localStorage.removeItem("userData" + userID);

View file

@ -21,6 +21,7 @@ export interface InvitationLink {
created_by: string; created_by: string;
updated_at: Date; updated_at: Date;
updated_by: string; updated_by: string;
has_user_setup_sso: boolean;
} }
interface OnboardingProps { interface OnboardingProps {
@ -47,6 +48,13 @@ const OnboardingModal: React.FC<OnboardingProps> = ({
setIsInvitationLinkModalVisible(false); setIsInvitationLinkModalVisible(false);
}; };
const getInvitationUrl = () => {
if (invitationLinkData?.has_user_setup_sso) {
return `${baseUrl}/ui`;
}
return `${baseUrl}/ui?invitation_id=${invitationLinkData?.id}`;
};
return ( return (
<Modal <Modal
title="Invitation Link" title="Invitation Link"
@ -67,13 +75,13 @@ const OnboardingModal: React.FC<OnboardingProps> = ({
<div className="flex justify-between pt-5 pb-2"> <div className="flex justify-between pt-5 pb-2">
<Text>Invitation Link</Text> <Text>Invitation Link</Text>
<Text> <Text>
{baseUrl}/ui?invitation_id={invitationLinkData?.id} <Text>{getInvitationUrl()}</Text>
</Text> </Text>
</div> </div>
<div className="flex justify-end mt-5"> <div className="flex justify-end mt-5">
<div></div> <div></div>
<CopyToClipboard <CopyToClipboard
text={`${baseUrl}/ui?invitation_id=${invitationLinkData?.id}`} text={getInvitationUrl()}
onCopy={() => message.success("Copied!")} onCopy={() => message.success("Copied!")}
> >
<Button variant="primary">Copy invitation link</Button> <Button variant="primary">Copy invitation link</Button>

View file

@ -32,6 +32,7 @@ export interface ProxySettings {
PROXY_BASE_URL: string | null; PROXY_BASE_URL: string | null;
PROXY_LOGOUT_URL: string | null; PROXY_LOGOUT_URL: string | null;
DEFAULT_TEAM_DISABLED: boolean; DEFAULT_TEAM_DISABLED: boolean;
SSO_ENABLED: boolean;
} }
function getCookie(name: string) { function getCookie(name: string) {