From 87a84fd43b772be1b3c76e698f52760d7e82cb67 Mon Sep 17 00:00:00 2001 From: cedrick2711 Date: Mon, 20 Jul 2026 14:57:12 -0400 Subject: [PATCH] =?UTF-8?q?ajouter=20des=20s=C3=A9curit=C3=A9=20sur=20les?= =?UTF-8?q?=20URL,=20les=20Roles,=20les=20mdp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 7 ++ README.md | 26 +++++ __pycache__/extensions.cpython-313.pyc | Bin 1301 -> 1513 bytes app.py | 45 +++++++-- discord_bot.py | 1 + extensions.py | 11 +- instance/team_tryouts.db | Bin 167936 -> 167936 bytes requirements.txt | Bin 1148 -> 1164 bytes routes/__pycache__/auth.cpython-313.pyc | Bin 6488 -> 7214 bytes .../__pycache__/evaluations.cpython-313.pyc | Bin 11431 -> 11097 bytes routes/__pycache__/matches.cpython-313.pyc | Bin 34206 -> 34675 bytes routes/__pycache__/teams.cpython-313.pyc | Bin 14550 -> 14821 bytes routes/__pycache__/tryouts.cpython-313.pyc | Bin 27083 -> 27151 bytes routes/__pycache__/users.cpython-313.pyc | Bin 73364 -> 73461 bytes routes/auth.py | 25 ++++- routes/evaluations.py | 94 +++++++++++------- routes/matches.py | 5 +- routes/teams.py | 5 + routes/tryouts.py | 8 +- routes/users.py | 1 + 20 files changed, 177 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index edbc9a2..f8b51c8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,10 @@ instance/ documents/ __pycache__/ *.pyc +*.db +team_tryouts.db +.pytest_cache/ +.coverage +htmlcov/ +.DS_Store +*.log \ No newline at end of file diff --git a/README.md b/README.md index c1e771d..ceb0414 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,31 @@ ### Plateforme centralisée de tryouts +## Security Configuration + +### Required Environment Variables + +Before deploying, create a `.env` file with the following: + +``` +# Flask Configuration (REQUIRED) +SECRET_KEY=your-secure-random-secret-key-here + +# Production Settings +FLASK_DEBUG=false +FORCE_HTTPS=true +SESSION_COOKIE_SECURE=true +``` + +### Security Features Implemented + +- **Rate Limiting**: Login endpoint limited to 10 requests per minute to prevent brute-force attacks +- **Secure Session Cookies**: HTTPOnly, SameSite=Lax, and Secure flags enabled +- **CSRF Protection**: Enabled by default on all forms +- **HTTPS Enforcement**: Automatic redirect to HTTPS in production +- **Security Headers**: X-Frame-Options, X-Content-Type-Options, Content-Security-Policy, HSTS +- **Open Redirect Prevention**: URL validation on login redirect +- **Authorization Checks**: Proper ownership validation on all sensitive operations + ## Discord Integration for One on One Requests The application supports sending Discord direct messages to coaches when players request One on One sessions. diff --git a/__pycache__/extensions.cpython-313.pyc b/__pycache__/extensions.cpython-313.pyc index c654ac2b999397effc7aa3b1266bcf86b31d7e04..a36f59a10426c13acf8e7b11e1d97a4ca2d4663d 100644 GIT binary patch delta 535 zcmY+9&uZI15XNUE*_O4EoH$MnxkPc&7(sE9LP!GXpG%=lLTS-eR29+6Q6pPrBpqTX z%_G#9K##rloYLno^b!Q3l%9I4>pnnMjsxw$&VIj{VZYf6PyAPJc1r+1 z3%Cu=)0li2wDz7_V3iK52MQ_%a9uv62P&!t*rW;Dvq{u!@gLHyimivWSraUo)7;?0 z;@Z5eG{IJ_s-@f$EV?IP-BOnU7c7nEMn^YmgRky;lVp{npc^C%xvY0s;vnlr33FV} zLl(z<#Z8lwb{KVnUM2-SKkC!_o14a4hKA>!_38GFK8aE^g-kwWXO5rtjx*h3zMF=L z6Bf_M$K-ZLev7j+xqL+0q%_BI=@NyPMANy91)dB8Yr~P)SZ{DxE2_mO&*AS9s4E}e zeWN=gX$R+d?YX%)lqK8L{Lqb0Z|oSN%v)Lgzgjy@f)K02*V5w`S!KExA1vIlaQO_= z_pmU^!wsO^2 a{q$h8ur)4;R5-8vQ5AA`vP{YD)ZiL*M}K7i delta 321 zcmaFKJ(WxSGcPX}0}!0$iOBM0VPJR+;=lkCl<~Q1qPk^R45Jc*DO9A0Glm(7#}&*P z!f9UIe;X8I;SSr#0QB&Ra}XsB^i1-`RSQ?w^%as z((*U2Vsv3-1sTD=`7=`>BiNC}AonOJ6md@uWD!vj0CHr2xR@VEG%$P-W8jgWQG1!& z`~r)45id~8Pg7uWCriv^Ayym5B9Ku-BE$KK+w$<0qG%}KQ@k_K`afwj`ObQz)Gu|nMpkM{>TY%G5${J2gS#YEx74kcS|zC;lYxuKh{&pJ zRaI7JW=X9!GjlRW9&8wUZLkgAgUv9&;`V|uaE@)f9N^e%!vbUDF&O(W-~;x0jcvf# zUgO;{X1_l&GhP|Dx-uBn_7K%ws?5AMzkB1m|Nr0rzt5XDuD1J5z18V%oBg_&Ih4s} zGcVNZnap#)mdRvJ<2R4rTqbkud&aNv$NkApUaukD-7EyuZQZta=#Ag{!OeKtlbQYNc)0uUxye`qJuJ{o3_g^=r4UUOiKTAQrOS)HA} zv(vBNT745w8+c-?>zIAVUPq%|0$aDPyt+z6?CsidAHTMC<<*t7x9YE~zEyvwZJ(yB zFJ50;z4FpE`s^96hNtUmt1qsutzNshdb7UQ0~fLRt^Jd-HmlOBcmmMKX4VOV~DNE=PbrXtsMG_PjL10@aBB!h%9FxTyY5Lh(3>2!8% zM#`60ZYK}GWc9%t)zXpU$FpDm@iD7A{jO>CLFM?xLWIBZ$NP}x=}Igs5$PT2X5P8V1ooRM;@@0$Hh4@>5_ z2v&PzhlNEo@A>J|NN$F{+28AV1qhsLSzOEMnyk>{M@-ciJo4$6+ft3&BP1aEYwF%^ zu(?8C4C!m5QaYmR*#|d9-NvYovD>}f&Q5!xv13_@d^#VJx%fPZ1@I4o+IlL)WcO1q zmrF;E9?gFG&Zv8F`}f|co9|Ua{Ea`{hp>LTzZKA^s213;tljo5`5~UrZUl8uj;IZF zv2L#$Qp~N6<@z!KZ5XKo3}uXxY@6?{k9g=!3<+t=#6C9fPd*J50-L+x^w%d69Iz+B zFVCn`ewmE+Anc&KFkOq#7bmqF^@Ql!E*D20)+=sfk`}EK!QH;O(F?yThnhB=woePY z85usqzD+r)hSz93Y*>hKv{9eDXD^31YSlPHb@BSOo43|h;Gx#{cG~akIqTjN)NfzA za^v=D{h4u9&(x{tDMxIa8lsavn^5_YQt`<3)7eaW$9CTBy#v3%SvU9k9rwLon#=4u z@0ISpuM9tucRRxet8P1Z&E&l(hS>K$-iP>5U&Ti?YtlVWL^&v<3<00W;sHc>!YI{Y zipWXP3?`!zJt?+%pYet2X}(ZAvU;3Fy+_tM@4YNN_;9v#tqB3gsdiOeAdwtXF`Q1G)Xkk(Ac0y(_oY>miLd+91dsEHnY{MJ%^k7<3 zHaj~(GwZfVj6c-wt-}Rt--YB_R%Z{<&4ugNudc3K3yMdi{YhDRo^?ilYSH6P5krqm zD8US5BPZUJBDu+1j=2Hl?DW=myB+eAybOY@xH;mqdm?X>Qr(P`4_$8DPeO*0!Xm_5 z!Klg;4>z)^o<5x`9{K1ImRz~Au`alO3a95jIIFAlJ;d|$b7&zDo1dtCY5wP`U#$FW z`HQ7Di-+?+nfs=>ug?7DDCbwwzr)Lmhcd^8H=cOW-?Jj$nzFDru^Wm!hdV zj-qp##x+%evrO4mY=yUZLF6o3vrJAyx8gXchFOeKwn)J1YO(g_jh~GpFclR zg--9>#>V+k(dS3SZM#JeuP%(JTs}=y-Z-)N#+XWZipm$IkG*rDc~9HX-+5D)Zm*o= znvP@I5~|vgT8geVO-)joV2k5q!QyyHX>kItwM<)en!2cnX3G|Bn-?Y9QXE^;CE4LL zyQ!*9%a&}-)GSVRWX*DAA0__!OCZO4si?-Fs%= z+60xan`jVor$55Gx-3E`_u+6<7{&~TAFYllbR|+f;ZZ0~QF!_Cy^f`QY;$Mtt(}`2 z{mql6V#=BWm)SQgz7ap+XgKHx)n(>RV3Sv zh3*7;Z0kJFwG_qDIcTh;iLxSaFnQqDlpR<*$RHSb=y{m$EKufKHZq}G%q^gNgqWpU66j%$Hqj4HQnUl{fZ{1sH$pNttRiFz zLE^C+iCOV0brh z@=UHN+9m`=h8*dRBsHOXX47Ra@cgriSlIa1&KKK*7kzbKV_$_Bp-?$ff=SkFM{wZe!iLyR zQ`9ur8eUaI&_W#`FIi1afSJ*uuP|8>99Irc1(yQm$x>|}>CPQqYf~fa#=(`n2U3d@|>DaYvWWlWiu|*fw{}4W~OT z8JBKv-oAS2?nU#W^!nPHTW_7@1;^pw00^L>sW;(dwRE8cuS(O2RWD!m~9!nN*2;}LorB*Q7*4x z^5MeC7M>eZ1<>iHBP%TgK8j{(B0?CneG^r0IZaE1WrS-a>!K=XHXM9Sv*7jE$mVDe z5eLnzL(b8!HPIz0Eem#2klb!gZ#h~E?mhZ>Xbi8SyF*dB1lef9uW4r%rXy?_PUPf2(7Pw>y{bUy|S4JjpAZ zWVIx<1&u(Yr`d|8a4<#apLkw@zX$;qc*waVphYz@3Gi;fi)L9oLLN(y9TiR>hcE<= zAlen-B3x{RS4hPlNEt{ONEt{ONEt{ONEt{ONEt{ONEt{O_)2GBF?T#W8NufcFXWC*-sy(|Z?szA zjk*+gqbvsA$P0lt(tO~JSPi@pDuFkAxte<-I~u_*(MxxPOe}gYg@X5z&(jwtqguJZ z8-8vfcf8__AB9a{X<(;Gx9+g2jggcMl$ML&>-$@yZc)f(*C-7UtZ~BumkTQ@mkTQ@m zkTQ@mkTQ@mkTQ@mkTUR<$-rYdKHKh@O=oK>uzv6G!@1`W)MBRFU(HU#`7}7wJ#RK*~VM zK*~VMK*~VMK*~VMK*~VMK*~VMK*~To17g0I;WOEl&?z=`I&*6-_H+_m(^Vel`elw~ zD=U+ea*B+silCgFD`p->){8fV0wWhM$zm3@K(25taAeO;KK@GzPPU=>|9tK5W@`Vo z_IGQ)T>Izo514+MGLSNmGLSNmGLSNmGLSNmGLSNmGLSNmGLSOxd&dARlL&|VH0a>& z{htgg?EPNqT`-R&7Ui))6_s^(FwMUnJZRsbM{`%6l zEZtf9=+YyLzrOfWi+^qLk1tw_U%mLm!fzmZ`jaw{GLSNmGLSNmGLSNmGLSNmGLSOx z`+$MtM~a#36MW|IrB2_h3zJ>srj29b*KK@u@-bvsK706Z;FghauFk#JE}nc8xt>1t z=%G-|TI1nOr|+A_O$)=gah^V-E@ zhmh;o>CDn31PtW3Yi`wRl>FS`#mTi_!TY?{TRX2^e0m8vo__Ap#n7XNvW=ep%px+L zfBNXc)Qv9wl~(Ir<#w~(>$LhO7m)kx)0u@R4}r{9??dxcy{9wtliGVXJFQdYt4Liw zb$C9|c76tGw__Fh{;9*&$>Vu98@bQ6Tt@P#(}$};3jHi&21|r-I#Zd{!N1psD)Cei z$>)(g)mVXiXs)9L8m(Ax5xwDwdG}F+;}KOigE#(AaqAcwZz#JtywQb{TY>4(grW-_96czw z)ejFDQ9Xc%q@bSiZ>KM^Z&UeE>pX;^aJ>l{-g|~45SRC45SRC45SRC z45SRC45SRC45SRW3=HzQh0NKvj-5ERoXuvv&Fl4B-TR%re(&awyubA$?_2$LXQ#f~ z+3VJ~p8k>d-`Q*LI(6HrPv)N|e<$10d2My4zSZ8?aoRiUyWP%4*Xi|Yx!fdO;Oe~g zyevO2N#}S@Ruy&dWx;W3%47)5hmp&Ru7#-gQvJu4A8z&YyebOOyKT?OL7f_RdCaE_P2nD#z=} z;Bi3*2WOB}h)(v?@!u976rU>m!@}Ezh5YyCU(5Y^ z?pt%p+|SItH+L-i_p=t39K7!e%$Mt*#lyt=>DIiXiqBc%@;Jg|Fhuj?qTxNpHjb{K}9@U%+feZY>z$GTQ)Ogwh z=k+i+adH4!1SYuHc*+Bhk}O8bisxL@nc#wP3gB6yrHrF5!f*%Sh0|q5i&Bbl(nm*= z2APPoaC{Ac!C)OJvauXO4@%>K1O^x)yIkYL9(o}O??7iXDwS)T2%rlgW}=D}t4YA% znROS8dH_ArjC{C}S?C-SoimOH&=bjw#E?i%mvrN+JoJ1-NDkPllE$jLq#4J2^eE8> zv0$hnQ{E-j_>hkdLlBbps9lKDRiHCW7?o=r_0V%LT0wcoqXY|MK0i~NB-wZ}fF7aZ zAa({dWNMQn8BYYz6Z9NFXKIrq8jt(vNenR1nc5`r#*qMef{qF3Ol^`l<1rr{7C79D z;YMcYs>swPQ8ym-(Ie`Ew;>T?p)<8f)Qm@b^r#3%8kvR8)Fx3i9`?}ZlJy=UQ=3G^ zcqo7#rXr%cENv2*+9b-xVIMtVt1`7olp2SC?z&;+WQIUwDw8NSY94q()|t8_@{J`A zJej*8GF3_B8jC)7qFzs6YLcKg7Ci7|iX<=Axe?R;Q{PWB${B!hKn*YzFcl!|w z=}*c)%0S9M%0S9M%0S9M%0S9M%0S9M%0SA%S1bcG|DUaWiJtGN+Lx9-vvhv(3yXV; zk1zbtf-(QQ^Pio+Q2mAKr>oCY{!wMCvQ+-Q@>=OPOW$77i(f2$viRY`j}|ry)%;)0 zU(5Yk?oZ{Uxu2eUckapT-^qSWHk0|>mxnZiCjQ-d;zB;b42z2S%J?xSH^rct|1o-k z0d2yFA9Qk444V3%)P4W4o48pD!iXPra#IYN`}gY}n!kyR{ISsEhgS(*F=+CCQg&4P<B)m^~+OzCknpBk+U`%2XtYYtY317(9`r zi%eO>+_^XJpD#d5gO)v_w6VM>v}A#qBKq_GK%ZTY2tD3@xhej&`KCiId#<|1o$XyaLo)%$QvS?2_I{(lUf$UJgPQRjGn{y$lRih6t$Nz%gk^Z&^VyQs6&2r2&jf0FoD z(M1F^aq%C*^Hc8pe{S(irj}bej6dm5%0S9M%0S9M%0S9M%0S9M%0S9M%0S8h7#K8Z z4S9BWdu1h8T-k7T`tEk^`i|q+*q7bu-Z@u))x3j^+k0K7-tW|V{kC<7USO+tx8Ch+ zIla2MW7qrLyT9I)Uj{g&1`i$z22tT?$&p=Ov~A(!sx5JUFWRX z-ll@Lk=NYW0O+~;Ex^>RO=r8^>v!+h+r9c7=l+S>iW z%nj#U{mPErzT39-Vf~dz+fhafN`Iy1fm$uz8{eNoOQU+26QU+26QU+26QU+26QU+26QU+26QU<>A8Cb0Sc4lGj&CKG* zYoD)uZed~Z8<>K&AmVOX6?6;fqp*y?Oox>^q;)SszV+IIcQwOgw%t*+ItUB6YocKhnpGxchv-~GyG z`G3489A1OCoH&tvu;Gde4RfyVbTH28zX}VB|M@<$(&=ujyBQg>WOYnybDf#EgYmb1 z{nqN6kiLKfS~SY(*y|YTyaegJb>-Dn63o3_JMQDx)~>v|vi4T}mDRWE&$R8+l=a2y zYpYjYx<;Qp6R6|q`r7J?t81&*F0S6JPd@L(errA@J6+CDu$FXE)6QjFBT&FjX&OpGz{k6ce*Sg zj7aZD&bB(H4f$){XML;FZEv)9%&qlSd&}7|LFwx&YZouCtUV*h^63z@y?zHr*&ZMx zL9Oqa{Y?)`=C}wJ9m&hUqMG;o^l2nF1BTBrVDD9ybG5~_1Fjl|9zUXfWAMnQAEbRA z`-~>15`)@0ZjVsz{WW!OH`rXEFNXBBQ7IkK_3VQiu4IktWw(2~ot^eXW8*)a56N77 zp2Pz92SIH=s1d8DKlO6CbmZvK?5FRHx|h-E)0q0AL#V4E{>C5fLl}-*-U?_`R10ia z)^2;(od=@6gG6W}sDpAuZK&IDdyian!1DhU((= zYd3GLt-uSeV>a)dJ!jo}g8J=iS8m*1tv@ra>X|wfJ>^`CQ$uvpXA>$vQYs#~ema|J z@7T_}y?5XjIP2zKzvI65OLLiB=e^S1_m$yC@@{ANVAX90ubI3T#Sr`6$NLZ;>Z|yu zW=*>1i6{qUlp){~Sv-IUPZ*^-Oc6OLn!#jLq9?^R?=!woJ>s!Z)0I&1M~|@N%9V|E!TnP>J@>&`U8V2k5RsNCDy8}VO6JEh zmH%4)&cc;)D}QwEmlpr>!b|w*575tp+bM)%N20gyj*>rjcSK?B(w?~W7Vw2sj z*xBhh)8D)cyNno!Rs>EbQlH%3rNx8OX(o*MeXO6GPC+=0Q6>i~6XPC&mQA7?n0h!Y z(Cgw}pmD;BSFf+QQ5`L+#5a6LVsh)wde<4HA+!sIaAS34$l(kFh|`gQFC1QI;Azqs zS`<1OWI8h$Wp~?n+QFDv)IDT|U5KhiP{bHtsh*Y(6^~q5X8ADwqxRPMz?(;g>r10C zPjh5}Mi0|*U-bJ7sQ2+Q=V`w|>EW^-VOusz|HV~oEDrlZCpk-o{J=cCT9N2;)YEs7vjtPkm2ll6%KtP z=!rde^I>!3p|Ir=m{bn)vO`ikH9QT?4%HI$t`jK`lm1tWX) zG{yh5MLSdb=e751Ut7CgTVDFlOaEx;JC}BsR+b)J{H4Xev-r(OPJdDcQU+26QU+26 zQU+26QU+26QU+26QU(sdz_YaycGRyN&EyWp?n38fS<-eo>%Gm+y=Rvuc@Hu23aUQI zyEw^Pi_eQY&$)5l=N8<&$1=GkMqWkG%{~^$Vg&2i`3dkMBeS53W8kw@H}8o|ZXrG| z&bgKRfzjtGlX}dvvrp>rTzQhY%FHZ!nM;$*6=r5(lKI)9n;A`l>m6fGqUPyz-U-cx zao#!B6LP#d$(x_#WjvmssPZ`PnOq6S4y>HWFdk0eWW^`$**Q1&u?*wscr^Cp=}u?e zyhk&PhvOu1B5xG`KRNeErgm@Xx0dcM{?_8%h5xXyGyfmwJJnyS?pA)Oa;^NMWx4d- zr6-GjqVWF~-p+p!XZ`=Z96$Ftd@23;iez9g|K!6kSpHAxj~v61CyRw#C6_OktNH2+ zPqy5jo1HC;!eW4%#xU!hR=szpy|vYw9FNi8#Bmko_U^fxm{r5X-d3!x35w3R=G9FZ zEIbjd&2g$tzEUU`O4ULs|H701gnmAM(Ar$t!E$+=xY1=+C;kLfs*W_6e>_?pB4?pc zDVOr)e7RVD;plhJ&;NYT>U>QvzEZGq`Zm{@JTzx8qV!2!@6mq4RtgT(Mj0&D zqLm?0hV?@SQ6wYy!sD}<S(3j;algEJiBw4pk(d z&!Z9r==emXUg~skmRGPI*W5$I#-~TQ$9=VT>b(xO`j}XJ)H_hk;?H@enyG`rLZoJ| zqGn4)7@lG^Un-)X5Z#~Yn$5_Nq~dz=IGUjOugG_Z@;7y^RgxldFg#na<27OUkPwL=+? zKhm53hQ}8~hUQ90`FJ@}K0W?ozF5dtO4V}7^A?gnz92Fb6~)V+C`F2=$Dc3e%hfU- zzfgVQ*jNZh=gtN7BO-+?`bCv-uZn;aZBQsiDnME=U%(w^#WJ~*lP>rJpZ|mG1WIFQ zBh~N|g>W^pR1LVY=v*og#d7Jty?mVQ7Ox*7FY}+shs!U$`Y6hW7!(WWYpa!6i~*|@ z#;J3Hh)DGlxtQ`RaDxiPVxa&r9LE<2p&`C}L1H~WQiM;=MasX9=dXbHBAks%rSd}k zpmGQ~U{nCnJ2OQa6tmG9Kmrgvl&Yn23GU!bHQ2Yu;&?6o<^qA^b-qgRe{DX4eg6Nk z_KUTDRr_bPe_Z?F+TX5yU+u5dzN_|awa?T(Rl8s7)SOzQcBA%E?V~lZcDi=F_9(uT z{-g|~45SRC45SRC45SRC45SRC45SRC45SSF!DFCOpaEn|b3QqFm49OVYN7D>I2 z_|<&jvB|6aqvKcA!XuMc`G?1^Dust8ukwe-ugZl(lUMm#xj;kLwc^tFMSgMgVyU<= zeo>eozrYBEn|ZNV8Na}MtozA)u{3^x`4aaN%>IpE+7qsH{qUu0$^?XE6XkRVaU86(o(~|NPQ& zruGfBhn7Bz7wJ#RK*~VMK*~VMK*~VMK*~VMK*~VMK+3@Xs0@5Bba_sSy2X}G7o_mo^ z>}0ksDvG$9L*d6qg`c9r=iCyvagm1iC5cx<#o%qQ646rWHdn980#`?!W%+qYI>%Fq z!rojhQ#tLGSjmp&-0${APwbV7-Sgp6_4rch2>x|Ese2TE+@!r*>fBy7QtXId6zi5B zC{_y>8)n>l*n<=+>vQh>f32RW{ma_-*S@8;ReKR{(w~%pl!26il!26il!26il!26i zl!26il!26i|G^kI>0T6&&8+0-=lmGlA2Sd*k<+!4?iB%)tIE!$EW4KjRFEt741a>> zxMkc5fSvis6nl6-G6}M@jLQJB*-Qo}eaBadS2SfAw*ZVX#ok3AV1!1JDgH0b|D{ZA zb?JMS4&n6wpIf+sx9LyHK*~VMK*~VMK*~VMK*~VMK+3@H5d(t{m*(Ng53a9Z{xef6 zzSyz$diBmu-Rf*_@9nfLv){%$+!ryv68>EMqPewI@9g#K&W^KvzdpI(zE`)l9J9N1 zzi#f>b+dP;j*Bqro6gp5{obatGd@TBTwF}SYl^PKU#vhW;^5(;TS?xnLw|6#hUsUT{{f$od4n50#H~$7MimIE<_7-kv!Y%fD zJ- zcTl){6NlB=>EWsrs#|{(&#|#d)Z)dC-D6b?r|dImd-gze%fX+{?k<}!S;YbP zar4j`Qk21wT%cx^{HPh7Zrj|dZF8(?NjZy|$)~(vt zj;rR9w(VG(CX};B7r4|%7rUUEC+eR1_WC0|u*|LP$(3oF?TyW|XxCnE58uMW?YYX` zJ2#Th-BHuivlbZt9T9&}! zDz&C5;U-SUZnk(ww-rIO98MZl^!ir&ZriNCYIb2zaRVRTyx?q`?X7d@5M)_C%kjJ% zI}XKZf+ERl9qW$MuU~b{jXmegi=A#C7vec1)K`}U+%~n@@9*}WKYt#b1+sK{=QcLZ zkIFtjDsS5@d-&?Yh|A^E#N~|>i*Jm%l&83SQTo_B7n=999sQj*b?NrXNv`QQrY)ha zEvcpGYSYvtr3tz??4q$aUQ$||z-ukjR-Go+$cSdk7HyjsCEHRQThk@k;WWFcs!q$6 zY|YdxPIhF?a-^23nz}99w&Q4uBnpacNuUqxYG}BohBqy3^gP!(=4Sntd4H?Z-NDN# z9;JPFyoe5DpUrn(yz|-(<r9mdmR#V0)Ad1eboG7(y-c-S> zX_&Cp_wb_Zo~PIZcVSuMIdLEUsH7$pIlNTybQ=7v_&P02@n^l! zxprN?cjpyLxvQ>SzI)Pw{_>CwO%+=rFDkl-i-Ma?t0_ySW2uUzh%Fd7O>r#QY(WBB zEm5$9rliY!3leIGY+6m++GNSLZri#gIySGUu(GPmTe72xswt?v(rihN zDa&FD9FOhe>h`u-f644xZPUTaDFS01vk`$Kn@BgAPFS4CA%VWMEWvl$heaxG!!7tz z+45nxn>i|y7!HmfB1xu- zWZSXOqd<{uo#(lhqF8W0xTd6ublVV&9{4q72lh^KbW1fY9cE8zIjY@KWk|Cvw@eB4 zPitu+r+{KXYJt6$BwLm$ptn(={+2wF!<|moe%ss`{}I#|HX@x&tV`q)DNJZPQ=hVu z^(ixXrXKE+>H$MAE*+KTIaQMAE<{}9rD*y-%`pDX;JwnJ%oD>KN0!gx+GL8IGCd2P z`*Z_byuZc0Ej906xc$!CYp=g_>7>?_B=kL^WJw%srYP7=8-m#6Rozq-PPSy#(VUh- z4uL8dZ7kt$nX!FA-_UT4Ss=$sitL2Se@p`Z{;!L%W}zjE`^>&}a9>)lJQ zYzS|^dJ@-vNs=hx4_d2bqYH$efya`-xM?~FR4m@)b(3?@Wun(`EZm+B9aK4`C7`F$ zWSx@{FXP#H9)6z5HAUNmpvaIT-I1gwbkA&>jxIw}TcT_$_Bp-?$f zf=SkFM{wZh!iLyRQ`9ur8eYYPvZhHLATL=>PJo%wp|3Dm5`0$=E+iR6+NRV zp-Zr3OAwl-AXu>9qS%7##(_jxg;Gf95&{(5+U#uadQ#Ajp+Jg@Xo&(*pSB!>Pv+b% z?&$LQiE503{qD46T)Mq^`|72;7tM>(>uYary>*fo9EXDgAb^Ud-h`Lc(uEe>DosQ4 z!2=bVC=!-HNB0HM(d}jv{Z>qLKkW$th23o%qdZd`^t;#I)8Fct z;_c4m`}fXU+iCCu{S(hC@E0M#0uMQt1hl9|CIQ|Jc+o72 zN6=#lvZKNYT$f?<498Yy?DDkf<->td@eevSo8#Rd1SI z1TZgmHg}*OJ$e&E1JmIVC5r!_&Agwf{l(ggOaE@^GfU?ezp%Ks`1r#Aw(vCz+4;XT z|5EjrtKV4VDqpDdE02|bu>7&o@07l)^g{9979SL!D*VI3+l7Vv_vT;A{d(?ObIRP$ z%)K{vEc^Gf7LFiz|I0dX(0I0-$yT0w4)-()LTIOjAci(X#90CixuF|lrl^h69(ZK) z2sVcVcd#%aVr{TntlW48;N?-xxp2+-gMr6dHy2!LJneyFKT2@hi$EK1@c(!#4zuR5 zI^u$hji)^DD9K`^EI~QTsb@pGX}~$-=1FP|!8ir*EYVUfqOPH;AA}bUenpLh?TK4=!Keq&BhAQ%8<~Y38&$aIoN+vWo=9dOc#Lu--S{dGJ)gu3AkvDz`Wc)_ znsLlWPh!Dw2&}6bCQe@iD@AIQg5W{Up;IAK z#)4ofksvgRK6s+8T3{-Xz&8pWcrp(Oqd%-?;u?7mJef(yWDEnG*BdzxJeh6bV+UKw zFXgqyoDaV5sQ}FSF_nl{8d)Da9E~2ZALW_S!n^DLpUvFQ)c!*4!qU$#Jy<%m_+yJ3 zi{*v)7hazKmHE%ipRN9QwNpJ*`J0tn<=-m*+4A$HFP1)0suzE_Xcvox?=HNY|JD3A z<@EU<#EYw2urc%^OqZNlEf9gk3#^-B z480JuhS}b*uXg|gM=vx=0)f@kyqZo!Qq1)8pV zSxXR20fW&6P>S9dQlL3T-}lHEX5E=eg+5?Nf#wK3nZdzSU|jb_bypfg3N**)iKGTg zi&*G#V@QFf$9t$%qoay3BYjjurpWVBV@QGKxb6}CkLVYxhD?d)#m0~V%`ti+Yk(yS z%yJt;3N**eBr?QUEW<+Q8$${-T{p9q%#vZL7z3Syyi=e#LXRlt0ZjpBi&*H8cM3Ge z=(9ULkeT@DYu-(13@OkYGm}VWu!@XTcW4tunq&0Y-FvDbvj^dzO%!T+yoa}BY zWNH%!ZK7Dy<2`8Lr_4^gu5wIy=LBO&!KUk$Ezz)iu=3HaJeFP&3uWUU3n`*8q;PW# zpTw~N2&49tVhkzZoYX#nF#rfdT`9#FQph=hkC?8AQH~`?z$%A$awx?ZQqVbqFC^g} zAdC(QDVi~)uyYI_F#wUKVAhe*Ls7xTkOI#Me6-hy2vVG20)(NmNKuR-g`N{~5?BO) zFm#quj3EV|BX|l&LUMnA0Y$7!!eTd8qeJc~{Pf{NS{sp576?P`Q82WY0?-M30*@OY z47o=N)QUpTF?1yhra!N)`J!P-YlV@Qt16ijVA|NqVh$Nzt$vR3|$^0$`d(odJ}l^!qt z?c&D@Un=~$!bkJ}I{(T1iQJFmoLp({{kd1N|33Ro+4Grynt3NS{5J+`ljm8a_-AHK zzyff_^B_es1~+{8B#jLaM*l?x8-v$;_$d7ms}b880Ac7c6>JQy2jC-k5w2M93_S)s z*BD#_yz9&^MkN0rgS`qCLNdB9N@)yU_0W^_Ucg`&2})@Uu6pQ623&x_P-3L0jlnA( zI+}H6B?AVd@1k-WgO`Esx@?O{9C2VUECiL?7+mqt6V{!nP?(b$T=vi-LNe78#P#Z^ zAyXnn2>eSxcYBtFVgiwrby_tTrwVS0F?cZmKYNe@5H()n-4tW68i1d@XCaVyk#|#! z!6hF)fkqKXe81?X7=w!;_>d)z=m3LZHD2fm9{u74A3lM`5=gw%QwsXU6%W2JC5d4T zX4O7k?A?Nm!3#cog3z2m;`i25jVvc{NWIM$eu+?W0AvDE0r?_ zANAprX|22SC}zzVmTu|B;131hXZHs|0k-}!o=ee;!AE@f1g<{8$9E-e!N%ZuA3lKx zLLf}FQlM742cJ)1?g^f$RtnTg^Wl?et-@3*1^$y7fKOzQ6{cD#=uVUXd?JI)*yB#L z@y0;*;gd;@%ishG3ra;S+=< z;GVTGl2VMpSsy-u6G-q(!NkhQ!5I&JE+ITqaZ=4Kfx-4rp!q1U`}6GvZ~p;L-a3UefsgI}2Z(|2O$hZ6U{ygIwN8Myb4Wz*N0Ewxe-XbCc7!d;9dZJcF&DK;x(C4 z5T)M*ygPoel)%anMtm3GmfINY`REB|K7byt$8NceLEl49CiH5&Uq$FrW6<-^6GSV7 z!Bi<#Xbid@dPGAbGQ?6?MzBKV@{PeeKzBz+mJ%|sDipH81w4* z=(Be%gc0vjQ3}Sqc0BZCt;+Dl2^}5RHqhOkWiioIACIm0@eNKX#$d~bPhuzm!jO2R zz@xh3!^6x6dy!~Z6z8yEFq0+nlwu6t4!}pe;{z-&C3uEZA_Wdx+lPR=2ueE2BY2XK~AN0!)A!N#B!fKN0E&Ir)mf{lUW!P7Kr z0?A|;l1>mHOtqo_y%~U?JxHWFGI|n9F$QJ` zJ~S{DDUC%AQ>`dK|Dyr;1bsL!G6HmZE(GZ79(+ETDnmF?{$0@jrnuyAuk~8v*#)19XCCxM!4N3_j+=C-Fi6!jyY# zx*WXa!6(^4N3~}JB~-96cryT>sJCJaFSrF8gExHmWRk-eULbg=)$0NH1W^O#HkrK@ z4KLjG;S-G7p^kLyNBra#4KLjC;gd-YV`C>347IxH!6#bK3lOHOZUAtv+7)p^8!d-STJ47fL^0dQdu5{ITLjv0Qi`>;Hcx|C#*R+>hrv zxkGb*V{R?`8`*Eo%9)=ADEr44dQtS@VvPkS<!;q+memX6%rlV0v8luo6X@nOS$HPGR@+kYZ`C?jf0v zGy_96Si9^{ckE%HP6s2PKm`n@Mq&T+kRs_ZdLj>pwFwU8Vh;la(qr_9PY`(& z7BfskFJMOy#nJA7^5Lj)IUqVyov^=pNMZB{Jy|COSzm8 zAqCN6W+GG^OlDa7>wu0OK@>xe(GwYA)($wJLz^gs9-}9S|AE0Y@jQAEFM>W)OwvK& zct6H~ETtGjFM>Wq>l}l&AYwG*Wdk6L5SLPnp%+2B@Il|>Kt>oK3`0gK#?XtP0iVG4 z00_e=rW9l7MbIvMsPwXCXgI}FnVvKz&Y?1v;#hY*aL)N%&1^v z=q+eJR7lbT0)%1BD23+#v$el~_@5oDrzt4|DFZ13DFZ13DFZ13DFZ13DFZ13DFZ13 zDFgqV88C+4a(2u$Cz?57Evvz7t+Ke-%j7HHk*R#g()TZ%ss8K#&QFuVrwpVFqzt4C zqzt4Cqzt4CuoxI#SQ_8M`5fJ?t1f$Y>#9@NQ(b$h_1>oa?v2g6uXS48_iml!brE;8 z$qKF_Ynixjt!b-VtA)F71yRGzk{qt>6LB#oZlQBD4%d<5L|I8vbrLG5*0OQU6*eT`MqV6nCO4g?(4re)c)F<#*GA$ZG+bqd)yZ_tA+7{e1gAN^$8@!i z3$W`Kx6CfC8^+72i#Qdm)tO#a>)u?aco#?p?=*E0FD|R9wBKE(ZebP`JACD^d&$=m zr+K>Pj6c2jM<$nW;)=z{ZO^yws(tm^TdTs_-p<9fcW}`e?jgp1Cf(cUU~4_D7;SRR zrp?<9kH@w-30D^KEfv=hgD@N_>|n*NBRYyA)UF%PnA@ZVDT z7F`={jT=ywWq9&eoP$U$Jq~S$q6$ip$u^ zXO~JXaGU0tOP#)17bci1xX@JG?^@kSaVC{LymV+Jk|&-cHdmItt9A7$GS|8nmAgWh zf9>_RudE0cg_F2vnCEbzqM}+FubR004)^D&xCvI~aGfXa(dKd6np=SO6|YT}x-`eocpzfG^ESd5)xcUe>~(q;Cz81~X$|NfcKeWfnwP3xHH(%VkcM@9bh`7%h zSHs~J+$Lzm<^@f}t=71RP{wL{2RHNKcpQ<(CFD3aSZ`@9y~%6199L;I>1I9LF^7vw zHC=&B;<9mET#8NXIeZ6k%LV^*8)7R@pZ-)i)8_PHdg5T$QSM%=hrP@dmK_Oi{{uM_d+c2tME)LCm z)PSS?|DVm&K3BW6^ovVhzx3?lKUv&aJhbpP7jDh}*8HEHf4=&~>L;r8$`4oUO0oRi z<(EspTKc9ESNuY;Uwo|agN2Xf|4aU}`HQ(<%zZ=d%-m1Rb>|+<{$RF|`Q6NCSzX&K zpBvuciu1H~F}Q9fx)>s+E8$0{B(YNX2A!?wE<>A72F_S)g<`STijFxLgU`O!iXdXG z5j_*uTG6P`7<~5Sc?1!wrY^YJpv@U0@W|q%gDfT@@c7j}Zc2l;W_aMi291;z=Nq{7 zi(l(Q;BtdDX1Hs_<`eA8Ac**tK9nLgXj{ezJW|zz(h|SU$1S!&n=-tb2bVxaC6gh| zj8n`BoNv&Uj4}A^Lx=I+216Jr#qc&_06nr) zIU+x?Ee8ySz#xS;ynPs8X7)9%s5@hmEy~SHXBkuzon?r+GqnkO`MjeHfS$lf z0ygs`dY@jo>30v*FgABlXB1^#7$Va?OhIfvEiyo4haAEO1GccI) zj(vRIF$Q2JVsa1S5db}Y#WK}hFuYR?s)>#nlqJS4K0@aV?+^o^Cx|zI&hTa^#qiEB zs3zHDhZ-{W@VP05cZ5MT(XFLGXUaRa@OdW~RFholMKxsFckJNvwqKy`34Af2GwnMz z@Ois0Q1?U@n00L_m1}sLFI@DXHbo0#*ht3qJ>pC-yuBA-CcGOWVi;Ju$~b_V(793k zkNy9jt6g0Bg{7}sdTQ|>EN(7V7QScUmHA(t|Hk=q)t{*DRuAL;znkUXEPq>BDgBGm zyQL?JKU8cKey8xAg+HACx%?o%ocqz-My@jVzs|ji^Z&jjD`x)rzWe_hgJm^WZBoMTF93{Lszv+pLsp^hvA zgcO`tcG5>rpa{TVbhuP*W3cR@ClOU(Fboit+ZcQp=x(^Skl-{lVusP-Qn`)62_HRy z3kVE`d83rZpzfh3aif62FmII77##P|lSw4Y00AA^^i>{ug3VvVJ5%1VeRy!pM^9kh zaAy!xA_W|&^dX?Td;aH>Y|cU?&x%Trg2P#khTub{Kcc*`rXL`TcBO)i!IJ^_1nLS9 zhMA-kWAKCzpG02)!myK+VhkSl;gdvF0AUCYr5J-F9z2CrA?_nwF}P<1J}OUV9zW*8 zCnJXyn*bh%5~9V|c?#*zv)1`qr2Nh}UP7}k>es0H97Mkb;L z47q0oKvb|XSPH-=h~6+|%ZPxG0<~K7;Ukh5(K41`vOEwf*cdGM@X3q<%LM^EUnNeW5Aylw2 zDEaV7{1AXJ)e0Ae4~hZ!1VICKS;wxypeZX6YE|&y6NR9FXR4I|xz7jS6Bz}T8-;?= zo#cG@B>pQvn7x$%wVLzclUYp0Stj6KfLdh(@QF+s<1iC~hgxNP_=pXQXc^0-F>drG zc+vQzkDg={6BtYEZ<>-b}&;4qI@J@`Zii2)>D+1!GS z;Z+|#fw&S#d>=+BILzY}A3lLD6G*&k=%yIMmjm##8#)4scMaVXV|XP1KYIsHAo1Ep zDL9JjvIkECz`-7PO1EP?PXb}eJtkI%FZuAvK1GaUkt!UT+IVSTmWHAt-2}3 zaMg!TCYY?_k5MoVvAN{KN5^F&Egk1KQyrOVg`t4q#Q=N+FJdED@JzX<6HPC8@JSXz zQSF&>PbZqL`0xqba)OU{4c&r`;R`-|0@sj0m}*5QntpWvJ~G90kaMj7&r~Zq(ew`o z;AfvmMC};|zq3L%BZUp+|h3 zgG3aB!R$*U9C7!25ItD;NMW`?a#O2QF$x{p5l+9E<{YLtW{?ld1gk)>yhF;KZU zgiZyzI~IN@Ld8LOXX+DXIEIRco~#>T3}8^*aipW{p(isni~$CqV}@fW`RKEUafA^+ zo=hpwCecHWs!zo3Ftmv=yg=xXcfm)Gbf}RYg@w+PNIE2y2f8~JUQ2Wa9TpBSY&=qM zP#NdLgPWkXMyZOeJwO;TNd+6j^8xq>*9Woj0Abj8D%cpF^Wh^ZH07wpd1hcBKKxgx zU}JbT1V6Lm2l)8#AMkWI@Rd0`ND8(2)>%%7)*&%pF zFG4BC@N@uv_F*rAXLKW!Vhm|2d!$yh+$DkBGv@ZFV5k+%Wl!J}INbnYs+Bm7|38E3cJ)v-IsH zz4*oACyO60{AgjbP|g3<{I%S#<^EJon)~UwcjunW{+;aCWHXu1vE%<)QVdTx3Q6Vy zR8D8aG;WIF$wnbbtW6m4dg-PZo@k)%vrldkM!Z_ODTXH*ZrwwI7wJ9X3@%~Bs|}?n z2CZ^%-_H>`XV5B#5&B#*bVeuxbWEDkDu)UB>`pIX zFtrJDrnJgofeV%(iKFA@%D>SM8jLz0QA}Qh%n;q zmz!dE3mbr*AOHh;y!|3{&hXYX?+p;TVt8vB^0|lzMns6oOuPXjblLEhG`Q#qm@WZjIDuGb)`Wo8Ajj@x{rT$GRiCc> zAC)_m#qwV--zfb?>Dx*wzLNf=45SRC45SRC4E+9M!0?u16q8(+h%j7Z4i;&u;jP9f zCb{7o&w36ZB-Q8Xs>_-K7RC#Bf8RBsZ541XGz*u|exF#^8x` z2}_F^TBZsOT81$KpNfG#IBKDqGgV3D8{R66gtRE}!JJ!c!&`)rgxUpDX*nj>1sp5|nH;U!L`-PYDzmoq<{%r2YbDi9wxxX>Dmi>+F zw`S$cPw%_`KePd0rVd4Xp4ZRG3hs4`8s$JrXam4Z4eoq7vn&U?o*x1Heokpa(KF#T-b9ZU6}CL)0S- zcTPS?bg(Uu650STrC<9gvkwwAqJ9ciQu!MIW*dj}L6~xneYueh07=M+8tFj6p$z~L z-zn145y!v>VX76aAPj5(h_+=U3}Q7cQ>|zPVR!>Th@5?$Sikm6wW1Y-Uc`AQ7nu`_ zC^3tisTG8N!B|1)g`9w&#nJRYrdAO8DOf@1#hi!e0G?1}6`frPbu&Il)HCr@u&2oj zIuFfudXNvoR4ZCR=tZ5V{VW~456@I95i7+h>~x)3TA?2joPD=;_~=Zn!r356*Z=$I zOsU7&Ac6J&37KIEJVt@vANGM!wgfw!S(;M1?N6ILoNXi-@}VRYqLeT9z4qs zW3U&3=`HcD&2~Vl56^Jv0T189TjO1u?L0~!p5c1{9xlDN$Q$spjHCJR3|kC%sFkuj3^@FG=UKn^LGX~RintnKTjfJUCC*P#8gw>;>+BLOA@HbG51DZvA}Viwirlyk zaMvrU&g5D65K$xUr$`MNmKlN1WWs%js0k0GG-y!9t9g)?h$9f`-+hRv0S}}!Xh>#U z^F(saSk+1zz%^(mN5JpNdyup}Ck`0kmLxwa`it&4M&_V{CY zq;d^!B*tYXB<Ykv}0|rx@uxY`Y`LE12UEndpGVi!|Pxoj3 zD~XP|2L@B#v1!4Z`FHCcl$itddq8J6GE}ZV^AGe{!Xlp;rZ!>If;aQ;q8~h3_tBZ! zglz@h%s+UaC1Ui^nY{-u8Z-nm>Q`qAT6}b-L}Jqd4Z%#%BOU1hHq%3AIHrBa!hafq z8KEaSq95o?iNwNx8iE<4C$lq*h5z(aszF0A6ZAxufU)wQ&=tcQf~h1r1rQiaiR5I% zJAt8+=n#FNGbNIf`~?6&pM7TmJwMaFbArDB0L&!n`8cMx#PR+D0GIcmC5WnSe9tE_ zmjBaJasC1TpeNBLiLw44KB}%aXz<02$0`wv8?l%SR}8n5GE=ls{Qs_h{@+uJ|8Vi` z#f63MU3hK&*XO@=Ua9^}^}XtcDu1`~M=RO#=gJpLzfk(R(o@BMP~0q53g1(BCI74W zZ_J;|{X}jzcX;k^&fU!ZX7<~%O6Ff=-rX<$HwF|RyLz@TlkY9*&kOoFNmHk`#s^Yx z9wtS|6Zjci%m7HF+X|%MJWPs_NANR^w*^2V{Z=r=7*LcvfuBJc0w57H5lk@#-tvWo znLM=sNW@MAQ;dPPd|_cGS1kY%QRP4i&VluoFSxZ2>T;wz2#dWB5?x>5r_ib8-tq;& z&(Hz*AkjVS!4zZQ#mgXP23rvTiKufh#Ta-2GvH?km;)e8!K8D)y@+{XCa)v_&lF5t zdNlAtX23^AvLYT<$V>;|nSx2jihD8h!c0er1mGh*SRe)GAA3PF;AiL{10YPfr*pr( zsCi+gtBC^e5sxsKVhp^nd10pI3qDA6eL)}v=YD(37XY8gAV)XM2jF4vz2yrFGp#QO zfH3VnhM@*t*HgmA7!-JmEk9s-7A?5!~7_j3pxdk+Eq zLHEJhrxar^GbcJ<&k)f6=)Fkb-Kk~lWb`SZ-6v~^SNC`xx^{7O z?^TuDs!~}}bx)14z0ypK**FP=0Lz9qhQtXF!fr?cB+CzP*j*Bb*gO&l#B4$Wd5{or zScntY|GARxOZTf$-OR4R-1>EON&2q--?{(qob#RUb?%Mpms@?O*6eh5%zjPG9Lr?0 zna66iOy+C`KlEP?zw?>QiN892jX&;BcJhM#7hk{dKi-qa1=;Es>9XV1FD@-DKU02v z@n;tPR`I3WAI^Vj?#1fwRe#UL_}KkR#g*07?4S8i-)yuU=eF72H~X#5ZZGh%a&cpQ z?dE#z=Gx*& z{^y4<)#+|+x)(B9@{a;n0U1MVO+IHaHfrV2SA2rYXNNZyiz~96ef^S4=;%((+h(h6 zHd^ggzvcAeK3fi{WZV^p(0wSKwt2AGX>Q(eoLjLr*Xx_z{${_mU^Y|L~aA-G0}!`k-?BVkyGk_~S!J^K2!Sm5BC^Y_i=kZP;q# zfMw>LZfmPWPN&&wJG&+*eec@F#i!Rc9uj2vY>3)kztaWQ8(&CQZ0?!;Z4XQ4xCmBj zYnO#ZlK1@bX(ac8zS-aJc@+qp>segS>AI}Y?MGbI7(DXno9(H_?GaLt!!31xFW6k6 zFNW;3UMjBWdiM3}qi$o=$JnjjUT3#8vDmS$L_S>%>0JCZi52jVgV}l}#ANriXBLVp zCr)HPc5BqVxcz%?)Xn$GA^yf69zs~Z)o%wZDryBbJZrbLM{$U!v|B+Nlp|(CU98*d zh77aavD{cDU=1U4fTN5tk{$Dv%@Gg1i6bFvnfS-X!Q|6WCGfdhPJeTv!2y2~{PGcP z%CD2r9)ukX7pA2MeQ_e)s3%0%_H$Kg z_1Z&YQP0(=>Zw3%TpFU2KATYa?qXr(+SzQTwQD=C^gf8Vz}YnS`yKbaUz^M9Chyhm zzAp{mopU?G*Vo;4@S4ecQ3$c`eS8SjFh%5~ zY6g>0jh>hPV&b7zZxbO{>ozpkvO4=1-8_En+U51Nt3maMv_Bb3&-2dcPc6FLDPriB2_<*| z`N&B$rATgZ+cCFboSok0UbjPWl6Qe1EAAC>);-a;Nv-aMlMh{P+)qM|l7>YXZv{tH zo_e?=tMb{i^M#f7tg!UT)s0QT{gXdC_r{mHM&HADo_>xk1;*y5s$X3EeEFA4zqs(r z#TN_5bDy36b8}yr`NHVRFQ@;9PcI+KoE%=i@4@pYGo>~Cf#v!KMqRCfp8jC3|EMUa ziuS1XD1T<_(v#0V{`8$K{qbkTwY9w~XUqm~YqqAUoGweUC3C7Q>w@GsO{ppAsw6mq zrs}3(>bC0Gf~M$>WQwN3>kV6w8-i+cyr~JipbI+RbR1r5@DAs2Dz8gTQPyS4(M;V` zc~NTcs%Y~f-!ys0l6i{*raTh#TDx_-W!A1>h}GKdxNjbFRqTS**^y=WJje60zzNEE zPCd_SHGzLrRv+c~3zEdks9czJ0=zCb zseH2`E0XHSrej;2CF)I4kQxoJC<~gXYZ~!sDz>QF4Nlc%Q?g}Cl4S>kS(eDlP0nem zf@BM-rCE*+W^KXZnzruPCcfI#Y)MlUyP=wvZ1aLBYKqA>6v@FAqx-qqF}G_s&4YHQ zyNj1oG)hbw)r;hvobLG>{ch`)Qxnc@n62GL=T7ZL>lOSuTMmKWTvZfaI)qTFw(kls zd~9(<=V@0nFcaFSpE{+T-2M zo!vHGO%W)t30&KAUIF!6m|y96W=(L9uWCFe9>O2h)SxPdPn3cdz2;lAFvZ{1C)&4O z-ZFd7w>Dl@Zd`u+jKT?$-sE+j*K|%rOPjXnSQ>m8FTp)Zf(l#YB~G_3QZ`t;;)o5^ zGP$N=b6UfKpA_(kra<{5k(U~}Y%8L`^EQwqxCybLIGW6Jb_2SrDb0rMG{goxfvqS? zW5nP3&W>4o%IsP#)4|Ir0uh-o9HTt)h_z1BV{xK~1p3sfB+0@dEK+q_ZpE)<7mmAq z%n6YkXuJitgPuv|9JqWP{t*xjS%YJ+c*RzD zS=V?)G96VmRj~;dO8u7%-!bNK7ctZa%-!)HL49nC0=z&E!!e1JCbXTaO*zThl$ShL z3wKDhfFl^!4*$h-sw64sn^ZV)O+Ta=#@`vdT0EAyZ+LxW)%B(n!XPf(?@u?t`U{q= zbTplBOD|v14qiB;$*OINypCRlZz?)0*0LmYBa)!#yuwL}qk=|7G@EL}f(zAo-Qm!w ztBxXy4tjjc<|K<6#O66guq`OO)36j=EJ2asA>c)u__iRbItPWf1hIkUZdjVouqBup zBNjw-5+&O%1mOS7kOGQ3Y2} zVoS22Qiu>OTR>02!HN;ZfUPF{BHjInf>Dg;ygH&_Cx!xcpeGFP%7rQ5M_Mp#0@9v! zW9?%9(lc9kG>&UuKV!oa@C`@eL4<1SHq_IRbi`LpmBFEW?eI-GV1-LROyw1V13mh)6coUvVwLq(l__ql^-_&Fd<`JOa z=5}Xi&(nf_3ELo136v8ic%L)L9_OQr%dN2{w)e>hr+lQ_Y0L`miwP??5iItU{i3jvygK2FkY zQIu@QM0nvyGF%NWBIvh8#caY$!#PkqA;YaWnxoi=wp6DHND(oXB#k9q-)XfEYEPKA zTRn$f#&E(8wD@6WZp6T71QcLkS~1qInB03`mY=(NvG=NQ<5ueoZp7x{6D{gsIEZWt zQM3`#J0b-fnrVR#M2wu!6fp!cMMshx!9uWPiA{-Z|4IJ_whGN*>u zPd(@PIb`S3-$-P`%hfH?re9oXD&W@#x^CcDJu>KcdcVq!Ilx-A^w&z z(ojukwruO@2XND12@#@(yKjo{nmS@g8v&oJ!_w6zhFXGYn(%pggU5fc2(yV89dWz> zLTJp`6f}#s(Q#M`;%?hf;c|6P)Ej0O!bt7nQ2ki-pxUl(RyV5aRjvB3tG`(No$3#elB7Q=11SS311SS311SS3 z11SS311SS311SS317Ge8EYF|HPG<1A<4f}=C-3xQfj3$;@J6i!-YCm~H}X>8jkFke zBbEbigi_!QzfhjPFFTsSF49YPhDC$n1ffj9iz()_8CH-9j0wK@E) z;rF5O-;?-z2ET8^?>q6kir>@teLH^Nf!_!4OJ@Tf#P13G(sgt_eYP_GOYbLTbls$! zun3Ypre^1}BE*lT&t}7WewXOF=*HDWx-PsGvrHd_Hy4&x=0%{nTjqj$ee(Y2q3tiZd*>g49FYwm^HgPYOT^qg^N*CKb_lrZ|CMjfRISQSR$r-pUG=Y2 zf1;{Yey;M()dwm+Sp9{{f2%%Q`Hkwos~-FERJ8Q2QU+26QU+26QU+26QU+26QU+26 zQU+26zN8GKp*>e!&s6_G^?R$Y;zjzCGLSNmGLSNmGLSNmGLSNmGLSNm zGLSNmGLSM5&w!XKWcW;WEtJKkW?H5_7n_{~sZk^udHpgcv!%63rkn!fQkfIa%oQ?s zaI3$mz_5WP;?`5k0qWbr$|F-(`@i&-$nlg|w zkTQ@mkTQ@mkTQ@mkTQ@mkTQ@mkTQ@m@TFsbwn>DC`!wm`p8cOpE6f+?@6KZ4VD8>| zz7)QerUq#Je`V=&nd+}q|M%**BJZD3m8$nveyj4?%3rU1edSi=J(YWwe|!07mw#aS zYnH9$uUx)w>348>`jaw{GLSNmGLSNmGLSNmGLSNmGLSOxM}UD-D}_wIc>^A=pNdj9zFz*9!TS9R_@ck#@uiIYV zYgzBO2Uk7t;PGP@@fkff=866Ofq#+Jv7fv6j=OQq`PED{`Xm&bTEDYRPiVAyXYRr^ zk35j6PCbCeyQtl1pE*voJ$$?pe9n)1(%ovC-CLU`9{=;)#goTy)ycD&%47%_xZ<|i zu2re{;p5Ab$9@H$=e60~eeU9e6K>&R-0Yr z`9`bPY4*=7;p+1bW|pEn1TMCEZ(Ahs9?UFGr1zfev`NC3QM&rT@x?&f`4>>Toh;G! zA2?o~+@AMjBlq4`7f}4b+2iFPh5jXD28)DoHdC6&;6K-ggm`BG#gCwPs<8ssp}9`v zaoy^P;|l>{#|Z+SyLcLV2C@&Hx_2Q&vRm9TcRQq9@5By)>?0>mEX2_3rWqI$`*v&( z$euruS(ql)z3hH$49K2b%`8lAXDq~O!?bDrf2HzFruxsS--agu{PpT@SO0YNcGarh ztUg{9t8c63D*rcT1OB(l=PF-U`OB3LS6Y>4DlhyIF!NM|l!26il!26il!26il!26i zl!26il!26iFA)Pvx$J$}@ubVj@mzK_8=PQy%iX!`gW2$8%Ds2xvJYfKQz!SlC6_&) zoti1RdnK2BBs)Dha+iw`nhrT$$z@Mwy}6HL?ptrJqw0zgp4h0k;L~#$%dRX_vlUA& zqBlLU=-z5@KBDYi;LSgjTp2_24GZoS-t0or6<~Tcq2NLXXAknO_~9voocrzRIf8jt z{HZyCxn=NE@}>f4{eQl~WvVwTe+z%opOk@=fs}!ifs}!ifs}!ifs}!ifs}!ifs_H4 zfkAG5DRcg%lc!IvX0uuE@OtfL_n@=i@7?(Hr`n(Xl+|x_c58c`{cf%O;HN+J!Tr{r zQ?s4gWc_*a*E^Q3tnb#^t*u?BwY$03?QC_OUavYo@0Dvcfqzt1ALaN9lFA9XI=Fvs zIkP&LKY#kJcA-aA(sb3H#^6l?got@V1R&|bXre2q1ad0-foKXkW2VIha2lDr~ z9A8YAFi+0jUKhAE!0$QzR=;)IfzJ|H8a9_X~5c%-x^;@$CDv z*~}+hg?SVHtG{a@lU;cD;kC6AuRbbrk8<1v&i&yvy!PMvEcp5&^SaEb%o44BHV7U> zi`2!0keDTk@eshXB>6%C8XQE&+u)tVV<N)z zg{g=%GJ{=SW}?f+JA8Cr4+)F6Gqoy1Bf*ShoDQHTFeYFKOxZ=F=Ak1|Nl13Qok@0{ zi7ptYeDs7m!?Y%zH{R}}C({{@X-%ANoD84~VJafV#n2g!X-%AFye)tpQNh{#49i6- zI;R>Z0_c%uj8w*wooP*+V%+bc=aP9bj;ZgQY~1Ig3lY`V%+7ULnA{@aYor$)}-l*aXf$?G5v`8$LqVMv#d$e zW#gES9#Qbw`X)_hSrh6tssZ#QF@dJDtO@njD?l%~u~?$cLqpHUqD-hS2f>4FI-4zN zI?I+&QeX1HqY*%)kBtrHH1NWZT+{1|9(c5ip2@ds8q<-E?F^Yf9ZpsAU^n2BM+h@W-E?Ffgavx5xEtn?D6Xqgs%AO|9Ncl z2{990>WZ7XCXD#mTerkt|A*{KXx0;NgU!H{J%0L@&?SHUAF@YOFfv!nl0AO@me56i z{Xb8uPobR=VHIM^9zVxQ=z_ogpU1AC5PGy3ShB}YvJyJ)um1x*Ld6WLPrzWzvJyI1 zr}ck#1&1~lgjzAu3JlRzrZHibm6rd<;K?Ls+LEf)Y4v{up3G2HrYWgPofiMy3IBXD z52iA8U6t#!_CE#>YhUC>Sjwm}Rb7?pwDdm)51VCVg^mf%)J0YBSN?OvOURDG3jp!x ziXt-5QNsHx|1Nq^86!r^LKouK6GW=lU-<`m0w18@2}8_!HGkzF=n1?d&>6Z;CH~5P zE*H^}nR+E)FqEB2{FQ&8C(s#SF!~KD@mKzVo=9f|hPne?@>l+Ii6R+b#4pW3X9Uq- z`FEKK>3f(cre-ku4XW2)`Oi@_5YqQqA{k&XtqD6By_J8UC*qy4lM&2duOY4cyD?sl z`s<+V(M8PIjsqA>YvMJ7mj1`+Qwkn(J1pKAI~hrK)u6Ti33|90BSbOLnbyQB1}*-N z&?y=SF*8e-3JDo~2TEjvR{zK7$vgqiv?lCiq~-q!dNQ41S`#n&>;L3t5*QQDG$vl~ z*Z;}O1X+(bduE$5ZHedH^?x_kNV1&{O2^p7=$5$a|6}m5*-xo{+=v6RSqU;j@uh6F^sDq?tk(p~?bUp|+q&R34( zPx_NGkTQ@mkTQ@mkTQ@mkTQ@mkTQ@mkTL)U1`XOno*h2Fwl-f_+j4gM?s4tfuH)D^ zm)+^!x=_1f-onA{{jO8%cWS+U%eqA`aMZh7>vq~suV(JrwSITM*B^aIN78r9zSFx< zdjSA7oSSzqwz{2OZ`*8jYkO_ea(1XP`s!ZSId8UhsNx-5Ywm6V^g`_>U~1O3v(xJJ zy9c#auXf8hI2}8EfCKb6LLWELMJ2+ZLwnh?!_L~;T>h!9WA;(!=tMrw(*yX9x3 zd)9IG+*_tY>l2}SJ00A8yS3}=?~U$szu#)N`UkcBp1I{*s9oB%Ten;GJ`(xwbxouR zupIg_-B7=ar_njx9?8CE9=Nd0=7ri-=as%&C;r1>d27E%-`FwtK*4SwoQ@eaZG7cI z{Cz2+7(X*0p+p}$ofwH)zy&abA6$3?ig3|#8RNAO8Bd*3~+-?O@{9cntq zRRU`2)(+^x;eBfH9mm=>cUu;27sB??HqNef;0jFILN|hQ{r&T3;(iBjzyYpukNIDy zZJ^P28V<4;blle7C!oGlv)Yc?ZI4iTee+;MP zD!!Ea!}(9my;%J{TtGh`yML*;vbvi6Gau@kjke?9If(o2+(<9*vT|`_eeLFY?dIC! zm)C27g4#pnQp>Jgx_Wc{sr8N8)oV9vSD(Lp`CP4B>UY2VS^krEhr_E-m(!=SuWz~P zLerd^yB*AP`me&u;(vaKu5`Lvo9=}SU9vi+wY|wK+{OG`zjkx|MQC3@11*~6bnHz` zbv^;@y?N=%Iw|Jo1gxE2mCnKmOq{tC&*8yfmmBzgUX!H~#n#(lD8O;B;9^7}4I5p0zus4gG5zu)fvl zwzgWkW_z>QYCF3oD1GnR#>J=CHXagW`D}>VUcZA>wliEvP@8*Zf7`>7IWB@lDR~)K zBzey-pGI;o!1OsL>^)&Q*IHaZ;F@9R_9Nyu29JFDM%L%C&uDV0F=(yh_6S{lxTWsz z1)D4M#gM($OT`sk&%S=$)vU2xcB{A7*=!4p>yw3T$}RZfnn72co`%RA?(`gL1@d zsM~ORl8|AxJ6OJ5h=OZa&HLcKv+3PI z?fI*hu0OwCduS}`xf)eH6-c^3$>;$9JF-4lJA)aqV1`OxLY{p4uTNBQj8 z`NGP3R#c*zv{>h)6d*e%8qwmdQM7ltuQd<8nWqvAC`oo29%U@b(=1$E0#`5Rz`8`I-aQkoMpZI}|Rvk!JYaeIe5dmmT8BypUw#t3uuG#G5b*-;{AD zb_s>XwhZeTg*%asKTchPdg$F9!@E&k!RsgYJLBaO(L(#+Qz_6nXvy zp(Jr_+B6=F__#M}#IYD*5}2fUf`zVk{m5j-;o;+M@ASIt8Wq92T=njqA_?un?470* zGGsZ2_b7h18%y7$_^3p^p}tJ%`*?=vknaCu_gCL|e)R4TbF{Z3W{f3h zTtkOB9f2Kcv_Y-6Ngbv$!Jyl>I@?YU!cN#a)ruU3u(GOeJKDBPkr3e~Y-J_%VKXBJ2(Q`TaP$u>Z59i--9R+-QSO93jr<>q(`jfYYv}n5g5G5L2 z%$U#d7>QLPH%RKW@G!dZS|kxPar19V7&Z|u#FzUaBiQu>4t*mSiM{^f-R^AX*A`tX z9fOVktW5Jr6(x+v63m6pWPn6-Xw`>4IP#Pzjez znt}7xA`Z~6ozBc3k2{WzM8>kD`BR1GE0fEQF)kMr-oO0eW%u%vnfYq$<*JV7qndr} zbj4KE!%LHkD~yX}ne#4QoLs!jxR_U!$;Iy~yBD9x%rC`XtRXezZfCQ%-MRB{X#%{+ zyjGk5pI>k<276`p#Q{2s6YLW6a$$n~aA9)s0`p>zj`R7kAajg6M5<~vAV_YEJ;%67 zRpLDCbMr-H9Ds^2?om-R$%j5SH@=#2i%5$b(5G|R@zsoblsPqkP2>Oj=N`;d?^OOn zWq0{MFSnQe<&w4dFBe}ff42Nm>9eJch0iXm6@R$+K;at;3;9=bznOdg{6Cui%DErG zm(rg zD-{a)!mM&!9Ayz{J8VH>(`UMZ2!o}!h6Ne%vXn2E^QCfOzL0@7zV=Z)JcKH0lV5BJf^-3YDxpW?N9*Vd}}a&f+hhFU01$Qh@$a=Ns6 z+uXH~o$3Z+RV1J4c92EOxo{N=+wqpf$s9vjP$GYLIU>N7yVuq#aCYwS#<4JQ@iQsF zVFYNp%;;>WL?0ANaf0LvK!f_rM{)^=Y6Pl_H%$^m85Htyg5(e=E`W+s`LR=D)BNw&2visE zqDT<=PA)7*_8K&zOnj7}orpY=H-)O`jHW<|{*n1`#YL*PR4A2DF}!2(vD$yHDyRZW z6;(mw28Fq(08j;Z1$ZPVLK!wWs{n6glZwX22kIJ1gh3%2BM8(H4r(D+hEBd|svzky zqeW1nmudW8UCiK||9`Ch>*}vmf3Erusy|u%JJlbm{^jboSHH3PiRx?BgKDShRO{92 z)u*cOsfyLJ)l=1b@ul=9WgulBWgulBWgulBWgulBWgulBWgulBW#Eq=1EoC88e>iK z{>iJ{edAY4`L|A9epSxjGkKM}d;F@DziaX;cYOS6A%AT0Dp$qK zJ?gFIE29^c!t(eWySTquuUMvCPTJ@vV8`TH!CjChnNEt{ONEt{ONEt{ONEt{ONEt{ONEt{O_;O+3T?LGivnSW) zmJd5NDPxo^sps-^?di3-rNhsrxmPRi$`x<|?bO=hp=VXc*UpcwEx+-#GP3&v_vBjX z4F#4S!W2L$dtz(8~(>hcXd8r5HmR(S1Ek$aTmE^zJ- zuj94*vQ`uLM`Zx;7e+Pi&s8&}vtEs*>}bvXc5igYlUjK#a&veaTPvmD z-^87|srX|S?cG}Wx&3UU+7Z8~)~!EKtrou7Fyr3M&Qh(W&$;XW)moOLZ%gsWl``O7LV^YSVl4SP5;7M|=_`@LFcw`O&AcJ_B$mf3INU2C^CekA;b z+C{V7u66eNHD}k^IjBt@aNnz0ZO81k4{GMFT{C;PYIyoZZQE(@)$VLNyW`yU7h=a0 zvV!NE7rV~Hh* zvFYDsSr$Zou#$7d(p|9@^H&|muJt>$J3%sfJDpojZL90-nzmEJLb>HibD{QJ*R=XA z3!inJ^Prfxr1tEA>ZXG~oxMFaVG^DI5KkCNw86@JK(bP9)QnEIWwvWu=8oHZsHcPW zsNL)ykkAdMw$tf4HFFEH_Ij<`4z(ve=&M0m=qeUkx3+Dfq_>CLU~ne!DuXkWh=aGz zxstuxv(>2e0d;O6eSY~@? z@_e-I*4FlUv}>=ok8k1T_Fd!dT^MQT-l*y6cKh3%o>ObKTYEKJ?(|uj2mu)ef)bg= z|I3;Gl&Spw^6xHvVe#|jUoQRP!Y>zJEF8~$cK*-Ky$3~KUO&T2%f~V&hu80W@chYa zX-$7%x&DFCCKLsVbFkNcR1{Q2dsKUrKeKh|$!8yb`p%aA__J^zdsohw4c^vlO;tH$ zT97R~N>P?|L2{g?)Rc5p5*$HObyF~PTg78@G(~qLQ#2J`Z`gv|5LBDvO%0Dz(gmGw zIu5Tjc!zU1mDi=FDC@H2Xr^weyeKuG@-{E>O_O&lnYTD#$|FIqwOhAaX6=gEg+s;T z_VDI0XUA-{FQ7w!gE@~GDLHl?N|Jb4)iyfTEvH|*?3i2o&bcQ$-9Dbh=ZsLVtO|mz zZTI_oy+GUpaZ9Ouo`;k$7J8s>>D@!9TSI-ic*H16MFy>O8;?jOz6kb(Z zZM)yPs@{0+r85mp<(mx|$7>wfbZm>WM7=2rQlkMH@njuQ$FoX6sHxbZYB%tR8QGL< z*^*@00b`aW@^X`NnyMh#f@*1&qk~#ou(+nJJGP0hHZ@z)RK;$nrX}0-;6P0=`Gz7n zxMFlaS3Bl*?WTFq?sRwYa*9WZ&7*n|9YEjdp1;xWw&1CSa~tUC8l5|}8?9IH>ufoM zesfj9qfifFlXqOS}9N-S%z1+v#s_=(ci3*EtJX z22~TehQK+JDD#rjlx?B{u3czoO|HQ=o4Rc_9E(>a720VxRU8Cz8oUK2xkl6Gnu5$V zB_6GznIfmyI%iv!&`>y0mUIECZ8jxSwM3IgcVU@=VrmUX5v&oPH#&RU(9O$^({o=< z5h}6?Rj%E3x_3gPILEX&UTtW4(`txzQ3E3RKX zxbgI@_8DF$J!(2UI1rkaB#>v6!C%7^G+S&44lgQNLkAm8%aTo*S5>Qt4n}V_IU6Rf z;|apN1;2yNN#-0leH|VW5Di&_XRvt1R(M(0cttWDRW?vwLf6) zj{gYiV_T6@@ROA2}?6>eP94{3(+ zcLuK(k7e!~USC-~e=Cn8g~)xn0oGrzY^9^=d|P_?igxhA8BJDgQ{;8@D|}PY zVX>Aap(Bw5MduYxQXCaDDx%p`8y0-1&g%|`eqD7GQFPGhTQ(3cn+p&DJq%*_m4nD$E^y2rOVJrx+6{Gs9SDmnj&aT)5KH1n&@C`N4C(%;32G7 zz2X%Ou25^L4qi(N(4cj?;-K5N6k8TtIWLm1wKY7&yxZ&Sx*uIIV=qgd@OE zIo{G0!EBnchWm#>q4&TwmMZZAyb5ALuBqYH;Hoe{6IE~pCAK6RDup1?vIX=M9IO~| z4A^SIFVfwQC>Vu#&Z{E|c48=qf8-qbGhujFE=&QRadDg6#&PZI zXKZ)^zTrqbh)`|ahI%@ZjtHv>)8G{zI@^Rkn;efI3vG=CR!mqYoIA*A+6q1B9tE}~ za-5CuR}utSfGdN->l{40fM)~B7H`5+sTOFp5dt^i=bM_$!8`&K+}!T$?0H(ykD(xT zV#3t|e%f;kK05DqaVJ(EnV80Cu-~25jDwdqF1~cFwQaXw==a+j@)?msD~b)VscC#e z)@^c2rUElZtE*6dI9iF5IKgs6N#+|+nTBRM2qPQ|5t@TOPSR~rlx)XDc;QGgTn#TG z?zct7Y{E;!IZ!+y!>u@)qu2*X<-)U3G+4{RZ1^oIEjA%AgKjE z%*>4#z#wSs8OONOim`shu%ObdJ|dl&Z6qc<@$FasV%q4EE_GFborp6Zj8e^dEH<&ovj zE$=VCb?L{J-oKPx{N&*1UYWZ;`{UX7W8=Y_wu_+tu7yl?;o*nzq{hh64jeZLo-c`= z*5Y*x)0rh&{cI3Ch!&}f2@&giWU5v%9s+olBwr}tm`M;FZ-d80;?Wg2NoEOeJm{lG zn24OiVPV8SNLhw;3!L#zA6*c`vWqjx&TH&?jR$=6!;kMUF&K@C08KT{_~=ohh48J! zoyfuc^RaOSF~cjyY5<)JQxR!o7J6)#?V`)ZJACxRj~FqTVQ3^|myFW^^zb>Uhzc>$ zW1XriyJ*xr^t?a^zwmE-6bx@?(RV)9SGnkdamq(es54A!;(6okK6)~p;mBH*^PGB~ z51uN6&TzVMGJr0GsfZXC3xjD*oMyZ&fF4o7+58O4MM8GfI1xaPG-ISPmh4Pxf{(o4 zL(fH2a7Hi2G4-93jr)9bA!7QGW?;!48%L2wa+2{@A3Y*$#B`aO!L%k$G*&$H`DDEZ z$FwF+Fy7*$N4mO5GcaXmS`)_`_xk9GtVz>Z)`Sw{o&b8J8E3L44Fj3@_C|?uw}(EL zs8zN2XJA8TP-5KWqeCM@PH{%rS=OZKig7%E9x?rh`m^qzWleaPym8D&k0^NL_$!lL zmNjX*WK;v_haTx=qO+_?)5UrP=p{E6OVoK_c8FbVeK`moY}46n31bH)I9Fft!NZ4) zr~25~?nl#@rljfhMGrjMMbG5hHH~RWnpQ7+;1M1ocE!4RjTPi+np!V;;K}SrV?|+@ zys9tw;EBu#3oZ-;)8u;515ai}8qhnH$lDHS^x-7|6y*}rGCmJeYMVSe%)w3RW#A+feF(H_$sJi3- zyBB^ZQ~j>$!J0cOf4q=jC@4xCBV3~r&3KK5Yh(BX(IA1u5AfX*<24$v zO{5R!*P4p=V*-2vi68g4CB|zsWSdAI839HHLUH380*Mbd-4f$98njK|k3RcBAo0T! zDiMv>XxQe%hbEvSwXvj+AE3Au8?Vv8Z3IvA+re>ubb^irA3sDPc;0x8hHgH5Ncu=^ z40w0JXnG2o;5p+p8oZ6+6AZ@zqQsB&-4f$98oo{7k3PgAkoZxMTVlLM1Got}i4;>| znw6p$uh9^01W(h@!KR&Mm_;(i56Y=THn1Pehfid+3RCYD=zS&tfApy{l98$R3U3TQ z8h{V4xkk8(9fJ})Q|}ed7=FZuPcT^wcGl5~T{0H`VDUQ_A20t}`D5jWN7LpUyY)rP%fVOy;}aoZ!Hq)qmG8^SLBGSCbfXt0GEx zgO>lt=m}OqfWeqtC3MbT|Ia6KDnMs=T&mY!|Ig1yeAG;l05BMOP4)Wg|3FV*^T1%J zHI?}5|M~f3t;(3ar52I>^?%5&gl0YQHt5^q?HU-2Acji(^?%5opeF(bqpP42fBipC zt4~31HIuIe2E&!3MDW-D^Votj7Ze!;@3J{`x=ABd&FZ)hDzaW0sX#m8;YG zzq^8yDEI<6)0kAPPRsve@MMxRZAn$@wE8~+PiCmB5drQ=sng=WJK>*C=D}D2JHX{S zt^JR|6GTOTV5+(*)oJN}3?4Si$gqrMrHu1}q(_3k@}DDKLUt66PUF!Pg>iyYL<#S& z{JZEuml82r7CLtR#abBE>#zI+J%JBEq{~osD)CqTfu6uS0)wIJRN}Av=W7(H1G4k}%0JK(@y^)E2wUZKgI50C7%xZtbx`)`A|~Vh z89NyXT{FDdB*=dB<$1zjS`&6MdXq^&4>x0kC>HOGos3kkVt8{&If@2C%tQhk7Bh@E z3FxxnO(o^#l6eB0z+l#Ecr!^rPo^_WYvM(J{h!=S0%KxqWF*=JfBm1lOpx`6vuEBt z)0TM7UH^AujU?OYARl8Jqg&#x|Bu1LWGG6?&=eje_i>S%0tWl*YXFJ z7ni<&>B{1NSp4&gT=}!*Zu#!g|5|!};dd9lc|j}wV)51DiNcQ;-k1M_{CDIZ%l(Vo z>$!K%|DW?8m|vRv!MW$Mzn%SyStavNGp`;#{;v;c-o(|VN&>GeXfk6q(k-bEXxe0i zo}j-XjCf_D616^f)ki9(sc13Bri?%5J^& z!5yHxF?}V0R|W=7xyIR} zN>~!cdwsXw`k?2bC+JgvA;ynK2%W1Bx*mFhSp!0k_xo;1eegjaJwn9{vj&90v{ezd zy62%Mh(ZXRsqdm}3_3t}gUjVYr1y^aop{AlM8;yGKqbat*N0Cq*F+!;Zm7fTL?ZnD;g8Xl8lTFoGLa3b_hN+e2CV@fM=Q&783_n06sxC2pO4Xg~h}{ z!-r22Apo9fRtogq48SLF_kd@b71k;TU**9k8SPWkGR+EWm4nRye57}ZEdDVR6Nlni zCAu+qzYm|JA6G=iS|#pFhFQHY1Rqk&@JN&?Bh##8=)Dnuk1Bd-6wQQZnw1Q_uY2%G zrYnd7rryiwPF@bcN1RMVPgxDf)O#6b^^y;ttTRM{2xi5`;KcxZBBy{n9V~d5)eAm+ zg5wt89@(Q9@DlX?y#e^hs`(73jR~GUZ~EvFMTvL_ z76DA7l9c-3hKHUkz+{c6AiKQ0+{@%j-;ybPOXWwF&Xxc9;bZo+G-V)VAY~wBAY~wB zAY~wB;QuoQhL2aqc{m@Y+`8(jms?kzN>A0e{k*ci#!CkW%?)Jxyn3c7>Jnv(vXIra zfvk=q^7A#2tPcq^xd!s&BJUt?>&WgVAPJ{}w6B__Aq}4`BR5#X#KaF0vkAyPjC^m% zav?Vq8EK3~-V!XNJd=>~ko?PA;P zBHb`vP9@@0u=Q_x`om4zrg;e@gE>vt3FhLes!E6DGIcAnsM_JP$K51f_nqY_&l!Jq z`F)cloZJ+P&pdy*t7(>a`Ni$0_2;fWc?Jpokgp4A?;H`gj2qV_q%cDsL}b}iY~+AN zZbYQtRD}kwARlo9`E~_dub4>jN7+d^q`5j zr_V^brI>bul4tYCIjqRw)X|U{S5$DnmV^Xof}<;(h4h#T^2KqSqo}wV&qPB~LbrB;ZEgVjH=KWwk-cVxgKyj)!D_NI}@NRb&@*OcR-&o2uGqHlU&-GOw6O zlU92gdFglP)f9`dW5^MUqem(g*EOeTBLO$~eM zL89^cspT7ErPQWqd`jOxIN)#Jc)6i$=~wtGXOP%c;T)vYLyAPsl&}_wnMg_~h?JU8 zSGm!oBy^I6@v7Q1EwiD4KxA=5LOvVm_XO;tRFG&9ifqd$M@Cin16@UiLI=rtEdh_0 z6W|??m=g((B|*d@D{@LxreY+OMUKW1jcd2f+6&uA8gAa6POlp~t?L@KKAm1SnjIF% zFRaS4!XLtzbF4h?qB6TmU|HE z|E>A*++Ul!oc+!0pUZNY&t>|ozOg=h4l^3=8u782*R(JhDq&Vjyr3$Qq6eq(!X;9D zc+EqfdA=l!5uC+~l+=e;ee_vZ{zDj{^8%p~oJJU40lK@ISe<<}F#sZBBT1l7jg%O} z%RcFroPx$btI*Ig5y%z;zcrgG!>(qOgg5cD9xCBn& z@c?|HrVZ^i55vQ()&lUeuA+xPLYvJaCC2bEAAaV2USSZXSz)*N@GCv|*>??w;hB1m z?dHR;2*C%1$Pze+>wBbB_Uofjb<-;fGX>kIAX;#>AK74lo zK9RoH55XhT?eGyFJ`$EqwRF5$g;L#y;nAI32*4*&%+NmZFuYP9o(H_U z4qZi2kQ=-WHZ+rZrrL9IeR$48Po|a}Q|qw}efY449#O-Hb7soU>`OSIKBQybBW7k^ zp%1gmjLlIGAx_x_XYQ4<7=$JD#m3iEw}(oOcS`71diGKIo$-uqT== zi*FICcQpRLP2>NIm0zp;>B>8o|BvPE<HUI6g`~SZoD`kE@bDK5(&t#0@J8@5=aV{Mc4jMv~LElNB zM26Au7i2t1ArzQ|OW>d%2*5}8^RetK3=-6)a0wjrnE?FJH>;5JK?fa%hl5@Xz$ZAB zMUN8qAN~@5vr=m=V)p zs0|;CO2X*y&fWp@>QrA6)=?IE&^w3G;hnt`=GCbHa3-B(orVMN@Xp>5^J;`>ws#Io z81&BJ60tt?&X~{c;ldb9d*b1py+h_nd1smv&chA8Q|82bNM|DI&uG<9JQBJq4%S2o z=10CinZAd@kuW-}i4x3vJxc;N$vRkud{BtOy#(`>B#F>)G$Pb-`NC5PMnGPM`AQ)g z`bE5E9P^WZ*0_AL7;0TPS_!xf{0_0r5k`dP!|Fi5;ZAnDP; zdU@t6d3@`rdQTwn9)U{G!Fq}2UHB09Gjt;W2~FaM?+YU!@4R^>N#+TH55|Kbcx3$< zdI!!cxn$%p(*C#=8$<8Jc_qntQvzX{74rTJy(8z4K0-EgjUY?`)2xvAXXu?dccl+% z&Mf^PNzXJZWd0d?ht47W(K{1@kMCxw1ZL%>FRUcVEJPqovqJ8lp_jf8@QIuPBl(Y8 zu`%@07rN3XFe^s(AA*NjdFcx)NfIppgpvP;N?=xA`a(z_(ZmQ>jNXcMkPq;fSn$#p zLi$7=lXZ{}@GvVceW5FTQ17ER!;+q9RyfEv^wJkX`tVFe<*A8-tJd@DVm<^f|19e1ON<+YbZY9rGkSQO!EX zhm+j;X#D@x(e?jhOFz7HbMZefe$%2_{-@=G^8KYBD;W#_ZQMt*>VzRit`JM z7zHI#eQ@1JPhii$VCWRpTOZIK2Dep~lXSwsV0db(w?6RpFf7B|g4P_dzIby31|$BV zlKQ~g!;p~o&@z3XbH>9z_4w6aoz?7z$X~7&?A(YwBgjLw}D|P$%7RIz%z!@RALOg4Gb=P0`3_(+o)nFrni9s z@Uw2*2^c;=n2L#{Z3Axug9{(D+GuSI%?T}{M=CZ3-Uf!HL@__4XDTKVs|~yj41iCh zn4v}Vu=G$&Zv(?pl4MiVfK0u|Lt+Qs1_r<<@W)6R%G7%-q7S?c41iDMF&W9#a9>zN zA9x!WT}c^ep9_@GmWC=ye8rru)_ec)|i zSW2>h1?fYJ=wa?*R^A2%NT0~87?aj+#m2zfzyRqJOk+TLW_N--ZUfrD;Ks&F3591` z6^HZCw0~iYo=EFMEA%1ev4%dN?F*jlL8YIHdEz}nXbnA#jxh=CUKpWAhvgC1&*-VJ zA&XUlF$ry67^5eOK^U3hNNF%8@%An(;kH7|M8?!C37H0kHS_`PU2r4sr9{lAp%wZd z?nWQwb#A%BTdBv^092z#n~Z zP9X6ems@e&Jp$kw@v#K1nK0tb%&oWXo&fOBgUS@Gj6n?}iwDppEQZqXd5j+M36U8X z7J7UzNa$D$rQ!1!J>o5A7{L)ne5d7>)ZOj>p6nqliB!gtJ>JX+9a{@%@H|F8x@#tk z_z@75U~2)5ok!@&6rM4W=GI$x_x`&M=vaa;DKnLAOHaOly*`wSWfB zWAum$&ep1o$tbE9OA_9=xte531RH@E$)KnNOA_9=*@X}3WJHN#brK*9ji-u(8~>B6 zaFdMjahh8a+W3!7H|T33eQ<22OCSuLM+ufBym50iKjqd#wK3u2r=#48gB$-7J#7Lq zGPE8g=tR76b9MFuFMd=*GBVVjDh_V^C-@-u(dde$IgDNe@X&j2+zja>b~KAC#xvrW zJJgU2DzNdN9)A$j`-ppEHLW{q5%U~0R1CfM#?8>2qmLlKy~Nae2`hcxxVf5SLWkfP zJqcAD-1whlN`&B?QKdo>Fxu4M~ zgbp}{ypagA@;Ck`S)V8AnTmM3BbLZd6}Q^gt@WGc|{f2-4tr z0-wmN7|RcIUrb8b42@PvjIB zs~ZFlhfD+L5&Z0@fkG58RyPP9ib=cw$ME=85^|XS9;+Mf_Wucdf)ExmGUFUUqVfOR z@;?z?|L-i{we&ZaUR?a*;IP+>X%ZM1jDQ6t zis8-6xUvVsp-5!PWQM7cSe5p+GeGtP!4@QBL_<_!c(XEvM3Eve7-I}7F}%$Tg(Pdn z)OJj3!jiN%Cj;3N1kpfeS`*J1-c|+|J=Ch<^=VeCG8Uw9SDbEmGcrI=Fe3vDrZr(b znkHo2*>Rer4T=s83TuD7BVavOjH<2EHii*+WNvCkFT+@kCSH^}O~#BRkBA!4{n%j_ zAeg3vrEZ#v8B3mEE(739OX9>jO~knK?uBHoow3kOvSg zo;=dN3`Lh13(+JwSEs!TBk)9?7YRAp)f(OeOg_mrJ3KX)vC2&)nn7C^+;J$-lbDgj zSmk!>HN5GUe3CO$gu&{QP-1wqFD^47o1e*$kouF!yJUEiFL*+I1THeo7H_5!*Y``8f9f-&p*@;x{fT<$qFs zrF>uMM@#jE|GMyP3tv(EeDQxMo-X`k!7AkP-$3cz`TqtqW8zu??ez$?Mr48`jxT{85*~X;RDykLlrO3i?gS7<6hb9vTFPB8)|@1Y36S_O#I4vEQ1XHa{Lwuq zfyA#nQ3-aNp7P+6teO!>ybElAV?Y@U#_-X#l*syMtV;t3Q|~$KHl-8>6ZoSCf&%4{VHq3; zv!WaZ6ZixUnG(sw!&Ak^fRY$Y;1lRQKp1946&nLDi@{=&4wA@Wm=#rQ47@A`F8qkX;#PsFj)5B6Kz3Qnf9>sQsg3ihV zKqbb&%VMyY=rlJ#n7x&X&D@m5z?~LZoC-X`V-=S0Otn|BnVXUrjL?&W-;kZD^(u}= z8L-z+fbMlK2c5%zjGc z>jTPP;GqX&*Jx!7onehQQ7>2js1KgV53|Obf<8I_kNp2f+y8%G;l~Q^%l~2iyYd&2 z|Nm<+{-^W*%X2?CcP;x{Z@mBiRosyq5FSsol}F-OJ3~=|9TNvWd;$~333=A>0xB_v zuY}-(tvyv+%t(dc<9)wdVhr#2@KNoF1aNW6L?H31>XsP8+a7$9ewRSveZN~`4EKHb z1Wp1VtgV|U!A_xm0RHG+g5cwa5^lxDu;;@kaL)u1@BH10jbS$cfAnD-fy8@%x8nNn zgMfGYmg5OR9>RzZdEAovaL+?eGW`qmc<)c>I0ex0(IZ+u!!U&~m^OuU8pB;5JraRL zhPw=tVx2KSy*OjAv_fP2Y(T-bheR#`5kBmuX^ku9q zp;T{u_yM51{o--BvY@AodNu~{jAR`oJI>U!eDq|@Fs(_%nVM}6Js~qpjYKky;TF){ zF7Q~A1bR4r!ibSkLb6s#ZF2iaSrdeTWY51}SpD3z_^h~o-U{){r@Ca{0niGx$ zSq-Q#0uWRTvwG2oPv$WdrdcU4s~3FuWO}bK%}Rk;y*B_KW+PG?lN_d5DKM+&1Mo+m z?t)?>t1CmN=$zwCA3jOg4R~g6MduuEc<_mKbpf7fRx-?LBLIK&Xbegf zjhbe&TGn7070aXb|L@CGzo+_S<=<33QF&zfbIbe7Z(aJarS~so7eBf9RQcbO|4jLO z>1RqGEZx2EHy2(g{(kW<72i|%d|^;n&Hw%UR=$+`Yq=})|8f57=f%07pSv@6fA;TY zzbZSQ`Mx)ALu{QC#Em1%2~1zWmcMw<;+E7&Jx1V3rqKZ&-(3^9R43&aOMdk3jv(T_ z3YCa;QjM|XM<3P@M7&RNtF1rhfhUN60l|0(0fBRdm!hGRWS0Z4vob)qCB{{V?grPT zB*(G|Bc64)#JCbbk67RgV^zY4_q9~Q82k&gCD|RS`!Y`dWQhqRt=&@`zeM-GG>UCC`q3kGLT6eN4g!0J0Km-Ahj>6|oTR0C1BU=gv`s(Ad*oJF`p!5=OZ6)L zA%Ienl?lRNY9!9l(jkD62cC-O z%?y=i@6X`JB=oH61x)sfAv=te7~aHCdG@|Ij1e4BL`n>AUZ_0#KBX{5us@5G7~Zsy ziyl&oh&S}rB8(9<#c+va(BR+gErA}FeD5gb5=OSC!-{zu@Ez+tMR z@LY5lf~oB|2T5c9G5D-~S{R(E>^KKWL;o=?5%pt+S7GpApB5>p)5zcTujPcaFbxUk zAZg$~0*_9xF#EKyd`vsSIY>JEKL$@m3scc?4w8obWAG$4N*lNtz5(YT>FB=)9_s2N zb;U!_Hda=NUZ+9-2pm)fJtI4$4RTH!23g>0oeutw!DsF2!{97!!FIgBkbm}R(Ez#> z&L5Gqd;d6L-huR{IgHGh9zXU7pgZfL|LoJEVc8j-Db?$r`!CIY>Nkwe@G(H=14Di~ zvKI6Mk&$k^K}z8~lA(IBThJf!&wgSpjKT0Ss8=)S;Qx3^H%TA>45l?9izJ==AED2_ z(;zH6Q{Rz9l8*k5(G&8{)OSpFO9Av*C%eP4GkhdClMEX6k7S=c&0rXvX-&u=DfsA7 zPoy)U$?h<^>Yx8V!h~@cgK15ej;7K7n3-grAT-$>u2=HU|6^J- z679~Wk)i46FuFKe|Nk!5`v3PVu9ttk{I%tWOP?uqO2-%emxbqx|4;E-itjG`O5vl0 z)A>*5oqRF(sob;kzZn_-|3T)v-h}agCR6_i$aUkb#hHhS=wxNYH~au4l6qRW8 z5BuP=j&j4`LA3~%D8^S$)S^6lFc-k!MaCXXte|5L=7&P)vvr+e^kDZBF3}9{ghqMx za4U=v4CW#whIc>%=(DPH7=yt(n2`CmXeW--U-U zf*nM-L@>P58RgkSmJkMGlN;#_XLyG*%1M%r1B0>0jY>qL?`dR`6fT6pWCmN@dLH`h z0a2KlU?3VU;f-zpebx{?gu&S0hP%QBw+{x;BjekM+hOPoV}Bc=YX%+6aJ!!}t^Oy_ z$k4DZd{?SL2Q$X#=sSYwk;pL+RD>~@o&dYs=wOB~d(ewTZ-vE7Fc1ybD;sn$VM-nov3Bx)%m@z?*M4rr9%&_cCYr?RO z4rctnot;q*0wD}UucWAPJxjU&Ih;Xkc8HTmzc-mJ4J{PUQNrS(jMGu>BlO%TQdbnl7X9-=0(k z&ecov-4I|@zxRYPpkDKgjVAOkC6h7K#WU;A^6XpYyOeeMrS(KI#(l_U%JvhyLRBQX2KX#yp+OB1Jn3z z5XJb2FU#DYkt8LZI_wbyF{b6E1ZLWoX!$LOVNCI5bFrjiGZ&>?RLffs!kBI;r2x~s zM9Z&11Y?S?K`Cd0E_<0b3@CmH0vLJYaNfpXh#IlK6M%Wa0OIWx#4qNhK+SR+MC}N| R7iqbgD*G|(78^wE`37y$RP_J= diff --git a/requirements.txt b/requirements.txt index 179f7485b89e3ac7c14e40890fe0f89d1b2f3d81..6293689ac15428c2a2ff9185cfef21991f403adb 100644 GIT binary patch delta 48 zcmeyv(Zji+gHhRyA%`K6p_n0?L6^aYA(J5&h)Wn!8HyNefzX&ikHK_vJ>x?r03f>y A5C8xG delta 41 wcmeC-{KK)KgK_f~#v4pBybN3nN{I!<$r-6Br8%iZcDBZP26_fc3JSbj03SRH-2eap diff --git a/routes/__pycache__/auth.cpython-313.pyc b/routes/__pycache__/auth.cpython-313.pyc index b634b0aaf54e614224b70ecb35b532557a825325..b2adcccfb4776e1e822afdb4484b535f7ad7add1 100644 GIT binary patch delta 1703 zcmZ`(+iw(A7(Zur_P*QgUb?$2+jdHeWuRb_O-+3B!3Upwkbt3?s2IZ^0M-~|LgII3J1snLlJh&4-*-LV zIcII(SHmsq(P)TZTUnf0{IKm>OO{Sv>z5l7afnMqrn2`$Vj>C2i4>%qweLjRL>kgK z_jeG;ObgqI9GK3^K{+(yf%fV4(hxWIWRt^kM2-#$a?6M?9WOoqB+FN5B{oEx`*LeX zgSb}Q5pqv49p5r7CptFupz|s6N!QJESWa!pZ~Ez4Xv?QphiNyZB0DGaHf+DHEoqht zdBGOTrV4iCsQlvT(wW&~V<(Twd1?phpf4+6s&g9|IC*#wd0)Sd@Bt~4oz6BP6_i?_ zQ=777;)YbCWm*z8#5RWOo=9pJ8Ks-rorFlFOxXh=)tC}p;gZjtZH>j_g!GZTaOvh* zMb|2drAk&+u_Q$*mnJ36luTt_mF5kQK&@z?&RLpKlXjZQk}4UXEoilTAru;e1+y5E zuwj}-=}c91)Gb50*w`>zaOhA;wdzpY5WZj-TpH@Cq|Kw>rk3k;C#bq&X&1R84C8T! z<9X2zqN`cwNNrzDwRB?+0+^RARt?j_I(U$`eaj(!=oOQn7<(%)@={ncXL(MuNW(VV zjbbzN389=m3LO|gmZVpqaXP`kA58eHuGKXo7^pF z^k;E&9kmF%CXB=`MfdKJmQ|3Jv|8O#*{O6|$a+{H^ty*CN7#AKJ^CX%EDq6|?4tPD zblKyUN=Xnh)z9tlR`$4McKHehE{AHKsqUr(uEJ%ZYqX8FZ7}b;C38GwpU$JG#z%} zhTS@6)aEr(?Th)S-P(A|@&hFyi<-nhfbGKlC&vtVtu#RL`8y1g1oWmf8 zJskG3lgWXG6N%%QrvL{yIMMJx>=ski@hPd5*#$gY^YNHC9jhv470Jzf`EO|QsQ43e zTeAuQP!}{4zaBV^{BbULOdzZOCSMR}ZuLmINa;9R$>cIQq?#%|%+sh_Amh#z_H|~r zpL3`3-Ryp5aR6FzVf&VhimIE<OKKQ{Aw_GPv!HFlef-6K1GBRzllqV&*1iG)+^mu%1WH5z!k j^UcmHjoY;Qq0mbQ*F8j#IP7~M;_|^H^S7TAxr_e+-Ar`1 delta 995 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 E4RkgpPXGV_ delta 3988 zcmb7HTWlLy8J_Vqj_(=U<69EvZYNFBG?(VyZkuhk*-e_wbh6z#RWOYcOnnJw(zHb_ zmUuDcRn(qPq8lHyHGP-UFqaTtWrr{W-wSw6#*kz0&lQqW>Z7Y<=w0o6YAa@P&cNLO< zkno23V@EAo0T{5bBaxE?mU8l-@2mbGaKLs=o5F&pX!wEV!lgQ_AO(>-IdYX#pa)l> z<;);*d(F>v``3NJ+rG6xB+PZK-pd(+0d8$k1cF|~(eHkuaY`gkE$nRiHQdzy5FQ%p zI;E_1&V**a)ELF^9fI)H6NFdJPccgPjiv^wiX|A;kc5BfX2NTMzcjk(<3>ySkC58pZg{#Xj>>{gWVRaeMF_b{ph+&feYsIixfOTcq#aIMb z-%W%KtlVAN6d!UwYk+$#rY^6igb}QqSLb^d!75IsD0o%AM-=7B6eX|9_lQz;GNp=F z<$FX?olH^js(ghIR+{jg7ZrZR31w|KH-VU(_mzF}Y-8w^Kc;m!On_IXqsi-xmoXC|XD}DH8elxNNP{cdva1l0pYw%|k ze%wOISo7R0eBVI92MXmdYi8=YB(sv~9)eI4E+R}JU4~zksb?jul$Eg*;~J7TqxD?q z7d9^wo`&6@T0bKtzy@F1>-)b+t$};!_`r3o2mE&yiw{r9oT@ll3@kmBf^oDgC>&I9 z27&=-bNqpvfrpNU$uCWv$MbJtaRG~qSWIB?HWrt#n8e~8EN}$`m$8_F6Hd3*jD{Xb z$r=nf$IxNo7tU7uDXgNz0v|#EKKTGG7B2Wtr#q^_L!5&^hXpRXpcRRnd^rHPHBg1e zijaTpYmud#Y&il#puGV3R#*MurJOVnUdu_j+o7B^=nsSQXzu@ch0FXqU>*uf0<<8J zllqpHa*CUgP$;klnlW71I~f{zETSEq!BaRYz!mti_SR**AGxa42p7NyxtxN#83BGS zr;M!ofiD=$RhJKRU>57@5h_0A>DFI8)Fmk^GCRLEk+;0Jjj@hko2g zaKCOH{GpKSJ)E_VCSZa^o8_?H?T%$eta@@aYvP!c@eyd0Z;zIbn50#N49G` z&3u57j{Nzl6tiPbEv9D+D(y#BotRB=JEPg|^Xd8Pm~tvVIG&=m=Tna76ZpCHpuGq8 zZ3iu#Sxw_X-zfZ3LtQj~dhju~Ir#X&vnBCxcoOZRHrqV#;%ffP2ztfKN!@c(e&DTS z)8?&gZ&nLvh!3pHG``MWfA~tN;Ggcc!4L@i%%oVF{3ho6x7Sf$OT>|1?g>+J;bAgJCR2uo%T+42wdN zokOD;j}%ouf7`hzPQIM7m=m1G4DbyTyuRGv+4r%S!$OZmq1qJEyHJL3VJTM1azVni z2;j;L=CLSL0A-;9jDW%)&;tJox~hTV)eS_D7P{v7R{$#Qs22F|qRBvbg~KJVc;!QB z4SjB#oI}XZcBAe-Qi-#9#$$)S^Ze*+PF}bX7Vdk6R0mk`DFLuju_9EBh9XP1gZ^>w uErg*1D8c;}!uDRtD+T;MG@PM@PF1ukt_LJPkYm diff --git a/routes/__pycache__/matches.cpython-313.pyc b/routes/__pycache__/matches.cpython-313.pyc index 16fa6afcf04539a47d9c9b7270e09b9c0f6757a9..9b144d3d6dca965eb17f8c56a60dd3bbfb02222c 100644 GIT binary patch delta 2844 zcmai$4NP0t6@cIMKQ=aCzh^ME!D$GI1H{drGYDnS#0`*Sq;+57@}R`Pqp^X<_H2?- ziNIQQQ&lNydZJ}pv!rBd*Q8s=@nFltl*_Ui6}A%`!u^?}t4bm2{cPSiQL-TH(kz$G*euDOBZhD4Yc z;o<3Clet&JaXST$lgeQ2Ixk3mhep(AW+$smkzJadjT~p;e4JuX^b%#9`G^J#XJ29ZHgP=M-TxBZqdHVTtj`!lO7dXr*Y zFqFbPbUs*7kX!Ip%e-4Ge|rZ@vtpDgpV{kyU80p$LOSSyc0Uh?#Ns8Re{D3w>@D+U zyDeIal(Y9SvrfrhqWBsbSaV}rxNswz-wwr=Q-fqNYn6gBa78L$+n#kSA0FMaUK90~J}X0H@gDzerf@A?y@I5QWU~f7EVe0+^+m1idwBw0GLzz4-nXN=|W$JWcx}VS1S&*x9C0@PTR%wBbM$<-gy;CaLh45Y%F1!kM#fTeM z4u_*>wAcagg;IJ}^7NbV{+B|$JH$$1vU|DF-KGgwDQGa;rz`U+fj^h9*aq zS1A*{(qLA*$C+KN&W^pT$9PWBi$7eO1sY_hO(~a#vc_T=9PDxFJ)&DIhhsf5^T1rx zNr}K;qLC$uSuvgCz4J-k<-xASoJ4$tnpfb4mHlr(Ee6E3p4c3WJSm#PYimsv_=NiK ztLXc(lJU+G+>l!`H$2!^Tl-occaOH5>thR1ghk(+^nc@}UhJ!4z=vv^RluUvBZvgO zFg}&;xB6woj7RQ`CA$Wr$)0^{>Ia5;q63Kr6rzLOz0rm@(OUfq+0sLyeihz(q>+6c zy!)#C$H>{(DlJE^eH^R@<`$&GC z@B-mQ!YRUO!b=FhCSyCV{T6AzO`yhBw-R245BDu&-vf87w2)F#X9)8#Ar#vt^E8eU ztc33qen4m;mP))Ab9R|(jZocbfej|nD`D9*~o=dVw_ggr5+8N;pf{PPj?L&l1gf9|~ zabrk{#3egG!#4??gog=t6V?)bPI!y(HX)aJ=3j=(q`N{$6X--{@@1vBD;XP-)r?;q`1|eVSyX?A;nX06`iIB<$X8DD)oH$Znh&P= z;92J-*E!b}K6r)SKFznK`L-$k{_&l^`BZ4iF1~7=TzlF53-^>`&5;d9>kii)Z8+TU z+_G<0O;lZTta-&czWs__e9vAoAx*i0C#C0Ds_DeC2`TLgrt9xd+aDO;{_DVo=|EFD z&@>$gr30a>fz&_yKQP!vwVY!&`*6k;#BVcvppK3uG0pP=A7i@nH8sc!E6g#2>B4#g z@}ftGS#)n83w4)}g6^&A)v;3U?O>BTZsLAvZ_3A?DWfy)GhN-}jIS`gTPDO;>)x#u z;sL|&>V^0Q-JeTW#r4`7Iz}`V#l6}a9)^6>8K{pb$<(#PAq|@bc|W!Em-gRl$@Iu| z%u2loSNCt!{{hA3lIao2;T1aOZ8Xind?Cf>(SoSa+*TI7B;8LXyz2y{6x=x0kR>xF8}}l delta 2749 zcmb7_e{54#6vyvt26s?byc8v2H8_VZ%+C3>|FK3A8*cbTG=+ZH4lH*G-2A zn+XJs2@y^r2#O+#;6H#Bm0!Y$q7f9DnLw7QB>o|P@CSyN7!#xCbZZM*f4!uip8LJ$ z-h1wO=e~P)r*PmUA^WbyVq*Agyb)NVL{DVbiiTWueQ!@FS{Zv<{Ou8ghpRs9_rT_c zIku<)%>i~H`1#t4Yt>1lAG+M2(Y=nAdH~{$&+xs_)U?qtDv-K`aFFm8Ty2`q4}-IL zn(;Ua(J2U6+*}N+nsuJe4NGG=DunW9s~lzTW} zgIamYzYWpjA+%$BnQ4apu~zRy*| zE3m2L$T{9QiTUO(%PK2k{=o>yEG<7ThCQH=MCHQsuL);40f&N@us_t?ye4A{e%7brf zE!+mT0u|`xZ2f1iFIxBCA2|%HC^qZw4;D3tYf>^U#ejRZ%X+N#O}MH9Zm%fMbtHRz zdf((4jpBqigT=65rJXyWb>#w$-5Y`#b{n=fblP`sF-a|(M-PW=_?vl*|7o87)I1o^ z()-RK9*34x?!;t#=>roL@QmT=|9@1XSgZ?w$3qSBsu%u}KnLUmO*wgqH09y;`jTnN zNvCOQjqtBYs%v?6EJ--a{|i&GDk3aeJ*3H(IsEjX9j1qj;%vs`d}wTM;Q4T&!vTxx z&8C?e#kElIl`Agz-swcSxWyek1aIg;`qL^mBPUKq`N$Bcc)8q%PzSPp6v*6H{9%a=Dski6Jb6Vx+hHNuh-|f zWGhC2UvFhxrs-%Y2aMEPU~Xq{>MVbkZ1ByfF-*n}^LYFzVrK>mMmn1k-;C0(nwnTA zo1-aaoqTMBEj1!-Wt=6pudAFJRWI%l@2v?iKRlPe3OmBnJbO_ZmfCw&HKg>kZHNRn zh19liM^xQ|9qK&7KEi$&30EvnUte#KxEg`;%_n>GDkJM{!aIb+gm)1hA>li!9wGH8 zf$CI!jPM?`ubEJOg3MhAkA74xCt(?Z;#9vOjNO^X)-33^PJ-*Mw_~S;x|O9NW<% z1Uw&^ZW>187)PZ9&O~}EMkME+vR!4@rINT*a-{K^RB}zKx-QklrMj~{=eM5PIwU=L zUGl~y?~t@4_S!m8uiKBgA8)857{UR2pOCnJN>AE;i?&sjw zt<6?S{IDUBu4s>{eg;eWr}LYzy?>AR6LzhLO&IuDBa!LRjFwkouES8etyKI43N6y~#k*OkbTbZgZTz zC?k;5$fZ2lPhE8KdbO*Q8x6f!LYad^CKpPGOm0&bWfYtI(MXh)EtDxp0>olwH)ff9 zK+cp~3gk2p2$Gq+kX2Yx9>PN=gE@jZO_?SilvJ9mFU%`lz`zi#43iIKjAnzegH(XF z2@A6Xb6GOyu|})G#Wg@CGirlmC(knC;YOFyom|KwHTl0VFDp0D&0v=@^B6NvQZ!-G zL16QG+FOhtjDnW;RZ z=;RN|nv?5Ixdb?X78IF*2y+k-J$Z$xqEe9tkgds90}-|$0_2mTNDvV-`I)Jz zU_6k?#>l`>%n2kK7`{kMUMMCq*}yD&^BprsMzt`I%2*HqQn-?#NC(8$0ur}4Y;yBc iN^?@}iZV7kTXZo>C@~s-W@ca#01+XKA6bBWuyO$WOb8NKKE+lJnolSIVkq?raqkTAW%`9OImtQj{6vlAm0fo0?Zr z98;2-n5$b-RGD82M>PvsmJ=qr39>Bbn5mk z-89!t(BZkQCX!&tcH2%JNZu6?Nibu(tDz1g@0rRcSa1I7Sj5JtvpK`#4GUxF=1|{b zER1=ZWr9AlGj?nih^k{{+_t$t{w6Eq`OTTBlFVW*OpFGfnHiV_K!n-{77+h~-{u(^ G#*6^&N_w^c delta 346 zcmeCb!g%^JBj0CUUM>b8(7F+wb<=MnUnwhxU{QW)NoukF=8dcwtc*sJrMUl2)=QC^ z>}Sfm*@3UP5=M^ diff --git a/routes/__pycache__/users.cpython-313.pyc b/routes/__pycache__/users.cpython-313.pyc index 76615e3ec00b6aea873b973a36b081a54e5faeb1..1ddb4ab764987c4608dfd4387874f5cdfaf1f14c 100644 GIT binary patch delta 453 zcmbQTm*wkT7T(Xiyj%=Ga3Uiv>*z*aA70jOMh1q^$$@;rlM8v}l(q2!E(IW%+|L`# z0%kno^%4d#z`~A2>BUwM1;%{llhgRzA&S=Webh5`wu%WYPAw{qaZXJs%8YTzPcF?( z%_}L6DM?Mt)h#Ki%r6CUihyKlam?gp{058?i?8#uFq&_^A^ez&(R1@2Ib$})w#^?@ z{*Z8qO+&CYJa0#p>hICy#$oK1P${I4GI7N006t#TWYDX4NMIIP?K5>HIuRpQLvr#4g z2LYe6Q7uIT0n@XWF%by?^v#Rnd5&>bi n)&v1N4+pFT004910+-wb0W!Br6ai5M1s?%8@I<$u839!V`G$CX diff --git a/routes/auth.py b/routes/auth.py index 2e66085..b34381a 100644 --- a/routes/auth.py +++ b/routes/auth.py @@ -5,13 +5,32 @@ This module handles user authentication including login, logout, and new user re from flask import Blueprint, render_template, redirect, url_for, flash, request from flask_login import login_user, logout_user, login_required, current_user -from extensions import db, hash_password, check_password +from extensions import db, hash_password, check_password, limiter from models import User, ESPORT_GAMES +from urllib.parse import urlparse + + +def is_safe_url(url): + """Validate that a URL is safe for redirection (same origin). + + Args: + url: The URL to validate. + + Returns: + bool: True if the URL is safe (relative or same origin). + """ + if not url: + return False + parsed = url_parse(url) + # Allow relative URLs (no netloc) or same-origin URLs + return not parsed.netloc or parsed.netloc == request.host + auth_bp = Blueprint('auth', __name__, url_prefix='/auth') @auth_bp.route('/login', methods=['GET', 'POST']) +@limiter.limit("10 per minute") def login(): """Handle user login authentication. @@ -36,8 +55,12 @@ def login(): if not user.is_active_account: flash('This account has been deactivated.', 'danger') return render_template('pages/login.html') + # Regenerate session to prevent session fixation attacks login_user(user) + # Validate redirect URL to prevent open redirect vulnerability next_page = request.args.get('next') + if next_page and not is_safe_url(next_page): + next_page = None flash(f'Welcome back, {user.full_name}!', 'success') return redirect(next_page) if next_page else redirect(url_for('main.dashboard')) else: diff --git a/routes/evaluations.py b/routes/evaluations.py index 99c5bb2..817df38 100644 --- a/routes/evaluations.py +++ b/routes/evaluations.py @@ -12,6 +12,26 @@ from sqlalchemy import func evaluations_bp = Blueprint('evaluations', __name__, url_prefix='/evaluations') +def validate_score(score_value): + """Validate that a score is between 1 and 10. + + Args: + score_value: The score value to validate (can be None, string, or int). + + Returns: + int or None: The validated score or None if invalid/empty. + """ + if score_value is None: + return None + try: + score = int(score_value) + if 1 <= score <= 10: + return score + return None + except (ValueError, TypeError): + return None + + @evaluations_bp.route('') @login_required def list_evaluations(): @@ -75,6 +95,14 @@ def evaluate_player(tryout_id, player_id): flash('You do not have permission to evaluate players in this tryout.', 'danger') return redirect(url_for('tryouts.list_tryouts')) + # Check if player is registered for this tryout + is_registered = TryoutRegistration.query.filter_by( + tryout_id=tryout_id, player_id=player_id + ).first() is not None + if not is_registered: + flash('Player is not registered for this tryout.', 'danger') + return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id)) + player = User.query.get_or_404(player_id) if player.role != 'player': @@ -88,41 +116,31 @@ def evaluate_player(tryout_id, player_id): ).first() if request.method == 'POST': - mecanics = request.form.get('mecanics_score') - cohesion = request.form.get('cohesion_score') - communication = request.form.get('communication_score') - gamesense = request.form.get('gamesense_score') - versatility = request.form.get('versatility_score') - discipline = request.form.get('discipline_score') - analysis = request.form.get('analysis_score') - sport_ethics = request.form.get('sport_ethics_score') - mental = request.form.get('mental_score') + mecanics = validate_score(request.form.get('mecanics_score')) + cohesion = validate_score(request.form.get('cohesion_score')) + communication = validate_score(request.form.get('communication_score')) + gamesense = validate_score(request.form.get('gamesense_score')) + versatility = validate_score(request.form.get('versatility_score')) + discipline = validate_score(request.form.get('discipline_score')) + analysis = validate_score(request.form.get('analysis_score')) + sport_ethics = validate_score(request.form.get('sport_ethics_score')) + mental = validate_score(request.form.get('mental_score')) comments = request.form.get('comments') position = request.form.get('position_recommendation') - scores = [] - if mecanics: scores.append(int(mecanics)) - if cohesion: scores.append(int(cohesion)) - if communication: scores.append(int(communication)) - if gamesense: scores.append(int(gamesense)) - if versatility: scores.append(int(versatility)) - if discipline: scores.append(int(discipline)) - if analysis: scores.append(int(analysis)) - if sport_ethics: scores.append(int(sport_ethics)) - if mental: scores.append(int(mental)) - + scores = [s for s in [mecanics, cohesion, communication, gamesense, versatility, discipline, analysis, sport_ethics, mental] if s is not None] overall = sum(scores) / len(scores) if scores else None if existing_eval: - existing_eval.mecanics_score = int(mecanics) if mecanics else None - existing_eval.cohesion_score = int(cohesion) if cohesion else None - existing_eval.communication_score = int(communication) if communication else None - existing_eval.gamesense_score = int(gamesense) if gamesense else None - existing_eval.versatility_score = int(versatility) if versatility else None - existing_eval.discipline_score = int(discipline) if discipline else None - existing_eval.analysis_score = int(analysis) if analysis else None - existing_eval.sport_ethics_score = int(sport_ethics) if sport_ethics else None - existing_eval.mental_score = int(mental) if mental else None + existing_eval.mecanics_score = mecanics + existing_eval.cohesion_score = cohesion + existing_eval.communication_score = communication + existing_eval.gamesense_score = gamesense + existing_eval.versatility_score = versatility + existing_eval.discipline_score = discipline + existing_eval.analysis_score = analysis + existing_eval.sport_ethics_score = sport_ethics + existing_eval.mental_score = mental existing_eval.overall_score = overall existing_eval.comments = comments existing_eval.position_recommendation = position @@ -132,15 +150,15 @@ def evaluate_player(tryout_id, player_id): tryout_id=tryout_id, player_id=player_id, evaluator_id=current_user.id, - mecanics_score=int(mecanics) if mecanics else None, - cohesion_score=int(cohesion) if cohesion else None, - communication_score=int(communication) if communication else None, - gamesense_score=int(gamesense) if gamesense else None, - versatility_score=int(versatility) if versatility else None, - discipline_score=int(discipline) if discipline else None, - analysis_score=int(analysis) if analysis else None, - sport_ethics_score=int(sport_ethics) if sport_ethics else None, - mental_score=int(mental) if mental else None, + mecanics_score=mecanics, + cohesion_score=cohesion, + communication_score=communication, + gamesense_score=gamesense, + versatility_score=versatility, + discipline_score=discipline, + analysis_score=analysis, + sport_ethics_score=sport_ethics, + mental_score=mental, overall_score=overall, comments=comments, position_recommendation=position diff --git a/routes/matches.py b/routes/matches.py index 1d1b2c3..10b3b38 100644 --- a/routes/matches.py +++ b/routes/matches.py @@ -450,7 +450,10 @@ def edit_match(match_id): return redirect(url_for('matches.calendar')) teams = Team.query.filter_by(tryout_id=tryout.id).all() - all_players = User.query.filter_by(role='player').order_by(User.full_name).all() + # Only show players registered for this tryout + registrations = TryoutRegistration.query.filter_by(tryout_id=tryout.id).all() + all_players = [User.query.get(r.player_id) for r in registrations if r.player_id] + all_players = sorted([p for p in all_players if p], key=lambda x: x.full_name) current_player_ids = [p.player_id for p in match.participants.all()] # Get players grouped by team side for player_vs_player matches team1_player_ids = [p.player_id for p in match.participants.filter_by(team_side=1).all()] diff --git a/routes/teams.py b/routes/teams.py index 208d2be..6b94204 100644 --- a/routes/teams.py +++ b/routes/teams.py @@ -283,6 +283,11 @@ def add_player_note(team_id, player_id): flash('Can only add notes for players.', 'danger') return redirect(url_for('teams.list_teams')) + # Verify player belongs to this team + if player.team_id != team_id: + flash(f'{player.full_name} is not on {team.name}.', 'danger') + return redirect(url_for('teams.list_teams')) + content = request.form.get('content', '').strip() if content: diff --git a/routes/tryouts.py b/routes/tryouts.py index c634332..76dc584 100644 --- a/routes/tryouts.py +++ b/routes/tryouts.py @@ -31,7 +31,7 @@ def list_tryouts(): - Manager: Only their created tryouts - Coach: Tryouts targeting their org team - Player: Upcoming and in-progress tryouts - + Returns: Response: Rendered tryouts list template. """ @@ -248,8 +248,10 @@ def view_tryout(tryout_id): can_view_calendar = is_registered or player_in_match - # Get all players (for manager registration dropdown) - all_players = User.query.filter_by(role='player').order_by(User.full_name).all() + # Only expose all_players to users who can manage players in this tryout + all_players = None + if can_edit: + all_players = User.query.filter_by(role='player').order_by(User.full_name).all() # Get matches for this tryout with participant info matches = Match.query.filter_by(tryout_id=tryout_id).order_by(Match.date).all() diff --git a/routes/users.py b/routes/users.py index 5632dd4..511f6de 100644 --- a/routes/users.py +++ b/routes/users.py @@ -20,6 +20,7 @@ users_bp = Blueprint('users', __name__, url_prefix='/users') def update_user_gamertags(user, selected_games): """Update gamertags for a user based on form input. +++++++++++++++++++++++++++++++ Handles creating, updating, and deleting gamertag records for the specified games. Used by both edit_user and edit_profile routes to avoid code duplication.