mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
Introduce a single, typed caller identity that is resolved once at the auth boundary and read by reference downstream, instead of being re-derived from a 50-field key object or rebuilt from request metadata. What this adds (litellm/proxy/auth/resolvers/), organized by responsibility: - Principal: a small, frozen, identity-only value type (user / organization / teams / project / end-user / roles / scopes / network), with its sub-models and the role mapping. No budget or policy state; those stay on the key object. - DbIdentityStore: the auth flow's resolver, owning both halves of resolving a caller. resolve_key does the one combined_view lookup (cache, then DB via the shared lower-level helpers, then write-back) and returns the key object, which still flows for budget / rate-limit / policy unchanged. principal_from_key projects the identity slice of that key object into a Principal, issuing no lookup. user_api_key_auth resolves every key through the store rather than calling get_key_object directly; auth_checks.get_key_object stays as the legacy entrypoint for its other callers until they migrate. - network: the X-Forwarded-For / trusted-proxy CIDR primitives live here in one place. trusted_proxy_utils now imports them rather than keeping a second copy. At the seam, user_api_key_auth projects one per-request Principal off the resolved key object and stamps the request network context onto it once (X-Forwarded-For is trusted only when trusted_proxy_ranges is configured). It is attached to request.state.principal for the downstream consumers later phases add. The projection is additive and defensive: a failure never rejects an already-authenticated request, and a missing principal must be treated as deny by any future reader. The Principal is always identifiable (credential_ref and a stable subject off the token), never anonymous. This is additive and changes no behavior today; it is the identity foundation the spend-attribution and authorization phases build on.
35 lines
1.1 KiB
Python
35 lines
1.1 KiB
Python
from __future__ import annotations
|
|
|
|
from litellm.proxy._types import ProxyErrorTypes, ProxyException
|
|
from litellm.proxy.auth.resolvers.exceptions import (
|
|
IdentityResolutionError,
|
|
KeyNotFoundError,
|
|
KeyNotInCacheError,
|
|
NoDatabaseConnectionError,
|
|
PrincipalMissingSourceKeyError,
|
|
)
|
|
|
|
|
|
def test_all_resolution_errors_share_one_base():
|
|
errors = [
|
|
NoDatabaseConnectionError(),
|
|
KeyNotInCacheError("hashed"),
|
|
KeyNotFoundError("hashed"),
|
|
PrincipalMissingSourceKeyError(),
|
|
]
|
|
assert all(isinstance(e, IdentityResolutionError) for e in errors)
|
|
|
|
|
|
def test_key_not_found_preserves_the_public_401_contract():
|
|
# The auth seam catches ProxyException and rewrites the 401 message, so a
|
|
# missing key must keep mapping to that exact contract.
|
|
error = KeyNotFoundError("hashed-token")
|
|
|
|
assert isinstance(error, ProxyException)
|
|
assert error.code == "401"
|
|
assert error.type == ProxyErrorTypes.token_not_found_in_db.value
|
|
assert error.param == "key"
|
|
|
|
|
|
def test_key_not_in_cache_names_the_token():
|
|
assert "hashed-token" in str(KeyNotInCacheError("hashed-token"))
|