mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-25 02:34:29 +00:00
* (UI) - Improvements to session handling logic (#8970)
* add cookieUtils
* use utils for clearing cookies
* on logout use clearTokenCookies
* ui use correct clearTokenCookies
* navbar show userEmail on UserID page
* add timestamp on token cookie
* update generate_authenticated_redirect_response
* use common getAuthToken
* fix clearTokenCookies
* fixes for get auth token
* fix invitation link sign in logic
* Revert "fix invitation link sign in logic"
This reverts commit 30e5308cb3
.
* fix getAuthToken
* update setAuthToken
* fix ui session handling
* fix ui session handler
* bug fix stop generating LiteLLM Virtual keys for access
* working JWT insert into cookies
* use central place to build UI JWT token
* add _validate_ui_token
* fix ui session handler
* fix fetchWithCredentials
* check allowed routes for ui session tokens
* expose validate_session endpoint
* validate session endpoint
* call sso/session/validate
* getUISessionDetails
* ui move to getUISessionDetails
* /sso/session/validate
* fix cookie utils
* use getUISessionDetails
* use ui_session_id
* "/spend/logs/ui" in spend_tracking_routes
* working sign in JWT flow for proxy admin
* allow proxy admin to access ui routes
* use check_route_access
* update types
* update login method
* fixes to ui session handler
* working flow for admin and internal users
* fixes for invite links
* use JWTs for SSO sign in
* fix /invitation/new flow
* fix code quality checks
* fix _get_ui_session_token_from_cookies
* /organization/list
* ui sso sign in
* TestUISessionHandler
* TestUISessionHandler
124 lines
4.5 KiB
Python
124 lines
4.5 KiB
Python
import pytest
|
|
import time
|
|
import jwt
|
|
from datetime import datetime, timezone
|
|
from fastapi.requests import Request
|
|
from fastapi.responses import RedirectResponse
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from litellm.proxy.management_helpers.ui_session_handler import UISessionHandler
|
|
from litellm.proxy._types import LitellmUserRoles
|
|
|
|
|
|
class TestUISessionHandler:
|
|
|
|
def test_get_latest_ui_cookie_name(self):
|
|
# Test with multiple cookies
|
|
cookies = {
|
|
"litellm_ui_token_1000": "value1",
|
|
"litellm_ui_token_2000": "value2",
|
|
"other_cookie": "other_value",
|
|
}
|
|
|
|
result = UISessionHandler._get_latest_ui_cookie_name(cookies)
|
|
assert result == "litellm_ui_token_2000"
|
|
|
|
# Test with no matching cookies
|
|
cookies = {"other_cookie": "value"}
|
|
result = UISessionHandler._get_latest_ui_cookie_name(cookies)
|
|
assert result is None
|
|
|
|
def test_get_ui_session_token_from_cookies(self):
|
|
# Create mock request with cookies
|
|
mock_request = MagicMock()
|
|
mock_request.cookies = {
|
|
"litellm_ui_token_1000": "test_token",
|
|
"other_cookie": "other_value",
|
|
}
|
|
|
|
result = UISessionHandler._get_ui_session_token_from_cookies(mock_request)
|
|
assert result == "test_token"
|
|
|
|
# Test with no matching cookies
|
|
mock_request.cookies = {"other_cookie": "value"}
|
|
result = UISessionHandler._get_ui_session_token_from_cookies(mock_request)
|
|
assert result is None
|
|
|
|
@patch("litellm.proxy.proxy_server.master_key", "test_master_key")
|
|
@patch(
|
|
"litellm.proxy.proxy_server.general_settings",
|
|
{"litellm_key_header_name": "X-API-Key"},
|
|
)
|
|
def test_build_authenticated_ui_jwt_token(self):
|
|
# Test token generation
|
|
token = UISessionHandler.build_authenticated_ui_jwt_token(
|
|
user_id="test_user",
|
|
user_role=LitellmUserRoles.PROXY_ADMIN,
|
|
user_email="test@example.com",
|
|
premium_user=True,
|
|
disabled_non_admin_personal_key_creation=False,
|
|
login_method="username_password",
|
|
)
|
|
|
|
# Decode and verify token
|
|
decoded = jwt.decode(token, "test_master_key", algorithms=["HS256"])
|
|
|
|
assert decoded["user_id"] == "test_user"
|
|
assert decoded["user_email"] == "test@example.com"
|
|
assert decoded["user_role"] == LitellmUserRoles.PROXY_ADMIN
|
|
assert decoded["premium_user"] is True
|
|
assert decoded["login_method"] == "username_password"
|
|
assert decoded["auth_header_name"] == "X-API-Key"
|
|
assert decoded["iss"] == "litellm-proxy"
|
|
assert decoded["aud"] == "litellm-ui"
|
|
assert "exp" in decoded
|
|
assert decoded["disabled_non_admin_personal_key_creation"] is False
|
|
assert decoded["scope"] == ["litellm:admin"]
|
|
|
|
def test_is_ui_session_token(self):
|
|
# Valid UI session token
|
|
token_dict = {
|
|
"iss": "litellm-proxy",
|
|
"aud": "litellm-ui",
|
|
"user_id": "test_user",
|
|
}
|
|
assert UISessionHandler.is_ui_session_token(token_dict) is True
|
|
|
|
# Invalid token (wrong issuer)
|
|
token_dict = {
|
|
"iss": "other-issuer",
|
|
"aud": "litellm-ui",
|
|
}
|
|
assert UISessionHandler.is_ui_session_token(token_dict) is False
|
|
|
|
# Invalid token (wrong audience)
|
|
token_dict = {
|
|
"iss": "litellm-proxy",
|
|
"aud": "other-audience",
|
|
}
|
|
assert UISessionHandler.is_ui_session_token(token_dict) is False
|
|
|
|
def test_generate_authenticated_redirect_response(self):
|
|
redirect_url = "https://example.com/dashboard"
|
|
jwt_token = "test.jwt.token"
|
|
|
|
response = UISessionHandler.generate_authenticated_redirect_response(
|
|
redirect_url=redirect_url, jwt_token=jwt_token
|
|
)
|
|
|
|
assert isinstance(response, RedirectResponse)
|
|
assert response.status_code == 303
|
|
assert response.headers["location"] == redirect_url
|
|
|
|
# Check cookie was set
|
|
cookie_header = response.headers.get("set-cookie", "")
|
|
assert "test.jwt.token" in cookie_header
|
|
assert "Secure" in cookie_header
|
|
assert "HttpOnly" in cookie_header
|
|
assert "SameSite=strict" in cookie_header
|
|
|
|
def test_generate_token_name(self):
|
|
# Mock time.time() to return a fixed value
|
|
with patch("time.time", return_value=1234567890):
|
|
token_name = UISessionHandler._generate_token_name()
|
|
assert token_name == "litellm_ui_token_1234567890"
|