fix: 修复绑定企业微信&钉钉的一些问题

pull/6127/head
xinwen 2021-05-17 14:20:51 +08:00 committed by Jiangjie.Bai
parent 70055b8af2
commit 3743761024
11 changed files with 150 additions and 98 deletions

View File

@ -8,7 +8,9 @@ from django.views.generic import TemplateView
from django.views import View from django.views import View
from django.conf import settings from django.conf import settings
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.db.utils import IntegrityError
from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.exceptions import APIException
from users.views import UserVerifyPasswordView from users.views import UserVerifyPasswordView
from users.utils import is_auth_password_time_valid from users.utils import is_auth_password_time_valid
@ -29,6 +31,20 @@ DINGTALK_STATE_SESSION_KEY = '_dingtalk_state'
class DingTalkQRMixin(PermissionsMixin, View): 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): def verify_state(self):
state = self.request.GET.get('state') state = self.request.GET.get('state')
session_state = self.request.session.get(DINGTALK_STATE_SESSION_KEY) 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) response = self.get_failed_reponse(redirect_url, msg, msg)
return response return response
user.dingtalk_id = userid try:
user.save() 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') msg = _('Binding DingTalk successfully')
response = self.get_success_reponse(redirect_url, msg, msg) response = self.get_success_reponse(redirect_url, msg, msg)

View File

@ -8,7 +8,9 @@ from django.views.generic import TemplateView
from django.views import View from django.views import View
from django.conf import settings from django.conf import settings
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.db.utils import IntegrityError
from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.exceptions import APIException
from users.views import UserVerifyPasswordView from users.views import UserVerifyPasswordView
from users.utils import is_auth_password_time_valid from users.utils import is_auth_password_time_valid
@ -29,6 +31,20 @@ WECOM_STATE_SESSION_KEY = '_wecom_state'
class WeComQRMixin(PermissionsMixin, View): 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): def verify_state(self):
state = self.request.GET.get('state') state = self.request.GET.get('state')
session_state = self.request.session.get(WECOM_STATE_SESSION_KEY) 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) response = self.get_failed_reponse(redirect_url, msg, msg)
return response return response
user.wecom_id = wecom_userid try:
user.save() 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') msg = _('Binding WeCom successfully')
response = self.get_success_reponse(redirect_url, msg, msg) response = self.get_success_reponse(redirect_url, msg, msg)

View File

@ -21,8 +21,3 @@ class ResponseDataKeyError(APIException):
class NetError(APIException): class NetError(APIException):
default_code = 'net_error' default_code = 'net_error'
default_detail = _('Network error, please contact system administrator') default_detail = _('Network error, please contact system administrator')
class AccessTokenError(APIException):
default_code = 'access_token_error'
default_detail = 'Access token error, check config'

View File

@ -22,7 +22,7 @@ class RequestMixin:
logger.error(f'Response 200 but errcode is not 0: ' logger.error(f'Response 200 but errcode is not 0: '
f'errcode={errcode} ' f'errcode={errcode} '
f'errmsg={errmsg} ') 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): def check_http_is_200(self, response):
if response.status_code != 200: if response.status_code != 200:
@ -31,7 +31,7 @@ class RequestMixin:
f'status_code={response.status_code} ' f'status_code={response.status_code} '
f'url={response.url}' f'url={response.url}'
f'\ncontent={response.content}') f'\ncontent={response.content}')
raise exce.HTTPNot200 raise exce.HTTPNot200(detail=response.json())
class BaseRequest(RequestMixin): class BaseRequest(RequestMixin):

View File

@ -39,7 +39,7 @@ class DictWrapper:
except KeyError as e: except KeyError as e:
msg = f'Response 200 but get field from json error: error={e} data={self.raw_data}' msg = f'Response 200 but get field from json error: error={e} data={self.raw_data}'
logger.error(msg) logger.error(msg)
raise exce.ResponseDataKeyError(detail=msg) raise exce.ResponseDataKeyError(detail=self.raw_data)
def __getattr__(self, item): def __getattr__(self, item):
return getattr(self.raw_data, item) return getattr(self.raw_data, item)

View File

@ -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 # 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全部非法或无权限。 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 # https://open.work.weixin.qq.com/api/doc/90000/90135/91437
INVALID_CODE = 40029 INVALID_CODE = 40029
@ -141,7 +144,7 @@ class WeCom(RequestMixin):
data = self._requests.post(URL.SEND_MESSAGE, json=body, check_errcode_is_0=False) data = self._requests.post(URL.SEND_MESSAGE, json=body, check_errcode_is_0=False)
errcode = data['errcode'] errcode = data['errcode']
if errcode == ErrorCode.RECIPIENTS_INVALID: if errcode in (ErrorCode.RECIPIENTS_INVALID, ErrorCode.RECIPIENTS_EMPTY):
# 全部接收人无权限或不存在 # 全部接收人无权限或不存在
return users return users
self.check_errcode_is_0(data) self.check_errcode_is_0(data)

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-05-14 16:12+0800\n" "POT-Creation-Date: 2021-05-17 16:17+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -1547,44 +1547,48 @@ msgstr "返回"
msgid "Copy success" msgid "Copy success"
msgstr "复制成功" 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" msgid "You've been hacked"
msgstr "你被攻击了" msgstr "你被攻击了"
#: authentication/views/dingtalk.py:76 #: authentication/views/dingtalk.py:77
msgid "DingTalk is already bound" msgid "DingTalk is already bound"
msgstr "钉钉已经绑定" 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" msgid "Please verify your password first"
msgstr "请检查密码" 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" msgid "Invalid user_id"
msgstr "无效的 user_id" msgstr "无效的 user_id"
#: authentication/views/dingtalk.py:129 #: authentication/views/dingtalk.py:130
msgid "DingTalk query user failed" msgid "DingTalk query user failed"
msgstr "钉钉查询用户失败" msgstr "钉钉查询用户失败"
#: authentication/views/dingtalk.py:136 authentication/views/dingtalk.py:219 #: authentication/views/dingtalk.py:139
#: authentication/views/dingtalk.py:220 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" msgid "Binding DingTalk successfully"
msgstr "绑定 钉钉 成功" msgstr "绑定 钉钉 成功"
#: authentication/views/dingtalk.py:188 #: authentication/views/dingtalk.py:196
msgid "Failed to get user from DingTalk" msgid "Failed to get user from DingTalk"
msgstr "从钉钉获取用户失败" msgstr "从钉钉获取用户失败"
#: authentication/views/dingtalk.py:194 #: authentication/views/dingtalk.py:202
msgid "DingTalk is not bound" msgid "DingTalk is not bound"
msgstr "钉钉没有绑定" 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" msgid "Please login with a password and then bind the WeCom"
msgstr "请使用密码登录,然后绑定企业微信" 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" msgid "Binding DingTalk failed"
msgstr "绑定钉钉失败" msgstr "绑定钉钉失败"
@ -1620,28 +1624,32 @@ msgstr "退出登录成功"
msgid "Logout success, return login page" msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面" msgstr "退出登录成功,返回到登录页面"
#: authentication/views/wecom.py:75 #: authentication/views/wecom.py:76
msgid "WeCom is already bound" msgid "WeCom is already bound"
msgstr "企业微信已经绑定" msgstr "企业微信已经绑定"
#: authentication/views/wecom.py:127 #: authentication/views/wecom.py:128
msgid "WeCom query user failed" msgid "WeCom query user failed"
msgstr "企业微信查询用户失败" msgstr "企业微信查询用户失败"
#: authentication/views/wecom.py:134 authentication/views/wecom.py:217 #: authentication/views/wecom.py:137
#: authentication/views/wecom.py:218 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" msgid "Binding WeCom successfully"
msgstr "绑定 企业微信 成功" msgstr "绑定 企业微信 成功"
#: authentication/views/wecom.py:186 #: authentication/views/wecom.py:194
msgid "Failed to get user from WeCom" msgid "Failed to get user from WeCom"
msgstr "从企业微信获取用户失败" msgstr "从企业微信获取用户失败"
#: authentication/views/wecom.py:192 #: authentication/views/wecom.py:200
msgid "WeCom is not bound" msgid "WeCom is not bound"
msgstr "没有绑定企业微信" 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" msgid "Binding WeCom failed"
msgstr "绑定企业微信失败" msgstr "绑定企业微信失败"
@ -2123,7 +2131,11 @@ msgstr "邮件已经发送{}, 请检查"
msgid "Welcome to the JumpServer open source Bastion Host" msgid "Welcome to the JumpServer open source Bastion Host"
msgstr "欢迎使用JumpServer开源堡垒机" 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" msgid "OK"
msgstr "" msgstr ""
@ -2135,6 +2147,10 @@ msgstr "获取 LDAP 用户为 None"
msgid "Imported {} users successfully" msgid "Imported {} users successfully"
msgstr "导入 {} 个用户成功" msgstr "导入 {} 个用户成功"
#: settings/api/wecom.py:29
msgid "Secret is required"
msgstr "Secret 是必须的"
#: settings/models.py:123 users/templates/users/reset_password.html:29 #: settings/models.py:123 users/templates/users/reset_password.html:29
msgid "Setting" msgid "Setting"
msgstr "设置" msgstr "设置"
@ -2462,34 +2478,10 @@ msgstr "邮件收件人"
msgid "Multiple user using , split" msgid "Multiple user using , split"
msgstr "多个用户,使用 , 分割" 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 #: settings/serializers/settings.py:196
msgid "Enable WeCom Auth" msgid "Enable WeCom Auth"
msgstr "启用企业微信认证" 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 #: settings/serializers/settings.py:203
msgid "Enable DingTalk Auth" msgid "Enable DingTalk Auth"
msgstr "启用钉钉认证" msgstr "启用钉钉认证"
@ -4953,7 +4945,7 @@ msgstr "实例个数"
msgid "Periodic display" msgid "Periodic display"
msgstr "定时执行" msgstr "定时执行"
#: xpack/plugins/cloud/utils.py:65 #: xpack/plugins/cloud/utils.py:64
msgid "Account unavailable" msgid "Account unavailable"
msgstr "账户无效" msgstr "账户无效"
@ -5041,5 +5033,23 @@ msgstr "旗舰版"
msgid "Community edition" msgid "Community edition"
msgstr "社区版" 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" #~ msgid "No"
#~ msgstr "无" #~ msgstr "无"

View File

@ -1,11 +1,12 @@
import requests
from rest_framework.views import Response from rest_framework.views import Response
from rest_framework.generics import GenericAPIView 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 django.utils.translation import gettext_lazy as _
from settings.models import Setting
from common.permissions import IsSuperUser from common.permissions import IsSuperUser
from common.message.backends.dingtalk import URL from common.message.backends.dingtalk import DingTalk
from .. import serializers from .. import serializers
@ -20,19 +21,17 @@ class DingTalkTestingAPI(GenericAPIView):
dingtalk_appkey = serializer.validated_data['DINGTALK_APPKEY'] dingtalk_appkey = serializer.validated_data['DINGTALK_APPKEY']
dingtalk_agentid = serializer.validated_data['DINGTALK_AGENTID'] 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: try:
params = {'appkey': dingtalk_appkey, 'appsecret': dingtalk_appsecret} dingtalk = DingTalk(appid=dingtalk_appkey, appsecret=dingtalk_appsecret, agentid=dingtalk_agentid)
resp = requests.get(url=URL.GET_TOKEN, params=params) dingtalk.send_text(['test'], 'test')
if resp.status_code != 200: return Response(status=status.HTTP_200_OK, data={'msg': _('OK')})
return Response(status=400, data={'error': resp.json()}) except APIException as e:
return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': e.detail})
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)})

View File

@ -1,11 +1,12 @@
import requests
from rest_framework.views import Response from rest_framework.views import Response
from rest_framework.generics import GenericAPIView 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 django.utils.translation import gettext_lazy as _
from settings.models import Setting
from common.permissions import IsSuperUser from common.permissions import IsSuperUser
from common.message.backends.wecom import URL from common.message.backends.wecom import WeCom
from .. import serializers from .. import serializers
@ -20,19 +21,17 @@ class WeComTestingAPI(GenericAPIView):
wecom_corpid = serializer.validated_data['WECOM_CORPID'] wecom_corpid = serializer.validated_data['WECOM_CORPID']
wecom_agentid = serializer.validated_data['WECOM_AGENTID'] 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: try:
params = {'corpid': wecom_corpid, 'corpsecret': wecom_corpsecret} wecom = WeCom(corpid=wecom_corpid, corpsecret=wecom_corpsecret, agentid=wecom_agentid)
resp = requests.get(url=URL.GET_TOKEN, params=params) wecom.send_text(['test'], 'test')
if resp.status_code != 200: return Response(status=status.HTTP_200_OK, data={'msg': _('OK')})
return Response(status=400, data={'error': resp.json()}) except APIException as e:
return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': e.detail})
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)})

View File

@ -190,16 +190,16 @@ class SecuritySettingSerializer(serializers.Serializer):
class WeComSettingSerializer(serializers.Serializer): class WeComSettingSerializer(serializers.Serializer):
WECOM_CORPID = serializers.CharField(max_length=256, required=True, label=_('Corporation ID(corpid)')) WECOM_CORPID = serializers.CharField(max_length=256, required=True, label='corpid')
WECOM_AGENTID = serializers.CharField(max_length=256, required=True, label=_("Agent ID(agentid)")) WECOM_AGENTID = serializers.CharField(max_length=256, required=True, label='agentid')
WECOM_SECRET = serializers.CharField(max_length=256, required=True, label=_("Secret(secret)"), write_only=True) WECOM_SECRET = serializers.CharField(max_length=256, required=False, label='secret', write_only=True)
AUTH_WECOM = serializers.BooleanField(default=False, label=_('Enable WeCom Auth')) AUTH_WECOM = serializers.BooleanField(default=False, label=_('Enable WeCom Auth'))
class DingTalkSettingSerializer(serializers.Serializer): class DingTalkSettingSerializer(serializers.Serializer):
DINGTALK_AGENTID = serializers.CharField(max_length=256, required=True, label=_("AgentId")) DINGTALK_AGENTID = serializers.CharField(max_length=256, required=True, label='AgentId')
DINGTALK_APPKEY = serializers.CharField(max_length=256, required=True, label=_("AppKey")) 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_APPSECRET = serializers.CharField(max_length=256, required=False, label='AppSecret', write_only=True)
AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('Enable DingTalk Auth')) AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('Enable DingTalk Auth'))