diff --git a/paskia/util/avatar.py b/paskia/util/avatar.py index 30bff7c..4c765d1 100644 --- a/paskia/util/avatar.py +++ b/paskia/util/avatar.py @@ -46,7 +46,7 @@ def avatar_url(user_uuid: UUID) -> str | None: """Return the absolute public avatar URL for a user, or None.""" if not avatar_path(user_uuid).is_file(): return None - return f"{hostutil.auth_site_url()}api/user/{user_uuid}/profile.webp" + return hostutil.api_url(f"user/{user_uuid}/profile.webp") def current_avatar_url(user_uuid: UUID) -> str | None: diff --git a/paskia/util/hostutil.py b/paskia/util/hostutil.py index 1150ca9..c8c5498 100644 --- a/paskia/util/hostutil.py +++ b/paskia/util/hostutil.py @@ -29,6 +29,16 @@ def ui_base_path() -> str: return "/" if is_root_mode() else "/auth/" +def api_url(path: str = "") -> str: + """Return an absolute URL under the canonical /auth/api/ prefix.""" + cfg = _cfg() + base = cfg.site_url if cfg else "https://localhost" + if not path: + return f"{base}/auth/api/" + normalized = path.lstrip("/") + return f"{base}/auth/api/{normalized}" + + def auth_site_url() -> str: """Return the base URL for the auth site UI (computed at startup).""" cfg = _cfg() diff --git a/tests/test_api.py b/tests/test_api.py index 197ae57..7f6a819 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -13,6 +13,7 @@ These tests cover: import secrets from datetime import UTC, datetime, timedelta from urllib.parse import urlsplit +from uuid import UUID import httpx import pytest @@ -21,7 +22,7 @@ from paskia import authcode from paskia.authsession import EXPIRES from paskia.db import delete_session from paskia.db.structs import Client -from paskia.util import oidjwt +from paskia.util import avatar, hostutil, oidjwt from paskia.util.passphrase import generate from tests.conftest import auth_headers, create_test_image_bytes, create_test_session @@ -59,6 +60,32 @@ class TestSettingsEndpoint: assert "picture" in response.json()["claims_supported"] +class TestAvatarUrls: + """Tests for avatar URL helpers.""" + + def test_avatar_url_uses_canonical_public_path_in_auth_host_mode( + self, tmp_path, monkeypatch + ): + """Absolute avatar URLs should preserve /auth/api even with an auth host.""" + db_root = tmp_path / "test-avatar-db.paskiadb" + monkeypatch.setenv("PASKIA_DB", str(db_root)) + monkeypatch.setattr( + hostutil, + "api_url", + lambda path="": f"https://auth.zi.fi/auth/api/{path.lstrip('/')}", + ) + + user_uuid = test_uuid = UUID("019c6831-84cf-7b88-b66c-c8165890b7c5") + path = db_root / "users" / str(test_uuid) / "profile.webp" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(b"RIFF1234WEBP") + + assert avatar.avatar_url(user_uuid) == ( + "https://auth.zi.fi/auth/api/user/" + "019c6831-84cf-7b88-b66c-c8165890b7c5/profile.webp" + ) + + class TestValidateEndpoint: """Tests for POST /auth/api/validate"""