diff --git a/src/auth_helpers.py b/src/auth_helpers.py index e9265f4..6206039 100644 --- a/src/auth_helpers.py +++ b/src/auth_helpers.py @@ -107,6 +107,8 @@ def require_privilege(request: Request, key: str) -> str: privs = auth_mgr.get_privileges(user) or {} except Exception: return user + if not isinstance(privs, dict): + privs = {} # True = permitted; missing key defaults to permitted (unknown privileges # fail open — the UI gates display-side). if not privs.get(key, True): diff --git a/tests/test_auth_require_privilege_nondict.py b/tests/test_auth_require_privilege_nondict.py new file mode 100644 index 0000000..e86ff35 --- /dev/null +++ b/tests/test_auth_require_privilege_nondict.py @@ -0,0 +1,36 @@ +import types + +import pytest + +from src import auth_helpers +from src.auth_helpers import require_privilege + + +class _Mgr: + def __init__(self, privs): + self._privs = privs + + def get_privileges(self, user): + return self._privs + + +def _request(mgr): + state = types.SimpleNamespace(auth_manager=mgr) + return types.SimpleNamespace(app=types.SimpleNamespace(state=state)) + + +def test_require_privilege_tolerates_non_dict_privileges(monkeypatch): + # A corrupt auth.json can make get_privileges return a non-dict (e.g. a + # list). The privs.get(...) call sits outside the try, so the old code + # raised AttributeError and turned a privilege check into a 500. It should + # fall back to the documented fail-open behaviour. + monkeypatch.setattr(auth_helpers, "require_user", lambda request: "bob") + req = _request(_Mgr(["do_x"])) + assert require_privilege(req, "do_x") == "bob" + + +def test_require_privilege_still_blocks_disallowed(monkeypatch): + monkeypatch.setattr(auth_helpers, "require_user", lambda request: "bob") + req = _request(_Mgr({"do_x": False})) + with pytest.raises(Exception): + require_privilege(req, "do_x")