Skip to content

Gate tournament creation behind an operator-approved creator allowlist - #303

Merged
amrtgaber merged 1 commit into
mainfrom
creator-allowlist
Jul 25, 2026
Merged

Gate tournament creation behind an operator-approved creator allowlist#303
amrtgaber merged 1 commit into
mainfrom
creator-allowlist

Conversation

@amrtgaber

Copy link
Copy Markdown
Contributor

Summary

Implements #296. Tournament creation becomes invite-gated while everything else about the auth model stays put:

  • New tournament_creators table (expand-only migration a96e9a05b112, validated against a throwaway Postgres 16): user_id String(36) PK — the token sub — mirroring tournament_owners' FK-free shape. Identity stays in the auth service; app permissions stay modelled here.
  • POST /v1/tournaments now 403s callers without an allowlist row, with a stable message and no contact address baked into the API (the management FE renders the request-access CTA).
  • GET /v1/me gains can_create_tournaments: bool (additive) so the FE can gate create-form vs. CTA off the call it already makes — no probe POST. Consumers regenerate orval clients.
  • Defense-in-depth caps, generous and constant-based like the presentation-bag size caps:
    • _MAX_OWNED_TOURNAMENTS_PER_CREATOR = 10 — checked at create, counted against owner rows (creation records no separate authorship; ownership is the closest durable proxy).
    • _MAX_ROSTER_ROWS_PER_TOURNAMENT = 100 — checked at roster add; every linked row feeds the poller's per-profile upstream fan-out, so roster size is upstream quota for the whole platform.
  • Per-tournament authorization is unchanged (tournament_owners); approved creators still become first owner of what they create; PATCH/DELETE/roster/teams need ownership only, not creator status.

Operational notes

  • Grants are out-of-band by design (same path the first owner rows used) — a grant API would need a platform-admin concept this service deliberately doesn't have. Since prod currently has no database (dormant), seeding happens at reactivation; template:
    INSERT INTO tournament_creators (user_id) VALUES ('<operator-uuid>');
  • A 403 reply to a keyed request is idempotency-cached like any 4xx (existing middleware semantics) — a newly-granted caller retries with a fresh Idempotency-Key, which clients generate per logical attempt anyway.

Tests

New: 403 for an authenticated non-allowlisted user; can_create_tournaments true/false/per-user on /v1/me; ownership-ceiling 422; roster-ceiling 422 including slot-freed-after-delete. Existing create-path tests seed the allowlist via a new seed_tournament_creator fixture. Full suite: 567 passed; migration chain validated on Postgres 16.

Merge order: after #302 — this branch is stacked on it (shared create-handler/test context, and keeps the Alembic lineage linear).

Closes #296

@amrtgaber
amrtgaber force-pushed the creator-allowlist branch from 92540f7 to 249577f Compare July 25, 2026 21:17
@amrtgaber
amrtgaber merged commit 91eb7e7 into main Jul 25, 2026
3 checks passed
@amrtgaber
amrtgaber deleted the creator-allowlist branch July 25, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gate tournament creation behind an operator-approved creator allowlist

1 participant