mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
Merge pull request #3952 from BerriAI/litellm_ui_invite_for_spend_viewer
[Feat] Admin UI - invite users to view spend
This commit is contained in:
commit
7bda701b5d
4 changed files with 54 additions and 14 deletions
|
|
@ -97,6 +97,7 @@ from litellm.proxy.utils import (
|
|||
_read_request_body,
|
||||
_is_valid_team_configs,
|
||||
_is_user_proxy_admin,
|
||||
_get_user_role,
|
||||
_is_projected_spend_over_limit,
|
||||
_get_projected_spend_over_limit,
|
||||
update_spend,
|
||||
|
|
@ -1108,7 +1109,7 @@ async def user_api_key_auth(
|
|||
|
||||
user_email: Optional[str] = None
|
||||
# Check if the token has any user id information
|
||||
if user_id_information is not None:
|
||||
if user_id_information is not None and len(user_id_information) > 0:
|
||||
specific_user_id_information = user_id_information[0]
|
||||
_user_email = specific_user_id_information.get("user_email", None)
|
||||
if _user_email is not None:
|
||||
|
|
@ -1273,6 +1274,8 @@ async def user_api_key_auth(
|
|||
if _end_user_object is not None:
|
||||
valid_token_dict.update(end_user_params)
|
||||
|
||||
_user_role = _get_user_role(user_id_information=user_id_information)
|
||||
|
||||
if not _is_user_proxy_admin(user_id_information): # if non-admin
|
||||
if route in LiteLLMRoutes.openai_routes.value:
|
||||
pass
|
||||
|
|
@ -1326,11 +1329,37 @@ async def user_api_key_auth(
|
|||
):
|
||||
|
||||
pass
|
||||
elif _user_role == LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY:
|
||||
if route in LiteLLMRoutes.openai_routes.value:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"user not allowed to access this OpenAI routes, role= {_user_role}",
|
||||
)
|
||||
if route in LiteLLMRoutes.management_routes.value:
|
||||
# the Admin Viewer is only allowed to call /user/update for their own user_id and can only update
|
||||
if route == "/user/update":
|
||||
|
||||
# Check the Request params are valid for PROXY_ADMIN_VIEW_ONLY
|
||||
if request_data is not None and isinstance(
|
||||
request_data, dict
|
||||
):
|
||||
_params_updated = request_data.keys()
|
||||
for param in _params_updated:
|
||||
if param not in ["user_email", "password"]:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"user not allowed to access this route, role= {_user_role}. Trying to access: {route} and updating invalid param: {param}. only user_email and password can be updated",
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=f"user not allowed to access this route, role= {_user_role}. Trying to access: {route}",
|
||||
)
|
||||
else:
|
||||
user_role = "unknown"
|
||||
user_id = "unknown"
|
||||
if user_id_information is not None and isinstance(
|
||||
user_id_information, list
|
||||
user_id_information, list and len(user_id_information) > 0
|
||||
):
|
||||
_user = user_id_information[0]
|
||||
user_role = _user.get("user_role", "unknown")
|
||||
|
|
@ -1365,6 +1394,7 @@ async def user_api_key_auth(
|
|||
"/global/spend/keys",
|
||||
"/global/spend/models",
|
||||
"/global/predict/spend/logs",
|
||||
"/global/activity",
|
||||
"/health/services",
|
||||
]
|
||||
# check if the current route startswith any of the allowed routes
|
||||
|
|
@ -11869,8 +11899,7 @@ async def onboarding(invite_link: str):
|
|||
|
||||
response = await generate_key_helper_fn(
|
||||
**{
|
||||
"user_role": LitellmUserRoles.PROXY_ADMIN,
|
||||
request_type="key",
|
||||
"user_role": user_obj.user_role,
|
||||
"duration": "2hr",
|
||||
"key_max_budget": 5,
|
||||
"models": [],
|
||||
|
|
@ -11895,7 +11924,7 @@ async def onboarding(invite_link: str):
|
|||
"user_id": user_obj.user_id,
|
||||
"key": key,
|
||||
"user_email": user_obj.user_email,
|
||||
"user_role": "app_owner",
|
||||
"user_role": user_obj.user_role,
|
||||
"login_method": "username_password",
|
||||
"premium_user": premium_user,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2658,6 +2658,17 @@ def _is_user_proxy_admin(user_id_information: Optional[list]):
|
|||
return False
|
||||
|
||||
|
||||
def _get_user_role(user_id_information: Optional[list]):
|
||||
if user_id_information is None:
|
||||
return None
|
||||
|
||||
if len(user_id_information) == 0 or user_id_information[0] is None:
|
||||
return None
|
||||
|
||||
_user = user_id_information[0]
|
||||
return _user.get("user_role")
|
||||
|
||||
|
||||
def encrypt_value(value: str, master_key: str):
|
||||
import hashlib
|
||||
import nacl.secret
|
||||
|
|
|
|||
|
|
@ -338,6 +338,15 @@ const AdminPanel: React.FC<AdminPanelProps> = ({
|
|||
);
|
||||
console.log(`response for team create call: ${response}`);
|
||||
// Checking if the team exists in the list and updating or adding accordingly
|
||||
|
||||
// Give admin an invite link for inviting user to proxy
|
||||
const user_id = response.data?.user_id || response.user_id;
|
||||
invitationCreateCall(accessToken, user_id).then((data) => {
|
||||
setInvitationLinkData(data);
|
||||
setIsInvitationLinkModalVisible(true);
|
||||
});
|
||||
|
||||
|
||||
const foundIndex = admins.findIndex((user) => {
|
||||
console.log(
|
||||
`user.user_id=${user.user_id}; response.user_id=${response.user_id}`
|
||||
|
|
|
|||
|
|
@ -28,15 +28,6 @@ const Sidebar: React.FC<SidebarProps> = ({
|
|||
}
|
||||
style={{ height: "100%", borderRight: 0 }}
|
||||
>
|
||||
<Menu.Item key="4" onClick={() => setPage("api-keys")}>
|
||||
API Keys
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2" onClick={() => setPage("models")}>
|
||||
Models
|
||||
</Menu.Item>
|
||||
<Menu.Item key="3" onClick={() => setPage("llm-playground")}>
|
||||
Chat UI
|
||||
</Menu.Item>
|
||||
<Menu.Item key="1" onClick={() => setPage("usage")}>
|
||||
Usage
|
||||
</Menu.Item>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue