From 2c65af7a9b21ca194f5926e63a8fe53505b5ae65 Mon Sep 17 00:00:00 2001 From: S0okJu Date: Sat, 1 Mar 2025 16:16:41 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[Test]=20DB=20Unittest=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 4 +- app/__pycache__/__init__.cpython-310.pyc | Bin 213 -> 0 bytes app/__pycache__/__init__.cpython-39.pyc | Bin 206 -> 0 bytes app/__pycache__/factory.cpython-310.pyc | Bin 6148 -> 0 bytes app/__pycache__/factory.cpython-39.pyc | Bin 6118 -> 0 bytes app/extensions/db/repository_test.py | 32 -------------- app/extensions/kafka/__init__.py | 9 ---- .../__pycache__/__init__.cpython-310.pyc | Bin 160 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 153 -> 0 bytes .../__pycache__/async_handler.cpython-39.pyc | Bin 1839 -> 0 bytes .../ctf_metrics_collector.cpython-39.pyc | Bin 2252 -> 0 bytes .../__pycache__/loki_logger.cpython-39.pyc | Bin 3016 -> 0 bytes {app => hexactf}/__init__.py | 0 hexactf/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 217 bytes hexactf/__pycache__/config.cpython-310.pyc | Bin 0 -> 1879 bytes .../extensions_manager.cpython-310.pyc | Bin 0 -> 3566 bytes hexactf/__pycache__/factory.cpython-310.pyc | Bin 0 -> 5657 bytes {app => hexactf}/api/__init__.py | 0 .../api/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 183 bytes .../__pycache__/challenge_api.cpython-310.pyc | Bin 0 -> 2677 bytes .../api/challenge_api.py | 12 ++---- {app => hexactf}/config.py | 0 {app => hexactf}/exceptions/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 164 bytes .../api_exceptions.cpython-310.pyc | Bin 0 -> 1616 bytes .../base_exceptions.cpython-310.pyc | Bin 0 -> 1279 bytes .../challenge_exceptions.cpython-310.pyc | Bin 0 -> 1326 bytes .../__pycache__/error_types.cpython-310.pyc | Bin 0 -> 1106 bytes .../kafka_exceptions.cpython-310.pyc | Bin 0 -> 2418 bytes .../userchallenge_exceptions.cpython-310.pyc | Bin 0 -> 2549 bytes .../exceptions/api_exceptions.py | 4 +- .../exceptions/base_exceptions.py | 2 +- .../exceptions/challenge_exceptions.py | 4 +- {app => hexactf}/exceptions/error_types.py | 0 {app => hexactf}/exceptions/handlers.py | 2 +- .../exceptions/kafka_exceptions.py | 4 +- .../exceptions/userchallenge_exceptions.py | 4 +- {app => hexactf}/extensions/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 164 bytes {app => hexactf}/extensions/db/__init__.py | 2 +- .../db/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 332 bytes .../db/__pycache__/config.cpython-310.pyc | Bin 0 -> 2965 bytes .../db/__pycache__/models.cpython-310.pyc | Bin 0 -> 4499 bytes .../db/__pycache__/repository.cpython-310.pyc | Bin 0 -> 5215 bytes {app => hexactf}/extensions/db/config.py | 0 {app => hexactf}/extensions/db/models.py | 2 +- {app => hexactf}/extensions/db/repository.py | 25 ++--------- {app => hexactf}/extensions/k8s/__init__.py | 0 .../k8s/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 193 bytes .../k8s/__pycache__/client.cpython-310.pyc | Bin 0 -> 5824 bytes {app => hexactf}/extensions/k8s/client.py | 16 ++----- hexactf/extensions/kafka/__init__.py | 9 ++++ .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 372 bytes .../kafka/__pycache__/config.cpython-310.pyc | Bin 0 -> 1287 bytes .../__pycache__/consumer.cpython-310.pyc | Bin 0 -> 3631 bytes .../kafka/__pycache__/handler.cpython-310.pyc | Bin 0 -> 2543 bytes {app => hexactf}/extensions/kafka/config.py | 0 {app => hexactf}/extensions/kafka/consumer.py | 2 +- {app => hexactf}/extensions/kafka/handler.py | 4 +- {app => hexactf}/extensions_manager.py | 2 +- {app => hexactf}/factory.py | 39 ++++++++++-------- {app => hexactf}/monitoring/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 164 bytes .../__pycache__/async_handler.cpython-310.pyc | Bin 0 -> 1874 bytes .../ctf_metrics_collector.cpython-310.pyc | Bin 2286 -> 2290 bytes .../__pycache__/loki_logger.cpython-310.pyc | Bin 0 -> 3014 bytes .../system_metrics_collector.cpython-310.pyc | Bin 0 -> 4511 bytes {app => hexactf}/monitoring/async_handler.py | 0 .../monitoring/ctf_metrics_collector.py | 0 {app => hexactf}/monitoring/loki_logger.py | 5 +-- .../monitoring/system_metrics_collector.py | 0 pytest.ini | 2 + requirements.txt | 4 +- tests/units/.gitignore | 2 + tests/units/__init__.py | 0 tests/units/db/__init__.py | 0 tests/units/db/conftest.py | 25 +++++++++++ tests/units/db/test_userchallenges_repo.py | 36 ++++++++++++++++ 78 files changed, 127 insertions(+), 125 deletions(-) delete mode 100644 app/__pycache__/__init__.cpython-310.pyc delete mode 100644 app/__pycache__/__init__.cpython-39.pyc delete mode 100644 app/__pycache__/factory.cpython-310.pyc delete mode 100644 app/__pycache__/factory.cpython-39.pyc delete mode 100644 app/extensions/db/repository_test.py delete mode 100644 app/extensions/kafka/__init__.py delete mode 100644 app/monitoring/__pycache__/__init__.cpython-310.pyc delete mode 100644 app/monitoring/__pycache__/__init__.cpython-39.pyc delete mode 100644 app/monitoring/__pycache__/async_handler.cpython-39.pyc delete mode 100644 app/monitoring/__pycache__/ctf_metrics_collector.cpython-39.pyc delete mode 100644 app/monitoring/__pycache__/loki_logger.cpython-39.pyc rename {app => hexactf}/__init__.py (100%) create mode 100644 hexactf/__pycache__/__init__.cpython-310.pyc create mode 100644 hexactf/__pycache__/config.cpython-310.pyc create mode 100644 hexactf/__pycache__/extensions_manager.cpython-310.pyc create mode 100644 hexactf/__pycache__/factory.cpython-310.pyc rename {app => hexactf}/api/__init__.py (100%) create mode 100644 hexactf/api/__pycache__/__init__.cpython-310.pyc create mode 100644 hexactf/api/__pycache__/challenge_api.cpython-310.pyc rename app/api/challenge.py => hexactf/api/challenge_api.py (87%) rename {app => hexactf}/config.py (100%) rename {app => hexactf}/exceptions/__init__.py (100%) create mode 100644 hexactf/exceptions/__pycache__/__init__.cpython-310.pyc create mode 100644 hexactf/exceptions/__pycache__/api_exceptions.cpython-310.pyc create mode 100644 hexactf/exceptions/__pycache__/base_exceptions.cpython-310.pyc create mode 100644 hexactf/exceptions/__pycache__/challenge_exceptions.cpython-310.pyc create mode 100644 hexactf/exceptions/__pycache__/error_types.cpython-310.pyc create mode 100644 hexactf/exceptions/__pycache__/kafka_exceptions.cpython-310.pyc create mode 100644 hexactf/exceptions/__pycache__/userchallenge_exceptions.cpython-310.pyc rename app/exceptions/api.py => hexactf/exceptions/api_exceptions.py (88%) rename app/exceptions/base.py => hexactf/exceptions/base_exceptions.py (94%) rename app/exceptions/challenge.py => hexactf/exceptions/challenge_exceptions.py (83%) rename {app => hexactf}/exceptions/error_types.py (100%) rename {app => hexactf}/exceptions/handlers.py (86%) rename app/exceptions/kafka.py => hexactf/exceptions/kafka_exceptions.py (93%) rename app/exceptions/userchallenge.py => hexactf/exceptions/userchallenge_exceptions.py (93%) rename {app => hexactf}/extensions/__init__.py (100%) create mode 100644 hexactf/extensions/__pycache__/__init__.cpython-310.pyc rename {app => hexactf}/extensions/db/__init__.py (62%) create mode 100644 hexactf/extensions/db/__pycache__/__init__.cpython-310.pyc create mode 100644 hexactf/extensions/db/__pycache__/config.cpython-310.pyc create mode 100644 hexactf/extensions/db/__pycache__/models.cpython-310.pyc create mode 100644 hexactf/extensions/db/__pycache__/repository.cpython-310.pyc rename {app => hexactf}/extensions/db/config.py (100%) rename {app => hexactf}/extensions/db/models.py (99%) rename {app => hexactf}/extensions/db/repository.py (84%) rename {app => hexactf}/extensions/k8s/__init__.py (100%) create mode 100644 hexactf/extensions/k8s/__pycache__/__init__.cpython-310.pyc create mode 100644 hexactf/extensions/k8s/__pycache__/client.cpython-310.pyc rename {app => hexactf}/extensions/k8s/client.py (93%) create mode 100644 hexactf/extensions/kafka/__init__.py create mode 100644 hexactf/extensions/kafka/__pycache__/__init__.cpython-310.pyc create mode 100644 hexactf/extensions/kafka/__pycache__/config.cpython-310.pyc create mode 100644 hexactf/extensions/kafka/__pycache__/consumer.cpython-310.pyc create mode 100644 hexactf/extensions/kafka/__pycache__/handler.cpython-310.pyc rename {app => hexactf}/extensions/kafka/config.py (100%) rename {app => hexactf}/extensions/kafka/consumer.py (98%) rename {app => hexactf}/extensions/kafka/handler.py (95%) rename {app => hexactf}/extensions_manager.py (98%) rename {app => hexactf}/factory.py (85%) rename {app => hexactf}/monitoring/__init__.py (100%) create mode 100644 hexactf/monitoring/__pycache__/__init__.cpython-310.pyc create mode 100644 hexactf/monitoring/__pycache__/async_handler.cpython-310.pyc rename {app => hexactf}/monitoring/__pycache__/ctf_metrics_collector.cpython-310.pyc (90%) create mode 100644 hexactf/monitoring/__pycache__/loki_logger.cpython-310.pyc create mode 100644 hexactf/monitoring/__pycache__/system_metrics_collector.cpython-310.pyc rename {app => hexactf}/monitoring/async_handler.py (100%) rename {app => hexactf}/monitoring/ctf_metrics_collector.py (100%) rename {app => hexactf}/monitoring/loki_logger.py (98%) rename {app => hexactf}/monitoring/system_metrics_collector.py (100%) create mode 100644 pytest.ini create mode 100644 tests/units/.gitignore create mode 100644 tests/units/__init__.py create mode 100644 tests/units/db/__init__.py create mode 100644 tests/units/db/conftest.py create mode 100644 tests/units/db/test_userchallenges_repo.py diff --git a/app.py b/app.py index 87f9391..07e495b 100644 --- a/app.py +++ b/app.py @@ -1,6 +1,4 @@ - - -from app.factory import create_app +from hexactf.factory import create_app app = create_app() diff --git a/app/__pycache__/__init__.cpython-310.pyc b/app/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 6b7d3809967ec3f8013172f005d7c205d1150ee3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213 zcmYjLF%H5o3{29ZqKdBkp#x+E2_Xh%7R1uUikc82+9pcM&>!#>c3#t!i7y}q+)kY2 zv(CP=Y_lvSi0AjN*Vtc!_yn8vgKOV{<&O>O0^QrqEy5F^b<%6`NyG-Ut) diff --git a/app/__pycache__/__init__.cpython-39.pyc b/app/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 2a3b394cd9571f96403f50e72103b8bf2bb3e31c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206 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 diff --git a/app/__pycache__/factory.cpython-39.pyc b/app/__pycache__/factory.cpython-39.pyc deleted file mode 100644 index 6a79da7afa6df243465d05f7be70c3d21aa9e36a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6118 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 bAFo$Xd5gm)H$SB`C)EyQcrg=@U||3NB0DDo diff --git a/app/monitoring/__pycache__/__init__.cpython-39.pyc b/app/monitoring/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 8f77c97dc507caab9570e81554a1bd6c1d424bd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153 zcmYe~<>g`kg4by)(?RrO5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HWerR!OQL%nX zVs2`7mA*@Aadt_5fqrsEVopwKUV5r-VnL>UVnKm^Zhl^7Nq$jgUb=pKd?t_`AFo$X Vd5gm)H$SB`C)EyQ?q?uo008T2B}xDQ diff --git a/app/monitoring/__pycache__/async_handler.cpython-39.pyc b/app/monitoring/__pycache__/async_handler.cpython-39.pyc deleted file mode 100644 index 60e9b7680c8026b2de3735dda7a2f4c3cbb1bcd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmZWqOK%)S5bo}I?Ch@Bm|!3x291b>wLF5bLA=2M3V&1%2g&_y?S*>X|6(Fj99{Kdb7iuWI66uS4Mb=ip-e zt53*ZI9dH%n0yXR4WQ$M(~OMjk&dXHvl07}aK@c8!W}^-&Z#qUIlV{v?%!~S>?PQ* zeoiw{AA=wWzB6J^$pPh*v$J62aEH5R1YYsD52I)IT0DR~|0!|Fs0FXL&Ds}go+n?$ zInRVl*oq32(Q?$J?m(x>DOu8ULdk+INW`?Wbk7M$p7xh5D+Su0v=?O3K`Wjy*z0l! z=G|xw`(+PrbAo>Djn=K|JH>dvkViuHnelZj$3h!7kEh~(f8BU$Rtjl?C`$8GN0DhA z)|MLYcqV4TcwgJ0ofYG#UTNz?DPo@HW8>|9FLG@p~35x+k^gOIg!Iu*rCSJ!r> zEM$m)>C17t5$;vjALQb3DH1JsDC|K9zU3+^8pDMfVZSST0I2a(6)B;c&_b%(7nQ)S)oLq*1m7T(hIiB zZJ53fO<^rSCp)yda>=Foc7SydDIy_eH*yK&u~eP;I# zgyjbyMyd*hR&5o7oGL+8Doa6fp-4gEFS%Eqh|m83QV}zE_Srbeo^E$%cV>6z`(|g& z>FG%VrMuQH{*Wf*FBC?L0>TCO#UfOcP{}4`Ntep9E|(QuDJS%VL{mUoZ++FE1PK zzf)LVEm&wZS`r{!fM2`|6(hPti7r!#$_=6`RG|rI6EsOv&?Z?5zBEm@6sppRMnYF{ zgif|(dW=qiJVB@7Y%~73+%?m-S*6H6U@k9NBIkf;xem7^*k@3%pM*aLzgU3E1sJGY z1b2Uy)|EOjBv+>4_B36T!x4oN*h0Vd4J!|}(&9OdGvV2;@F(ISKNXE-5**3p zz&hawuz&in#oLmf5X@d{6Mhnu{&_F^5u8b6DV*v?EqjrP8?IB$#-^{C)l$~1RV{y%iMv}U1Ofmx?4Ac$6p4w9`$bRb$51yjXPSm zeLoa_2{s#A_uiB4_GWCE&&XVcrF@mMVo5+e^Wz|fNJZ5w&Yl>|4p2A)qXG>{qV~$s z&W)QLa1uk)!nF{(I=N0hCK9$1wtZJWhpG6$Bd*55t_7QWy+)_^eKXj63a)GWzwh>% z;A{JS&}en;H%ZH=DuZyS)d^U}IP7cAC8(o3TE^6NVRZa40hAemJ~< zPGAs_q$<^3JQ|X|K37523JuT*3ik02IiG_L!f{=yOLa&Tx8*Ilu7v!{z`x@C0SfUk zgD2m0cRIkk7Oa2OTW@K>#y8#D9Y6z!kF}PnCO6B&+Y0w{NgJA8x;U;mxVxbB^=|M} zJ9u~ppnlrjX^d-zcN^5A-4|Y8(oD-Oea2qZ7MHYAB@!~R6`(o+=jWKY^BEP8vrWzZ5+P?2;Buj0QcMSfOWJercj zmubr!7va-TSqd)KFW8TXIt#)$QQH{15lx#g9lJfmI(T>&DAM+S?d<>dr?$V>8NHKz zp5lB5xJK$hYpd6Mpei7zW%8LxOt1KO7=}M- z7-ffgHuk3s<5SPHqZxh*Hsa4=g-Hp*$K^h_;PP`gHjmXXZo(d>G@gPAnG_g=s-(`U zDOH`F3di%Aw12W1Cso6;OYq=@i86X}kbaI&YN)Qy4^xBI5 diff --git a/app/monitoring/__pycache__/loki_logger.cpython-39.pyc b/app/monitoring/__pycache__/loki_logger.cpython-39.pyc deleted file mode 100644 index bd26880c6b51d091b19a9883508aac92acc2d93d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3016 zcmb7G&2J<}6|buPoblM}-7F!54RXYnM!y-lmj_&;a|u97FBN^aXS~0ux}@F?Awi8yhZMH z{K)6T>*PTvj6%+CGU-Zhk4bM=L^aHO8DJJDu~px7q8Z8Wux9uOC$aS=H;t8IrB?Mb%xBhm+u3xQ64UwSU~&z;_&SJU1TGTFk^F#5A)P(IQjYSreCZwt=}8|efeh$C zb73`4ifxj<)@!%zSz(S@JkWVcf=?QaslSF^JP$JBBNnp}oA8l1q%mv(k$k=Bm@w)0 z<8IPXW;QNVIq1i^m2Uj%4Hmah`G)o(NP8ehOw(=rdu8!%p>(k*v3YyAcwH6SWv{=O zZYFuIx^4B1q@OK9`eLWo&B~t6y6r`aw-Vgy@0c)-Gkl9X@P$ul+B#ut z3X8z_8c20F6|`g82p_?-THnS%Lenjl@v$g5EFj3h?>b{=4qZxWQ=R!HA5u8rJk1dF+YOAt&~u<-cs;G+-zx^TSr=i|xmjRQRme+9;J za2v)glXhW(`!G+|>o)6^@#s!W14dr$=}uCXN}G!$>xV_Jd%C1aClip3U@%i_t8%Tn zkPti9)P0p3@A{2b@7^@^M9LGt^K}jTtabE7D`B(p$Y?ep|BROASaUkORGP+eQV{7H(p=A8LzIqzIONa4RSG=rN|k>nf2bn$)xE- z^GGp>nIw;=D5Lp}D$o!}aV2n0tF)(OELE9gdC_$AR|&A6C-+odSX9l1CJ!=B)-9nF z5Ssu&hE=KPduFTv9@-ErDS~?d>6h+3bY(eT4 z-vnV?h|6xn^||}8@B6}r_>+3rpwP$4B{A<@;=b^akmsJbPyE{Og=dmgs}lN%V$V85 zYI8V=N24)iW%^o-K=#=_-}ZEaDkz0U$#;dm`j9>355EnC3FD3y(EZpb&sMnumht3_G0_P4X<_F}P$A5bNqrd&)c=FEi z!QYX>7s!>4|G2ks{FiqgPu~CMAAUD{Zs}I?U_ruZHy5-@(oH3oQ}XeXM(R+ucn-bz z5(uK0y>;bhVvPJS;sC)$!a`sK;kV*#_YOPa`io#a;^qR%tj>}=8!GF_2p!S7YMgDg zW4!xGK2S%FnN0^;<7(lkz+q~g(P06&2!p_%cN-{!hfGGW6F-JlS@=Su^#l$hycd-BcNuEMq*vP#Bjs7!Ut9xiSvQ zmI*eKf`VEvlK_&^bgOW5Wl|>kIxW9O#A3Hv*m^|#KS8gmr3kr;+Bhftk9~I@Zv>B9 z=aoC-J=>(fXJ^gtaUm@}mk?#<(=q!RWVqs7P`wIHBEQYVxnuhGvqHW(I zV(Y6@>=ZUHMOW~X(mMMYYd!sAK(`$=rvDxI3Ea*!dAcpOrnY+W%VJMCmt4*+He9L- V{xWG(%!7pZb+8X*7kBQ({{jR|34H(n diff --git a/app/__init__.py b/hexactf/__init__.py similarity index 100% rename from app/__init__.py rename to hexactf/__init__.py diff --git a/hexactf/__pycache__/__init__.cpython-310.pyc b/hexactf/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b6c39ee91792b54c04be9a933f1838402de0b904 GIT binary patch literal 217 zcmYjLF%H5o3{2W0P(@e%&;c?dRfvI^1+jFoqQ;~ZZ4)I$qCem(?7XHc6JJ0KxJ;bn zv(CP=Y?35qgvaNm*VLb*_(wouOXU|tiYZ>RhUc6!6=dU0B&e25E&vO|#8 zx-qyKY{kbz%6eanw)wqg+DQOZZPfv+Y(LDh#;8k;oAC)B5zSc~d;zezHVXg% literal 0 HcmV?d00001 diff --git a/hexactf/__pycache__/config.cpython-310.pyc b/hexactf/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e154e6c6a6b90c57d4d439e6f35f464efbe52ea GIT binary patch literal 1879 zcmY*a-A~(A6t|raLJ}ZQTE6<>eRLaZAz^5nilzxS7>EdQ7Mp(f#qx4*Lqrm%cEU=Y zx)rLb?OrC(Xn_hex(%rhQDIUmv46y`ds^(gPdv0czDWyQ%Rm3_Ip?1H%ehB4T5GG1 zf|394d3vOsqW*Cv$A=3hWAGY%0Ej|#mXfJS3NgsFL?PEbMy3%hQUmUP!B53g4jvy) z_Az*kF`A+<1<{nuAX;`IMt0+-Ee~=b_ckLp6W)Y8z`cYwBQNk4!ds9JxQ}o@3IO+` zR_t4+P#g9Gw&P~Py?{a7vQBOKyk}51Wb6P% zov24XMQpvO4{Tk8_oK7GPZNF)odlE$>y&m~2iTNB|f|zr-p7k;s+-A_w1|y$GA*gcL^hC7$txUQ! z3+42&Feb99G0)noTeX$P*6%OugS+*4|zF!J75_fUVvCtG2q|SW&I~Vs9?7_HPIFvt_HYZf_h|yOqnV{p)?};R*{Y z?WYyC`l@2>y?Ph>(+m6QZf$kfdi210v@=9@aS}XTc_+huy6%t= z&8w`nyJA1uu>V*+>5OUf8P?j~sJ>o4?vTvp@vF*|AOgv$dPYUx6H7u!O0s)oB>WYb z#<>|@4y5MPY!>I!SZPGfihNRTJ2ICHtebvFnBYY5x{w@~JCB?N)i8e0bW{%J7LD84 zONB-99SwyJ>Cr=a?2rykm;q<8AjA|gD)T1&rP&UK8SaK6T;r1yG2yz&42Mmh$VZaA zq)hWSKz`B7`Ixf!RMbi~i@0i`w+{84eTonXKl#*OR0ilpUEVpH~l$xE>jN<5( z@RbqMUk{XoL^M(kz85=a6xCwMFx%?x$)qrwP@?1Io}-AAmN!Z{tPiGjtyD0*Tq2=3 zWh!?Z#Xvb0GAV4f#)Rppl1K`Y5D{Xgd#+e4m@UNUl)l_D{KeNpVR(ic)<*e^xCD0xUcgzc zki+@n0Au>qLLr+;sl|+zH~l!jkkPfg&|tmH#y*OpxFU0fZ6S|cq}r- z&)ihTIf=WpSwI z2m+Oe0B6A~Q<9Vr;<1|`<)lM|GZdo|uXj3XPdU)#z-b50IMCxjuLI7%gy|*cIGc>= z{VF&6>u?bAMJZXb{Sywi?9dbN8;;j2pp#On|f1x3->W)xQU)l=6?csTZ8}r literal 0 HcmV?d00001 diff --git a/hexactf/__pycache__/extensions_manager.cpython-310.pyc b/hexactf/__pycache__/extensions_manager.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5da603f2e0604046bd98e9ac3431977e88ab3859 GIT binary patch literal 3566 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 literal 0 HcmV?d00001 diff --git a/hexactf/exceptions/__pycache__/api_exceptions.cpython-310.pyc b/hexactf/exceptions/__pycache__/api_exceptions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d333903cb270c3da81d7546459b61087b148c23 GIT binary patch literal 1616 zcma)6&u<$=6rR~1_S&&aN}7}(p)8#01M!6;LKtcxOTmDh_OO@HvNLvF*1PV^Zk0qn z0F6{s35i3I3<3nA6|w{psv^XhKVz?)Dw|U{Ar8DZwo?a;#H{x1doyom-hAIT6PL?H z0xkUR*Y0_Xkl!#^ZqP?wgIDc>;e^wW^r=TF`bKE-bhsv_h0vwsP6}2ko4jRy34geuP$XQ_YMX^ z1(>|t3iCj{42~ck<-{{M^-NxbGAwQjC_&_SUf3Py4(U+t@WN5q%ikdu@f;{%Ei2XD zX`C*ZUVw?9!<~*ko__v|`}>E-ldnh9yZ0acK?!MQMS;bz1ggpMeW8@!71^3f{3KN@ zh`GoLvy8s#2KG{^=;z=y;Z<8;5^|e-Lx*%|49%hSt%YE=$(S}PMrNyYAY|q+){A-x zV--8Iln6T#XOMX`4jSD4m}UjWg3woru`&7ct?E@Jq^fp%eeQP;L>zUiSA=>$i3io- zx*vuj>WY{AL9cpU?E68|sfq>OstVogakw)$kR@0G*HQ+UEqb`QjMmO+fri8lHY)`z zI`aF1v8>2gKjvwOeu=T0sUOZq@{I8~U`!$!nW++)nY}2{i)H2cZUPa8kg^UbGjk5T z^Jq|y2!%r95Q|=>y6<25STJyM84}OvBgai)bquWu&2 z#JFK{%8eoA<|lLJD^~MxYrd>2XY0D1SoZy-_bF&^Y^`FyV#zg_RwJj;^qy!muhn)N zS6Hk5?$vsGZ*0giW}sH2hEOA_aYS-)@C2P4wWy(=;$Mds&jN5tw8V>IoR?3+M{c0O z10y%lpsHtuJcZsW;t&F?_hdobHkH`-pTx+P0&4w6?PeG&UFfjQh>v756H%jYq96@(~74Y!LK7&QT( z7w3n0XX*652Y2bxX_hEP@6w9I$9J028q#M;^P=ZpD&tf-ne)o*Y`+T9Bnnkgq9wX+ JuG<%ie**2MT3SRw zs}b9(plC&;ph6Fx{4?h2Nq2Jd4gvLE{itmx5c;W= zaWX+U3ri4S3^DAY2Cia^E+A&GL$ilKP9n?!<=!ccXfD1=k z$0<8h#7Pi1s$zz!2E%Ymj+xwKNuFdWo?>a9oXb2wbC_jVwo|C4A0QJ|Gmz5CH3tn1qco5ZnulX4xlp~scZ*Rq4wu$O4sTbR%clT?&(N)WD2ZwMz9oEFDk=73Z z#&yj!f}+X9e=S(2Mpw1(sEvDjA{$%?y9Qw*3U{63&8KQ*O})Rf$6rKt)D+b58N}4R z2x2Z)DwoNXzMaf~eH2r^R4MJPb&|v%Kg3U74-1QzpOSq>qSiY^wePC?596(uYPF+Q zzmwkTX1uZ#ue_ZarPvw3*V*hPrYs@e%w{hz7n;uq?W(^m;2Wrc(St0?M_~% zhqIIi#)c%b1CNHNImt0BX&HdqU*ZG^5o)8?xMjS-7`1Sai;NqI7NTa$2o5m}QnID> zRLcaOW(dG!ly4uK_DIv?3juc`&IULjzT-5*kTWYIlcE{$P-ZBtd-aIY(129c!&l3t zYnMwimC|`ynw`Bg8|rexlxAtoGQg~)GzE9(_B-3eGE1rB+CoseiT;+0R|OA6u~u&| zySB)EuU0(I#m&eMiq5?4y4hMsZ{@jOQ2`%Jt z9_Mfh*6m~aAZ2Q&H_Hh1w=Y{+-LGdiIHfX2X~SntSIc=yZ#8Xqc#@`+`Oa`8G6DFP z2>^6Iw=4|1bGYk*%R#tz!17u;q?vv_JvO1omO&<-$n|Yy%aq55ZBC7~wTl$de+8Kx d{r`U4SdxA^okV6%52`-{XhTA`mp6_Y{{YsEiNF8= literal 0 HcmV?d00001 diff --git a/hexactf/exceptions/__pycache__/challenge_exceptions.cpython-310.pyc b/hexactf/exceptions/__pycache__/challenge_exceptions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..286fe0b8ee72e68dbb4fc2bef3e1550c141b2a16 GIT binary patch literal 1326 zcmZuxJ8u&~5Z=9qU$IdVhlrPuMU|5vZYU!}NSshamm<)(Ssk})$KmWVyJsnhTp%R^ z3g|$DB2o}SB3&dXQXmR`#cy+)kO z56D0qs*4L96@|?^o)VQ!U$mn}*aCN9zTK!u8OnQGZJ~S&9zEHCp>BdANR@I@H8`!B zJO^bhZVMV>Fz5blSieW zpB|qad?@`qK05h$T%BSt@rx4T>YTs^Sp*&GbWALoAN3Sm6Rw&D5s@8oK)bYSbj_~y)V~(vYC4!p7 z3^I)(g90Hrq&mx(A9zYJc1V7&m2ntVt~Z+8t8a<0RWC0J^)L$CWq&yR4X@oOuZvC3 zk7{Kx+<`g`V5iR6_Lj^;AuOwaA!M3vUrxYE7C4(UYe*c{b+R}>%WDe8bdIrR$U6b* zdBz@fykPJo%@_}T#v~%6O%+LPHd;|!QWpl3`637)WE?{J;v8xdD3G66Pa!rLV$m5I z`~Kt4a2sj{0{`Jpikrf!88K&oN5C07=1dAWlhzZ-tZo=Bg`F1fT!##a_vHQOQt#+X zZ-2Mcd$w2Vy?%n~$*bqReXwHkxbdcZ1aEjyPxx>N4CKQGjf@SGQ*LxAH(v}m;aKi= zF)3GSg;A-7Z8Tm2B^~Q5Qh;|1a>gL`fh{iFonKk0xXTshuHIuytM}bS$B+e#LK?{! z(HP(uR@^vVg(5{QYQ(?XpY2OYD?>bz)l2-8IB7Wvf3k>T3dMO8$oYOh7f?HEl^5Xw zW=K|fg-Z0fPD)=JuyuBXm=E9sGD1{|nCh!%1fIUMDn=tFNSu14k7Rb8nd@`)7T`-1 NzG-=qr{m_7{THZZNFD$H literal 0 HcmV?d00001 diff --git a/hexactf/exceptions/__pycache__/error_types.cpython-310.pyc b/hexactf/exceptions/__pycache__/error_types.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2cd78f3f8323725ee4ab09be8e4a91465d60c163 GIT binary patch literal 1106 zcmY+DJ8u&~5XbNAbK)d^C$U2TN}B>)(IJ$>w+;)JJ#&u;wpm%8!$kPSog zM5IZepg~Z8luy9N*p>?7d;uzUY$q`*-S1}pb2B%)+UaMri<(-S$3NR&mo)7cIg96;^l|R(S==d<`n9p*5K`2fHMS`jNjs2;;q*>d$6_u8%&vJN@`= z{p{6?(eazn(d+fmr-RYa;n~6A>D%`zdh+MT_p`%;^|O~Jr^kn*x1VJpVB}$g)O=zI z;<}D2^B(a$>M)^n>Jm$4Dcdn^Y6;)DPngUE%nbayj!U@;78}IfrjrDVFsh^qWnNIP z(;6kr7Y�P@8NKPcGN)nzl{Y77@(xMcoOQB}fkERk3D6 z7O}}}sN@pQ3EbLj^~!~2+NzTENbQPxzUsvz?haMos@?3U(&inT$U?()YPfs0b$%Ew zc@PlK7e3u4PTGmrv5gjLllR0Ldue@~%Qg&YglbfygsLKw@h}?em+elhYZy)6 Snw;lt9OD>&o6$0fa`GQY!ythG literal 0 HcmV?d00001 diff --git a/hexactf/exceptions/__pycache__/kafka_exceptions.cpython-310.pyc b/hexactf/exceptions/__pycache__/kafka_exceptions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f986c6f8af4dc30e12813703193060ebf6106caa GIT binary patch literal 2418 zcmb`J&u<$=6vt=Q>mPCAkkS%p31z7es*$LL#0^xDrlztePHQJQ_%hn=jNNV4UUz04 zH6e$HL~2Dr;!s3DK}sc5YKcQgd&q$kf5u)pHE~WsLP8vPZ`MweHlY=;(T?Wrj%VI{ zzTbTlKa)ulxIVr6Ywg`6A-`d;_c9^)5N_o`!wIJzsnZIj*f%_*9;?JCxj=Y~n@fb7 zf-c4?Cd6@`fH)ych~p3^c?#l`juQ~~@ifF~9Va2~=K~NA=s3kQWs)6y1S^s(4Udks zRN&Xo*-GSZIHDQ2eght7Mw@P4N?%UQHidF<@ZPJh8>si7BS?jEQZYEK#CRIgGVrF2Q9@MpNp_B8c|m@C%z|h z>?h!k!L5d&3CJQ@rSo*&n2*hycTHqpCT&{G8ev?unnH#t#@vP*FqVynrV?IN;tUc5 zB{GE#(rDALk1@xym13+-{v6LuDj`*_=GM7gn-zYemOC%hbl^90&Q;sf0K{Y3O_*HHi0kFpp&NOG`04##*Dg&BfI`{H^d}nr(G$d}anC-*F8+KhV7N!}i z`@H30e}J*;E!*q9NixQLhcSsn!k7wV7;_teUM|dZcjO4qgCRpOBx(k+cLW=>Fy0Dt zOA(Wvrux}amg%Xma~cNjvrK}=gjqGTO~5)}leo4?0&J2DkLi7m`HhBf(45+pt+!zk z{le;ev$nauW_51=u>HmT&g~UzYi*^o`2F_R%Uhq{>AI$iG`@BS-YOC$$*5R2m=4m&=9X1(q+B#!K=fj5|mHP3(1e*`@x({`OOs7S3`I)l=|7 z%YY?MV)RU&M>Mtxw=Vov5W##Oes?I$mCdy!tMl;I=7V4U-8Zqzbfgp!>=6YqtpM`i z7G&ZsACC2fQvUK}zC6Ju3ZLZ1CnpZ2Y9R$p^~6)Vd|sq)!W>#PjTED2B8>{mlK~96ic_N(3+GuE z?V(7qJVr>P^6@UCSm>IfKLm-=NnB~qm$O?2!_)`SF?JRh5rs8!fDF(fV<<6{9!eie{|%W+V<`Xt literal 0 HcmV?d00001 diff --git a/hexactf/exceptions/__pycache__/userchallenge_exceptions.cpython-310.pyc b/hexactf/exceptions/__pycache__/userchallenge_exceptions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97d7ab7b71762c7ba8f029049c865ea8dd463009 GIT binary patch literal 2549 zcmb_e&ube;6rNeFep(7{oVrOGl5JW_RHy}^mqICZWjQq%ximHfU53rhNZE9yRcBUU z8y`w-NNH0_A%LseRL%}62_}o8ZuRSG_PwuJo(Dz0^tWD(N6tCE)c{5M*X1?#6 zH}Z4241ve}`H$L8l8`^q8GTG}K7dzwU^wB_BXwG#6x)Vp)MJ$xCD#a#adV4sQ_#&= z#RNaj6W}L=34R>>Bu{~#(tZN`G|zya(S8#A37!Q%tNj$uT_^cdPaq@7({QTTQh{Ip z&{m?f>WF6G`VHvL%{JYVl)hYEYYOFH@aU87JL(;12vVV(R18ilF`j{CnmjIGi6X(1 zB3@11CRNH)JiV2xBySUwR8p{jiEw&F30b^pd!A_24wJVo!<3OB>*;S_baozDPxp2^ zkN2$Zy|23W_n!Pk2`Psefs14TtTIg2g;I7+gcB;TgO*~B&qbK-MbuTziH~dn+X;AM z@TzGr0of!wbc1df8?g=Zfr%tsCv95J8)00vnnH#t#@vP*FqV&prV?IN;tUd)B{GEu zVQJGa&6wlaN-@?Ze}7uQ&8R}nt#iA!Cj3UNFelV!f!{1RgWbPvH{HTbv1&U(wIBvO zs6q>VIt*fm12fGvnT2GSS{Y!b>H4JzXhSk*hKn{NZn~UL<3<{GT`(4A7_0ldyIQEV@R%^GhSm>g3iK1#`bmI(lHvKtev5vi>bXv!McaB8riu((-G|?I z9{+B2H-GAG{c3f;+3tL|1=i~9ZbRSx5BEA>-|b7Qta)@0*1?1Afm#mEgM(_hMFZnj zj8krGP#*iXub8}9UOzjOwPkx!*nG_rtF8)^dmmKWHpl5EZ^{XnSmWEn{B-f7*~P_D z`C5q;m&)^t3&rKOA#)gnLX#S64ZQ{&^?>gInglhep+A{_)?bf65kYFG4vCejos_5H zOHQFVgXU#4cszRRI*YAwY$GN(!@+><3shp?k!;IzF#0@;02WrD9*b8YB(n5F`X27z zdG!D2ldoc8AnEE#!@l7OEATA`(+X>N(q^oWJpmQb3gnt)*KD_8c@QA=0g@N=1kbW{ zZiKB*O0&xgOJz1cyRcZAlNaF!N1h7st!Aog>@ z6aR_*m%ns(w&CcUEcP7Jo*edbrNz>5u(uGUG1x~?I4<^Yz+{?ajXioVh<(`)=KWTK zAF2BXyZb-hIT`Fg`kf-QeK(n0iN5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!H!erR!OQL%n{ zW^PJidSz;UUb?J(Zel^Eenx6VVsc5EeriQYYF=?>eqOPD he0*kJW=VX!UP0w84x8Nkl+v73JCFs%OhAH#0RZ!KDBu79 literal 0 HcmV?d00001 diff --git a/app/extensions/db/__init__.py b/hexactf/extensions/db/__init__.py similarity index 62% rename from app/extensions/db/__init__.py rename to hexactf/extensions/db/__init__.py index 2457388..db783c9 100644 --- a/app/extensions/db/__init__.py +++ b/hexactf/extensions/db/__init__.py @@ -3,4 +3,4 @@ __all__ = ['MariaDBConfig'] from flask_sqlalchemy import SQLAlchemy -from app.extensions.db.config import MariaDBConfig +from hexactf.extensions.db.config import MariaDBConfig diff --git a/hexactf/extensions/db/__pycache__/__init__.cpython-310.pyc b/hexactf/extensions/db/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..674d45947e99583926c3cf1cfa0412c22ab930fd GIT binary patch literal 332 zcmYk0OHRWu5Qgo%s0xvK0MAa7ZqBUZ1c~w`r(Vs{l3GJt=yPFG}6{_4Nw58a*O`?7u;7PU4 z)D=L#2Oz8k;IrHcw*mKH#m`3T<2*ks)K+9&o~mu9s$t2ca-C-5oe}v)+6X5{`MPHJ z4%tkbQVP9Ob*0%txlLC$EGzJmsYbgI)Jp<^^?=GmXB7Q4UfAC LN+HtYHW_Ddj zN~J)I6p^AvB^D9HQIJBbAP#{>6$jKmWUoCXyFON`a%lVBc)f{3(yFuCnQz{_ee-7C zn{QTYZS`|d@_#-}{}$x9-?6j1h|uYUSAQQW!Vx~p<@f~8b1MHCM+D+2al|tt%@H2v>4jH+29?Ssc)}%wF|eKRsG{0Lgmawgh1LTt z_C=>{a$4^>jx;Bl*_aP-OTx$W{eT0B7KVd>TN8eU+W@yG0t|OBogl-VV6T;QsbWeX zhsfcQ2&-sQ+sRvM2k9bQN+fTSBTU*!j>6iGnXN+#%_svG`n7y2lP;tI)S$Z8w*7D()$B(L)$)=v(boEp)`L}?&R&_em!DS4Yu4PXHMiPR zv;1n^`sEcUfs@V`*8Ea^GW>8_vYy?w=a%f1+xF6H>j@bCc^Xy#eglx~@;w_o`=KOR z^9`0waYEA#Q!HrIFnyQ<WHPIo?MW@4 zSCfY9=$Kxr8bz88TPk6K?06_u7t8is#jb!~wJddK zZ?K0pC3|re(jg+{CiUytQ>=D2S)-9~7~>nk(Dq{dBN+ZL8OMl!>O}Xv6K>b^)bSji zCNh4?X?NS(<|EsD?=~NvYHct9E1z1Pa)of~wqsO4TeJMN! zUg_a{@W@v>lIF-$+`Lz0hQpkiS)SF7uNQKp#hMLziT3!d|!{85` z@}9z^+3C0jBQor6CZB#6CeXW}>S_^r@o)DPynjudXkdw+H@-E!HOol>zONU=?eMZ} zOaZvX-Qo#<4C)m|v`+-+i`XYktoAx%&BTXepw&YB4hoQ`77ujy>3#KZ?Vxw&%!eQCuYmiDhH{fI>j6e^>rn?O%jgF_528n#miC_YYhl{A^IIpi*O!9>?Vb*#Brw zH6}D-ipVka1ju)-8t)B?%cDSw9NsbP$Jft^g3ssUYfsP<1Z2O(XwFaQ7m literal 0 HcmV?d00001 diff --git a/hexactf/extensions/db/__pycache__/models.cpython-310.pyc b/hexactf/extensions/db/__pycache__/models.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07f26190402b8aec30302ce78619858043426882 GIT binary patch literal 4499 zcmb7H%Wot{8Sm#z&&wWcjH971{53d5SJ)&;>{9-u#nb()R$K8RC(Ove#}<) zB=%L`y$}U>egyEz(>q(p_Drdm}U=(=}D!46Vp^ zZPmBJT2y!I=-aGzq`A&_HCAWNk;WWujvMcrZWBm@HGwn>qy?nK+CbU`(gxCDb3o<_ zqyuE0b%AsXWR5M|(+0g0tWO&l(qVqavmoNKL6Y$>^9Q;#*nP<6-9+$U6n~!|;I1ur z=x0F^r+dLJ^Js_Oewx+rl9b-w(?uJXOPe8uO|+Eh zn#MI(XPRrEncN!N+&Zo?{Ry_lOf>7U<<@7o4V=*0W7BnJC~2Fl{@8FE&tMMF1~i*j zqOE7pW}(}D1}$gk^pY{rff|_5c%T3+vf+;iKk)c-w!$X5(f{a{A3Aw zPYuzN3-Mle-*qT&K<3NAF51uh^Tf+eNR9Y%H}%y3JKJItA3g}uie%xJ4X zHCF_jdEPCM(uS2zudrBWJIUaW!#vCFAiR*xWXGRoNf6`1MaUw`pkf^l?g|P*%5W#2 zB7YTzTYRzMWc_<_#z$O8Ghh#MGtMjs&kZs}IcJzaDbCW|d{@}XZA?P{;2y#ZyV7JE zSlZXoR6Zt?^^6s`GdZp@6+M5xss4Ut-us-Uf)Ov7s=`XwJa5v%F^?4iim`hLcnCDi3RHJT}Im zuB5H_20fVt9RaH&^1Sk50*i0bpl^{-F=$3!w==8Db`B=ot-){h5+S|~RWKU4X_O!( z)+3&#{s=iKh=VNf!yo(Ml)D|qhyKCcX*{GF)3w#D+m4_VO(Ufvt=-TcxhnPE4MJYU zHfg6r)T(YPO~Q9L+fv#0Dz9lx<1iWS-&5Mn{6;0nZr-mxo7)M}AEE#aQaIv}U+HAu z!OmVoOX&-rcQSIcCB2K7NbW{iw9KDn7DcqA(U)A1a`7fw`YFULTI#Ne^3&q>xQ13o zfrfk=_h9&K0W*0FW1ig@{7}t|9tj`_tsAT^vBc3f4iE~ zkLIcg)+xABPCX%xOKUrcxeCy#XmOh}s-`%S)bIPleZhB?N!)o#%wDCSa;vnrJWMGU z=;7=&sw}f6iO6IvRaM!L&JN{|`Uct-T1wgYP(Rc>?a+YK4^2qp(1J7%ZAgpJK}p{p z*m*YJ!~pRIi8o2mB!il?QXcL;SK%B@iAhxTl9zejKJHS=?KM6BH}yx?s1BL!C0#Fd zWlivGD&irWM>bObt64V?Sf;W{kazv6FG_xc&r(-;e6pk1<_?a&ES> z-yW@-}~2Zem$t?5k-|Tn;39mkKiD@ow@j@X+(!O!_F!He%GF_9t`z6$_3B*hMShIPE%K95Gkp;H z;c$;f2dV~@a}}@-0|?qdem1dxK&Nr#da6hiRDLLLQ!l=1?b3NIKc&4+%uA`%>S}GB Q{*KYJdi`Fv*Y2i_@% literal 0 HcmV?d00001 diff --git a/hexactf/extensions/db/__pycache__/repository.cpython-310.pyc b/hexactf/extensions/db/__pycache__/repository.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8e8632a4f9e279df2fd77aafe9224ff3fdd5b32 GIT binary patch literal 5215 zcmcgw-)|g89pBlXcY8nVIEj;{0dHstIiNPB2!vIk7{@|J?nU@qD&5QKd^0{@vUlgq z?h*U6E}=G33ytVQTSQ1uB2Z9J9#T;d&wb$^;AthEcmkha;H?PC_dDyGyS2}WBUS8a zXTLM^-I<;Fe&#cuxmGS`D{y`H*B{k>cU)2ahMB=jgUm&^{QwfGP~s_Ne5xL4sAZL) zt$A8QFYChAJ)>ckO<^0J)ku|7!Zy8h!!Fyxw!BOuTh2Ceu>cD3SptXX4zlz-J{yq~XPKZ92uG8f_Y??b|rGNDRYrO!~6 zYTKl&rFD9S8ce4qGpGg66g<=Lw3*4QXOxvJ&Cu+&QBH~4Ua3xVG{3Ev({zMcbX54; zD<(Zg$DlMLO0gxIVd-&t0!p)@6iak^l8!@3PV|z8UQ~LDo`#Zmww{enCr7o&)>?i& zXz`6=RfTc%F0}EUh1(y6BqVEkpj;uL;=({n$^%j?sN95_$nu%*!_33{=|1yTcm_%z zC_D#`j~Aw}Lw~APZ_r9@gSDEqsY}ei8no7?s=dj6p|Vz=T4n2%YOpc|VPZ{e&!3{p zQ(S)FdlCA!4G(0da$)SAMV`WPB@E6b1u}I0eZ^b<}W3@4$P@+TfWgf*fvi4!KQbA z_Hg$ne@ZkTM`lQyTR+;}x-DI$u_z+AZ`iL6#@&a(hzZ+y=XQTsvVnrZk3p3A7$!i1 z3O|9#Nk}@5pbw|YStY19YmP*;Q*SzSdD?kEqBKJWavOfFSg<&vM=X#?t56!5)mEcX z4>%HOl;SPVTdq{Eas#r36wjjbVVy^5*;_=ISZ#5F!~_lFH!)m;z5|ImqLTj4{CC!} ziAnOrR>w%{hWek*L~>60#4s7Rl%NOvT)f2^_$(Mm4Oi5V0Q$*Y!1|^ds(y~DB_)Kr zptb)3PI5|zVpd?n>E3&^d+TlKDUIRKO{`ssiD4@ui8$Hp(a>?QLD@io2uu_Ux(ID# zTx&DF5v5n^9&9u2@BG(r9aXj9@XR*IhbKTr zEP1E<^B?crd<2{_NEI?b2Z1B!-MNsQyCnbh*h;p9eA%BeiMBoyKMeydw^|K&^{l&szNwo%@``>yQjsdTn{YiP(oqz@|Z zZgR(g&f4t%6(P!o%W4xP2t9-X8-Dv=bcWRc0^~j^%(@s&DIC zq-;?G(HR||?VX1E+ZvyO7WgVd}q;$oJD zA#)7z)&EY!&0FxgkKB^Y(MpBAO&JvDgfOyMm)oMVqkr`~P!BgP5ocFJJmH3aWNw!`UvMW&t z^|4etxA*@Ex-s!jm_G@BtGNBFP-@YczF+Tu7PnDpR%&oe#jh%#J?Jn=mnUHnGS81= lfCmtPeev$I literal 0 HcmV?d00001 diff --git a/app/extensions/db/config.py b/hexactf/extensions/db/config.py similarity index 100% rename from app/extensions/db/config.py rename to hexactf/extensions/db/config.py diff --git a/app/extensions/db/models.py b/hexactf/extensions/db/models.py similarity index 99% rename from app/extensions/db/models.py rename to hexactf/extensions/db/models.py index f29b18a..235e482 100644 --- a/app/extensions/db/models.py +++ b/hexactf/extensions/db/models.py @@ -1,5 +1,5 @@ from datetime import datetime, timedelta -from app.extensions_manager import db +from hexactf.extensions_manager import db from sqlalchemy import ForeignKey from sqlalchemy.orm import relationship diff --git a/app/extensions/db/repository.py b/hexactf/extensions/db/repository.py similarity index 84% rename from app/extensions/db/repository.py rename to hexactf/extensions/db/repository.py index fc48a0a..2fa9bb2 100644 --- a/app/extensions/db/repository.py +++ b/hexactf/extensions/db/repository.py @@ -1,13 +1,10 @@ import logging -from sqlite3 import OperationalError -from typing import List, Optional +from typing import Optional from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy.orm import Session, load_only from sqlalchemy.sql import text -from contextlib import contextmanager -from app.exceptions.api import InternalServerError -from app.extensions_manager import db -from app.extensions.db.models import Challenges, UserChallenges +from hexactf.exceptions.api_exceptions import InternalServerError +from hexactf.extensions_manager import db +from hexactf.extensions.db.models import Challenges, UserChallenges class UserChallengesRepository: def __init__(self, session=None): @@ -76,13 +73,9 @@ def update_status(self, challenge: UserChallenges, new_status: str) -> bool: fresh_challenge = self.session.merge(challenge) self.session.refresh(fresh_challenge) fresh_challenge.status = new_status - # self.session.add(challenge) # Add this line to track the object - # self.session.flush() self.session.commit() return True except SQLAlchemyError as e: - # logger.error(f"Error updating challenge status: {e}") - self.session.rollback() raise InternalServerError(error_msg=f"Error updating challenge status: {e}") from e @@ -139,16 +132,6 @@ def get_status(self, challenge_id, username) -> Optional[dict]: return {'status': challenge.status, 'port': int(challenge.port)} return {'status': challenge.status} -# class ChallengeRepository: -# def __init__(self): -# self.db_session = db.session - -# def get_challenge_name(self, challenge_id: int) -> Optional[str]: -# """챌린지 ID로 챌린지 조회""" -# with self.get_session() as session: -# challenge = session.query(Challenges).get(challenge_id) -# return challenge.title if challenge else None - class ChallengeRepository: @staticmethod diff --git a/app/extensions/k8s/__init__.py b/hexactf/extensions/k8s/__init__.py similarity index 100% rename from app/extensions/k8s/__init__.py rename to hexactf/extensions/k8s/__init__.py diff --git a/hexactf/extensions/k8s/__pycache__/__init__.cpython-310.pyc b/hexactf/extensions/k8s/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..804a33733fb38c7ab1c0826a6c15f206dcb238e5 GIT binary patch literal 193 zcmd1j<>g`kf-QeK(iMR8V-N=!FakLaKwJz`mco$E7{!pn7|fu_bc@s5qS!enGc~Wo zPm}Q$dwhIiPELIMN`@k4ph__D%UeIRIJKx)KRq)yB{97+H9s$1-zBv;yClCrKRE-a zJ~b~rRX4F9Q$HiMA~CrnO+U4wBsH%%Ge56bKii^MKR!M)FS8^*Uaz3?7Kcr4eoARh QsvXEl#VkOAiIL+!0N^(?2mk;8 literal 0 HcmV?d00001 diff --git a/hexactf/extensions/k8s/__pycache__/client.cpython-310.pyc b/hexactf/extensions/k8s/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b8094259131778622072ba0294e84455753e542 GIT binary patch literal 5824 zcmbtYU2q%K72ZEdtF=F>d5#Hpq;6JbNtS)E* z{1;5IC}~nLrp0*d7tMGvp(R*ZGUZ}YOBNMPVfC2VQ%q^8Vz1T<^|+QM39au39Fa+~ z#*yTLpk)Yul*=kNU`=i^D|qp|Sujct{I^Ooh1o3c?HW6tGfks3YfM_sb5^B9VAwq` z+Xjs~#;B2V3RY>HQj2!=J!_ao)CV(nN3*7kvSk+>i_Uq29qjCO&kdBy4FO0Vcewp0 zLFH-q>|;O-4rK)L;Wd#6M67X|L^331#AXB%Cy822ixb&MKucmqB1xjuBu#F$B%!5; zq@YECIlUxZQJpeut3vaJ zy0vk}|KzIw+v{p$rPf?pQJZgW_^a0&s~e3=o6U2Ztp4$Gh z`tN?wSh?=6t*G$%zk)4vtd$3$L_aLc@QuM|KLey1cepyg#_`-MTq!Ma4tGMB8{nbN zL%A+u&ri7$f0}codTfnzIXw=g1S`pe0~PR-*(60P(C#v=9MKDJIZxK%AO%O)2Z6Dj1|sk&J`JDnA4N>_)&9uthQpy=f*3Qo ziV2sK8cv0l@^}TpAMOwx43qsjXu{zZxOvIp>w?R#@$)e^X7@WH;a&bCZdD+{JN#Lm zh*%?%E7YYm4%(&pxXaaJZX9M{DUKz3*nx}V#@q9%vdf*2>q%E!`32rhlAaZTq*er1bYr9!%IOtpUV+_ZPV6FmB;$%?m#dKe zcVhEBPJewj*^N7Pd+GyZpunvO^C>q4<-34$ubZ;}#Kzps#vqToA|5?t&t8rU!nYTB zeFcYJ`>!CYd$!98PIK*yJ`C$Fy>yjw=GY%7cBk0$}|`%yTRaO)c?UP zwA{g@MT{3M>YzjL*O!|gEO%W}WEb&0LTByKsD@pU?dk=h9<&{r9R(MIey6!qZ||eA zx~xt-+cC2O_o0+48sV&H&u!NQdt{F1?A6AHSHPdB;0ygvY8`BlgSATnwH2g-@H87DL7n=juZdUlk;Veh562TCyB<2E#j03OgQE zukc>dz>uyN?b(}3K5}t4`Jw9IMCnw{1cQm1RaE1sI!Q4Mc!{Y>sZ=P%!aufrK7c;HX2K9F9C5sk8#Z^Ox;Kc@b{d&RHnckx|il?Q$K(sXAK*9#H+w3Ky zpzMj@R=j&~$>}*Awb!*Y08Rvv20e&dkjmiBXbO4m3))*5;+# zt;{@m!%KJ0*3y9p!zHx-_LO3-RG2Yr$4f?SOeQpodra7seBQ8a21o*u!S=l^DMfR1 z+JdC}q1!$MB;K!x5})A*M1@!2zaVA+aHM(p3_uP^;`@0;jKilukH8B;To8EaOX*8N z#{cnu#WT>_`itLEb-&j`4dBvc=41DUVfBQyFuqO|abWwa}X zW{yL_<*Dod1x&%8lxWHUY_R|c#sz${#?AANSeJ+hNQPe%TeVnNgE3Hs2oIy_p>13q z11|9Z4+Xp+3MVSJV7bUyZo~Qj>>F=gz@QU^Fb0rU>dg;7Wk7#KonLF z!g&AcCPuwO>ejVC`0JnHB!~dOq50lod-T@lf6gk=qPK@=G^8*%Ha@xt5ig9R(WD)t z8{E24)n~iG=yr$GM0{@i8_r+(4v_Ge8>;_Bt#N4sGK8(q*8Q`~>el8abYkRs?AzbO zBmonijm52N*PBb9R(oE3JvUT+bm)np*ACG=z^Ez=9jZJ7g$$2Gb)g+(A<Ukz;HwS%t?wMq|% zP1)}s$i|o&DPDLw4g^D~WqPq<&dDFoO53!_dK2ZG1JR4AtK`g9PeRv;mDhE;3pa`$ zmmWca#|%XELoA^ipwA;|ABdF@EzuqzSYQt?G6yaqkx}|*`v=o|SRcIm36{;=5d9X6 z(-eG73ZDGfV^Z)S^-!497~$KKJsb&1Z#M?8`p^!eJH)zu2F4VoYENxX3*VB4$@DPd z&*1Ttp?PK?TF;M;{6wD`e`#uB{HT^b`oj45i~7XmOXE|=j=aDQwv#kG`N=@mPl0RU WY_vz2gO~^qE+qhv12_lZQ2Z~72~m~+ literal 0 HcmV?d00001 diff --git a/app/extensions/k8s/client.py b/hexactf/extensions/k8s/client.py similarity index 93% rename from app/extensions/k8s/client.py rename to hexactf/extensions/k8s/client.py index 0eeed02..59e189c 100644 --- a/app/extensions/k8s/client.py +++ b/hexactf/extensions/k8s/client.py @@ -4,10 +4,9 @@ from kubernetes import client, config -from app.exceptions.challenge import ChallengeNotFound -from app.exceptions.userchallenge import UserChallengeCreationError, UserChallengeDeletionError -from app.extensions.db.repository import ChallengeRepository, UserChallengesRepository -from app.monitoring.loki_logger import FlaskLokiLogger +from hexactf.exceptions.challenge_exceptions import ChallengeNotFound +from hexactf.exceptions.userchallenge_exceptions import UserChallengeCreationError, UserChallengeDeletionError +from hexactf.extensions.db.repository import ChallengeRepository, UserChallengesRepository MAX_RETRIES = 3 @@ -148,19 +147,12 @@ def _normalize_k8s_name(self, name: str) -> str: if not name or len(name) > 253: raise ValueError("이름이 비어있거나 길이가 253자를 초과함") - # 1. 소문자로 변환 name = name.lower() - - # 2. 공백 및 비허용 문자 (`[^a-z0-9-]`)를 `-`로 변환 name = re.sub(r'[^a-z0-9-]+', '-', name) - - # 3. 하이픈(-)이 연속적으로 나오면 하나로 줄이기 name = re.sub(r'-+', '-', name) - - # 4. 앞뒤의 하이픈 제거 name = name.strip('-') - # 5. 최종 길이 검사 (1~253자) + # 최종 길이 검사 (1~253자) if not name or len(name) > 253: raise ValueError(f"변환 후에도 유효하지 않은 Kubernetes 리소스 이름: {name}") diff --git a/hexactf/extensions/kafka/__init__.py b/hexactf/extensions/kafka/__init__.py new file mode 100644 index 0000000..04c5eca --- /dev/null +++ b/hexactf/extensions/kafka/__init__.py @@ -0,0 +1,9 @@ +__version__ = '1.0.0' + +__all__ = [ + 'KafkaEventConsumer', + 'KafkaConfig', +] + +from hexactf.extensions.kafka.config import KafkaConfig +from hexactf.extensions.kafka.consumer import KafkaEventConsumer diff --git a/hexactf/extensions/kafka/__pycache__/__init__.cpython-310.pyc b/hexactf/extensions/kafka/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f77c3e2e45647e03c5d2ceb95f7a557f218f122 GIT binary patch literal 372 zcmaJ+%SyyB6iw2{h%*D?4~RRvFd6qEg1VA%BW?l`LX&1n+oUv|VaDHa4e)&=e~SRQgyT=3Da4V&iWtJNid0;aCgKrB zFei1~Bs_sRy+_&PDLJDHx)=^`Wl_nSjkf)jvwq#^ZkUEUphamuKow=U8*KnEz+ILO zQz16G^QE&w3=<(_T?@gFR(dP*zMy*BYddZ|tww#C2eF{SkN40EA}}FJTlPZGb~o&uH%_fETP6o*Oyc5xkQ@tZHUBaZ^gAL6 zWW;?E&2gE|o;-f?M0=kHdR;`tpmBuhEw&8B2277S?LxNr7* zebFu7KUMix^=`3z^TSoEKXq1WC0E~;-5t(YOs+=^cYXT2)e6BIBo+Y{n7i`v3s-$# zE7u>o>iLKA%i_Sca2OF$$YgrevwH)gJJy)mg|0EZJBDX%n+V-84~Y?YbO)+O#(*vM z!eDfJvo(=ZAe56p>^(LzVcVN_#s05-cz87au<@*Vz3z?b$%;vfVO&%(ZgHAH+PE0U z_-00;tv90>Q|@Cd^pB`;62TKD(giUN><~6RmO=v*dgAogA@>1s3;<AIBY}Y$On$nUh z&n_d8paMH$3d5J8K_!@Jr9f`nHUj*>xI*Cq=}XaHv5)Oj&E-oUgIs#fESC}`wOwM) zT=vYF**V|&=FlG>&r0z4@Bgj5JRwQ{#LnJFfzCN7!OuZZi4spLCN(J%=*ym5RfNL|jbmDw3*u~N9gmK`@N71(mfYC#1& zRaiiu72JW-rWYHLVcV5jCA94^FdpD`bXg}l$&%*rkwctL6(f~8p%dwxg>_zY@vGAZ z+d33+3Kle4a1NB5Knbn`sR*fp8?`$soP=Kg2dHJOPB-4%>9@PRdmFt! zUxS7u`fktiThQ1KJaT(&<%1R07*vNu_OQA8FA44$L zoGP>JC~MnQpVmEW=WP3G-SGx*z#{cs7*=2!KLRGBG1qf~pvuB4KIICojVtk^D2}5L z``H!f7mhf`BNZMoB$Yfxv*3%dXi8A-#Wz?jOnJAC2P1|p2%6r*`~91btoRqVdw*$zqjov%K$zke zp2z(l<`S|(OxO^vu$Ek}!f*=Sf&HP?c<09BN8SFrYpFt7ABZntno^}~ofZ~g&(O)w zSaJJ)ytO`v<8&@wFnXdNnKm?%dqqxQaSFDx#|^>*=({|WKn0;^fU!;(<&3f&Dul^>1}kYjCHc>17IdAe(OQ}o9%S! zY;o!h$E!1&GKgNG&iKfIA%Wl>HwQ&PCX}||D7N4e@4*itGuzbaVd>2G@=By4T}7Jb zJ2Z%N%3Pl^$Pmc#J{LyD^PW?^Or5i5(Jp#rQfi(##EU5&rl0qG*YSe0&`4%BPrl?- zJVve1w_MJgkXfnnjI~W7lVKiWSA%js!@mS8aD)p-Kt&o{niV%Xl1@O_0YoG7;N1gmo7K;CMgCM*v!CeZ#HUlTaZUbAR}MvJYGWiu zab3J01VaA~jpnO}(F@Yg&acSLTu8R$7GU%qSy3Q*l~q9CCF!aPh*amb#lO7)^{SfGO9XWE#&`dZMyB!d|I%23)sQK`R;J+$G{-`N%AwhUyi7!a0#Trf zD1h}b3i4`w4bNzR{~==uunT$td?VQ7zi#xpA6otO58@jgyldiz9k`9tiD`Y1Zy^7< z?6?avKrYuubG^hue|?|v)YNPjChYr6{w)}zPzDKT z7Sd1kv*weB?Hz5*=1u+tnt2k%2^3GE5VyGjiN_-2VI%R~H}M79-?Qd0zu>t0uRsa# zM!{R$AaXWKjH%oy@h+c>3`i0gCM;3L^UG!UXA|jRV-e~kO;BKFQCq;wI-wQ|2c8*Z v-_tCml3-fs*t-J>LNJjKYaO;hV8|qoIg1P|nAeaDGD&i<(;S&lrcV6_{73d( literal 0 HcmV?d00001 diff --git a/hexactf/extensions/kafka/__pycache__/handler.cpython-310.pyc b/hexactf/extensions/kafka/__pycache__/handler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ace2d47d736a97def23cacb45d670c150dfe56ef GIT binary patch literal 2543 zcmZ{m&2QX96u@WvZD+GYh0+F5VZ?zhltgeqC_)vXDNxZ?&`=5s$Z~dOH*UT5G9Hue z%8Npi141AnK_Ng>B?5{nap0o|4xIQi=E{j?^A8{-cr*KvO;K(6c|7yx&3ohbdv-cG z=^;4iw|_K_O(68Ad>B7;cz6!3>@*012#!%z-nAGvwW`+Ct2&m?dTcb!s;SyWY&Gqw zjS)p#4t2M2)q4>UlUVDBSZi8!g5X6|vHt>#=xjw3`h3zB#zIu%6)f(1HK#d!g{3u` zWl_>N$ymyu|M2;YvJ=Z;9MhyhvvahSW)V+WyC%(yA1*vR2Uqq62#TthpsKbEu78WG zIyHz!O=?lQu1$m6)Pc4->&7&y8^j>yy0M0Ml52h5sx_FzAMUSz{9XXUkVcv zQ+C{d<*LpJMnevM3;SH2B(hT z%8$LTZ}zT#>i0Hp?c7*D=J$U7c<07X5Rrds0iM=A+}Xa?`}!8NKG%%JQFUDASfO-Tb-x+F{ljj=lnB~ z%6XsBck_r*;@2Y@lgy8j;Wo_qH?eT3gd%8WjZS61fm6wQVH^=3tiwwC=2ct+`2iEd-0N&A)bj@#JE~WD+mo$ncV|Nvqx< z#2_NVgBXMU(#rTv#O?>}zfaFfc(d6?)Ffe}P1B??yFjx`JZ;U^hDbjVwxZc(dO57| z`YgT7X_7^0lFeR{sWrRYhisz3kkeq08ZxZR2cb<^Se_m(VMm=Mto<`(O<_-) z!X>Tq(3yS6&fIBfABwUt%1fcBLAG}mIobjcxAEIX7hvoHgan}Y2FhW9x3q1Ekzt^& z*)_SohPqZ^Z5f5Rjk`8Cw=AL;Cg>W4E%mlBAZrsdN9<|PvlW(5Bny2T%Ba$}mA<1O z?CV=wP61GEYh9;svRAoFT&V-_dJ^6XP-Yhq6a}w+5u>h)QD*{lCiq0*uHm6x;a<^* z*>&G{S1{<7+N)!_rMv5{pvq+4mAf*a-ai2^C~wl;tJelF4q@H7a!Zo5x$ZAK-}~}Y ziFfutOsU2@GWdwerbMS>UPlzuQ(jSfZ9UX8w%VcTo968@2swNMy zNbBR8hR;*K^W=VzUI?QY_=AyLPNriM`UyVgvwNiRLqp_-q1RK^47uN#95XoQS0)ut z>@ds`W}L17!m`$j2O5+IOYK1VAB_ADBwNCY(?$czh)L*D-Vjzzep{%R?7K81O~NX? z*TXm;A`B%>xF;{yXo~|v6;CnC?vr|@y(J>T4$3YZi1J>qTG(Llc@^dTJ_$R^Yx1is zFjZXl#UW{<+~;#hWCi-NqaZZr06vT@-NW|-j|V`OGy_jz>))~tJ*#EezpDM)IdTV? z`|X{rOb91Xn-T=V3xZ}!@|Z%q6a??)VJy840(P(Tr6gZaRBKYcoDwE~VA#V_XmZe0 zg`kg1jF~(?RrO5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!H!erR!OQL%n{ zW^PJidSz;UUb?J(Zel^Eenx6VVsc5Eer|qVW=VcgCQxB~ fd?t_`AFo$Xd5gm)H$SB`C)EyQK`|4MU||3N)`=*o literal 0 HcmV?d00001 diff --git a/hexactf/monitoring/__pycache__/async_handler.cpython-310.pyc b/hexactf/monitoring/__pycache__/async_handler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbe3f713bba8b1c01eba3d4125209331beebfbfe GIT binary patch literal 1874 zcmZWq&2Jk;6rY*>@Y;?|2_ZmQp;Zg2RtWosphA3zrt}a+MQIh)mu1=^hE}zu^wqjj>;C z)^egg1wjzhGhk220p*mlGjCvVi`!=ev^d;_(J^}s?!lh>l-Oj@0PW3c?Q1p5ROt6f$6yt0S2RUDP#UvC)cJG_rsvY8uBV!oM){8EW z#JI@8D)^znA6tp+KsMm*&uP^R!#InS3d85*zn%UAC8X*n>6k~!OcZ(2zb(`uSn9{a zD9c2ih>xR5+8>JJDAoskP@L*Qf?xdzArF@?=}l%5NJH>bUFcfWr7ifaQ;&9O`Sxmn zdKW;v7AX&IeuFALfv#)=+GIrMh7HdcCHsWidk{{|M%HYLLKG;`Hn&bGB@fNs5eb~~ z{d-gGM?PTVOA!~6`)Tg$q3{j1y*mM|oFB3xJ}hrS-0D?7RsO*=%X^pF}awdQUfGpp_UAHzTH(^qD@8Gg6#;JY-B!xN{+XO_qjJ+t| zyo3s#iYc79XvkZRCA9MIS&`FntqJL^+& z0?3se-2e`=U&yZv(b_k7ov_C&cest?5j}!c)VlDH-?{rz#-02+=xdHzv%59!56L5p zggg5lELckm&fI#%g7w%k7GOjRpwqlSg4I0}vA`{F+>=Etlv+U?f+36XO3;#@f5P_1 zmk_*$PRF8{>hkKgl!f#WIDI){xBT7m#)DiOPeiN*_k}qKG%&11LkV2CWbk;^SJ*O595d6A2n3C{L-qGy3^I3TaWkwIsf7NN0Q zQSk~TB(edyp+11F#Tf9xrdNO$Tg-dHUa%+E>@Kz0#eDsr^4&{ZSxNj$ltKDn$us5d zItUklL%GAyS{PbwLkVbr= mCakgz2dl>NKgAqb(P=W=B)9i@?c)~+y~5Jq5f6&qCi@=_HqN~O literal 0 HcmV?d00001 diff --git a/app/monitoring/__pycache__/ctf_metrics_collector.cpython-310.pyc b/hexactf/monitoring/__pycache__/ctf_metrics_collector.cpython-310.pyc similarity index 90% rename from app/monitoring/__pycache__/ctf_metrics_collector.cpython-310.pyc rename to hexactf/monitoring/__pycache__/ctf_metrics_collector.cpython-310.pyc index 7c078cdc3ec3c83fd68a5bf116d205ff4ef56142..b1c37d50ccfdb7b63fd0e09ce13871fb20387e8a 100644 GIT binary patch delta 32 ncmaDS_(^bs1ruY)WJ@MZ_Kehu#N?8+&8bYnY>ZKpD>+I5vrq~; delta 28 kcmew)_)c(x1ruYyWJ@MZ=EQ=6&6!NXY>c-i*K(8s0Eu)77ytkO diff --git a/hexactf/monitoring/__pycache__/loki_logger.cpython-310.pyc b/hexactf/monitoring/__pycache__/loki_logger.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f039dedc77bd7fae969cdd0e3865e5d1c89dc2a6 GIT binary patch literal 3014 zcmb7G&2QYs6`v1sNUm0ES#ew^X^IWnqF4l7H9=CKsUk#)EjNLz0(Oj`L<)lD%&sVL zKQf$kq{VKKKmx2w3KVS*1=L~%G-zGq(4Kn9Dfj#XbM48AdTa|6u-*5_)k=;JMM>~+ zX81Vsdmq0y)0>~K5V-#R=g-@JnkVG%*g1PS(0L7>{9O=|NScwbeAAE?eHOCf%|i}v zmN^|abSdiaOmw`^qvR@)j&%2lboW^3Lr+K#dY)oi!5$AQlHMQ<{~oMBHX77cSM%L& z@{_nLGo_(Fx8B=Mr`-gV&R#QsO&C=KK>;ADc#BCU`Mwi6*prTOw?qqINC9J>^l(Xg zZY_)RZJd9-*KR9q{S4aCKxYX*vkJEHc>|vOB8U+kk%)}QgpSw|w!sH5A>C+j>&N|m z)QvmJ&PTa2gMO5ibBpo~Do(DlmKHFPc0t@F8l&)^x0Y_^O6N=Mv?Js8uIhE$OP5u? zZF>EsWHZh()orWq$Nh9^Q|-iwX)Sen-PH7S3W^2ra)oAp*ZNVE!nY{GnRE1#pbK<( z>1zfzPt>f?fK_)B2C63)B1C|N-PfTZ!PYI3(lIj>d{DUfV?O31K4FIMkt0fpaYlTP zjveDp1Zp4Au{&}no^iK){0{A4`5j>fSBo<}b9?`!bn-vV(;FUj@^ z@ZB%yM1f*Rr$Ao-rAHD7*FacB0{sgb!z+duYS@GwK{8z+dlXUyQfEXZOUWLl5}i0l zK(H-W@m|LeQC-tu*6XM|kK4++ z8785opmPre)(ErPSNb{V*udyGQMck`+XiYUQGJ69U@)G-1SanEbshB9V5q9P`9P~E z&XY8?o>Z-PkQwVLt$R9LT>I(uYpZX+{pQBiXl?b)^_$nPV0y_kfkH8y+vwF#It?B! zKwSa8#91^&87{O+k^zaLa>ANcNl(j2Difz!-r)Ls2v}tCEtTa3szy~~?pU674HyN8 ztp@}Iu4-F>#udqNtt$teer|&+GnWJfW3iMW zs<1lqPt+~Jgw_Ts^!sIf&Zs9=KDsB>T3zZm!*vVRUQJyxum<;3@p zc|}4bNX(N9+sIk`n(u%(AP02Y)dKfWB!;1TOkcV~?$A4|#fvQv2!eFG4AhzGa0Au3 z#X9aHJ}cl-6@KYsMp`hRQIa0Dz2Gu{^=$f5qNoH^^P8zYVH)sXYgqtWM)B9jYRcfjX@D1IxG7 zu61w6*+AXpc0L(s4WWfZ1spc$#5-lPEb+6#Xe-NTLDK8=S3<;trCkug79Wa-!!e0? z^ePUYMV+O2i8_KWQ`C$t{NJc&^XTVs3G{Izu$44VyHIGliLwql$hrt4w+`xC7Y;d@ zvK)3U>utt4a$CQO|KlVLr(~rgV-xE)aQJl;1^mL0VoO5(oA4AHCeQ}>Q$xNySa52T z!MoyIpbi^8_dkQM0ls<#SHLW5xcV7ve-p*CD1L;ZKvy6*6$K33!qDpJ&gj$H3&pn9 znA3Q|)GwjHO}3~_VSRcMYo4COnvWh1Fydi#diH>$;C8Ax{*Fwx!P?2OrC8)lkvR>n Yzg%wmD`-x>0Kz9e#T%E+Ek$za z*`XZ~RAIn=tbajUKze9_{-t^Clm9_bv`xMgx=(qdzLBnq_<8y;M+h|6C-cEZj*!$|fn z`yvi`L`%>9Y*aqQllc&_;WKXdCST=dY;{a-ar=dX4dpd5)b<@-;ob|wcX^f9@UHMW zZ{Y3m1-^)Pl`rvSylZ@gU%i$L*SWge7fWBfE+b4(hOuEGtI1m;?K; z)3u_dGR5A#$7uDzaji<*_KXRH)6c#<6y<5zE^JU_ZV zAQl9pG#N^NZFW+U7V}p0yf3!yl5M$;Y6{CknWRJiYB6UR%crS02&4!*og^yeZnX2} z=3d@Bvjh~zq7z23s`5m@k+j;Cxz(a{ILy1U$+h-)@F*6|w9_nC-G1Gzypc#{8l`>Z zW-^rHtW_06O;tU`TQv#*HTtTi!O>+wWKk8}kfds9ZZ~SRY?v`fdC4%50jP&_W@mA~ zBPas=!w}!Y>t7GI9%Qk|wz|oHhuy2?i-KRW~uAU>7r0Zk3L=NC`{Qg&ew@PVK0p5?8xLc!jLoBydQTHoLSM7`*E|_ zPBR{IO}OUMMD91krjAif06FotiZKyZJ@X@1?&8T7A*3PMb3>Y(y)>c6Y_}L9WP-Uk zEiFrsal$20Awm9_5m!-Fl|s=kQOPJ=ESi&_o&!SZ=gk}aGz$CK&COym>2*XpmZbCw zX(0xPcSyWeO8UfQh)?ijv=Pr-WamO&YKs^~mOrwPcD0QsYePumn4K6$Y-;q(o<+g! zB|EmKtmjbJDO#=5R(GZq!MGS((ILtKgzjXuh^I}8MaBM;PJa?5ls^BL9|Sbm#+_}q^Wa&snvq? zI$Rd^+`!DtgYe9Cw$84w$xqI4*&8OGk0-~9|35qn?447TJ&m$U_wr=DL^+Q{r$9S0 zm0=%9H(-l~oh!Q|;#j#+812Wrjd1pT^!bZV5+TQ7Kgd@Q$TbY{0}?+Z@ji(UNc@;Y z3!=bUS(q`8@_GqW5g7}hHO+|2InWyF>u-Vj>U?Ac6^X=A zS;}3vr0FUTN9-tf}g$X659!9?hL+#9GM+t*PL{ml&9R1ktcu)?h9!8(cX5t)ce6CG(kQ zX~ul`7RJnKIKvlW9))jBLxqAgdMuA|#Fu09!~l1${?0rywO6g4J=Liu{O-W7WwguN z9c~xxnnRCk;DZM~*ol!KYyOLQckRfXy1lBb_38(vxWOw!#=WBo+8RgR)R|T; z8^;S%B#9>`uU#_omZ^8cc>M9y!Tb%3%}3?-Ex|tTs0w~9!oCV-)?vreG-sv%_&#rd zV+)f@CC7>+qof&*CpA^bYBtzkAuq@)gI$!MgkF&~Ma z(~w_~piATx2~1p}h6@nw)^fo}s0@(lm26TD_&UhMB^s%N$|g#FO=p||Y%gxg0AeO4Wdw2WGV0Z8S&S!VDT4i-{5p2-f6suab{0?w)BlNy}uQbK4 z&n)cMsFIthY$_{9=X?3Oj-E8ngNW zTSMPF%rog-jxqm@Txs(yw4PTte~%NpHM2V?RE^06d@dL#21O#e)Abt+(r=624~^kv z+mQC*M+`SUGbRfXx673M+4!sZ9QB9BSNL8$Vt-^qCx1U>PYvlFS2*G+A|OL5)+||u z)U#w*6YNS-o*=2|AMQ=A>1@yn%_*` ze+%@bhvy-;u&9QdQZq>*AK~OBJ3`ttWdWXJ4K%lKAnnO}yAm_8}&hxjT34)_KUvfR3xFEqdZWjzS2*4aJq{u7qSDDX8*3bT_B z&e`t#ycTP>8uVc>3GF!avxn9NdybbSKqamojwJ0d9R!)ch`H<@*59Bdt1qT;?+K=1.0.11 +# mariadb>=1.0.11 prometheus-client==0.19.0 python-logging-loki==0.3.1 flask-prometheus-metrics==1.0.0 -psutil==5.9.8 \ No newline at end of file +psutil \ No newline at end of file diff --git a/tests/units/.gitignore b/tests/units/.gitignore new file mode 100644 index 0000000..d9cf955 --- /dev/null +++ b/tests/units/.gitignore @@ -0,0 +1,2 @@ +.pytest_cache +__pycache__/ diff --git a/tests/units/__init__.py b/tests/units/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/units/db/__init__.py b/tests/units/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/units/db/conftest.py b/tests/units/db/conftest.py new file mode 100644 index 0000000..ee4241e --- /dev/null +++ b/tests/units/db/conftest.py @@ -0,0 +1,25 @@ +import os +from unittest.mock import MagicMock, patch +import pytest +from hexactf.extensions.db.repository import ChallengeRepository, UserChallengesRepository + + +class DBMock: + """Manages database-related fixtures for testing""" + + def __init__(self): + self.mock_session = MagicMock() + self.user_challenges_repo = UserChallengesRepository(session=self.mock_session) + self.challenge_repo = ChallengeRepository() + +@pytest.fixture() +def db_mock(): + """Provides an instance of DBTestManager for database tests""" + return DBMock() + +# autouse 명시적으로 call 하지 않아도 사용할 수 있는 옵션 +@pytest.fixture(scope="session", autouse=True) +def set_test_env(): + """Automatically set TEST_MODE=true for all tests""" + os.environ["TEST_MODE"] = "true" + \ No newline at end of file diff --git a/tests/units/db/test_userchallenges_repo.py b/tests/units/db/test_userchallenges_repo.py new file mode 100644 index 0000000..9d22036 --- /dev/null +++ b/tests/units/db/test_userchallenges_repo.py @@ -0,0 +1,36 @@ +import pytest +from hexactf.exceptions.api_exceptions import InternalServerError + +def test_create_challenge_success(db_mock): + """Test successful challenge creation""" + mock_session = db_mock.mock_session + user_challenges_repo = db_mock.user_challenges_repo + + mock_session.commit.return_value = None + challenge = user_challenges_repo.create( + username="test_user", + C_idx=1, + userChallengeName="test_challenge", + port=30000 + ) + + assert challenge is not None + assert challenge.username == "test_user" + assert challenge.C_idx == 1 + assert challenge.userChallengeName == "test_challenge" + assert challenge.port == 30000 + +def test_create_challenge_db_error(db_mock): + """Test challenge creation failure due to database error""" + mock_session = db_mock.mock_session + user_challenges_repo = db_mock.user_challenges_repo + + mock_session.commit.side_effect = InternalServerError("Database error") + + with pytest.raises(InternalServerError): + user_challenges_repo.create( + username="test_user", + C_idx=1, + userChallengeName="test_challenge", + port=30000 + ) From e831adfc7b9e38734478045f072874266518bebb Mon Sep 17 00:00:00 2001 From: S0okJu Date: Sun, 2 Mar 2025 15:44:40 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[Test]=20DB=20Unittest=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/units/db/conftest.py | 47 ++++-- tests/units/db/test_userchallenges_repo.py | 163 +++++++++++++++++++-- 2 files changed, 185 insertions(+), 25 deletions(-) diff --git a/tests/units/db/conftest.py b/tests/units/db/conftest.py index ee4241e..ca4a675 100644 --- a/tests/units/db/conftest.py +++ b/tests/units/db/conftest.py @@ -1,22 +1,45 @@ import os -from unittest.mock import MagicMock, patch +from flask import Flask import pytest -from hexactf.extensions.db.repository import ChallengeRepository, UserChallengesRepository +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker, scoped_session +from hexactf.extensions.db.models import UserChallenges +from hexactf.extensions_manager import db -class DBMock: - """Manages database-related fixtures for testing""" - +class TestDB: + """Test database setup for integration testing""" def __init__(self): - self.mock_session = MagicMock() - self.user_challenges_repo = UserChallengesRepository(session=self.mock_session) - self.challenge_repo = ChallengeRepository() + self.app = Flask(__name__) + self.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' + self.app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -@pytest.fixture() -def db_mock(): - """Provides an instance of DBTestManager for database tests""" - return DBMock() + self.engine = create_engine(self.app.config['SQLALCHEMY_DATABASE_URI']) + self.session_factory = sessionmaker(bind=self.engine) + self.Session = scoped_session(self.session_factory) + + with self.app.app_context(): + db.init_app(self.app) + db.create_all() + db.session.commit() + def get_session(self): + with self.app.app_context(): + return db.session + + def close(self): + with self.app.app_context(): + db.session.remove() + db.drop_all() + +@pytest.fixture() +def test_db(): + """Provides an instance of TestDB for database tests inside a Flask app context""" + test_db_instance = TestDB() + with test_db_instance.app.app_context(): + yield test_db_instance + test_db_instance.close() + # autouse 명시적으로 call 하지 않아도 사용할 수 있는 옵션 @pytest.fixture(scope="session", autouse=True) def set_test_env(): diff --git a/tests/units/db/test_userchallenges_repo.py b/tests/units/db/test_userchallenges_repo.py index 9d22036..a571b90 100644 --- a/tests/units/db/test_userchallenges_repo.py +++ b/tests/units/db/test_userchallenges_repo.py @@ -1,36 +1,173 @@ import pytest from hexactf.exceptions.api_exceptions import InternalServerError +from hexactf.extensions.db.models import UserChallenges +from hexactf.extensions.db.repository import UserChallengesRepository -def test_create_challenge_success(db_mock): - """Test successful challenge creation""" - mock_session = db_mock.mock_session - user_challenges_repo = db_mock.user_challenges_repo +# ============================ +# create_challenge +# ============================ - mock_session.commit.return_value = None +def test_create_challenge_success(test_db): + """Test successful challenge creation""" + session = test_db.get_session() + user_challenges_repo = UserChallengesRepository(session=session) + challenge = user_challenges_repo.create( username="test_user", C_idx=1, userChallengeName="test_challenge", port=30000 ) - + assert challenge is not None assert challenge.username == "test_user" assert challenge.C_idx == 1 assert challenge.userChallengeName == "test_challenge" assert challenge.port == 30000 -def test_create_challenge_db_error(db_mock): +def test_create_challenge_None_value_failed(test_db): """Test challenge creation failure due to database error""" - mock_session = db_mock.mock_session - user_challenges_repo = db_mock.user_challenges_repo - - mock_session.commit.side_effect = InternalServerError("Database error") - + + session = test_db.get_session() + user_challenges_repo = UserChallengesRepository(session=session) + with pytest.raises(InternalServerError): user_challenges_repo.create( - username="test_user", + username=None, C_idx=1, userChallengeName="test_challenge", port=30000 ) + +# ============================ +# get_by_user_challenge_name +# ============================ + +def test_get_by_user_challenge_name_success(test_db): + """Test retrieving a challenge by name""" + session = test_db.get_session() + user_challenges_repo = UserChallengesRepository(session=session) + + challenge = UserChallenges( + username="test_user", + C_idx=1, + userChallengeName="test_challenge", + port=30000 + ) + session.add(challenge) + session.commit() + + result = user_challenges_repo.get_by_user_challenge_name("test_challenge") + assert result == challenge, "Challenge should be retrieved by name" + +def test_get_by_user_challenge_name_Invalid_name_failed(test_db): + """Test retrieving a challenge by name""" + session = test_db.get_session() + user_challenges_repo = UserChallengesRepository(session=session) + + challenge = UserChallenges( + username="test_user", + C_idx=1, + userChallengeName="test_challenge", + port=30000 + ) + session.add(challenge) + session.commit() + + result = user_challenges_repo.get_by_user_challenge_name("invalid_challenge") + assert result is None, "Invalid challenge name should return None" + + +# ============================ +# get_by_user_challenge_name +# ============================ + +def test_update_status_success(test_db): + """Test updating challenge status""" + session = test_db.get_session() + user_challenges_repo = UserChallengesRepository(session=session) + + challenge = UserChallenges( + username="test_user", + C_idx=1, + userChallengeName="test_challenge", + port=30000, + status="Running" + ) + session.add(challenge) + session.commit() + + success = user_challenges_repo.update_status(challenge, "Deleted") + assert success + assert challenge.status == "Deleted" + +# ============================ +# is_running +# ============================ + +def test_is_running_success(test_db): + """Test checking if challenge is running""" + session = test_db.get_session() + user_challenges_repo = UserChallengesRepository(session=session) + + challenge = UserChallenges( + username="test_user", + C_idx=1, + userChallengeName="test_challenge", + port=30000, + status="Running" + ) + + assert user_challenges_repo.is_running(challenge) + challenge.status = "Stopped" + assert not user_challenges_repo.is_running(challenge) + +# ============================ +# get_status +# ============================ + +def test_get_status_success(test_db): + """Test retrieving challenge status""" + session = test_db.get_session() + user_challenges_repo = UserChallengesRepository(session=session) + + challenge = UserChallenges( + username="test_user", + C_idx=1, + userChallengeName="test_challenge", + port=30000, + status="Running" + ) + session.add(challenge) + session.commit() + + result = user_challenges_repo.get_status(1, "test_user") + assert result == {"status": "Running", "port": 30000} + + challenge.status = "Deleted" + session.commit() + result = user_challenges_repo.get_status(1, "test_user") + assert result == {"status": "Deleted"} + +def test_get_status_invalid_challenge_failed(test_db): + """Test retrieving challenge status""" + session = test_db.get_session() + user_challenges_repo = UserChallengesRepository(session=session) + + challenge = UserChallenges( + username="test_user", + C_idx=1, + userChallengeName="test_challenge", + port=30000, + status="Running" + ) + session.add(challenge) + session.commit() + + # 잘못된 challenge id + result = user_challenges_repo.get_status(2, "test_user") + assert result is None + + # 잘못된 username + result2 = user_challenges_repo.get_status(1, "wrong_user") + assert result2 is None \ No newline at end of file From 9e06aa4c3a2e63afb3473f18dd204f9849a88aea Mon Sep 17 00:00:00 2001 From: S0okJu Date: Sun, 9 Mar 2025 22:56:25 +0900 Subject: [PATCH 3/8] [Test] Exception handlers test code --- .../__pycache__/challenge_api.cpython-310.pyc | Bin 2677 -> 3749 bytes hexactf/api/challenge_api.py | 46 ++++++++++++++++++ .../__pycache__/handlers.cpython-310.pyc | Bin 0 -> 764 bytes .../db/__pycache__/repository.cpython-310.pyc | Bin 5215 -> 5301 bytes hexactf/extensions/db/repository.py | 4 +- tests/units/exceptions/__init__.py | 0 tests/units/exceptions/conftest.py | 26 ++++++++++ tests/units/exceptions/test_handlers.py | 24 +++++++++ 8 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 hexactf/exceptions/__pycache__/handlers.cpython-310.pyc create mode 100644 tests/units/exceptions/__init__.py create mode 100644 tests/units/exceptions/conftest.py create mode 100644 tests/units/exceptions/test_handlers.py diff --git a/hexactf/api/__pycache__/challenge_api.cpython-310.pyc b/hexactf/api/__pycache__/challenge_api.cpython-310.pyc index 5a5b35707017186dfb81bbbea69133b6acab80aa..47b0b8d9a2ef99b2338df947ef9e473c16e8779a 100644 GIT binary patch delta 1368 zcmZWoOK;p%6u#&BF&@X`B-6>{1;P?llo3e=DWs%o1&Vl7s7(cm1ga%8a_8Pm9sHPF zdm0jX7^La85@A#csT3s=Y}jT8KY@SXWj7!scCcXqoa-dh1-9<-=kwijeD3*<|Em4) zvK?0{Cc*2EXTNlw%pBVcHt`pli`#* zRbV5S4y$ez2-jGdUl~%j#%p^s%w!WCVAf;GD$IUFhTzuOB%69f+}VPk9`jXprQqk# zzdpliV`7HY3u3+qm>v7)*nHt%VCoKOEFAy-7Ti5shW8byjAFGhXe;ty7nhDtx!%E* ze2*q2Cy?EeeD9FHnkny+PjB=!2ATGdh+B+ii0297<~d??o;)Q? z$)K?GDS4({GB*iRv%z@)h#Nk+Ak>>{3^K~JZ|Nxj$jvuvy9^+0g^7nh(@fjaPslV%U zk*wuL$WxDbsh6vf7xKShHP-|0J`a)x6?JTesG(_;a`n&)1Gk)bySx>|9?MNX@uMX5 zqBhUfz)#X#?{zT~&vmccl$#b(wEyDcvW8oF!A9IbYY-^bcsq<71LCvi`-dE!<> zc_^Q=*4URDmr%SSD=LhGD4#4gAGSW*OjTdthhEU*$=Zwd=jB3|-GLwQT;oE-;%Ii;Pf*_u=H2i4f-?s-30UHP1=#n>SR@(g z|B1f44MWJkC|CEAILeg{Pjdxj7aBT?Zs+CqK5z4G>PtFFMMD>Jk{eH1Nvz8E=4eZ= zuU9UWDTI9+iDUr{Rdg_m+on~N)}}T%V0=mc71z$5sE@%EYck0jXhv&#T@xD;-O`uf zqfw+cVe{-e{Z}Z7^=2k&YeXpsDT`}8jhL;Ce~(xQMVUOu%~e?+VCIZiCHKB delta 346 zcmX|+F-yZh6vyu+O>HiBm!?glU<-maBq%tEn@)8U#6h^V7pf&`bVxL-Ww0lFRVn$To&eZCSyx>^#{@~ zEhw$0t$;*b;}CY$6F!7+^@)Fh7WUw7nB2~8@`>D2ZzO`g`X&c(s!V!JBON=Bw~E?$ rnorb##=zB#4xvy#G_Qx{=15(!aHl*+5CI>Ep`{xe%)r0`t!)PXBjE6vyvQl5G+@6bhbA=%S(0QaXiDnwD%{LKiRMsyoYaB%Qb?mAE)lJar1R zp&44*Kq(Y5b?V%Yp*2&n^94GzeJ8s~AUU|ldw2K#zkBaabvkPV$Mo!HbU_KZFu~2i zFnEF!9-t9K(1fIPK&kOr!cuEsp>GL$Mh3QU_K4f~g(rzitL7L~77r)r-rp&qQ0Y?+ z^7%n1^CDJRh`6g^Fc!`ooX|xxC7($_�h+&Bzfwq{2dRyL@C$-Bw$Cob?vpPv>9H zFHcVAZ{GD5?~mu7&U=?9Kjv@0pmz&rqtKqgZK4r$O2%YtEEDVlixF5@Q+i}p>>)$6 z)vp|>wbCxD>`)0=HB$+|Bk4NTTD=n#lU!ELE1r~6H&9lax~g?3cu_)Nl3b<@xN2z$ zxynF(BYGVde((A(Kx*)#I2AmaNR>tYGYR{J%KdP}lSF2be8}_IAISqA7DHeD?ee58&uyzr;puqh6#uLW%D*y<$BlU ldN1)G1=n#6uaj?3O;dG^Zf&JXl!@Fn9}dj3v`L$_**|Ez)(Zdt literal 0 HcmV?d00001 diff --git a/hexactf/extensions/db/__pycache__/repository.cpython-310.pyc b/hexactf/extensions/db/__pycache__/repository.cpython-310.pyc index b8e8632a4f9e279df2fd77aafe9224ff3fdd5b32..fdc8e49d54e4718e787636ae9b4cf0893dc4f0cd 100644 GIT binary patch delta 682 zcmXAk&1(}u7{=$Fo$U8+HmTIKRIOOsMs2n6lVHJv#glkQMA%D_Oo*wwjcnGIhK<#< zc<|#;=2(RG=tYpllb}LxUc7h;h*$j+>_L2Ione0SJkLAtF!OWvL)qy#j!AG#9v-xQ z?>NUc6EvJ~2#EiG2OrSWfQE{%fXtMDg*iW$X%?uV=4+YG1$t=sMyA!k3@zWvv=-Q* z<2#}2yFhLcu5)9XaAS-5d2}YX&{^m_4z>6De*mm>m(D2QrU$ui0CPfUk2VMRuHJM^ z_JG4aaQaY*yqK;ido0dzWeYPP2|=$WsQXH$wM5BsnkdV9mKgg4$UVYYjW80M8#jRT z8IYcvRV`VKZJtZmE_p+_`Wg~8pj_)4>>kvvOgm1)Wx_ kz1oUq(2IEF9aOR9sHh&vkB?@j_3WSWuQqt#QnFcuf2cc_ivR!s delta 465 zcmXwzJ4*vW6ovOrHv68qi4qZ`AS5yIk@x~r*b7#omD5Q^lEAK!WEB)4h$5zxYiF5G zL6OBDprDO~y^XbS)5o@LQSi3r>Bb`7Pu5j=aCU4?_ zPZ$BIV_wWxnHp);i~AbWA|3a1-(W_hHP7@dW<^@}Y~NvyPZXmZPZVw*6P6Ip z;oS-SILP8H846cv1)v^w6G!qX z+igRD9d{2(`0I|Qi*kBWVp`x$<&A34n!$-=225N|j=>-{l3P#?Uz0h2GXA6{p^Ibv zmBOqn%}LD5rqd2KgGSZg3hGVXD#@HkEa1*SCfiyR`b?a*CSWK8G7x1&_f7YjwOV7l YhR^*su!y?@`E)88a^QcvkRYkfA2Nw%yZ`_I diff --git a/hexactf/extensions/db/repository.py b/hexactf/extensions/db/repository.py index 2fa9bb2..f2ada3f 100644 --- a/hexactf/extensions/db/repository.py +++ b/hexactf/extensions/db/repository.py @@ -1,4 +1,5 @@ import logging +import os from typing import Optional from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.sql import text @@ -69,7 +70,8 @@ def update_status(self, challenge: UserChallenges, new_status: str) -> bool: bool: 업데이트 성공 여부 """ try: - db.session.execute(text("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED")) + if os.getenv("TEST_MODE") != "true": + db.session.execute(text("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED")) fresh_challenge = self.session.merge(challenge) self.session.refresh(fresh_challenge) fresh_challenge.status = new_status diff --git a/tests/units/exceptions/__init__.py b/tests/units/exceptions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/units/exceptions/conftest.py b/tests/units/exceptions/conftest.py new file mode 100644 index 0000000..c4ce273 --- /dev/null +++ b/tests/units/exceptions/conftest.py @@ -0,0 +1,26 @@ +import os +from flask import Flask +import pytest + +from hexactf.exceptions.api_exceptions import InternalServerError, InvalidRequest +from hexactf.exceptions.handlers import register_error_handler +from hexactf.factory import create_app + +def create_app(): + app = Flask(__name__) + register_error_handler(app) + + @app.route('/invalid') + def trigger_invalid_request(): + raise InvalidRequest("Missing required field") + + @app.route('/internal') + def trigger_internal_error(): + raise InternalServerError("Unexpected DB failure") + + return app + +@pytest.fixture +def client(): + app = create_app() + return app.test_client() diff --git a/tests/units/exceptions/test_handlers.py b/tests/units/exceptions/test_handlers.py new file mode 100644 index 0000000..d8c0574 --- /dev/null +++ b/tests/units/exceptions/test_handlers.py @@ -0,0 +1,24 @@ +from hexactf.exceptions.error_types import ApiErrorTypes + + +def test_invalid_request_handler(client): + response = client.get('/invalid') + assert response.status_code == 400 + json_data = response.get_json() + assert json_data == { + 'error': { + 'code': ApiErrorTypes.INVALID_REQUEST, + 'message': "Invalid request format", + } + } + +def test_internal_server_error_handler(client): + response = client.get('/internal') + assert response.status_code == 500 + json_data = response.get_json() + assert json_data == { + 'error': { + 'code': ApiErrorTypes.INTERNAL_SERVER_ERROR, + 'message': "An unexpected error occurred", + } + } \ No newline at end of file From 1d0d688877627ea46765b819cab389d995be6d82 Mon Sep 17 00:00:00 2001 From: S0okJu Date: Mon, 10 Mar 2025 22:02:25 +0900 Subject: [PATCH 4/8] [Test] make kafka consumer test code --- tests/units/kafka/__init__.py | 0 tests/units/kafka/conftest.py | 31 ++++++++++++++++++++++ tests/units/kafka/test_consumer.py | 42 ++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 tests/units/kafka/__init__.py create mode 100644 tests/units/kafka/conftest.py create mode 100644 tests/units/kafka/test_consumer.py diff --git a/tests/units/kafka/__init__.py b/tests/units/kafka/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/units/kafka/conftest.py b/tests/units/kafka/conftest.py new file mode 100644 index 0000000..d8d4475 --- /dev/null +++ b/tests/units/kafka/conftest.py @@ -0,0 +1,31 @@ +import pytest +import json +from unittest.mock import MagicMock, patch +from hexactf.exceptions.kafka_exceptions import QueueProcessingError +from kafka import KafkaConsumer +from hexactf.extensions.kafka.consumer import KafkaEventConsumer # Ensure it's patched correctly + +@pytest.fixture +def sample_json(): + """Fixture for a sample status message JSON""" + return { + "user": "test_user", + "problemId": "1234", + "newStatus": "solved", + "timestamp": "2025-03-10T12:00:00" + } + +@pytest.fixture +def kafka_mock(): + """Mock KafkaConsumer globally to prevent real connection attempts""" + with patch("hexactf.extensions.kafka.consumer.KafkaConsumer") as mock_kafka_consumer: + mock_kafka_consumer.return_value.__iter__.return_value = [] # No messages by default + yield mock_kafka_consumer # Provide mock instance + +@pytest.fixture +def kafka_event_consumer(kafka_mock): + """Fixture for KafkaEventConsumer with mocked KafkaConsumer""" + mock_config = MagicMock() + mock_config.topic = "test_topic" + mock_config.consumer_config = {"bootstrap_servers": "localhost:9092"} + return KafkaEventConsumer(mock_config) diff --git a/tests/units/kafka/test_consumer.py b/tests/units/kafka/test_consumer.py new file mode 100644 index 0000000..deb9b14 --- /dev/null +++ b/tests/units/kafka/test_consumer.py @@ -0,0 +1,42 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from hexactf.exceptions.kafka_exceptions import QueueProcessingError +from hexactf.extensions.kafka.consumer import KafkaEventConsumer + + +def test_consumer_initialization(kafka_event_consumer, kafka_mock): + """Test Kafka consumer initialization""" + assert kafka_event_consumer.consumer is not None + kafka_mock.assert_called_once() + +def test_consume_valid_message(kafka_event_consumer, kafka_mock, sample_json): + """Test consuming valid Kafka messages""" + mock_callback = MagicMock() + + mock_message = MagicMock() + mock_message.value = sample_json + + kafka_mock.return_value.__iter__.return_value = [mock_message] + kafka_event_consumer.consume_events(mock_callback) + mock_callback.assert_called_once() + + status_msg = mock_callback.call_args[0][0] + assert status_msg.user == "test_user" + assert status_msg.problemId == "1234" + + +def test_consumer_creation_failure(): + """Test handling when Kafka consumer fails to initialize""" + with patch("hexactf.extensions.kafka.consumer.KafkaConsumer", side_effect=Exception("Failed to process request")): + with pytest.raises(QueueProcessingError, match="Failed to process request"): + consumer = KafkaEventConsumer(MagicMock()) + _ = consumer.consumer + +def test_close_consumer(kafka_event_consumer, kafka_mock): + """Test consumer closing""" + _ = kafka_event_consumer.consumer # Ensure the consumer is initialized + kafka_event_consumer.close() + kafka_mock.return_value.close.assert_called_once() # Check that close was called + assert kafka_event_consumer._consumer is None From 7d1faed1f2dc47cc6584a609aed2c9b03c0388b1 Mon Sep 17 00:00:00 2001 From: S0okJu Date: Mon, 10 Mar 2025 22:05:06 +0900 Subject: [PATCH 5/8] [Test] make kafka consumer test code --- tests/units/kafka/conftest.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/units/kafka/conftest.py b/tests/units/kafka/conftest.py index d8d4475..4a9c423 100644 --- a/tests/units/kafka/conftest.py +++ b/tests/units/kafka/conftest.py @@ -20,7 +20,7 @@ def kafka_mock(): """Mock KafkaConsumer globally to prevent real connection attempts""" with patch("hexactf.extensions.kafka.consumer.KafkaConsumer") as mock_kafka_consumer: mock_kafka_consumer.return_value.__iter__.return_value = [] # No messages by default - yield mock_kafka_consumer # Provide mock instance + yield mock_kafka_consumer @pytest.fixture def kafka_event_consumer(kafka_mock): @@ -29,3 +29,10 @@ def kafka_event_consumer(kafka_mock): mock_config.topic = "test_topic" mock_config.consumer_config = {"bootstrap_servers": "localhost:9092"} return KafkaEventConsumer(mock_config) + + +@pytest.fixture +def repo_mock(): + """Fixture for mocking the UserChallengesRepository""" + with patch("hexactf.extensions.db.UserChallengesRepository") as mock_repo: + yield mock_repo.return_value From 4f716206c52edcd4f93bd46881112cebfbe0d997 Mon Sep 17 00:00:00 2001 From: S0okJu Date: Wed, 12 Mar 2025 21:46:45 +0900 Subject: [PATCH 6/8] Add readme --- README.md | 24 +++++++++++++++++++ hexactf/api/challenge_api.py | 44 ----------------------------------- imgs/image.png | Bin 0 -> 17493 bytes requirements.txt | 3 ++- 4 files changed, 26 insertions(+), 45 deletions(-) create mode 100644 imgs/image.png diff --git a/README.md b/README.md index 7ad6ef9..fdb0c99 100644 --- a/README.md +++ b/README.md @@ -1 +1,25 @@ # Challenge API + +클라이언트로부터 커스텀 리소스인 Challenge를 제어하기 위해 사용되는 Control API + +> [!NOTE] +> Challenge에 대한 정보는 [Challenge Operator](https://github.com/HexaCTF/challenge-operator)를 참고해주세요. + +## 아키텍쳐 + +[Challenge Operator](https://github.com/HexaCTF/challenge-operator)에서 상태가 변경되면 큐에 상태 메시지를 전송합니다. Challenge API는 큐에 있는 상태 메세지를 얻어 데이터베이스에 값을 저장합니다. + +![시스템 구성도](./imgs/image.png) + +> [!TIP] +> Challenge API 제작에 관한 내용은 [블로그](https://s0okju.github.io/p/hexactf-10/)에 자세히 나와 있습니다. + +--- + +## API + +| Endpoint | Method | Description | +| :------------------------: | :----: | :-----------------: | +| /v1/user-challenges | POST | Challenge 생성 | +| /v1/user-challenges/delete | POST | Challenge 삭제 | +| /v1/user-challenges/status | GET | Challenge 상태 조회 | diff --git a/hexactf/api/challenge_api.py b/hexactf/api/challenge_api.py index eb74589..efca40c 100644 --- a/hexactf/api/challenge_api.py +++ b/hexactf/api/challenge_api.py @@ -33,50 +33,6 @@ def create_challenge(): return jsonify({'data' : {'port': endpoint}}), 200 -def modify_k8s_yaml(yaml_content, username, challenge_id): - """Modify the Kubernetes YAML content to include unique identifiers.""" - resources = yaml.safe_load(yaml_content) - - if not isinstance(resources, list): - resources = [resources] - - modified_resources = [] - - for resource in resources: - if 'metadata' in resource: - # Modify name to include username and challenge_id - if 'name' in resource['metadata']: - resource['metadata']['name'] = f"{resource['metadata']['name']}-{username}-{challenge_id}" - - # Add labels - if 'labels' not in resource['metadata']: - resource['metadata']['labels'] = {} - resource['metadata']['labels'].update({ - 'username': username, - 'challenge_id': challenge_id - }) - - modified_resources.append(resource) - - return yaml.safe_dump_all(modified_resources) - -@challenge_bp.route('/generate', methods=['POST']) -def generate_yaml(): - """API endpoint to modify and return Kubernetes YAML with user-specific values.""" - try: - data = request.json - username = data.get('username') - challenge_id = data.get('challenge_id') - yaml_file = data.get('yaml_file') - - if not all([username, challenge_id, yaml_file]): - return jsonify({'error': 'Missing required fields'}), 400 - - modified_yaml = modify_k8s_yaml(yaml_file, username, challenge_id) - - return jsonify({'modified_yaml': modified_yaml}) - except Exception as e: - return jsonify({'error': str(e)}), 500 @challenge_bp.route('/delete', methods=['POST']) def delete_userchallenges(): diff --git a/imgs/image.png b/imgs/image.png new file mode 100644 index 0000000000000000000000000000000000000000..ceacd049d641b99c01fefd5b56457a7cad3d40ed GIT binary patch literal 17493 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 RT Date: Wed, 12 Mar 2025 21:47:30 +0900 Subject: [PATCH 7/8] Delete --- in README --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index fdb0c99..886209a 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,6 @@ > [!TIP] > Challenge API 제작에 관한 내용은 [블로그](https://s0okju.github.io/p/hexactf-10/)에 자세히 나와 있습니다. ---- - ## API | Endpoint | Method | Description | From 5349e8f23be9193c7675260c49569fb44996cc71 Mon Sep 17 00:00:00 2001 From: S0okJu Date: Wed, 12 Mar 2025 21:53:34 +0900 Subject: [PATCH 8/8] Fix README --- README.md | 13 +++--- md/user-challenge-delete.md | 82 ++++++++++++++++++++++++++++++++++++ md/user-challenge-status.md | 83 +++++++++++++++++++++++++++++++++++++ md/user-challenge.md | 83 +++++++++++++++++++++++++++++++++++++ 4 files changed, 255 insertions(+), 6 deletions(-) create mode 100644 md/user-challenge-delete.md create mode 100644 md/user-challenge-status.md create mode 100644 md/user-challenge.md diff --git a/README.md b/README.md index 886209a..ff77b71 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Challenge API -클라이언트로부터 커스텀 리소스인 Challenge를 제어하기 위해 사용되는 Control API +HexaCTF의 Container Control 프로젝트의 일부입니다. +클라이언트로부터 커스텀 리소스인 Challenge를 제어하기 위해 사용되는 Control API입니다. > [!NOTE] > Challenge에 대한 정보는 [Challenge Operator](https://github.com/HexaCTF/challenge-operator)를 참고해주세요. @@ -16,8 +17,8 @@ ## API -| Endpoint | Method | Description | -| :------------------------: | :----: | :-----------------: | -| /v1/user-challenges | POST | Challenge 생성 | -| /v1/user-challenges/delete | POST | Challenge 삭제 | -| /v1/user-challenges/status | GET | Challenge 상태 조회 | +| Endpoint | Method | Description | +| :---------------------------------------------------------: | :----: | :-----------------: | +| [/v1/user-challenges](./md/user-challenge.md) | POST | Challenge 생성 | +| [/v1/user-challenges/delete](./md/user-challenge-delete.md) | POST | Challenge 삭제 | +| [/v1/user-challenges/status](./md/user-challenge-status.md) | GET | Challenge 상태 조회 | diff --git a/md/user-challenge-delete.md b/md/user-challenge-delete.md new file mode 100644 index 0000000..11cd32b --- /dev/null +++ b/md/user-challenge-delete.md @@ -0,0 +1,82 @@ +## 기본 정보 + +- **API 경로**: /v1/user-challenges/delete +- **Method**: POST +- **Description**: 사용자의 특정 챌린지 컨테이너를 삭제합니다. + +## Request + +### Path Parameters + +| 파라미터명 | 타입 | 필수 여부 | 설명 | +| ------------ | ------ | --------- | ------------------------------ | +| challenge_id | int | Yes | 삭제할 Challenge의 고유 식별자 | +| username | string | Yes | 사용자 닉네임 | + +### Request Header + +| 헤더명 | 필수 여부 | 설명 | +| ------------ | --------- | ---------------- | +| Content-Type | Yes | application/json | + +## Response + +### Response Body + +| 필드 | 타입 | 필수 여부 | 설명 | +| ------- | ------- | --------- | -------------- | +| success | boolean | Yes | 삭제 성공 여부 | +| message | string | Yes | 결과 메시지 | + +### 성공 응답 예시 (200 OK) + +```json +{ + "message": "챌린지가 성공적으로 삭제되었습니다." +} +``` + +### 실패 응답 예시 + +### 403 Forbidden + +```json +{ + "error": { + "code": "FORBIDDEN", + "message": "해당 챌린지를 삭제할 권한이 없습니다." + } +} +``` + +### 404 Not Found + +```json +{ + "error": { + "code": "CHALLENGE_NOT_FOUND", + "message": "요청한 챌린지를 찾을 수 없습니다." + } +} +``` + +### 500 Internal Server Error + +```json +{ + "error": { + "code": "INTERNAL_SERVER_ERROR", + "message": "챌린지 삭제 중 오류가 발생했습니다." + } +} +``` + +## 비고 + +- 삭제 요청 시 실행 중인 컨테이너도 함께 종료됩니다. +- 삭제된 챌린지는 복구할 수 없습니다. +- 사용자는 자신이 생성한 챌린지만 삭제할 수 있습니다. + +## 제한사항 + +- 삭제 요청은 챌린지 소유자만 가능 diff --git a/md/user-challenge-status.md b/md/user-challenge-status.md new file mode 100644 index 0000000..8195e80 --- /dev/null +++ b/md/user-challenge-status.md @@ -0,0 +1,83 @@ +## 기본 정보 + +- **API 경로**: /v1/user-challenges +- **Method**: POST +- **Description**: 사용자별 챌린지 컨테이너 정보를 조회합니다. + +## Request + +### Path Parameters + +| 파라미터명 | 타입 | 필수 여부 | 설명 | +| ------------ | ------ | --------- | ------------------------------ | +| challenge_id | int | Yes | 생성할 Challenge의 고유 식별자 | +| username | string | Yes | 사용자 아이디 | + +### Request Body + +```json +{ + "challenge_id": 1, + "username": "test" +} +``` + +### Request Header + +| 헤더명 | 필수 여부 | 설명 | +| ------------ | --------- | ---------------- | +| Content-Type | Yes | application/json | + +## Response + +### Response Body + +| 필드 | 타입 | 필수 여부 | 설명 | +| ----------- | ------ | --------- | ------------------------------------------------ | +| data.status | string | Yes | 생성된 컨테이너의 상태 정보 | +| data.port | int | No | 컨테이너 포트 번호, Running 상태일 경우에만 반환 | + +컨테이너 상태값 + +| 상태 | 설명 | +| ------- | ------ | +| None | 생성중 | +| Running | 실행중 | +| Deleted | 삭제됨 | +| Error | 에러 | + +### 성공 응답 예시 (200 OK) + +```json +{ + "data": { + "status": "Running" + "port" : 12345 + } +} + +``` + +### 실패 응답 예시 + +### 404 Not Found + +```json +{ + "error": { + "type": "CHALLENGE_NOT_FOUND", + "message": "Challenge not found" + } +} +``` + +### 500 Internal Server Error + +```json +{ + "error": { + "type": "INTERNAL_SERVER_ERROR", + "message": "컨테이너 생성 중 오류가 발생했습니다." + } +} +``` diff --git a/md/user-challenge.md b/md/user-challenge.md new file mode 100644 index 0000000..8195e80 --- /dev/null +++ b/md/user-challenge.md @@ -0,0 +1,83 @@ +## 기본 정보 + +- **API 경로**: /v1/user-challenges +- **Method**: POST +- **Description**: 사용자별 챌린지 컨테이너 정보를 조회합니다. + +## Request + +### Path Parameters + +| 파라미터명 | 타입 | 필수 여부 | 설명 | +| ------------ | ------ | --------- | ------------------------------ | +| challenge_id | int | Yes | 생성할 Challenge의 고유 식별자 | +| username | string | Yes | 사용자 아이디 | + +### Request Body + +```json +{ + "challenge_id": 1, + "username": "test" +} +``` + +### Request Header + +| 헤더명 | 필수 여부 | 설명 | +| ------------ | --------- | ---------------- | +| Content-Type | Yes | application/json | + +## Response + +### Response Body + +| 필드 | 타입 | 필수 여부 | 설명 | +| ----------- | ------ | --------- | ------------------------------------------------ | +| data.status | string | Yes | 생성된 컨테이너의 상태 정보 | +| data.port | int | No | 컨테이너 포트 번호, Running 상태일 경우에만 반환 | + +컨테이너 상태값 + +| 상태 | 설명 | +| ------- | ------ | +| None | 생성중 | +| Running | 실행중 | +| Deleted | 삭제됨 | +| Error | 에러 | + +### 성공 응답 예시 (200 OK) + +```json +{ + "data": { + "status": "Running" + "port" : 12345 + } +} + +``` + +### 실패 응답 예시 + +### 404 Not Found + +```json +{ + "error": { + "type": "CHALLENGE_NOT_FOUND", + "message": "Challenge not found" + } +} +``` + +### 500 Internal Server Error + +```json +{ + "error": { + "type": "INTERNAL_SERVER_ERROR", + "message": "컨테이너 생성 중 오류가 발생했습니다." + } +} +```