Proper handling of auth site runtime change done via web interface, making the change immediately effective. Kept in origins list that is still also visible on the same dialog, where it can be removed if needed.

This commit is contained in:
2026-08-09 23:00:22 +00:00
parent df8a7c0026
commit 4b156b712c
2 changed files with 127 additions and 6 deletions
+12 -6
View File
@@ -48,14 +48,20 @@ def update_runtime_config(new_config: Config) -> None:
return # No runtime config to update
# Recompute site_url and site_path based on new config
site_path = "/" if new_config.auth_host else "/auth/"
old_auth_host = current_runtime.config.auth_host
if new_config.auth_host:
site_url = new_config.auth_host
elif new_config.origins:
site_url = new_config.origins[0]
site_url, site_path = new_config.auth_host, "/"
else:
# Keep current site_url if no auth_host and no origins
site_url = current_runtime.site_url
site_path = "/auth/"
# Never derive site_url from a just-removed auth host
origins = [o for o in (new_config.origins or []) if o != old_auth_host]
if origins:
site_url = origins[0]
elif current_runtime.site_url != old_auth_host:
# Keep current site_url if it wasn't derived from the removed auth host
site_url = current_runtime.site_url
else:
site_url = f"https://{new_config.rp_id}"
new_runtime = RuntimeConfig(
config=new_config,
+115
View File
@@ -37,7 +37,10 @@ from paskia.db import (
create_user,
)
from paskia.db.operations import DB
from paskia.util import hostutil
from paskia.util.crypto import hash_secret
from paskia.util.runtime import clear_config_cache
from paskia.util.runtime import config as runtime_config
from tests.conftest import auth_headers, create_test_image_bytes, create_test_session
# -------------------- Additional Fixtures --------------------
@@ -1789,3 +1792,115 @@ class TestOrgAdminAuthExceptions:
headers={**auth_headers(regular_session_token), "Host": "localhost:4401"},
)
assert response.status_code == 403
class TestServerConfig:
"""Tests for GET/PATCH /auth/api/admin/server-config/ runtime updates."""
@pytest.fixture(scope="function")
def restore_runtime_config(self):
"""Restore PASKIA_CONFIG env and cache after a test mutates runtime."""
original = os.environ["PASKIA_CONFIG"]
yield
os.environ["PASKIA_CONFIG"] = original
clear_config_cache()
async def _set_auth_host(self, client, session_token, test_user, test_credential):
"""Configure an auth host via PATCH, as the admin UI would."""
r = await client.patch(
"/auth/api/admin/server-config/",
json={
"rp_name": "",
"auth_host": "auth.localhost",
"origins": ["auth.localhost", "localhost"],
},
headers={**auth_headers(session_token), "Host": "localhost:4401"},
)
assert r.status_code == 200, r.text
assert db.data().config.auth_host == "https://auth.localhost"
assert hostutil.dedicated_auth_host() == "auth.localhost"
assert hostutil.auth_site_url() == "https://auth.localhost/"
# Session for requests coming from the auth host (sessions are host-bound)
_, token = create_test_session(
test_user.uuid, test_credential.uuid, host="auth.localhost"
)
return {**auth_headers(token), "Host": "auth.localhost"}
@pytest.mark.asyncio
async def test_remove_auth_host_updates_runtime(
self,
client: httpx.AsyncClient,
session_token: str,
test_user,
test_credential,
restore_runtime_config,
):
"""Removing auth_host must clear it from runtime config and URLs."""
headers = await self._set_auth_host(
client, session_token, test_user, test_credential
)
# The dialog still lists the old auth host among origins, so it is sent back
r = await client.patch(
"/auth/api/admin/server-config/",
json={
"rp_name": "",
"auth_host": "",
"origins": ["auth.localhost", "localhost"],
},
headers=headers,
)
assert r.status_code == 200, r.text
assert db.data().config.auth_host is None
rt = runtime_config()
assert rt.config.auth_host is None
assert rt.site_path == "/auth/"
assert "auth.localhost" not in rt.site_url
assert hostutil.dedicated_auth_host() is None
assert "auth.localhost" not in hostutil.auth_site_url()
# GET and settings reflect the cleared state
r = await client.get(
"/auth/api/admin/server-config/",
headers={**auth_headers(session_token), "Host": "localhost:4401"},
)
assert r.json()["auth_host"] == ""
r = await client.get("/auth/api/settings")
assert r.json()["auth_host"] is None
assert r.json()["ui_base_path"] == "/auth/"
# Middleware no longer redirects to the removed auth host
r = await client.get(
"/auth/admin",
headers={**auth_headers(session_token), "Host": "localhost:4401"},
follow_redirects=False,
)
assert "auth.localhost" not in r.headers.get("location", "")
@pytest.mark.asyncio
async def test_remove_auth_host_without_origins_falls_back_to_rp_id(
self,
client: httpx.AsyncClient,
session_token: str,
test_user,
test_credential,
restore_runtime_config,
):
"""With no origins left, site_url must not keep the removed auth host."""
headers = await self._set_auth_host(
client, session_token, test_user, test_credential
)
r = await client.patch(
"/auth/api/admin/server-config/",
json={"rp_name": "", "auth_host": "", "origins": []},
headers=headers,
)
assert r.status_code == 200, r.text
rt = runtime_config()
assert rt.config.auth_host is None
assert rt.site_path == "/auth/"
assert "auth.localhost" not in rt.site_url
assert "auth.localhost" not in hostutil.auth_site_url()