Send Patreon tier access policy

This commit is contained in:
Meta Vens 2026-04-29 14:36:03 +01:00
commit 7722007e02
2 changed files with 53 additions and 0 deletions

View file

@ -199,6 +199,7 @@ class AccessRuntimeConfig:
jwks_cache_seconds: int = 300
discord_access_guild_id: str | None = None
discord_access_role_ids: tuple[str, ...] = ()
patreon_required_tier_title: str | None = None
@property
def enabled(self) -> bool:
@ -464,6 +465,7 @@ class AccessController:
start_endpoint = None
discord_access_guild_id = None
discord_access_role_ids: tuple[str, ...] = ()
patreon_required_tier_title = None
login_url = _env_text("GC_ACCESS_LOGIN_URL")
logout_url = _env_text("GC_ACCESS_LOGOUT_URL")
verifier: HeimdallVerifier | None = None
@ -504,6 +506,7 @@ class AccessController:
start_endpoint = _AUTH_START_PATH
discord_access_guild_id = _env_text("REPIXELIZER_ACCESS_DISCORD_GUILD_ID")
discord_access_role_ids = tuple(_parse_csv(_env_text("REPIXELIZER_ACCESS_DISCORD_ALLOWED_ROLE_IDS")))
patreon_required_tier_title = _env_text("REPIXELIZER_ACCESS_PATREON_TIER_TITLE") or "Inner Sanctum"
verifier = HeimdallVerifier(
issuer=heimdall_issuer,
app_slug=app_slug,
@ -534,6 +537,7 @@ class AccessController:
jwks_cache_seconds=jwks_cache_seconds,
discord_access_guild_id=discord_access_guild_id,
discord_access_role_ids=discord_access_role_ids,
patreon_required_tier_title=patreon_required_tier_title,
)
return cls(runtime, verifier=verifier)
@ -628,6 +632,11 @@ class AccessController:
"guildId": self.runtime.discord_access_guild_id,
"allowedRoleIds": list(self.runtime.discord_access_role_ids),
}
elif provider_slug == "patreon":
request_payload["entitlementPolicy"] = {
"kind": "patreon_membership_access",
"requiredTierTitle": self.runtime.patreon_required_tier_title or "Inner Sanctum",
}
start_url = urljoin(f"{heimdall_base_url}/", _AUTH_START_ENDPOINT_TEMPLATE.format(provider=provider_slug).lstrip("/"))
try:
response_payload = _post_json(

View file

@ -899,6 +899,50 @@ def test_gui_heimdall_callback_flow_adopts_local_cookie_session(monkeypatch, tmp
assert app_headers["cache-control"] == "no-store, no-cache, must-revalidate, max-age=0"
def test_gui_heimdall_patreon_auth_start_sends_membership_policy(monkeypatch, tmp_path: Path) -> None:
from repixelizer.gui import create_app
start_calls: list[tuple[str, dict[str, object], float]] = []
def fake_post_json(url: str, payload: dict[str, object], *, timeout_seconds: float):
start_calls.append((url, payload, timeout_seconds))
return {
"authorizationUrl": "https://www.patreon.com/oauth2/authorize?state=test-state",
"stateExpiresAt": "2026-04-27T12:15:00.000Z",
}
monkeypatch.setenv("REPIXELIZER_HOSTED_DEMO", "1")
monkeypatch.setenv("REPIXELIZER_SPOOL_DIR", str(tmp_path / "spool"))
monkeypatch.setenv("GC_ACCESS_MODE", "heimdall")
monkeypatch.setenv("GC_ACCESS_HEIMDALL_BASE_URL", "https://heimdall.gamecult.org")
monkeypatch.setenv("GC_ACCESS_APP_PUBLIC_BASE_URL", "https://repixelizer.gamecult.org")
monkeypatch.setenv("GC_ACCESS_ALLOWED_PROVIDERS", "discord,patreon")
monkeypatch.setenv("REPIXELIZER_ACCESS_PATREON_TIER_TITLE", "Inner Sanctum")
monkeypatch.setattr("repixelizer.access._post_json", fake_post_json)
app = create_app()
start_status, _start_headers, start_body = asyncio.run(
_get_response(
app,
"/api/auth/heimdall/start",
method="POST",
headers={"content-type": "application/json"},
body=_json_bytes({"provider": "patreon"}),
)
)
start_payload = _response_json(type("Response", (), {"body": start_body})())
assert start_status == 201
assert start_payload["authorizationUrl"] == "https://www.patreon.com/oauth2/authorize?state=test-state"
assert start_calls
assert start_calls[0][0] == "https://heimdall.gamecult.org/v1/oauth/patreon/start"
assert start_calls[0][1]["entitlementPolicy"] == {
"kind": "patreon_membership_access",
"requiredTierTitle": "Inner Sanctum",
}
def test_hosted_job_options_use_autocorr_and_allow_rerank() -> None:
from repixelizer.gui import HostedDemoConfig, _normalize_job_options