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.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)

View File

@ -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)

View File

@ -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'

View File

@ -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):

View File

@ -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)

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
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)

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\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"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\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 "无"

View File

@ -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})

View File

@ -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})

View File

@ -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'))