teecup/app/routers/organizations.py

62 lines
2.1 KiB
Python
Raw Normal View History

"""
Organisasjon-bootstrap (opprett en ny organisasjon og bli owner).
Eneste stedet i API-et som setter inn en `organization`-rad. Bruker
`get_current_user` (IKKE `get_authorized_org`, som forutsetter en ALLEREDE
eksisterende org-medlemskap -- sirkulært for akkurat denne handlingen, samme
resonnement som lag-/roster-opprettelse i oppsett-runden).
Selvrefererende RLS-bootstrap: `org_self`-policyen (etter migrasjon 005) er
`USING (id = app_current_org())` uten eksplisitt WITH CHECK, som Postgres
speiler dynamisk til INSERT-sjekken. Genererer derfor org-ens uuid FØR
innsetting og setter `app.current_org` til NØYAKTIG den verdien via den
allerede eksisterende `org_connection()` -- da er WITH CHECK trivielt sann
(id == current_org), og `teecup_app` (NOSUPERUSER/NOBYPASSRLS) kan sette inn
sin egen FØRSTE organisasjonsrad uten noen egen privilegert tilkobling.
Verifisert med en negativ kontroll (mismatchende id blir avvist) i
scratch-testingen -- se plan.
"""
import uuid
from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from ..auth import CurrentUser, get_current_user
from ..db import org_connection
from ..errors import translate_db_errors
router = APIRouter()
class OrganizationCreate(BaseModel):
name: str = Field(min_length=1, max_length=200)
class OrganizationOut(BaseModel):
id: str
name: str
role: str
@router.post("/orgs", response_model=OrganizationOut, status_code=201)
async def create_organization(
body: OrganizationCreate,
user: CurrentUser = Depends(get_current_user),
) -> OrganizationOut:
new_id = str(uuid.uuid4())
async with org_connection(new_id) as conn, translate_db_errors():
row = await conn.fetchrow(
"INSERT INTO organization (id, name) VALUES ($1, $2) RETURNING id::text, name",
new_id,
body.name,
)
await conn.execute(
"INSERT INTO organization_membership (organization_id, user_id, role) VALUES ($1, $2, 'owner')",
new_id,
user.user_id,
)
return OrganizationOut(id=row["id"], name=row["name"], role="owner")