import os import shutil from pathlib import Path import pytest from porchlight.config import Settings from porchlight.oidc.claims import PorchlightUserInfo from porchlight.oidc.provider import create_oidc_server def test_create_server_has_endpoints() -> None: key_path = Path("test_keys_provider") key_path.mkdir(exist_ok=True) try: settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path)) server = create_oidc_server(settings) assert "authorization" in server.endpoint assert "token" in server.endpoint assert "userinfo" in server.endpoint assert "provider_config" in server.endpoint finally: shutil.rmtree(key_path, ignore_errors=True) def test_create_server_has_issuer() -> None: key_path = Path("test_keys_issuer") key_path.mkdir(exist_ok=True) try: settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path)) server = create_oidc_server(settings) assert server.context.issuer == "http://localhost:8000" finally: shutil.rmtree(key_path, ignore_errors=True) def test_create_server_jwks_available() -> None: key_path = Path("test_keys_jwks") key_path.mkdir(exist_ok=True) try: settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path)) server = create_oidc_server(settings) keys = server.keyjar.export_jwks() assert "keys" in keys assert len(keys["keys"]) > 0 finally: shutil.rmtree(key_path, ignore_errors=True) def test_create_server_userinfo_is_porchlight() -> None: key_path = Path("test_keys_userinfo") key_path.mkdir(exist_ok=True) try: settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path)) server = create_oidc_server(settings) assert isinstance(server.context.userinfo, PorchlightUserInfo) finally: shutil.rmtree(key_path, ignore_errors=True) def test_signing_key_files_have_strict_permissions(tmp_path: Path) -> None: key_path = tmp_path / "keys" settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path)) create_oidc_server(settings) assert (key_path.stat().st_mode & 0o077) == 0, "key directory must not be group/world accessible" for name in ("private_jwks.json", "token_jwks.json"): f = key_path / name assert f.exists() assert (f.stat().st_mode & 0o077) == 0, f"{name} must be 0600" def test_startup_fails_on_world_readable_private_key(tmp_path: Path) -> None: key_path = tmp_path / "keys" key_path.mkdir() # Simulate a pre-existing private key left group/world readable. leaked = key_path / "private_jwks.json" leaked.write_text("{}") os.chmod(leaked, 0o644) settings = Settings(issuer="http://localhost:8000", sqlite_path=":memory:", signing_key_path=str(key_path)) with pytest.raises(RuntimeError, match="permission"): create_oidc_server(settings)