fix(auth): per-user allowed-models checklist ignores cache, [None] doesn't block (#3355)

Three issues combined to make the per-user 'Allowed models' checklist
unreliable (#3032):

1. admin.js _loadModelsForUser fetched /api/models, which is backed by
   cached_models — endpoints that haven't been probed yet (e.g. a
   freshly-added DeepSeek API endpoint) simply didn't show up in the
   checklist. Switched to /api/model-endpoints, which always reflects
   every configured endpoint regardless of cache state.

2. _saveModels sent allowed_models: [] both when the admin clicked
   [All] (no restriction) and [None] (block everything) — the backend
   had no way to distinguish the two.

3. _enforce_chat_privileges treated an empty allowed_models list as
   'no restriction' (falsy -> skip the check), so [None] had no effect.

Added an explicit block_all_models privilege flag (defaulting to False,
and forced to False for admins) that admin.js now sets when zero models
are checked. _enforce_chat_privileges checks it first and 403s
regardless of allowed_models contents.
This commit is contained in:
Lucas Daniel
2026-06-08 17:52:39 -03:00
committed by GitHub
parent 0a324f20d2
commit 5462030cde
4 changed files with 114 additions and 14 deletions
+58
View File
@@ -69,6 +69,64 @@ def test_allowed_models_nonempty_list_still_restricts_without_new_flag(monkeypat
)
def test_no_restriction_allows_any_model(monkeypatch):
monkeypatch.setattr("routes.chat_helpers.get_current_user", lambda request: "alice")
privs = {"allowed_models": [], "block_all_models": False, "max_messages_per_day": 0}
_enforce_chat_privileges(_Request(privs), _Session("provider/model-a"))
_enforce_chat_privileges(_Request(privs), _Session("provider/model-z"))
def test_specific_allowlist_blocks_models_outside_it(monkeypatch):
monkeypatch.setattr("routes.chat_helpers.get_current_user", lambda request: "alice")
privs = {
"allowed_models": ["gpt-4"],
"block_all_models": False,
"max_messages_per_day": 0,
}
_enforce_chat_privileges(_Request(privs), _Session("gpt-4"))
with pytest.raises(HTTPException) as exc:
_enforce_chat_privileges(_Request(privs), _Session("gpt-3.5"))
assert exc.value.status_code == 403
def test_block_all_models_blocks_regardless_of_allowed_models_contents(monkeypatch):
monkeypatch.setattr("routes.chat_helpers.get_current_user", lambda request: "alice")
# Even if allowed_models contains entries, block_all_models wins.
privs = {
"allowed_models": ["gpt-4", "gpt-3.5"],
"block_all_models": True,
"max_messages_per_day": 0,
}
with pytest.raises(HTTPException) as exc:
_enforce_chat_privileges(_Request(privs), _Session("gpt-4"))
assert exc.value.status_code == 403
with pytest.raises(HTTPException):
_enforce_chat_privileges(_Request(privs), _Session("anything-else"))
def test_admin_user_is_never_blocked(monkeypatch):
from core.auth import ADMIN_PRIVILEGES
monkeypatch.setattr("routes.chat_helpers.get_current_user", lambda request: "admin")
class _AdminAuthManager:
def get_privileges(self, username):
assert username == "admin"
return dict(ADMIN_PRIVILEGES)
class _AdminRequest:
def __init__(self):
self.app = type("App", (), {})()
self.app.state = type("State", (), {"auth_manager": _AdminAuthManager()})()
_enforce_chat_privileges(_AdminRequest(), _Session("provider/model-a"))
_enforce_chat_privileges(_AdminRequest(), _Session("anything-else"))
class _FakeSession:
def __init__(self, model="selected-model"):
self.model = model