mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-15 23:31:29 +00:00
test(auth_v2): repoint to frozen sub-package layout and pin H1 group provisioning
Follow the sub-package split (oidc/saml/scim/*) and the token-claim hardening: - import config models (OIDCProviderConfig, SAMLConfig) from the top-level package and the moved helpers from their concrete sub-modules (saml.router, saml.config, oidc.router) so tests are stable against __init__ re-export churn - resolver: a token group claim is no longer authoritative on its own; it becomes a TeamIdentity only when it resolves to a provisioned SCIM Group in the store (split into provisioned vs not-provisioned cases) Full auth_v2 suite green (153) and stable across repeated runs.
This commit is contained in:
parent
a9be3d23e0
commit
341e75ab45
7 changed files with 38 additions and 19 deletions
|
|
@ -7,7 +7,7 @@ from cryptography.hazmat.primitives import serialization
|
|||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
|
||||
from litellm.proxy.auth_v2.authenticators import JWTVerifier
|
||||
from litellm.proxy.auth_v2.config import OIDCProviderConfig
|
||||
from litellm.proxy.auth_v2 import OIDCProviderConfig
|
||||
|
||||
from auth_v2_helpers import TEST_AUDIENCE, TEST_ISSUER, FakeJwksClient, TokenFactory
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ from litellm.proxy.auth_v2.config import (
|
|||
HttpBasicConfig,
|
||||
MutualTLSConfig,
|
||||
OAuth2IntrospectionConfig,
|
||||
OIDCProviderConfig,
|
||||
TrustedProxyConfig,
|
||||
)
|
||||
from litellm.proxy.auth_v2 import OIDCProviderConfig
|
||||
from litellm.proxy.auth_v2.errors import AuthError
|
||||
from litellm.proxy.auth_v2.models import AuthMethod
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ from pydantic import ValidationError
|
|||
|
||||
from litellm.proxy.auth_v2.config import (
|
||||
OAuth2IntrospectionConfig,
|
||||
OIDCProviderConfig,
|
||||
SAMLConfig,
|
||||
)
|
||||
from litellm.proxy.auth_v2 import OIDCProviderConfig, SAMLConfig
|
||||
|
||||
|
||||
def test_saml_config_requires_idp_metadata_when_enabled():
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from litellm.proxy.auth_v2.config import OIDCProviderConfig
|
||||
from litellm.proxy.auth_v2.oidc import _provider_key, _user_from_userinfo
|
||||
from litellm.proxy.auth_v2 import OIDCProviderConfig
|
||||
from litellm.proxy.auth_v2.oidc.router import _provider_key, _user_from_userinfo
|
||||
from litellm.proxy.auth_v2.resolver import InMemoryIdentityStore
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from scim2_models import Group as ScimGroup
|
||||
|
||||
from litellm.proxy.auth_v2.errors import AuthError
|
||||
from litellm.proxy.auth_v2.models import (
|
||||
|
|
@ -80,9 +81,8 @@ async def test_subject_lookup_prefers_stored_principal():
|
|||
assert resolved.subject == "from-store"
|
||||
|
||||
|
||||
async def test_self_describing_token_builds_principal_from_claims():
|
||||
store = InMemoryIdentityStore()
|
||||
credential = Credential(
|
||||
def _oidc_credential(**claims) -> Credential:
|
||||
return Credential(
|
||||
scheme=SecuritySchemeType.OPENID_CONNECT,
|
||||
method=AuthMethod.OIDC,
|
||||
subject="sub-42",
|
||||
|
|
@ -92,19 +92,39 @@ async def test_self_describing_token_builds_principal_from_claims():
|
|||
"email": "dana@example.com",
|
||||
"preferred_username": "dana",
|
||||
"name": "Dana D",
|
||||
"groups": ["eng", "oncall"],
|
||||
"roles": ["org_admin", "bogus_role"],
|
||||
**claims,
|
||||
},
|
||||
)
|
||||
principal = await store.resolve(credential)
|
||||
|
||||
|
||||
async def test_self_describing_token_builds_principal_from_claims():
|
||||
store = InMemoryIdentityStore()
|
||||
principal = await store.resolve(_oidc_credential(roles=["org_admin", "bogus_role"]))
|
||||
assert principal.user.email == "dana@example.com"
|
||||
assert principal.user.user_name == "dana"
|
||||
assert [team.id for team in principal.teams] == ["eng", "oncall"]
|
||||
# invalid role strings are filtered out, valid ones become Role enums
|
||||
assert principal.roles == [Role.ORG_ADMIN]
|
||||
assert principal.scopes == ["models:read"]
|
||||
|
||||
|
||||
async def test_group_claim_without_provisioned_scim_group_is_not_a_team():
|
||||
# H1: a token group claim is not authoritative on its own
|
||||
store = InMemoryIdentityStore()
|
||||
principal = await store.resolve(_oidc_credential(groups=["eng", "oncall"]))
|
||||
assert principal.teams == []
|
||||
|
||||
|
||||
async def test_group_claim_becomes_team_only_when_provisioned():
|
||||
store = InMemoryIdentityStore(
|
||||
groups={"eng": ScimGroup(id="eng", display_name="Engineering")}
|
||||
)
|
||||
principal = await store.resolve(_oidc_credential(groups=["eng", "unprovisioned"]))
|
||||
# only the provisioned group resolves to a team; the unknown one is dropped
|
||||
assert len(principal.teams) == 1
|
||||
assert principal.teams[0].id == "eng"
|
||||
assert principal.teams[0].name == "Engineering"
|
||||
|
||||
|
||||
async def test_mtls_credential_resolves_to_service_account():
|
||||
store = InMemoryIdentityStore()
|
||||
credential = Credential(
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ def saml_env(tmp_path: Path) -> SamlEnv:
|
|||
from saml2.saml import NAMEID_FORMAT_EMAILADDRESS
|
||||
from saml2.server import Server
|
||||
|
||||
from litellm.proxy.auth_v2.config import SAMLConfig
|
||||
from litellm.proxy.auth_v2 import SAMLConfig
|
||||
|
||||
idp_key, idp_cert = _gen_cert(tmp_path, "idp")
|
||||
sp_key, sp_cert = _gen_cert(tmp_path, "sp")
|
||||
|
|
@ -355,8 +355,8 @@ def test_login_rejects_open_redirect_next(saml_env):
|
|||
|
||||
|
||||
def test_map_attributes_applies_attribute_map():
|
||||
from litellm.proxy.auth_v2.config import DEFAULT_SAML_ATTRIBUTE_MAP
|
||||
from litellm.proxy.auth_v2.saml import _map_attributes
|
||||
from litellm.proxy.auth_v2.saml.config import DEFAULT_SAML_ATTRIBUTE_MAP
|
||||
from litellm.proxy.auth_v2.saml.router import _map_attributes
|
||||
|
||||
ava = {
|
||||
"email": ["alice@example.com"],
|
||||
|
|
@ -372,7 +372,7 @@ def test_map_attributes_applies_attribute_map():
|
|||
|
||||
|
||||
def test_user_from_mapped_builds_name_and_email():
|
||||
from litellm.proxy.auth_v2.saml import _user_from_mapped
|
||||
from litellm.proxy.auth_v2.saml.router import _user_from_mapped
|
||||
|
||||
user = _user_from_mapped(
|
||||
"alice@example.com",
|
||||
|
|
@ -413,7 +413,7 @@ def test_safe_relay_state_blocks_open_redirects(candidate, expected):
|
|||
],
|
||||
)
|
||||
def test_metadata_source_classifies_input(metadata, expected_key):
|
||||
from litellm.proxy.auth_v2.saml import _metadata_source
|
||||
from litellm.proxy.auth_v2.saml.router import _metadata_source
|
||||
|
||||
assert expected_key in _metadata_source(metadata)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ from litellm.proxy.auth_v2.config import (
|
|||
ApiKeySchemeConfig,
|
||||
AuthConfig,
|
||||
HttpBasicConfig,
|
||||
OIDCProviderConfig,
|
||||
)
|
||||
from litellm.proxy.auth_v2 import OIDCProviderConfig
|
||||
from litellm.proxy.auth_v2.models import AuthMethod, Principal, PrincipalType
|
||||
from litellm.proxy.auth_v2.rbac import RBACEngine, Role
|
||||
from litellm.proxy.auth_v2.resolver import InMemoryIdentityStore, _hash_api_key
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue