fix: lessons

This commit is contained in:
2026-06-07 15:15:11 +02:00
parent 83779e0d7a
commit 4f13aadc1a
121 changed files with 2038 additions and 23690 deletions

View File

@@ -0,0 +1,95 @@
# Les 9 — Huiswerk
## Verdieping: profielpagina + eerste RLS-policy
Duur: ~2 uur take-home werk. In te leveren voor Les 10.
---
### Doel
Je QuickPoll-app uit de lesopdracht verder uitbouwen:
- Beschermde `/profile` pagina
- Eerste eigenaars-RLS op `polls`
- Korte reflectie auth vs. autorisatie
---
### Deel 1 — Profielpagina (45 min)
Bouw een `/profile` route die:
1. Alleen zichtbaar is voor ingelogde gebruikers
- Anoniem? → redirect naar `/login`
2. Toont:
- Email
- User-ID (UUID)
- Aanmaakdatum (`user.created_at`)
3. Bevat een "Wijzig wachtwoord"-knop (mag mock zijn, link naar `#`)
Tip: gebruik `getUser()` in de server component zelf. Geen `useEffect`-gedoe.
---
### Deel 2 — Eigenaars-RLS (45 min)
Voeg een `user_id`-kolom toe aan je `polls` tabel:
```sql
alter table polls add column user_id uuid references auth.users(id);
```
Pas je `createPoll`-action aan zodat hij de `user_id` van de huidige user mee-stuurt.
Schrijf RLS-policies:
```sql
-- Iedereen mag polls lezen
create policy "polls public read"
on polls for select
using (true);
-- Alleen eigenaar mag updaten
create policy "owner can update poll"
on polls for update
using (auth.uid() = user_id);
-- Alleen eigenaar mag verwijderen
create policy "owner can delete poll"
on polls for delete
using (auth.uid() = user_id);
```
Test: maak een poll aan met account A, log in als B, probeer A's poll te bewerken. Wat gebeurt er?
---
### Deel 3 — Reflectie (30 min, ~300 woorden)
Beantwoord:
1. Wat is het verschil tussen authenticatie en autorisatie?
2. Waarom is RLS in de database veiliger dan een check in je server action?
3. Wat zou er fout gaan als je RLS uitzette? Geef een concreet scenario.
---
### Op te leveren
- GitHub-link naar repo met:
- Werkende `/profile` route
- Werkende RLS-policies (zie screenshot in dashboard)
- `REFLECTIE.md` in repo-root met je antwoorden
- Screenshot van Supabase dashboard → Authentication → Policies (laat je policies zien)
**Inleveren:** Via Teams chat. Deadline: voor Les 10.
---
### Twijfel?
- Geen idee waar te beginnen? Lees `Les09-Lesstof.md` opnieuw — sectie 7
- Stuck op RLS? Vraag in Teams `#hulp-vragen`
- Test je policies altijd met TWEE accounts. Anders mis je security-gaten.
Veel succes!

View File

@@ -0,0 +1,105 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
5 0 obj
<<
/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/Contents 11 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
7 0 obj
<<
/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/PageMode /UseNone /Pages 10 0 R /Type /Catalog
>>
endobj
9 0 obj
<<
/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607115144+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607115144+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Huiswerk) /Trapped /False
>>
endobj
10 0 obj
<<
/Count 2 /Kids [ 6 0 R 7 0 R ] /Type /Pages
>>
endobj
11 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2006
>>
stream
Gb!;d8TWWE'Ya0?]LabK>/:3K!)*nDBC$mY1Tck^mR8oL>(S#%>SJgaN">mSI39u%Ot9g8h6aFLU57oJptgVCJDu?Lhr>"#bn*Y,112)V;d^)T@/'cJLXoObE/tCI"_(,>q](E1Rm0GV<Xc-$4J2KZ,S`]S#9:jAPeI9\qh/>Y;?bNN('>aC.\m/71=q`Hepa*5S0Mg)\=Vl%m]Y`97Qd;f[<*g/ooI%1"fmo'W7h`TbS;`=,O#+*a411H-Sq$Ik=H<'AY,pl4GOO3!_ru>VJ]D$/62NZL,Uf6AJfNF3Z]N;TOOJUqa+q>.U?"Q/qTLO"(3,V+45`l&g"7<nn^dKn(MIO8*t);\1b--iDrFE&E8UD3;(Kha4QujH2I$GfP4_0ZO(b[B(iZC*jGn?TqTJn?`e(12'ZV31EBXVZY"m!e7d$ACf(lk`:&##r@juP3GWNkW=gO([o%@0aKS<r8@6D.l-=,kBa`VESI7b1F_.U8pZkfgZY%/>hRk\V2^>]+7U'L)=Ed-V,q`\j[>Htt]?Eds=F$/,<5:_DM`a,^)[J!$4%BOfkg3#Ug]i<R;C_SW],suVe?=tG_XT\\bE0N^OC1:Ff-`ap7KOi^OX=#/_I_g:5CudIQ7GdXVW&^hZi6j5T:^3:Ub#2%5mQX,BKY&BBX?Wt7Eaq\/Pn''b06=t[b=NnQIQaMVtsa2*=+3kKJ@.7Ul9u?X'kID:$A`Z52sXs^FsH4:A].[5KtsC7[gE1:o*)u-T_G94\CPe'hgq7X/@i.QbeD'=%SR,JJFr@8UluoK/c9RUak4gqP/^nVt9Z%2$.s"JgfA_no9:bK2pOB>U@d]2mD!gE9q2QFrpSR%L;GpT9g*tRdhnPa)D-QG7_^NYTe=rRV,'O^4sB^i"PZhU&+@+;IH#e^`Wi'e_8AhgE%`qC%OkE`tr$\=I$laT=YWg.>="OOGO>=i$cP6G5,(Ra$Ph'(J,J;KB3^QS\rAoT-_ZP)OSqQbqN"oRCQ(M]>pSr"==B&@OopaJOt$:a3K!=%keeT+a+3Lae?j+.pgs5.l+6**MYsiN@CDt)PNEg*[bmnH<K]IBqg'TlLEEu*`Qi]Vb"*,CArXp=1f$mqtu!R#0Co/#YTj!G2VS_q@,;FasVX2;UdL&dnl$FXWT%_3S\5br('_V"ZX#A@'k@UHiZO4&>uQYH`b(#p!AqcN^kap%N&<DFsK*_r>5U?R#1RXI[UsacA?uqiHD@RilJY"*2$cQj1@\El]-BuE>+)HB(m5(kqtL4kcDoW3hBcV1W*<M;qG`W([,C*@]M@&5;LH3HQP?]AuBV]CDHfRJsis]R_.D\d-;M@gPNp#D%7m`D5.f)XlBL79TMnYs)1.4P9eG_*HJ%u\EeT.$2m+nS4A.e?"a*p\bSK*/i/dd#A9D./h[HBO!K;F_=`F?$A-5BaGU<=g@X;)<@-[)"aEoB'',PHjoO:^KA&L)eDJoh&$A`IK=jn+0E0H[pNd$fVNAR1KJRo$?c6'RNI"GUK.%Tj0Ibhr569Vs(Cu;F+-&$p;ep>!Nit<pHMMlP(Uq>OWSF.qZkB9@Ep202@;m!c/hg%pAJ2%'LO0b2$+tV`H9kN$:T'R<2JMhsC6GMY/q-LjcWfT&Wm3\5X1&V=o9e\_Du6S'boWtXN;n=RP:\jJOt__8:U".8@^t+**4>NmmSW)^[0h+ZL934UgSZq[Pp))Ahdb"s/,q+=VG>ki1jr<-d5Eg5:hhns6Z(;::ksqrLg7(HFAm&5mA&B+L,%/&Xf0qp1ed;B$YpfW`JBV]*2:\!C1s"nWGJE+XVCGo<mLDOh*#'4IbN_H(nsE92^qhAa_m.p#]jRg+4m3<noQ"A9[I.`,)W'XB.+,b_TS&*KoB#4hM-iSbXO`Y)85tDW@fNmU,fc:@<`Ap%q2Gps/'M8Ku3oZCe7l`n34Z6!crbl&)>]Zi#$?a13h#?D=kKh$kOl<cgmf$o0!-U`dq1H"7u<NnN>>\;a=FQj,*!)<nX?kJFti.lhe^~>endstream
endobj
12 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1860
>>
stream
Gb!;dD/\/u%0#[%_0$ic'_PIMHFZt/+\:4l%1oTon^9&pL9jS.8n?%*8I:grqsEE8[d5u>h0;5o`J,D(6lu/cI)n)KLL.XiN2?SnHLUbO#;2Z.!q%cRHKT8$lhoFE+qW2oJ[rpC6NJ`[m2q^8-Nu_B5q#&0b)o@h%m7h!;P=.XE`-oa#%@/0_h#qFOidd'\0%[-J\9/-+_YYPq*>RpbKoQSf"@(JF6gokqG#,r#sNR5dHZ&%iP+rX2hcnA5$jhsLMVop1s*$+IP2+!P61SI82rV^[s3Ns2FDNk]dr<[Zl?(po`ED?l,``o5Ep/:MrqV%nk"o&<.e<#n<kD(W!L>s0&QDk&qM7j4,c6`$j`Kf]>iO`diguEn7b%=BucFC`jT#C3hCAni,Ye'5enAcOfL!InP%GHf!l&h"`pu\VHBdMQ)p8Bd&A+%/8/'TeaapKE$giR\a:r53Qo+1NjCPhTo6qA\bsmFjhFZ*d]!l$^?TsXIcj%"-*5#64=+#P(:$bU&HE?S$MQ#Xh(G5V&.N(U;a#k<Q6\I9G_bPG\2RT=6,lcZ>:kt'?(_pmUMoXr<\c01F@ce>LG*i7o#P4XCPr1uFDf)H1QZ*u;6`Jp&?%ARk43()Vdnp4'W/hJiQjSr\R^_BWkGl(jnOc>^:1oE,Y9Or(lpE%g/QFaZQXn("1HRQ!/>55CtWZ_"sTV`<E1I6J<#fBof_<,%FEX/[`\n!o/rkf)K`PA-EN/]rIb(mr'JS?rh-;t@/?$hA,(4$4eDn\<84Xi6$!uPft?*kVs?,&$DFog3M[qs5!0"X9I/^H4DQN1i]I0h4Ihn/d?P.sg>2r)q`_bV:o.+@Yai:5g$1fc@9socSGF-T^t'+EKZ4]6cr$$mB`TTb+N_V7VkD/T2m6V$Z2kSS-U6_#"Q;7(bNYBLpo4]=irD_N!1Mqpk&5FY$!//*FgG?QDXg4rSrc/ORiT.19Ydc7b82.$?AO`jFWHjhkpuPPFW@p91,HkUJ%45k:PJ[uqZ_1H<1=o9maO(":\ZG%3S!L#B/b-*1LhusM>>t]ENN/0$ZN/TEn4tL0lPm]QTI>;#+H0D%MMG`W`^5d@`k&ncRIafdUQJ!Og\\u(GZZep9Hi&BN09jT>c$L/GAa>Rquiqrc3Hr94s&G9WJCZ7m\@oLrkH)W7#M872(W_m8I(Wc--_k<.%-)V;!\K&d[:]J7/DUhdn*2!"i^4P[J4E&d\;H0e)/B%Z:CLiB?2!L*93HSgdD)LhPY`fgFd`[hYn^asSErO#+sm#n=B<HV7#n\dmW$EbR:TJe7XdPc[EdSGA[*^9Z@cUA-o)5*U\;&uX>XF^B^,b]\bb7T4uaoD,fHl_21!c-i@%cRCrVAjo-km9ljOqOrPneD*N5K(JYhn,X@u@bb"ElM6rnpZk>/do:hQC''8IVri4ogmd'@9Ck_U<,AE&aWT&E\>;\.$Tl\tnm\fr34k[>r8mS,q<>)rZVrb&4pWStC=B_/1@j-o4@Do%NkM:Yamj$9H`otI/>IJX[!PZqlH[ksFclFTRSCX0emOs(JWl@"9dB,%jZb?g/^_/H\k,1[^eN9[/(k<lXtXa?8r-1jEL+n<rTHO6>fI\.W2DXuA6gm69BT3I5!4E9apC$T;sE)MHYO8/s('BQ7u*TH-LI!^dt`1Xk61U!:tHrfqRrF1iZTN$g+H>F36#s,@X:^VTt%2q35s#H]C_L-fk==SB<sG7o!U_3jRkO,h>[EmfkglVU*s`7b\!0uO5\N#?(K^7N*ZbQa%;]C\i3*>s5-dg:U'T76mkEN26?Scp\"0#EK&-[S5AZ13?87oHI.[E',TQ;A,DJ6,=YB[s1s9`dN26V15[dc@;S[$r!h+.6eV~>endstream
endobj
xref
0 13
0000000000 65535 f
0000000061 00000 n
0000000122 00000 n
0000000229 00000 n
0000000341 00000 n
0000000446 00000 n
0000000523 00000 n
0000000728 00000 n
0000000933 00000 n
0000001002 00000 n
0000001287 00000 n
0000001353 00000 n
0000003451 00000 n
trailer
<<
/ID
[<d5daaac40f30a39cb41aeb934d2f99ea><d5daaac40f30a39cb41aeb934d2f99ea>]
% ReportLab generated PDF document -- digest (opensource)
/Info 9 0 R
/Root 8 0 R
/Size 13
>>
startxref
5403
%%EOF

View File

@@ -0,0 +1,76 @@
# Les 9 — Lesopdracht
## Bouw de auth-flow voor QuickPoll
Duur: ~60 minuten in-class, samen met klasgenoten.
---
### Doel
Aan het einde van deze opdracht heeft jouw QuickPoll-fork een werkende signup-, login- en logout-flow met een Navbar die de sessie-status toont.
---
### Vereisten
1. **Supabase Auth aan**
- Open je Supabase dashboard → Authentication → Providers → Email
- Zet "Disable Email Confirmations" tijdelijk AAN (voor testen)
2. **`/signup` pagina**
- Form met email + password
- Server action die `supabase.auth.signUp` aanroept
- Bij succes: redirect naar `/`
- Bij fout: toon de error-message
3. **`/login` pagina**
- Form met email + password
- Server action die `supabase.auth.signInWithPassword` aanroept
- Bij succes: redirect naar `/`
- Bij fout: toon de error-message
4. **Navbar component**
- Server component
- Vraagt `getUser()` op
- Toont email + "Uitloggen" als ingelogd
- Toont "Inloggen" + "Registreren" als anoniem
5. **`/signout` server action**
- Roept `supabase.auth.signOut()` aan
- Redirect naar `/`
6. **Middleware**
- Gebruik de `updateSession`-helper uit de Supabase docs
- Configureer matcher om alle routes te dekken behalve statische assets
---
### Stappenplan
1. Installeer `@supabase/ssr` (10 min)
2. Maak `lib/supabase-server.ts` (10 min)
3. Maak `/signup` route + action (15 min)
4. Maak `/login` route + action (10 min)
5. Maak Navbar component (10 min)
6. Test de volledige flow (5 min)
---
### Op te leveren
- GitHub-commit met werkende code
- Screenshot van Navbar in beide states (ingelogd + anoniem)
- 2 zinnen reflectie: wat ging makkelijker dan verwacht, wat lastiger?
**Inleveren:** Push naar je QuickPoll-repo én plaats een screenshot in de Teams chat.
---
### Hulp nodig?
- Stuck op cookies? Vraag een klasgenoot, dan Tim
- Stuck op middleware? `Les09-Live-Coding-Guide.md` heeft de complete code
- Stuck op Supabase config? Loop met Tim mee in een browser-share
Veel succes!

View File

@@ -2,7 +2,7 @@
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 0 R /F5 14 0 R
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R
>>
endobj
2 0 obj
@@ -17,12 +17,17 @@ endobj
endobj
4 0 obj
<<
/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
/BaseFont /Symbol /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
5 0 obj
<<
/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/Contents 11 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
@@ -30,14 +35,9 @@ endobj
/Type /Page
>>
endobj
6 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
>>
endobj
7 0 obj
<<
/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 10 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
@@ -47,217 +47,59 @@ endobj
endobj
8 0 obj
<<
/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
/PageMode /UseNone /Pages 10 0 R /Type /Catalog
>>
endobj
9 0 obj
<<
/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607115144+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607115144+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Lesopdracht) /Trapped /False
>>
endobj
10 0 obj
<<
/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
/Count 2 /Kids [ 6 0 R 7 0 R ] /Type /Pages
>>
endobj
11 0 obj
<<
/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1745
>>
stream
Gb"/'gMYb*&:N/3%.`Ic9T-JY<8Z,@ZOKa<C$VS1+tnmKgIc#(V.9hp_g_ImFcTh)[bL2Hh:dSm;<5rk_jSE).G4Em`HSZhGWpl0#mKV0GRDqF*fOIDbH^`^+qAYqcNiHMgC5/GTkdu-B3#@',:mq'$JZ*s%m9>@-(K`1**a9_1]T+9qUhN-!bM\m"2_%`387X*'';1;g*8dkiTBtd5&S4F$*0f<0ZstF_\of_eQ?rW@T,M\@D%-,S$s33n4Lr9(<r+:i&oUHRceYS2ut"k_@*o?M-<OM#fbIMIV^!GN-`34I+Jbo)LX1n[@CpX(ci>:3#+\?7c#;H[2%h'TeVms+S8rMs'CR7L?fV@(\f;[2C*MZ'$"VH:5<c-?)PAJ&6[6]V(T$$T:31T(aEo`6YNPu)O@]JB?`tXF`&R-jfB?)>AJEJ/@'(#,Jih`Ie@2?1$bpOKc<Z5kDkJQ*D?sP8/%#"Hp?tXM^I81GHj41Z+98n(:t5\ZPmK$Lr%qQ1t\90]<u7[cFt1.1$0.A;GLG(*cZQG@Wg0hA_q"dO&"%,1Za1Ej](p#8RJQ5)Rte8!JY:B6!1$<fO:2YSQ*=-!JN5S65\L)9nt!9W!NMW5?FmiaPp@6>)j5L2.=5L.YfD@7]`eHrb@"Wft^,c(.`X9;qsRCRgG]<]=;-fJO.sX^eUY7om.Q^?7nMpjl`dm?t'uVL="(6+bkUWEV&Nld+tWebTd4<=53AMmGl[SEL+RH/gn%:/S(=E]#3U5%g)91[F/DPZ)dC('Rqnif/SID*Cbn,ZMVL'./K!&hlN)Lm""":!3\dOabD;$1B2709p;EfNs?E?)W&Q`G(Sm8'=JFfpOD2\D,K[?(>uI%/9DREps<L^AYD#l2o&o"U"ZDE0&+'c$CS\oK8%P."p\`q<Ht/.1>N)oKg@X?>+T#h^\o,<(%LfL4scC54>M*s$B34_eXB$;N[SJ/:*cq2C*Gl8BTDVnPg^VTNIJQF`R2qu8s8.4ko5`*cRl#e1^crn)a8sBh1gIS8qe3Z'6RN3(`5H]\l/0.&kap:-!%VtB">ua)<=Ddq(>@Qrc_lK6>C6,AinVRK\!KINn^+YQ@iC[:iC]e6d=)/@Q_9f`8$!pjMP^]Y!aVqGa6T@Lp*.ngK4[J@?e.AqCS4BdNIab"jd4SD)bA2h_$WhY9c$t9LLeR;F40Pn2WOL2kk;><o)F_<A9?o=<*/[<pBVLca6lr'</>:QbH^\\6hmRHU@X\NAtSnr^,^+H4afL.Zl"qpd:n,=>(":ZQmQOl\S<plPhfIIKPph[8Io`Br_%#d2pEr+.18:]"YYUPr;)Z?=gH10apOJ6s'1]],e\mGXU;W*s^QhKD=uiO3=fZf3V$lK&'CGk0"sbSfg$rG,t8rT#tbogj^DHmAka&l'JjXK@F\95t$FnQp\@*JiZa5=f$"(TL9BU.Hek`,oN(+5qBM4n&,/E6/RlbkOCE?X!j.a1OO/3`*,VjU]S/j9i=GFZX+Q;;s_8j(&aB]ZD?&D%i<l6M4*.ohV!JQrm:>BkZ#<"%OjWH*W.LE5Ed(YVB=iGIb)L<>8mP1dR`&pq']?.SO[G"<9]bKGjAq:r:@g9^S2WIZI."@:F1T/>)6=q<S0*qT4_&/%]W$rY/mU("-^2W2fNU4=DH-eU[pIc(18K<,7mENEB9;.IG``Oi0CE4G]]D*^_#XT3K(4K93hW7WCg[2fY+596]:f7GC\g`a)bXh#L'ag64Zk"r<"?m]/K~>endstream
endobj
12 0 obj
<<
/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
13 0 obj
<<
/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
14 0 obj
<<
/BaseFont /Symbol /Name /F5 /Subtype /Type1 /Type /Font
>>
endobj
15 0 obj
<<
/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
16 0 obj
<<
/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
17 0 obj
<<
/PageMode /UseNone /Pages 19 0 R /Type /Catalog
>>
endobj
18 0 obj
<<
/Author (\(anonymous\)) /CreationDate (D:20260331161655+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260331161655+02'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False
>>
endobj
19 0 obj
<<
/Count 10 /Kids [ 5 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 15 0 R 16 0 R ] /Type /Pages
>>
endobj
20 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 367
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1420
>>
stream
Gatn`h+kg@(^Api?H%8IB%6fuo1TfT-D3Zi"Vb!G7OJ!uDr5Hj&d[EPD:7mGDu2\33EVZ]>N[Npf&GtdK+$<jE((N.#^;T!K^Y(%;5oL0Lc(raAh';qPG8/@oRW0%@771RW(`RP"_]C?JSIJ;;r0Tj4S2k^Kn)"^6AX28GT:VJ1*g[j_8t5!04<[G0/;8>%,AsEO\++'M,`]<[8]68FL--H=*r_=R\_[+I.!I$ZfmDGnM<Y:W",D_cYB9?"!qC3p*!RT_;QVApQ6J@;!3brFWI0XO2\OkDjgsl4Nfqd\#Xn0-hfK><J6^do[eNrb70`"`@%oO:&;XI=LjK@n:e&Nj0NMlNj5YI9*clg!=IlmkuaGJ~>endstream
endobj
21 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1444
>>
stream
GauHKD/\E'&H88.0g2KN[)@4f%TSc@3HB<q:!s#9.p7`FZ]HKQbO$p$<L!O?Ymh1?i8U9%)@9I<XIj-sH.db!M'%n/7l,[Z4uCRk&.A>E+HGDG^C-V3_<1p+[^7'mG!*eI5A1q;A)"L(*ugMYq2Pi;*LM#n!i'>)(Wa]!^6<BLoOeG<!=:N;+9e^WmrZ-+g+_GGAeR.1kZ^"]8]9b[-$AEIU&SUpj%oU>U>jo3U,j`gh$/mH(mO2U$EeP)8&k4jC>^T%GZ985fg[m+a3S^p*!g[G1E\1O,)-glY(Q$Te5A,hBHC$q)2,E`Nu)4FBVJQpa:X5R)cl`Aa:am2kn/#LPfN8,bc+JjZtM^C0rIC=(bD*9A'S$UI_5$#(3d9$_Nuff^d9a^@Q7q]hKWXEn6q<Q05LX/2arm,bsjWkLNVWg"h[n%70Tbu!i/R!a]7<jEW;HQ(lK9&^m)Z+YiH(md58Nk(NHNqFdLs4cS\6O@53)>_WOV1<O\=Kn-F^rj$V`D[Vq>B$0HN/0W:FJ7AR4E3('qeP,WU2QB&XIijlk8XH,,T@Y+%q^7XR^F-A>Op`s"diJ26c92qTt\QeR,$$6C/@nLa'@*fNI@5H,N%!j2;*JPuDQ[h$n.e#k]W0a2_Cjd%k/iODQ=ICcBkE@B7Ik5'R&'$NDC''8Q<<CRUWut_ZR2a^@#Vh0="$U]&EA;80dudKSPcjC^b9660Ec[(?(fW)r^Gn-b;^!$K/WpBd9o0!l>(L+/QF3'Pj'.4'<J"r`%H\s-\P@_OrM=%7+_ObaVnSsimL1:/DLM.:&8VM)&rG%U%noShR0WKjk_`g_<U7q@Ufh6h]YYT@jbH4N3Zc07cVO?(@dA7C+m)iO[*6DF/L:7Y>]Y>*WP&pbRT>">J'%MNj0qWr[qm:."+.5Y[c\G2D^"MeU-og(SQ5bp:!\7Q+cW^,7N[C&T':Yl+6"%?<V;b64i">fAUUYG?\#jb^5](gD>_Yu\qdtU[q8`73\*AhE9c=2S`L*28R-b[JSPu)Pp[k)ELMTXJeKS6WL'M;a2+I&fXQ6<nX9PtNL9n-)>EGdbbe8nXLE20M/Zd:^lhVuO"Ur%(F*Nc<An<IJE)m38#)HRW+=.o\f=W$m.IMR9VN<m^6/1hM0:WBiWKI#Mgd3$)Y\2e^k&Qf<t%TTAs@-s<.)V*]ns]2^XJ,n1JM(c[b?5D'25-[6.Hs3q67DLoMB,81N]$&cWVu.;iZN5]*a_Ik!Gp[RK%EP;eR2,#8g)gK6L+cnZF:YXhB!4'cReZ75o-&*AON7mB\tB9=BVUJ!1/Xfi3TVobr2eZV;VqICLpQ!O'.kjHsWcYbHDZ7e5qiO_p3hs/72)Hjjddm*]Z%7>0?+Y[hI<GQ.$f>RjN?T2'<NK?N;=j'Ln\E7OoinbR+Ck83;?^N]\1Z1n7NS_VQ8S+bn&/1'645K=L.!<~>endstream
endobj
22 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 581
>>
stream
GasalIr!dm&H1LYi`[0YUJkVpp/8oMP2a;[aKes"%1GPmPh)=@V398DP^N4rm3HNeBB^:,+ME-rlcFc#IU9^b"i-3epO`^B%t"C!,7O>Q,4=5Ih?HOhTe'^tKHVWBUD3Zs(DAA:8f,?Jq-tVUE-aBH?TZ?=FnK^I+n,PC0^.+3r$m4):[D9=6(THAdXcbld]RZuWD^Jd)-`p`H+8>+l\c8BT>4eTd[1KFEA_"?dT2T&*@eDlmg2BnO!tC$*f9Lno5X5m'SX2M4-?^c;bNUZd_Hd=^3t3gomsZQ'+7@d2Su82OpcX.?d'Ed@FH03nG4R$88h^mpGj;8hVFi`^9e4rn48UHZ:[[T!'f*:DB7_lrbE8LTm]&qJR$XGSnP8&CSCR&\OOfNddh52263R@$R9$'[JFa9-J%PFfU>4ddp3U4Q@$C(VY7)n.W)H\WKtED@?\Z1T#oF?=;G'TE;k<UA_.cI^2%B;;Yd4;=KKZP;T"5:hDjll2/d/H38=1L(d)dKk.9%lQ,XA<hN;gMNR&s#iTT=P/=oT@_a,9eVI)i+LN5B=2AgobcTXeTc^q*TE;fmqBI_^~>endstream
endobj
23 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1164
>>
stream
GatU2qetI`&H4hB`>n.F:?`Z]![LXsP)*)8VRc><UfMVl2J+\Xpm'Ym8Het[2iA-R_9cG*/M,PFq<$FWU-fkAIJ%bNJ4gigCE7L!lj;kkZ,"))JUR04V!!o<780alFeF(rOqZ"q;%J^b;;YdG@#(\_FL>ldFAnkFULSDJ'-LnjD4VY2://-HHdM].+--Nt9K@_ITV'@U>gX[=T^H:tm*B(S<uh4XViF:AUr9%A3p'b6OJ)r^0%O?7bYZg3<d>SiLa-:``a=;'Vj%KZi.Ju:'@+O\$'8'iJdLD2S;\Om"<rTP3/)>lr@#Kl&q\d-fGNUIOuEs@R&&?A'^tm>Zb&DN3nj&9)V5d#c[sLP2`E$\r&6G'oOMh7q`AmbbX3j+)rFLfCH[Vo22tpSg4EYQa*sDo>f%IccDY8@`mM\*J^5MH9:53.KZ:2>:$;Zo9G<":cI_t$h-9Rs5;A;=O^Q3P?PA`DXhqH]M/Z6(.glO'$AlHtOJmD`>aV=L.od,gY;jchjC`%3VZlZ$)8HtpLt>n`I,L_cO!kW9F2#pC=?<SU$AaUL"UqF"HH'Z"1t5-BlsfAp(<sqfi%KG!GCOo0iG(3^^[#8#LP+"QVjhnN?a&%qrHLF#IIMH?mX/XpUE;_s9UtK1<XUFO*s>>;q%*EH;]]dJ.T\!ObW.4uD6K;I`H!gt*jXV+Cp1,u_;lV5GD_\?-kQnCJS$`0R0j@-r4GB*/NV.>SC?1%i"!eGc%uD=rts``A[0<0EY,L9k2%%qOIargcpQF;'@N?:AS#KnP<Mp[-l,-h1oSp7a!tHX:.7-<[I&kf6Hcs;OOYSi-bbA0X/6[+4a5'D)>qt1<![qu2p/^jUsaH'fag<C*a;>%bXWA!Y6B4?3!?)(`6;F2Ogk+UW"8J8Sj-@?Z\QJ5IHRt6XO[*TnB(76d[TP`F0@ri[aj1q1sA^A,Ohg6K-lGP4Kc6j0\/3Y[^V5-6SE\sH%tNpkMQatQdt&t^GC*)S>`&E>2VVYla6KP.ji>U"1pBNp5P%__rfUq.!MT3!MR:Qj1a\H8E'[pp'fTg2c(d]62fGTj0@l&AL(7d::$Wh)V[m`"B=h0ZeD"]1PrDPg[DE'P1s'5m)>ODaCA:bq`<lWlu1@A9,1r>=2^@6b1YG1bXMDse&hs>8hS!.(QeA!d/~>endstream
endobj
24 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1414
>>
stream
Gb!SjD/Z1=&H88.0h(Au;\^uE[Zd5PlfI6(8njP;Xt$jILBY\b"r;)Z@],OlHY4-^S:GZ@l0_1\al[/@Sp=pkFr]_ojlg&9!B?R#rQtYD#Lb[mY3!Om$8/4]T+7BnADafkma)ukkigY;\G\VUd:]L=m#Bd$=T,"4s0r<bEF1:/i;qVRG2,b)EmY!P8@piZ%QYDmQWMNqJK(,S5jfkeVZFZ9f,MPu(sh.b7=GNkIpi,*K(BpeD3.58MnHY/;S.H@WWc^ifr6_#93O/aQ=Y4\KtPLnN.kreI._c<NpG0p^:tFC:IW-r`8I'jG0ne!:Ye*<(_ATb^kIrYSd+\ma:\>068%.rSi]f4V*`$;I"=uND36Q6j`i-#AWGbT9>^tr,#,lJac7UlcF5R+<sfi6Rb#R19j1(%[4]LbZoPaZ67/IlmnpZ]"JXHj8A8n4?:Fd0f4n"*O&B-o='@Ap3@ePFo7Hk^/)4#(Ia;VsfcdG4ZTu]0GqS&/9NW#/#W^%#Fn[ggC/hO"@E]5=[:s2'c^1U:[,UcPiK^t\juJ]Z<rJl-J#-bIZ68/#f"<L$T5)YSS7B1:.$HH49U+P49sVo"ahCRq:UH`s=>1f7=>EZQ=T6*aQuu5B?coSFO_Xa`?5YbVam$.EW.e8!:^LBkl"i[dl]Hm(7uF#Qpf;"ME>u*BOOo"LUoJYGmug,#1g$i@$S1,#&]p:A;cNg6g5JLB'sFpAlL"rcrRT[<!U7Iu)*VX%R?mBZpX=_;-=].`#jb<8Vo%-Le]Nbs5uMlrL(QAuc0k/Q=t,qQ63YLuE!iIND&bt<e6Ifa?Whso@qj5H3/3*gbb^^1OK;,b/qqLh)Q+$]D.2Di)N_>4c/'q)H-2uJV(pZmPk#9<;(dRfqdY!3lnP@U-)CIIn<-=;f"3fLfl`KkbX_9@FSDH.@b^o_9>o&lGh9)7Vu@_#+S)=e+i,9N&f$+1SEff'[^Ft)Ce$T<,fkb"bj'4)i1@mk:=f8U6XcQ><#nZ`<);U&WYu_fYTsl*8k\Ko%\&1AHo86tR6C&<\uB19Jai^BK1b*$&IAl]IXsKU7HV;fYCA6U+dXX/rGlqZ9jI9o^">hpe`smib=TXUOG@j2=V!GuKlaGZOq,>ai`pC4"+hq6?Au^2]ff>$`o"`&Kr.@kS&9-Q9[7Yjd6Xp%g;X2PoH(tC,rd*bb3MVkRkU!9k+MUg/l\m'#OfJg_geH.Z7H)Q^t<?.`d[8a]8V27c`gEuB`GNtG%c-l#0,=K!JrYh\fUOZ:F?lO7AlErAo)+Xm@[d$C2ZK^@R0"ib2_DCN:h"oTL!QqFoOp0XV4]H6tJm8#_g:_c^[s$eQ8$(I4SsS%kU5ZH^`I#lTr!sbj@JmJ(/W7%cU6-:^_Z16!R@["`sH(j6Ib7d2_Rts-I6*AdJtH"6fRN[/~>endstream
endobj
25 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1156
>>
stream
Gb!#Z;/b2I&:XAW3!`XF%$P3^E]0`%$`$FH!a1X3#5U%BJWe:k=,Bm*.X#G!^[F]rYnP5npFou3CeN9++7Hp**YlJH5(WZl$\58]p`BPC`irQ2&:,KNDW#gqpc8]SQKOrc*aXL90V1tN8U,\Z\A8]b$<L+?9P[=>R::f22-9$m(gCZ-Jk8Ll"g&BR06W2W=^]=/;CW,7>^V*fOp*fH2qlUBhKaGPMn`Zi,FIp22qBb#'ruBbX'3H19*$t,j^L5TT#%kC*s.F(MFtc.=>6?H";U3;F7gp)TW[cj1QBSOj"/O5p7Y)Z<GSQH$bY<QHmcA<Wa[D\k\n`=G9V+M&h[(?]P4O@,?HG"YTKrWX\gt,GP9a(dUu_5-&YlZA/d$EhY[8iQ'1G?ebbl3d*L&l(c!G$*3J]u_Q$*<_fCR"E/-!qh?U'9lNb=I#.+,`[$ZDNWR)L[g*F#jW>PpQQ:%e>:>C2c]u5?)*^&9\.O>hrN5'/J%k;h*esTV0<.#CP\^&o>08G3pC8WWff_$i3=!i+.DG'Dd7s<J<f)oGif9Lq#8uO(`?<L>I"lc"Bg,[ko$I*ZW_6HVGjg",`;5(.X+LaFKdtBqNS5[4q=Fi8@mB5_:LOsBu[9FnQCg>C]6Kqf9o+'J0AAL!OVVUU!2`[t8cu(+MieS$<UW@#8U6td[E.Z4('p.FhNXDq%gS:e'*A:9T/4TB]%8V5u`c_-W[E)L(q(ZfPbP2It!32d`^VGNSKo$pkk-gmUI>dt,2]$&-5sLZ'CQ$>d#F<t$gY07[7JWOl950!XVV*kEa.`l91R4^./7s1Y/V9=aS&*Mn6UWe*/V<7[,a^Pj#%)/K0EM1giVZcBq_*'ibi2=",rtkk`GN*LPJ6G&.>\SirVrs><,52u>n^^ZpO`PSY?7$!1>D6CNRp>]H5i\u7%q1.UJ[58FiH;m+SOYZ-6H&i[Y@#1@Ctg$Ui"0FUH9@,ZL?"Nh1Kq0>iFR&ofUQO95rT#i1+t<l8PE/kRbIB&cL%aqmHlNhS?K\NkWkXpf)_9T<`ML+f9;ePr!=bG#Q/GpNuZehNmG;Gb8LR.A?eXa/$4o#?1LHPYoo%3PIM=lI+qhe#09)kqT/8oYuR0r!lY3Bna\d<^V"%\?c")V[/@0Os^S"V==3[%/p9pBBc;~>endstream
endobj
26 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1392
>>
stream
Gatm:D/\E'&H88.0sXba;Oq\&&kG!T<N9h7.@900mSd%aCO7e]R9b#\9gFO7hUsae3uF.Sc:B(joCC,n4O:)6*W10E4.ZIXNPYSD"4)bm"ME(f@=/M+a!^nWGT4gKFFp_9G+j*t39O]]TH>dJIb4%BkMQIZS`%p$k;(BpM"Cg[HJ:7tN+\Np#;<#RkQIe<T,!knVPJR`;dQK);CTm2,o:2JIY@6P)T6:dZiE,TH!;CUg^6+$@RcTVaXn+cUgqW<0Y@njSVmhH#T!]$Dh,JN-&">)&B_WJbbl>1'+.bO$VD,H0+/C)8Zr8HP#V?gXC2PO_.R0Sm,hI3b)MB$<CYPprFhS`%SNX-lc\KY\W"b=Pc0PISWe&h$fa2q=fb\u^/GQ?%qi-#joBd<cD1M=AJjj@C"!SEQHtZ?p.1a0]VgE/3NXE4"<s0`#(pYMH3$](jG@EZPR].me<*nVr[:u+kbkGu.7uUK343#WL^eNXT\Ab/c+<^P:fRjW+SPpH,."8a)&=6B*Nn\XXg(*HPW*,BLR,djZfMV>D^riVmWLWFj`;pJ]6BU4N]#P6U7X*,b'BFR[ZA8,5W&H)-*8-9BHp!uF"Vd4k!f/nE;mNL*=7*\jq/2DUTX(YSe8W'\NqTZA+#(Pf,=HhB2aijgW7u_nTubJY;'/uS$lFA,&Y)bL6ArC\RN[Hd+-D^UiMHf'n$SDE/:>@8;JJg8pn#6XLH$7>YM-n,29Ka#\d,FlI:'a-Y8l:`'m"H*C4+h0ZiP\*_WK<ajQ3JHD6WiVW^_-0uCrtD5YLk*:";#A;K$u,N'c$$"qD@Gb/$4cR!W*QKS(ZeSYU9-<UGQ5upJW.7(lWnE=Lr[d^3'X_8t@*+<V,$c64M'l.>83cmisHp)GVdi2aTRaXFf0K?@u:"fOf[FS+dV(Z?Ua,-'_$<f3$*Fpk37_c9%-]ah__*!%LPXTA^.(,h_Udhf;];I*$!>Go'J7<5>HDhsDA:s+!]Vj*9Ni&4DJr7%R3ki5lp,7&u:\WZLqNgU[P6#SdNo(.i#=jDjiGT1>FZ,>UE-&>'0%<5hH`/JlKkU$rpA0='<]L'X1a+tDbJOu[/IRF;pk^Jp`NX@XC/ala-m%sVlK@V3SFP`a9ad#OEh;E8h]&IYRe)?rdYXf:MdC+HD<C1ZdMJ[R'(T"s\L1(FIN)f.V^n,JOc_&iIB[PB#bj&[-/)Mj;;i`kQ0cD\<fZoIB)Mo!HIpXjRkfc,RZE/piVMm!lZ98P1u"NcIs2Yqg_#6A,+W3oitskO1)=#dV^/G#UJ(bCi/tm0bdkeC17$rMPud:8GOSess,Wad]lS#48a(hlH0o3ma18c>*O#2hSn<8")?K.fgej5RQ7iKTfB-YG*:nTr'+4&&r<?ZPJ<PH`.e;!i~>endstream
endobj
27 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 317
>>
stream
Garo=;+ne\'SYH9.h4pTo:^$P=<)3B#l:?,@NP9JgG\@IBo6UE8EkU_X`Gn[2fKpqF7+6>3$NkId'F(hQO"Znp_7`V/cl(LKEoWU)*^)j&]>k*$'Nsm\/')NF6mpYe[Dn$3N)E_=^ocd[fu@:J"k`-."\N_6f;QA9RFPl^8u>semLY45e]NjIB.e@GHTB@A;\@IkgA4C@5+O#reX<Idk%IrMfs0h(923+/"%a:P:\66Wl%,NY"ejf-0%VG,rI10FQWZ,A4cZ_/AhE=D71s;DO'J3Y8R%\T#Tr%s2_tOH?+AF+/eq\G[Xue01B_i~>endstream
endobj
28 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 757
>>
stream
Gatn#h/8]I&;BTO'M"98ftANj+1LNhj),c7A37#u'a_u+Y_d'JU^P[ET>%p1*'(!f\nXN5L=r!dc#O"/6ZMDV2r+IH'W:+TF9D.>\trQ&!=8(@lfjH\9hR8oa)^*a,L*Yj;rie>AiPN\g!EE2BT<L7,?#ZA[lmSehij`Q+^mY1P_:CfqI-d#^qR,V&PH?"2\1`A@1^D1!U*V3\!#]JS;0(oI?(!.^Jm'O<RKGqW>_=B&E1>Qnhau:=F!j8P[V2,O<u9Z9)MC%<$dtj6^BRNF](")@4Ru22587oH&8K5<tV^5?=-b?c_-q,cgBn9Z!f_TOV].?'ZopCin=tRgTK\LQL+?RgbGG\Ds!RH[Pg_+Y\]p#jP^"C(0uFF+q*6KfJoNL"<F*<7.+#OK1sfi3*RRL?V#^'9GX<tYMS``jjfuVA5!<lZbW2<iC)e1C0%RG=X?LVK%[L]\BF/)*9XY)e-j?N@M/Fb:Vm5DDco1^U3L"qN/Q\tG["aCVhsX4'cDK>OcX&:dgJi%aQ0\SP.=bcB5p-W^lL1?;+qclE0Pfu0.at@e[B#d(#?k,UWhQ%pIt,[;U-Q^lM=pb;`Wg`HT""2(g?@rNA*2\(*#Q4qU:+dh+'^j@4=Z3(fZFtCYo$CUUFh&8cR%3`k(KQB,T`gQ+.KkfGX-J/oK]uB9-Q:$b#*n$c9D%,X>\mZD+qT(g>n%)L$%$Z5qC@\Pl=qR>um)]`.sXBV[IdgTtcZ*-;[T4IsTCiBco[JQ%$d~>endstream
endobj
29 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 735
>>
stream
GatUq?#SFN'Sc)P($Dae<12D7E8YW!g9+>_Yr1i"`\\QR#VJlB`<ipOISKs#TlpeT92Uppm.^&?CR]oc)f=H\']:HmrXB%R+IW?(L^2<j>XuKb4hAp\4lUd=(B=sN`:mNgp9S2E!ldAFL`8DCJ,pOto`IY2ZX_E(R4,HU$u"9aKXJVkLJ"!M(5iQVSfi>\.t4bj2-'&W/)\sXY:"RBS'W16DR%u?jpfld(:fUWGmWO&HSe!Yn\q^7,9,tl3)+X)OL9\Ke4q70)F6u'<9T`<"h@07-=G%7#Ee3J]`,+=DuV%2c#X*u8rpoH[*G="=:e/[<1!!]N7T0#!_%t902ZnCE#c_^rq:XA+kn=`7`bp`'FQIbe/;jPO\_'SV>edT"Q#9(NuA\a9RlU7<@)-WBVeW8JW7<n;G=;&Kbk_#nL]eR!8N!K\GdCtbb3i;%<p0RRl0L3_SRN`)P<PU>*Fb2$*i(#&:QI%q0<0*LUg0bp1jm;He,FJp#!-5+,8O';n)Fbc73>5[*D<YXb3Y&Y4fE46Z>Sa`ftPSeN-P5TAN`3]\V*K<A#W8+N2.@-X%3=1_fm-J$2*.K6:^CA8"Fo\<f?gQ86Is*8\)o7YbaSm$)7EKJ=uo.oN%VZE9IU8TpGKQfWT6SLM8;^uBCOq;<@WZ7"`\iDalX$=$2._,@<:ZPZ!%l/414i9h^-;Qoe')X^lNQ[]n@''rUV[D<]$V-d-Ep5a+Tr;gEmk@a~>endstream
Gb!Sl9iL(A&A@Zc3]HLSQ8SO4?:_@.$WZNp_;XIu=DHp5dYsn<fi@k@C!!e;ef&AI7ApA+VTP@!manZ"]J0MXITc8$c2iA9_;L7QF's)#bd0DK?,E5/PCH=Y-ZDDu#Kr*CGNaQ7FR("sdh<%:?j/o1a<7cU.geH/,%a9k0ER$\Kcpht7(E8A_S?K\4,c;+,3B,-crog@J`G,.T&g3nE-qO5^SjnGBEbI'.*^k3K$b6J<E'pfodlU.Tb!n<HJbmn0&U!2W8jjT)>n>DN2Hm"NYB9J([q[T0iJ7A+(4^@UQ>'P)aQXE[X?[7T*DtsXbrq8D4k[9$H^-;#]`9\G#A\SjN+en6D$HMRu`n91juTt4udBqf'<WEHb&4*^2.=sK3.4r3-3Y*S'L`I/.A/H\='/9/>'2'[<m#u0#KfKC,r=rR6p?qKnY()r8>e'4jh_Y_'`K_`@EaPZq0:YA]DFRLXh%C:d*`Am`^XjGf0Ab=$@s_W+A57??T]nFLJm9g:lZ>0RP0:gg`UfXIT?:p1a`aNS<stM>U@!8VRUZiW30Vd18;l?Z8nD@Km-^fMWFj2gC6a$RN7eV*DqXoEm'VlF"+%!J<eZqHXQW9Di0n@t.PM?DLSX<*RXB4Jd7e"4&`U;H3oY(oe3]1R'T0.F7,^g2J9-g(CjGBY"1[;_)h<e8jpcC0>?Z_tNtF=M-!RW(Hs=n=i0NAC!eKbA#]%Fe!(;Rn\.>eUa7)OOOhV(j6Y`fC_]hd(8S"s0tT!j[-,qL$qr"3_K@r*q`%,/,q-_ZVKJIQjhQRpGJ7X/?KXa%n7[$$3KW:#R!Xc?r.sB<RE8+R;2L7G\fq6C'=GN&b(g4J@>abhD#V`'H+hP!-jn5c;=T2pAi"^o:4^dnHOM^Hp/\m'W\lC&uF9b[\K'&*7)H;<Am2"*4^,Z&snE=mO4K,:hdOT]CYO^_Vf*_cW*i7M](>\9&/J%nQ>9QHPsMfK6Z2;TE]fVk.I/TM01hHUDs^oYU$hG4c<=gopu,d?TbR&gR,()e5KpdcK0Ah7.#[rZ?/EDOiBO-;nCAR8ZaL@8V-^/HLfp@T`b<%8K%XVhIgrH')2bKg].Xq8k+s0?mZq,j>[c=Kn`u4?d!q5*m(oM1oBC!rfcRV2fB\J[GUeorE5-=ViK86=*hHj)Eo;!<$Io>PD]!*//4%KT1L1"aGgDh1$HYY;h(dW,NQCiGfn9KEI05[4J&lBYHe5*,-gHCf_sf`ffuRFgmT^aJLX2]e4cN!KtEN>Zt\-NGa>('MJ-(TE$Y!u-8c$SR'tY[]sF>S4O#0eHe?(IF6nB'K#k>!V'7(gnt\YP.H5bsEn5[^6h@T'^NWOi&SeN,EH?OkKhGG+7RmuoJDRQ&i0kpT;D#ugp[rX#Da<#bG)"<)0HZVB*F]5aMIY.#D/BS,rrHGV#Ts~>endstream
endobj
xref
0 30
0 13
0000000000 65535 f
0000000061 00000 n
0000000133 00000 n
0000000240 00000 n
0000000352 00000 n
0000000467 00000 n
0000000672 00000 n
0000000777 00000 n
0000000982 00000 n
0000001187 00000 n
0000001392 00000 n
0000001598 00000 n
0000001804 00000 n
0000002010 00000 n
0000002216 00000 n
0000002294 00000 n
0000002500 00000 n
0000002706 00000 n
0000002776 00000 n
0000003057 00000 n
0000003178 00000 n
0000003636 00000 n
0000005172 00000 n
0000005844 00000 n
0000007100 00000 n
0000008606 00000 n
0000009854 00000 n
0000011338 00000 n
0000011746 00000 n
0000012594 00000 n
0000000122 00000 n
0000000229 00000 n
0000000341 00000 n
0000000418 00000 n
0000000523 00000 n
0000000728 00000 n
0000000933 00000 n
0000001002 00000 n
0000001290 00000 n
0000001356 00000 n
0000003193 00000 n
trailer
<<
/ID
[<c2b0ca93638236c118c39bf1e4748e63><c2b0ca93638236c118c39bf1e4748e63>]
[<9b100e9ac191c85172926033485b5ccd><9b100e9ac191c85172926033485b5ccd>]
% ReportLab generated PDF document -- digest (opensource)
/Info 18 0 R
/Root 17 0 R
/Size 30
/Info 9 0 R
/Root 8 0 R
/Size 13
>>
startxref
13420
4705
%%EOF

View File

@@ -0,0 +1,229 @@
# Les 9 — Supabase Auth: Eerste Login Flow
## Lesstof
Eerste echte authenticatie voor QuickPoll. Aan het einde van deze les heeft je app signup, login, logout én een werkende Navbar die de sessie-status toont. We sluiten af met een eerste blik op Row Level Security (RLS) als opmaat voor Les 10.
---
## 1. Waarom Supabase Auth?
Je hebt drie opties als je een webapp wil beveiligen:
1. **Zelf bouwen** — password-hashing, sessie-cookies, e-mailverificatie, OAuth-callbacks, password-reset, rate-limiting. Een fulltime baan.
2. **Auth0 / Clerk / Cognito** — externe service, prima maar duur en je data ligt elders.
3. **Supabase Auth** — zit naast je database, gratis voor onze schaal, werkt out-of-the-box met RLS.
Voor deze cursus kiezen we Supabase Auth omdat het een directe lijn heeft naar je Postgres-database. Dat maakt RLS (autorisatie op database-niveau) mogelijk.
### Authenticatie vs. Autorisatie
- **Authenticatie:** wie ben je? (login + wachtwoord, magic link, OAuth)
- **Autorisatie:** wat mag je? (RLS-policies, role checks)
Vandaag focussen we op authenticatie. Autorisatie (RLS) gaan we in detail behandelen in Les 10.
---
## 2. Supabase Auth in vogelvlucht
Supabase Auth ondersteunt veel methodes. De drie meest gebruikte:
| Methode | UX | Geschikt voor |
|---------|----|----|
| E-mail + wachtwoord | Klassiek | Apps met geregistreerde gebruikers |
| Magic link | Klik op link in e-mail | Lage drempel, geen wachtwoord onthouden |
| OAuth (Google/GitHub) | Eén klik | Snelle signup voor consumer apps |
Vandaag gebruiken we e-mail + wachtwoord. Magic link en OAuth zijn beide twee config-regels extra.
### De vier kern-functies
```typescript
// 1. signUp — nieuw account
await supabase.auth.signUp({ email, password });
// 2. signInWithPassword — inloggen
await supabase.auth.signInWithPassword({ email, password });
// 3. signOut — uitloggen
await supabase.auth.signOut();
// 4. getUser — wie is ingelogd?
const { data: { user } } = await supabase.auth.getUser();
```
Dat is het. Geen complexer API. De rest doet Supabase voor je: cookies, JWT-refresh, e-mailverificatie.
---
## 3. Sessies en `@supabase/ssr`
Een sessie is een geldig JWT-token. Supabase bewaart dit in cookies. Het pakket `@supabase/ssr` zorgt dat zowel je server-components als je middleware automatisch toegang hebben tot deze cookie.
### Server-client setup
```typescript
// lib/supabase-server.ts
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
export async function createSupabaseServerClient() {
const cookieStore = await cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll(); },
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
);
} catch { /* server component, geen mutation */ }
},
},
}
);
}
```
Belangrijk: in een server component **mag** je geen cookies muteren. Daarom de `try/catch`. De middleware doet de cookie-refresh.
---
## 4. Server actions voor signUp en signIn
```typescript
// app/signup/actions.ts
"use server";
import { createSupabaseServerClient } from "@/lib/supabase-server";
import { redirect } from "next/navigation";
export async function signUpAction(formData: FormData) {
const supabase = await createSupabaseServerClient();
const { error } = await supabase.auth.signUp({
email: formData.get("email") as string,
password: formData.get("password") as string,
});
if (error) return { error: error.message };
redirect("/");
}
```
Dezelfde structuur voor `signInAction` met `signInWithPassword`. Server actions houden de wachtwoord-flow uit de browser.
---
## 5. Navbar met sessie-status
De Navbar is een server component. Hij vraagt de huidige user op en toont "Inloggen" of "Uitloggen" op basis daarvan.
```tsx
// components/Navbar.tsx
import { createSupabaseServerClient } from "@/lib/supabase-server";
import Link from "next/link";
import { signOutAction } from "@/app/signout/actions";
export async function Navbar() {
const supabase = await createSupabaseServerClient();
const { data: { user } } = await supabase.auth.getUser();
return (
<nav className="flex gap-4 p-4 border-b">
<Link href="/">QuickPoll</Link>
<div className="ml-auto">
{user ? (
<form action={signOutAction}>
<span className="mr-3">{user.email}</span>
<button type="submit">Uitloggen</button>
</form>
) : (
<Link href="/login">Inloggen</Link>
)}
</div>
</nav>
);
}
```
---
## 6. Middleware voor sessie-refresh
Zonder middleware verloopt je sessie na 1 uur. Met deze middleware blijft je token automatisch actueel.
```typescript
// middleware.ts
import { type NextRequest } from "next/server";
import { updateSession } from "@/lib/supabase-middleware";
export async function middleware(request: NextRequest) {
return await updateSession(request);
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
```
De `updateSession`-helper (template uit Supabase docs) leest de cookie, verlengt het token en blokkeert optioneel routes voor anonieme bezoekers.
---
## 7. Eerste blik op Row Level Security (RLS)
Tot nu toe was je `polls`-tabel "open": iedereen kan alles lezen en schrijven. Dat moet beter. RLS = autorisatie op database-niveau, ingebakken in Postgres.
```sql
-- Zet RLS aan op polls
alter table polls enable row level security;
-- Iedereen mag alle polls lezen
create policy "polls zijn publiek leesbaar"
on polls for select
using (true);
-- Alleen ingelogde gebruikers mogen polls maken
create policy "ingelogde gebruikers mogen polls maken"
on polls for insert
with check (auth.uid() is not null);
```
Belangrijk: `auth.uid()` is een Postgres-functie die Supabase invult op basis van het JWT van de huidige sessie. Geen JWT? Dan is `auth.uid()` `null`.
In Les 10 gaan we dit veel verder uitbouwen.
---
## 8. Checklist einde les
Aan het einde van deze les heb je:
- `@supabase/ssr` geïnstalleerd en geconfigureerd
- Server-client helper (`lib/supabase-server.ts`)
- `/signup` route met server action
- `/login` route met server action
- `/signout` server action
- Navbar met sessie-status
- Middleware die routes beschermt
- RLS aan voor `polls` (twee policies)
Als één van deze ontbreekt: pak de Live-Coding-Guide erbij en loop het stappenplan door.
---
## 9. Veelgemaakte fouten
| Fout | Oplossing |
|------|-----------|
| `auth.uid() is null` ondanks login | Middleware niet geconfigureerd — sessie wordt niet ververst |
| "Email not confirmed" | Zet "Disable Email Confirmations" aan in dashboard voor lokaal |
| Navbar updatet niet na login | Vergeten `revalidatePath("/")` of `redirect()` aan eind van action |
| `cookies()` errors in server component | Niet proberen te `set()` in een server component zelf |
---
## 10. Volgende les
Les 10 gaat veel dieper op RLS in: meerdere policies, `auth.uid()` overal, en het verschil tussen `using` (read) en `with check` (write). We voegen ook eigenaarschap toe: "alleen ik mag mijn polls bewerken".

View File

@@ -0,0 +1,175 @@
%PDF-1.4
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
1 0 obj
<<
/F1 2 0 R /F2 3 0 R /F3 5 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
>>
endobj
4 0 obj
<<
/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
5 0 obj
<<
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
>>
endobj
6 0 obj
<<
/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
7 0 obj
<<
/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
8 0 obj
<<
/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
9 0 obj
<<
/Contents 18 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
10 0 obj
<<
/Contents 19 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
11 0 obj
<<
/PageMode /UseNone /Pages 13 0 R /Type /Catalog
>>
endobj
12 0 obj
<<
/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260607115144+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260607115144+00'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (\(unspecified\)) /Title (Lesstof) /Trapped /False
>>
endobj
13 0 obj
<<
/Count 6 /Kids [ 4 0 R 6 0 R 7 0 R 8 0 R 9 0 R 10 0 R ] /Type /Pages
>>
endobj
14 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2254
>>
stream
GauHL>BAOW'7JK9fVabY8dkaWqpP#dUoiCY\N+B%itk=>,X`oOnffseofW:Gl=*=cSBMd0V?m9]h6X_+(LZ=`_p%R@:@u;*HMS:"`p%Y9b]eHGI6l1t#QG2P*qIA_aRN`k9/nqgVuKUS*u#EB:71O(;=$bI*;S[9b_b@*-B<4G`$-Z7c+Y2PEh^p?@\90X"^rD->OOQqkLeL4JhTk.f=1Uq>.qi\>OA)\rIHZH@'*;><6obn(.82Zk&g::bP*sfMjfQ=Z;<$GGBG>]Mjj&nO6HFpSr'kre4Ps$kk5,7I]GE[&aG-+9F.DATeAf6eS8]M]Lq@m8;Z^>GRXd;#pF6;$9dN>8`0XFZg\GkXNLit'Ne%7b4(QRg^Wp84'79Ca1(L\9a:P(A"M]b!RkhnA"hpO/>6Ag@u6c,Dq9tC#!61)K6FSM`8+%,\!p(r0%@u3]:IhCoI(o$YSb0T%Z=ib_01jSn4\YV5i_1`c97!UU3P:5*]\-)#4^8fT!Yj#c2&#hHiMOKhN,0NC$p/N0=G-o$_2aPQu*<(*?MEfRF*=Frn+`1q@iQ.F_W&'GafX6To4;1OiKVBTVCaA8Jeu!-8oo!0]%k<6O:mIJp173iZt%."X(49oA\S@8G[U1f]UGT*mu.pHCU9gHLp,JaeCE<K389OOld?gamOgG]*"]"'E/lZk3EKgB]g;YGu%u,H5@4\&f>20p?;\,V+o@9[R(BA?G0.$hsW#-X&XaZdMQ4=S*eD5^):3XQ)n^Tm>*/,7=B2<Qb<)j7f8RKe\bEm.\,<2:=jT3j\aKk2#CBHZ+PF(`X;,tc5!'YX)(OGorp$7DK0'B:64%>a8<6:Rpu$%aAr%uMDQpLD6ljTBdM&mp,L6'Q7/V^13%l;g-+lI[F2KQm:]p&_m$DJ1PDp#H9pGUoN[;p.`G!qo6UhW5#A'$ec99D`uqaACu%hdV0*I>G6;c4`o+j5?VEJ;D.6WYX@mJm$T.e;$9SDHM"d9`T8O?(7_/[5PeqTSS"-:^'XUo@Ut:Tr/75A-F6FP1?\k/(e:(Q\D(7^e#@U`YCAIo:X-KBi#1StgZiT<_)RL;e^krWB.j58RLiR37CakKs2'c''%[LR'eS3i=<%Ctu%fM7SXH>B+V)-9%(ALg1ji<co+pI1ig47"*)Y@'(RSq]Yqu,rci6](`P\(YBk%S"'o5"[l-Ne1CEYphLR['(cof]dVE40!+o1IX?^M2rBFUEc;a=`bHLZK$_W9M<K>'sANs!lsJED=mpSkprU5\0qJ?`h)_g:\Gnd24:T9Hli"kN95MgdC5`q@lj>I3+crR=q.)Y4&eNXR@/Q&FGW4KCE@Zo!c\P1fhjjf[/InFCf/tPucF)Q.+,)XVN%B@2Ic6V3\o@87Ig]1'?(JRE`>e^!t=K:!tR3Vit[Lm%_(W<f0MBQRL<dN&$gE)PS6u%O]Mi/?<?BUe&to[,nkHr#4Ma$c(lE_"Aef<eK>Y;u/3N:["^F(U@)^E>JT*U+A-HP\W8[L9"57.HS-19m$mL1^N,U>,R%`"aso[ON0A3^'['\"o(1iSip[F%a$/%lRH2jS`tJ"OQHff=]t`&"W/3a_)0DkL6-0P=c_'KKM!p:X+(T1JiiGUEO#`<j<>)>l#i&?hJ9Mm@0)'`VuTAP:PO9-C:),lOtB6"H7Eol74lUALS?@jVEodpY>eNJOYNU@K&2foB97W\=W.t$YFtrr;HB[mTqHK87YP**ZpFZ3EGic3b6od(O7in^ZXIqM4<6o*!@>0knU=oL*">33l,*qRH2KW+hY)n8Op#t[MG>XXURF@)FaH9'-@0@>^R#3Fi&*F7rr`17=q^[l2C\Kn%3fXMK`C4UK"*7q/b>QJeDkC)qgSV,Z$:AAE<H^an+.;(`;F;F1-%(CW)OXTDBVhrNZ`:%gR7s6NWUYdBaT9#C=6(Hc#Vn$?6?`pldRe4F-JUH>O!iJ:/+NiJ2EBS$3i/$lknB48+sH=Hij<4-@BN;a.3;FK;NEY.I4'$6scN<WlD1sT7ut>Ihku34uWXk@=b9n9YXD&!HNH4C_)m@J`8C#j0Ffj>$TG?`<OO/Gn;NT_XZ[R[E]$`*SfkGq]Gk)PkNPgJAHE%#hsFHeaM)!j*-q#c$dZsUmq;MYN,4:*]Z?5.dam@oc5ND0su+9aW\PKVq&+t:sViL\494'BPQ/`M["U=n:8)no(?`tR;tJ-V3oH+/oc?WJZJGsJmh0.p?f'Ys5E#J*KI-pd%47rR=+0MdVhp^qEoo8!"c#tAH~>endstream
endobj
15 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1624
>>
stream
GatU2gN)%,&:N_Cm%]`t0EI=ZQqjQ%a/SIUD3A'tqOiU/'L]d38;.j1j%THc$h,LrZB_kqCfoV)ER%l#5f(Epg;!.bi7B]Rr'l5m(sdd0`/!jt]H0/,*PscDr;_X$>VBPO,1$0POZ43]SFD>''SID<&c1Dn8q`!'0O,EK1k\33r1uc3K\R#j%n+cp$iXhQ7lY"g[8-kLUPJ/>I)<[W#]J*6biXQ'd"8^%HEWo9>T/7rN)q_0oBh<+\B#=``eurTiMJ)(]Vo5T=9tgkE2N&&R]f`]!lh]'IVC^mEp@^s'R7.RoLbB[Q-UBsmpr.K$#If$H]6l-=?I9c"KmfJ587KA68eu>R/).mc-r-V@\3s`9Un7j34=a4s/]QHE6O)Q$\GbehhhY/4uNOnW+qI@eGcCDNhfelFiH@in$hmj86CC=>.\j>-,$D<0$h!?i`c3h+,/omGSiaT&!IVnWjCdfKWY4AWEqp*[OXUUTT3)OV[$&-CTh9C?rj5V>,=`N.$_V<@Z>5d`!l2F"'*ZH[bhr]AC/HQe%/_uA9=F+8ZGP2F`gPW-SGt&Dq;XoGQH&sM&[!JDX=P_Q)4m\SRhEI`Z!i!/*iHM5d&`#Q4m3;Dt*MP+]Ja0@DO_cB?tb0^_2C:c5dP5VCTd=:g`uLKkkLZpH&i#=l0c33i1=_`.!=]kRC5+5kEZGHed/5Z9$R$X];XR#FWi49J@R;U=Mu$'\0&qIb77(JY?*EXdVZ5le;iF9$\2N)l65UjuPLT<2.eRe<d1sA>eD\'>V+r&YMjV59-<R_^lJ^,:oHC"a:juZrBJ;4Kl*$d],D<_<9<?1a4'lV%Lbloj]407N*8r"Y:7UO'q)E@G%H]RnFc/\7X\Re`W>6f6>."):@-U:-V5N9&(P^el=rtTameXFj;FJg`#hkqAFB4Hq>>N66WdD]7Vn`fIKGU&-E/]^tPq5fF7f"cGFiPMI%eo/eO.U-R'.tN2%X)&r#*fE--,PS(K("=Z7]sVH(;1)97#n<VnkcpI%C.L2PJDE[,]1A\FC.=XkW#5Y:![1AV@#Pk5QhAQ`o0i-_$,q!ilU(]0'p(?X(HO_6PH-c6iXQe=l7/`(W^=^jEbo-ATRi];j^FYE5gd<pi#Y5A7=O%7._;6?GQ(PK`"TcHA$iV0cu0*enf^5%sq&FP83=]4I)1-,]qo18("-D$";Y]n;;WIsROPdp"U6trm02d1bb0dWbtGr#9mq)u@;3&>24"^b[b/,9OmgA(T#cVLd+=M*ld@f'0HpGs&cf0Xp5.mn!D.K\eqE/jJ>3`TMuh!)FDJi>[%`?=YQXt$r!+Z?7PXa5]Pq%At`#i9qKUOLP:G&c"Q"F$lmEudo<ddL"R4S=LH\ker"?53#9cee/7=S#kem6-dnXK`\InWo$Qc(XdoQS.tZAQaCAN7A<1/LW^X;-RF&E/.h>,m>q9\7KAu2j$$Ra'tM%`5]%(nLu7VUo.rPAV'Bl$IMumDZU`BNjW+EENj\IL,OC<G1a/T4!5cXc\nTc`t;>sc>6I;fgJX?k3U`"5b=sV.RE'6N%\4X2G4VfhIj)Qo+e,:YED`D.Zmkc`A=&/M^h5al<^k@^E_pu<mLa6$%C]^:Gj=d.TO02!!&O=!:gh00`~>endstream
endobj
16 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1805
>>
stream
Gau`SD/\/e&H;*)EQ]^gBIT(<6n(Q[,u&sq$drj2p&Z':(n%h_b*#ChG.YQiIA_9nENL!)[Q'*hMYGo^F!RA6"VX,-q!Tj9c:22H;P4Ff;kTY"(gY%uB^f_$R)9'9JQT'hGmoLAjZfDNK*Z:K3$.Z>\>CCOi4rVj>h=%JTq)`oJ6"?e*`*ur0nmf%\C>\,kF'"P*6->_-ar&[?2H*82\44Z:)+e(=oKT^cCe08%B@C(8SqnRBE?#?pO?Ga]6L1cIicWRgo"?Oo+]3'#roVMF1"nS]N"_2N'>&@3-VS1arM9sE5XBm[7ZT=SsCM.EY_%UX=\t[OMMjO@9$F"]*+=-aS^QbPBm_edp,P7F*\&3-XeonHa"?++\HL=Z#IKa1H0-*)ZCadE`6e8\FghkcrS8pLrZGo5W*$]Tbau.#%sMU-T)Jm,7)g`KEFi"lYaBo>;3DVjU]QnQk1@.==a8,S+bDTbQl*Jb!0IJNCsYl@_J5KX]EA@_7\PBF+=QjN3ffrL&k`PBI)8/50?:,8sEX2:"!R%d2NK5mjq=$"H-im;g8@N/"*EL!o[u5W$m?/9Qe!uUL.a@7Zuu1f0/'%*)):E*V;>j(oX6]N7@%?Qn(0o3pB)?OEJ2[i.4H.a^o)K<dcUUb2c4V__d;L1l.nf_^_#?9ZP'@$[CrnJhg6%"di)MUfCH>BoLgC%EPpclEMUim*!R/#rb"R6#)Zs3ndMfnaJ4BF^8J:2QLcsQ9+_Z-PI5saN_\2UPIV$Zg4[$%sRcWVe;oO(D$8T?iTc(#?3\$p0@UaTVja;D]XA@pq%?qiPipd+SQFCD2'!$[#mVunO]o5a1^P3Dp&i&VUEpI/5+i]hA29]mpEu00,Na`:H3aB5uI__;3'WtQ1hC78d-r'\Jm%aWk\10RPpk"br_4#9go%m)X@dqcS;W^'1?l7?G]<h).)<6^--FR@ZYMW53rYQ4gq[J]&$$hOb3+BT9Jf'_t\Im>;eu;mc+!/-H@@06n[e-DSVU<5K?Y@a&&@g(b$M!&al`e\/80JM_(.4$#sdsk-,Y+O?>Y:j/$[ZE18hQE49Z[/1c\bMm:j/-S1"HOEVPlr*H\oCHd0q/+D&&"THk.)ZCH`d\=K+3\E3i0Z'hs4bM^]$=u'C_2"(!&4DWOh(8^C&K$k9Z6(^[X;\)^L[[Tp`N#1+Ml*k=#b4PU9?KZQ0NN%CHcIP7hZZ@l<G+I""HuEUee6<u8Z^>[W:]UPX/T.ASEpfoT-BI4MA=;`g,m-fI]K&ZYRB9\[jY(h9@55pK'+:):s&l2^YHB#37a/oZfliE3PTf_(%k(VLBaI-F8l3bNOfTKqVuOA%*qt@GtmHk4gU[.7*pnRSQk^4FDf?Zfn,WoYQ)+RgX/1NguNKijH[lR7TILc`be3QO0V/R>H+fYg<m`Sa1%rgC-+Xi:Oh'`D-I\nDd#j:r/-[*[XaKF"gd4Xm7#jZa)BJErG>rQ:JQh'nIT8OE@t18!ghGC^JoDFYj;O/P1iCp@(Y*UD+=h((Jg^2`gshXI+1oJbgJL#1IHTj:8Lnt)2'A6;iu8hj5EiiHA$+U"Hl^hdB#jFSn[4_-HS="P@r88Msish("#`/K,k7-n6>:aTFZHO?!kS68U2HeCiAE/_5=DSh\Fml@:NW`d>q:6MFl;&o_0m)\b\gM;j9*!2tPKLe"ol_A[7PYE^MsQh1\J9@dn#lc>TaM4r;k36I"P,K$#oaATs-YBY)3.-Ej:T]S7scV"[a,CHQ4C6$"J2M8O3#<R#Z3rn\5O6??uF2feR>F[8QM.g>g&"P^pIksPl2IfZOoNt2~>endstream
endobj
17 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1950
>>
stream
Gb!#\gN)%,&:N/3D"T&d@ZV]u&p.rfBp24Gj#@i:I;%0q:_Y2&+pir+'do%A]aL"_`H=jcVUMMeTa5%McN9#W6W5]/7Eb-R!VkahN0b6W"d]UiN%hb(K,9lL:Es>ZQH'sf2E,L1!ohu2&$k:F"ihplDtt*Wkn3><@%7kTI1@%HGolW$'<l^<U-LimoJcriWT/$n+8.U4/IOp%ijc.@f=o8F2*@M@2*-Cn^!R^XTQO.l=.9ec3h?G?#u&Y%rq77:aed(rQ'="N<1+"n$Ds-N_6_b_>\lh[2^1>6b:C)MU]Y_/17)JXY1IlD-LtE`7HNT9Y`.!1=NA7)SACa$f;`8)Q:8;n17\/\Jt[MbPJ@^\+j+@NX(B[m:S%qR&%8eeK.$sJ;TM;8'cDZ[fuR-gMSQ:V$FX(K//?A4XGU[4/HlhJ&EF&B6qNdr(r&gU\Qk#6%%+p,A#,RJbq=dn/BBQk4:&60OHNpbpEkYQ6Fc>ZSK+=Mp$l[AX_LD1r<S=8=:Ve:OGoRi<HZct`?WAsD:ga(`T7_rR9m@Vn&C$UVTktZnSHB$>r?I_nX8d[cFIf>C/BPUBgGhf9g$97S9MQhesY42$:e\9-H;MZCMhs@e,BbGX&6E!oW'GDj/!36\`tO#Fh)^r!s;cWEl%;ei];6_N/#^q6#KXj6<&D^ImH"G\4o(df%MB9lV,,(KUE+1\0s69Y.qIp/*tkI6H=$QMbGHOrn3+&R??)g%]_'!58aahiBf6AksdamdWOq5("WfKVX$N4%fWcI53P+4VJck8/6%a*4#hl;`mB.>:e"9)k(EXN-`0U2S.CRa?)"M@Kt&a4"SnB@KLM#(+K@kt:8S881D%sNEj3c&AcNW&bn]SW(JZ+J*(9(CFjVpuP+i_kF(uoSi7:rF&RJ+V\Ad&Peh/og$FM'NTg%IlIDV?u@A_?lSib(TEKCWE1mq0/QGRb7Y]&g;`:5jgXT4C1?jgB_jS=(_Lm.?Q(>#c!2MV_5B-ssqa:;kCdfI39/,?79Mk8,'>\.Aa1,d.Mmu&T!^QWYbD!6m&Iu]6;h.'`?o;IF$M%=T8jWH=5"Me^f)8JtW=AO^MbMpJ7>h1]#-juFh:i1oMS)1Ta*J/o3B6pG)a!:H)2aQ0@00?P]V^54bOs#X#Nt"6a."LOnl8j,4@t7^U'Q,AmAV8k2@[9DsI+fQDa%"TDi81r*FLq]U4#cer%+sP_@QP"+:Y-A+p<heZ\3=>[F6d_nDM_d?;*$nsS]Q5DRH"kgLu(F>qlggQ1W/p2[l+[Hc@K*+X\UpW2*3^8mgU,m@EGp=X,R=o@s?J@DhiG!bo*doC.,%@rr?U"h1,`qCeo8cQ0beK!Ofe=TTi_7JN(u(*fJ>F9%'i+)%*Q0O2N1o?RG?o*n&,MHZ6]oqq/Tfmrt0DP<mX5+L5+lNIiZX6NL/Y'T+)%X?p^;YG,edMkd=A3F'Z`W^'9C8Y@7A`0q)A+lYWf*d)=/3mRaji),818E+M+DK+48)hCL"V]+&-BNhdYCMk&Af<gpI1UE)6*t,_&K;64tQP5Y?%o@BnOu.U(3U3[Vi_q#k<mR&bCHiQcj64L(Z'fBEU+<Jr^NqZ?`BP'4bLClZo)0K.%69ti<ROCn)&Uq:GNcZi.[<WkA_dJ+,e>%<n'BU4dW7P`Q,LT/7C5Hc2l&_9>`Hr=Y!dF9IuFh9I?UQnrDf,=6HTSK<B!bf/B,(eY`1=D_6<)(Gr.Z$s7uEpGlGerd8QMmPk4.RLZF=;>L)SSC\bbN#1,H7lCRV`XmXut:(C>LhB:?D-V>YR(^EBY$b[500UH4.2%pA2HgphlP]@e$a!Dgc'VDp;aLr>+id2Haoid/F2,D^mfbbIG(,C7]&UAMd"K>'>OW[;TY>VGjQLD`1'LfAr^OH<*9D@Hlr<k!G8)OQf;WU3Mf)J>:+@!c3rjN2=Lp4rEo`Z`1_qE$$i!siGr=/\XNN3~>endstream
endobj
18 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2272
>>
stream
GauHLgN)%,&:N/3m-%%*<%nVb!2e4Ze"fRa8^k%De!Z5'+:=iN;1o:>dPP=g"aH02S)q"e>I$p.3.&L0f`ccmnCMl6dhTY/'=`>TK25o2#<sIW9]!UHPJQ,Y6=\0T#Y;bE+JD&`_#Al+"pICLM%c?Q^cb.H$R+RpiB6'aE?/OQ"!_7Re\DF,@ZU$j#kt*]a%,Ft&lX[^6P@@XEk&j6O'D8i'"?SgR`u5VE/*6geY<<lnbe230Nl6UrNDjM_G<4VA\-5LLHd75dmXGLPVRLR(1,+Bbk5/.B/$lKa]DNY*bZC31a-K*6/TeiXRs.+q&hFm#`/0SIR=Wh#Vg2H@[%KHGp.JT^YY0*Df0!3kC0l`T!as4p15ur9lpP-%@*6`'d_X,E:RmQ#OUm*>3C1a%X&DYE@%J;LETi'%%i081MNX9?muEoGqMb2j_&t\(HTX9]8\?I]Fks\[&dXb.qo:erpD"YgY"LuFet'I7AV9n29Z3G&>^?_C*Q1G25h^_;mBROVCLMV0!!4;P2,ZT8In65,B:N/oapXV5D_+FqTgK"D#b#!iWtN_4Fu$*A?6*OIOlRogp7B46[aX-`!\`:5Jk#ml(B-i;5Y]5@L(GE7DriK<?8Rka3CDdK73rsFq=Da-]IZ1YItsD\!*'e]U^f<54lFplZ^qHr_3WUJ]fnIgE_H3M8Wu47sLLZHf:9PK_dOFE5SliT=B[uO5VbIDnFBu6Naf?(J[Ild3DI_&f-6460&k4J>&(JAhD#eHSBc(6&&MHV+b["Wdha.!7kf$A"HWc_CQ3\(e8#4rnB'4=!D^Zh.kp.qm+!_5]8!^JP(n9Q!:?<IpgoZHrbFpXQsT'j:W&93>KC>0;E"3"AsuFBi+nQd4.!)#!J*l[(<n6:.Q4B:>>GaM0T96UWV*Wp=TO#N#8gorQ?f%UF+f#&&(<,\X^p+&[Rl.9Oh#%V)0L(5peYq<atld7tZ>rJ5Uj=UY"Hhi*;.2BhQDokGk8@joOXgT?Zr/WYU[KA@b/6,J>["X_,&;gLm0O-lhj@q0f05-87[L$1&qVO$2Jk/-mu=\!Qpi#FUJo6UG*C5@XJ;%1`d&C2A+!UYYIQ/lKmB1OO\Rf8clY7@Q?ACmsB/Y1R.-0/3EUrc@)ua3@b[!0t[OAC#!Q.^AIl8`5RCJWmjd;GgPYO&GLeZM!YEUasSVR'8_lms4B9k?V9[O;kBahA1WAgl>s3n@$n6'l5/De4(@P&u\efeb'^Di>&A3,blJQX=C+qKLnDe@Wurjosdki]',OaD,f[$?J8N+<EXlP"`>(kngfs#m%+]Jo%/dX65k,DN-of#EHmB>aRc_FoSj`"qbHY9f3QM``R2C)mFH>_BHH?HY]nm:%C\N2B=/.q')=`cGJ]@P]8R1oAaK/&Rp@saBoZXdPIEirMR'Oa#m8Cp>a\9.0iqLpm)W>Sd!jl!6MiiQkNtLU=<FBUbSd<\,Dun],0-Q!l1?[_F#CYq4>ebI)H%UKXY"=YFpArbADZN\4Ydjl8sL.aqBuc2Y-A\D0Td:&5p/2Rie"EdHobcjWPcgm`aUr0`W'=]'L/.jSZFKt=>]Bcg1tO6Srh*H0<:*$`F^Sn]5tF=O--lNqL".9KDuSte5"SiXSPB1B"?\(e+>sZ3_MToiji-Z<HZEn-a`9!W4+jsD)A]9=5=8:K>^g!9t?GK$,6B*>TZ7As$-,ffVE"'o^09Cob"d6VH="geT4iNV<pM)WJqn4e2'3>N8J"j.7'E%]!HN8f@[UeAd$"l]p?Vu[kMLm'qH6P3JhHj^/MpZ5sO>CG%q>7:Y8NVE[AX:\SuFrmQ<Mrm_qMj_S^@pfQEPlr?]E"-$OQOm>ZXePJjArOMp"t"`X7i/n4H=glR!d/e'Jn\os/V[Y&q`[V,X&XT#dUihLQ9%:bQ/e(mguFe.jH*hi>j+*O=!H&:<p/iaEfL3RaTfU[k.=i.<KSl_i/g#Jen@3FIr"IjbWL3d:/c)bYlD-VoHc^8NEr=45Mj%?9*SQd,7=io?bj)hJ+]@>[<%:?*_?/.7j0,k1*b??oJ1nubZ7(J\"J\!nfan8Pfr1%*\*CsC8lLdhMZ8JTm=1PG%p`rQ\0nV/j2Bl3<kC]]K\"hp,&3!#'nV`ej19K=9=^CE?`R2n,@D8XKEsn:K]sM<`(JA8F.BT^hAP_u6XUFK4+4.EHH>foi)a0Gjou5]>AYTq@g;EM*."%?VHk4o9_U&"k)\AW_)IWD?`l#StWH0<fq#/Hf7'Q(LHaQKrI*1F.GlhX6>rW#t#K#D+T*!^/~>endstream
endobj
19 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 599
>>
stream
Gas1[?#Q2d'Re<239h0V.L-osTl6F/BU,<?ZL3D_O5,7)JhnP6hk*VWQ)*6O6HHn8S9*"28*F8CI`ic$50%-S_$0B=Gg'XX.t#QRIqDAFU#H2+9#E%j?s2P4FOdH],+6YC`fZ"5V(+s)EgES*$K:iGiX-]7F$8Pk8qngT_,?lG<5.XYQk[D7a"3?.]Bl0^V-[KJjJ:)CHs.$(<Ua_$(XsjF+\u'hI#$YOQhrjlZ5*%DD/Fot6Ut]NTFaHKh&S]+OW(j0DN=Vqq6_S0!KC[U7ZcmTF@CUBfZb)N='_4p6_4:*Et1AY8284aZ!^E['Bf9X$:=PdnD=@DoS&B5d:2?6*,Q<uL&hf&mVPm"]-]AaT8@oY?)mCIrJ\T<mp&C)H+Yf(B4>=R>r=\&[k"Qc*/PT8%67?qp\;K!!Ron78(=Z_"hV7'.1Pr/(f`/Rd=`A#_bd[l=Vj@YT?a%]!n\`?]"%jLW6)Af/lL/+3:so=]>Qc&VQDI9]E:G3FJRO-*,dYsR\<2R;n7<F?NcF\)5roYr$!+43H&?n(1c3mem=?mA$C'5/)<&CNm/>$?"%@e>!I/Yj#9,!\#&eU>pP$T?S5=>+o~>endstream
endobj
xref
0 20
0000000000 65535 f
0000000061 00000 n
0000000112 00000 n
0000000219 00000 n
0000000331 00000 n
0000000536 00000 n
0000000641 00000 n
0000000846 00000 n
0000001051 00000 n
0000001256 00000 n
0000001461 00000 n
0000001667 00000 n
0000001737 00000 n
0000002022 00000 n
0000002113 00000 n
0000004459 00000 n
0000006175 00000 n
0000008072 00000 n
0000010114 00000 n
0000012478 00000 n
trailer
<<
/ID
[<4dac3b99daee1a608629bd626643645b><4dac3b99daee1a608629bd626643645b>]
% ReportLab generated PDF document -- digest (opensource)
/Info 12 0 R
/Root 11 0 R
/Size 20
>>
startxref
13168
%%EOF

Binary file not shown.

Binary file not shown.