diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py
index 45c2abdf02..a13e730029 100644
--- a/litellm/proxy/proxy_server.py
+++ b/litellm/proxy/proxy_server.py
@@ -7791,7 +7791,6 @@ async def user_auth(request: Request):
params = {
"sender_name": "LiteLLM Proxy",
- "sender_email": os.getenv("SMTP_SENDER_EMAIL"),
"receiver_email": user_email,
"subject": "Your Magic Link",
"html": f" Follow this link, to login:\n\n{base_url}user/?token={response['token']}&user_id={response['user_id']}&page={page_params}",
@@ -7801,6 +7800,42 @@ async def user_auth(request: Request):
return "Email sent!"
+@router.get(
+ "/user/available_roles",
+ tags=["Internal User management"],
+ include_in_schema=False,
+ dependencies=[Depends(user_api_key_auth)],
+)
+async def ui_get_available_role(
+ user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
+):
+ """
+ Endpoint used by Admin UI to show all available roles to assign a user
+ return {
+ "proxy_admin": {
+ "description": "Proxy Admin role",
+ "ui_label": "Admin"
+ }
+ }
+ """
+
+ _data_to_return = {}
+ for role in LitellmUserRoles:
+
+ # We only show a subset of roles on UI
+ if role in [
+ LitellmUserRoles.PROXY_ADMIN,
+ LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY,
+ LitellmUserRoles.INTERNAL_USER,
+ LitellmUserRoles.INTERNAL_USER_VIEW_ONLY,
+ ]:
+ _data_to_return[role.value] = {
+ "description": role.description,
+ "ui_label": role.ui_label,
+ }
+ return _data_to_return
+
+
@router.get(
"/user/info",
tags=["Internal User management"],
diff --git a/ui/litellm-dashboard/src/components/edit_user.tsx b/ui/litellm-dashboard/src/components/edit_user.tsx
index 850793d01b..0d5d14e64d 100644
--- a/ui/litellm-dashboard/src/components/edit_user.tsx
+++ b/ui/litellm-dashboard/src/components/edit_user.tsx
@@ -23,12 +23,13 @@ import {
interface EditUserModalProps {
visible: boolean;
+ possibleUIRoles: null | Record>;
onCancel: () => void;
user: any;
onSubmit: (data: any) => void;
}
-const EditUserModal: React.FC = ({ visible, onCancel, user, onSubmit }) => {
+const EditUserModal: React.FC = ({ visible, possibleUIRoles, onCancel, user, onSubmit }) => {
const [editedUser, setEditedUser] = useState(user);
const [form] = Form.useForm();
@@ -43,9 +44,9 @@ const EditUserModal: React.FC = ({ visible, onCancel, user,
const handleEditSubmit = async (formValues: Record) => {
// Call API to update team with teamId and values
- form.resetFields();
-
onSubmit(formValues);
+
+ form.resetFields();
onCancel();
};
@@ -94,8 +95,14 @@ const EditUserModal: React.FC = ({ visible, onCancel, user,
name="user_role"
>
- Proxy Admin (Can create, edit, delete keys, teams)
- Proxy Viewer (Can just view spend, cannot created keys, teams)
+ {possibleUIRoles &&
+ Object.entries(possibleUIRoles).map(([role, { ui_label, description }]) => (
+
+
+ {ui_label}
{description}
+
+
+ ))}
diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx
index 523c951b59..5299c0392a 100644
--- a/ui/litellm-dashboard/src/components/networking.tsx
+++ b/ui/litellm-dashboard/src/components/networking.tsx
@@ -1390,6 +1390,34 @@ export const userGetAllUsersCall = async (
}
};
+export const getPossibleUserRoles = async (
+ accessToken: String,
+) => {
+ try {
+ const url = proxyBaseUrl
+ ? `${proxyBaseUrl}/user/available_roles`
+ : `/user/available_roles`;
+ const response = await fetch(url, {
+ method: "GET",
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ "Content-Type": "application/json",
+ },
+ });
+
+ if (!response.ok) {
+ const errorData = await response.text();
+ throw new Error("Network response was not ok");
+ }
+ const data = await response.json();
+ console.log("response from user/available_role", data);
+ return data;
+ // Handle success - you might want to update some state or UI based on the created key
+ } catch (error) {
+ throw error;
+ }
+};
+
export const teamCreateCall = async (
accessToken: string,
formValues: Record // Assuming formValues is an object
diff --git a/ui/litellm-dashboard/src/components/view_users.tsx b/ui/litellm-dashboard/src/components/view_users.tsx
index 435b13c15f..3f310c3eb2 100644
--- a/ui/litellm-dashboard/src/components/view_users.tsx
+++ b/ui/litellm-dashboard/src/components/view_users.tsx
@@ -24,7 +24,7 @@ import {
Icon,
TextInput,
} from "@tremor/react";
-import { userInfoCall, userUpdateUserCall } from "./networking";
+import { userInfoCall, userUpdateUserCall, getPossibleUserRoles } from "./networking";
import { Badge, BadgeDelta, Button } from "@tremor/react";
import RequestAccess from "./request_model_access";
import CreateUser from "./create_user_button";
@@ -62,6 +62,7 @@ const ViewUserDashboard: React.FC = ({
const [selectedItem, setSelectedItem] = useState(null);
const [editModalVisible, setEditModalVisible] = useState(false);
const [selectedUser, setSelectedUser] = useState(null);
+ const [possibleUIRoles, setPossibleUIRoles] = useState>>({});
const defaultPageSize = 25;
const handleEditCancel = async () => {
@@ -76,7 +77,7 @@ const ViewUserDashboard: React.FC = ({
return;
}
- userUpdateUserCall(accessToken, editedUser, userRole);
+ userUpdateUserCall(accessToken, editedUser, null);
if (userData) {
const updatedUserData = userData.map((user) =>
@@ -106,11 +107,16 @@ const ViewUserDashboard: React.FC = ({
);
console.log("user data response:", userDataResponse);
setUserData(userDataResponse);
+
+ const availableUserRoles = await getPossibleUserRoles(accessToken);
+ setPossibleUIRoles(availableUserRoles);
+
} catch (error) {
console.error("There was an error fetching the model data", error);
}
};
+
if (accessToken && token && userRole && userID) {
fetchData();
}
@@ -273,6 +279,7 @@ const ViewUserDashboard: React.FC = ({