Pasirašytojas nėra vartotojas: žetonas yra autorizacija
Dokumentų paviršius veikia, bet kiekvienas dokumentas vis dar privatus savo savininkui. Ši pamoka jį atveria: savininkas kviečia pasirašytojus el. paštu, o dokumentas juda iš draft į sent. Po tuo slypintis dizaino pasirinkimas ir yra pamoka.
Pasirašytojas nėra vartotojas. Pasirašytojas neturi paskyros, slaptažodžio, prisijungimo. Jis pakviestas el. paštu ir turi nuorodą — tai viskas. Modeliuoti jį kaip vartotoją būtų klaida (jis niekada nesiregistruoja), o modeliuoti nuosavybę L9 būdu būtų neįmanoma (jis nieko neturi). signers eilutė ir yra pasirašytojas: kuris dokumentas, kuris el. paštas ir žetonas.
Žetonas IR YRA autorizacija. Pažiūrėk, ką pasirašytojas siunčia: GET /sign/<token>. Užklausoje nėra dokumento ID. Žetonas maišuojasi į lygiai vieną signers eilutę (token_hash yra UNIQUE), o ta eilutė įvardija savo vieną dokumentą. Pasirašytojas tiesiog negali išreikšti „duok man dokumentą X" — nėra jokio X, kurį įdėtum į užklausą. Palygink su L9, kur paimi dokumentą pagal ID, tada saugai jį su AND owner_id = $me. Abu teisingi, bet skiriasi iš esmės:
- L9 yra gynybinis: ID valdo užpuolikas, tad pridedi patikrą ir niekada negali jos pamiršti.
- Žetonas yra struktūrinis: nėra jokio ID, kurį atakuoti. Visa „o kas, jei jis paduos svetimą ID" klaidų klasė negali būti parašyta, nes užklausoje nėra ID lauko.
Tai stipresnis modelis — jis pašalina klaidą, o ne nuo jos saugosi. Šiukšlinis ar atšauktas žetonas nesutampa su jokia eilute: švarus 404.
Saugok maišą, ne žetoną — jau trečią kartą. Sesijos, slaptažodžio atstatymai ir dabar pasirašytojų nuorodos — visi laikosi tos pačios taisyklės: neapdorotas žetonas keliauja lauk (nuorodoje), o saugoma tik jo SHA-256. Šią formą matei jau tris kartus, tad įvardyk ją: nešiklio paslaptis saugoma maišuota, tad nutekėjusi duomenų bazė neduoda naudojamų paslapčių. auth.GenerateToken — tas pats generatorius iš atstatymo pamokos — grąžina neapdorotą žetoną nuorodai ir maišą eilutei.
Kvietimas — daugiau nei vienas rašymas, tad tai viena transakcija. Dokumento siuntimas pasirašyti daro du dalykus: sukuria signers eilutę kiekvienam el. paštui ir perverčia dokumentą draft → sent. Jei trečias iš penkių įterpimų nepavyktų jau perverus statusą, turėtum sent dokumentą su sugadintu pasirašytojų sąrašu — pusinį kvietimą. Todėl visa tai vyksta vienoje Postgres transakcijoje (pool.Begin → Queries.WithTx(tx) → Commit): arba kiekvienas pasirašytojas sukuriamas ir dokumentas išsiunčiamas, arba niekas nesikeičia ir jis lieka juodraštis. Čia pirma vieta, kur programai reikia h.Pool — neapdoroto telkinio, kad atvertų transakciją, kurios viduje vyksta užklausų metodai.
Ši pamoka:
signerslentelė + užklausos ir transakcijų vamzdynas (h.Pool).InviteSigners— sukurti pasirašytojus ir perversti į sent vienoje transakcijoje — bei savininko kvietimo forma ir pasirašytojų sąrašas.
(Nuoroda nukrenta į pasirašytojo pašto dėžutę — atspausdinta į tavo konsolę dev pašto siuntėjo iš 7 pamokos. Puslapį, kurį ji atveria, statysime kitą pamoką; ši pamoka — savininko pusė.)