CI / Build and push docker image (push) Successful in 1m11s
OIDCConfig now asks for the openid and profile scopes only: kaya-oidc's default would also request email, which the platform never stores or exposes. The display-name fallback drops the email claim too (preferred_username, name, then the sub). Tests pin the configured scopes and cover an IdP that still returns an email claim: the claim is ignored and absent from /api/me.
73 lines
2.2 KiB
Python
73 lines
2.2 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, email: Optional[str] = None
|
|
) -> OIDCUser:
|
|
claims = {"sub": sub, "preferred_username": name or sub}
|
|
if email:
|
|
claims["email"] = email
|
|
return OIDCUser(claims)
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def oidc_user(
|
|
sub: str, name: Optional[str] = None, email: Optional[str] = None
|
|
) -> Iterator[OIDCUser]:
|
|
"""Context manager: make ``oidc.get_users`` return this user.
|
|
|
|
``email`` is optional so tests can simulate an IdP that still returns
|
|
the claim: the app requests only the ``openid`` and ``profile`` scopes
|
|
and ignores email where present.
|
|
"""
|
|
user = make_user(sub, name, email)
|
|
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()
|