mirror of
https://github.com/calibrain/shelfmark.git
synced 2026-10-05 22:05:50 +01:00
Auth mode resolution fell back to "none" (anonymous full admin) whenever the configured method's prerequisites were missing: no local password admin for builtin/OIDC, no Calibre-Web database, a blank proxy header, an unrecognized AUTH_METHOD (including "OIDC" in uppercase), or any error while reading the config. Deleting or demoting the last local admin was allowed on purpose because of that fallback, which exposed OIDC instances publicly. - Only an explicit AUTH_METHOD=none disables authentication. A configured method stays active when its prerequisites are missing, so sign-in fails instead of opening up. - An unrecognized or unreadable AUTH_METHOD resolves to "unavailable", which still requires a session and accepts no login. Values are normalized, so AUTH_METHOD=OIDC works. - Restore the guard against deleting or demoting the last local password admin while builtin/OIDC is active (unless DISABLE_LOCAL_AUTH is set). - Require a local admin before enabling Local auth, as OIDC already did. - Log a recovery hint at startup when builtin/OIDC runs without a local admin, and document recovery via AUTH_METHOD=none. - Drop the "will fall back to No Authentication" UI toasts and hints. Fixes https://github.com/calibrain/shelfmark/issues/1387
This commit is contained in:
@@ -52,27 +52,11 @@ class TestGetAuthMode:
|
||||
assert main_module.get_auth_mode() == "none"
|
||||
|
||||
def test_get_auth_mode_builtin(self, main_module):
|
||||
with (
|
||||
patch.object(
|
||||
main_module.app_config,
|
||||
"get",
|
||||
side_effect=_config_getter({"AUTH_METHOD": "builtin"}),
|
||||
),
|
||||
patch("shelfmark.core.auth_modes.has_local_password_admin", return_value=True),
|
||||
with patch.object(
|
||||
main_module.app_config, "get", side_effect=_config_getter({"AUTH_METHOD": "builtin"})
|
||||
):
|
||||
assert main_module.get_auth_mode() == "builtin"
|
||||
|
||||
def test_get_auth_mode_builtin_without_local_admin_falls_back_to_none(self, main_module):
|
||||
with (
|
||||
patch.object(
|
||||
main_module.app_config,
|
||||
"get",
|
||||
side_effect=_config_getter({"AUTH_METHOD": "builtin"}),
|
||||
),
|
||||
patch("shelfmark.core.auth_modes.has_local_password_admin", return_value=False),
|
||||
):
|
||||
assert main_module.get_auth_mode() == "none"
|
||||
|
||||
def test_get_auth_mode_proxy(self, main_module):
|
||||
with patch.object(
|
||||
main_module.app_config,
|
||||
@@ -92,12 +76,31 @@ class TestGetAuthMode:
|
||||
):
|
||||
assert main_module.get_auth_mode() == "cwa"
|
||||
|
||||
def test_get_auth_mode_default_on_error(self, main_module):
|
||||
def test_get_auth_mode_fails_closed_on_error(self, main_module):
|
||||
with patch.object(main_module.app_config, "get", side_effect=RuntimeError("boom")):
|
||||
assert main_module.get_auth_mode() == "none"
|
||||
assert main_module.get_auth_mode() == "unavailable"
|
||||
|
||||
|
||||
class TestAuthCheckEndpoint:
|
||||
@pytest.mark.parametrize("auth_method", ["builtin", "oidc"])
|
||||
def test_auth_check_requires_login_without_local_admin(self, main_module, auth_method):
|
||||
"""Regression for #1387: with no local admin, visitors were treated as admins."""
|
||||
with (
|
||||
patch.object(
|
||||
main_module.app_config,
|
||||
"get",
|
||||
side_effect=_config_getter({"AUTH_METHOD": auth_method}),
|
||||
),
|
||||
patch.object(main_module.user_db, "has_admin_with_password", return_value=False),
|
||||
main_module.app.test_request_context("/api/auth/check"),
|
||||
):
|
||||
data = _as_response(main_module.api_auth_check()).get_json()
|
||||
|
||||
assert data["auth_mode"] == auth_method
|
||||
assert data["auth_required"] is True
|
||||
assert data["authenticated"] is False
|
||||
assert data["is_admin"] is False
|
||||
|
||||
def test_auth_check_no_auth(self, main_module):
|
||||
with (
|
||||
patch.object(main_module, "get_auth_mode", return_value="none"),
|
||||
|
||||
Reference in New Issue
Block a user