diff --git a/src/tool_security.py b/src/tool_security.py index 6d29a6ab9..3dc53ff26 100644 --- a/src/tool_security.py +++ b/src/tool_security.py @@ -177,13 +177,16 @@ def owner_is_admin_or_single_user(owner: Optional[str]) -> bool: defense-in-depth for callers that bypass it (e.g. trusted loopback). """ try: + from src.auth_helpers import _auth_disabled + + if _auth_disabled(): + return True + from core.auth import AuthManager auth = AuthManager() if not auth.is_configured: - from src.auth_helpers import _auth_disabled - - return _auth_disabled() + return False return bool(owner and auth.is_admin(owner)) except Exception as exc: logger.warning("Unable to evaluate owner admin status: %s", exc) diff --git a/tests/test_review_regressions.py b/tests/test_review_regressions.py index fe782f151..b753ae9d7 100644 --- a/tests/test_review_regressions.py +++ b/tests/test_review_regressions.py @@ -701,6 +701,34 @@ def test_single_user_mode_keeps_full_tool_access_when_auth_disabled(monkeypatch) assert blocked_tools_for_owner(None) == set() +def test_auth_disabled_configured_mode_keeps_full_tool_access(monkeypatch): + """AUTH_ENABLED=false is still intentional single-user mode after setup. + + Once an admin account exists, AuthManager.is_configured becomes true. The + tool gate must still honor explicit auth-disabled mode before requiring an + owner/admin match, otherwise agent mode hides email/MCP/local tools from the + operator. + """ + monkeypatch.setenv("AUTH_ENABLED", "false") + auth_mod = _install_core_auth_stub(monkeypatch) + + class FakeAuth: + is_configured = True + + def is_admin(self, username): + return False + + monkeypatch.setattr(auth_mod, "AuthManager", lambda: FakeAuth()) + + from src.tool_security import ( + blocked_tools_for_owner, + owner_is_admin_or_single_user, + ) + + assert owner_is_admin_or_single_user(None) is True + assert blocked_tools_for_owner(None) == set() + + @pytest.mark.asyncio async def test_webhook_tool_reuses_private_url_validation(): class FakeDb: