From e3511df4f8b47349cf63e8bd21257a22361e7e25 Mon Sep 17 00:00:00 2001 From: fit2cloud-jiangweidong <80373698+fit2cloud-jiangweidong@users.noreply.github.com> Date: Wed, 28 Apr 2021 19:25:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=AE=A1=E7=90=86=E5=91=98=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E8=AE=BE=E7=BD=AE=E7=94=A8=E6=88=B7=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E4=B8=8B=E6=AC=A1=E7=99=BB=E5=BD=95=E9=9C=80=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E7=A0=81=20(#6006)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 管理员可以设置用户是否下次登录需修改密码 * feat: 管理员可以设置用户下次是否需要更改密码,本次修改:字段命名规范化 * feat: 管理员可以设置用户下次是否需要更改密码,本次修改:字段命名规范化 * fix: 用户下次登录是否需要改密,函数名及变量名规范化 * fix: 管理员设置用户下次是否改密功能的国际化翻译文件 * fixs: 管理员设置用户下次登录是否需改密功能,逻辑修改 * fix: 管理员可设置用户下次登录是否需要改密,字段名称更改 --- apps/authentication/errors.py | 9 + apps/authentication/mixins.py | 10 + apps/authentication/urls/view_urls.py | 1 + apps/authentication/views/login.py | 19 +- apps/locale/zh/LC_MESSAGES/django.mo | Bin 72201 -> 76682 bytes apps/locale/zh/LC_MESSAGES/django.po | 507 ++++++++++-------- .../0033_user_need_update_password.py | 18 + apps/users/models/user.py | 2 + apps/users/serializers/user.py | 2 +- 9 files changed, 355 insertions(+), 213 deletions(-) create mode 100644 apps/users/migrations/0033_user_need_update_password.py diff --git a/apps/authentication/errors.py b/apps/authentication/errors.py index 8f9ba9307..46f3b8fc5 100644 --- a/apps/authentication/errors.py +++ b/apps/authentication/errors.py @@ -275,6 +275,15 @@ class PasswdTooSimple(JMSException): self.url = url +class PasswdNeedUpdate(JMSException): + default_code = 'passwd_need_update' + default_detail = _('The administrator require you to change your password this time') + + def __init__(self, url, *args, **kwargs): + super().__init__(*args, **kwargs) + self.url = url + + class PasswordRequireResetError(JMSException): default_code = 'passwd_has_expired' default_detail = _('Your password has expired, please reset before logging in') diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 89d7b85fd..b336307c7 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -194,6 +194,7 @@ class AuthMixin: self._check_login_acl(user, ip) self._check_password_require_reset_or_not(user) self._check_passwd_is_too_simple(user, password) + self._check_passwd_need_update(user) LoginBlockUtil(username, ip).clean_failed_count() request.session['auth_password'] = 1 @@ -224,6 +225,15 @@ class AuthMixin: ) raise errors.PasswdTooSimple(url) + @classmethod + def _check_passwd_need_update(cls, user: User): + if user.need_update_password: + url = cls.generate_reset_password_url_with_flash_msg( + user, 'authentication:passwd-need-update-flash-msg' + ) + raise errors.PasswdNeedUpdate(url) + + @classmethod def _check_password_require_reset_or_not(cls, user: User): if user.password_has_expired: diff --git a/apps/authentication/urls/view_urls.py b/apps/authentication/urls/view_urls.py index a95342fa6..062044047 100644 --- a/apps/authentication/urls/view_urls.py +++ b/apps/authentication/urls/view_urls.py @@ -22,6 +22,7 @@ urlpatterns = [ name='forgot-password-sendmail-success'), path('password/reset/', users_view.UserResetPasswordView.as_view(), name='reset-password'), path('password/too-simple-flash-msg/', views.FlashPasswdTooSimpleMsgView.as_view(), name='passwd-too-simple-flash-msg'), + path('password/need-update-flash-msg/', views.FlashPasswdNeedUpdateMsgView.as_view(), name='passwd-need-update-flash-msg'), path('password/has-expired-msg/', views.FlashPasswdHasExpiredMsgView.as_view(), name='passwd-has-expired-flash-msg'), path('password/reset/success/', users_view.UserResetPasswordSuccessView.as_view(), name='reset-password-success'), path('password/verify/', users_view.UserVerifyPasswordView.as_view(), name='user-verify-password'), diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index e1a3ab4b6..a43ddf5eb 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -31,7 +31,7 @@ from ..forms import get_user_login_form_cls __all__ = [ 'UserLoginView', 'UserLogoutView', 'UserLoginGuardView', 'UserLoginWaitConfirmView', - 'FlashPasswdTooSimpleMsgView', 'FlashPasswdHasExpiredMsgView' + 'FlashPasswdTooSimpleMsgView', 'FlashPasswdHasExpiredMsgView', 'FlashPasswdNeedUpdateMsgView' ] @@ -71,7 +71,7 @@ class UserLoginView(mixins.AuthMixin, FormView): context = self.get_context_data(form=new_form) self.request.session.set_test_cookie() return self.render_to_response(context) - except (errors.PasswdTooSimple, errors.PasswordRequireResetError) as e: + except (errors.PasswdTooSimple, errors.PasswordRequireResetError, errors.PasswdNeedUpdate) as e: return redirect(e.url) self.clear_rsa_key() return self.redirect_to_guard_view() @@ -241,6 +241,21 @@ class FlashPasswdTooSimpleMsgView(TemplateView): return self.render_to_response(context) +@method_decorator(never_cache, name='dispatch') +class FlashPasswdNeedUpdateMsgView(TemplateView): + template_name = 'flash_message_standalone.html' + + def get(self, request, *args, **kwargs): + context = { + 'title': _('Please change your password'), + 'messages': _('The administrator require you to change your password this time'), + 'interval': 8, + 'redirect_url': request.GET.get('redirect_url'), + 'auto_redirect': True, + } + return self.render_to_response(context) + + @method_decorator(never_cache, name='dispatch') class FlashPasswdHasExpiredMsgView(TemplateView): template_name = 'flash_message_standalone.html' diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index bdb351ceb42856cc8ad5002068b470d0ed9b3304..b661bf7fde6ed307d17345315bfec8f1a3006672 100644 GIT binary patch delta 25566 zcmaLf37n4g{{Qi77-QeZzTJcDWQmaMNsD7Eq{J{crkG)7tl=K}zK-l$S+g^Cp=@O< zsjQ{Kj3K2gog(_b-uL&Tobx-6|MmENJ-_>YT{G%;8owoU!kN&(#XOV48$8L*YkdF=Xqt*dEOuR8tDlBn2hh>V9ean^9qG{-Y9HGy+yBkUN+KqJ9*v& z%F}iByhNOg7cfT`&pSc+b=>870dGfF&kLtvd^gWKNriEK&&!9eb@#k5jK%EOAG6{p z%#YJB0#}+xumtHJu@GkM;d!jddjhLr1+0v3pxRBq@{I4TB~qM>(^vuTVs-TO^t>wA z)*OLnNpHf7I3e2e@?i8^o|hAcU>2N&*>JYG%-n=pz+TLYM=_l7y^BOL;%%#NAJsv| zx1Hgr1`!yBH8BU)L$!O!^4p?zsv~MbJ+S}|MD;ffGvh)`k1H{diO3ov>Szn*!VfVV zPhb|jWd4Ne_;=JmnRUSz!}_QlX^E;Ig}OuoEPpPlC5=!G+M>>^2j)VRtHCtXj8~)Hj_sHePoR$OThs(oP_OYl%z*{^x`~y+BBY;1 z)q4$dVIYQxIt-vXeAm*;Q4`v2>CaKGSBm9lh;v6$2=$tk#e!G~)!%bi7+*&X*x$-0 zTKO`UAMkb&(V2gaI?Jz7XMGWMhBvS@{*Jng#p2z2Tn=^S?NB?@1+}8ysJCPos-MwT zo`mXW398*8%&qtTb0U0yydO{vub@`+BkD|lK@E^G!KK5o6zPgs6I)^p9F28xCu-$) zQ2pfW=LQT%?Qn6did8U7?|)|^T3L70jQe6f44}^TUDS*hqB>lOs<#$(S9YN$e#$(D zYWF=B!!*>&!}`0UsEF#PAqLd(b41iZOH_yLP+J*;xiMf)H>}H`S54d zQHBn16VGlIFeA*01K596tVxD;pgt=7f~DJ-olq0$iFI%=YJyu(6F-FI@HlGacTxR3 zK-CKy=-L-RrOTs^sO~`aU$57zWN7BGs2v%N>S!wJ%x0S_@k!F#u>f90wf_tCx)mDa zwzwRs-Lt66-2yeywy2})h`Lif14J}XKhzEkMKv5}`E#%&=_Qtb0JRfeq6SPs9l;$d z&otPzFNj)cIn++nL5}s@-(d0P|22Ta4O?73OACy}j50KSAx7FOmH(N~B66Hxpk%rKey+T!U(O z1N9o+#qxL`wIfdqcLUZ$UD|r6%i039((b7CG3Ef&+mwiEzjQb=(d&>*MiD%WW$+S~ zz|14uFCgVnTlzX`hsL7X%|{Kq9@XJK)KPqHUPRUV2{j>aq$|&aI^qHWA}T14+PZ3} z8P~S_Mwb5q79+ovmB*ltZh)mnq1sPI^*0lB1Pd&G6{_D2mcIj^A{{tLB!b8vs1A#a za%WQxHN#q{l{H7LyaP7IUf2#d;0g>I&F>1h4#P2ejO%Y0>h4U$Fr0;&;6h}l0^V{W z>UaxkD?ddwIET6?9nCr~?94SiT2Ght`c06j5yM5vt{g}TJE zP&>Ln`Hb(aC8COZF&!Q=KSQnT3(SPqQ61ezO*F%JcboH?weVT;UqfBq*{Ge}j$wEV zi{mL=jlW_*mukTTx5c|qGu&_KPc3~CpCtb@w#2ZB?l+irsG}H*+L1-5iLOTNKr(8g z`%yc35`%98s-8EA{Z}OOB$tr~GmXnvyvW%8gM4+Rxh>k zv#9t9e43Z3dEk_VM8hMHhS)WD5V?E-HS$wA~TEQ5nkD_DYRum-i|`%wcPxAfPjdf%ZI za2++_2dGP$WtuB5YL-P!usZ5Dr)J0w2D~?kXaauJhbRGc1hY^B&qEEo40R{gA!B%F zP%G#$-E|y~TEJM;gx2E|_%W*9Rn&xUV@`aC!QcOL%y4JrLlx9QA2vg+s0V5y38;w< zL!JF(b0%tl`Ka&1O4Qk}#o*gyeu}zNpP}BmpRl0b|Ex*wJui*=bUusfpbh51o~Sc> z$I54;j%KZ;5140B3-}2&;RmP*Wt{2iXG85|DbzTXF!=ZXMntsI7f}t`V|9E3E8$eE zh99EdQpNw8UV?sE)c?el%*}zNnoWhB~t87>-LYH*Q7k)F-ppe>FHmhE{L| z^+Vzss^f^+ZoqPwmvmLsSvR$GE7X=op(fTFHE=)6A7@TSjWZ9mz?G;8?GF&iPvi`0 zz@JeoyK5D^Ic|V_sQ0)yY9f`e05&u`U>VYVFc;1?S6TT^^pXDsYDaG2lNbn@>$aqt z*#xyUuc9uU9}D4d)C%XL23TrtLDfHmrSWUj9k^$D^V|Z$Pz%V3+JR!oP6fQ$mhlp5 z!0xDyMqoi)iG}bZtc>STTbyaWJF+6Et@WWMSQ&LBHBkfBM;+PAs5{mHbqBg)M#lFN zh^WC3)KQE^-SP#f20N|%fTfS4j_5RM_iB@LE`cbZ68A1E{T^gz9%b z2LJxQ+%ndpRDl6PCV!IXHq`3OEk6b;+oeet~*BPGeKNiAC_K_uO}(6>6nDQ4@|s zO*9eJ?>N-a29k&rCh|V&?Dk+jyos7nhWA~?e5jR`#^7s)+L@Y`ZfxZ*qK>33mciFh z6HY`;Xf&#ysmKuoyg5XkB4Zh9YtFd>?|an5ZlG40aj837AC@Ow4Qt`+s1?mXwO@!@ z*)r6bC!;2C*wV*P^)6x#z5gkeaR;@s`=|laFLUXlSdMgQR7WjPJJSYLzbk5s-$D&I z5H<0Ms1>h34Y<+Vj=Fq%G5F8_j}p<0ze9EOvsL)h@-r=WcccKS=3>;P-GzEB&tgryh9$AYO7>qXZ@kiNX=~J0M`8p{z_Pd&JKDH@+4J##hZKaj{tGW__$8dqThRwaEN)lt^9 z?l$*Ct!OHi#&xKAr!WE2uXBIV7>F%MFGKy9zJ;1Vmi7GZkByN3^O6GmsS1rZxc9yz zY6}xkE1!yGa64*+7qJfJ-{>x1E6h&11Lnf+sJk%$^+}#+`AbmWnN3(64_JQS5)pN9 z8?#}^CU-k?U`En~QJ1PDX2!BO9IN47Ovc6d!Dct`3(4-+?oJp-{zzyceGHM6rq3*;oD-YT3wl*_r0!2`t@Uj?& zjm+k#x2+{=0v)k1_s{D?L}xM)OWW+Pcddoh;iulwn&c7OwF1y?SGcX6~MW_|8L#=Ed>a{zAHSjL#sLJnl1Jp&; ze-1U#_NWh8H&lQ9Q9Cdi)$U!?f>-Qj|2311mhlZ1B%Nv%|3YqGY?EQOj-C9^5k zB>e{J2xnjh+=RL_+pz&2#7r2v$CYPC9ZlW<5zVYHYD+7iR!|)^!}|C(Hb;GMwxA|- z4t2|Kpa%NYe2Dc)XWi=tcoFN6ZjIG(B1Yg|+=PMeiIgHTW1n*y>QbG@C-5e!gM1&k zf4kKZ3zMFJx(mxu^>?4Lc`9mW?xOn1bbuYxz<3kuA9SDODu*>HzwTp{Vt*1T zNQKBxc&*a$FPA=LOG(c^%0CUE;;Li(Wdute=eHwVj{7L@c7lI8Lb}xF`~V{TIr0tj zj(*`L5;)~1JOg#4vr%_oDQe|*6T%mmCsyWyyQW}adHwN;DAD2GQ-GrNTyG1FOh`@5kUEJO{w63gOx^R)TU z%FCSNw<5|bV+|aFTEIpuj(adKeiN{QUr+-)z=D|Zyldb?HIXBhPD8CMpBpI~FIuFlwMWs0la0^!Ns9fG()^y-^cRu>5J} zLM%-FY7A5)a)?Mt{1x>L5C7i1_f1e8y@Klab+adGBK=V1Gg0O9QI~L)xgAx0*gRo= zg{ptyd-h+KFU<<>qgImc2e-m7EI>LJYQS=+6+e&axC^SI9;h8jK;4aDsD5XnCbZJr zW*#!X`XS)Hc$dl0m+x0A_zTrxj!VvhW=XS>Sqn9hhNv@t-fVC7Fb9}pu_5(lqb7Pb zKtvt7eWXe#H6l~5B&Kpnw5sE((g`k8~;$>o;bYU%yvDOCMS*opDI8$|RzzIfGj>_=7T zh3cq3s^c+Mo@C|oP;bRDOCLbhKY?2Dx2U814Z|>7n(HU8>BHc^|F3Krb(-R zk~t66?mct8z)Wo&8rw?XSB5qRh8Z`Eh2zoP>?ZpKIxF zP&;)EtKy%QuKc4*H$<(xFKPh+)B?w#Cb~))D%gSQXdi0eQF3u35`U(?_018{)qW8&rj|s1LcY6k~Bt5po96gISlnfWIATXb(jse zn+MG=Pz$+erl9KIu=H=J{vTL=uAhVb@b7_gq9%UK(pOPieg}2-p*Qu9VmSZ$L>7=S4L4xrU+nck4fGpoLg{~X=`5&p0ZSK0 zy%iNvD{N}{?ae4u{k~WX2clm0Sy({t|0W_i@d)ay&Y=d*b<2H9i=oox@M)}xs^1&c z@lbOVs@_D@gyx`jc9WIwHut0I9l_xHf0~Fc)n!zJ^tWAw?5KeXn-x$4*2P@d2n%5w zRJ&f74*Q#fPSGUOd>gQf4dtsG!ZKn=VQHQ+LQANQgL zh`j5{2bjam(dHy&5)>KBnImR^BcaWbm@ z9`m4i3@ea-3NzsY49Bp4yZS{?M^G|gktfZ1sN4L4rDISNd=UL!c4aEkF5Ml%fD>?g5_xU zz|v*zxe3?6-~>=d@dj! zADBBW{|FW*{~Od!+&9Dj;`}vG<-c4(L)1iGGrOY(jz_&tgHaP)j(Kr|r4N`V%rmH; z4L_Kfy^!GV5!Fy{S7+3OM)B{zgA-Uvh6dVZ?m@H#%%opP7*F!C!395cO zEQH-qTRs%EBXh0%19O{sAV5R|pES={!9`TZKcYH#Xz3j3Tsi_ZU^TNos-KroU$!=u z9%hb4ZT%!vyX9tJGZAg+K2!%sP%}Si=^Lm9w=M0ZcN55H7C=q3EUJB7RDX>yKfYx7 zT~Yl+oBfe?0dFJ`t#B4<;1!nMkDBo*4DJN#3-}P#kuTJpVP#bMWmG?}SvnfkUw>3T zGf)#;W96HJ23Wae97i=cXX(4B2I(@m0WzEUQLj}gOV>dS+{|o;nrL@ay>Y0^x(IbC zH=`ze9)th>|EgvDtPBe7qqZ17#10>O^fSix3vzxlcO4QeZ|p;q=6YT!JXT!)3sGN_5wK$Smh`AzW|(ydWPJlV=W z$i(}vjyIB_86QTy=ciFK&7Ro}SQzyIs*KwD4yZHkiy9~iRev6;zm=B1$2^Fd*fCVS zuTeXBDPS2PSzJdsP%F%bs#wmfiE7XoHPJ}3FKWQysQR-le;H;cy#@92e!uyddEN}9 z646hoTd0m7pk|sqtFwR^fx5*NQ7dj>HbV{E!t97zKo2V)jM}l`<~++!c4_|iKSX*` z;Y+JfkAL>A2Ct$f5@QZT4LkyMi^rn|o`IU!V$`=i8P)!*mH%o!G_z)RJ5~_0@%{0t z5K+g^1T)xT)NiXTF*p$`k1_k9c4`P}=O$rgoQ;~$AuB(HYJbVRk6KW84tIBoV*|$b z8WK^%MASqkqZ-aM*IE8yRJ{|Z-|@~{`l6NpXx>8o5c!JI|F)SvS4i;p|GcOPRLvEV zKiHrN8EV)9H9-5MI(3T#KTV^_(bQ&`i>=05EJ}Vds^v+`Kab@||A6{*Wy$TVj9OAF zRJ&*_fMZboFT%pO2K5a+oSRRRIyyr}3H0)~wfayEs-nuDMGe&5(&Mou>CM;-)8!2b z{&n*O)QdV2^;2Lj>bic6bukrdV??<7M0E`3(-i#qMTV}|3~Y}d;Rr04FC_T?H!~kK zk&~!T*B_{Xs^)hSZiY&?Lm&1+^*0&y3EPeOZSh;w4&^A|CRR2;qzV}?;|zQUb!s{I zmt``-dC>o=8leVgZuzg{VA6go{~mR_uUh%JH#W484~Z%>t!d#a5`D>4fU2H|obwqNQhIBi7<7MWIi)iRD1mi@^%= z8&ErZJ3vG~X6~Une28k0sf??T&n#+|!d{eDKn*w>wKE%0{Tx8`^9ib-FYqP2h#Ig$ zS@$8ThEI?VbS0uM)HqyZ_Xw(;Q4pzXAu?F5iy(PZ#u3kOVYt|A~ z-;bKe6x0OfBOgKUedMhRcsnb&nO{WB{1R%w8|GiA4)RoVTVDcI?@3gLbxegbR2A=2l2%9UxK(u*(Wwp^}`R+;KxVt@xaOr?| zgNk{qP%Sc({sKFb_a$~DtRembf$xiVmB!PEH^;9in`HwL_4bj+kCEWRr^LIife3o) zVP)Dq{^a8O&QRJ;*;zI*Isg%P&j4#iW}P^xP)>4rTfV zTvi>zR24k-oTF|t@`n-hKaKE)5Lx_Kg*K!c(NO|H&nm(vez1X!{rOvBdvHJTRU=?i1c6V-5w+Fu)e9Mc#ST<0m8%u3KkB zgHIZD&y&Y*o?b&jTjDb?XDHu#t9usTQ62iz^D52KCgW4ZpzzfBh*V_gzY~5T zOtgt)Qja|S2`h;wP^Kq}@HKUwBApMrTYg{ii}3r0>UhT}_>6*`)|v7WiANKT(^+rI zo+G?Q{!l^=@|F`G64ny*{7&7A#NQ>%r*3HsvoifQ^oPY&cLU`WNq?yKe}a*;XsBU|NxPh5EIr z--3+u#PwU3emVv=J~sG5;)f{I^Mb?6&V$k^a{r4mPzV-iaS3lroC(?rOF<}aw@f%t2zjgG!s}LNzKb;ID9Y)z}*5NH|NT@?t zLVioaE5X|K2l+i|Q0keo5H_!Y9O6 zQl|#C!YB3qHz4wmifyQ{6hEb~FhNfi!oLXfD4UC^7;R-g<2!`8v=7IOly|eTX{f(P z=;=e7L4+NI`!35%!EdSamu`C)iI&!|9BDm82p`cP$&|!TVpIj6pVm?}?wF;7J=8hLt-62?-e^kbbW z|2gW_z}kc!l*L-RZp7CUDp|S#<(n-|G~lIL;(byhEPjRt2T4~Z{VapMWcjPf`(i>p$?$}uwJePh?Qe~XYUJ?r03MpF`@1U(x_XLr%y zpWVnWLBsb59}z!JZdU5(IgD@Pr@<883+m|ENWEFaKd1ahLS3t`vcvj=XMz6E{x6|0 z3*mJdR3cpl-zI$lpHd~BgVsTII)0Y)UCJIkJ}bM6FOi=}owq1_i*y^pVbb4|u0@zm z_?dJkLTkzbhpi+1mFqn+tC0SR@B@YGNzWtnC*6?p0t7vc$ji*fXQWF8|6EFaJx^g? z>Yt)c3h_>aL!?VnHjeNop*rbzaFpKvN>qHBLp7tT zEIyR7lf-M1UlhCGuU7Ui%BE0v6L}T!xb>4jd<7x6|FL9Nr@=leRJNWz)`{X(t?U?a z9}Q=d{w3IfO1X4a{vuW)zXl{NB~`6qN{|m`XAsi!~n?Xkt zdRxW0IF9sU!jF{y795V>ip^B=9)EUOA_s$xAbx>%E3L1()P0)xY4V;R6e9!*QK=#Y z_b9lF>8wNjVAA8qs)W6kzuh|PYvma&Kh4r=vza=z3F!#Ct-kVKB;2<28_LlB-zOuZ zG9RmWllTjC{4t%DB5bkFhm-Foe+izzI2-(Pn!jf46RF$5^53+)<&^Is=t)mrC}DfB zPrZLU-#NVWWPU*7Gx!x@De*g$y+AyUFpPL_!Y<;iaVP3I=kS`@fNe=nq--zgyH@r+ z=}A^@S>C|_84awHCyspGc5${F(IPr~yz_WDlH5n5~?8cK+SWR5daN>mt#fZmaF7jIwb`a0V1#2Iy z_0J!tt=%7#>nTW>K>vDvC*-yG0P3wJeJvg5KhqkiQZ^cXNqC;Hh5U)+9kBAbG}dX& z#%8K)PfyazsGpN`Q{pj?_4lZbe2*~Q%Gwa$Mw|Nj{{N^_o?#UJLI=GF&$}$|Z7MA! z9!6Oy%ge^f^<1Ie8p;PigiiAl z?@E|Y;bjV|;v5`Cc=VhklGzf!k)Q6dvX=BSk@khK8g)LTESdB+tAENAe^GXnvcO{g z(361)tRg=Au@05LhjeZ_+(6J%6=x8tSi9+zk0*VVFxlGHA%2kZ{e&KbwS-#aWnzq2 z+)CP>fVYQ?&NO@;?@@3C*Wo3?RN{KFVHVPjaWd)ea6N7I5pO~MEgWKlXTYz?d-Rl~ zZXWU;Vns4T?f5znFQ?!CS5UZ)@PsPTa24^qxWsy>P5c_+2IU{Ap5=c@{0s6L67CSn zlm7~Ne-hVIh4?apo^bMJSiB19ne^9|@U3pNo=sHfLdFi_(=dea=vhQ$k#(y7f~=ms zboLZ^afFjrHi9-eEdI9n5p_Q%d_&o6@;;;8TI@-B7Y4__M}_yvm}E07XHFt-9hLq? z(9;TEq%6`Uy=T+`;c4oPwmSJJFGEOA-aO1=6IPQqtzA*`ntuLQCvk+vBM41sl!4Hh zpl1$wnJ@$oke7+Dj`SK8@)T!)YJ^A6XViQ2yh^+aWls{e5e`u>iSTdL&*J0JAIWuF z_YTh#@9*XBmf(*{TI{cp*=rOP~@eqW1bjeNag zdql@1Rqei^X7>btoUdO@uh?#HQ^%Lk(;sYD>F=1YTV#yt`=VofF|l#IBYX84l>ANi zLLtxl{FQr0M)&gdjEwhn_4{Ld@&1?uUqbBPO}oVVRH19^z$#v&n5eke=qO)Q>;MLf zjEd(!W!lG=FsP3|Ikab7`llkh#`a6_#rb>3_SZb36B>Ap;{3irvHg7U{fI=dz6Ae& zC4DXxsF2|9TlMo*Xe?NQO*3{BeEaqGJ-0<_;NElx_0IM0V}vkMf!YH_cMoyyQYd zbEGd4%R2kS#deSG<@fc8?D436u)-6GTQY<*u=gT=1h>-{oKk$!nbDsXk56#h@4XyJ zv`=Jw{D9avj;l{pB+LYe|&t>ymto` zWGxh_*S2k&KR#ZzS>vmb`_Bw~kum?AoVC&Q3f?8_GI{!}2_czOu}y3*e^Tx_)vIt- zytctj)>ifL$0bDj=G5!HQZP-7@SH*ipM<3bS@9X97&zmx9Y1VWBst$j0(-m5V|-6!wMHG2x_YQGwMq5k`Bk6Syr!lQTYQI&so>QT<4jY!JA zu6*{#&mbbH%DNI6+(6CN9gA-2K1;9aWeCP~-rc#u{-`QmF!zO~&X%t_-OKq{{QtE0 z&&z8^n^a`|rizjM5_au1*`1GJQ_k`n^~8tWWuHSIW3;SN7~nntn5HQsCIUI%zAH zUYonjwYWNWcgmi3ug#rCos?-)Zhkl^b)6NZOioUjx*>Jl(v%%LT(#ta$0I@-q-@`u zGIjd3#NpRQtiQ5*TgteNt}BvP*Kd0CgCuqQ#MA}5uI!kYwr7u*vTG)Fu5Q|$vS?!R z%O`rIFMD<6Zsw3OZFU1Mb;PQx@2yW+KQCq6l1F)0_ADVUZGG~Nlb1un($+6a+b}$N z-l=!ehowxNmAY+Qa`@?G>9eJbo1e0Kmo0t!x#Bq=&$zmKRPtBn`iF$M%%mn4hG)CF zVz_F%Jv?xsa#-s4o!7=rO}=)aW`?lTNn@|>+L=`BhXGlGTb43y1C^7;-YlLx^M_y4 z)k@p2J!Q)7D?3(S*|qD+zV}jxZ@Bi})@uvjclFZ7u1?v$>FT^uDa+=i%wLkSZko+{ z)Rpxa3UN>=qnF!R{k>yMF{#@1-9_1Wx4~ZOf|~ws&TVd|U2*cgJ555frR-aCEphRced|+a z?o6up$624-bI+~W5{}ey(^4iZPRe_4SeWZDDexH9R+ zpT4A+2gQ;yKNyiyx6Q58t$+A~9XbAMpQ}CmDI}-cxxeSOwLr+6(9-|B5G#hKZW_UT zOI@%wb?tjLT5y}UuMZ6gPgjd=*Ab3h%O-Qr-5caq!K=Xyc(lzsb*1Nxy1H^WHD`Yu z7V>6gCP;mE0`_Wb9CPKvdG2NT`_SEMvb}i5kjWtdcjeiFD?8S?9SnZd-KqSa?39gT zQkHE>eRrH~_MW7a@sr&(VRl>sdoLeNgzNLa-%&c!#Fjn#KRRPQ|J4~6?06$d=Y_k3HKn_l;S$DUJU~lI=J@ z0W8MPfJg5|@O$bK?73?HGvUIyLvBdat`(&Pw@Yz4-2`OHXuW9g1A5C!kX7`oa9-22KL&pCH D-9Wyu delta 21441 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