diff --git a/tests/e2e/test_proxy_auth_middleware.py b/tests/e2e/test_proxy_auth_middleware.py index 3db86079..23047455 100644 --- a/tests/e2e/test_proxy_auth_middleware.py +++ b/tests/e2e/test_proxy_auth_middleware.py @@ -36,6 +36,25 @@ def main_module(): return main +@pytest.fixture(autouse=True) +def _empty_user_table(main_module): + """Give every test an instance with no accounts yet. + + Proxy provisioning keys off whether the instance already has an admin, so + two tests sharing a user table are really asserting the order they happened + to run in. The suite runs with xdist, where each worker gets its own + CONFIG_DIR, so that order changes whenever the test count does. + """ + + def _clear() -> None: + for user in main_module.user_db.list_users(): + main_module.user_db.delete_user(user["id"]) + + _clear() + yield + _clear() + + class TestProxyAuthMiddleware: def test_skips_for_non_proxy_mode(self, main_module): with ( @@ -231,6 +250,92 @@ class TestProxyAuthMiddleware: assert db_user is not None assert db_user["username"] == username + def test_first_account_is_admin_and_later_ones_are_not(self, main_module): + """The bootstrap account is an admin; after that the default role decides.""" + with ( + patch.object(main_module, "get_auth_mode", return_value="proxy"), + patch.object( + main_module.app_config, + "get", + side_effect=_config_getter({"PROXY_AUTH_USER_HEADER": "X-Auth-User"}), + ), + ): + with main_module.app.test_request_context( + "/api/releases", + headers={"X-Auth-User": "first_proxy_user"}, + ): + assert main_module.proxy_auth_middleware() is None + assert main_module.session.get("is_admin") is True + + # The instance now has an admin, so nobody is at risk of being locked + # out and the next account follows PROXY_AUTH_DEFAULT_ROLE. + with main_module.app.test_request_context( + "/api/releases", + headers={"X-Auth-User": "second_proxy_user"}, + ): + assert main_module.proxy_auth_middleware() is None + assert main_module.session.get("is_admin") is False + + def test_default_role_admin_promotes_later_accounts(self, main_module): + main_module.user_db.create_user( + username="existing_admin", + role="admin", + auth_source="proxy", + ) + + with ( + patch.object(main_module, "get_auth_mode", return_value="proxy"), + patch.object( + main_module.app_config, + "get", + side_effect=_config_getter( + { + "PROXY_AUTH_USER_HEADER": "X-Auth-User", + "PROXY_AUTH_DEFAULT_ROLE": "admin", + } + ), + ), + main_module.app.test_request_context( + "/api/releases", + headers={"X-Auth-User": "later_proxy_admin"}, + ), + ): + assert main_module.proxy_auth_middleware() is None + assert main_module.session.get("is_admin") is True + + def test_existing_user_keeps_its_stored_role(self, main_module): + main_module.user_db.create_user( + username="existing_admin", + role="admin", + auth_source="proxy", + ) + main_module.user_db.create_user( + username="known_plain_user", + role="user", + auth_source="proxy", + ) + + with ( + patch.object(main_module, "get_auth_mode", return_value="proxy"), + patch.object( + main_module.app_config, + "get", + side_effect=_config_getter( + { + "PROXY_AUTH_USER_HEADER": "X-Auth-User", + "PROXY_AUTH_DEFAULT_ROLE": "admin", + } + ), + ), + main_module.app.test_request_context( + "/api/releases", + headers={"X-Auth-User": "known_plain_user"}, + ), + ): + assert main_module.proxy_auth_middleware() is None + # The stored role wins; the default only applies to new accounts. + assert main_module.session.get("is_admin") is False + def test_returns_401_when_header_missing_on_protected_path(self, main_module): with ( patch.object(main_module, "get_auth_mode", return_value="proxy"),