perf: 去掉单独的flash msg (#7013)

* perf: 去掉单独的flash msg

perf: 修改使用库

* fix: guangbug

* pref: 修改 context

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: xinwen <coderWen@126.com>
This commit is contained in:
fit2bot 2021-10-18 11:25:39 +08:00 committed by GitHub
parent 63b338085a
commit fa68389028
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 177 additions and 288 deletions

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import inspect import inspect
from urllib.parse import urlencode from django.utils.http import urlencode
from functools import partial from functools import partial
import time import time
@ -204,7 +204,7 @@ class AuthMixin(PasswordEncryptionViewMixin):
data = request.POST data = request.POST
items = ['username', 'password', 'challenge', 'public_key', 'auto_login'] items = ['username', 'password', 'challenge', 'public_key', 'auto_login']
username, password, challenge, public_key, auto_login = bulk_get(data, *items, default='') username, password, challenge, public_key, auto_login = bulk_get(data, items, default='')
ip = self.get_request_ip() ip = self.get_request_ip()
self._set_partial_credential_error(username=username, ip=ip, request=request) self._set_partial_credential_error(username=username, ip=ip, request=request)

View File

@ -22,24 +22,18 @@ urlpatterns = [
path('password/reset/', users_view.UserResetPasswordView.as_view(), name='reset-password'), path('password/reset/', users_view.UserResetPasswordView.as_view(), name='reset-password'),
path('password/verify/', users_view.UserVerifyPasswordView.as_view(), name='user-verify-password'), path('password/verify/', users_view.UserVerifyPasswordView.as_view(), name='user-verify-password'),
path('wecom/bind/success-flash-msg/', views.FlashWeComBindSucceedMsgView.as_view(), name='wecom-bind-success-flash-msg'),
path('wecom/bind/failed-flash-msg/', views.FlashWeComBindFailedMsgView.as_view(), name='wecom-bind-failed-flash-msg'),
path('wecom/bind/start/', views.WeComEnableStartView.as_view(), name='wecom-bind-start'), path('wecom/bind/start/', views.WeComEnableStartView.as_view(), name='wecom-bind-start'),
path('wecom/qr/bind/', views.WeComQRBindView.as_view(), name='wecom-qr-bind'), path('wecom/qr/bind/', views.WeComQRBindView.as_view(), name='wecom-qr-bind'),
path('wecom/qr/login/', views.WeComQRLoginView.as_view(), name='wecom-qr-login'), path('wecom/qr/login/', views.WeComQRLoginView.as_view(), name='wecom-qr-login'),
path('wecom/qr/bind/<uuid:user_id>/callback/', views.WeComQRBindCallbackView.as_view(), name='wecom-qr-bind-callback'), path('wecom/qr/bind/<uuid:user_id>/callback/', views.WeComQRBindCallbackView.as_view(), name='wecom-qr-bind-callback'),
path('wecom/qr/login/callback/', views.WeComQRLoginCallbackView.as_view(), name='wecom-qr-login-callback'), path('wecom/qr/login/callback/', views.WeComQRLoginCallbackView.as_view(), name='wecom-qr-login-callback'),
path('dingtalk/bind/success-flash-msg/', views.FlashDingTalkBindSucceedMsgView.as_view(), name='dingtalk-bind-success-flash-msg'),
path('dingtalk/bind/failed-flash-msg/', views.FlashDingTalkBindFailedMsgView.as_view(), name='dingtalk-bind-failed-flash-msg'),
path('dingtalk/bind/start/', views.DingTalkEnableStartView.as_view(), name='dingtalk-bind-start'), path('dingtalk/bind/start/', views.DingTalkEnableStartView.as_view(), name='dingtalk-bind-start'),
path('dingtalk/qr/bind/', views.DingTalkQRBindView.as_view(), name='dingtalk-qr-bind'), path('dingtalk/qr/bind/', views.DingTalkQRBindView.as_view(), name='dingtalk-qr-bind'),
path('dingtalk/qr/login/', views.DingTalkQRLoginView.as_view(), name='dingtalk-qr-login'), path('dingtalk/qr/login/', views.DingTalkQRLoginView.as_view(), name='dingtalk-qr-login'),
path('dingtalk/qr/bind/<uuid:user_id>/callback/', views.DingTalkQRBindCallbackView.as_view(), name='dingtalk-qr-bind-callback'), path('dingtalk/qr/bind/<uuid:user_id>/callback/', views.DingTalkQRBindCallbackView.as_view(), name='dingtalk-qr-bind-callback'),
path('dingtalk/qr/login/callback/', views.DingTalkQRLoginCallbackView.as_view(), name='dingtalk-qr-login-callback'), path('dingtalk/qr/login/callback/', views.DingTalkQRLoginCallbackView.as_view(), name='dingtalk-qr-login-callback'),
path('feishu/bind/success-flash-msg/', views.FlashDingTalkBindSucceedMsgView.as_view(), name='feishu-bind-success-flash-msg'),
path('feishu/bind/failed-flash-msg/', views.FlashDingTalkBindFailedMsgView.as_view(), name='feishu-bind-failed-flash-msg'),
path('feishu/bind/start/', views.FeiShuEnableStartView.as_view(), name='feishu-bind-start'), path('feishu/bind/start/', views.FeiShuEnableStartView.as_view(), name='feishu-bind-start'),
path('feishu/qr/bind/', views.FeiShuQRBindView.as_view(), name='feishu-qr-bind'), path('feishu/qr/bind/', views.FeiShuQRBindView.as_view(), name='feishu-qr-bind'),
path('feishu/qr/login/', views.FeiShuQRLoginView.as_view(), name='feishu-qr-login'), path('feishu/qr/login/', views.FeiShuQRLoginView.as_view(), name='feishu-qr-login'),

View File

@ -1,10 +1,6 @@
import urllib
from django.http.response import HttpResponseRedirect from django.http.response import HttpResponseRedirect
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.cache import never_cache from urllib.parse import urlencode
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
@ -15,7 +11,7 @@ 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
from users.models import User from users.models import User
from common.utils import get_logger from common.utils import get_logger, FlashMessageUtil
from common.utils.random import random_string from common.utils.random import random_string
from common.utils.django import reverse, get_object_or_none from common.utils.django import reverse, get_object_or_none
from common.message.backends.dingtalk import URL from common.message.backends.dingtalk import URL
@ -39,7 +35,7 @@ class DingTalkQRMixin(PermissionsMixin, View):
msg = e.detail['errmsg'] msg = e.detail['errmsg']
except Exception: except Exception:
msg = _('DingTalk Error, Please contact your system administrator') msg = _('DingTalk Error, Please contact your system administrator')
return self.get_failed_reponse( return self.get_failed_response(
'/', '/',
_('DingTalk Error'), _('DingTalk Error'),
msg msg
@ -67,30 +63,32 @@ class DingTalkQRMixin(PermissionsMixin, View):
'state': state, 'state': state,
'redirect_uri': redirect_uri, 'redirect_uri': redirect_uri,
} }
url = URL.QR_CONNECT + '?' + urllib.parse.urlencode(params) url = URL.QR_CONNECT + '?' + urlencode(params)
return url return url
def get_success_reponse(self, redirect_url, title, msg): @staticmethod
ok_flash_msg_url = reverse('authentication:dingtalk-bind-success-flash-msg') def get_success_response(redirect_url, title, msg):
ok_flash_msg_url += '?' + urllib.parse.urlencode({ message_data = {
'redirect_url': redirect_url,
'title': title, 'title': title,
'msg': msg 'message': msg,
}) 'interval': 5,
return HttpResponseRedirect(ok_flash_msg_url) 'redirect_url': redirect_url,
}
return FlashMessageUtil.gen_and_redirect_to(message_data)
def get_failed_reponse(self, redirect_url, title, msg): @staticmethod
failed_flash_msg_url = reverse('authentication:dingtalk-bind-failed-flash-msg') def get_failed_response(redirect_url, title, msg):
failed_flash_msg_url += '?' + urllib.parse.urlencode({ message_data = {
'redirect_url': redirect_url,
'title': title, 'title': title,
'msg': msg 'error': msg,
}) 'interval': 5,
return HttpResponseRedirect(failed_flash_msg_url) 'redirect_url': redirect_url,
}
return FlashMessageUtil.gen_and_redirect_to(message_data)
def get_already_bound_response(self, redirect_url): def get_already_bound_response(self, redirect_url):
msg = _('DingTalk is already bound') msg = _('DingTalk is already bound')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
@ -103,11 +101,11 @@ class DingTalkQRBindView(DingTalkQRMixin, View):
if not is_auth_password_time_valid(request.session): if not is_auth_password_time_valid(request.session):
msg = _('Please verify your password first') msg = _('Please verify your password first')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
redirect_uri = reverse('authentication:dingtalk-qr-bind-callback', kwargs={'user_id': user.id}, external=True) redirect_uri = reverse('authentication:dingtalk-qr-bind-callback', kwargs={'user_id': user.id}, external=True)
redirect_uri += '?' + urllib.parse.urlencode({'redirect_url': redirect_url}) redirect_uri += '?' + urlencode({'redirect_url': redirect_url})
url = self.get_qr_url(redirect_uri) url = self.get_qr_url(redirect_uri)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -127,7 +125,7 @@ class DingTalkQRBindCallbackView(DingTalkQRMixin, View):
if user is None: if user is None:
logger.error(f'DingTalkQR bind callback error, user_id invalid: user_id={user_id}') logger.error(f'DingTalkQR bind callback error, user_id invalid: user_id={user_id}')
msg = _('Invalid user_id') msg = _('Invalid user_id')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
if user.dingtalk_id: if user.dingtalk_id:
@ -143,7 +141,7 @@ class DingTalkQRBindCallbackView(DingTalkQRMixin, View):
if not userid: if not userid:
msg = _('DingTalk query user failed') msg = _('DingTalk query user failed')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
try: try:
@ -152,12 +150,12 @@ class DingTalkQRBindCallbackView(DingTalkQRMixin, View):
except IntegrityError as e: except IntegrityError as e:
if e.args[0] == 1062: if e.args[0] == 1062:
msg = _('The DingTalk is already bound to another user') msg = _('The DingTalk is already bound to another user')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
raise e raise e
msg = _('Binding DingTalk successfully') msg = _('Binding DingTalk successfully')
response = self.get_success_reponse(redirect_url, msg, msg) response = self.get_success_response(redirect_url, msg, msg)
return response return response
@ -169,7 +167,7 @@ class DingTalkEnableStartView(UserVerifyPasswordView):
success_url = reverse('authentication:dingtalk-qr-bind') success_url = reverse('authentication:dingtalk-qr-bind')
success_url += '?' + urllib.parse.urlencode({ success_url += '?' + urlencode({
'redirect_url': redirect_url or referer 'redirect_url': redirect_url or referer
}) })
@ -183,7 +181,7 @@ class DingTalkQRLoginView(DingTalkQRMixin, View):
redirect_url = request.GET.get('redirect_url') redirect_url = request.GET.get('redirect_url')
redirect_uri = reverse('authentication:dingtalk-qr-login-callback', external=True) redirect_uri = reverse('authentication:dingtalk-qr-login-callback', external=True)
redirect_uri += '?' + urllib.parse.urlencode({'redirect_url': redirect_url}) redirect_uri += '?' + urlencode({'redirect_url': redirect_url})
url = self.get_qr_url(redirect_uri) url = self.get_qr_url(redirect_uri)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -209,14 +207,14 @@ class DingTalkQRLoginCallbackView(AuthMixin, DingTalkQRMixin, View):
if not userid: if not userid:
# 正常流程不会出这个错误hack 行为 # 正常流程不会出这个错误hack 行为
msg = _('Failed to get user from DingTalk') msg = _('Failed to get user from DingTalk')
response = self.get_failed_reponse(login_url, title=msg, msg=msg) response = self.get_failed_response(login_url, title=msg, msg=msg)
return response return response
user = get_object_or_none(User, dingtalk_id=userid) user = get_object_or_none(User, dingtalk_id=userid)
if user is None: if user is None:
title = _('DingTalk is not bound') title = _('DingTalk is not bound')
msg = _('Please login with a password and then bind the DingTalk') msg = _('Please login with a password and then bind the DingTalk')
response = self.get_failed_reponse(login_url, title=title, msg=msg) response = self.get_failed_response(login_url, title=title, msg=msg)
return response return response
try: try:
@ -224,43 +222,7 @@ class DingTalkQRLoginCallbackView(AuthMixin, DingTalkQRMixin, View):
except errors.AuthFailedError as e: except errors.AuthFailedError as e:
self.set_login_failed_mark() self.set_login_failed_mark()
msg = e.msg msg = e.msg
response = self.get_failed_reponse(login_url, title=msg, msg=msg) response = self.get_failed_response(login_url, title=msg, msg=msg)
return response return response
return self.redirect_to_guard_view() return self.redirect_to_guard_view()
@method_decorator(never_cache, name='dispatch')
class FlashDingTalkBindSucceedMsgView(TemplateView):
template_name = 'flash_message_standalone.html'
def get(self, request, *args, **kwargs):
title = request.GET.get('title')
msg = request.GET.get('msg')
context = {
'title': title or _('Binding DingTalk successfully'),
'messages': msg or _('Binding DingTalk successfully'),
'interval': 5,
'redirect_url': request.GET.get('redirect_url'),
'auto_redirect': True,
}
return self.render_to_response(context)
@method_decorator(never_cache, name='dispatch')
class FlashDingTalkBindFailedMsgView(TemplateView):
template_name = 'flash_message_standalone.html'
def get(self, request, *args, **kwargs):
title = request.GET.get('title')
msg = request.GET.get('msg')
context = {
'title': title or _('Binding DingTalk failed'),
'messages': msg or _('Binding DingTalk failed'),
'interval': 5,
'redirect_url': request.GET.get('redirect_url'),
'auto_redirect': True,
}
return self.render_to_response(context)

View File

@ -1,10 +1,6 @@
import urllib from django.http.response import HttpResponseRedirect
from django.http.response import HttpResponseRedirect, HttpResponse
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.cache import never_cache from urllib.parse import urlencode
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
@ -15,7 +11,7 @@ from rest_framework.exceptions import APIException
from users.utils import is_auth_password_time_valid from users.utils import is_auth_password_time_valid
from users.views import UserVerifyPasswordView from users.views import UserVerifyPasswordView
from users.models import User from users.models import User
from common.utils import get_logger from common.utils import get_logger, FlashMessageUtil
from common.utils.random import random_string from common.utils.random import random_string
from common.utils.django import reverse, get_object_or_none from common.utils.django import reverse, get_object_or_none
from common.mixins.views import PermissionsMixin from common.mixins.views import PermissionsMixin
@ -35,7 +31,7 @@ class FeiShuQRMixin(PermissionsMixin, View):
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
except APIException as e: except APIException as e:
msg = str(e.detail) msg = str(e.detail)
return self.get_failed_reponse( return self.get_failed_response(
'/', '/',
_('FeiShu Error'), _('FeiShu Error'),
msg msg
@ -50,7 +46,7 @@ class FeiShuQRMixin(PermissionsMixin, View):
def get_verify_state_failed_response(self, redirect_uri): def get_verify_state_failed_response(self, redirect_uri):
msg = _("The system configuration is incorrect. Please contact your administrator") msg = _("The system configuration is incorrect. Please contact your administrator")
return self.get_failed_reponse(redirect_uri, msg, msg) return self.get_failed_response(redirect_uri, msg, msg)
def get_qr_url(self, redirect_uri): def get_qr_url(self, redirect_uri):
state = random_string(16) state = random_string(16)
@ -61,30 +57,32 @@ class FeiShuQRMixin(PermissionsMixin, View):
'state': state, 'state': state,
'redirect_uri': redirect_uri, 'redirect_uri': redirect_uri,
} }
url = URL.AUTHEN + '?' + urllib.parse.urlencode(params) url = URL.AUTHEN + '?' + urlencode(params)
return url return url
def get_success_reponse(self, redirect_url, title, msg): @staticmethod
ok_flash_msg_url = reverse('authentication:feishu-bind-success-flash-msg') def get_success_response(redirect_url, title, msg):
ok_flash_msg_url += '?' + urllib.parse.urlencode({ message_data = {
'redirect_url': redirect_url,
'title': title, 'title': title,
'msg': msg 'message': msg,
}) 'interval': 5,
return HttpResponseRedirect(ok_flash_msg_url) 'redirect_url': redirect_url,
}
return FlashMessageUtil.gen_and_redirect_to(message_data)
def get_failed_reponse(self, redirect_url, title, msg): @staticmethod
failed_flash_msg_url = reverse('authentication:feishu-bind-failed-flash-msg') def get_failed_response(redirect_url, title, msg):
failed_flash_msg_url += '?' + urllib.parse.urlencode({ message_data = {
'redirect_url': redirect_url,
'title': title, 'title': title,
'msg': msg 'error': msg,
}) 'interval': 5,
return HttpResponseRedirect(failed_flash_msg_url) 'redirect_url': redirect_url,
}
return FlashMessageUtil.gen_and_redirect_to(message_data)
def get_already_bound_response(self, redirect_url): def get_already_bound_response(self, redirect_url):
msg = _('FeiShu is already bound') msg = _('FeiShu is already bound')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
@ -97,11 +95,11 @@ class FeiShuQRBindView(FeiShuQRMixin, View):
if not is_auth_password_time_valid(request.session): if not is_auth_password_time_valid(request.session):
msg = _('Please verify your password first') msg = _('Please verify your password first')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
redirect_uri = reverse('authentication:feishu-qr-bind-callback', external=True) redirect_uri = reverse('authentication:feishu-qr-bind-callback', external=True)
redirect_uri += '?' + urllib.parse.urlencode({'redirect_url': redirect_url}) redirect_uri += '?' + urlencode({'redirect_url': redirect_url})
url = self.get_qr_url(redirect_uri) url = self.get_qr_url(redirect_uri)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -131,7 +129,7 @@ class FeiShuQRBindCallbackView(FeiShuQRMixin, View):
if not user_id: if not user_id:
msg = _('FeiShu query user failed') msg = _('FeiShu query user failed')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
try: try:
@ -140,12 +138,12 @@ class FeiShuQRBindCallbackView(FeiShuQRMixin, View):
except IntegrityError as e: except IntegrityError as e:
if e.args[0] == 1062: if e.args[0] == 1062:
msg = _('The FeiShu is already bound to another user') msg = _('The FeiShu is already bound to another user')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
raise e raise e
msg = _('Binding FeiShu successfully') msg = _('Binding FeiShu successfully')
response = self.get_success_reponse(redirect_url, msg, msg) response = self.get_success_response(redirect_url, msg, msg)
return response return response
@ -157,7 +155,7 @@ class FeiShuEnableStartView(UserVerifyPasswordView):
success_url = reverse('authentication:feishu-qr-bind') success_url = reverse('authentication:feishu-qr-bind')
success_url += '?' + urllib.parse.urlencode({ success_url += '?' + urlencode({
'redirect_url': redirect_url or referer 'redirect_url': redirect_url or referer
}) })
@ -171,7 +169,7 @@ class FeiShuQRLoginView(FeiShuQRMixin, View):
redirect_url = request.GET.get('redirect_url') redirect_url = request.GET.get('redirect_url')
redirect_uri = reverse('authentication:feishu-qr-login-callback', external=True) redirect_uri = reverse('authentication:feishu-qr-login-callback', external=True)
redirect_uri += '?' + urllib.parse.urlencode({'redirect_url': redirect_url}) redirect_uri += '?' + urlencode({'redirect_url': redirect_url})
url = self.get_qr_url(redirect_uri) url = self.get_qr_url(redirect_uri)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -196,14 +194,14 @@ class FeiShuQRLoginCallbackView(AuthMixin, FeiShuQRMixin, View):
if not user_id: if not user_id:
# 正常流程不会出这个错误hack 行为 # 正常流程不会出这个错误hack 行为
msg = _('Failed to get user from FeiShu') msg = _('Failed to get user from FeiShu')
response = self.get_failed_reponse(login_url, title=msg, msg=msg) response = self.get_failed_response(login_url, title=msg, msg=msg)
return response return response
user = get_object_or_none(User, feishu_id=user_id) user = get_object_or_none(User, feishu_id=user_id)
if user is None: if user is None:
title = _('FeiShu is not bound') title = _('FeiShu is not bound')
msg = _('Please login with a password and then bind the FeiShu') msg = _('Please login with a password and then bind the FeiShu')
response = self.get_failed_reponse(login_url, title=title, msg=msg) response = self.get_failed_response(login_url, title=title, msg=msg)
return response return response
try: try:
@ -211,43 +209,7 @@ class FeiShuQRLoginCallbackView(AuthMixin, FeiShuQRMixin, View):
except errors.AuthFailedError as e: except errors.AuthFailedError as e:
self.set_login_failed_mark() self.set_login_failed_mark()
msg = e.msg msg = e.msg
response = self.get_failed_reponse(login_url, title=msg, msg=msg) response = self.get_failed_response(login_url, title=msg, msg=msg)
return response return response
return self.redirect_to_guard_view() return self.redirect_to_guard_view()
@method_decorator(never_cache, name='dispatch')
class FlashFeiShuBindSucceedMsgView(TemplateView):
template_name = 'flash_message_standalone.html'
def get(self, request, *args, **kwargs):
title = request.GET.get('title')
msg = request.GET.get('msg')
context = {
'title': title or _('Binding FeiShu successfully'),
'messages': msg or _('Binding FeiShu successfully'),
'interval': 5,
'redirect_url': request.GET.get('redirect_url'),
'auto_redirect': True,
}
return self.render_to_response(context)
@method_decorator(never_cache, name='dispatch')
class FlashFeiShuBindFailedMsgView(TemplateView):
template_name = 'flash_message_standalone.html'
def get(self, request, *args, **kwargs):
title = request.GET.get('title')
msg = request.GET.get('msg')
context = {
'title': title or _('Binding FeiShu failed'),
'messages': msg or _('Binding FeiShu failed'),
'interval': 5,
'redirect_url': request.GET.get('redirect_url'),
'auto_redirect': True,
}
return self.render_to_response(context)

View File

@ -1,10 +1,6 @@
import urllib
from django.http.response import HttpResponseRedirect from django.http.response import HttpResponseRedirect
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.cache import never_cache from urllib.parse import urlencode
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
@ -15,7 +11,7 @@ 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
from users.models import User from users.models import User
from common.utils import get_logger from common.utils import get_logger, FlashMessageUtil
from common.utils.random import random_string from common.utils.random import random_string
from common.utils.django import reverse, get_object_or_none from common.utils.django import reverse, get_object_or_none
from common.message.backends.wecom import URL from common.message.backends.wecom import URL
@ -39,7 +35,7 @@ class WeComQRMixin(PermissionsMixin, View):
msg = e.detail['errmsg'] msg = e.detail['errmsg']
except Exception: except Exception:
msg = _('WeCom Error, Please contact your system administrator') msg = _('WeCom Error, Please contact your system administrator')
return self.get_failed_reponse( return self.get_failed_response(
'/', '/',
_('WeCom Error'), _('WeCom Error'),
msg msg
@ -54,7 +50,7 @@ class WeComQRMixin(PermissionsMixin, View):
def get_verify_state_failed_response(self, redirect_uri): def get_verify_state_failed_response(self, redirect_uri):
msg = _("The system configuration is incorrect. Please contact your administrator") msg = _("The system configuration is incorrect. Please contact your administrator")
return self.get_failed_reponse(redirect_uri, msg, msg) return self.get_failed_response(redirect_uri, msg, msg)
def get_qr_url(self, redirect_uri): def get_qr_url(self, redirect_uri):
state = random_string(16) state = random_string(16)
@ -66,30 +62,32 @@ class WeComQRMixin(PermissionsMixin, View):
'state': state, 'state': state,
'redirect_uri': redirect_uri, 'redirect_uri': redirect_uri,
} }
url = URL.QR_CONNECT + '?' + urllib.parse.urlencode(params) url = URL.QR_CONNECT + '?' + urlencode(params)
return url return url
def get_success_reponse(self, redirect_url, title, msg): @staticmethod
ok_flash_msg_url = reverse('authentication:wecom-bind-success-flash-msg') def get_success_response(redirect_url, title, msg):
ok_flash_msg_url += '?' + urllib.parse.urlencode({ message_data = {
'redirect_url': redirect_url,
'title': title, 'title': title,
'msg': msg 'message': msg,
}) 'interval': 5,
return HttpResponseRedirect(ok_flash_msg_url) 'redirect_url': redirect_url,
}
return FlashMessageUtil.gen_and_redirect_to(message_data)
def get_failed_reponse(self, redirect_url, title, msg): @staticmethod
failed_flash_msg_url = reverse('authentication:wecom-bind-failed-flash-msg') def get_failed_response(redirect_url, title, msg):
failed_flash_msg_url += '?' + urllib.parse.urlencode({ message_data = {
'redirect_url': redirect_url,
'title': title, 'title': title,
'msg': msg 'error': msg,
}) 'interval': 5,
return HttpResponseRedirect(failed_flash_msg_url) 'redirect_url': redirect_url,
}
return FlashMessageUtil.gen_and_redirect_to(message_data)
def get_already_bound_response(self, redirect_url): def get_already_bound_response(self, redirect_url):
msg = _('WeCom is already bound') msg = _('WeCom is already bound')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
@ -102,11 +100,11 @@ class WeComQRBindView(WeComQRMixin, View):
if not is_auth_password_time_valid(request.session): if not is_auth_password_time_valid(request.session):
msg = _('Please verify your password first') msg = _('Please verify your password first')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
redirect_uri = reverse('authentication:wecom-qr-bind-callback', kwargs={'user_id': user.id}, external=True) redirect_uri = reverse('authentication:wecom-qr-bind-callback', kwargs={'user_id': user.id}, external=True)
redirect_uri += '?' + urllib.parse.urlencode({'redirect_url': redirect_url}) redirect_uri += '?' + urlencode({'redirect_url': redirect_url})
url = self.get_qr_url(redirect_uri) url = self.get_qr_url(redirect_uri)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -126,7 +124,7 @@ class WeComQRBindCallbackView(WeComQRMixin, View):
if user is None: if user is None:
logger.error(f'WeComQR bind callback error, user_id invalid: user_id={user_id}') logger.error(f'WeComQR bind callback error, user_id invalid: user_id={user_id}')
msg = _('Invalid user_id') msg = _('Invalid user_id')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
if user.wecom_id: if user.wecom_id:
@ -141,7 +139,7 @@ class WeComQRBindCallbackView(WeComQRMixin, View):
wecom_userid, __ = wecom.get_user_id_by_code(code) wecom_userid, __ = wecom.get_user_id_by_code(code)
if not wecom_userid: if not wecom_userid:
msg = _('WeCom query user failed') msg = _('WeCom query user failed')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
try: try:
@ -150,27 +148,24 @@ class WeComQRBindCallbackView(WeComQRMixin, View):
except IntegrityError as e: except IntegrityError as e:
if e.args[0] == 1062: if e.args[0] == 1062:
msg = _('The WeCom is already bound to another user') msg = _('The WeCom is already bound to another user')
response = self.get_failed_reponse(redirect_url, msg, msg) response = self.get_failed_response(redirect_url, msg, msg)
return response return response
raise e raise e
msg = _('Binding WeCom successfully') msg = _('Binding WeCom successfully')
response = self.get_success_reponse(redirect_url, msg, msg) response = self.get_success_response(redirect_url, msg, msg)
return response return response
class WeComEnableStartView(UserVerifyPasswordView): class WeComEnableStartView(UserVerifyPasswordView):
def get_success_url(self): def get_success_url(self):
referer = self.request.META.get('HTTP_REFERER') referer = self.request.META.get('HTTP_REFERER')
redirect_url = self.request.GET.get("redirect_url") redirect_url = self.request.GET.get("redirect_url")
success_url = reverse('authentication:wecom-qr-bind') success_url = reverse('authentication:wecom-qr-bind')
success_url += '?' + urlencode({
success_url += '?' + urllib.parse.urlencode({
'redirect_url': redirect_url or referer 'redirect_url': redirect_url or referer
}) })
return success_url return success_url
@ -181,7 +176,7 @@ class WeComQRLoginView(WeComQRMixin, View):
redirect_url = request.GET.get('redirect_url') redirect_url = request.GET.get('redirect_url')
redirect_uri = reverse('authentication:wecom-qr-login-callback', external=True) redirect_uri = reverse('authentication:wecom-qr-login-callback', external=True)
redirect_uri += '?' + urllib.parse.urlencode({'redirect_url': redirect_url}) redirect_uri += '?' + urlencode({'redirect_url': redirect_url})
url = self.get_qr_url(redirect_uri) url = self.get_qr_url(redirect_uri)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@ -207,14 +202,14 @@ class WeComQRLoginCallbackView(AuthMixin, WeComQRMixin, View):
if not wecom_userid: if not wecom_userid:
# 正常流程不会出这个错误hack 行为 # 正常流程不会出这个错误hack 行为
msg = _('Failed to get user from WeCom') msg = _('Failed to get user from WeCom')
response = self.get_failed_reponse(login_url, title=msg, msg=msg) response = self.get_failed_response(login_url, title=msg, msg=msg)
return response return response
user = get_object_or_none(User, wecom_id=wecom_userid) user = get_object_or_none(User, wecom_id=wecom_userid)
if user is None: if user is None:
title = _('WeCom is not bound') title = _('WeCom is not bound')
msg = _('Please login with a password and then bind the WeCom') msg = _('Please login with a password and then bind the WeCom')
response = self.get_failed_reponse(login_url, title=title, msg=msg) response = self.get_failed_response(login_url, title=title, msg=msg)
return response return response
try: try:
@ -222,43 +217,7 @@ class WeComQRLoginCallbackView(AuthMixin, WeComQRMixin, View):
except errors.AuthFailedError as e: except errors.AuthFailedError as e:
self.set_login_failed_mark() self.set_login_failed_mark()
msg = e.msg msg = e.msg
response = self.get_failed_reponse(login_url, title=msg, msg=msg) response = self.get_failed_response(login_url, title=msg, msg=msg)
return response return response
return self.redirect_to_guard_view() return self.redirect_to_guard_view()
@method_decorator(never_cache, name='dispatch')
class FlashWeComBindSucceedMsgView(TemplateView):
template_name = 'flash_message_standalone.html'
def get(self, request, *args, **kwargs):
title = request.GET.get('title')
msg = request.GET.get('msg')
context = {
'title': title or _('Binding WeCom successfully'),
'messages': msg or _('Binding WeCom successfully'),
'interval': 5,
'redirect_url': request.GET.get('redirect_url'),
'auto_redirect': True,
}
return self.render_to_response(context)
@method_decorator(never_cache, name='dispatch')
class FlashWeComBindFailedMsgView(TemplateView):
template_name = 'flash_message_standalone.html'
def get(self, request, *args, **kwargs):
title = request.GET.get('title')
msg = request.GET.get('msg')
context = {
'title': title or _('Binding WeCom failed'),
'messages': msg or _('Binding WeCom failed'),
'interval': 5,
'redirect_url': request.GET.get('redirect_url'),
'auto_redirect': True,
}
return self.render_to_response(context)

View File

@ -14,7 +14,8 @@ def sign(secret, data):
digest = hmac.HMAC( digest = hmac.HMAC(
key=secret.encode('utf8'), key=secret.encode('utf8'),
msg=data.encode('utf8'), msg=data.encode('utf8'),
digestmod=hmac._hashlib.sha256).digest() digestmod=hmac._hashlib.sha256
).digest()
signature = base64.standard_b64encode(digest).decode('utf8') signature = base64.standard_b64encode(digest).decode('utf8')
# signature = urllib.parse.quote(signature, safe='') # signature = urllib.parse.quote(signature, safe='')
# signature = signature.replace('+', '%20').replace('*', '%2A').replace('~', '%7E').replace('/', '%2F') # signature = signature.replace('+', '%20').replace('*', '%2A').replace('~', '%7E').replace('/', '%2F')
@ -39,9 +40,9 @@ class DingTalkRequests(BaseRequest):
invalid_token_errcodes = (ErrorCode.INVALID_TOKEN,) invalid_token_errcodes = (ErrorCode.INVALID_TOKEN,)
def __init__(self, appid, appsecret, agentid, timeout=None): def __init__(self, appid, appsecret, agentid, timeout=None):
self._appid = appid self._appid = appid or ''
self._appsecret = appsecret self._appsecret = appsecret or ''
self._agentid = agentid self._agentid = agentid or ''
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
@ -74,7 +75,7 @@ class DingTalkRequests(BaseRequest):
def post(self, url, json=None, params=None, def post(self, url, json=None, params=None,
with_token=False, with_sign=False, with_token=False, with_sign=False,
check_errcode_is_0=True, check_errcode_is_0=True,
**kwargs): **kwargs) -> dict:
pass pass
post = as_request(post) post = as_request(post)
@ -86,11 +87,10 @@ class DingTalkRequests(BaseRequest):
timestamp = str(int(time.time() * 1000)) timestamp = str(int(time.time() * 1000))
signature = sign(self._appsecret, timestamp) signature = sign(self._appsecret, timestamp)
accessKey = self._appid
params['timestamp'] = timestamp params['timestamp'] = timestamp
params['signature'] = signature params['signature'] = signature
params['accessKey'] = accessKey params['accessKey'] = self._appid
def request(self, method, url, def request(self, method, url,
with_token=False, with_sign=False, with_token=False, with_sign=False,
@ -102,15 +102,16 @@ class DingTalkRequests(BaseRequest):
data = super().request( data = super().request(
method, url, with_token=with_token, method, url, with_token=with_token,
check_errcode_is_0=check_errcode_is_0, **kwargs) check_errcode_is_0=check_errcode_is_0, **kwargs
)
return data return data
class DingTalk: class DingTalk:
def __init__(self, appid, appsecret, agentid, timeout=None): def __init__(self, appid, appsecret, agentid, timeout=None):
self._appid = appid self._appid = appid or ''
self._appsecret = appsecret self._appsecret = appsecret or ''
self._agentid = agentid self._agentid = agentid or ''
self._request = DingTalkRequests( self._request = DingTalkRequests(
appid=appid, appsecret=appsecret, agentid=agentid, appid=appid, appsecret=appsecret, agentid=agentid,

View File

@ -69,8 +69,8 @@ class FeiShu(RequestMixin):
""" """
def __init__(self, app_id, app_secret, timeout=None): def __init__(self, app_id, app_secret, timeout=None):
self._app_id = app_id self._app_id = app_id or ''
self._app_secret = app_secret self._app_secret = app_secret or ''
self._requests = FeishuRequests( self._requests = FeishuRequests(
app_id=app_id, app_id=app_id,

View File

@ -8,12 +8,12 @@ from common.message.backends import exceptions as exce
logger = get_logger(__name__) logger = get_logger(__name__)
def digest(corpid, corpsecret): def digest(corp_id, corp_secret):
md5 = hashlib.md5() md5 = hashlib.md5()
md5.update(corpid.encode()) md5.update(corp_id.encode())
md5.update(corpsecret.encode()) md5.update(corp_secret.encode())
digest = md5.hexdigest() dist = md5.hexdigest()
return digest return dist
def update_values(default: dict, others: dict): def update_values(default: dict, others: dict):

View File

@ -47,9 +47,9 @@ class WeComRequests(BaseRequest):
invalid_token_errcodes = (ErrorCode.INVALID_TOKEN,) invalid_token_errcodes = (ErrorCode.INVALID_TOKEN,)
def __init__(self, corpid, corpsecret, agentid, timeout=None): def __init__(self, corpid, corpsecret, agentid, timeout=None):
self._corpid = corpid self._corpid = corpid or ''
self._corpsecret = corpsecret self._corpsecret = corpsecret or ''
self._agentid = agentid self._agentid = agentid or ''
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
@ -79,9 +79,9 @@ class WeCom(RequestMixin):
""" """
def __init__(self, corpid, corpsecret, agentid, timeout=None): def __init__(self, corpid, corpsecret, agentid, timeout=None):
self._corpid = corpid self._corpid = corpid or ''
self._corpsecret = corpsecret self._corpsecret = corpsecret or ''
self._agentid = agentid self._agentid = agentid or ''
self._requests = WeComRequests( self._requests = WeComRequests(
corpid=corpid, corpid=corpid,

View File

@ -275,7 +275,7 @@ class Time:
last = timestamp last = timestamp
def bulk_get(d, *keys, default=None): def bulk_get(d, keys, default=None):
values = [] values = []
for key in keys: for key in keys:
values.append(d.get(key, default)) values.append(d.get(key, default))

View File

@ -1,5 +1,6 @@
from django.core.cache import cache from django.core.cache import cache
from django.shortcuts import reverse from django.shortcuts import reverse
from django.shortcuts import redirect
from .random import random_string from .random import random_string
@ -8,6 +9,17 @@ __all__ = ['FlashMessageUtil']
class FlashMessageUtil: class FlashMessageUtil:
"""
跳转到通用msg页面
message_data: {
'title': '',
'message': '',
'error': '',
'redirect_url': '',
'confirm_button': '',
'cancel_url': ''
}
"""
@staticmethod @staticmethod
def get_key(code): def get_key(code):
key = 'MESSAGE_{}'.format(code) key = 'MESSAGE_{}'.format(code)
@ -29,3 +41,8 @@ class FlashMessageUtil:
def gen_message_url(cls, message_data): def gen_message_url(cls, message_data):
code = cls.get_message_code(message_data) code = cls.get_message_code(message_data)
return reverse('common:flash-message') + f'?code={code}' return reverse('common:flash-message') + f'?code={code}'
@classmethod
def gen_and_redirect_to(cls, message_data):
url = cls.gen_message_url(message_data)
return redirect(url)

View File

@ -20,21 +20,21 @@ class FlashMessageMsgView(TemplateView):
if not message_data: if not message_data:
return HttpResponse('Message code error') return HttpResponse('Message code error')
title, message, redirect_url, confirm_button, cancel_url = bulk_get( items = ('title', 'message', 'error', 'redirect_url', 'confirm_button', 'cancel_url')
message_data, 'title', 'message', 'redirect_url', 'confirm_button', 'cancel_url' title, msg, error, redirect_url, confirm_btn, cancel_url = bulk_get(message_data, items)
)
interval = message_data.get('interval', 3) interval = message_data.get('interval', 3)
auto_redirect = message_data.get('auto_redirect', True) auto_redirect = message_data.get('auto_redirect', True)
has_cancel = message_data.get('has_cancel', False) has_cancel = message_data.get('has_cancel', False)
context = { context = {
'title': title, 'title': title,
'messages': message, 'message': msg,
'error': error,
'interval': interval, 'interval': interval,
'redirect_url': redirect_url, 'redirect_url': redirect_url,
'auto_redirect': auto_redirect, 'auto_redirect': auto_redirect,
'confirm_button': confirm_button, 'confirm_button': confirm_btn,
'has_cancel': has_cancel, 'has_cancel': has_cancel,
'cancel_url': cancel_url, 'cancel_url': cancel_url,
} }
return self.render_to_response(context) return self.render_to_response(context)

View File

@ -4,7 +4,7 @@ from rest_framework.exceptions import APIException
from rest_framework import status 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 django.conf import settings
from common.permissions import IsSuperUser from common.permissions import IsSuperUser
from common.message.backends.dingtalk import DingTalk from common.message.backends.dingtalk import DingTalk
@ -19,24 +19,19 @@ class DingTalkTestingAPI(GenericAPIView):
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
dingtalk_appkey = serializer.validated_data['DINGTALK_APPKEY'] app_key = serializer.validated_data['DINGTALK_APPKEY']
dingtalk_agentid = serializer.validated_data['DINGTALK_AGENTID'] agent_id = serializer.validated_data['DINGTALK_AGENTID']
dingtalk_appsecret = serializer.validated_data.get('DINGTALK_APPSECRET') app_secret = serializer.validated_data.get('DINGTALK_APPSECRET') \
or settings.DINGTALK_APPSECRET \
if not dingtalk_appsecret: or ''
secret = Setting.objects.filter(name='DINGTALK_APPSECRET').first()
if secret:
dingtalk_appsecret = secret.cleaned_value
dingtalk_appsecret = dingtalk_appsecret or ''
try: try:
dingtalk = DingTalk(appid=dingtalk_appkey, appsecret=dingtalk_appsecret, agentid=dingtalk_agentid) dingtalk = DingTalk(appid=app_key, appsecret=app_secret, agentid=agent_id)
dingtalk.send_text(['test'], 'test') dingtalk.send_text(['test'], 'test')
return Response(status=status.HTTP_200_OK, data={'msg': _('Test success')}) return Response(status=status.HTTP_200_OK, data={'msg': _('Test success')})
except APIException as e: except APIException as e:
try: if 'errmsg' in e.detail:
error = e.detail['errmsg'] error = e.detail['errmsg']
except: else:
error = e.detail error = e.detail
return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': error}) return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': error})

View File

@ -6,21 +6,18 @@
{% block content %} {% block content %}
<div> <div>
{% if errors %} <p>
<p> {% if error %}
<div class="alert alert-danger"> <div class="alert alert-danger" id="messages">
{{ errors }} {{ error }}
</div> </div>
</p> {% else %}
{% endif %}
{% if messages %}
<p>
<div class="alert alert-success" id="messages"> <div class="alert alert-success" id="messages">
{{ messages|safe }} {{ message|safe }}
</div> </div>
</p> {% endif %}
{% endif %} </p>
<div class="row"> <div class="row">
{% if has_cancel %} {% if has_cancel %}
<div class="col-sm-3"> <div class="col-sm-3">
@ -44,17 +41,19 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var time = '{{ interval }}'; var message = ''
if (!time) { var time = '{{ interval }}'
time = 5;
} else { {% if error %}
time = parseInt(time); message = '{{ error }}'
} {% else %}
message = '{{ message|safe }}'
{% endif %}
function redirect_page() { function redirect_page() {
if (time >= 0) { if (time >= 0) {
var messages = '{{ messages|safe }} <b>' + time + '</b> ...'; var msg = message + ' <b>' + time + '</b> ...';
$('#messages').html(messages); $('#messages').html(msg);
time--; time--;
setTimeout(redirect_page, 1000); setTimeout(redirect_page, 1000);
} else { } else {