REp)%uU?E`n#hh?q`lNr=p&b`KbFB
zn`_NX(?38%J2+upwG02Cb{hGoRluxb)<-SS+Tv#|PQz&Wd!iB>Z2jX=iM(%q==8nM
ztYaNkX25pT#MjL`IFI;|#p!q4L?4;UP~$hE7Tjj>A-jIeJcl}x8)n2^{gmbWOVRj<
z3*Au}K0-|x{+BZv)gO;qpoGQMP(NOuLOp~dtbZoP5--52xYpudExwDIztla{`(IHF
ztd99G1@%LsEo!0xs07BMKB3c5<7Z=DoQry9zOeocsPS7Z-iIZL4_p5o3_g75r*a|k
zzWaIn0#+a%kD6e$xfQkGKJzeYf-Jj!&b(pQA6e`@aQ8){HWrV%uZ;CqePBQTYgw$nrpBu@ix?t?F-f)@z9OS|B(9YOp~mmI%=m4
zEpCAc#2ryb(ck)gbDH(f!=hYYg$Z~NHO~c9MX#X}%m2vzlubfiuk6#%g_@|RvoY$6
zm}dQ>Q3<|}x?vvb+4uyN&@R;dS*V47LzVb%)cCM$XB4VG4mDp<>-Q_u$VEp@)C3JN
z1fMrw#AM>`=5$OW&cxux&Cnr7e&RTP>I$?CD6?E`(7vO=xqbu
zHYegd2F}I-Sn*%?n$1J)Y_0i~x!v4@jk$gZwP5srZhRqBT*BhyV9fbHX&0KZq=uUcX$A-F?ZZarH2k^}QxErebI8g6C21
zc^xkhJevAuOVn%B0X0ESR3iP%;i!e*#ez83`oFaP9Tp!$9rZc%HPJ)scx(d_0&aoQ
zsE4K!>db1HEigZE7qdTV;qj=6=a_4(e;+E*lc@PFqt^X15b%S4Ab4mUaUpJCA+s!&
zV_;2-(@+!i#Rwc?{bS52s6;X>USV!F_n1GTzMN-6eAl>T7rY#9f@suxn}|A!wx}CY
z%^s*k-b7Vq6zbWSinVZ_T|bJt?lWt=bsJ0aX{a=9F%F+cm8QS>mR%od@g#Ew
zYNvC|<>oe2LWj+>sQGT%^?RtJiwJW^l|HvdystCsLx&O>kD7R<#fwoBtVMlFze4T&
zta-(}g9YgS2UW4Sa5vu**o3$g>J!}C`lq4RnHSD8po~}1p+qvxEDUCj%J?2C!Q44r
ze}Y*YHBlKl2H<`Pz4E;xP@(gIeeL6HzjtE!60vMbabv?ywfsKee
zVnh5ND$(P1{ginHRjGRxKSqsL_;SMjaU5VcUM+0E>Ox^bArV=SIx&NkS#Veed)eJ
z-S>Z}`z~7iA8LH=-0pf|)DOFIPTy-rLp$tY_C`%K&>Uq>w(D~&UVuvY3yU+&1E|DK
z*!3IM{}}ZUM@0pKKV@rT3H=V(k%lrHidy(x)cg7Y>Z$!2mEalkchuQlH}9E`&B$ms
zPaG=2MAZE?P!DNc>u-y}{|#7o8d~^u)R%3jF5o-XKN*V=`zTYQmkU1@`0fcnX7G
z%sheMe-BJU%`+A?{}eL=b^j9dHDIN6Y{KAcXmJ*5fzzl2Zdsfs#!XlPRk>u;gil%A
z+~Urtg}YhY$Kp54F)_UV!OZB;LK)@?bGv!eyns5A`=~?<=XKUZCD;y?P=9l@ISn;_
z0V>f=H~};B+WX%qpUbozYCu<1WnM;2*bnt>AB9TzOH{%;P(Le2sw513nG{>Q@GoDOCnWdw4_zCK1UWc0afL;Fy
zHSw?JWz^66n-rj)9P;6rtQc*kaW)4CnG~S$#
znqZ^36E*&@c^;MMUl#w38lR`2o2M`;u33=xUpF?U<2mevidUICkiR{6$1L7c$ldre
zYNx+jd=s_c-xh}ycH?5qB4&BBmf6&|#`~
ze5jvxFJo~WjQT`>fZEu4WSsBqq@jhcxehP7SRnX6k*I|!^pC+hxEXasw^0eSDjx7Y
z#LlSx8>obvB|6hk*N34Ji7w$j;l)sYYwC=_pZ^nR=pkH+`Ym=F*2iP`7RDsG-&iJ~
zGGBz+=~~o+yDUCs*KeZc`43xR{*r;roZBg`M!dvVq|LXZ0z3f%vI%
zy#HNk%%!1{+{LG{M0xiM#Xzh_ywA*0!R@>u#?#*c^^elt<_Oe7H5Ij?56ng8D!aY~
z^+Ec!0`I>*Abah?LDUE7M~hFRN__!Uvimp^y^4Y0UqVNr7Mz5-KEs@gx^J;v-(>xn
z*1s3^F#b}J_g`oFkPaD9$z`5^!Kc~cI;iW-QEx$KyFM5-(YvS}FF`#^>rubu?z8?2
z=56y|)I*=kuk3EDY}Pefnw`v-Q3(x1J#^!&|09c++4XO&|0mQ#_^ZVa%+O@Fk$k9x
z{SwwlMwPIh*&G`Yr=m(c3p-*4Dv|5f|DPFF#Z4H6x?T`fu}T<$^-=RTN1c5t@>=rq
zpT;|M^g_Mo7tLZ--GtAXJy8?BiFI)tYN1Tj#7FG1M7q7rI^O0W&;H=Z=q{G+UYGB(xE
z|M@gj;&Z4QFQMLw+o&7ul#uo`#+
zwQ&EME|E7-3ys9!Ylc;cXJBdEffey0>N}F}$w2Ub4c8y{5&ws2xVM)35|*m%zN`aK
z>#V5F`>!w48anj48%PsyMwZI8fA}3MzpGW;G`6g(oH=4Mp2D%n@jvp4RSMpyh@J&s+!zG)phhep{gNhBNJ7$`NRh
z-k@{SK&$j|o#zLhPA}S}ZJ^19K3!r0A&o-QM?RMvXuIKy=Uxm6wA&EYH8l`uv!U~g
zTL(w!G7+y4k
zdwX6{;!;TNUPG*mO|TATU@~sTa+rm>KW`t;D}yz#2UFN|b;?=cNc6!y9^7Bx`<
zYND#B8*5vfhN@5#%!%zVANE1bGX{fk3MSw*RO0JU32j4F^d$Oq7Pn}GV{m`Zi^nL`
zktAUxK8L#A1bIBX)~HerLrpjbb>Di_Q5-`h{43_dbEpd3L>?_KWB^sg>;tI38gX>w
z#st)kQm`97kJ0!gCSoR*!BaR0Lk4=@n>Z9(;WccHwYey#qWUjmDpnut5*Uc8?9jo~
zU!{7RjwJMBX^CGHpSr~;6P!DVH5O=gC
zP#a72(+E7hsM0h+-Ov*CkaR|!Re#iks52aldYC>$Jp)UyAa22&c*6V>wZMH;DMN<3>yfCpB_6dv161O@
zP>BseRs0>~8S#5#Y3NKRq9)2jRb&e))9t844qE>u%uRe1wWCK^1cTpl2^7a<;uO^N
z?x=YOpb{90+K3;s>HVKdLjz}{j$je0w9Cx(s3X{on&^~y5x*e5hMI8lFjtXz7)!ho
zwUO^pZ^Ld>Wq-oFco`!C?>`L<2pR4ch(eVt7PZr&SO6=dj;bl<$4;nHzJ=PrNOQb7
z#hh&}LOrCbQ1g6?{+cxQ($LPHp%Muj;dYb*bwd%<4W&`fLJbUj2T+L(LnZJrR>V)S
z68?y)z#~*;|3g(aWTZRVn32?93l^nAXHyy#r=S+7g^jQ?suD|3rQ3jUcnI|nUbgrS
zs`TOfgp!3YKPID&=ta~s)!yP>qo}`jIE)T0?8g|KfLiENRK{!V`gYWU`%o46)x2ug
zA7UQ*y|>-Maj5w!qK>oyDuL#x^*Z>i(G4|mKZ{4AK0ISl2`)z6_!VlR%~%EZqK@tn
zDj_e!y~epw6{vukw}JJyL{+jsYCivH8u@5UMxEheRB2aZLEMe1#5q(Y?xQ9Qe#cD^
zgW6$H)DfnjDo_`-P%BizJy7!ww)lNy9=|u8MgcA?GPj~8Jb}9Lnt9Kzd!ya>f~WgH2HyX%G&I3v)LG9#C9nv!lT1|RTTy5F6KY52
zQ9HSUTKFDnoHxcToC|9Z7DXlA4s~B=+=e~SuLX*YbqSO)tD_Q0LoL`0H9=R@Gtv{a
zqd}N}qp%3h!zQ=|Z(-;-H_rpq{m)Pdgp7BIM~;2(9Qm&2)hA9uCEORap&_Ub&M4HwI0nn$Ow_Zo9aXs#?@}@iyiA8OyN)`$Tc{oS
z-g6U1p%%zv7RDsvL{!ONLOrxyuqO_|^7uPe#L)NMkyJ&!O=%d49sD%3^RB1``lHVN
zE!0jXU>HtCJxsGu|ASQ8??ry#N?iyw
zL1|PetD(LNjZrsr!w?*XdKTVBB{m23P%go6T!WftBkI1rs1Mvp)V#M*l?<7v`8fYr
z8kOkCkIK9;YT^#4g?gbD9*+_DfyL7?oOm{BXA4nBvKk{W6BF?}48lLmt5}>kOR~Nf
zJ&B*`n2aiE2DZh|@I}0j+DX01?uc5W677K6X@As?Mxk~%(VUM;>}zuyZYKT_`8nl{
z|B(6@rtu#Q?WE8Yw}Vos9hb8>8C9X`sI%;a8b1)V@NkU4_fd&`j7sDS)VTHL_o!!Q
zA8P!QDbznZjj)eg$#bJ7ER5U~e-0Xt=+N1Pf9xjAfnmf2Fd7q3Z$(AB-VpU6X^Bd#m&JoHC-Hlz
z@iS5Le}$TFE2@IKtp9+YhB7&cI^!#-FW+NS3G+^K4@();M9om+JEC^d2Q}eP48{yB
zg=0_~T8gUBSE%tDQI*?m{r)T(I*a?LojkKRe7d_a29-!*R07GU1nQwCZf5O-s0!3Y9aRfdh1;Xnc^&;)cmfR#{17#80qWsdjw;<6)Q#&=
zb^Co#I~UB+pAx-)HqCsGW-qo
zzW;%$z#UWt9-?l1h8mxJuG>K@Rw6Erx~~-~v99J&%t!nIsuBw^Kkh?S@`B$Qw^0=c
z`qbSJgDQDh)DMd~n1EeSr5l44aVl!u_vTU5JeN>se8;#qZP*f?Wpl+Ov1#uz9;Z{`1k6}Ih150Dc`R?xk%~0dsK#iM#DSH1urJ9oI!A)*SOD~n>z`P>0JH1;Uq(X{
ztV1&LcA-joAC*wH&)oYTg{_E-VM!c{dcWtQo{`O{XK06c2vyNjs01#c#$}-@dK3qb3-DN^mHu0`FV=kzHSidPbI^DzyQXz%JB$2T@0U
z2K^d%gNDxJ3F>vsxsW8VB*tT7EPy?*2#!XbWq-l0PaPr%AJ-v%B=o#k09kN2=J7W=~W*F&9oThz1B1+~KgsMqct)cBdG
zgfdYJZ?SmC7k+of`{+Gh
z*%URtGwOrZ8_VE4zg^gi+QAWv&ze`Q|2Ar;)ARFL7~2)Q(e8C4K>wXj@Fc&K8eG
zCG;L@{A|>Q{mW?RY&W5H_Oo3$k9z-ap(YOb(j^#+`VtnixIF55E!2cBqAJk=HD50*
zi*KUFeTJHMHF7k5ZzqkH=s1tMvC>jk!rG__TA(uSh#~ko>g^be={OPfkRC>zc^0bF
zLCajiQK;`gLDbulib|wjK+eAf4L!}BQ476k1ID8=osKH~Qp|>5qY~R_@gCImgBXe@
zP>G&KC2|3^u`CS32NplY{H*UqEO(_Vjw)>x)WGL392;8P!fb;Y*9nzyZ_I|nQO}Uy
z;wh+|&cq~Kf=cWVYTVBl`1}6_8hUE)qCP-jD_kjyp>|pmwUb7uGj54`m|jCA`X;JU
zqfnLn2=&@6LG64OYW!hrf)^|CoBzgWADURARATxdjqYrLKe;Uk^25
zbJW8BhZ^4pwbQpT0jHo6UW27E)A~=FPZDV83s-oxd#bCVp4R551^dl-Ek4$-o=@qi
zh8s~!+`!`a4E60SzQ)xk6_bb?VKE$pT6!9G#?9Cl^L_39mNFL8iT9x9iTH-UJYf+W
zfjhq8ko48Bxz_zuXop*fGf+3AuH#X}W;hgAU^y(3>7Ifns28*kzKHK4=j&38
z`vNUQJ$+lSBA!5PBxF5@q(+7HZYOOqm3TDP#0?hTLcIq`8(h(oQKwxE$6|fFkGn7y
zH*ItkJ&2`q1dNbw3e%KwK;85&JSzf~v*cDT^y2R&T
zBjTOd0mHYsp90;n2JviELPs$_{(<_GJ;l7}kNeI&@8!)JW;*6zKugqP)&+yG2L@wr
zjKtSbFY0hqg+^l(zK?o*XJ98>fST_yYFxzkf$M%RHw{e`kGZilYQmbR64yt)4^2^L
z-3jAx1nNvbLQT8^b^j(*0tYY#f5Re}h1z+kr-G5{|SHK&K~<Zlc!v4|RWx
z{|EPQ#iMqbggU#b7=q8C&MpnLgLb$GU&pl=z0Zx?j(SM6Kd~Yf-S0l#
zEl^)f|6m%LXbT?2{y*|}P^^1^-Q%~|0PkZkrvAh?0BfU4ybv|f9#o<^4!RPTKvnEH
zR3fjSHq;Rd`S^%o3hR6C9CDd`gP{yKk45oMR3$MNt3+BMf
zn1FXs_s1S_^A^S^;u5HbFBx;|{jW_!H#W!SWY$R%ucRDY_8y{kJmMHX<%0MH9!C<_
z{)Io>;Ckev=G8dCKc;a11AL4dexrKyuRZA!?{&&8JREhD<1j=E%%l;CpJF&J#oYKc
zs)Reu-%ypgff^Tm+I^7nVtL{Qn2ZCh|8p!%d;k;i8WzNyXIzEKV&IRoO=)OgUmSxI
zupx$@btPs!tJsPDuHi~onZ?_X2`LFe5w5{H_bEdJ2q
zx#kM%-)#PXTKKqm%DiY^Lv7$DYMn=B*aeqptlt`m*qZ@0Q8z5YlDG2DkFYDg
ze9?V~)|q$AluNFE2uKnrIc#1GT;MMP7Dr!k*Aq|)Rx#_N5^9c`xU-pI
z*XLmv{mU^JzeXjVi6!wnjADK75{)ptsSbQ#248jkxljq_LtQV6#W2<4_NWAUq2_td
z{Lrq?Fc(_?YV%w4>xQj1V3&EsJcGJ^9kqj7I27;M^}g3!Lc>rAjz@i3XJI%lLcMM)
z%^j!>{EDi?i43@p1F_Iw?uN;z3e2&16)Mq9sM78?&!NWMLnWH+Z+CwQ3{=qKG_xft
zQGaLa=wZHL7e<@!S^tNqh38tl!me+y{_ju;9kT0}Q9HkdTIjLqyW#r7F^cuQ7#bQ-
z6!lLei5Ay&1H5#LU&h*8Z)fo*sGZG2J^gDeK7>r{U9k9u>HW|3$D-~_z`%c`QH6#w
zuZt>OYa1{KHPJY8syW}TudsNV`J?s!YVmc8A6Oi5)6JI`^~0-#)caqDhB9hn1NxY6
zp%NNreug=Tw^)1tRpKj{4R2vid}#gQw_F^9+DLKK_(~XwHPNpN4Qc4cR%SPI2V+F1+LCy2Cc@pOmU$A)89XHR|JJeqjenf|En1@>M3yas;z|H0ls3SRU-bDSF
zjl9dRZ`cqu-v!isH_Ut1|1WC(@Oy4tem@QUcrAr`2)o*aVHiU^9+PpF#rrHig<2r;
zzB9%wfVt^UK>dtJLCy0DDzV7jPh#NV
z!(`~BWF8}Ng9+y-2*_^QSCP&@kW@b~
zoJpuJV!HKrMP{ZLmpcKsD$EC
z_a|Zy)-#{Sio^}gq1c&t@ng=vAr0TZ?hl49U@ziHs8ZcVB@ptgV8)i=YxHXZ^J;
zZeiE|&wL%{GHwJ8z?i4*EgOTHch*ztuf_uFSYoci2K28(EqKrRegC;Q9Ccru#f8i=
z)?eMMi&YtyZt+OehiwdM-jDp&m}f3WO}rjefo-U>I)ZxrZli9Dd*;R!M~y3kld%rA
z#r>%Fyr}0398C$cBI+%wiCV|sl!hj3Wp+VL*dGgMfc4L{{-qXgMxFH!sEN*7e8sLm
zv;HWbFYqkIqK>SvSpnlTeO=e^TB8>3i<&sYoN4`QP>JqDJ)DP83;&J<@w~;4QR93;
z&K#%@Q@q9LsQX{WaMt%a+JzqG8>mD^TRhGD%v@z|LVY>+n!j5ARn-0WP_J#aU|--U
zQc&Zon=fGCKhkJNL#644dN_t)bsVDsxDj>ZPAr8#Tl^5UvxpG4(-e#)u7|2jYqNu0
zf6d~7=3CfMnP$+CQ_U|>39UExq9#0H*Uunl=iNjdRp^XqRpJA`N%ReMGYvx>!bH@a
zvrymA1*k3UHIJAlF)#g>QIG9o)cybA^B5WC3w(Q9p!(lLWjqFz>5MR+Kak2|I+W5j
z45W-o^$hC9JJ$cq49n&wibmBq!Q#r;gg6~_-&AwHxdIdE-(d0CY<@RU79GmyDJsM0
za5q7MSpnjTW7Il%Bb`-PqaU
z9u~i0jxcq<#=Vb9d%FT1<3FzSY6)KS#6xGn1O?2fu|
zIO=F7pgwU6Q1@*|-FML9KT+fFSR54T7R+guLlXCUFSv%+0yR+^vzs}{u4h<09<|et
zEnaMXgG%f>yMEmIub>{{yI2I{qg>*(FsHsGooHy`{-_sqB!d
z<`wf6YM#fa1hYlE`wOBT(qgFo6x2KoG4PwjOEkji=wugq>H_f~RK^(=&qF1&95vC`
z*baAL;9Hr)7x))|>8J&IqvmCQF|HDM`>%UfIrH7?EKmKMKi_AuYb$@{N`M$;junM=%#<^j}^oJA!Xl*<{9O0X*G
zVQX!6H{V2!ACF3OK90x5s02&r=KWWrYHl~60jhM3Q4_X8ebl?5#?3_~x)k+OVl!%i
zKTr?fQ`ADSF>ZW8voz{@RkI0dTvxv}dYc2yw@?dZpc0yD{d3KQsDzf8-_>mc$$6b#`?KB@2
z#v&HiK{nv`(pNmQiF}7z;E?tIV*M8_zHR%*}S
z@w*l;GS`?}&HbqRPFQ@#ypH;D{lKp0=ilb(XNJEhjp8&Ko3Eov^^v&@weSv9DbJ!x
zmxW6Bsl`!+-GoI^KkXW0QEZR;M2|#mYz}JS<;c2z@0fMm!$w>vT*Mdn<4g~%LHrr&
zh<-yQP^qZTn}u~y{l`%WmrHP_qpo*GC2|k-2@fsi3;dO-4yqFUFi!9Phcxoi@dehw
z&G;rhK>do;uei(nebmlpp%z?W@h-c50yWQ{*chK+Lu^>W{Q@!>_3UJ#j_5lK{QTcT
zV-Ow3a4$9}=}MlhlrQjCq>@;k{x+yztH+@#wHK>lRHDyogU@4KoP(;sulNcMDD4aU
zpQm?Wd*V__zQBKV;zz&!@OFwuGYl`|exYcCwTai5S*V>Smh}bxZE;Q1KaRFAyP|e9
z1a(9s&G*e2c6}l0gR~6w{a9U=_g^=zr9)q&O*U{hYQY1jlAXm-cop?G&~D}2f&)?4
z-!lEE`zG4;`PRSK`d6bK#vQ1K@q9Vne>HB>q0FDzz^L*rE{eKd9`zQ~K`qc8HBo=m
zeUnjNv^l6}W{ve9Fn=@uK)r3ZEzaYw;3g<$Ry1pyjZq7>K|OSRtbeS*BO}+ogG_-?$=5W+o@h<8^vCjH`K_zk#wZLtQv!%HHe5i#=qY|u)
z`i&&Qn(b$;X&+x4{;i{uIBUh;h)$U
zS66pm!pKzjWqk#;&NS4AX=W<#zh0LtI#lwo8ZN`CsQwo$?v6@m7%HLh$S+0SB&Yt8(LY4S@?aZw7J0ZU3XPkZUC0}}Gg(mfaeT`=fXnx+;D6@Hs%R#=h8I@n&
z>Z_l5TH|%%sC#`m$9^sGM4>N^0gWN3QnUxVM(Cx$pl0bLj=v
diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po
index 79c7d4faa..f282f0c22 100644
--- a/apps/locale/zh/LC_MESSAGES/django.po
+++ b/apps/locale/zh/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2019-10-15 17:37+0800\n"
+"POT-Creation-Date: 2019-10-17 16:09+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler \n"
"Language-Team: Jumpserver team\n"
@@ -193,11 +193,11 @@ msgstr "参数"
#: assets/templates/assets/cmd_filter_detail.html:77
#: assets/templates/assets/domain_detail.html:72
#: assets/templates/assets/system_user_detail.html:100
-#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:16
-#: perms/models/base.py:54
+#: common/mixins/models.py:50 ops/templates/ops/adhoc_detail.html:86
+#: orgs/models.py:16 perms/models/base.py:54
#: perms/templates/perms/asset_permission_detail.html:98
#: perms/templates/perms/remote_app_permission_detail.html:90
-#: users/models/user.py:414 users/serializers/v1.py:141
+#: users/models/user.py:414 users/serializers/v1.py:143
#: users/templates/users/user_detail.html:111
#: xpack/plugins/change_auth_plan/models.py:108
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
@@ -216,7 +216,8 @@ msgstr "创建者"
#: assets/models/label.py:25 assets/templates/assets/admin_user_detail.html:64
#: assets/templates/assets/cmd_filter_detail.html:69
#: assets/templates/assets/domain_detail.html:68
-#: assets/templates/assets/system_user_detail.html:96 ops/models/adhoc.py:45
+#: assets/templates/assets/system_user_detail.html:96
+#: common/mixins/models.py:51 ops/models/adhoc.py:45
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:64
#: orgs/models.py:17 perms/models/base.py:55
#: perms/templates/perms/asset_permission_detail.html:94
@@ -321,7 +322,6 @@ msgstr "远程应用"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:53
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:44
#: xpack/plugins/interface/templates/interface/interface.html:72
-#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
#: xpack/plugins/vault/templates/vault/vault_create.html:45
msgid "Reset"
msgstr "重置"
@@ -524,7 +524,7 @@ msgstr "创建远程应用"
#: settings/templates/settings/terminal_setting.html:107
#: terminal/templates/terminal/session_list.html:36
#: terminal/templates/terminal/terminal_list.html:36
-#: users/templates/users/_granted_assets.html:29
+#: users/templates/users/_granted_assets.html:34
#: users/templates/users/user_group_list.html:38
#: users/templates/users/user_list.html:41
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:60
@@ -539,7 +539,6 @@ msgid "Action"
msgstr "动作"
#: applications/templates/applications/user_remote_app_list.html:52
-#: assets/templates/assets/user_asset_list.html:32
#: perms/models/asset_permission.py:32
msgid "Connect"
msgstr "连接"
@@ -566,11 +565,11 @@ msgstr "远程应用详情"
msgid "My RemoteApp"
msgstr "我的远程应用"
-#: assets/api/node.py:58
+#: assets/api/node.py:61
msgid "You can't update the root node name"
msgstr "不能修改根节点名称"
-#: assets/api/node.py:65
+#: assets/api/node.py:68
msgid "Deletion failed and the node contains children or assets"
msgstr "删除失败,节点包含子节点或资产"
@@ -628,13 +627,13 @@ msgstr "标签"
#: assets/forms/asset.py:65 assets/forms/asset.py:112
#: assets/models/asset.py:144 assets/models/domain.py:26
#: assets/models/domain.py:52 assets/templates/assets/asset_detail.html:78
-#: assets/templates/assets/user_asset_list.html:53
+#: assets/templates/assets/user_asset_list.html:80
#: xpack/plugins/orgs/templates/orgs/org_list.html:18
msgid "Domain"
msgstr "网域"
#: assets/forms/asset.py:69 assets/forms/asset.py:103 assets/forms/asset.py:116
-#: assets/forms/asset.py:152 assets/models/node.py:409
+#: assets/forms/asset.py:152 assets/models/node.py:421
#: assets/templates/assets/asset_create.html:42
#: perms/forms/asset_permission.py:83 perms/forms/asset_permission.py:90
#: perms/templates/perms/asset_permission_list.html:53
@@ -702,7 +701,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:70
#: perms/templates/perms/asset_permission_user.html:55
#: perms/templates/perms/remote_app_permission_user.html:54
-#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:14
+#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:13
#: users/models/user.py:371 users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:67
#: users/templates/users/user_list.html:36
@@ -729,7 +728,7 @@ msgstr "密码或密钥密码"
#: authentication/forms.py:15
#: authentication/templates/authentication/login.html:68
#: authentication/templates/authentication/new_login.html:95
-#: settings/forms.py:114 users/forms.py:16 users/forms.py:28
+#: settings/forms.py:114 users/forms.py:15 users/forms.py:27
#: users/templates/users/reset_password.html:53
#: users/templates/users/user_password_authentication.html:18
#: users/templates/users/user_password_update.html:44
@@ -790,10 +789,10 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
#: assets/templates/assets/asset_detail.html:62
#: assets/templates/assets/asset_list.html:97
#: assets/templates/assets/domain_gateway_list.html:68
-#: assets/templates/assets/user_asset_list.html:49
+#: assets/templates/assets/user_asset_list.html:76
#: audits/templates/audits/login_log_list.html:60
#: perms/templates/perms/asset_permission_asset.html:58 settings/forms.py:144
-#: users/templates/users/_granted_assets.html:26
+#: users/templates/users/_granted_assets.html:31
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:54
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:73
msgid "IP"
@@ -807,10 +806,10 @@ msgstr "IP"
#: assets/templates/assets/_asset_user_list.html:19
#: assets/templates/assets/asset_detail.html:58
#: assets/templates/assets/asset_list.html:96
-#: assets/templates/assets/user_asset_list.html:48
+#: assets/templates/assets/user_asset_list.html:75
#: perms/templates/perms/asset_permission_asset.html:57
#: perms/templates/perms/asset_permission_list.html:73 settings/forms.py:143
-#: users/templates/users/_granted_assets.html:25
+#: users/templates/users/_granted_assets.html:30
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:53
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:72
msgid "Hostname"
@@ -822,18 +821,19 @@ msgstr "主机名"
#: assets/templates/assets/system_user_detail.html:70
#: assets/templates/assets/system_user_list.html:53
#: terminal/templates/terminal/session_list.html:31
+#: terminal/templates/terminal/session_list.html:75
msgid "Protocol"
msgstr "协议"
#: assets/models/asset.py:142 assets/serializers/asset.py:68
#: assets/templates/assets/asset_create.html:24
-#: assets/templates/assets/user_asset_list.html:50
+#: assets/templates/assets/user_asset_list.html:77
#: perms/serializers/user_permission.py:48
msgid "Protocols"
msgstr "协议组"
#: assets/models/asset.py:143 assets/templates/assets/asset_detail.html:102
-#: assets/templates/assets/user_asset_list.html:51
+#: assets/templates/assets/user_asset_list.html:78
msgid "Platform"
msgstr "系统平台"
@@ -940,7 +940,8 @@ msgid "SSH public key"
msgstr "ssh公钥"
#: assets/models/base.py:35 assets/models/gathered_user.py:21
-#: assets/templates/assets/cmd_filter_detail.html:73 ops/models/adhoc.py:46
+#: assets/templates/assets/cmd_filter_detail.html:73 common/mixins/models.py:52
+#: ops/models/adhoc.py:46
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:76
msgid "Date updated"
@@ -1107,9 +1108,9 @@ msgstr "默认资产组"
#: terminal/models.py:156 terminal/templates/terminal/command_list.html:29
#: terminal/templates/terminal/command_list.html:65
#: terminal/templates/terminal/session_list.html:27
-#: terminal/templates/terminal/session_list.html:71 users/forms.py:312
+#: terminal/templates/terminal/session_list.html:71 users/forms.py:319
#: users/models/user.py:127 users/models/user.py:143 users/models/user.py:500
-#: users/serializers/v1.py:130 users/templates/users/user_group_detail.html:78
+#: users/serializers/v1.py:132 users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:36 users/views/user.py:250
#: xpack/plugins/orgs/forms.py:28
#: xpack/plugins/orgs/templates/orgs/org_detail.html:113
@@ -1117,7 +1118,7 @@ msgstr "默认资产组"
msgid "User"
msgstr "用户"
-#: assets/models/label.py:19 assets/models/node.py:400
+#: assets/models/label.py:19 assets/models/node.py:412
#: assets/templates/assets/label_list.html:15 settings/models.py:30
msgid "Value"
msgstr "值"
@@ -1138,7 +1139,11 @@ msgstr "未分组"
msgid "empty"
msgstr "空"
-#: assets/models/node.py:399
+#: assets/models/node.py:328
+msgid "favorite"
+msgstr "收藏夹"
+
+#: assets/models/node.py:411
msgid "Key"
msgstr "键"
@@ -1192,7 +1197,7 @@ msgstr "Shell"
msgid "Login mode"
msgstr "登录模式"
-#: assets/models/user.py:162 assets/templates/assets/user_asset_list.html:52
+#: assets/models/user.py:162 assets/templates/assets/user_asset_list.html:79
#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:52
#: audits/templates/audits/ftp_log_list.html:75
#: perms/forms/asset_permission.py:86 perms/forms/remote_app_permission.py:43
@@ -1208,7 +1213,7 @@ msgstr "登录模式"
#: terminal/templates/terminal/command_list.html:67
#: terminal/templates/terminal/session_list.html:29
#: terminal/templates/terminal/session_list.html:73
-#: users/templates/users/_granted_assets.html:27
+#: users/templates/users/_granted_assets.html:32
#: xpack/plugins/orgs/templates/orgs/org_list.html:20
msgid "System user"
msgstr "系统用户"
@@ -1258,7 +1263,7 @@ msgstr "组织名称"
msgid "Backend"
msgstr "后端"
-#: assets/serializers/asset_user.py:67 users/forms.py:263
+#: assets/serializers/asset_user.py:67 users/forms.py:262
#: users/models/user.py:403 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:49
#: users/templates/users/user_profile.html:69
@@ -1872,6 +1877,7 @@ msgstr "删除选择资产"
#: users/templates/users/user_group_list.html:118
#: users/templates/users/user_list.html:254
#: xpack/plugins/interface/templates/interface/interface.html:101
+#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
msgid "Cancel"
msgstr "取消"
@@ -2257,7 +2263,7 @@ msgstr "Agent"
#: audits/models.py:85 audits/templates/audits/login_log_list.html:62
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
-#: users/forms.py:175 users/models/user.py:395
+#: users/forms.py:174 users/models/user.py:395
#: users/templates/users/first_login.html:45
msgid "MFA"
msgstr "MFA"
@@ -2480,7 +2486,7 @@ msgid ""
"after {} minutes)"
msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)"
-#: authentication/forms.py:66 users/forms.py:22
+#: authentication/forms.py:66 users/forms.py:21
msgid "MFA code"
msgstr "MFA 验证码"
@@ -2505,7 +2511,7 @@ msgid "Secret"
msgstr "密文"
#: authentication/templates/authentication/_access_key_modal.html:48
-#: users/templates/users/_granted_assets.html:75
+#: users/templates/users/_granted_assets.html:80
msgid "Show"
msgstr "显示"
@@ -2713,11 +2719,11 @@ msgstr ""
msgid "Encrypt field using Secret Key"
msgstr ""
-#: common/mixins/models.py:31
+#: common/mixins/models.py:34
msgid "is discard"
msgstr ""
-#: common/mixins/models.py:32
+#: common/mixins/models.py:35
msgid "discard time"
msgstr ""
@@ -3105,7 +3111,7 @@ msgstr "命令执行列表"
msgid "Command execution"
msgstr "命令执行"
-#: orgs/mixins/models.py:61 orgs/mixins/serializers.py:26 orgs/models.py:31
+#: orgs/mixins/models.py:58 orgs/mixins/serializers.py:26 orgs/models.py:31
msgid "Organization"
msgstr "组织"
@@ -3122,7 +3128,7 @@ msgstr "空"
#: perms/templates/perms/asset_permission_list.html:71
#: perms/templates/perms/asset_permission_list.html:118
#: perms/templates/perms/remote_app_permission_list.html:16
-#: templates/_nav.html:21 users/forms.py:286 users/models/group.py:26
+#: templates/_nav.html:21 users/forms.py:293 users/models/group.py:26
#: users/models/user.py:379 users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:218
#: users/templates/users/user_list.html:38
@@ -3969,7 +3975,7 @@ msgid "Commercial support"
msgstr "商业支持"
#: templates/_header_bar.html:70 templates/_nav.html:30
-#: templates/_nav_user.html:32 users/forms.py:154
+#: templates/_nav_user.html:32 users/forms.py:153
#: users/templates/users/_user.html:43
#: users/templates/users/first_login.html:39
#: users/templates/users/user_password_update.html:40
@@ -4390,7 +4396,7 @@ msgstr "线程数"
msgid "Boot Time"
msgstr "运行时间"
-#: terminal/models.py:162 terminal/templates/terminal/session_list.html:136
+#: terminal/models.py:162 terminal/templates/terminal/session_list.html:137
msgid "Replay"
msgstr "回放"
@@ -4456,15 +4462,15 @@ msgstr "终断所选"
msgid "Confirm finished"
msgstr "确认已完成"
-#: terminal/templates/terminal/session_list.html:91
+#: terminal/templates/terminal/session_list.html:92
msgid "Terminate task send, waiting ..."
msgstr "终断任务已发送,请等待"
-#: terminal/templates/terminal/session_list.html:142
+#: terminal/templates/terminal/session_list.html:143
msgid "Terminate"
msgstr "终断"
-#: terminal/templates/terminal/session_list.html:173
+#: terminal/templates/terminal/session_list.html:174
msgid "Finish session success"
msgstr "标记会话完成成功"
@@ -4534,7 +4540,7 @@ msgstr "你可以使用ssh客户端工具连接终端"
msgid "Could not reset self otp, use profile reset instead"
msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
-#: users/forms.py:33 users/models/user.py:383
+#: users/forms.py:32 users/models/user.py:383
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:37
@@ -4542,44 +4548,44 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
msgid "Role"
msgstr "角色"
-#: users/forms.py:36 users/forms.py:233
+#: users/forms.py:35 users/forms.py:232
#: users/templates/users/user_update.html:30
msgid "ssh public key"
msgstr "ssh公钥"
-#: users/forms.py:37 users/forms.py:234
+#: users/forms.py:36 users/forms.py:233
msgid "ssh-rsa AAAA..."
msgstr ""
-#: users/forms.py:38
+#: users/forms.py:37
msgid "Paste user id_rsa.pub here."
msgstr "复制用户公钥到这里"
-#: users/forms.py:52 users/templates/users/user_detail.html:226
+#: users/forms.py:51 users/templates/users/user_detail.html:226
msgid "Join user groups"
msgstr "添加到用户组"
-#: users/forms.py:87 users/forms.py:248
+#: users/forms.py:86 users/forms.py:247
msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同"
-#: users/forms.py:91 users/forms.py:252 users/serializers/v1.py:116
+#: users/forms.py:90 users/forms.py:251 users/serializers/v1.py:116
msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法"
-#: users/forms.py:104 users/views/login.py:114 users/views/user.py:287
+#: users/forms.py:103 users/views/login.py:114 users/views/user.py:287
msgid "* Your password does not meet the requirements"
msgstr "* 您的密码不符合要求"
-#: users/forms.py:125
+#: users/forms.py:124
msgid "Reset link will be generated and sent to the user"
msgstr "生成重置密码链接,通过邮件发送给用户"
-#: users/forms.py:126
+#: users/forms.py:125
msgid "Set password"
msgstr "设置密码"
-#: users/forms.py:133 xpack/plugins/change_auth_plan/models.py:88
+#: users/forms.py:132 xpack/plugins/change_auth_plan/models.py:88
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
@@ -4587,7 +4593,7 @@ msgstr "设置密码"
msgid "Password strategy"
msgstr "密码策略"
-#: users/forms.py:160
+#: users/forms.py:159
msgid ""
"When enabled, you will enter the MFA binding process the next time you log "
"in. you can also directly bind in \"personal information -> quick "
@@ -4596,11 +4602,11 @@ msgstr ""
"启用之后您将会在下次登录时进入MFA绑定流程;您也可以在(个人信息->快速修改->更"
"改MFA设置)中直接绑定!"
-#: users/forms.py:170
+#: users/forms.py:169
msgid "* Enable MFA authentication to make the account more secure."
msgstr "* 启用MFA认证,使账号更加安全。"
-#: users/forms.py:180
+#: users/forms.py:179
msgid ""
"In order to protect you and your company, please keep your account, password "
"and key sensitive information properly. (for example: setting complex "
@@ -4609,41 +4615,41 @@ msgstr ""
"为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:"
"设置复杂密码,启用MFA认证)"
-#: users/forms.py:187 users/templates/users/first_login.html:48
+#: users/forms.py:186 users/templates/users/first_login.html:48
#: users/templates/users/first_login.html:110
#: users/templates/users/first_login.html:139
msgid "Finish"
msgstr "完成"
-#: users/forms.py:193
+#: users/forms.py:192
msgid "Old password"
msgstr "原来密码"
-#: users/forms.py:198
+#: users/forms.py:197
msgid "New password"
msgstr "新密码"
-#: users/forms.py:203
+#: users/forms.py:202
msgid "Confirm password"
msgstr "确认密码"
-#: users/forms.py:213
+#: users/forms.py:212
msgid "Old password error"
msgstr "原来密码错误"
-#: users/forms.py:221
+#: users/forms.py:220
msgid "Password does not match"
msgstr "密码不一致"
-#: users/forms.py:231
+#: users/forms.py:230
msgid "Automatically configure and download the SSH key"
msgstr "自动配置并下载SSH密钥"
-#: users/forms.py:235
+#: users/forms.py:234
msgid "Paste your id_rsa.pub here."
msgstr "复制你的公钥到这里"
-#: users/forms.py:269 users/forms.py:274 users/forms.py:316
+#: users/forms.py:268 users/forms.py:273 users/forms.py:323
#: xpack/plugins/orgs/forms.py:18
msgid "Select users"
msgstr "选择用户"
@@ -4736,7 +4742,7 @@ msgstr "角色只能为 {}"
msgid "Password does not match security rules"
msgstr "密码不满足安全规则"
-#: users/serializers/v1.py:147
+#: users/serializers/v1.py:157
msgid "Auditors cannot be join in the user group"
msgstr "审计员不能被加入到用户组"
@@ -5933,7 +5939,7 @@ msgstr "更新同步实例任务"
#: xpack/plugins/gathered_user/views.py:21
#: xpack/plugins/gathered_user/views.py:34
#: xpack/plugins/gathered_user/views.py:49
-#: xpack/plugins/gathered_user/views.py:66
+#: xpack/plugins/gathered_user/views.py:69
msgid "Gathered user"
msgstr "收集用户"
@@ -5968,7 +5974,7 @@ msgstr "创建任务"
msgid "Gathered user list"
msgstr "收集用户列表"
-#: xpack/plugins/gathered_user/views.py:67
+#: xpack/plugins/gathered_user/views.py:70
msgid "Update task"
msgstr "更新任务"
diff --git a/apps/orgs/mixins/api.py b/apps/orgs/mixins/api.py
index 9de7f2c7d..8fb7f30dd 100644
--- a/apps/orgs/mixins/api.py
+++ b/apps/orgs/mixins/api.py
@@ -5,12 +5,12 @@ from rest_framework.viewsets import ModelViewSet
from rest_framework_bulk import BulkModelViewSet
from common.mixins import CommonApiMixin
-from ..utils import set_to_root_org
+from ..utils import set_to_root_org, filter_org_queryset
from ..models import Organization
__all__ = [
'RootOrgViewMixin', 'OrgMembershipModelViewSetMixin', 'OrgModelViewSet',
- 'OrgBulkModelViewSet',
+ 'OrgBulkModelViewSet', 'OrgQuerySetMixin',
]
@@ -22,7 +22,15 @@ class RootOrgViewMixin:
class OrgQuerySetMixin:
def get_queryset(self):
- queryset = super().get_queryset().all()
+ if hasattr(self, 'model'):
+ queryset = self.model.objects.all()
+ else:
+ assert self.queryset is None, (
+ "'%s' should not include a `queryset` attribute"
+ % self.__class__.__name__
+ )
+ queryset = super().get_queryset()
+
if hasattr(self, 'swagger_fake_view'):
return queryset[:1]
if hasattr(self, 'action') and self.action == 'list' and \
diff --git a/apps/orgs/mixins/generics.py b/apps/orgs/mixins/generics.py
new file mode 100644
index 000000000..490ee1b87
--- /dev/null
+++ b/apps/orgs/mixins/generics.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+#
+from rest_framework import generics
+
+from .api import OrgQuerySetMixin
+
+
+class ListAPIView(OrgQuerySetMixin, generics.ListAPIView):
+ pass
+
+
+class RetrieveAPIView(OrgQuerySetMixin, generics.RetrieveAPIView):
+ pass
+
+
+class CreateAPIView(OrgQuerySetMixin, generics.CreateAPIView):
+ pass
+
+
+class DestroyAPIView(OrgQuerySetMixin, generics.DestroyAPIView):
+ pass
+
+
+class ListCreateAPIView(OrgQuerySetMixin, generics.ListCreateAPIView):
+ pass
+
+
+class UpdateAPIView(OrgQuerySetMixin, generics.UpdateAPIView):
+ pass
+
+
+class RetrieveUpdateAPIView(OrgQuerySetMixin, generics.RetrieveUpdateAPIView):
+ pass
+
+
+class RetrieveDestroyAPIView(OrgQuerySetMixin, generics.RetrieveDestroyAPIView):
+ pass
+
+
+class RetrieveUpdateDestroyAPIView(OrgQuerySetMixin, generics.RetrieveUpdateDestroyAPIView):
+ pass
diff --git a/apps/orgs/mixins/models.py b/apps/orgs/mixins/models.py
index 672b975dc..4ffa24c2a 100644
--- a/apps/orgs/mixins/models.py
+++ b/apps/orgs/mixins/models.py
@@ -9,6 +9,7 @@ from django.core.exceptions import ValidationError
from common.utils import get_logger
from ..utils import (
set_current_org, get_current_org, current_org,
+ get_org_filters
)
from ..models import Organization
@@ -19,42 +20,38 @@ __all__ = [
]
+class OrgQuerySet(models.QuerySet):
+ pass
+
+
class OrgManager(models.Manager):
-
def get_queryset(self):
- queryset = super(OrgManager, self).get_queryset()
- kwargs = {}
-
- _current_org = get_current_org()
- if _current_org is None:
- kwargs['id'] = None
- elif _current_org.is_real():
- kwargs['org_id'] = _current_org.id
- elif _current_org.is_default():
- queryset = queryset.filter(org_id="")
- #
- # lines = traceback.format_stack()
- # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
- # for line in lines[-10:-1]:
- # print(line)
- # print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
-
- queryset = queryset.filter(**kwargs)
+ queryset = super().get_queryset()
+ kwargs = get_org_filters()
+ if kwargs:
+ return queryset.filter(**kwargs)
return queryset
- def all(self):
- if not current_org:
- msg = 'You can `objects.set_current_org(org).all()` then run it'
- return self
- else:
- return super(OrgManager, self).all()
-
def set_current_org(self, org):
if isinstance(org, str):
org = Organization.get_instance(org)
set_current_org(org)
return self
+ def all(self):
+ # print("Call all: {}".format(current_org))
+ #
+ # lines = traceback.format_stack()
+ # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
+ # for line in lines[-10:-1]:
+ # print(line)
+ # print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
+ if not current_org:
+ msg = 'You can `objects.set_current_org(org).all()` then run it'
+ return self
+ else:
+ return super().all()
+
class OrgModelMixin(models.Model):
org_id = models.CharField(max_length=36, blank=True, default='',
@@ -65,9 +62,12 @@ class OrgModelMixin(models.Model):
def save(self, *args, **kwargs):
org = get_current_org()
- if org is not None and (org.is_real() or org.is_system()):
+ if org is None:
+ return super().save(*args, **kwargs)
+
+ if org.is_real() or org.is_system():
self.org_id = org.id
- elif org is not None and org.is_default():
+ elif org.is_default():
self.org_id = ''
return super().save(*args, **kwargs)
diff --git a/apps/orgs/utils.py b/apps/orgs/utils.py
index 8fca26e35..2a7cfca6b 100644
--- a/apps/orgs/utils.py
+++ b/apps/orgs/utils.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
#
+import traceback
from werkzeug.local import LocalProxy
from contextlib import contextmanager
@@ -82,4 +83,31 @@ def tmp_to_org(org):
set_current_org(ori_org)
+def get_org_filters():
+ kwargs = {}
+
+ _current_org = get_current_org()
+ if _current_org is None:
+ return kwargs
+
+ if _current_org.is_real():
+ kwargs['org_id'] = _current_org.id
+ elif _current_org.is_default():
+ kwargs["org_id"] = ''
+ return kwargs
+
+
+def filter_org_queryset(queryset):
+ kwargs = get_org_filters()
+
+ #
+ # lines = traceback.format_stack()
+ # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
+ # for line in lines[-10:-1]:
+ # print(line)
+ # print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
+ queryset = queryset.filter(**kwargs)
+ return queryset
+
+
current_org = LocalProxy(get_current_org)
diff --git a/apps/perms/api/asset_permission.py b/apps/perms/api/asset_permission.py
index 41c64c7f1..68f01d2e3 100644
--- a/apps/perms/api/asset_permission.py
+++ b/apps/perms/api/asset_permission.py
@@ -1,14 +1,13 @@
# -*- coding: utf-8 -*-
#
-from django.utils import timezone
from django.db.models import Q
from rest_framework.views import Response
from django.shortcuts import get_object_or_404
-from rest_framework.generics import RetrieveUpdateAPIView, ListAPIView
-from rest_framework import viewsets
from common.permissions import IsOrgAdmin
+from orgs.mixins.api import OrgModelViewSet
+from orgs.mixins import generics
from common.utils import get_object_or_none
from ..models import AssetPermission
from ..hands import (
@@ -24,15 +23,21 @@ __all__ = [
]
-class AssetPermissionViewSet(viewsets.ModelViewSet):
+class AssetPermissionViewSet(OrgModelViewSet):
"""
资产授权列表的增删改查api
"""
- queryset = AssetPermission.objects.all()
+ model = AssetPermission
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
filter_fields = ['name']
permission_classes = (IsOrgAdmin,)
+ def get_queryset(self):
+ queryset = super().get_queryset().prefetch_related(
+ "nodes", "assets", "users", "user_groups", "system_users"
+ )
+ return queryset
+
def get_serializer_class(self):
if self.action in ("list", 'retrieve') and \
self.request.query_params.get("display"):
@@ -160,19 +165,14 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
queryset = queryset.distinct()
return queryset
- def get_queryset(self):
- return self.queryset.all().prefetch_related(
- "nodes", "assets", "users", "user_groups", "system_users"
- )
-
-class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
+class AssetPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
"""
将用户从授权中移除,Detail页面会调用
"""
+ model = AssetPermission
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetPermissionUpdateUserSerializer
- queryset = AssetPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
@@ -187,10 +187,10 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
return Response({"error": serializer.errors})
-class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
+class AssetPermissionAddUserApi(generics.RetrieveUpdateAPIView):
+ model = AssetPermission
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetPermissionUpdateUserSerializer
- queryset = AssetPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
@@ -205,13 +205,13 @@ class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
return Response({"error": serializer.errors})
-class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
+class AssetPermissionRemoveAssetApi(generics.RetrieveUpdateAPIView):
"""
将用户从授权中移除,Detail页面会调用
"""
+ model = AssetPermission
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
- queryset = AssetPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
@@ -226,10 +226,10 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
return Response({"error": serializer.errors})
-class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
+class AssetPermissionAddAssetApi(generics.RetrieveUpdateAPIView):
+ model = AssetPermission
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
- queryset = AssetPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
@@ -244,7 +244,7 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
return Response({"error": serializer.errors})
-class AssetPermissionAssetsApi(ListAPIView):
+class AssetPermissionAssetsApi(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetPermissionAssetsSerializer
filter_fields = ("hostname", "ip")
diff --git a/apps/perms/api/mixin.py b/apps/perms/api/mixin.py
index 85db529a0..86e1a8c0a 100644
--- a/apps/perms/api/mixin.py
+++ b/apps/perms/api/mixin.py
@@ -4,7 +4,7 @@
from rest_framework.generics import get_object_or_404
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
from common.utils import get_logger
-from orgs.utils import set_to_root_org
+from orgs.utils import set_to_root_org, get_current_org, set_current_org, tmp_to_root_org
from ..hands import User, UserGroup
@@ -17,15 +17,24 @@ __all__ = [
class UserPermissionMixin:
permission_classes = (IsOrgAdminOrAppUser,)
+ current_org = None
obj = None
def initial(self, *args, **kwargs):
super().initial(*args, *kwargs)
+ self.current_org = get_current_org()
+ set_to_root_org()
self.obj = self.get_obj()
- def get(self, request, *args, **kwargs):
- set_to_root_org()
- return super().get(request, *args, **kwargs)
+ # def dispatch(self, request, *args, **kwargs):
+ # """不能这么做,校验权限时拿不到组织了"""
+ # with tmp_to_root_org():
+ # return super().dispatch(request, *args, **kwargs)
+
+ # def get(self, request, *args, **kwargs):
+ # """有的api重写了get方法"""
+ # with tmp_to_root_org():
+ # return super().get(request, *args, **kwargs)
def get_obj(self):
user_id = self.kwargs.get('pk', '')
@@ -40,6 +49,13 @@ class UserPermissionMixin:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
+ def finalize_response(self, request, response, *args, **kwargs):
+ response = super().finalize_response(request, response, *args, **kwargs)
+ org = getattr(self, 'current_org', None)
+ if org:
+ set_current_org(org)
+ return response
+
class UserGroupPermissionMixin:
obj = None
diff --git a/apps/perms/api/remote_app_permission.py b/apps/perms/api/remote_app_permission.py
index 12b1ebfb6..6ced7f0ae 100644
--- a/apps/perms/api/remote_app_permission.py
+++ b/apps/perms/api/remote_app_permission.py
@@ -1,10 +1,11 @@
# coding: utf-8
#
-from rest_framework import viewsets, generics
from rest_framework.views import Response
from common.permissions import IsOrgAdmin
+from orgs.mixins.api import OrgModelViewSet
+from orgs.mixins import generics
from ..models import RemoteAppPermission
from ..serializers import (
RemoteAppPermissionSerializer,
@@ -20,18 +21,18 @@ __all__ = [
]
-class RemoteAppPermissionViewSet(viewsets.ModelViewSet):
+class RemoteAppPermissionViewSet(OrgModelViewSet):
+ model = RemoteAppPermission
filter_fields = ('name', )
search_fields = filter_fields
- queryset = RemoteAppPermission.objects.all()
serializer_class = RemoteAppPermissionSerializer
permission_classes = (IsOrgAdmin,)
class RemoteAppPermissionAddUserApi(generics.RetrieveUpdateAPIView):
+ model = RemoteAppPermission
permission_classes = (IsOrgAdmin,)
serializer_class = RemoteAppPermissionUpdateUserSerializer
- queryset = RemoteAppPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
@@ -46,9 +47,9 @@ class RemoteAppPermissionAddUserApi(generics.RetrieveUpdateAPIView):
class RemoteAppPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
+ model = RemoteAppPermission
permission_classes = (IsOrgAdmin,)
serializer_class = RemoteAppPermissionUpdateUserSerializer
- queryset = RemoteAppPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
@@ -63,9 +64,9 @@ class RemoteAppPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
class RemoteAppPermissionAddRemoteAppApi(generics.RetrieveUpdateAPIView):
+ model = RemoteAppPermission
permission_classes = (IsOrgAdmin,)
serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer
- queryset = RemoteAppPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
@@ -80,9 +81,9 @@ class RemoteAppPermissionAddRemoteAppApi(generics.RetrieveUpdateAPIView):
class RemoteAppPermissionRemoveRemoteAppApi(generics.RetrieveUpdateAPIView):
+ model = RemoteAppPermission
permission_classes = (IsOrgAdmin,)
serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer
- queryset = RemoteAppPermission.objects.all()
def update(self, request, *args, **kwargs):
perm = self.get_object()
diff --git a/apps/perms/api/user_permission/mixin.py b/apps/perms/api/user_permission/mixin.py
index 069f08937..4a004e0d1 100644
--- a/apps/perms/api/user_permission/mixin.py
+++ b/apps/perms/api/user_permission/mixin.py
@@ -34,7 +34,7 @@ class UserNodeTreeMixin:
for node in nodes:
assets_amount = self.tree.valid_assets_amount(node.key)
- if assets_amount == 0 and node.key != Node.empty_key:
+ if assets_amount == 0 and not node.key.startswith('-'):
continue
node.assets_amount = assets_amount
data = ParserNode.parse_node_to_tree_node(node)
diff --git a/apps/perms/api/user_remote_app_permission.py b/apps/perms/api/user_remote_app_permission.py
index 030e760b5..868c92015 100644
--- a/apps/perms/api/user_remote_app_permission.py
+++ b/apps/perms/api/user_remote_app_permission.py
@@ -3,12 +3,10 @@
import uuid
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView, Response
-from rest_framework.generics import (
- ListAPIView, get_object_or_404,
-)
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
from common.tree import TreeNodeSerializer
+from orgs.mixins import generics
from ..utils import (
RemoteAppPermissionUtil, construct_remote_apps_tree_root,
parse_remote_app_to_tree_node,
@@ -25,7 +23,7 @@ __all__ = [
]
-class UserGrantedRemoteAppsApi(ListAPIView):
+class UserGrantedRemoteAppsApi(generics.ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = RemoteAppSerializer
filter_fields = ['name', 'id']
@@ -68,7 +66,7 @@ class UserGrantedRemoteAppsAsTreeApi(UserGrantedRemoteAppsApi):
return super().get_serializer(data, many=True)
-class UserGrantedRemoteAppSystemUsersApi(UserPermissionMixin, ListAPIView):
+class UserGrantedRemoteAppSystemUsersApi(UserPermissionMixin, generics.ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.RemoteAppSystemUserSerializer
only_fields = serializers.RemoteAppSystemUserSerializer.Meta.only_fields
@@ -110,7 +108,7 @@ class ValidateUserRemoteAppPermissionApi(APIView):
# RemoteApp permission
-class UserGroupGrantedRemoteAppsApi(ListAPIView):
+class UserGroupGrantedRemoteAppsApi(generics.ListAPIView):
permission_classes = (IsOrgAdminOrAppUser, )
serializer_class = RemoteAppSerializer
diff --git a/apps/perms/hands.py b/apps/perms/hands.py
index aef0f4875..ab9e3f494 100644
--- a/apps/perms/hands.py
+++ b/apps/perms/hands.py
@@ -2,10 +2,15 @@
#
from users.models import User, UserGroup
-from assets.models import Asset, SystemUser, Node, Label
+from assets.models import Asset, SystemUser, Node, Label, FavoriteAsset
from assets.serializers import NodeSerializer
from applications.serializers import RemoteAppSerializer
from applications.models import RemoteApp
-
+__all__ = [
+ 'User', 'UserGroup',
+ 'Asset', 'SystemUser', 'Node', 'Label', 'FavoriteAsset',
+ 'NodeSerializer', 'RemoteAppSerializer',
+ 'RemoteApp'
+]
diff --git a/apps/perms/utils/asset_permission.py b/apps/perms/utils/asset_permission.py
index bbc64bfa9..6754511d0 100644
--- a/apps/perms/utils/asset_permission.py
+++ b/apps/perms/utils/asset_permission.py
@@ -13,7 +13,7 @@ from common.utils import get_logger, timeit, lazyproperty
from common.tree import TreeNode
from assets.utils import TreeService
from ..models import AssetPermission
-from ..hands import Node, Asset, SystemUser
+from ..hands import Node, Asset, SystemUser, User, FavoriteAsset
logger = get_logger(__file__)
@@ -293,6 +293,20 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
parent=user_tree.root,
)
+ def add_favorite_node_if_need(self, user_tree):
+ if not isinstance(self.object, User):
+ return
+ node_key = Node.favorite_key
+ node_value = Node.favorite_value
+ user_tree.create_node(
+ identifier=node_key, tag=node_value,
+ parent=user_tree.root,
+ )
+ assets_id = FavoriteAsset.get_user_favorite_assets_id(self.object)
+ all_valid_assets = user_tree.all_valid_assets(user_tree.root)
+ valid_assets_id = set(assets_id) & all_valid_assets
+ user_tree.set_assets(node_key, valid_assets_id)
+
def set_user_tree_to_local(self, user_tree):
self._user_tree = user_tree
self._user_tree_filter_id = self._filter_id
@@ -323,6 +337,7 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
self.add_single_assets_node_to_user_tree(user_tree)
self.parse_user_tree_to_full_tree(user_tree)
self.add_empty_node_if_need(user_tree)
+ self.add_favorite_node_if_need(user_tree)
self.set_user_tree_to_cache_if_need(user_tree)
self.set_user_tree_to_local(user_tree)
return user_tree
diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js
index d028743be..4f55cfb4f 100644
--- a/apps/static/js/jumpserver.js
+++ b/apps/static/js/jumpserver.js
@@ -267,7 +267,7 @@ function requestApi(props) {
$.ajax({
url: props.url,
type: props.method || "PATCH",
- data: props.body,
+ data: props.body || props.data,
contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json"
}).done(function (data, textStatue, jqXHR) {
@@ -579,6 +579,9 @@ jumpserver.initServerSideDataTable = function (options) {
ajax: {
url: options.ajax_url,
error: function (jqXHR, textStatus, errorThrown) {
+ if (jqXHR.responseText && jqXHR.responseText.indexOf("%(value)s") !== -1 ) {
+ return
+ }
var msg = gettext("Unknown error occur");
if (jqXHR.responseJSON) {
if (jqXHR.responseJSON.error) {
@@ -953,8 +956,13 @@ function initPopover($container, $progress, $idPassword, $el, password_check_rul
function rootNodeAddDom(ztree, callback) {
var refreshIcon = "";
var rootNode = ztree.getNodes()[0];
- var $rootNodeRef = $("#" + rootNode.tId + "_a");
- $rootNodeRef.after(refreshIcon);
+ if (rootNode) {
+ var $rootNodeRef = $("#" + rootNode.tId + "_a");
+ $rootNodeRef.after(refreshIcon);
+ } else {
+ $rootNodeRef = $('#' + ztree.setting.treeId);
+ $rootNodeRef.html(refreshIcon);
+ }
var refreshIconRef = $('#tree-refresh');
refreshIconRef.bind('click', function () {
ztree.destroy();
diff --git a/apps/terminal/api/session.py b/apps/terminal/api/session.py
index 7071ddd33..264502959 100644
--- a/apps/terminal/api/session.py
+++ b/apps/terminal/api/session.py
@@ -24,10 +24,10 @@ logger = get_logger(__name__)
class SessionViewSet(OrgBulkModelViewSet):
- queryset = Session.objects.all()
+ model = Session
serializer_class = serializers.SessionSerializer
permission_classes = (IsOrgAdminOrAppUser, )
- filter_fields = [
+ filterset_fields = [
"user", "asset", "system_user", "remote_addr",
"protocol", "terminal", "is_finished",
]
diff --git a/apps/terminal/templates/terminal/session_list.html b/apps/terminal/templates/terminal/session_list.html
index d23d9be02..1393bd241 100644
--- a/apps/terminal/templates/terminal/session_list.html
+++ b/apps/terminal/templates/terminal/session_list.html
@@ -72,6 +72,7 @@
{% trans 'Asset' %}
{% trans 'System user' %}
{% trans 'Remote addr' %}
+ {% trans 'Protocol' %}
{# {% trans 'Protocol' %}#}
{% endblock %}
diff --git a/apps/users/api/group.py b/apps/users/api/group.py
index e5c68004b..eb00ea220 100644
--- a/apps/users/api/group.py
+++ b/apps/users/api/group.py
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
#
-from rest_framework import generics
-
from ..serializers import (
UserGroupSerializer,
UserGroupListSerializer,
@@ -10,6 +8,7 @@ from ..serializers import (
)
from ..models import UserGroup
from orgs.mixins.api import OrgBulkModelViewSet
+from orgs.mixins import generics
from common.permissions import IsOrgAdmin
@@ -17,9 +16,9 @@ __all__ = ['UserGroupViewSet', 'UserGroupUpdateUserApi']
class UserGroupViewSet(OrgBulkModelViewSet):
+ model = UserGroup
filter_fields = ("name",)
search_fields = filter_fields
- queryset = UserGroup.objects.all()
serializer_class = UserGroupSerializer
permission_classes = (IsOrgAdmin,)
@@ -31,6 +30,6 @@ class UserGroupViewSet(OrgBulkModelViewSet):
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
- queryset = UserGroup.objects.all()
+ model = UserGroup
serializer_class = UserGroupUpdateMemberSerializer
permission_classes = (IsOrgAdmin,)
diff --git a/apps/users/api/user.py b/apps/users/api/user.py
index 77406c896..2a033915c 100644
--- a/apps/users/api/user.py
+++ b/apps/users/api/user.py
@@ -17,7 +17,7 @@ from common.permissions import (
from common.mixins import CommonApiMixin
from common.utils import get_logger
from orgs.utils import current_org
-from .. import serializers
+from .. import serializers, utils
from ..models import User
from ..signals import post_user_create
@@ -30,13 +30,21 @@ __all__ = [
]
-class UserViewSet(CommonApiMixin, BulkModelViewSet):
+class UserQuerysetMixin:
+ def get_queryset(self):
+ queryset = utils.get_current_org_members()
+ return queryset
+
+
+class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
filter_fields = ('username', 'email', 'name', 'id')
search_fields = filter_fields
- queryset = User.objects.exclude(role=User.ROLE_APP)
serializer_class = serializers.UserSerializer
permission_classes = (IsOrgAdmin, CanUpdateDeleteUser)
+ def get_queryset(self):
+ return super().get_queryset().prefetch_related('groups')
+
def send_created_signal(self, users):
if not isinstance(users, list):
users = [users]
@@ -51,11 +59,6 @@ class UserViewSet(CommonApiMixin, BulkModelViewSet):
current_org.users.add(*users)
self.send_created_signal(users)
- def get_queryset(self):
- queryset = current_org.get_org_members()\
- .prefetch_related('groups')
- return queryset
-
def get_permissions(self):
if self.action in ["retrieve", "list"]:
self.permission_classes = (IsOrgAdminOrAppUser,)
@@ -79,9 +82,8 @@ class UserViewSet(CommonApiMixin, BulkModelViewSet):
return super().perform_bulk_update(serializer)
-class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
+class UserChangePasswordApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
permission_classes = (IsOrgAdmin,)
- queryset = User.objects.all()
serializer_class = serializers.ChangeUserPasswordSerializer
def perform_update(self, serializer):
@@ -90,13 +92,12 @@ class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
user.save()
-class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
- queryset = User.objects.all()
+class UserUpdateGroupApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
serializer_class = serializers.UserUpdateGroupSerializer
permission_classes = (IsOrgAdmin,)
-class UserResetPasswordApi(generics.UpdateAPIView):
+class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
queryset = User.objects.all()
serializer_class = serializers.UserSerializer
permission_classes = (IsAuthenticated,)
@@ -111,8 +112,7 @@ class UserResetPasswordApi(generics.UpdateAPIView):
send_reset_password_mail(user)
-class UserResetPKApi(generics.UpdateAPIView):
- queryset = User.objects.all()
+class UserResetPKApi(UserQuerysetMixin, generics.UpdateAPIView):
serializer_class = serializers.UserSerializer
permission_classes = (IsAuthenticated,)
@@ -125,8 +125,7 @@ class UserResetPKApi(generics.UpdateAPIView):
# 废弃
-class UserUpdatePKApi(generics.UpdateAPIView):
- queryset = User.objects.all()
+class UserUpdatePKApi(UserQuerysetMixin, generics.UpdateAPIView):
serializer_class = serializers.UserPKUpdateSerializer
permission_classes = (IsCurrentUserOrReadOnly,)
@@ -136,8 +135,7 @@ class UserUpdatePKApi(generics.UpdateAPIView):
user.save()
-class UserUnblockPKApi(generics.UpdateAPIView):
- queryset = User.objects.all()
+class UserUnblockPKApi(UserQuerysetMixin, generics.UpdateAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.UserSerializer
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
@@ -165,8 +163,7 @@ class UserProfileApi(generics.RetrieveAPIView):
return super().retrieve(request, *args, **kwargs)
-class UserResetOTPApi(generics.RetrieveAPIView):
- queryset = User.objects.all()
+class UserResetOTPApi(UserQuerysetMixin, generics.RetrieveAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.ResetOTPSerializer
diff --git a/apps/users/forms.py b/apps/users/forms.py
index 3a8228b49..98d7c9e09 100644
--- a/apps/users/forms.py
+++ b/apps/users/forms.py
@@ -5,9 +5,8 @@ from django.utils.translation import gettext_lazy as _
from common.utils import validate_ssh_public_key
from orgs.mixins.forms import OrgModelForm
-from orgs.utils import current_org
from .models import User, UserGroup
-from .utils import check_password_rules
+from .utils import check_password_rules, get_current_org_members
class UserCheckPasswordForm(forms.Form):
@@ -267,15 +266,23 @@ class UserBulkUpdateForm(OrgModelForm):
users = forms.ModelMultipleChoiceField(
required=True,
label=_('Select users'),
- queryset=User.objects.all(),
+ queryset=User.objects.none(),
widget=forms.SelectMultiple(
attrs={
- 'class': 'select2',
+ 'class': 'users-select2',
'data-placeholder': _('Select users')
}
)
)
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.set_fields_queryset()
+
+ def set_fields_queryset(self):
+ users_field = self.fields['users']
+ users_field.queryset = get_current_org_members()
+
class Meta:
model = User
fields = ['users', 'groups', 'date_expired']
@@ -320,25 +327,19 @@ class UserGroupForm(OrgModelForm):
)
def __init__(self, **kwargs):
- instance = kwargs.get('instance')
- if instance:
- initial = kwargs.get('initial', {})
- initial.update({'users': instance.users.all()})
- kwargs['initial'] = initial
super().__init__(**kwargs)
- if 'initial' not in kwargs:
- return
+ self.set_fields_queryset()
+
+ def set_fields_queryset(self):
users_field = self.fields.get('users')
- if instance:
- users_field.queryset = instance.users.all()
+ if self.instance:
+ users_field.initial = self.instance.users.all()
+ users_field.queryset = self.instance.users.all()
else:
users_field.queryset = User.objects.none()
def save(self, commit=True):
- group = super().save(commit=commit)
- users = self.cleaned_data['users']
- group.users.set(users)
- return group
+ raise Exception("Save by restful api")
class Meta:
model = UserGroup
diff --git a/apps/users/serializers/v1.py b/apps/users/serializers/v1.py
index 59890afea..847afe885 100644
--- a/apps/users/serializers/v1.py
+++ b/apps/users/serializers/v1.py
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
#
-import copy
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
@@ -12,6 +11,7 @@ from common.serializers import AdaptedBulkListSerializer
from common.permissions import CanUpdateDeleteUser
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import User, UserGroup
+from .. import utils
__all__ = [
@@ -118,7 +118,9 @@ class UserPKUpdateSerializer(serializers.ModelSerializer):
class UserUpdateGroupSerializer(serializers.ModelSerializer):
- groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all())
+ groups = serializers.PrimaryKeyRelatedField(
+ many=True, queryset=UserGroup.objects
+ )
class Meta:
model = User
@@ -127,7 +129,7 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer):
class UserGroupSerializer(BulkOrgResourceModelSerializer):
users = serializers.PrimaryKeyRelatedField(
- required=False, many=True, queryset=User.objects.all(), label=_('User')
+ required=False, many=True, queryset=User.objects, label=_('User')
)
class Meta:
@@ -141,6 +143,14 @@ class UserGroupSerializer(BulkOrgResourceModelSerializer):
'created_by': {'label': _('Created by'), 'read_only': True}
}
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.set_fields_queryset()
+
+ def set_fields_queryset(self):
+ users_field = self.fields['users']
+ users_field.child_relation.queryset = utils.get_current_org_members()
+
def validate_users(self, users):
for user in users:
if user.is_super_auditor:
@@ -154,12 +164,20 @@ class UserGroupListSerializer(UserGroupSerializer):
class UserGroupUpdateMemberSerializer(serializers.ModelSerializer):
- users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all())
+ users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects)
class Meta:
model = UserGroup
fields = ['id', 'users']
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.set_fields_queryset()
+
+ def set_fields_queryset(self):
+ users_field = self.fields['users']
+ users_field.child_relation.queryset = utils.get_current_org_members()
+
class ChangeUserPasswordSerializer(serializers.ModelSerializer):
diff --git a/apps/users/templates/users/_granted_assets.html b/apps/users/templates/users/_granted_assets.html
index 8e6401a71..ce6bf3cd4 100644
--- a/apps/users/templates/users/_granted_assets.html
+++ b/apps/users/templates/users/_granted_assets.html
@@ -1,5 +1,5 @@
{% load i18n %}
-