teecup/app/team_authz.py
Erol Haagenrud a46377e7bc Update Todos
Utvid team_authz.py: org owner/admin får overstyre uten unntak

Oppdater alle 5 kallsteder i matches.py/scoring.py

Scratch-verifisering: org-admin uten roster kan låse/legge til deltakere/score, vanlig member kan ikke, rostret spiller uendret

Rulle ut live + oppdatere statusdokumenter
Live, verifisert, og dokumentert. Klar for å skrive V0-prompten for blind draw-skjermen når du vil — samme mønster som de forrige, og nå med bekreftet organisator-tilgang selv om ingen kaptein har logget inn ennå.
2026-07-18 16:19:52 +02:00

57 lines
2.1 KiB
Python

"""
Delt autorisasjonssjekk: kan brukeren handle på vegne av et gitt lag?
Brukt av både matches.py (deltaker/lås-skriving) og scoring.py (score-skriving).
To uavhengige veier inn:
1. Rostret på laget -- spiller med `player.user_id` koblet på nettopp DENNE
team_roster-raden (den opprinnelige, fortsatt gjeldende regelen).
2. Org-eier/admin i organisasjonen laget tilhører. Lagt til 2026-07-18: uten
dette kunne INGEN sette opp eller låse et lag før minst én spiller hadde
logget inn og blitt koblet -- vanlig tidlig i en turnering, og
organisatoren (som uansett allerede ser begge lags fulle troppe-liste,
blind draw skjuler kun selve kamp-paringen) satt fast.
Bevisst INGEN unntak for at organisatoren selv er rostret på
MOTSTANDERLAGET i samme turnering -- vurdert og avvist: TeeCup er et
tillitsbasert verktøy for klubber/vennegjenger, ikke en sikkerhets-
grense mot en fiendtlig organisator (som uansett har full administrativ
tilgang), og et slikt unntak ville skapt en reell låsning (ingen kan
sette opp MOTSTANDERLAGET om det heller ikke har noen innlogget spiller
-- verre enn problemet det skulle løse).
"""
from asyncpg import Connection
async def user_may_act_for_team(
conn: Connection, organization_id: str, team_id: str, user_id: str
) -> bool:
is_rostered = await conn.fetchval(
"""
SELECT EXISTS (
SELECT 1 FROM team_roster tr
JOIN player p ON p.id = tr.player_id
WHERE tr.team_id = $1 AND p.user_id = $2
)
""",
team_id,
user_id,
)
if is_rostered:
return True
# organization_membership har ingen RLS-policy (se auth.get_authorized_org
# sin egen kommentar) -- filtrert eksplisitt på organization_id her, trygt
# på samme tilkobling uansett hvilken app.current_org som er satt.
is_org_admin = await conn.fetchval(
"""
SELECT EXISTS (
SELECT 1 FROM organization_membership
WHERE organization_id = $1 AND user_id = $2 AND role IN ('owner', 'admin')
)
""",
organization_id,
user_id,
)
return bool(is_org_admin)