From d92305e5043cc7e7d8904a2a32e3f21f245e4f16 Mon Sep 17 00:00:00 2001 From: Martin Dimitrov Date: Tue, 27 Feb 2024 22:48:39 -0800 Subject: [PATCH] add rotating keys --- bun.lockb | Bin 202567 -> 205168 bytes package.json | 4 +- .../server/src/middlewares/DoorAuthModes.ts | 36 ++++++++++++++++-- packages/server/src/server.ts | 5 +++ packages/server/src/types/Environment.ts | 1 + packages/server/src/types/IAuthMode.ts | 1 + packages/server/src/types/RedisKeys.ts | 4 ++ packages/server/src/util/EnvConfigUtil.ts | 12 ++++++ 8 files changed, 59 insertions(+), 4 deletions(-) diff --git a/bun.lockb b/bun.lockb index 2c8eb1f8924f5adc1b1fe899b2671ee83f7e223d..45ed243a58ca5d826a182e3de8e20d302bf2f976 100755 GIT binary patch delta 40332 zcmeEvd3;S*+x9s-IpiS3oRAQ642dMjD2Esm31Uplk_bX3iI9XyVybysSYjSZDT-E$ zsJ5zVZmGFw(W+Ui6H@fMu07!CtIzYk@9+D4-#^_ySMGIR>t55n#(mB{_Mvym{Pby= zd0q`a`}N~5Zf4hP{_)ho$>p2$N{H$qqIUOuFuSnEsvSPt>%4o_-Oj|T?EL7OdA%JJ zRWX^8QZmz%Jcq;$hF;2K%KsFyJmhX&A89t3DnaiHNqz(P3Xq*4D?%n@rKKliWSGXp zre}jPT|+6Q!H>>POYlt0FfF&&@=Z=mPCzlH`Or)Ef}ANesM`r&0N1!UtR5HCpwwy=Y6QFqx4k$ z&_P#P_dIZUOcc`Xi5Sx*m5|0Fij8D%_ z8=Y!0wFAcrj;OB5L6A&;4U+8(LOd(d1d8iyW{OFAM)RduliNmVa(0ZyYBs2Wg z&16E^`QJmbXGdpvrlbx@Fc}48tL-qEwz+FFX+0zh8j?74D9SQP=qxZceHc~KZ0L-S zjZaNy(p>1wH<>aIfdM+vS(o0BC_BGtU6TpU$lnTyM&&PttP0r|k*sJKB&Y79dRhVZ zAn5_j-_)cr38u64O{SXAk3q5m`yg4d*pp4=*a+6^^QKz#hmfq7x<-Td_t)&E zLDFG4x=g@~P8*#VzcWA^0BayCB7RALC@N8-b|aY4JE-oQ{cr$c? zArB)%cJF1hixt?}Mr)^ATdhGkknGm6sp&(wT#eH83XrUjI%rLpS$|!_%E4hwXa~sx z&bn$DAJAo7W=33Uw#k&yL5u$YJS%F<{1wnSA&qpCb$tvZJ+wJY>nG=MRD?6T1On`# zwTNKB6CvFp!#Zg-RSluHfX)t1Lp<%fMrawlSCf|WWy zL)XXZ@d-NLP3MClu@U4q&~+!ss?dwOYW&Tv@INy?4T2f(hh)pP>bleoCqvRM6_PWo zpB~>{kN44aZMBXc$-Yfb$TF>j2b^GEK1P!zA=%KUx*VM$DwL{{cfF5BKLpL8F}|CZSCS=v{>52t^2TJ^ZsV@%FEvj1J?8Y zK&{|okhP(Y$neaLO-?dl2=G9kGe^&P2Y zXoJMO$?plC8UFP=^I?)!@%NIoA%$XMlVXP?Y=F*+E`X%#r$VxUD`8&)auy^T+7$Wn zaFc%`&SYu?xf;?Fay%q<()<`*HiGnm{xnvTXCWIv-vLRx9r}=31jz~~q>N4+otTjE zYqU0WuaDL$bXJ#hA~nxsMZ*8=yYz(Ec*L2SjL~Wu07?Evrly~PV@Axb&ehBQ?@`YHcXg?8>EgqJmd8(T(gL2@1&dR^O_$1g&7BV#es70-hDwiE>%7&3Op0rLFR{ zF;E*Ba4Xz>Fe*;PFtIW4jgT` z(&959S&@>uZijffyeuSJTnx`JePTvzdU|ZO>3is`Fe;LsFf1YK9CXgMLy+Y-|MwzL z2E-PXzyds18xz&$X&EjAPfxUg>)F>=^{#3&U-ML%NUeZhBDB2^hd4|ClWFS$E#D`Q z=#BiY4q)ogi)EXNhXso4BV0695UuYGjp-lvI&sPsIJs1FdRBt z_&s!bA`&{MVRFjo;T#J=&?`XiSg6_6gRBj`G-TOo2xKqS5?GgtMo#tfykXA@yFhYs zl+@GzzDRSxZAi5tE42I$L1%vJbvbsW#$SVEPhirhD_{ZQxm@&stOFUBxIE*#8?~p* z`+fcDA~(BGtEt!}M0+F44kD<5%~BWs_Y=8wf|b5jQ3PTZh$d>X-BuCgVYB`O z!iHK}ggGt1TuKyy8;0I*t$HI=k_&2Rvp8X7wGkn8gO!ZZEWxq^L=bCaxr>mOcve5y z;(~e9M(l%8zcMUeSyV<8H-clJHMUvZFlvI3uSx9h5ny%{MU8EiW#Ih8K4fzWp-yUs z)iFO?v)0yr2sKx0f__ovmKDWKY|8nvBFNKbDUJ0GF6r3PTuu~u+LXd_q8Q?OIT7TA zPH+-AUN&ngmKHWe5&LWTTem^;g=R<1at)eR+dAb%PE(r_UtSb7wOLnTmb04ZxH|sI z_3|Rf8~IgG8`QIcDDt*hCFYZ#k*#tY^AT$W;UdO;Db?auz6QZlXBIX5E6-kOiX}n5~U!m`q)usq@U51x<4h zN6fo5L~*dq>VRWaC`_;U;{<{9wKxo<` z!LFQMOXP&utOr4Q!vr2_;BWa2T00JTYhZ2dbOZ;WkYUi+Cl0Eswm@s9YN5XV)^DL{ zU16^8uej9_IZ>_bu_Cq-&sqd4OY4ZDRyOMe5Yb42$pWLWx+18xO*vdwX>lRjx0NaI7K5 zNn?LyaRX7*#%8?%Qk!tBnu~`BYHPD}^I#jF)eg2ULP#GC4g8fu9-_Fd&04>q=0Wr| zmePT`W+y_?0jnB{qINdxMUWhmS_il_(q<1l)xh6602=FoDGHYtLZc-#cW1z9BCQDAjB%8rAX7v z#~5++S3ERrN#kr^r)!vm&HSz9&^h!%33wsEL07P}!*D*d-iXq=;3hNdI=(g@$S{Pjv1!T`Qrcy9135fQGobNJL9DPo=k?x&bW&*^=9iauP(8&Dt81 zT5}rOWW4~5Ljbi3^|!jB%e4W)VKo>U$3O|O-_zf60>QrO#?v4WONkoFL`dD;tlJP` zoiQL9`&({MQ-?wIAd%C{P& ziLqIifn+CxtnY8V2(1k?I1sZW9PR4BQE#1#kT!eFsOeE?T5%kJ_890~H?@J$92(oC z4jN^6sL1JSv#!%gbt$l%r>2g|(kKg|fMBIn8c{sDW^J$qQN%BCrlJWd=w^vVr|ws;o8oH9z)&wLPObF-zuxZ zMRBZ6`6^rl#o3e|okUKY%`y`s(O=~D4Yq!Y5DH-b1^O%22oV%-QwBtc9EfERq9}fX z^%zR$pu>DbZ)=jdK$4k5Ylp4ijZda9YR`u9=!~16hc~lA0gCH>>JtAzBg_o z)a;@V(vlV;q?!F-a8?`kY4)nN6ODo92MrrHj+|Sdg=vn%Lgp8v%^P*ZS@%MtrKXkW zqd5a>nlIw8RQB|@j)ta{z_=~Ym`=02r>E1VxJTa?3wwyabqX{Ndv%ssKZeHc z!3;!Qg8FF-l{z9UlcB|leeHrRKOhvKj+uu2wY+c(RTm3Ee-V^sv+f0{Rc!xY>^u=gp`2a2F{oAq~)FDk5f z4-!Qn*ALR#t8F5eps^6`yjO9sHYMQ;v?)o~)b?7oKx@P0-1-P1W~(iHKCzmHT@q}({qHdS&YW^nP6}` z5Q-5Y>A@E3P_6}twH`)@HOFGoBfx$bny=E`5J~}!iQmRw`FNNp9&1yo4;MjMHVeNP zR1;g?LnuM)OAl6@6Gd^B&6<<=qEeWWTM|W1w$1tnNbWx9vR?j{z9aO7*|HiT8%tBZ z86k3VY?jQCtlzV-!ODq|q8NlnlF2kgJj+5g5u&qj_`o8rBpZW`S^zXG%NS6hS^mnA zWDzvpW^I7lYu(OeaZrjV8gEn9rifyQUsFU-uFVphYBCKJA!)&uLWDXnWc`*Qj-G-iNv}L+IUstPE~ajKEHhtSMV>|U1$?t#3iP`s@;T!jPyLCGhWHXvL71! zkCVh7hgLLs|ZoKcR(+eFecv>#-tel1<4UD{>~;EWeIL=SrVx=4N91 zc&kH}$rOS#<*etm@Jl2Fv z3098fh@vSrtMfRllSqm)ijTi#7_@Yen-gsL1)<&?6qax}KZTsKYrH6)YEz2Gi=a1b z*50|sXyBxsl`D$guqj{UieiY06GYH7%xm-pM?-aP71lSQVJC(b>g8{}2`vy>dG$Er zo~JDf+Iln)8g?1oazC_}>Fo10r)jx#hQ^Ig(-uI(zLW2$c9>G2tp^UmjPj;H<4`s; zPWiY%1kJQrD@-(&LA1ddqifjpFt>+I62&udXFf?Cqz!^CZj-U`scT4Qgy?T|y|gZY z)*PC;3R#XbPCWsYonn;8wIvK1eSyyQ@Ks&-D@V9Q#Buw-B# zEk=k(0d*g>ba+EuM+1VbixKLnRRMRd_R~xzEXKOkU})-x2G$P|Y6)K35FWqKcxGrm z-KaLtgTr6dEX$#FSIciS<3%ITg+pFyD0L;YFryBZAE0$o8}2>xl{A)2Xf`#k_Yl(R z%;C^*mbN}=b$bI^l-7b~{+9EM<3~)ETC#ClNQ-(o-CSXFz?7e&%$eG^1GL^}#|bARR5d=a$7W=&e4 z`CPm6+5@dMOf=0-XnlpA4i0dD!V#wJ)&(!3ICJ(v(-&UcWIu=I`yvzXg)as)_dsaP z5r>h9d-T-{MbT25qAU`{5N|E!2T#hi#Uf{!%@Qto(BbD#^Q0(VX0!eX!iJ={ZN`$~ zxx{1|3=RIl9m{NJ>IaV2eF${{UrLz6ar<6q^k@O@7TzipIVPbKkhl6-W3}ZXXO+zwy<961 zJ=omex*i()0H+BYux>%4V@j$^O2ZY}0)u@U`$iVD2-T7Y`~xdQ@oIbuxl&v1QEE$n z>uhKoo|vI4{VjK(1*yZ?Yt<{Aro^ohL2GTwp;aPhZBSlGlgUKb0i^&lP#RDGM}XJs zWC`#Uftmn%PrY81Opl&Z^J}3+n_eTaGU8%{J%Hs@Ees=By(n3sj+**fX+a#;7xns| zOEwi36Pm@d_o|9AJDu|Ai*BTGQL<{isNi~4vTB%S>P5*m$LO-J%BsDihW?#S|Hta~ zkeW|kA=D`9W*`nI1Ec_~SUNyH1K{;K$%JM3uUBPRFk1lnf43S-34gI8KGcbSC(8@R zT~&qaE{Fdnobp(;lX}jtlZ^jLkAGFtcV~6}RY@0|)A?5=`SUtY=>U9dj70=gpIn9H zbyfYVNhZ9e>y*^5>pCU%?{xXSE^p}a2S{F&Y{)HugXsalbU$(~Fz}O3Jk;gSkZj>^ z0I%0cru!XW-xLF-0SibLRFaYZK{EeR{A!K1rS$~=8_5D4_4JesmeplBT{@A(MahCI z>N2m29`QQK_1axGtfSk#DmhCV>HMpb1$csI#hU8zuaj6>^GwZQzy$t!fAepcqBnL|@B>tJ= z_(zwFAENUKI{)91G##p^8wP2|wRi*qtZ^!1|AVC6Xgz~WU5?Syy(&qK)#I~tnXTJV zGMIxuEME!hhz&UKyq|Xgrtk7K=OK>q}_Bq-Avu?RmuEjgJ;wnUCuS5{0bn} z=m{v9(RxTur|r7o4oFsDCnT@e$tvKF>hb@5skZ-rlTfY4|DwRZq%-FKF}>!L^uU*p z?CUeSya-7bUV`LBN&Sj0zlCJfRs3PP8&q`3{BG;|tCEy(*jI*M;hXM3ql={?R3^e$(|=C8^(ao|3sf)^$n-pXmCl(h6RIaXHBHO!Ti(>!DX8Sb<8q z;j5C%RSlhgRkF;Q;3?f9X;K@1m`^<_x}={P>N+KZjTMwnLoYpolDZEhR|kJcW*nf) zKuC6gP1jpN@}gt`Z6TSygUxRr@fDwHmS@QvqtiV8B4$|ddNc=O!@sBQij>804s1#m**kz&va4OzovqV zk_CLD%j-H%$qL=jbxPXZh9q@Y=P6mvJ)M7`!2cwE)QN|>`~{K=$5TkgKZ9fe{DYuL zQ58)x-cHvk*)j)6rY{dk`-+gXs|3mGrPS7+R|qXZ6+Hoek;99U88}07EArBLN*3U) z>y+#=Kb;TIWe_CO2Sd^`t#rLL?-udT)E0l3QF}e2gRY0^dbln-LGt={lKDi!js-^`IeYfxb zzq|ea=WbseH2>V~YqROqyL^sO>i^vB(-YcVz9G5tT%&@ElKOQjxG1TAr_1kkc|(^! zK=PvGdhyTQ{y%s7|J?2WbGQHU(*XS}_mbdp@&EAd|DU^k_0X+gb^hmWpCj+T-2MM^ zxBt)G{y%s7|J?2W|GL|+^&ju{h0B4_tuvj*{e1Mq^MR{3ja)yXX7_DXm#nI~Vsyc- z)7R!~-1aP}``N?0{av5jz1?U|pB8iaznNBg*wn!dU2dN4yvL(yr=r0D;=}>xyp)^U zYc^{-c;CL+=jV0qUOM};r9(E|e^#OQoT+W9ip6(3L)i8q0X>4>cI#Sa{6W93 z`Xw%Sl3n`!4|jci{9YaHcHnur=eQ@(Kbqi@YO?>)-RaGch2G1L&5XM9anY2xj7!6x zj!Ssh>DJ?E2g{b55IXnp<@FcCd|aMvdr;V^!IHPvP5#1e#qY=Woh@E__RcTkJVHh8 zXU=(J<9avo*jBi>V%QA#ai^Q)M-{&F&A2npZ;pyxvcUWO1uhB6{*xAtD7?_BX}SHa z7B2oUCSh6XdpDMSdA{D1aqjak4ns}MCare-c*VHvpZgZBKALuNP>GJ#tY_Cx1^nbc z>QU95+Z(oO*UT%S;?r?+t&83*`L46eZ%dD7ZSf0!a|Ni@?hh6)9zu|`YgSP^&%*y}dc!})LP93IQczSlmzH<|P>)_e` z;fAT}`gLgcRc}#!nMd@D`RzX*R{bfeW7G~0wjGETw+=dr8wVoHR^jtmv{>@FqgeD= zgt@f11}*H6qiBCH0>46=e=u4+hV~n@aw7EeXtD9IqgelWgt@$U2rcG_qv&-g!dy|T zITS4%jyej<;RthO(d}@w*bVJ{XjO&zNVFJ!%uys9i7>l}9nhSQJBrFjBh1xB?9pg( z7}`N-Zle6LXpwcoQDh#AFxM3Op?Q4aDC!-LFxL{Jjz^0N(9S}uBWj(97Sq0T6jM$_ znCppC&;m|6iWXl)m>Y=Ua|P|YiuQdIVeTcm zeS`Kvdmmb~Fn^2oT|@i6jWG8SJD@pVNBgcunEQ#?t7spzgU|+u^4HM5@6f($5#~W+ zKQxc;(Z1^u=2$W6I@$;AEVOu0>pQgX2HN*sggHT+f)?;Y8PWXv$j+Qe#$_Mv)T!{h z{+mk1b+c5t*z(R-SN^#FQI`MEIErV`GDP?-IO?vW*m^6%oGG3_+YN2N z?FjQ&vFSD(bz6^BY?C7_Ec0Ojv(M>!2n7 z9${V~_CU*eg4R8XFs~AckI=eev<})D;qn-*gO>X^!n{r#fi~?AwC+iSdA-Pfg4R7n z>!7_Q8Wy8<&}J7$m^X=w(3U(y>;8x^Zx+-4Kz+lJcZkAg(TifRls$v?o(O*)Egj5Iw?2s7w&<{hG$z~Ay@B|$tUv7#i1FXe3#8%u%c>;U4FEOY=7QyRoG5~pRj6@)_>5L>Mv&dMhwc9R%T z3dDK2sT7FejvyRMgSaUBlm_8k7Q{Xhm!!1}h{Gh3%Ye8d_mIdc2f~dG{8lD9g79zx zae~A(=~5QN1roVsL3}5VkeF5;gl9PrH)M7>5CIiHTp@8&Hgp1Ui^Oav5Vz$;5=$zA z2q_QZuAE*TL|7#d_etEBffYbJCb6Odh#%!`5*sUn=v)!RLs?i6L`)SB&q(|t!z+Ps zs0w0hB@n;KCnR>07*H9+Be|(Eh~ds49IJqMBKuSU;p_rpABjJtwJM0iB$BIwcqaFd z$f^dy%^Ad>GSL}?M|BVTok)3u5GR?x7LJ3W%;D0Ioe!Bc5{cQDmPI$OLHv< z7ukoRn%qHAU0Q2HxXM@xH@SzRhAdwPqNYrwaF_cjYDt&65VhqfiaPQLMO|5|9z;Ev zO;KN-qG%u+)`#$r1r!bCMT$n!rvXG`Ih~@3yhh?SdwF$jOTsWFJ*ULYKsfC!X*nt*U_3Su9LU}^OP zahOE1Cx{TaheVb)2sbYftz@DX2oE0+CrE@!m!=>tkjQNcqOCkaVwx`qPj3+IWwtkn zfMy`Bkmx8I`hd7aVzv*6aCwo$lI9>nd_hFW>AoPsT7bAuqO%Na2I4V^70p0&mA6T3 z^aIhkIf!nuusMhre-O_|^pN2#KseyPACOyHfaoQkkl0OPfFFoxxycWIh6jRh^as&L z_VEYd90Xz?iGI==0OBx-R* zLSkA72+v>;2{JnvM1T#%6%xZ_!!dip4Pa;_c z+CV%evBCx-Ro*7CF%(4SRv<>n!d4(++JJaQB149^2I0^a#MagzGUXEzyGaZP1u<4` z3I#E|9SFxZAhKnjHXxkagV;x6oV2zDahODMTM)T&4~eV}Al%x4$diffKzMWnae_pF zbZHOb0*T!AASTHpB&LOd@azC$ip=f+A|M>Z6%udAh8;oNA~Cxoi0Seoi6xytgoJ^Z zDW`{l2*aUX-X}3z28M&cCJcXsgP1FCli0}BqH`w@^JQTt5HX!WJR>1wIER2k7Z6)9 z<-WtZpFO zx`J3G6T5=&=nmopi8azC3d98xxlth2$s;7D^#I}74a9nx-3>%QPY_o~yd@iU2XTwU z?Cu~o$%`bG^a2sm1H@)Iy$6V}-XQLi*eU~if_O|~MNbgh;)nQ zhY9(N#CtNlHwcG5Ahz}f@xFXQVmFBa(I9roP0=8R_XXh?17eTt69dAzABcS<_DU;H z8HY(E_W`j_?je!YAB0<95Fg9Lz92jXfH*;7zjWyb;sS}>ejpCWBP6B`1mW2q#6g+e zA4I?)5LZYXk_`udxJ6?201!vyMG{K}g9sT2;+UL15JXrki2Ec?$iP7$9+Oxx2*j82 zHi?aKAUY2QaY_~r1`!hv;u(q4GCUT9!w?W#V?msiPe|-0F(3}adATVL#P9?Vj`1Ka z%0BTRoQHzgN8*yS4gqnPMDh?2SL7ZNS;IiMC4l%=CMJOJ7!KkDiEGkjD2NLra)*NW zP97mKEfIw0Fc3Fn_An3uBS2gsaZ@%N4&oMx*~3BHmKRAZ83`gJ5yV|NJrP7$5{UaG z?#sXtARd!gF#^Po@-~T$$sjt91o2Q7jsy{t0^%8oUu1X^2!~Vpr1Mmf3FZnIb4l4S1AC%_ETFK;ixj1# z&uEC!aymsBd5ywxS74@jomtMCYR(egwd{9Y9b=wVVnMA+(tU#2epgzqIn8WYR~ygU z@HcwvXrlCWlXLRSy)EPF<3Uf-tw_rz`DO>pr3QF*WEX$Vr>JDwM357j>SSq^+%;>m zd9qy%x3=nICix8@@ojoOzxB-=mXtU&dql>hw!6HhnHMN7y*jH8ld6%%e{HxNT)CI! zy3RB^xNPb2G85x}HhciCH0!e0tk&h}EYziGG!m1nsa3GaT##}QQZq-jf$}Q2Dv#sU zpPQ&98~>$V;1KzIu6eP=gxlK6w4bDA>^>iPj7rpbGm&Df5*|{QG%L{=L++)Drzag>G3+W}20jZqNVJF5a5o#Yb@X_u{%MPnng9 zd3>gnG(Vjqc-_}IK6us?5vhQ2DIXI-TP)sm!k=fS*07*F&A-^LKop zf)}4wg|pQEFVAGJm;9?ns*fe9Jpw~Mriy>+v&IEZB9M$^frtI#(XyY5-fw&oS{&eNwO{B(Fbpj=zM$ z&p@;b6=hv30WM5{i{Ge`WB!>WaM|;_B*yb`w7HDMWzo5+2v60y5<2G$jv4VPsdFv} z+v^+$0P|yonGY|k&Q(X)c-*^`&gHp+oTk^XG)Sg#1BL+fhNEs+1L3{^pOGx9bMydz zuEmy@(>Zs9gZ2EJbgmY-HsI(DJ|xWO+JHTuE$3B1C+i@5M^9W)=jwvv*rkW~Br*P( z>H(4gTvc=qtJI6H>YTwb8>~{MmI%`ue1e*s2f)Ysc~#?+!wfeBenEg0sIF(&2w~0+ zR=`b9%>Ly}Ajij-nYIbQr$>3!)HzRt3lU~T-F40j;ibA=El38Nvi-{tqG26KX6Owp zXDqI|I_HD%3Z1J5$)GQ=O6M9tGHo+pwa$6y9G}Tst8)!?t_8R^5mx=*NGJV3uGbA4 z>l_}fGi}njCOQ`Yj=zkcD?N3Ngx7thi*(-YgkeaJ{$&2_t02=hr(y4O#)YmM-)I_Ix*q2S&}m{)+#wPE}D zxGG)Fr@|TE7T}j#ba^l&Thb2rN#{7A$h8Of3@p1L1QP%F^IP>fS$0D!o$H7&pP^+p zgz8)v!Z#3ZLI3k{bNn-f1AI7_-Oyg=Iw5?Lk+?cSvfv2dxXy*?c9GyVBh2pN)8>rE z{%P9E0Io<#rtJcxBfu`~3{G7syFy`~un&z#(W4Ol3rlxgI*#6Wn%$S*e~nH@uv}Z32DjEC)I$egiBBbD71DPE7NF`M?4| z01JUdz+ylGOMpUPDXR=GDb;tOwKw8UP+ZL!c4R7*HQhhAah?2Fd`AKv|$1-~@16%ST(eZRG+J z05156zzAR@kOU+H1Au-&91yQy5cWnO8sP72HUMt{8v*|MOlUr>?E|P!bwlzga6T$t8Q`Pj z6@a;L?0jGmz%BL-U?RY+m0PI=;FilRmRqV7;Fif9vJAi-upHn75qSFS81=6)Rl*nn0*Q=l2( z4%7l_1I4iY1K_uk&w%H^pMVK*Wa;Hu7T#uwn$!mXqQP!-_O!3`)6R8(*heS(a5j{HNeD2?HL6~Sx3b>KVT zBG3)CT-~|2axLZB*$?~xU?4CE7z}g(!hmp~6To9gbHER%3)BPZ10Fy_pgd3kC=HYW zO7gtqfWQ|h;7i~;;2LlhI15YxrUKJ|>A(zN60ii|p`OPS9zl4zSOKgAc!F3BtO0mR zSO@TAupW2|*a+|dzym=mo|jr9zMfg31%4zLgQp8`*TtH4FzFt7{g0lU1O_)`v84y-|f zr9ckC*+4afms1DE0Sk~w08@ZTz+_++?4|&dfjPhs*hK<;0q(zTfR^Cf0NDuNg`AFb z-25Bxyu{)3lS)dHy2pEMhAEq%1cYh_06>vrQYT;F( zQ~_8x3uES$0oIrU*(lHj;o3lbz!UHQ8URgz#y}&Wp{|o-yf@GkU?q%-8I3H5^40(B zgRQdxblI!5rDs|KOh8Wr18j8*fc8c_^$>s_2nE^$ZGm<`M}RwC0x$?*e*FN>vb?VN z!!5Bh5D9bvqJTa?48Yd+1iAy=04`#^fM|e=8hLU9fdN2&APyJ|!~*fa5P)$bfJ9x6 zgjARC5@v0m#so z%8+k^+XQUX<35Kx2z&-?1wI9~0UrW;fxII8*#qnb-UHqRb^z}H+W{Ky0zLqC0`CJK z0sDZDflq+_0Mj!50Kon?^q~m<2s{HW0Z)Mo0K54#9sd;qhk;|jQQ!!03OEUT348%? z5S{>z1LTab!T$`71+f6;bq4qvU_obr^T0XaB0%mT@Dp&89nZqP0R{kdfy)3Za|K{! zegIg|4d5#9EkK>N*Yq&;>pI8q_rQ0$ZrIS?NOvD$dgTGiSMM8cL*4>t#K^nAJzxy* z3-APZ4Ezer10Dg>f!~4OfIdJBzygYa?!X^FG(cAyCsXFf4UrsAQ+b$!>i@C9BNdNU zJW-VbtN@KUEy;76=K+Q11M)nMQ5xW``91I*uoYMjya}uZRso9vZuc{R`2ct8RIX!H z5NH8Z1}XvUAD#nw=4PBL;0*ANY@uEuIm%h7+UQTJ)j{(H%?0oUd;o8tDc}Wo0!`#1 zXQfphSB^KJ^1S5-F!yMnH!ua53^2~%dLhg^0iMl*fIxtE2b4UOP!ipMt^n^Ac-O#k z+ClQv5(=~i+VZp-i9mZG90&tC0387ye2UxH9tNZV$v_ei3ncKRj9@~eQYaXW*|HY zV5NE7$^ym$V*s9hG69YV+8GsKK6(7}cVGcif$6|BU@=Uh7chqIY=r0NI>YQL zO4`vz01E)F$J80O2w2GeXW=V><-ig^0vv>f%a$R$6et8(A%kal1;F%%bnL020UXXVZTSAcH- zR)Sulo0*1Pavde z(LV9vr$8kE3t$h}0SaISxa07S;W_M|0gr&+f#34*=NI515D5GPXf@Nm0ArXH{1xD| z5r7d-ehsit=cqG}V&DnzSl7ut1;{h}hb|c&L6Qmo)JbN}nlqANwuWJT)- zXJ7OfKl|cmU~IJ!zob4CT($|aOHE}#p7E+q<7GCAd7zJ%x0jE0*sh3m{Ljd3gZ3Vi zX=;qGm#^mSQiw5Lo5TFIYXd|D#2D|%L7L{s&09OVF^%zN9mF*A^6|=Rt{wNvBT9e! zPB%S~kGFc>drcx#p)Z=pG{%d0)XJ#^apkUr7~|DFYC+y!&9GAGZz-*^yZhGKn=jU9 zj4bP}lyH3y4PZHU8V|4OGIAN7GldhHv#I+O>FuugDn}JL#9fJW<%h?Pu$Ef}K1`Im%ADHv~W4PS&UmeYTx+^-z3WmmuS^$hh>FVR>i2ecqTE z`*`^y;|+FlH7s4X!=el( z3--@xJK3@hYIV&{&ZvV@=iAGV>Yx_O?d1jP|5{sTX{)PDe3iUh7m4+%%Qp3t=6b!I zmF;$NK|Lj3(Ms@D7MGM^^^s%0QgU7rN_eZ3%pa~)wxAN*OUdo^;X~t1!R@o#EZw`m zy`$P6fvjft($Z->?VCqbGj;3^FD=uCAaC8=0vEgzG>S1^ zBs}idbAfrA(_-jRUzTF}x{Tb};J>z7Z_`jk^QW_1?x9pu)P8lACq0xdh6h}YR|p^3 zW-A)fv!y$;#(=^w(7MxECN+cujlRB6PL^t<_(mCT9Dd?h`~AwTSO27#qn)%qHQwpm z6c30EJQn*a`W8Lw)vWo8w-BdWM*eWC^}NR{%8%pSbg{ggg-W2Rq_Q9GRE6QVPUau%q8Y{lGC$Qi|FkV@l zwDQWb%qn{l6mtp8KBnymX@zVz-)&!`mP;0^qY-Nhj*XS%H;pk|Mpl+TwS^p0S>`lB zJ@YEdcOYGj_Zj#3_?Wdh>_c zbi}+h8SlI6()81{E=LbkLLSZGFO=TYMIQG=X^&hMdm*b1)nqj%Sioh!|gXTXWL~uChi`IKg<~>71nbo3fuw zKdw3fizyB-t}>}9Ce%$=`JgG>7vv^edP5I$lOK7bes~P%f;aehH(B3D@%1xalH5Ds zLg%&zmVbntnrXE#UZec^e$%tbcAaW+=wPWt8D(q8IX-BC@#5vKTThL>f8M19(qe|N z7T)ga=VSRUA*B?*lG&vY&5&o)x^g^pSL4mhgTv!X*Ph<%Yvf5UV4iHPE4RbK z&v-ZUE4u;X3Zsg9OsR(Qi{_}Qb3>`LK))?+D5F{+&lm4> zUUBW~$tAyQhCDgtv6fwFBqz5(&l+!Y4*BJi9Y>v7;71h${m~{^81HwskMZq!qQu(= z5rbu(F>RX2d(7Qy z++8jAo<6XXF=z~Ow|dGHrq$mUo&UzHkWNl564Z?bE;E@-?w;Dh@#I2a-@^3+wJpf2 z1!6os<%dYCJZ&O>`gd}qm$doAZk!h$tA^indspMt&Eb>B{2n#&qc6}R{V4N!Q~8O% zGE}kmmL35}QPx}b4}cGhcSV1HrQ4yz`)&h~Liezjx10wHzqaUOZmDGkE_eT-NpH*$ zWBeO0sD74Y-kw&w9mf+Ec#esF-trri;yO%E8#3(VknwMfsLT2Us9nC(TQ&(q`S!5j zI6va|=Nhje37lQJi@)=cgJI$K06wh>i!G@>JCh#7uY`r(OU8?_-D+Q}^NF=KM;zA{ z*hl!vcbI!Fb{j75y}KRj66>_{5G*(bQ0BDe@_L{$)b;P%VDXj5gW%(9E#w49zt=9O zYF@9+LtSXKJR%i4e_1UUUN7e_y98r=JNwJ!c!z|m@s8}CMQ`t)A6)9^m*dOdU!Ft? zSK~F<&bOX>Ma<3o{FN5?%Ni}wqe}wh)RwSZ6QG^CzAPLWGJHny8?e;-erJF@^KUHR z4Or9;`L}so4w7jhsJT`zXIJBG-j}P733*zOwq7xF+94}bhhX`02+A#o`AA{2er*e1tAcQsxPe(b&_Cba&?ekh}vwrauh`_@=LjMs(l{pyFf zjTt*Nuc@^#UMRk{*|{#O)<5xo=})AMd|8SwOI;d@mKmj2Z!K50QCy_Cjnd53qqVk7 z51m=HS5w&(kIkyX7lYKNwTxT9-H>;- zktN!~Wfj_LP7UaE^nvfPYFQ|aQxaRZTU*(a7RGDH2b}*pb$L5;HY|MM5-h@x>dCdV z$Uz?5SgN1P@s7@J-o&0Y#?1ge5_)-Bv9*KcYfdTbAhZpqlv(Z20meJZ$K3v6-W;*x zmg+=xY8W-gR$jI}@-ki`9zJPjL4i1ushAsKF!37WD*Ly`WO?!0@pUaLU3UEn=NZFd zVwju<3%|8tTFqUyO|5yV$Fu=2EsS@b?`}MLVwr93gI>mz2$$b5cjG1JOMlEiurht` zdoL}Fn(ET^?@MXXNe;&G$j^9j`tRXE8{3=+p7k=X|60mGc#D^jyYW)>O?|sfFMnh6 zsF!K~QjZsXe=b6LcSNbiyKk4}x4f9y?8h7MHFhK%`Ao*!ai<-8=j)H7o~hfqwuaV@ zloOcNcxQ6(rb+8tm(N+oQK9Xle?6wL2mQQo#A_B7DUTzq(lb&vNroH}xi}0Ri5E(% z!)KgxaB<=rJ%=(ufVwRdM9MB{P0DksozJwQF_JcGAXY*QK8t`EGxfg#n1cY`l^EaLFjy z;a2je_BhwE8s4V%U1gaFbhz;jcjw|_pYBKf5?)%Qc9pGR;b*+{-Mh`wLAy$nQ(jsa z?}c})+V1wscg7rg8MC#koWr#HyJ{Z;B<^Xsv3ZH)A6{A*@0!0fX=n$Z#b57#8FRC% zyv&*zuczPKA#p%@s|L$oS~x{XC+r@64WhI={6-Z#nwe*Qx8|jV@yh$8!E66KH-As) z%b0Faav;+tAZ-Oq@0*K9|GcKv?|WZbj71FYY4WGpZy7$Qd=P$cYWQqMl-z=}t}FGl z`)|zOSg~ryM=ve5N6D+S_)O2kp}ei}Zu7$9FD=eT$x5BE;u&r7SZ3gnN;Em5YV&wSFN~2P>U{M)0%!-lQJ7auIijk3BP^$6L z^G{>kss$hB83D&|bvN1(BcH?4^{;t5y;wZ{nwRko`!_5LJT3)ax`bL_d1Ph}V`M%` zQ=Z1irCo5halMap?TTGzXkR&^D@JE}U%9d?;>Y)uyQ%-tSKgy6(@(lb!SfCKX`k5S zT&n4GJl>N_FLoLp32XG%mhInD7Ei2HYC|2w@ID+%!{+`nC5p?Vi)Qi4&0I6h;(mYm zA#zu6Q+E!Qeve?u+tRpEbwgU0E*GGdqJDDF0pB|DtaU2p?UUiZuEGMr;&J>p9UCZZ z-B8o}17$j-YmdQN9-C{$tSz4Mr+RMF96ETg{G=P&@&FdxB<_}RcPTxwo-az#Ph~q| z)vw3$`%KEJ(dv(_o-h02Y^*HX9ciD!!V78t_-JxUuS%ESe`(PuPKz1qaKOWJ?jqbU z8~qp)C;KC<>v&k;%eVX*dvY_LuJFUT!?0K#C+E@P1T3&INpk!R z#NBx5(Gzdh4M+;) zsy>vE|7dx+Yg2b0ovr1C&nvx03|-s{j-LVpH)IvGXJ*eoDi3+rFlb>~ipN2G+|&u8{l&K$;kWu68n{LO zH~!Y9qO+n6)++Y+&O;qOhNG+0@M$p%UtMxYeC}HD`HI`+oV9$LA>Wlra!w3dV*JD> zb;+0ICQOTzu)sNxA6?@Uu}@=^$f&8w+HGC41@SriSFFxe%-LYLa4dkt2Uq!bgJWZl z_;#_X{%Y>!=NGU+x7g);@bbx%&%Z~Edfsb}pAX4_eXxKS8}nbwcQV&V(H4xq=~FDP zdN1fLH$3n^m&Pdlrms>%IXhZD$DDV)K3Z$sr+HnjrGC3tyU$fm3b@zz?WZ)V`y(uB zqS2dmE8GZRZxyr2etj~YZUBhsqS5Vfn2(6Wep-U`4>q71l-XSmb3`wkgm?q5TJ6Rqt;~tB?BX zpI-GSkP~7t3J1V~XQAnT+({pPVn8QY@N1SpeA`|ik0YyTOAuU16goo#9TDKk-({J%S71)#(vvNX&$_{Eo4ol_j_p{NvmIm?L{{1AE#d? zIaRHns(pr|-a{XnD3>Fxa&DshAPye;exkfXSv*nJh)14wleAM!ok}0ASatr?J+%UA z%Cd-IfB8k`H1=BcqZeZM;WchTt51?iNc-Z;6Q4~Ly)l9zfRY-5d zlt)a=mi|B2O6raifqL%pX=ZwNl6=6t^yW&pA&S52VVHA6?-5`2T;t^fcj;ci0-%4V z;`&-ws3)nplVwyQGSt3masJQW&&W~<`2IvUSMsOG)(J>%H&t#K3i+4p{;m(;mC94K zv-?lGrp+sKxS^f;)Dz^Gsq!-O{7YU6z5;4A6n!ycsvO9&^fF)HIA41HFSU~23`N!N zydjH+Dv^F0r)h`F)klXp-7m7o9k#YN;Hd;X=J|lR(RaV2Me+wFRdo$AO(5nke z`PJMr>qp%J=U%NpEAw;2!;_?KB)niqy=sQ;I%1|aQcZ!sj#=ArCQo7dAwnNJHF1nD zIZLZ$*k>VCx}B2{`uhoIGy^mK@0(#v zfd7<@vr=-FOc?=x>e*WGHNan7YPeQ6`I}Lodhf3(b^Y(g&5NFOR`yPm8%E+54P)Wg zk=P)%&egWE-W%SZ7g%D^2fFXznQHU21*dWMJ*%SX=Y;Dq7)N@K$k9m{?tl5pwkF(l z2+JSWi{zyr)|wyt(WgksTOp)uGhcp_1Sc4a`d^oSSn`WCl&*b-f^9ug4#sCGepj(k z@EgP`&uh2;c-!eYu=Mu|afW$r6v!$m*y>ipl8fvvPrLQyzC3`BpN$V-wl0tpQc#a4 zu)w;XpHyaV=C-htM`5Aw5U;r*edXK56f7(uk&9E2MUO@5gW~yNJL>-CI(VSZ%Pa~I z!+W`RVh-k3ncW!!c?=kSCcJ);?83B%px1`pV1`T6rqQ!rn_#h44q7EmQ#quOAykWt{DJ@PHHaYx`tC~CR z^St@-rF|Mc!wy<3lhV+X$i;GX8d3~LiYiDkdG+9PA+<|RK#JztQxX#v%QLi`xmY@k z!fA3LEGxqDub+lFnT?pbQuczzNGU?iYtDlbgW8lq62Vc5SUd;esuTWlQe8)ohdpZVyb*Xkk=soS7X&e0y z*?87fzrZxg)4nm~ zzTo{1KRL$uhUIfuu!6ps_}XRcO3m`p=Ag{Ri+A#C4gITAeT>TEnMyU+YcS)|J2L-P-nH)jA{I_BCg^2GG>gDR@=)}JD5xkXmQ+}RrdK! z=yc@eim6v(gHp49c9nO=;3fgG)L6x)c)7|>W0e|t;kXZ=-J%cNwpV#$d%d2zp1tP- zk8a0*x-bYjXMC47(O;y08aOvaj}Nd0u3fviy;rs#zx~SCilObQADIT7vpK2uyebp+ zk6gPLdIjk6jZfAe-r@7bM(7+?Z-4b*<<>!sntk#Sh{_<2-~RT~al^#X)6nUMoB}pa61np7Q&M%)QcIFErWfvFlJSLe^>y_Ta=O655@22{Nv}#tEJ@TYEy~m_$=6LP zO-oBH0&CVy%E?bk%P-0W%NOM5RHkL-t&x`|9PTLJPnY>fZ_ delta 39079 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�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 { return; } - const isAuthorized = authModes.some((mode) => { + const checkAuth = async (mode: IAuthMode): Promise => { switch(mode) { case IAuthMode.FIXED_PIN: return handleFixedPinAuth(req) + case IAuthMode.RANDOM_ROTATING_KEY: return await handleRandomRotatingKeyAuth(req) default: return false; } - }); + } + + const isAuthorized = (await Promise.all(authModes.map((mode) => checkAuth(mode)))).some(b => b); if (!isAuthorized) { res.status(401).json({ msg: 'Unauthorized' }); @@ -32,4 +38,28 @@ export const HandleAuthMode: RequestHandler = async (req, res, next) => { const handleFixedPinAuth = (req: Request): boolean => { const fixedPin = getDoorSettingString(req.params.id, IDoorConfig.FIXED_PIN); return fixedPin !== undefined && req.query['key'] === fixedPin; +} + +const handleRandomRotatingKeyAuth = async (req: Request): Promise => { + const currentPin = await client.get(doorRotatingKey(req.params.id)); + + if (currentPin === req.query['rotatingKey']) { + await replaceDoorRandomKey(req.params.id); + return true; + } + return false; +} + +export const initializeRandomDoorPins = () => { + const doors = getAllDoorNames(); + doors.forEach(replaceDoorRandomKey); +} + +export const replaceDoorRandomKey = async (door: string) => { + const newKey = crypto.randomBytes(20).toString('hex'); + + await client.put(doorRotatingKey(door), newKey); + + const message = `New key for door ${door}! Unlock link: ${Bun.env.BASE_DOMAIN}/api/door/${door}/auth?rotatingKey=${newKey}`; + await fetch(Bun.env.ROTATING_KEY_NTFY, { method: "POST", body: message }); } \ No newline at end of file diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 6885363..e2e709d 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -1,7 +1,11 @@ import express from "express"; import DoorRouter from "./routers/DoorRouter"; +import { getAllDoorNames } from "./util/EnvConfigUtil"; +import { getRedisClient } from "./clients/db/RedisDbProvider"; +import { initializeRandomDoorPins } from "./middlewares/DoorAuthModes"; const Fingerprint = require('express-fingerprint'); +const client = getRedisClient(); const app = express(); @@ -25,4 +29,5 @@ app.use('/api/door', DoorRouter); app.listen(5000, async () => { console.log("listening on port 5000"); + initializeRandomDoorPins(); }); \ No newline at end of file diff --git a/packages/server/src/types/Environment.ts b/packages/server/src/types/Environment.ts index ea330f4..63a8fc4 100644 --- a/packages/server/src/types/Environment.ts +++ b/packages/server/src/types/Environment.ts @@ -5,5 +5,6 @@ declare module "bun" { REDIS_CONNECT_URL: string; // `redis[s]://[[username][:password]@][host][:port][/db-number]` DOOR_OPEN_TIMEOUT: number; DOOR_FIXED_PIN: string; + ROTATING_KEY_NTFY: string; } } \ No newline at end of file diff --git a/packages/server/src/types/IAuthMode.ts b/packages/server/src/types/IAuthMode.ts index 35612a8..4d1ad2a 100644 --- a/packages/server/src/types/IAuthMode.ts +++ b/packages/server/src/types/IAuthMode.ts @@ -1,3 +1,4 @@ export enum IAuthMode { FIXED_PIN = "FIXED_PIN", + RANDOM_ROTATING_KEY = "RANDOM_ROTATING_KEY", } \ No newline at end of file diff --git a/packages/server/src/types/RedisKeys.ts b/packages/server/src/types/RedisKeys.ts index 15be8e7..8da098d 100644 --- a/packages/server/src/types/RedisKeys.ts +++ b/packages/server/src/types/RedisKeys.ts @@ -7,6 +7,10 @@ export function doorStatusKey(id: string) { return concatKeys(RedisKeys.DOORS, id, 'open'); } +export function doorRotatingKey(id: string) { + return concatKeys(RedisKeys.DOORS, id, 'rotatingKey'); +} + export function concatKeys(...keys: String[]) { return keys.join(':'); } \ No newline at end of file diff --git a/packages/server/src/util/EnvConfigUtil.ts b/packages/server/src/util/EnvConfigUtil.ts index 4868151..fc36460 100644 --- a/packages/server/src/util/EnvConfigUtil.ts +++ b/packages/server/src/util/EnvConfigUtil.ts @@ -36,4 +36,16 @@ export const getDoorSettingTimeLock = (door: string): number[] => { // never locked (always -1 < hr < 25) return [25, -1]; +} + +export const getAllDoorNames = (): string[] => { + const names: string[] = []; + + Object.keys(Bun.env).forEach(key => { + if (key.startsWith(IDoorConfig.AUTH_MODES)) { + names.push(key.replace(IDoorConfig.AUTH_MODES + "_", "").toLowerCase()); + } + }); + + return names; } \ No newline at end of file