From 374376102414e37154db606ac666a4aa59cbbd38 Mon Sep 17 00:00:00 2001 From: xinwen Date: Mon, 17 May 2021 14:20:51 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E4=BC=81=E4=B8=9A=E5=BE=AE=E4=BF=A1&=E9=92=89=E9=92=89?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E4=BA=9B=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/dingtalk.py | 27 ++++- apps/authentication/views/wecom.py | 27 ++++- apps/common/message/backends/exceptions.py | 5 - apps/common/message/backends/mixin.py | 4 +- apps/common/message/backends/utils.py | 2 +- .../common/message/backends/wecom/__init__.py | 5 +- apps/locale/zh/LC_MESSAGES/django.mo | Bin 74959 -> 74979 bytes apps/locale/zh/LC_MESSAGES/django.po | 100 ++++++++++-------- apps/settings/api/dingtalk.py | 33 +++--- apps/settings/api/wecom.py | 33 +++--- apps/settings/serializers/settings.py | 12 +-- 11 files changed, 150 insertions(+), 98 deletions(-) diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index c2e139467..24861b979 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -8,7 +8,9 @@ from django.views.generic import TemplateView from django.views import View from django.conf import settings from django.http.request import HttpRequest +from django.db.utils import IntegrityError from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework.exceptions import APIException from users.views import UserVerifyPasswordView from users.utils import is_auth_password_time_valid @@ -29,6 +31,20 @@ DINGTALK_STATE_SESSION_KEY = '_dingtalk_state' class DingTalkQRMixin(PermissionsMixin, View): + def dispatch(self, request, *args, **kwargs): + try: + return super().dispatch(request, *args, **kwargs) + except APIException as e: + try: + msg = e.detail['errmsg'] + except Exception: + msg = _('DingTalk Error, Please contact your system administrator') + return self.get_failed_reponse( + '/', + _('DingTalk Error'), + msg + ) + def verify_state(self): state = self.request.GET.get('state') session_state = self.request.session.get(DINGTALK_STATE_SESSION_KEY) @@ -130,8 +146,15 @@ class DingTalkQRBindCallbackView(DingTalkQRMixin, View): response = self.get_failed_reponse(redirect_url, msg, msg) return response - user.dingtalk_id = userid - user.save() + try: + user.dingtalk_id = userid + user.save() + except IntegrityError as e: + if e.args[0] == 1062: + msg = _('The DingTalk is already bound to another user') + response = self.get_failed_reponse(redirect_url, msg, msg) + return response + raise e msg = _('Binding DingTalk successfully') response = self.get_success_reponse(redirect_url, msg, msg) diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index 097df8e95..981c12508 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -8,7 +8,9 @@ from django.views.generic import TemplateView from django.views import View from django.conf import settings from django.http.request import HttpRequest +from django.db.utils import IntegrityError from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework.exceptions import APIException from users.views import UserVerifyPasswordView from users.utils import is_auth_password_time_valid @@ -29,6 +31,20 @@ WECOM_STATE_SESSION_KEY = '_wecom_state' class WeComQRMixin(PermissionsMixin, View): + def dispatch(self, request, *args, **kwargs): + try: + return super().dispatch(request, *args, **kwargs) + except APIException as e: + try: + msg = e.detail['errmsg'] + except Exception: + msg = _('WeCom Error, Please contact your system administrator') + return self.get_failed_reponse( + '/', + _('WeCom Error'), + msg + ) + def verify_state(self): state = self.request.GET.get('state') session_state = self.request.session.get(WECOM_STATE_SESSION_KEY) @@ -128,8 +144,15 @@ class WeComQRBindCallbackView(WeComQRMixin, View): response = self.get_failed_reponse(redirect_url, msg, msg) return response - user.wecom_id = wecom_userid - user.save() + try: + user.wecom_id = wecom_userid + user.save() + except IntegrityError as e: + if e.args[0] == 1062: + msg = _('The WeCom is already bound to another user') + response = self.get_failed_reponse(redirect_url, msg, msg) + return response + raise e msg = _('Binding WeCom successfully') response = self.get_success_reponse(redirect_url, msg, msg) diff --git a/apps/common/message/backends/exceptions.py b/apps/common/message/backends/exceptions.py index f72e8694d..e28a80811 100644 --- a/apps/common/message/backends/exceptions.py +++ b/apps/common/message/backends/exceptions.py @@ -21,8 +21,3 @@ class ResponseDataKeyError(APIException): class NetError(APIException): default_code = 'net_error' default_detail = _('Network error, please contact system administrator') - - -class AccessTokenError(APIException): - default_code = 'access_token_error' - default_detail = 'Access token error, check config' diff --git a/apps/common/message/backends/mixin.py b/apps/common/message/backends/mixin.py index 3beb60272..5652a1520 100644 --- a/apps/common/message/backends/mixin.py +++ b/apps/common/message/backends/mixin.py @@ -22,7 +22,7 @@ class RequestMixin: logger.error(f'Response 200 but errcode is not 0: ' f'errcode={errcode} ' f'errmsg={errmsg} ') - raise exce.ErrCodeNot0(detail=str(data.raw_data)) + raise exce.ErrCodeNot0(detail=data.raw_data) def check_http_is_200(self, response): if response.status_code != 200: @@ -31,7 +31,7 @@ class RequestMixin: f'status_code={response.status_code} ' f'url={response.url}' f'\ncontent={response.content}') - raise exce.HTTPNot200 + raise exce.HTTPNot200(detail=response.json()) class BaseRequest(RequestMixin): diff --git a/apps/common/message/backends/utils.py b/apps/common/message/backends/utils.py index 6c6f2b593..5a2f90355 100644 --- a/apps/common/message/backends/utils.py +++ b/apps/common/message/backends/utils.py @@ -39,7 +39,7 @@ class DictWrapper: except KeyError as e: msg = f'Response 200 but get field from json error: error={e} data={self.raw_data}' logger.error(msg) - raise exce.ResponseDataKeyError(detail=msg) + raise exce.ResponseDataKeyError(detail=self.raw_data) def __getattr__(self, item): return getattr(self.raw_data, item) diff --git a/apps/common/message/backends/wecom/__init__.py b/apps/common/message/backends/wecom/__init__.py index 257da47a0..dd3e34c8a 100644 --- a/apps/common/message/backends/wecom/__init__.py +++ b/apps/common/message/backends/wecom/__init__.py @@ -33,6 +33,9 @@ class ErrorCode: # https://open.work.weixin.qq.com/api/doc/90000/90139/90313#%E9%94%99%E8%AF%AF%E7%A0%81%EF%BC%9A81013 RECIPIENTS_INVALID = 81013 # UserID、部门ID、标签ID全部非法或无权限。 + # https: // open.work.weixin.qq.com / devtool / query?e = 82001 + RECIPIENTS_EMPTY = 82001 # 指定的成员/部门/标签全部为空 + # https://open.work.weixin.qq.com/api/doc/90000/90135/91437 INVALID_CODE = 40029 @@ -141,7 +144,7 @@ class WeCom(RequestMixin): data = self._requests.post(URL.SEND_MESSAGE, json=body, check_errcode_is_0=False) errcode = data['errcode'] - if errcode == ErrorCode.RECIPIENTS_INVALID: + if errcode in (ErrorCode.RECIPIENTS_INVALID, ErrorCode.RECIPIENTS_EMPTY): # 全部接收人无权限或不存在 return users self.check_errcode_is_0(data) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 304516e3cddf64bcee3e4a11330bb169eedc3e2e..159bcda3ef65fb12268270a1b138fa7fb060d68d 100644 GIT binary patch delta 22071 zcmZwP1$b3Q+wSp&1xc_32u^~#yL+L5}3dP;st$48F(xRmVhaxS--JujH z-~Zn8a8A#6oprs_-^?>UYptCfXx~%EV%|I!(|0{M=5&wibqvo-jX$UJysu+WdG%;lk48b1*ERCIG1TvD?0G}5 zQWMX+f`{=W^-G#k=XpM_TXWA#LdP5}JTHm{Wm|b(QcT?1^8zpn#>d5#= zW)4Nx?k&X>cnD+SHOz;9VJ=M4#`PtK!5E5RsBwy7T&#p@UlS8yee@+E)1Hi0KFAz}+R;?hfJ-qOZbI$& z3UP#ejOdL~L+ya8(C9XfOV>M)oY1Up(cEf+ELJVu3s3cz5r^1FD>30wV^(!_M=g^WIk$~t*G{gePn{j{E6E6W7LXc zbaD481hvvssMj+uCcx^bi5sI9-V@bsB5HzVsPVR<#yerks`n486h`PrYF%DkC_;?2c@fBu7ubX?;GGQ*g|9Q#iUN=RZ zM0-qx5vZqqBx<1XsQS66fmWdgK8fmo8Fl10k&hGaHLCyLsFV7H>YuE;D`&tAdjE@) zDMUq6EP&InI37jqJVp<9GHFp0Wx#}(3-e+r)I?n{9!8)RJOp)u6Hu?+GF1PysFU7? z@%8@iBBO`n7;2y!<~>x$rWxCg4;095~RR$hQQkqxNV?KmdITc`#9 z6T$gwpqRbgJqs-N$@RRr~Q?|l-~Yn2PN@?xj~E2BEruyPaB5w=0K>x%m19Eb&Q0qP{qpgy23 zquRelo#ZE{&r8wI-ID^Sqp6B5u@0ud?WmodMh*Bo>V$5fj`%rh;7?}U{`};l9D?ds z5VfIFs1vM;$*>8=)cfCyjCRlu)nTNSC!!{phnjE=>I8P87J3X*;yEinMgK#E8ZYJm zH(p}Y38qAilODB@{1~A3zcLvOTphDwW6XggFeh$8y~o#43%rRb@Db{14jAYbk{UHp z5!3?8nAK4I8lom{iCSoH^eHohjCM2zbq}ZGm$)6(G5R34fI!qONQ64lP%{InT^?+J zMNua=8B^mzERVab{0YlaPCc0OSH~uU-Rsj9vr+DhI;t6{30I(Aw{@te_8{u1K8wlm zHfrbpqQ=v)%7m!5DmiMr5~#Q0D@=t=ePl9|>5u7fG3LVqs9W$7b!6#=xQ+!-3#g15 zus-V6wKIF8CK!QQ&@5E_BGiI6pyCHm8$0GBqm@RXDlVfcZeSX`hhZ2n)RnWK+E+r| z;~G|OgzDECHBKih_q6){s9QE13*tl!N8it6G|(N?j$Wd69AlWfXQ5b+^0&AGH{mX9 zFx<6I^1Zt?8ByaDLp@8CQ49JSwV(#5lW2|_zbkT*K5sY~b(n^FXcnS6tV2EJTT$=p zDbxb*pziq_)Q%F4a8Gk8RR2s^1Ph=h?1V9~zv)A@`yNy4{hwkL8&Ge<7Ax;XO>hXc zvr`y`S5dd%BWi+xk*+=|>K>=ZSXdCX;Nlkl5;aa^jE)^7^Lw4iXh&U91CK%tG#Rzh zrI;Q!ndh(+<+qp)3;p1Zv@Qlv?tp2r8?M2LsAsA8D7zJ?1vW=tY%*;v&;|2Q?u9jR z8Ro@Ts9TU}v^$ZK7>9Bd)CqizT4;0B``!ih7K}x;TVSpI8hKiD#htEl2I-DCz?x3bpe`R{sgrKK(d%@AIMBmqy)! z%0A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 delta 22092 zcmZYH1(a6R`}XlOFvI{u4-5=3bax{K~?`<}I2?|atTe)hh1?{m&G&mem9VDPbn!Ty!h!C!k^FXMY&COn zOV1mD9b0+cMZAMYsNemm=WX>o-<#UT^U~6>c3UQ+L7VoT7ly?;cwR!Rj-mJwCdHPR z4m)Eu^v%V{+PyuP0dHb3zQbY|ucPM`!jhY{;117_0^K)||Y6G({4KBg7%mf*n!qqcJ58L_aN=31qbL`Q|Frj$%*) z?!kO`0=45e7>&uhdR{6Vjv9C>s{L%_aq?DRUR;fN@DyqzZ!iI-=*Ic;{CHvA+&$`n z8ek!6;B}~-?Z6P+XCAlu3z(GnBP+i`?JP-m_l!iKPM|ny;ZdmihNum;@6P$_Vd-lX zlTjzK0M#J|BXA$;p}Ar8!9Cmr8BhZjMeVE-s^7;}-x)Q&Z}B;(jjcts->wSXtCOgK z9-unB!DJZL)4dJZQ7bNsI_eszh1N&CwjD7!jzYa1(@+avg=+UJYW(x4x9b6FJYP3b zft08f<+gHpE7wQ8MjbF5yIB1w%t(1QYJwQ6KVbFOE&dX9uaouiyu_FqLooumb-tI6 zOilu&FbOutLf8>?&u5@cVm_wC)u?A;8)~54sQTlmaW12tiFmzT|KzA64@W*`y!@#C zg)p7o|B_@hKj>b|r154mRERXL{J1^VEolGOtLYrXeehr zEqJSy&!FacfW9VvOhyyDMh)Qgb4MMHsVL?%-!to)tuYPty)iY8MeS@h>Rzu$jUQ|7 zG7p<)`f>hhc$I*T=pL&4%*t=gME%`DQe!#lb7D&T6t(bZ%!>n1C$kVW&Pr6f4XBOm zvhpd^iQMXM@BhC9!U$v-;8t7&HBdR!J*{Lmz#^2}qE2cW>UI4Nlj9ytiziY2?xLRl zm#BrlL*3fof$o`0?UT_&Sy3mD2i38-#VccW%C#)s1GST3s0pW_ZovYp-)Qbe?ffL_ zByOVSd4#%Ef1$?l6Af||8BhaeLv<`_@k$s@xhCqhYKMATdZGp#it0Do%F|Kzd@icp zGE9d*pgutlU58r$I_tB*g#?JzUyh;yS( zun_8~-$#vG+x!?KDYwSNdjCg|(at8Kj`C~NioZj>COc6(*^4^LlUBZnn&2L4!WXEM zNI29jGz>FRj-}#YM`E|g$zgSXgX@(IjE<66&An~SP zKKu!FQa4Z&K1IEDuTc+ga*kC`cUBC?VyK(v1@;CR&Q@eM}cYRrxM zFf0C!#V~Y)y9H6G6YGZR=c5)d9W~xk)UAs#cca>!KrQH&I;g=T)QaDrDnduPorR%R zoCy`rW${9onQ}>t#D-Sxi)ud&b!+EZc_r$kHlW6dwX(m%8tg;evtw8iFJd-KIm!)G z9JQk;)Q;<6Wo(a4a0RZwceo9gjdtzZjB&T77ivLcQP0wJWI?_+pNv+t40RG~Q4?%K z9py1phij;Z<{_&6Yt&O7G}gVY5vT^f9^K|5!4?1ooMS zQ4Noy9?mNke}fvx8|T_5K~0bXwX+C}#Js3mP!qMGhF0Geb&Gpq0vv%UnBN;`6|+zS zt-^S?$@~emqiv{xPooC9j9Tbp%#QEO$nkvYQT`CK;z-nqF2;no3A5nO=&vSok&GUu zaTDxTpjNon${Ve`6^jtxiS_Ua7R8Da-7V;iI*|#O7-ynRU_NT0Yf&e<74;VUHj(pJ z!v_T9GxIGbqMYCh_r8aso`Eu`dt4dyiPjK9um>i=fv5$HLQVV?>S3ObN$@0Up37JO z?|i}et0MIz_nj{@>Nl54s3WS1I@%8AcuY!pHR>ogqjvTS>SxAb)WkPX{r*I4B<+{( z^CS~$=Vej#wS6+`&=Zs4FjR*xP`6;ZIUBX|1(+09qV8!7Y61IE8#!a;Yp4bNff1Ns zvg?-(HD4jjjeZ3(>ewFDp(pA{$D$^jV&&PWc1ut@Scl1QC+cB5V)a+e2dD);$MhKT zl{>L)s0HLjK3{yV3>h8yC#Z>Ap(gHx`LGvG!MUiN=ck~7OQUvB3$>u$mbv_8HjWLUz3R>5Hi*6C_ic;WiUBbLfz~7W>eGzpQ1iDx}pY*M%}Wp z<|NcJG!6B3Z9u)oN6ot!`2F9T<^~8u?W6$eo_&D&sn#5IYoe_@&iocr5Z{1W@J`f% z4x!o~!{T@cb)p%jyYaH4%4IO{^S>e)b*ziUu^|@3u~-Z@qTbtEs3Q%U;og>Hs09>7 z^{~3N>)iuib=YFb(A> z)V*(H<<_W$^hF)%aMS|FTX~8(8#Uo#)CSjMT0H2J(Y?BYn(#Slz_->Q(JVJ`CJZH> z3$>6EsMo5x*&1_G9*8M%f%(1F@5D&r=THyzYb=6(;&0s1lr?Lij;JN-A?%L&F*^aZ zvn8krR-4;U?f-{4@e=A82%7CoggTi})CSU_P9P_8i+nH20u50U_CO6Z5yNpkY61JP z5MIZUm|~8*$5oII60a%h77j)o^>EaJC!rQF1GRv882Eg^5a#zblhMPm6V>4)s>20L zhc{8r$ls`kH2qvxpAA*ckGf?gQ6~|F#jzf0+)w?JHZnCTgMv7H^N*Kv&d6eNp3$LM`+w)I4+0SI4Df^fYcl z4SdKvfg0c(M&e!6El51iEu=ISqg(;it|#imhG9XRiCV}m)X^V8eG;C-z=r2>{>r=~ zpcMtrcPmVe8Yly5$3;>1wlwPDdk@vG4(eVvLcQ;8P~-GP?RXgKmQ1wx9MpoAT6yh! z&R+v;w!j|LN{?9iBI@XGTl{y_0B=wO2QP3tPm3z&KrO6Ea^S$%!XKz$p`j6-}f zRmjZ72)vBi!E4k);xBXyNQJt08Bj-^6Ek87)Ghc3!*B#@A+u2JR-hKV3ANGfs0|&q zxPOX_j`$Mh#v7=k4q4F& z+W8^#3S*_1UOa`_@GWZMEX(;ChtpRP zhxdKdYt;dB;wV)8N-Tiqur3C#c0WBE>i2s+m0bxmz{R*8pIL(?Yux*~6?HTQC@RVF>QSk$4=R zW5ad)DJRxowR*_zVJ>`&eK7m?d~wCEu@L@-x>X4_a9gl2Hb=idnX+V#V*(8M!3~%K z^|0i?^jHD)k=z)QVK>w>FbK8q38)3nwE8)ilJW}FPGeCYP|XkwxAZU*F1t+;2CU-S5Z5w^rJh0rl|HU%?_yXyQ5BGAnG9=iv^h9 zdq_qTrQhU^CI@P##ZgCJ1@%<7#1c3JbzCmUv;X84Rvk4^b1aH&Fda^_`c;^o@;21NcnbBf zo-=Rb2b7r~m==S#xz{fP zmZKbn;W!w}<8;i3M=%WUq5A#flPOFl&Cf1S8MX4Jm??;Rj)4XI;&whCbwW!~1Fl2e zx&x>qJ&$SdKI-1TLyebgyX&6{^=zd_)%*F#XeVV+1J%ZyniwDB$Q|w@v-d8&tlWF- zfKj{IA@$2JG9KS*_b>_ND|^{B^$+*)@rxbzQ-}9(2lXosc-|VypB>`C3}VRChxvg* zAoPe^$Zph%&!C=(3#fZismI+(RXO1vzK=1Kcz1IM>Le#vc@F9!TxtGj z?!tsR(xVnQW8N?yn{QE1Ysg8rbv0f*(=u^M2F>S5Z5>k7^(9jEkp0l{1*R%@SrsOiq1WOo1&?<99#9 z`77WP2*WAna?C<`n|T4%;kDH#JL?{{>=;J8q*=>sgAv3BSb4g+%G_@Cr+o`NLG3Kz zZ*DTN@YC*599Dcz~mg12s<_)Pg?=l)3-?tzr~rq+$vdz}1)?&thSG zje4*1UUUm7hZ?Y=Sr@gC=2k!0;v-NG-(+*1)vu9y|2JA-3u?ffsE6%{#jl}ua2Iuy z4>3JHM@<-d$+a(tX(>mccHS5>Vh<}%K%L-X)I6&&@bCXOkwXZt;q!1=ce^G25Fx%z>zdjJ(YK*F!OZfShfvGJi4;U`6WBp%$9qiW{H^ z>La@}YT~vQ?}lpE$DDxL@dDHa*O=R{aQ+IMAfN$mp;q>XHF$0&zUqD!M4*ngq1g?! z@G+|2|n45BfYpz22M>gPlN6Bb{>!_VQL=F7L%875d`fzMTJUi;C9)TL@TdQA!>c1A% z|7WW|X!XZ23-Jq9evK)Z-;00K?KBPQ9u`CmTp2Y`O|yyF-t2|C1;fm#7GH`wxeZo6 zXr481qQ-wBncsU!Mz7O9s2wD`}Ayf|i-FsD&J|`kScn z9+)rD*8st{-NZ>z4I<3EW+}`;yfT)<&n&(O)qkD&qs6zG2h1~AnRYjHTXz@Fo ze^Dx`T3`UGJP~ywTQMOXK%Kxb)Cv7*@u0hIpu}bbYR8398>x=!-`L76tlSy15|6&i z`K#d+0(uDNVNP6u1@SOyL9ehh=DX)!%Ql#f@_5vQ3sBEaEb0~ zsCf(emMLdeL#?=x*&IVCx3zLN)C7G{1CB+V%wi0|J*a_?Sowl^3pMT^=5y13OGX{y zKXC79O4JFIMa5g0T~S9r5cLd<#|F3y=V6+Mp7%X&K%GdNN6x{hai*gdu+YjYT-oTaX)H@*DU_b^nP~(gkomu)1p2HilcrS)X#8i$%_BkDxPYl@A+4l6qlnW`~fxKc2xTl7JrEPA@d4#OVj`5Zecc5`!c9! ztCrc`w~EhE9j2IbP&-_TI)T-wiDOU$?Zgy#%HlW8CszN?>Qg><{UXgmn3sO#t?aia zqZRkUzyeSc&annd%@S_P@j}vSa}8NTB_FsP;1~KG$4l?le!K7H~)E{eNK< z3IBFSoB_j$mo*!pChBVCVWUbaZ3_L?EDD_)6aW1nUYGEbJil|#z7jxqn)I!&w`fq&8`K!ZT z0vYfmM&KjV5hZ@-1`IcInZ;29MVZwsUKcfRbF&9(oDo(()tryI<*VOu{u*c}fdaT6 zReomvjk*;<|G0%@H1nX^l|-G)dseP$)hiZ5a)8Id- zhck6hP~g2UkJ?E$)WQd$CY)gLDOR3`I?<)(Iy2VXh2eVt50lY=w@?H9i7D|F>Ig&P zxq&jF+T})lLYA<21&dch9c?2k_rZdceawz)t^OElVSixY`~NFt2>fdm3F8L^-iDN@ zcz)DQi&?pnSp&78dRA_U8o0g1dzeGa@#aj_2`@%pJB%fxj)zbao<ew@Eb;JZw4LT5@d6Y7UjUev%PP$%@E zS=VfedPv)%7TVwZ9Q74#G-?6UQ5#r@daE{KXFTj%pkyL9L0z*cYQVOrhpjVepx&qn zhoe54r=tdpwff`cW%Ggg5;b0MVmoQncS=8;j0VVpYEZ`f0JZa~sFU~@^)sOjY9U`) z{kN$8Ys_C!Cv_S1I^V@83<+`lYNF<8g7ovfRxaZWu!wie-$!XaRb!KS_CTi1VarFZThHoQ>=cD z#aEb{F%$LsPz$(ezCiU0N$O04nkO?RV}38EGFZ&~z^rXHGdrR>_CZZF7&Y)Li!Z{w zlvkoYaE_bLP&-b?zn@XRBA6cQVBpXHJCMmppf~E1?@QD`^D!&#L!HDO)Iwiaef&^2 zQ9)GudRPw!VKqE}gcfqvy zIqFxgY35SY&Nrdj?Y8n6)PirIZpD4n!}hP0!}*tlI_kn0`00px2Kpek-1p{^(M~s; zJ8=l*L#TnP@?RJ0p{#>y*9!Fwsgsq5quNbC-I6J&r+lILGwN+Pk6PGURR6Gy8jts{ zAQ|nfwE3Y~AN9%C%Iu7qs4sqqLs0j2yT#9;7I@XXj~e$G>cqT=pum3-od)&LFTlY2 z{~H;t{1&R=U#QRcMEnb2l_Rh%7Dnx0Dr%zd%otSvU(6%sWz+(GM}2hvZRNa~-8iK% z@cw^9Mgz1!4crYiaevf=6R-j<#>{vHHSk+AB#Y~p4s|k-sFUh|`Uvl4_0gz)gROpC z7T$j~oNR%a*q!pXsEHG2bqmRc>R1xBlS-)iXmbMU;aZHEa3^XbzoPmbMg76WMbvzG zBHiaf;YdFy@YiNq6VM06SX_!LQJ;vlv$+qZ&rnbCDAY-XYvf>YL9qi)YCm z6!@jI80v$qBdYyK)LSwW)o-;=Mk_ppTG=(^)%5P7Cj2Ld+fk03Zbx}g0~Ir?p!zpO zEvy4-=lxLQ4M&~GSkyB$5!G%!Y9anQGWsgC2bni;<)g=qc~xw7!RpMT9t;4bMW7xZ3X zTFN_Tyo-wPXOYvj6EC`K;44KdhD}B6Ta3G2kUwdoC|1@g8WQJAU*MY0KNpZr*tk_p zotv%|R=&wNd_D66PsA(hoSMqy)?kPEm`*cD(bi^|#WmP*Vqe&Trkh#ucgufbE>mN! zSET8rmBhye#?w~>yVl`*49=H1zLfJ@o=q5R_52Y?;F?V5H{|Y;nv>t?!rnXDPqF+M z;*UtvNIwy;LcA?wwzI{*w6XOO>8+sAXaY4zUz69R$52;kIwT>s!4B{Et77(v#V=6R)`r5V#l>_@fPi7V=5efO+|Lnw{-r{g}uDw>k=!Ahb;Ei z+=m;9x1;X{+Jum9QqD_x6Z!pEm2}eDXCtUwW zt9n#FF?pjH{i;bthA{qFbK*O=2HCk-e zjq8D5kRCG0dD8o&YYgx)c2FJdqcJz-^46E4cZ4>La2fGM7GFSY5oKLppg)bw5Q6Eg z!DaHghEiFZw2iX<4dH$7S=&OcD^US*jju>ejNFfScb$87_X*H_5n6yK`BW5O6?8D%e47_ulI<5 zMg0pL9^c;o5(FCmr)3-RJ?MOw_*+r~8h5k$UnsYtZWpO7v9BplA^(K@e5^%VT@%y> zYf=BdK!m@liXT!xgu44!z~>($X`pXy_ers&U#Luvxk*}Lo3I0kA4;CD-&5;QnUhqPLEa+2)CGRPs`C+IYe@Rm zr>`lesn^xnOiJAhQg_lW`t>8;3w80Uk@o@dKS;XPQdfZ(UlhEjF6Mh($yBFeAHhMS z|6Y};>q*^Ff}^dXqqVt2e3ZowlGinpx-wYE4IYT;AEDGI#s{SH6aSKYB(cNfchjaEX)1YL zlN{bQJ;50W9;Wdn@^RM%b)xXA4XE$Jx=PZf4yiQxg7_sVKk2!(<2M&?J$?V8zMaLY zQLaM%vIgM#k~BgY;(_Ns2N_)@aldua7c^bztUQT<B@=<^*(S3RrmOnHdq6(2|WkhMvIR~6(c%2#ABJ%i|~h>b~otz49b#mRqy#Ylrl zy-B(%S)1j!nTdWSegKz}u97cr^=kVk<-y8u^(X!hZ3f2G2hN|0<20%rXu_L7SCR8kk3Q@SA0sUM(00Bcjyy$U8TH+r0W@Jx8;-5 zudlT`g`J51Y;|gzJ09o1!6xcMU@Vm@@CJ>;iHB3}MEO%|GoAQUVm)op2jn-BI*@c# za(Ew`31~Nny5aP%PFiH~IP||f@S4)-8I^-+*c+2k9z%RKDG~YBq#ES!kxEhD8g<f!F$=c5*zL|Vh(x;?fDF0{^ma;beDc>gk3;mCf!u9?~5jbECvYDxf zJ*V+V+(g43q-&)AUf)vp8>yTH57PH0eJT^vPt~}qGO;71`6OMJajVPn`*vXe92J3| z7~p+U2&ttt_zl0Zd_jz&ZZ+u)`LYc3nu-3#wWKnnlceR;t))%eb(YLG#OL5c+P5Kp zjTE>v|6dfY&>`|aogR{ZPTl*YP?^FpH6V>A|0@k*@CoGy)YqpxoAfg=UC}l{YGMg!bJWUn zsk=h@AF)To-eKHT%rd?3DDg;r|NoIj5!PAt8^|{y#gOvS=zAv6m6-hBF6(t7wvh4? zQWNsiNi#@SNy&(PMc+-Nd6e@}u4VlaQ$9h8yMp!pudu*GI_QrpcTny~r}h>vOgsnq z?$pI6Ux2iiasg6#8)+f=daASX-}K)@tPS2#jJ&Q3<}mW9NH2Uk^(PaLhI6UBh4F~> zLR~3HB`BZ5FdT|WXqS-sER^S3JHp-s@^7fSNBV@MYZ$7@Hc}hf$C7kaA%2+T-{&92 zNYzN4NfA_bM_pxY@cgdmWhVX;`AT>UXHdVL{3Fs58@LAUrS50!WwEX{j)u8G8mkOf zNj-nlX!I8imXX?$Zn`>eFYza&4;ft7QtI@yHm1BCzo+~?)}+mHYjcjgu62~}QXhAn zAoGm6i^LXMa47jY`u_i#Koe3q0xNBx1$f^E`;+)%lCFW~3eEA1E#M^ilGHuM3B>*(-;$(jyIG04 zAFRFNtB8L^nx_I+PU=Tc9*=ct_dB*C9!YE#NmpnQf;-7fRbfU@!}NZ0ng$c?M(Rma zR~DLgp?ugC1OJRBmW*6y(hgDt{ptm3_(DeA4a#%rQ;__JSReb4J|eCw2=}|J_a6CC zq%hs%Of-5!I$)IzO);MMZ!}y>{!{V?$?J;3*-WDA9&ufxur+Bv@%7Z_AfJYOEz)Vy zWMZd?^}`;dSke`JBYa6v*Hi*aNV>Mrp#t%aLHZx@Re=(1iGSu>!#*_BHJ@NNV*ipKLF}n@jz{b}V!MegBwe!h z`7y}aq@m5{zDO0mUOslR1fSVst5>6r?fZ1> z7c;lptz>EY^os7&wqKX%p6b;;x=$}U#S9%ZB3I(PeFM$%#UxtrCSgpA\n" "Language-Team: JumpServer team\n" @@ -1547,44 +1547,48 @@ msgstr "返回" msgid "Copy success" msgstr "复制成功" -#: authentication/views/dingtalk.py:40 authentication/views/wecom.py:40 +#: authentication/views/dingtalk.py:41 authentication/views/wecom.py:41 msgid "You've been hacked" msgstr "你被攻击了" -#: authentication/views/dingtalk.py:76 +#: authentication/views/dingtalk.py:77 msgid "DingTalk is already bound" msgstr "钉钉已经绑定" -#: authentication/views/dingtalk.py:89 authentication/views/wecom.py:88 +#: authentication/views/dingtalk.py:90 authentication/views/wecom.py:89 msgid "Please verify your password first" msgstr "请检查密码" -#: authentication/views/dingtalk.py:113 authentication/views/wecom.py:112 +#: authentication/views/dingtalk.py:114 authentication/views/wecom.py:113 msgid "Invalid user_id" msgstr "无效的 user_id" -#: authentication/views/dingtalk.py:129 +#: authentication/views/dingtalk.py:130 msgid "DingTalk query user failed" msgstr "钉钉查询用户失败" -#: authentication/views/dingtalk.py:136 authentication/views/dingtalk.py:219 -#: authentication/views/dingtalk.py:220 +#: authentication/views/dingtalk.py:139 +msgid "The DingTalk is already bound to another user" +msgstr "该钉钉已经绑定其他用户" + +#: authentication/views/dingtalk.py:144 authentication/views/dingtalk.py:227 +#: authentication/views/dingtalk.py:228 msgid "Binding DingTalk successfully" msgstr "绑定 钉钉 成功" -#: authentication/views/dingtalk.py:188 +#: authentication/views/dingtalk.py:196 msgid "Failed to get user from DingTalk" msgstr "从钉钉获取用户失败" -#: authentication/views/dingtalk.py:194 +#: authentication/views/dingtalk.py:202 msgid "DingTalk is not bound" msgstr "钉钉没有绑定" -#: authentication/views/dingtalk.py:195 authentication/views/wecom.py:193 +#: authentication/views/dingtalk.py:203 authentication/views/wecom.py:201 msgid "Please login with a password and then bind the WeCom" msgstr "请使用密码登录,然后绑定企业微信" -#: authentication/views/dingtalk.py:237 authentication/views/dingtalk.py:238 +#: authentication/views/dingtalk.py:245 authentication/views/dingtalk.py:246 msgid "Binding DingTalk failed" msgstr "绑定钉钉失败" @@ -1620,28 +1624,32 @@ msgstr "退出登录成功" msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: authentication/views/wecom.py:75 +#: authentication/views/wecom.py:76 msgid "WeCom is already bound" msgstr "企业微信已经绑定" -#: authentication/views/wecom.py:127 +#: authentication/views/wecom.py:128 msgid "WeCom query user failed" msgstr "企业微信查询用户失败" -#: authentication/views/wecom.py:134 authentication/views/wecom.py:217 -#: authentication/views/wecom.py:218 +#: authentication/views/wecom.py:137 +msgid "The WeCom is already bound to another user" +msgstr "该企业微信已经绑定其他用户" + +#: authentication/views/wecom.py:142 authentication/views/wecom.py:225 +#: authentication/views/wecom.py:226 msgid "Binding WeCom successfully" msgstr "绑定 企业微信 成功" -#: authentication/views/wecom.py:186 +#: authentication/views/wecom.py:194 msgid "Failed to get user from WeCom" msgstr "从企业微信获取用户失败" -#: authentication/views/wecom.py:192 +#: authentication/views/wecom.py:200 msgid "WeCom is not bound" msgstr "没有绑定企业微信" -#: authentication/views/wecom.py:235 authentication/views/wecom.py:236 +#: authentication/views/wecom.py:243 authentication/views/wecom.py:244 msgid "Binding WeCom failed" msgstr "绑定企业微信失败" @@ -2123,7 +2131,11 @@ msgstr "邮件已经发送{}, 请检查" msgid "Welcome to the JumpServer open source Bastion Host" msgstr "欢迎使用JumpServer开源堡垒机" -#: settings/api/dingtalk.py:36 settings/api/wecom.py:36 +#: settings/api/dingtalk.py:29 +msgid "AppSecret is required" +msgstr "AppSecret 是必须的" + +#: settings/api/dingtalk.py:35 settings/api/wecom.py:35 msgid "OK" msgstr "" @@ -2135,6 +2147,10 @@ msgstr "获取 LDAP 用户为 None" msgid "Imported {} users successfully" msgstr "导入 {} 个用户成功" +#: settings/api/wecom.py:29 +msgid "Secret is required" +msgstr "Secret 是必须的" + #: settings/models.py:123 users/templates/users/reset_password.html:29 msgid "Setting" msgstr "设置" @@ -2462,34 +2478,10 @@ msgstr "邮件收件人" msgid "Multiple user using , split" msgstr "多个用户,使用 , 分割" -#: settings/serializers/settings.py:193 -msgid "Corporation ID(corpid)" -msgstr "企业 ID(CorpId)" - -#: settings/serializers/settings.py:194 -msgid "Agent ID(agentid)" -msgstr "应用 ID(AgentId)" - -#: settings/serializers/settings.py:195 -msgid "Secret(secret)" -msgstr "秘钥(secret)" - #: settings/serializers/settings.py:196 msgid "Enable WeCom Auth" msgstr "启用企业微信认证" -#: settings/serializers/settings.py:200 -msgid "AgentId" -msgstr "应用 ID(AgentId)" - -#: settings/serializers/settings.py:201 -msgid "AppKey" -msgstr "应用 Key(AppKey)" - -#: settings/serializers/settings.py:202 -msgid "AppSecret" -msgstr "应用密文(AppSecret)" - #: settings/serializers/settings.py:203 msgid "Enable DingTalk Auth" msgstr "启用钉钉认证" @@ -4953,7 +4945,7 @@ msgstr "实例个数" msgid "Periodic display" msgstr "定时执行" -#: xpack/plugins/cloud/utils.py:65 +#: xpack/plugins/cloud/utils.py:64 msgid "Account unavailable" msgstr "账户无效" @@ -5041,5 +5033,23 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#~ msgid "Corporation ID(corpid)" +#~ msgstr "企业 ID(CorpId)" + +#~ msgid "Agent ID(agentid)" +#~ msgstr "应用 ID(AgentId)" + +#~ msgid "Secret(secret)" +#~ msgstr "秘钥(secret)" + +#~ msgid "AgentId" +#~ msgstr "应用 ID(AgentId)" + +#~ msgid "AppKey" +#~ msgstr "应用 Key(AppKey)" + +#~ msgid "AppSecret" +#~ msgstr "应用密文(AppSecret)" + #~ msgid "No" #~ msgstr "无" diff --git a/apps/settings/api/dingtalk.py b/apps/settings/api/dingtalk.py index e560f8626..4e4a73bf7 100644 --- a/apps/settings/api/dingtalk.py +++ b/apps/settings/api/dingtalk.py @@ -1,11 +1,12 @@ -import requests - from rest_framework.views import Response from rest_framework.generics import GenericAPIView +from rest_framework.exceptions import APIException +from rest_framework import status from django.utils.translation import gettext_lazy as _ +from settings.models import Setting from common.permissions import IsSuperUser -from common.message.backends.dingtalk import URL +from common.message.backends.dingtalk import DingTalk from .. import serializers @@ -20,19 +21,17 @@ class DingTalkTestingAPI(GenericAPIView): dingtalk_appkey = serializer.validated_data['DINGTALK_APPKEY'] dingtalk_agentid = serializer.validated_data['DINGTALK_AGENTID'] - dingtalk_appsecret = serializer.validated_data['DINGTALK_APPSECRET'] + dingtalk_appsecret = serializer.validated_data.get('DINGTALK_APPSECRET') + + if not dingtalk_appsecret: + secret = Setting.objects.filter(name='DINGTALK_APPSECRET').first() + if not secret: + return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': _('AppSecret is required')}) + dingtalk_appsecret = secret.cleaned_value try: - params = {'appkey': dingtalk_appkey, 'appsecret': dingtalk_appsecret} - resp = requests.get(url=URL.GET_TOKEN, params=params) - if resp.status_code != 200: - return Response(status=400, data={'error': resp.json()}) - - data = resp.json() - errcode = data['errcode'] - if errcode != 0: - return Response(status=400, data={'error': data['errmsg']}) - - return Response(status=200, data={'msg': _('OK')}) - except Exception as e: - return Response(status=400, data={'error': str(e)}) + dingtalk = DingTalk(appid=dingtalk_appkey, appsecret=dingtalk_appsecret, agentid=dingtalk_agentid) + dingtalk.send_text(['test'], 'test') + return Response(status=status.HTTP_200_OK, data={'msg': _('OK')}) + except APIException as e: + return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': e.detail}) diff --git a/apps/settings/api/wecom.py b/apps/settings/api/wecom.py index 3087efd1f..5059b7647 100644 --- a/apps/settings/api/wecom.py +++ b/apps/settings/api/wecom.py @@ -1,11 +1,12 @@ -import requests - from rest_framework.views import Response from rest_framework.generics import GenericAPIView +from rest_framework.exceptions import APIException +from rest_framework import status from django.utils.translation import gettext_lazy as _ +from settings.models import Setting from common.permissions import IsSuperUser -from common.message.backends.wecom import URL +from common.message.backends.wecom import WeCom from .. import serializers @@ -20,19 +21,17 @@ class WeComTestingAPI(GenericAPIView): wecom_corpid = serializer.validated_data['WECOM_CORPID'] wecom_agentid = serializer.validated_data['WECOM_AGENTID'] - wecom_corpsecret = serializer.validated_data['WECOM_SECRET'] + wecom_corpsecret = serializer.validated_data.get('WECOM_SECRET') + + if not wecom_corpsecret: + secret = Setting.objects.filter(name='WECOM_SECRET').first() + if not secret: + return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': _('Secret is required')}) + wecom_corpsecret = secret.cleaned_value try: - params = {'corpid': wecom_corpid, 'corpsecret': wecom_corpsecret} - resp = requests.get(url=URL.GET_TOKEN, params=params) - if resp.status_code != 200: - return Response(status=400, data={'error': resp.json()}) - - data = resp.json() - errcode = data['errcode'] - if errcode != 0: - return Response(status=400, data={'error': data['errmsg']}) - - return Response(status=200, data={'msg': _('OK')}) - except Exception as e: - return Response(status=400, data={'error': str(e)}) + wecom = WeCom(corpid=wecom_corpid, corpsecret=wecom_corpsecret, agentid=wecom_agentid) + wecom.send_text(['test'], 'test') + return Response(status=status.HTTP_200_OK, data={'msg': _('OK')}) + except APIException as e: + return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': e.detail}) diff --git a/apps/settings/serializers/settings.py b/apps/settings/serializers/settings.py index 3aca30c10..28ddf1cd7 100644 --- a/apps/settings/serializers/settings.py +++ b/apps/settings/serializers/settings.py @@ -190,16 +190,16 @@ class SecuritySettingSerializer(serializers.Serializer): class WeComSettingSerializer(serializers.Serializer): - WECOM_CORPID = serializers.CharField(max_length=256, required=True, label=_('Corporation ID(corpid)')) - WECOM_AGENTID = serializers.CharField(max_length=256, required=True, label=_("Agent ID(agentid)")) - WECOM_SECRET = serializers.CharField(max_length=256, required=True, label=_("Secret(secret)"), write_only=True) + WECOM_CORPID = serializers.CharField(max_length=256, required=True, label='corpid') + WECOM_AGENTID = serializers.CharField(max_length=256, required=True, label='agentid') + WECOM_SECRET = serializers.CharField(max_length=256, required=False, label='secret', write_only=True) AUTH_WECOM = serializers.BooleanField(default=False, label=_('Enable WeCom Auth')) class DingTalkSettingSerializer(serializers.Serializer): - DINGTALK_AGENTID = serializers.CharField(max_length=256, required=True, label=_("AgentId")) - DINGTALK_APPKEY = serializers.CharField(max_length=256, required=True, label=_("AppKey")) - DINGTALK_APPSECRET = serializers.CharField(max_length=256, required=False, label=_("AppSecret"), write_only=True) + DINGTALK_AGENTID = serializers.CharField(max_length=256, required=True, label='AgentId') + DINGTALK_APPKEY = serializers.CharField(max_length=256, required=True, label='AppKey') + DINGTALK_APPSECRET = serializers.CharField(max_length=256, required=False, label='AppSecret', write_only=True) AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('Enable DingTalk Auth'))