CI / Build and push docker image (push) Successful in 2m27s
kaya-oidc 0.0.6 can run several providers in one app (OIDCProviders), each with its own login route and namespaced session state. Adopt it: providers are configured in indexed, contiguous OIDC_<n>_* blocks (0-based) with OIDC_<n>_PROVIDER naming the provider in its login URL and session keys. The old unindexed OIDC_ISSUER/OIDC_CLIENT_ID/... names are no longer read; startup fails with a rename hint when they are set. With no OIDC variables at all, the single dev mock provider named "default" is used, as before. The platform now authenticates through OIDCProviders: auth.current_user returns the session's user (sessions stay single-identity, so at most one provider is logged in) and require_auth/route handlers use it, while the websocket path keeps scanning for any provider's user. New routes expose the configuration to the SPA and keep old links working: GET /api/auth/providers lists the login URLs, GET /auth/login redirects to the first provider and GET /auth/logout to whichever provider the session is logged in with. The lobby renders one login button per provider from that endpoint. Update the env examples, compose and k8s manifests to the indexed scheme, document the multiple-provider setup and the migration, and cover it with config, auth and route tests.
61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
"""Helpers for faking the OIDC authenticated user during tests.
|
|
|
|
HTTP handlers funnel through ``OIDCProviders.get_users`` (wrapped by
|
|
``tavolo.platform.auth.current_user``); websocket handlers through
|
|
:func:`tavolo.platform.auth.get_ws_user`. Patching those two entry points
|
|
lets route and websocket tests run entirely in-process with no IdP.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import unittest.mock as _mock
|
|
from typing import Iterator, Optional, Sequence
|
|
|
|
from kaya.oidc import OIDCUser
|
|
|
|
# Import the app first: it pulls in the route modules, which import
|
|
# ``tavolo.platform.auth`` themselves.
|
|
from tavolo.app import oidc
|
|
from tavolo.platform import auth
|
|
|
|
# The app's first configured provider (the test environment configures one
|
|
# named "default").
|
|
_PROVIDER = next(iter(oidc.providers))
|
|
|
|
|
|
def make_user(sub: str, name: Optional[str] = None) -> OIDCUser:
|
|
return OIDCUser({"sub": sub, "preferred_username": name or sub})
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def oidc_user(sub: str, name: Optional[str] = None) -> Iterator[OIDCUser]:
|
|
"""Context manager: make ``oidc.get_users`` return this user."""
|
|
user = make_user(sub, name)
|
|
patcher = _mock.patch.object(oidc, "get_users", return_value={_PROVIDER: user})
|
|
patcher.start()
|
|
try:
|
|
yield user
|
|
finally:
|
|
patcher.stop()
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def ws_users(users: Sequence[OIDCUser]) -> Iterator[None]:
|
|
"""Context manager: patch ``auth.get_ws_user`` to hand out ``users``
|
|
one per websocket connection, in order. Once exhausted it keeps
|
|
returning the last user."""
|
|
remaining = list(users)
|
|
last = remaining[-1] if remaining else None
|
|
|
|
def _next(_ws):
|
|
if remaining:
|
|
return remaining.pop(0)
|
|
return last
|
|
|
|
patcher = _mock.patch.object(auth, "get_ws_user", side_effect=_next)
|
|
patcher.start()
|
|
try:
|
|
yield
|
|
finally:
|
|
patcher.stop()
|