Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/common_utils/resource_ownership.py: 11%

47 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:01 +0000

1from typing import Final 

2 

3from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth 

4 

5 

6def is_proxy_admin(user_api_key_dict: UserAPIKeyAuth | None) -> bool: 

7 if user_api_key_dict is None: 7 ↛ 8line 7 didn't jump to line 8 because the condition on line 7 was never true

8 return False 

9 

10 return ( 

11 user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN 

12 or user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value 

13 ) 

14 

15 

16def get_resource_owner_scopes( 

17 user_api_key_dict: UserAPIKeyAuth | None, 

18) -> list[str]: 

19 """ 

20 Return ownership scopes that may access a user-created proxy resource. 

21 

22 Raw user_id is included for rows created before scope prefixes existed. 

23 Prefixes avoid collisions when falling back to team/org/key ownership 

24 for keys that do not have a user_id. 

25 

26 Identity-less callers (no user_id, team_id, org_id, api_key, or token) 

27 return ``[]`` — they share no scope with any other caller, so access 

28 checks against an existing owner always fail and creates that depend 

29 on a primary scope must reject up front. Returning a shared sentinel 

30 here would let any two identity-less callers see each other's data. 

31 """ 

32 if user_api_key_dict is None: 

33 return [] 

34 

35 scopes: Final[list[str]] = [] 

36 

37 def _add(scope: str | None) -> None: 

38 if scope and scope not in scopes: 

39 scopes.append(scope) 

40 

41 if user_api_key_dict.user_id: 

42 _add(user_api_key_dict.user_id) 

43 _add(f"user:{user_api_key_dict.user_id}") 

44 if user_api_key_dict.team_id: 

45 _add(f"team:{user_api_key_dict.team_id}") 

46 if user_api_key_dict.org_id: 

47 _add(f"org:{user_api_key_dict.org_id}") 

48 if user_api_key_dict.api_key: 

49 _add(f"key:{user_api_key_dict.api_key}") 

50 if user_api_key_dict.token: 

51 _add(f"key:{user_api_key_dict.token}") 

52 

53 return scopes 

54 

55 

56def get_primary_resource_owner_scope( 

57 user_api_key_dict: UserAPIKeyAuth | None, 

58) -> str | None: 

59 """Return the canonical owner scope to stamp on newly-created rows. 

60 

61 ``None`` for identity-less callers — callers that depend on a primary 

62 scope to record ownership must surface that as a hard error rather 

63 than fall back to a shared sentinel (which would collapse every 

64 identity-less caller into the same logical owner). 

65 """ 

66 if user_api_key_dict is None: 

67 return None 

68 

69 if user_api_key_dict.user_id: 

70 return user_api_key_dict.user_id 

71 if user_api_key_dict.team_id: 

72 return f"team:{user_api_key_dict.team_id}" 

73 if user_api_key_dict.org_id: 

74 return f"org:{user_api_key_dict.org_id}" 

75 if user_api_key_dict.api_key: 

76 return f"key:{user_api_key_dict.api_key}" 

77 if user_api_key_dict.token: 

78 return f"key:{user_api_key_dict.token}" 

79 return None 

80 

81 

82def user_can_access_resource_owner( 

83 owner: str | None, 

84 user_api_key_dict: UserAPIKeyAuth | None, 

85) -> bool: 

86 if user_api_key_dict is None: 

87 return True 

88 if is_proxy_admin(user_api_key_dict): 

89 return True 

90 if owner is None: 

91 return False 

92 return owner in get_resource_owner_scopes(user_api_key_dict)