d%y8xo!yP&^~RalUT6Hx*c&IC zu^msG#E;mUv3K{}s_L$)?&@k#udgh_MX4Cw0L8wcw_P0<)vcDKU*puFM&*@YL|-EZKH8q63fNaD>sU(H!oitF;zeV~wsHT&r)ghS93lYn9f6O;#%2Ga9ZsQI-RJ5xJVN zu_L*A#`@Nd1dYa8vs7%cH(RdpP+4_zupw5189>&CL#fD*t?CBrdQ@IGI9^yhFsK!L zcopLRv9C>@&J+$GPHiuA$wSjPRTsYZ*AxBxy$iev;;4q{7Z-2dxU#$oDpr ^$OMqm5r-b~^3`BZzw?e)<8Zp$ZB(00 z-o>CTTCCP`yBc)Jis(>PVbtuAoAMiCdG*552a2Vhs{^%^idR 9g5SMkn(-6jzQ%XIX3yJvP}cb~FhCU**ewRiFph4!eP+CGxRx>KMBx?tIoD4RlM zt5$s@-h*l1121h9!!hG24e&JsD#1zKwvlRf>>G54h?-J4dtsy)g#Vc*#|u~Id7c`7 zcHAA{B{(xUbA|jP!QZ>L=bp$E=I2vyrQFQrUJ2^$`Iz@<_=9E7)pwe)!=1K&6fIs` zdU@&E((3u8>&23N$EdB UX6^=a&Nz6c1oh9a#w8AGkF=sSYfiR&TM0<(4uD+qzusZFF& z;=^D_gOnj;kMOxc`)&`=z*=d4Qa}eIXhjVEo|T^3VIdL}nrP4NbI0%Y#z14TgIe(& zwOlmRh#U$;e*iDHukXne9?Yk<^FeA0HA~O11AW2^?}|VSQ&MFk$ZA2U@xkdV5Q=m! zp1|T&hca1Emc!_=tQ78h<@PHRnL=GkwLk2771Na-7wjDWdnR#uc(S?OSWXX z4#gBm$2t`wn5^0JC#D$pscRGW#x)BDpXE%ZAW5lh-3^+Uk};>oqG1#x@s8ptu&=wu z=*@1P)$y0hukIS>qrffK6MuQtWN&P-n#FoPjz&D_xp 0mtXJ@56Eb_FXtW zb#Mlb&yBN@{Lkb+lE0kamHUa@hjZ%8@6Y_*naa$Q)Bke%uT0;Xek%K~vY*c0%pRWl z&8fdL_1{cAGx_<+@0fgba(d#QO}sU6HuJ}sAIm( XV30aepPvUqfCq;PSuWi; o&&tin(5eJuA6zHJ(-LeXW3 zoIV07^sWkVr)33A(1j9|o$HoOHW=!He-4k09GqKhGI#HX+nij8D!L||D~^rKy|B1$ zJg9HAD(?Ir%1Irx09O>HiYRg?W6)BOO5(XgaXPxq)6qewtEIPjBvn#X<=nGzI!Lb_ zgvQz-Dhk!aa|h#e2z@#vHO?0TVIu11o{7;R=wTg1s}MobV|9qUCC(j))1me2h_ypd zG%5(^o{rO@_UoW=b_hh!b?MyxI2}qvhgu=7C%tW}p42%IdhV$>9dfS@Rf@4gJ*mkG zk;QX`I2|;iL#_~wD*e8wCsmz_5 ;*Wn=}|z4XM`$e|Y&?Year zZklXXp;UxC@0qc^iYt%=jlMM7ryzpeQxMaoKmk=nNqcE`pMnT;S3z8V;tC)L$xHb@ z1)`T`+#Cwq;!U`j#NrxFmZ+r3GzSXiA`BAJ8dNK)RFTilK(@cQ$Tv %iCV!tE9??@n&V6)z(_K }BQBB frd_7!$=>}z&4C3?7AejHC1Jms;{I$$!i@~Agj1$1p7ccX>6(r(C^|{OiEUC@f}EkI-1OF?DcJ-n%DOg_ z|Amq9{5Or{Kb!xi{BP&~e*QE0FW{sV2_OL^fCP{L5 _F52yI^ z%djf|4=Shkq(|pdJx@4Ym;&+H-1|oIzn1?G`Jc|K`H$w;^4D^|pZk37f6Cp>y)XCe z`9rWPFrEL*pQhia3ke_rB!C2v01`j~NB{{S0VIF~-dhAtr0343!q1?2f5VdXUe#j! zZnXPV=B8C|u-RkjIeiDhyXVq#@?bgtL7e=v>A7QrrE&+-bBALt!_yMROd&nDKjzx> z6Y0Xil>6LZ=UKsQA${OT%CFM@4}bn;>gn{Mxm5T0p01 SB-t@r(sm^19{(sZYr00&LxB>8eQTGXg z(BGrE^uB_- wlF`Tsfp_wt{H=K!4i)x45_ zHlNM?UhZGzek}K?+#L|XA0&VTkN^@u0!RP}AOR$R1dsp{_=`$lel|TacYcTK|L1mt zus&S)XddL`fpY#T{9q2Gjt!K0dIl~Z?!U~}|4&ZCwf+6qX0z#$!okS;|H-NJ$blpM z>;L_eY4~zW?D~Jt1YFwRz2vR`pUl9?L*bL&`v1xC$ocO2f6rKYWZ#jV_5Yr9dgS1N zp7sBMQE=u+Xw46ME#19 c38Xe(J}jJ~P#vx-zvem6`mt$$t!n;13c&0!RP}AOR$R1pY!1 zcrKk C~mv zEdLU5g4E*Sbn1L+cY=9mkEBx 6u6!{Ji9yk5s_Vi+j_la%v{f&QFrrbm~ZII;2|IlTIB= zO-Iz8^3Fw6p4^>I9ZpRL>K0s~tgEVX^YnykdDd6fIXmy2_0@IGKL12IHJ8eAg`HF7 zC)25g)Re2zN6v4J9(S=%`C3C4j(Hb+#i0w&xiwC4-J!E{E~!aZJzp}pH>%$=X|x^xo2Iz6I`Kp z;-K4pnT{%`1)k`B#;rCJX!A}V@J Z$`Qog3-Xu2G}D%Fu -*Wg`&m-0*bm-2dE$}i-P EWA(PhjTxe``fwi$$cjG zS971peRJ-kxd*u>7=k}Y00|%gB!C2v01`j~NB{{S0VMF=CondeO3hwykLTUvIrq58 zkGs#h$CupW``qIhe$2n<9$#>er`@B@k2%dfs_s#7k1{{bQ1>XgN6|eJew-HE<0v=P91lT$K2y{?s1MECy% 5MA1DSa*xlt$AkQs zdB!~+aF0*B$Nl^`{*-$xxW^~m<34^I+v^_pxW^~l<19a>ce}^Dd(62<`2PW>Qlr!E zG3y?u+~Xua?wW9q8TUBu9>>z7lc_X6jPk>-(b37t(EWeD6)^tZ4>I%s2_OL^fCP{L z5 %`LR!qT}c0N`p43D z(@%~5Z(s=iAc4o8z{-WOk<`kEX75$m{mQ!WpuW{=vc|ot#jJX*WmIdd(X{FfcHXEJ zxlGZMDf&Xiu2j(#yLukfU9HU8Y@M~(FomAfFqP*#mDX*ewl-9yCpARnrA1JA>DH`a z+rvx*ks->?odsp*uk21BI27K?FM*DW*LNpk<64Fgd-;8yo J#0TyfD8k67OkkDAWU9wQ)LmSzIG zJF+38`Kh7+f=A}3Ll*Z~d-N1Y96L1~F_g kq<2Pa?oGbF4>l!K?18H(*$~Uw}-nTVc;yJH`Z*NZt-{eS% z=jWirygJ3r?h)sfM`Ra{g6t`M(zU)jDBasy?=tS5EW%lG1jMB?lfL=gYkmRW{_bhF zNXaYW+uyzB7x5kFp5{g74?~d!b&@;Kz2M7p54z{L@Zll|A6b}iJ!n2?uGiPPw!_CF ze4aZ5BFD&tZ*?H#+uIR)))VtB4a9sKeX&CaLG1AS1h+1b;dc1~$DRR!vXtpq1m-%` zwTMyPsrdsSxS(bNy8_XWX}<8&o^W7YARIE#J2|%>gpZMoZ(|_YG1EJ9>?sf{OXIGk z)eU2fMIzTdK4sx>H>b=2!gOu{|I}=Er<1d&GP@UBM?jLEpk2LElDx_V6qS&d-f;>w2WP zef(5uH;BwFq`lT=&1SV;3) _^ lYgc-19C^^ z)7-3(5I4#HI6n;%3+kwA6*P8>)mp6wn*lHXT#=tVl?7q)!l-XvkGOAQq`;I{z_+uf zfNyKKKzR}h%*&(P;2vpibXfG+2@pM8-sM`p)~Ii7c4@kwN*TC5x3J4MwkzSA>B)?H zGQNFX8Q(JZ^D|>0bLiMEZc XBmL40KoR?f9Hd$k%8g19$)chz2o>F~lg7e&H z-=yH={4Q76H!C>Lg?-zClXEE$E)#BFaFR=MBZD(||NpUHY@&lm00|%gB!C2v01`j~ zNB{{S0VIF~Mmq2R&!0pXNB{{S0VIF~kN^@u0!RP}AOR$R1du?Nz%Q({XC@Dqj}-P# zEH3JfZVN +q5KsXqs*+vPCTBD8#mGM^r^wa#TaqL`{>Z zDj2dV6VtY-A{(+mEr(KF5d_&*?!&{kcZ~%CKbm-wDC$XalAIE0=+k=LGS Yb5{W{4eK!5dOd)B!C2v01`j~NB{{S z0VIF~kN^@u0!ZN9Bru&$kL=sms%|i%3BvqndSuVd&HJ`!h`KCF(t;)sbAd9)T+kg= zThNIjNrI_s)KT{CN{<{o$iH7wGd37}&8d0YXs{ 5=k``=|SL6n>3A|DVo(j(`9E zck-Xh|N6V>Al8KhkN^@u0!RP}AOR$R1dsp{Kmter3A{4`6Qlc5e9Iu68QqhD-Gb5S z(SxbZM!>Gw(eh+;{oi^1|DDx_rH}v;Kmter2_OL^fCP{L5 J9sNZ1gDG+34@c}&Gu4c# zC-D?i@$H4H$5L&%TC>@kR=w6Tsx{WAz>5hh)eU2fRjM}sS7q=&QlNxHB%VBfZE5kw zQgM0p!qNwdrO36|(xu{+)gHN0@pvgzQK>v}Z`XIM97~P31~uPUueMml*lN|?&y{E$ zQFA8VdjHW(;n=a%$M3cbbDc$GBOfxp;Wrk~U0#YYC=jmNrJ@CIXgn^7<>Km<8^zU| zmoFDzy|%ovc MuJ9Orzg>3Z??kL1a<##%y64Q4ETo`H{cm zBCM#|tXQ|1b+=SB>-F`xD}99lGVtJvDjRj1K`a8XK~Tcs6Vq7NmR?@EwzPVF>3Xpf zX*H)Fj=NI1c!3LDbHibIb@|5f;^oV?{F6%;%G<9zlPMfHka}y)4XoyaW@Wv;=Kp7J zG?@IeTtCRTh7Sv%q?o90vWC&B)@zm4gG~TD2tpvo-yOOWxqHU?){X>?#u@~g7am^2 zR5u4#U?qk_xlNwV6b>IwZ7+0DLZCTSm!|jE6a7HF3vQdmbe(X2EH2)>ab >q-L9-P>RX$^U)iY5!G(8Z zbF>MGECeaUx3=6qJ)0@4>8Up4YslO|g%t>xjcT(AA=3=~&6BuBE!omV7B8IhUGTGc zywCuRUSf|g!U+U6@eWaL&+pC@UI*h>l8ko|bGuFWzvNh^qsQx?*^%9S%7&TTDFD{q z$xjs8qk3xl$d1g7I9m}cTM}hcsBG1$Z^U~r?R($_VrMvwm-PaVQ%R_1$G$<(N7R(c z*$X4Z!t3*X)0}y7yl{1%560uqj=M7n3C>(RJZN|h*-V1JcW=) R z6%0P+W4cms^&Rym$N?=@Yq=?u2VmdeTMQ2p_8s_tJJsFsyi>@pb;vm3rVyL;^j;y< zdqE<4H8MRYZ?8^g3c8+pOAR=AaTow-23$iS(435+)>rf$!RvN}WNLv~JBAfFHM~)J zf@EiPlp$oF YB9+YYpZ`hOtJk@Zvyx$XL%OyR+NYC9jKwxCF)5Ad90 z!DNgFa77@7DXFp%WVN8w_~3LF2<=eyjy(zYy>k1NiA iEm$S9gu`QQ(&AiNCaKvNyI^&0;+tM
}bGdF$DgA= ?bIas>4_5){dX>S5 z)VFXd&x~CjnMrMr%>3P%_32-koSgXi(H|fE%e$VQ`1C|({P5VnO>KMsjQmFGjyp7u z|8e1ZIn~zPm|r&@)VEq=?Q}g;ICCcTi8&XLceVm+(0f&jxqZ-Rf3R(43ip>mTc~7I z#@!QWTJ;9&o`-Az|Eh$TnjzCT#X%wJQ}?Hq5n=H1yE28FVECE1>U~+SHksOnZQCTG zfE`NE&Y+WX5ZqSyx1+%8hEX-V>BGX%_x5hz&Ga!12!!@WBu#Tv#}J|_pG~wZv~I0* z$`3LuBwH$PZ*Du8!jU7XZ$1`oRR|%&ub=;UDjMpcE8J6mnU=Kl9J -4`zr4eNL;0t=eOB-;idh9CJnwP zeq^3}IK6FW3j6n`z8N6GR+M+yJ1)evOUSQ;2_iVOgLZqcvECV7`@LqhdxI|~uiYP^ zMQzr!I(2(n1`Xr>;Cj92_8jlshJxLr23u!F6ZR^4_m6z)d`6$#dQ-nQ@|o~**)Q11 zgEiK=UvJzEdbXP^1jDf{M}pliH-q#DA5HAr$r?Mo{Dtj1 zOEq8g5%=@nmCVy=wfLy+Tpb>|?d S}w~9`@@(+T3a=b8jTOYbh#qEJrXvzwSd@MX% zTX47azh%Y^^T@@>huLV5N3L;Cf)m3M21H0#*n$Zf>>coy8npQuO3M9v3t?T!xxHIu zbIXLk2iMmXT!ZoTzA st{RqqFrHPaoA;FdMDbPjeORui zzBG7qDd_AKG5Gr+6#9`J6(T{QXQ0qu;u{0se8!Eb;7ah g2~J zemL{_OlDjf`}*|9N53%o#I6geyCa`@*IaL3-IHCpdGYYYOz!O2oM0+cb|hP-f t-uaM&g1eWLc6E z#m=TZ0D=hrEC}j?AQp*!TA-(id{Tkp1fC&{xuWQTAPMdFKapMe(8~T5*C$m`sLm9^ zn59#j5>uoiwWwuLU9(Kt>KX!fEbPq&Yc{!G74S>1XgZa8-5MZ9sdPdUhAB&S3`na3 zToxosh4!Uc-dOv*#*$6hRs||+qDHAIS_V8=ODxr}sZ1qJu)DUj>Km5P^1Tv;icU4! z>s6eLCWIV{6G--kP*E3Sn@J+7MuhguyR$17by;_t$ XRNGCs;&s_t2s{nl1H70{COYCV6rMw!C;zZ*^cRGK#FOy>U53ux;k_@ zqRUFJ!I6tZ5r}X?76er|q4I+!NFp34KWJLK#$=zM1?1R%A*)m+y3oEc!#n&wufsD* z77fu74O>vCtuoWFB*#%r)i7*R)q@V-Y&Eu8O-N(4dp7UyN=2Ybzjp(~BE4gPOtM=N zWU|<}l4V&VkjJi0XIE}r+IPu~E8UQZWGlK&b)6DJGMPba#Zfd-VFELP?%uV<%VKJU zhzcRSZ5A&T?^HxlvQN 9&w38hhUJ N_dXIP?5q5G(cWl)uB zhN4NfAjpzICC8RXH~PD_`mv{01n{~qpA8U;cPgqX*)0`D4Lz=%C?sAO{@bgQp?0!t zQ!s7Q5Sb%WMr=b@AZ-v!(4j|5qSWt}*G@_WhDB{i=y^i%E``-3JERa*m17-J6`AL~ z)rpWp@C8UovPGgW$EFmL17up+F_}YP#DX>)?2sgA>QINcP_jc@BH1BIAUVW31Q}lC zJt-4%h%m|0VL~7?#iWuBk!cC4tyr4sD8w`pT1Zf}VLb^FBM2os#3hm)5@Fbn&EgOu ze4@}^9S=EVND?GDL2>wOMF59jM4>!C5b7{u>4REGs!)majZocw WNJ3dHx-C>QK8Vj zs-?p;hlMit-3RxL^}7*|ph^3t)y)BN(MB2|l ?kU6eE!A4HaR66&H0d6%{4$ z=9OL9mFr9UmfUtyscp)Np+Va^R2K{w%yb9lrNlBVRWgkrV0sME<^D9+FOujBs7Q82 zgsvK!5hPeTKt^sar?@Kx&lN~-RA5wBZPTHUz776_whVa=rmPw<0$1!U(+oEZgj;XB z#E8J`ga)h97=dJ82n_NaUtsZi=@J~iPKU$$o`b`gJ^cRv_4KZh^uL}tH}==Y#-_eu z<|~=s%Kg9N*T%mz_LJii>C5?z>>o`1ZuakuUCDkfeQe?bxjPwQ?BC9Oa_R>rzMQ$4 zElmH= xQ|L*LIx#E;Q@iXZ+GXHBP zJNX~eyJmi5d|}$2_{h}5Nd=6>A0&VT{uBuu9bHK|s%6XYwQZ9~7IhS=S(aj2nkZSS z4AW}qIv1i;Gi_T`Wyow)a|{C(e5S<*TPDKFR=>uD;46lZ28pR?mH;_kGpXQcilRz7 zY^}fq{VEq?FmJL5Bf1L7O}AhRK(v`7GWg<+r9*YEav=={1r2O~!5`MYrl<>!Z78NL zQ$v?EQ@g^2bhr*{HAYN}QITl|JW4NFGEB)}9s`q|RW2l`3_OAH1ssE+6ZR8ez2L~2 z#Wxix(^j|;5lonB!xs(>@D6qvOiQs~YZ0dJ3e!zfyUc~)qUpfm3D$)gg>{N18xow+ zG#$23U_bH_7ow636{xW1r^5!A00Wz$h%jGds!1G+$zSJcf-M3VG7X(jK0aEo+W~1> zu{D_Z!|sH&%!LGI!3Kq;Is~>Fm@UAbjUwAju^dMiEQ!3rg+vL+z?Y+75e~bg6sBze z!c=sXD6s3M7#F#aY`~;dl4U~xuSE;K 5fUhv&y4Z0_Rr3?_!T$^kIIEs!c!D$Izh(H~Sh)jZ2 zj>N!pl?y@FG7b15D>MV_r&4NI1a!d0s-!!L49vI?%!x$k+tBtV^aKb!V#{EepfCoV zj94-ka%^V6zMf{QvO;;y%qBWtia W3OH65G~xR3+g8kSBl{ZnA6E;F4kZ((jOn-1(%O5_w5GMFtX zup ^m@hL6@U!hkaN&pj#cMSR+#2joVf$4Bd5H_I;TnG#jU DQhg zy%h-GXC}N1uWALGpK$N0v!i=;m?+%c^i6oRJo;Lowf6}F*ih}(f92Td^*}Pb^9pxu zVY$%Rt6Ys}?Ai5KE3(qP8?`dr%+5X2%Soa{tB5cO@jbc}X@DM11a8F&qS{gWby1&; z4b$kbS1T&qhx2tVM;fZ{@e>7Enwv)BZV**4;pjx``G~c5 zHdu=_IyPu p$Pu*;0KU`YArro%-i1ITY9Yuss@>?)E3mv0I%=h4wR%N_+fN;4VnnUiAv( n` zlG5oi^O4wV*4MlJALQWSrH<3jM-=uADzN?HJ5-JchJzX0A+TZ7(Rf@Qtp{}O)ax7T zolNaYLK}iS?8sCIfn7dd{jmtIh#h<&fM;?#?fG295W4|yB3%z!UV-gTU*%jRz4nZ5 zU<2Pn^+k_H+Msp2TD!}ewG+{hiur>czVML*1GI_?3tnHa6!GZ}WGuIvbRNoq2QWH5 z9gdg^FDs};lZs5$PB$$^Y6mMJ<~r_+71(F#2p)>)1VQ*>I=JN34(`KtWv6YQog2Lp zG#M9#H&HB9Yu5S}9}IkV4jvqRDUkAJ74U$nfAyJ&T{UB^TJQEA1)e@sI=&u=3}wwN zcrD(&Y7>%P$2Mqg*f{Axd^)1n4~$I%Mv67oYTjn+>%IZ|BZARUN9ChV$I_<~1c8g# zMO}!tUo_Xs71#{vuzoU<5xVgsz8QL8abKj`z78aIN4vcd!Gvdr_v~QY=veqfL}{dd zMW1nJI!Ll4D_ zMN-|M=ScjfN=LQ!Jkbz(gkv;1-1G |ns2_OL^fCP{L5 zEK^usPJME*H8Qg6)0u* Pofd)Or2E3A xy`wCWAkZBN&yib{y78STRMdZuu*oNAwmQ{ij$O32hUY}+Q$ zY7Y4Uu)KO<=>vTM5UZe6yt3LW$*F~fOXc>B?fVd{%Mh$_ruBMP-7wae7aJAHQdKI8 zcGS{JPm$w^miGu(;`GNk(pw96EL0Z!da!+OqR`Glvz2$^SSOYOgS_U@70VDcGQ`Pa zkL2XAx_0XD06pORYuj6y!WJ~@M|R?SI3g;hYRZmeO3AJI+#@+0Tft7f3>OC1ob6Vo z@KI>i*+*~ L|G1o9=Q6|k8i#!A<{#}xKM#m`s}zce30|YKf)Lnss@JO z;WZ4(13Po9uQac|?<8-o;g*C`@Q~VlQGedhDY%0=I;K4W(9WP7_H0M~B*(oQ!u=DZ z$mHSN_8XbPU6>12homci8t9EWnyEOtswji%FYPov#cEAXN>NB|ySG=jH~krHvKPL+ ze&>L)6 +5d+mqWHnZlVfsZY$sEl;{xd{9VAzVkx{0UlkQ z+*yoC|90KF3xh+qI=7Dv91Xg2@7wieD`C{=$~9X?Ypb~v$<2CWM;gD@<5jrXzg8)= zzk>{^$uGbMQQn}@@_){9cxSwbhE#Ie=Ij5F-7#x>JcR_101`j~NB{{S0VIF~kN^@u z0!RP}JT?SE&;QSZQ)T!)4!`_e0MEfM|9=AD4-!BENB{{S0VIF~kN^@u0!RP}AOR%s z9w&hJ|KH;ch~^^!B!C2v01`j~NB{{S0VIF~kN^_c0fEr{{{;{~3cp9-7w`Y?fE|h; z0VIF~kN^@u0!RP}AOR$R1dsp{c&`w^`~UBimO?v`01`j~NB{{S0VIF~kN^@u0!RP} zymJDf`~RmvOXvPSe-8kD|Not1#ga$>2_OL^fCP{L5 |NkWX;{AVsfImn82_OL^ zfCP{L5 7fzbVb z2_z4}@3ZiW_y3cz;Z-Dn1dsp{Kmter2_OL^fCP{L5 62pkN^@u0!ZL5HUZXtWxVj &Aon zR;$uvjeAv#RjhigWmIddQE6KB2CI-A2&r#3+7~m0>(^7Cywozxb+&^tI}oAguPrU! zSSsFFJa>7iSlU5}Qt^0ch(A@kRJ3jzjpLG7F0Nj=QCz)w`Ev2qYs)K(*KQRrE!{d% zENuad3Ky)_T5OFqx^i&MSvS^7#d}7>RU*oAparg44Q2ph8xEx+KennHEL?nRlb=lx z@XZG)159wO=r{M;(#uQNmR8R%T`zY0 ;mGuWEuRc!Y4lJo|7;9{2Sbfo&Q|I+{3htnej%n|}$FOI^ z$(GwU+An7c&E-`4f!m*lMSkFXMY2?t%A%cIMDjb}^6G`94|F4?<6v*5=rH1ZJNiq- zE33l_hWYY%FOQ6j7AFer>~d ?>dqx zY(b-bWGB9ddvC>5P1%u5DY;djdnAWrE7+-*;lkXt)GL|7N1<6~AHlUC6IK*vtE#N9 zWdG*giGMM*Jff3<2JY#|Hx%m2gT~!t`zs{*x-ygcHzV1RPmJVCGj}GBPB@t#82_E| ze=z=3`g-~hoQ8j x?__yTcAYY&Pmn zb)8k#8uhKs=77^L`7PxS@B^gW{17u3hX?KA;>{aZmRCXF%F^l$e+ci`5I2$+!$W9q z#eR*$M>f|xcZ}|xDMa+cjO(^XVrQDmlZ{ +6wu6r8OY8!*f2>XwB-ck$-x^3|J5pvI{-ppK}h6q0mjKz$M Jnud)aBdSG90JhPVmC8TGe}CXs-*9Fj(adOvM=x;vahFipD*d(}C|@4+U <4GNx?(Y#x!!n~$Jx_`d-aPeU}Q#f=e_4ZXaT)J1ff4mS4knVXN z8Qur*qJO5k^NF>qB-&2BvujtR*)m#N&Dgnl=X#@+Fu`_(Sk3Me@xaz0 WXH> Z)1PH7rhu;-WwyMf;=KZb(P95Y=6^u z;YxW>Kt@YfNc6MTR<=KqDU{2p?PG3eMlVM{DbXN}UgUOlSlnd~f__Cqy(c`VK*#RL z^TEBpCBt6!_uz8-^!8uM6xQ@q8y3{C{_YvpYpey?@osm4<4sxx!?7(#f@Nl8S?&uT zjkZv{&;X6G^BG^S$Hv W#^{T3vmTLA+xqYD{JF-4r<1p(ZrAp;X z+ut}|sOW=YZUDI6nU}Bh1guE@C{xh&)Z3$O48|9aKRfR`?N4&zg*gA6bpLksVGgFy zS2|PZ-eJ7ro`4j#QFS|g(>)PPger<=TNOTJ?>gx-R$tyqQGN&xu2eJ%S1E91Ucq zh-;;C`_#kfOri2Zs(m#cl{XRhW}zI0u2~ipB(2XOPw@PZ<{zYPfKQ${Z)Eo`cbVT_ ze3;D??t;nHM3cFNL0D9%u8I^)A7J>oof$qrX^;62>Gp#^7yUkY&oG+%VrF6C#SREp z+;NA`0eOoACmv-p&l@F%)D=y3yfSD@cbj?m_7=o=v^Z>6djlq4&n3<@%k2+tznLlA zhn WxJR`} z00|%gB!C2v01`j~NB{{S0VIF~kiefc0et`8pLGjh{YU@_AOR$R1dsp{Kmter2_OL^ zfCRqw1VZ=!U-;VBhBY7oB!C2v01`j~NB{{S0VIF~kN^@u0)G|+`2GJqBcB<`(_D4t zyQY6S`+M2a)YZw4P5f}?^O?-JH1_rBkB@#~^od;;Qg=b@-TG-?-IHCpdGYYYOz!O2 zoM0*xUPfWdR1m135lvPELn4}K3XW{q>b &by>VhB^iGEt3r-^)0f#QV3UQu*Gkc9U8pUAF! zXl4J3>yxS|RA&ld%+je%i78V4RV4T#6?~h@l&!8I{9Esv4fx(I_p1Va=@m_ dM$&LYOb%4u)M5)leG|L-npVwHjDch<*Wlhv5RYl7%iAXHfu&GQXO|ZMR zwCWp{(ek|#g^Er!+Ur%Ej3$H}iW5lohEP!#W1C4Lsz!wN%e%8H7j;>8o5|4?)fNqd zYLY@7(blQxFons)gx8}8MCp3-CVVY=4Zhi5(Pc^Kbt7_76iGb?A&lmz7?FBNvGx5aEO@2&!;G (6o4s$v!~~$g%xG zR;fyKp?zb9cldo?hi8&38loi{wxCd3Wu{?Cj-#5YVc4ds2OYlIYHYQdkj85F;CrSJ zV-
N|t4fKpwj~on5(gY2PI`u5?2tlC9`A)pbe?$z%qx z6-UuTg$c|Ex_j3aFN>)aA}WORwpqMXyi*ZH$v#2rt4eJ6NfdgE&|b}ES6*A*x9s{v z9l?S@#-WhhC~-v1) M$HbBPNjd&DIY9fCnZ*Ww#V5_v~! z- #XA*MmF$)Z zqlO;WP81R^4FB!b$xu64wkeplX^6~`DI>O_E08vbCFszjB~j{k%WEg40>h#Pq5=aiQ4nc-jc~8oO93o7zbeIsxOfjjXLu6WlYAcqeItnq3gccH1ZCFo& z#0Wyk4snTOheR0mW3xDf2%jjlSI0vR8IlA^PEZ^^TM@t^7*QzC4}?05So)wAk}6ap zeIr!2A9(@g2_-q?NhCWY$ON))%nX%^f~Kf2T^kEc*L2x%ASxgU=n{1d$JBL)8iF9| z28>K3oYGdqYK-V8ulpOhrWrym@6;cIEogz9qMvRBD^DVrbB|4%Gz%1~c7(c`32rl`oQM z1Od}yh%Wc1xqgvEUqD5&D Ca8?%F?M1On&Rc7bo^-E{=bA><81IPfv^ryS_g4$&t^#Tg(3))ze;mE|i!d`^rRR zOgBWGnncxANF7YD7||3_3oYy;D@Ni?L;FUWSfNN%#Y!Z!g>n+|6@ghvUt)$UFwGPp zF|W+=4!_@DN5h+&WWf?lM^>n$Y8tGnb%Qe4WKf8#nB6X7H?}s7_0H~qT2WvE)fZSX zGCf-ZacYwN5d g5t J*ZNqr<93)L`+zU =~t#s>^T-8O37uGa2u-rnpk>a+^ iC1$$rUvk>4MC zuE(khIc=pBiamu=%~YU`G+Bi8k)Z3EJ2il$X6OP5{E7yRR#6pUNbGqsiLvL2B>Mw% zGgwde^(F8J7O4=J?bXAf R$xuh=TDqWs0+niPV@zqz_9L# z3pY(yVGk5q=h;x}P|=3<4oq92QK$n=BEh0mhY7arK>rBsAAlk7rfbLw-BhQ`q;FOb zFBSDCMoqFuiYV&PLjC*LnyRWA#M{|}*_A7cvy0x2h@_YfED>aC!PHHKJkB5wz~q8C z4mD-gjW=#9ybEffTC>)-_^Tp&GhClQj~|i3$u3Yy1TW%TPy~^}c>4S^p$$;URwbF4 zq5}ILFu{{dQG&$n7#eILNV*iv)4T~Z+!5$Cy?Y_j*SlwvjTIF+erE%A$)QUM?aK#v zZzy=Z0kWH7Td?&^RoFFWmI?PeARSw_Y?_KB2fd+YtX1pXTLTL0J}Z6sDo#d}!?y+E zB$C~M&Exp|tHBlm-$lRmbautKK6Bj-AHKh7>jZ!sj53&{nGUmHwyeX-SyQbbUAb@R zy;p6*JVOfEgN0c=|?_!IIrr94wOPo2tQ9VSHQ(nkdS!Fx$_Y%D>;?n1W`&Tup%8 zObV&jAh15OEJAf+!wzq-XXDQp`J0Rv)>x}~o2{?+hFF|TB--KxlD$!2;u7l(^m2`- zu1imays;G-HgPD^Ea*b8)uTHOZ10+|F~uyHRtF t-+f@$Q#A9Rag(em;!g(n9N{{8sg1?UJthqVZ%BI zm58M zT)0^X_o5ujf<=xlTCi?*f?jlo)nI3%n-$~=bjZFjGFAwNTY=vi2_@xNxn#FszSy~g z5OPZ CEgIH#r#8bSwr7HXC-_Eh57wg-8}Ory<#d2Dh5JlVkDC zxcgH*mqRY}TuC%ufz7baP~SD42iZx;(y%K75w KoCY^L`36JZ-Oe}}Xp3NbK29Ln8x>NPKXD0pBM2wKW*Krde*b?* zdkrXr1dsp{Kmter2_OL^fCP{L5 M ;>Sm()89FgYtMXg z`e&y1PW|TOS0-o2e|q9yq`!0g6XQ1@RUgVD0VIF~zFGng^@mSn3WpD;-agYZ%ym}X zFxFV5T65~bUkA=#TUxxaRJ^fx?($Nx6kI43k55f@PuQiRb=znhm&6l7xwv}eMsfA# z<;y1miJGy&O2vCd!xa-{Wq{nht@X-IWjE`M)=qTVtZ9{sE%s*1HK<|K?v{!TW~}!Z z+N{*!f?2QkT(Cg4#VR%&N=1HbRX13)?A9hfpD0poR+@kaRP@&H>b2#S#cQ{UmzHiF z?+~_2Wia;U>hjf_OU2Gv56t5A^W~|s^l J0I;AQ^ zOtscxYpl^FSqy 77 zwQwu(FoZA#_* ngP@})dtiN6_rAgAmUmPm53+<5w>Ae z*8@L9nMVBJwzdq%>9-TE7*?x#uP=s*B{%%;K;ttY)E}e8Qlq{e>VPC_v~ka9833k> zR^ZnGT^VxBp2F*DC(Phu`lW}n +o>KA3>JCctxDJd4 zLrdj$^ sX@pusU0r%qR|iz|2);%&l*-%3AI?k^+H>G7 zbVA5VLye9!26!8xR}q<}Sj;jiTea#NLv>y70r)v{$4wsfBFThzjs`k?#5D+vQxB&z zg~|)5_SJY)YmNHWX0zh{hX;bLSr!!}t&f8zcz#Ip4^lV4Cr_LQbjJWNdGTR3Q@9Hz zR})R{0ZMgMq+t30!_V!^@BvDPnjb9(PQ3AO3Su<#LTY>dQM~A2sK}y5RYM{1LFsW@ z+*v4k!o7VUzNSZY6e6yp2s-A6A5LZpSFWVqVzJ|QUmomhEB8bY!p*b3hyJv1sQfz` z@_X8@f4*>S>E)$sORMLXuJ>nWzs7R$0vEdGPG6T-mv1aDUcP+GKe=?FuVW@x0`sfe zp$xIJI?6LJYNqnS2>gpbNB{{S0VIF~kN^@u0!RP}AOR$R1dxDFpuLjHkF?7plZBa? zW0RAMb066>@7j*Y@1MY(qoc5&4nIe4Ik&G=uYZuR^$)Hdz4DFK>uWXT&ZU*j8#k8( z^@ehuSzAlS`S;7hMs@L$rf)5+oPT3&iQGJ6*c;WFdpPUV9phxPE()TsK*R-6DvIK1 zk({R5NlDaoS@_aF{DFV>_9uViJAU9xAOF$Yt=8u0Q>X6Vzkkv+;Py!U_Ez;|jkQkQ zu5YkY?){QeRjXcGFlujX8MOtt7g0aCSzF`J|EKbQI0FCT4-!BENB{{S0VIF~kN^@u z0!RP}AOR%scoCQwElj$*4E+B8o)K{*Kao2 _YREc+ zr%~XUS*H$9h@GnON3IsEd#oY~M6al#q@7-e=Te)kBoR%}NYeGQX|G*ij|6r}1a(0W zi$p&y(9=XYDQUVO>v31WQc |5HCtki8MM(IR=ZoRb=}e`1opBth4gwhNY35+0_Q^R4H8ZE zPZkx4#QLWaLD9fJCFGx`sHR~{rmY%;ij*1_fhH4mQPLenCPtTak1?w2pCn5|{OigM z_pd9O?4Lwc?p;jC$`U-1CbU=O?8u{x@4%bse^aWC2SdbnxHUdST?b` z{xz6gh1RRxvw0_zD}pM^YOiy#LW-p7$!d}vA_`UG9g--K6rueZ zT1>ShrcHt)Pp`Dv^7;Rjg1b5yoCmc1P4TJ-(r21)4)>UlOt_H!tqH=w=Cp zX}T%c%wZyfo}`;ZwM0WxEko9H$QWIB?wIvp=mKv !UOvUWnf1JJ90S?{Jc0~C9hvD6b(+MeY&y1MN})+X-DY>XLzY^Rb=o&x)na5q zO%r!H(H8=BXz_tYB%-N8`^A&ll}lopD>nV78<%1G=~*Q&CY2%MdJ8Hi)X(x+w*n zdGq$>g0s1?z`KaGR_$$+p38bT5A 2$_1HYq)LPgd4=MJ$#1h5g9I0yni=+P-eY>8wC zWuD{W5&?lc01el^d4i|7S$}>669@thS(44DX( =W%4ejpe#trv;gAvLLm9y^VGb)(0xbn|bI9SUK}}he9m*VC zl7hj=-ZIU4$1A-;_^{j?d~q^5{B+^O2_$ mGefhWW_`U8at2aMUwVrX(R5Af`Vw yX}tiIeH?L~&Ay zF2O7le2QxwLDw}!f>=8qiZ!O#43-uQ+>t4znjz>^qlRI~hRT%CG$FhW c~U*wBa_;$vV3U%~?M z3;Y4%Ja@aQy6SZ~TcmC&Ubt?`eco{2Ip^LgtmxkKlwuYt!IPT|qsbzUPtn+Sh{&aM zN{>Y%p(>*}y1cr*@7{378JjZBR28fZQVJfeB{8VzT=Ec!#HM h|5O^{wOiM4Ip*6gh7gfs@Zj z$MG9ukTr2&H<=K0IzOcuPV$EtG3 8=cA}dle95BOW(UU zvS+bA^(+LJDODO&0LoA!#{(sUz)#xKDx`+=b-zj$s!aEo(t@zlq0R0$j13u~tP-_S ziXcnwe329W+f!@FYoS4xoTYGM7zdBVSxi(kRm5pV8reH`kNwFWq yMm)00D HR&8DTuOK@ zkn><44i9z{2oq>xg69V7EIodDu>0geg9lTrdTX!{GY_W_FpNeC1u|X`yvk6G6K`_s zvz!GlO|u{~&RQQR^t*virkx2cgvhNolE4eB(S-e@@G$xY4uv1r!&8ou)`(MXG>i%P zSrx?z)|ArNPTN;ArnM=i3O1Ne#PA9;BxJP7+34USL9|2&{Q9eOGaE8HOxx}pm7`9% z$uJ%Q%~wq55Vo-O$mIJ^SGV2Ht(__JsHh7f*{rk#)FJkelmXcxLEfuY(wq7Tqw!OB z@ZD^pz=S}$dX!^vq#%N9D5CPQrFH&cj-^-r0Xc?GjePM<2GL8SxtS%$d2=3rHtW5~ z)+ep~<_IS&xrl-Say$v1VI5Z-mZ0X|x5UPy$LTQDEFX*u-c@%lOEJ*BjbbSU45Lxn z zp#3Ic=jp0y#&kZts}UUe-f}C&)IdrXhmjDgaQ2pvFe614#~-%EF|1i07z;X9f-~VF zg*PK2rxA2gG^xE~>|xG&Ij|5icT5$o4?@WaozAq^d=7w=5}Eeh{~+(0^(jGuVPW70 zYT-;2FmzpvT;tHT%&Op9yJqb64)HRQ&`N(4T&m9_7Q_ij QG zV49;Je3IT@-43_cZyzUkSy3!5>KzTp@eZy7z5 GO<>#Ss_~6?Gv{5Kx}zu@RM=&xJ1{3G zEL{b+nuDsOXn1T@eD-$zcwA3pHVNRtb!V~|RA-Um)nPCc#R?>m{)r_xp;&oY|DQX5 z{mjM7uiRPt`NAKof35txvW{OYKg$tVj=*vRmLsqnf#nD+M_@SugCj8cb|>5Nm!`Hn zYRORs89YRpsFVQhRUVCz9A$qRT_eY)4ko%3PYAw6gA?x&`~_iivTQ+2haR?yUqo#x z)D6&laZC?8SD4YbI=TN%OPZms=VN3LjRAzWa 24Z(FvIk9sm*{WG8VbS;|Vn0hJXO zshOKzJx8j3ze6!-9!|Hqly5T(2;*OF=rT-6Scj84U*@eeD<@Op349!fvNCMyWY9B& pI!ZRFR6-wN1E{!_dInWFs@_WVOqb&ZfoWsWgISy@f|dwH{sRk0y7T}5 diff --git a/vulfocus-api/dockerapi/models.py b/vulfocus-api/dockerapi/models.py index cf533fb2..b8b6395b 100755 --- a/vulfocus-api/dockerapi/models.py +++ b/vulfocus-api/dockerapi/models.py @@ -4,6 +4,48 @@ # Create your models here. +class TimeTemp(models.Model): + """ + 时间模式模板信息 + """ + temp_id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True) + user_id = models.IntegerField(verbose_name='用户ID') + time_range = models.IntegerField(verbose_name='计时模式时间') + time_desc = models.TextField(verbose_name='计时模版描述', null=True) + flag_status = models.BooleanField(verbose_name='用于判断', default=False) + + class Meta: + db_table = 'time_Temp' + + +class TimeRank(models.Model): + """ + 时间模式排名 + """ + rank_id = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True) + user_id = models.IntegerField(verbose_name='用户ID') + user_name = models.CharField(max_length=256, verbose_name='用户名称') + rank = models.FloatField(verbose_name='Rank', null=False) + time_temp = models.ForeignKey(to="TimeTemp", to_field='temp_id', on_delete=models.CASCADE) + + class Meta: + db_table = 'time_rank' + + +class TimeMoudel(models.Model): + """ + 时间模式信息 + """ + time_id = models.CharField(max_length=255, default=str(uuid.uuid4()), primary_key=True,verbose_name='ID') # 关键 + user_id = models.IntegerField(verbose_name='用户ID') + start_time = models.FloatField(null=False, verbose_name='开始时间戳') # 时间模式开始时间 + end_time = models.FloatField(null=False, verbose_name='结束时间') # 时间模式结束时间 + temp_time_id = models.ForeignKey(to="TimeTemp", to_field='temp_id', on_delete=models.CASCADE) # 关联模版id + status = models.BooleanField(verbose_name='用于判断', default=False) + + class Meta: + db_table = 'time_moudel' + class ImageInfo(models.Model): """ 镜像实体Model diff --git a/vulfocus-api/dockerapi/serializers.py b/vulfocus-api/dockerapi/serializers.py old mode 100644 new mode 100755 index 284a481e..9c51fe23 --- a/vulfocus-api/dockerapi/serializers.py +++ b/vulfocus-api/dockerapi/serializers.py @@ -1,7 +1,7 @@ # coding:utf-8 from django.db.models import Q from rest_framework import serializers -from dockerapi.models import ImageInfo, ContainerVul, SysLog +from dockerapi.models import ImageInfo, ContainerVul, SysLog, TimeMoudel, TimeRank, TimeTemp from user.models import UserProfile from tasks.models import TaskInfo import django.utils.timezone as timezone @@ -9,9 +9,52 @@ from vulfocus.settings import REDIS_POOL from dockerapi.common import get_setting_config import redis +import time r = redis.Redis(connection_pool=REDIS_POOL) +class TimeTempSerializer(serializers.ModelSerializer): + class Meta: + model = TimeTemp + fields = "__all__" + + +class TimeRankSerializer(serializers.ModelSerializer): + flag_s = serializers.SerializerMethodField('flag_status') + name = serializers.SerializerMethodField("a_user_name") + + class Meta: + model = TimeRank + fields = "__all__" + + def flag_status(self, obj): + flag = "" + return str(flag) + + def a_user_name(self, obj): + name = obj.user_name + return name + +class TimeMoudelSerializer(serializers.ModelSerializer): + + start_date = serializers.SerializerMethodField('a_start_date') + end_date = serializers.SerializerMethodField('a_end_date') + + class Meta: + model = TimeMoudel + fields = ['start_date', 'end_date', "temp_time_id"] + + def a_start_date(self, obj): + time_stamp = obj.start_time + time_arr = time.localtime(time_stamp) + return str(time.strftime("%Y-%m-%d %H:%M:%S", time_arr)) + + def a_end_date(self, obj): + time_stamp = obj.end_time + time_arr = time.localtime(time_stamp) + return str(time.strftime("%Y-%m-%d %H:%M:%S", time_arr)) + + class ImageInfoSerializer(serializers.ModelSerializer): status = serializers.SerializerMethodField('statusck') diff --git a/vulfocus-api/dockerapi/views.py b/vulfocus-api/dockerapi/views.py index 2b6704dd..beee5856 100755 --- a/vulfocus-api/dockerapi/views.py +++ b/vulfocus-api/dockerapi/views.py @@ -1,15 +1,18 @@ import socket -from django.http import JsonResponse +from django.http import JsonResponse, HttpResponse from rest_framework import viewsets from rest_framework.decorators import action +from rest_framework.response import Response from dockerapi.models import ImageInfo -from dockerapi.serializers import ImageInfoSerializer, ContainerVulSerializer, SysLogSerializer +from dockerapi.serializers import ImageInfoSerializer, ContainerVulSerializer, SysLogSerializer, TimeMoudelSerializer, TimeRankSerializer, TimeTempSerializer from dockerapi.models import ContainerVul +from user.serializers import UserProfileSerializer +from user.models import UserProfile import django.utils import django.utils.timezone as timezone from .common import R, DEFAULT_CONFIG, get_setting_config from django.db.models import Q -from .models import SysLog, SysConfig +from .models import SysLog, SysConfig, TimeMoudel, TimeTemp, TimeRank import json from tasks import tasks from vulfocus.settings import client, VUL_IP @@ -17,6 +20,8 @@ import re from rest_framework.decorators import api_view import time +import datetime +import uuid def get_request_ip(request): """ @@ -31,14 +36,219 @@ def get_request_ip(request): request_ip = request.META.get("REMOTE_ADDR") return request_ip +class CreateTimeTemplate(viewsets.ModelViewSet): + + serializer_class = TimeTempSerializer + + def get_queryset(self, *args, **kwargs): + request = self.request + user_id = request.user.id + now_time = datetime.datetime.now().timestamp() + flag = self.request.GET.get("flag", "") + data = TimeMoudel.objects.filter(user_id=user_id, end_time__gte=now_time).first() + if data: + if flag and flag=="flag": + return TimeTemp.objects.all() + else: + data_list = TimeTemp.objects.filter(temp_id=data.temp_time_id_id) + print(data_list) + data_l = [{'temp_id': data_list[0].temp_id, 'time_desc':data_list[0].time_desc, + 'time_range': data_list[0].time_range, 'user_id': data_list[0].user_id, + 'flag_status': True}] + return data_l + else: + return TimeTemp.objects.all() + + + # 创建计时模式模版 + def create(self, request, *args, **kwargs): + user_id = request.user.id + time_desc = request.data['desc'] + if request.data['time_range'].isdigit() != True or int(request.data['time_range']) % 30 != 0: + data = { + "code": 2001, + "message": "时间范围必须是整数,并且是30的倍数", + } + return JsonResponse(data=data) + try: + time_range = request.data['time_range'] + except Exception as e: + return JsonResponse(data={"code": 2001, "message": "时间范围不能为空"}) + timetemp_info = TimeTemp(user_id=user_id, time_range=int(time_range), time_desc=time_desc) + timetemp_info.save() + data = self.serializer_class(timetemp_info).data + return JsonResponse(R.ok(data=data)) + + def destroy(self, request, *args, **kwargs): + user = request.user + if not user.is_superuser: + return JsonResponse(R.build(msg="权限不足")) + temp = self.get_object() + temp_id = self.get_serializer(temp).data['temp_id'] + try: + + temp = TimeTemp.objects.filter(temp_id=temp_id).first() + except Exception as e: + return JsonResponse({"code": 2001, "message": "删除失败"}) + return JsonResponse({"code": 200, "message": "删除成功"}) + + +class TimeRankSet(viewsets.ModelViewSet): + serializer_class = TimeRankSerializer + + def get_queryset(self): + value = self.request.GET.get("value") + time_data = TimeTemp.objects.all().filter(time_range=value).first() + temp_data = TimeRank.objects.all().filter(time_temp_id=time_data.temp_id) + return temp_data + + + + +class TimeMoudelSet(viewsets.ModelViewSet): + + serializer_class = TimeMoudelSerializer + + def get_queryset(self): + data = TimeMoudel.objects.all().filter(user_id=self.request.user.id, status=True) + return data + + ''' + 删除时间模式,删除会所有该用户目前运行的容器 + ''' + def delete(self, request, *args, **kwargs): + user_id = request.user.id + now_time = datetime.datetime.now().timestamp() + try: + TimeMoudel.objects.filter(user_id=user_id, end_time__gte=now_time).delete() + container_vul_list = ContainerVul.objects.filter(user_id=user_id) + for container_vul in container_vul_list: + try: + docker_container_id = container_vul.docker_container_id + # 移除Docker容器 + docker_container = client.containers.get(container_id=docker_container_id) + docker_container.remove() + except Exception as e: + pass + container_vul.delete() + return JsonResponse({"code": "2000", "msg": "成功"}, status=201) + except Exception as e: + return JsonResponse({"code": "2001", "msg": str(e)}) + + ''' + 获取时间模式数据信息 + ''' + @action(methods=['get'], detail=False, url_path="info") + def info(self, request, pk=None): + user_id = request.user.id + now_time = datetime.datetime.now().timestamp() + user_data = UserProfile.objects.filter(id=user_id).first() + data = TimeMoudel.objects.filter(user_id=user_id, end_time__gte=now_time).first() + if not data: + return JsonResponse({"code": "2001", "msg": "不在答题模式中", "data": ""}) + time_moudel_serializer = TimeMoudelSerializer(data) + info = time_moudel_serializer.data + # 计算分数 + time_id = data.time_id + total_rank = 0.0 + time_moudel_vul_list = ContainerVul.objects.filter(time_model_id=time_id,is_check=True) + for time_moudel_vul in time_moudel_vul_list: + total_rank += time_moudel_vul.image_id.rank + trdata = TimeRank.objects.filter(time_temp_id=data.temp_time_id_id,user_id=user_id).first() + if trdata: + trdata.update(rank=total_rank) + else: + tr = TimeRank(user_id=user_id, rank=total_rank, time_temp_id=data.temp_time_id_id, + user_name=user_data.username) + tr.save() + info['rank'] = total_rank + return JsonResponse({"code": "200", "msg": "", "data": info}) + + ''' + 检测是否时间过期 + ''' + @action(methods=['get'], detail=False, url_path="check") + def check(self, request, pk=None): + user_id = request.user.id + now_time = datetime.datetime.now().timestamp() + data = TimeMoudel.objects.filter(user_id=user_id, end_time__gte=now_time).first() + if data: + # 移除所有的镜像 + container_vul_list = ContainerVul.objects.filter(user_id=user_id) + for container_vul in container_vul_list: + try: + docker_container_id = container_vul.docker_container_id + # 移除Docker + docker_container = client.containers.get(container_id=docker_container_id) + docker_container.remove() + except Exception as e: + pass + container_vul.delete() + return JsonResponse({"code": "200", "msg": "OK"}) + else: + return JsonResponse({"code": "2001", "msg": "时间已到"}) + + ''' + 创建计分模式 + ''' + + def create(self, request, *args, **kwargs): + user_id = request.user.id + now_time = datetime.datetime.now().timestamp() + if "type" in request.data: + dasdata = TimeMoudel.objects.filter(user_id=user_id, end_time__gte=now_time).first() + if dasdata: + tm1 = TimeMoudel.objects.filter(user_id=user_id, status=True ).all() + print(tm1) + tm = TimeMoudel.objects.filter(user_id=user_id, status=True ).first() + tm_moudel_info = TimeMoudelSerializer(tm) + data = tm_moudel_info.data + data["start_date"] = int(time.time()) + onetime = time.strptime(data["end_date"], "%Y-%m-%d %H:%M:%S") + data["end_date"] = int(time.mktime(onetime)) + print(data) + return JsonResponse({"code": "2002", "msg": "时间未到", "data": data}) + else: + return JsonResponse({"code": "20000", "msg": "响应成功", "data": ""}) + if "time_range" in request.data: + time_minute = request.data['time_range'] + data = TimeMoudel.objects.filter(user_id=user_id, end_time__gte=now_time).first() + if data: + return JsonResponse({"code": "2001", "msg": "时间未到", "data": ""}) + else: + try: + request_ip = get_request_ip(request) + sys_log = SysLog(user_id=user_id, operation_type="时间模式", operation_name="创建 ", operation_value="", + operation_args={}, + ip=request_ip) + sys_log.save() + except Exception as e: + pass + now_time = datetime.datetime.now() + end_time = now_time + datetime.timedelta(minutes=time_minute) + start_time_timestamp = now_time.timestamp() + end_time_timestamp = end_time.timestamp() + temp_id = request.data['temp_id'] + time_moudel = TimeMoudel(time_id=str(uuid.uuid4()), user_id=user_id, start_time=start_time_timestamp, + end_time=end_time_timestamp, temp_time_id_id=temp_id, status=True) + time_moudel.save() + time_moudel_info = TimeMoudelSerializer(time_moudel) + data = time_moudel_info.data + data["start_date"] = int(time.time()) + onetime = time.strptime(data["end_date"], "%Y-%m-%d %H:%M:%S") + data["end_date"] = int(time.mktime(onetime)) + return JsonResponse({"code": "200", "msg": "OK", "data": data}, status=201) + class ImageInfoViewSet(viewsets.ModelViewSet): serializer_class = ImageInfoSerializer def get_queryset(self): + now_time = datetime.datetime.now().timestamp() query = self.request.GET.get("query", "") flag = self.request.GET.get("flag", "") user = self.request.user + data = TimeMoudel.objects.filter(user_id=self.request.user.id, end_time__gte=now_time).first() if user.is_superuser: if query: query = query.strip() @@ -60,6 +270,11 @@ def get_queryset(self): | Q(image_desc__contains=query), is_ok=True).order_by('-create_date') else: image_info_list = ImageInfo.objects.filter(is_ok=True).order_by('-create_date') + if data: + for image_info in image_info_list: + image_info.image_name = '' + image_info.image_vul_name = '' + image_info.image_desc = '' return image_info_list def destroy(self, request, *args, **kwargs): @@ -498,6 +713,16 @@ def update_setting(request): return JsonResponse(R.build(msg=build_msg, data=rsp_data)) +class UserRank(viewsets.ModelViewSet): + serializer_class = UserProfileSerializer + + def get_queryset(self): + if self.request.user.is_superuser: + return UserProfile.objects.all().order_by("rank") + else: + return [] + + def get_local_ip(): """ 获取本机IP diff --git a/vulfocus-api/layout_image/migrations/0014_auto_20210426_1020.py b/vulfocus-api/layout_image/migrations/0014_auto_20210426_1020.py new file mode 100644 index 00000000..149a7f80 --- /dev/null +++ b/vulfocus-api/layout_image/migrations/0014_auto_20210426_1020.py @@ -0,0 +1,44 @@ +# Generated by Django 2.2.13 on 2021-04-26 10:20 + +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('layout_image', '0013_auto_20201223_2220'), + ] + + operations = [ + migrations.AlterField( + model_name='layout', + name='layout_id', + field=models.UUIDField(default=uuid.UUID('2358a3c5-8de4-4ca1-b1fe-815f46fc9155'), editable=False, primary_key=True, serialize=False, verbose_name='编排UUID'), + ), + migrations.AlterField( + model_name='layoutdata', + name='layout_user_id', + field=models.UUIDField(default=uuid.UUID('5515c0e0-b103-4cdd-92f2-d715bc083004'), editable=False, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='layoutservice', + name='service_id', + field=models.UUIDField(default=uuid.UUID('85e00eb2-fc6e-45ff-946f-f7b7c29c3ddc'), editable=False, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='layoutservicecontainer', + name='service_container_id', + field=models.UUIDField(default=uuid.UUID('f37571ce-3d14-4ebd-b327-ee1720f0901e'), editable=False, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='layoutservicecontainerscore', + name='layout_service_container_score_id', + field=models.UUIDField(default=uuid.UUID('438daa13-f346-40a6-8b4e-ea8ab39cacd1'), editable=False, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='layoutservicenetwork', + name='layout_service_network_id', + field=models.UUIDField(default=uuid.UUID('a0db3185-9749-42f1-ac99-47e32857d990'), editable=False, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/vulfocus-api/layout_image/views.py b/vulfocus-api/layout_image/views.py old mode 100755 new mode 100644 diff --git a/vulfocus-api/tasks/tasks.py b/vulfocus-api/tasks/tasks.py old mode 100755 new mode 100644 diff --git a/vulfocus-api/user/views.py b/vulfocus-api/user/views.py old mode 100644 new mode 100755 index e37728c3..6830f384 --- a/vulfocus-api/user/views.py +++ b/vulfocus-api/user/views.py @@ -23,7 +23,7 @@ def get_queryset(self): if self.request.user.is_superuser: return UserProfile.objects.all() else: - return [] + return UserProfile.objects.all() def update(self, request, *args, **kwargs): user = request.user diff --git a/vulfocus-api/vulfocus/urls.py b/vulfocus-api/vulfocus/urls.py old mode 100644 new mode 100755 index 741ea885..b7fb6c42 --- a/vulfocus-api/vulfocus/urls.py +++ b/vulfocus-api/vulfocus/urls.py @@ -15,7 +15,7 @@ """ from django.conf.urls import url, include from rest_framework import routers -from dockerapi.views import ImageInfoViewSet, ContainerVulViewSet, SysLogSet, get_setting, update_setting +from dockerapi.views import ImageInfoViewSet, ContainerVulViewSet, SysLogSet, get_setting, update_setting, TimeMoudelSet, CreateTimeTemplate, UserRank, TimeRankSet from user.views import UserRegView, UserSet from rest_framework_jwt.views import obtain_jwt_token from user.views import get_user_info, LogoutView @@ -32,6 +32,10 @@ router.register('tasks', TaskSet, base_name="TaskSet") router.register("network", NetWorkInfoViewSet, basename="network") router.register('layout', LayoutViewSet, basename="layout") +router.register('time', TimeMoudelSet, basename="time") +router.register('timetemp', CreateTimeTemplate, basename="timetmep") +router.register('userrank', UserRank, basename="userrank") +router.register('timerank', TimeRankSet, basename="timerankset") urlpatterns = [ url(r'^', include(router.urls)), diff --git a/vulfocus-frontend/src/router/index.js b/vulfocus-frontend/src/router/index.js old mode 100644 new mode 100755 index 7cf8d130..5f0a1bbe --- a/vulfocus-frontend/src/router/index.js +++ b/vulfocus-frontend/src/router/index.js @@ -72,25 +72,44 @@ export const constantRoutes = [ ] }, { - path: '/scene', + path: '/userrank', component: Layout, - redirect: '/scene/list', - children: [ - { - path: 'list', + redirect: '/userrank', + children: [{ + path: 'list', + affix: true, + name: 'list', + component: () => import('@/views/rank/index'), + meta: { title: '用户积分榜', icon: 'form' } + }] + }, + { + path: '/', + component: Layout, + redirect: '/', + meta: {title: "情景模式", icon: 'form'}, + children: [{ + path: 'time', + affix: true, + name: 'time', + component: () => import('@/views/time/index'), + meta: { title: '计时模式', icon: 'form' } + }, + { + path: '/scene/list', component: () => import('@/views/scene/list'), name: 'List', meta: { title: '场景', icon: 'table', noCache: true } }, { - path: 'index', + path: '/scene/index', component: () => import('@/views/scene/index'), name: 'Index', hidden: true, meta: { title: '场景', icon: 'table', noCache: true } - } - ] - } + }] + }, + ] const createRouter = () => new Router({ @@ -133,7 +152,7 @@ export const asyncRoutes = [ path: '/layout', component: Layout, redirect: '/layout', - meta: {role: ['admin'], title: "环境编排管理", icon: "barrage_fill"}, + meta: {role: ['admin'], title: "情景模式管理", icon: "barrage_fill"}, children: [{ path: 'network', affix: true, @@ -154,7 +173,14 @@ export const asyncRoutes = [ hidden: true, component: () => import('@/views/layout/index'), meta: { title: '创建', icon: 'barrage_fill' , role: ['admin']} - } + }, + { + path: 'time', + affix: true, + name: 'time', + component: () => import("@/views/manager/timetemp"), + meta: { title: '计时模版管理', icon: 'setting' , role: ['admin']} + }, ] }, { @@ -183,7 +209,8 @@ export const asyncRoutes = [ component: () => import('@/views/manager/setting'), name: 'setting', meta: { title: '系统配置', icon: 'setting', noCache: true } - } + }, + ] }, { path: '*', redirect: '/404', hidden: true } diff --git a/vulfocus-frontend/src/views/dashboard/index.vue b/vulfocus-frontend/src/views/dashboard/index.vue index 15edb88d..82e1daa6 100755 --- a/vulfocus-frontend/src/views/dashboard/index.vue +++ b/vulfocus-frontend/src/views/dashboard/index.vue @@ -95,13 +95,16 @@ :total="page.total">
d3aRqU3t_PsJtUf{nxrAmUSNbHwRDmEA
z<{|S&Jm&(|rtbu`h$AupAVDaQ=Ua6$zkkvI^)EeJKrxmsfcC|#wxeT+D?Obpu mb
zCF0LN3-CvJ+Vt^1@b};UrQw0H?HMz`kulF7nC5`53jzN|u>_m`gU`SI0+D!aM>0}4
z0u(V#gwJ_Gj1&G`B6tp%<_|0!aESq7nh{vM&uI?$i%3Fvgawfa^v@R}UjY6 GI3CI%9QDU+$i#!)DE-jrrLx4>Als5%rntnj7