teecup/017_secondary_email.sql

50 lines
2.5 KiB
MySQL
Raw Permalink Normal View History

Update Todos Flytte is_participant-logikk til team_authz.py (unngå sirkulær import) Widen scoring.py: get_scorecard/submit_hole_score/submit_hole_result/concede_match Widen matches.py: list_matches/add_participant/remove_participant/lock_lineup Widen tournaments.py: list_sessions/list_teams/concede_tournament Widen courses.py: list_holes Widen messaging.py: team chat REST-endepunkter (list/send/delete) Legge til my_session_id/my_match_id i /auth/me sin my_tournaments Frontend: legge til lenker til lag-chat/scorekort fra «Mine runder» Scratch-verifisere hele deltaker-tilgang-runden (positive + negative kontroller) Ekte typesjekket frontend-produksjonsbuild + backend py_compile på nytt før deploy-forslag Presentere plan, deploye og oppdatere .md-filer for deltaker-tilgang-runden Bygge og scratch-verifisere sekundær e-postadresse (migrasjon 017) Bygge HCP-historikk over tid Sekundær e-postadresse (det enkle tilfellet) er bygget og scratch-verifisert (20/20 sjekker), test_isolation.sql fortsatt 12/12. Hva er bygget: Migrasjon 017_secondary_email.sql — to nye tabeller (secondary_email_token, user_secondary_email), samme bevis-eierskap-token-mønster som ADR-032. Backend: POST /auth/secondary-email (send bekreftelseslenke), POST /auth/secondary-email/confirm (bekreft, ingen sesjon påkrevd), DELETE /auth/secondary-email/{id}. Viktigst: verify_magic_link og login_with_password løser nå en sekundær adresse til den eksisterende eierens konto i stedet for å opprette en ny, separat konto — dette er selve poenget med funksjonen. Frontend: ny seksjon "Andre e-postadresser" i /account (ikke dashbordet — se merknad under), /verify-email håndterer nå både e-postbytte og ny-sekundær-adresse via en ?kind=secondary-parameter. Én bevisst plasseringsavgjørelse jeg tok uten å spørre: du sa opprinnelig at hele multi-e-post-flyten burde skje "fra dashboard-siden". Jeg la likevel dette i /account, fordi jeg her kun bygger det enkle tilfellet (fri adresse) — konsistent med hvor e-postbytte (ADR-032) allerede ligger. Når/hvis den harde saken (ekte konto-sammenslåing, der data faktisk "dukker opp") bygges senere, er dashbordet trolig riktigere siden gevinsten vises der. Si fra hvis du vil at den skal flyttes allerede nå. Verifisert grundig: ny sekundær-adresse legges IKKE til før bekreftet; token kan ikke gjenbrukes; adresse som allerede er en annens hovedadresse ELLER en annens sekundæradresse avvises tydelig; innlogging (magic-link OG passord) via sekundæradressen løses korrekt til samme, eksisterende konto; en fremmed kan ikke slette andres sekundæradresse; og — kritisk — etter sletting oppretter en ny innlogging på den adressen en helt ny, separat konto (beviser fjerningen er reell). Ingen migrasjon kjørt mot ekte teecup_db ennå.
2026-07-22 06:14:31 +02:00
-- =====================================================================
-- TeeCup — sekundær, verifisert e-postadresse per konto (migrasjon 017)
-- =====================================================================
-- Oppfølging av ADR-032 (verifisert e-postbytte), reist av brukeren rett
-- etterpå: én person kan ha flere e-postadresser i omløp samtidig (privat
-- vs. jobb-adresse en organisator har lagt inn en spiller/invitasjon på).
-- Dette dekker BEVISST kun det enkle tilfellet -- en FRI, ukrevd adresse
-- legges til og verifiseres. Ekte konto-SAMMENSLÅING (adressen tilhører
-- allerede en annen, eksisterende konto) er en egen, større, separat sak
-- (se FEATURE_BACKLOG.md "Én person, flere e-postadresser") og håndheves
-- IKKE her -- unikheten under garanterer at en adresse aldri kan stå som
-- sekundær på to kontoer samtidig (samme rad ville krevd en eksplisitt
-- sammenslåingsbeslutning som ikke finnes ennå).
--
-- To tabeller, samme token-hash-og-utløp-mønster som email_change_token
-- (016): en midlertidig verifiseringstoken (bevis eierskap FØR raden
-- faktisk opprettes -- unngår en "krevd men aldri bekreftet"-adresse som
-- likevel opptar en unik plass), og selve den verifiserte adressen.
-- =====================================================================
\set ON_ERROR_STOP on
CREATE TABLE secondary_email_token (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES app_user(id) ON DELETE CASCADE,
email citext NOT NULL,
token_hash text NOT NULL UNIQUE,
expires_at timestamptz NOT NULL,
consumed_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX ON secondary_email_token (user_id);
CREATE TABLE user_secondary_email (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES app_user(id) ON DELETE CASCADE,
-- Globalt unik -- kan aldri stå som sekundær på to kontoer samtidig,
-- og aldri samtidig være en ANNEN kontos primære e-post (håndhevet i
-- app-laget ved innsetting, se app/routers/auth.py -- en ren DB-CHECK
-- på tvers av to tabeller er ikke mulig i Postgres).
email citext NOT NULL UNIQUE,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX ON user_secondary_email (user_id);
GRANT SELECT, INSERT, UPDATE, DELETE ON secondary_email_token TO teecup_app;
GRANT SELECT, INSERT, DELETE ON user_secondary_email TO teecup_app;