mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-16 17:55:26 -04:00
fix: renaming a user leaves their API tokens resolving to the old owner (#1932)
* fix: renaming a user leaves their API tokens resolving to the old owner * Drive rename token-cache test through the real auth resolver instead of patching a closure
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
"""Renaming a user must invalidate the bearer-token cache.
|
||||
|
||||
rename_user updates ApiToken.owner (and every other owner-scoped row) in the
|
||||
DB, but the bearer-token cache in app.py still maps each token to the OLD
|
||||
owner. Without invalidating it, the renamed user's API tokens keep resolving
|
||||
to the old (now non-existent) owner and can no longer reach their data until
|
||||
the cache happens to refresh. The route must invalidate the cache, like the
|
||||
token CRUD routes do.
|
||||
"""
|
||||
import sys
|
||||
import types
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def _route(router, name):
|
||||
for r in router.routes:
|
||||
if getattr(getattr(r, "endpoint", None), "__name__", "") == name:
|
||||
return r.endpoint
|
||||
raise AssertionError(name)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def rename_endpoint(monkeypatch):
|
||||
import routes.auth_routes as ar
|
||||
import core.database as cdb
|
||||
|
||||
# Neutralize the DB owner-rename loop (no real DB needed for this test).
|
||||
monkeypatch.setattr(cdb, "SessionLocal", lambda: MagicMock())
|
||||
monkeypatch.setattr(cdb, "Base", SimpleNamespace(registry=SimpleNamespace(mappers=[])), raising=False)
|
||||
# Neutralize the JSON-prefs rename.
|
||||
pr = types.ModuleType("routes.prefs_routes")
|
||||
pr._load = lambda: {}
|
||||
pr._save = lambda d: None
|
||||
monkeypatch.setitem(sys.modules, "routes.prefs_routes", pr)
|
||||
|
||||
am = MagicMock()
|
||||
am.is_admin.return_value = True
|
||||
# The real _get_current_user closure resolves the admin via the auth
|
||||
# manager (a module-level monkeypatch can't intercept a closure), so drive
|
||||
# it through the manager instead.
|
||||
am.get_username_for_token.return_value = "admin"
|
||||
am.users = {"alice": {}}
|
||||
am.rename_user.return_value = True
|
||||
return _route(ar.setup_auth_routes(am), "rename_user"), am
|
||||
|
||||
|
||||
def _request(invalidator):
|
||||
return SimpleNamespace(
|
||||
cookies={"odysseus_session": "t"},
|
||||
app=SimpleNamespace(state=SimpleNamespace(invalidate_token_cache=invalidator)),
|
||||
state=SimpleNamespace(current_user="admin"),
|
||||
)
|
||||
|
||||
|
||||
def test_rename_invalidates_token_cache(rename_endpoint):
|
||||
import asyncio
|
||||
endpoint, _am = rename_endpoint
|
||||
called = {"n": 0}
|
||||
req = _request(lambda: called.__setitem__("n", called["n"] + 1))
|
||||
res = asyncio.run(endpoint("alice", SimpleNamespace(username="alice2"), req))
|
||||
assert res["ok"] is True and res["username"] == "alice2"
|
||||
assert called["n"] == 1, "bearer-token cache was not invalidated on rename"
|
||||
|
||||
|
||||
def test_no_invalidator_does_not_crash(rename_endpoint):
|
||||
import asyncio
|
||||
endpoint, _am = rename_endpoint
|
||||
# app.state without the hook (older wiring) must not break rename.
|
||||
req = SimpleNamespace(cookies={"odysseus_session": "t"},
|
||||
app=SimpleNamespace(state=SimpleNamespace()),
|
||||
state=SimpleNamespace(current_user="admin"))
|
||||
res = asyncio.run(endpoint("alice", SimpleNamespace(username="alice2"), req))
|
||||
assert res["ok"] is True
|
||||
Reference in New Issue
Block a user