mirror of https://github.com/jumpserver/jumpserver
perf: Custom SMS (files) support obtaining more user information. (#14486)
* perf: Custom SMS (files) support obtaining more user information. * perf: Remove the useless modules * perf: modify --------- Co-authored-by: jiangweidong <1053570670@qq.com>pull/14490/head
parent
f92c557235
commit
e2904ab042
|
@ -1,5 +1,6 @@
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import reverse
|
from django.shortcuts import reverse
|
||||||
|
@ -40,12 +41,15 @@ class UserResetPasswordSendCodeApi(CreateAPIView):
|
||||||
return user, None
|
return user, None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def safe_send_code(token, code, target, form_type, content):
|
def safe_send_code(token, code, target, form_type, content, user_info):
|
||||||
token_sent_key = '{}_send_at'.format(token)
|
token_sent_key = '{}_send_at'.format(token)
|
||||||
token_send_at = cache.get(token_sent_key, 0)
|
token_send_at = cache.get(token_sent_key, 0)
|
||||||
if token_send_at:
|
if token_send_at:
|
||||||
raise IntervalTooShort(60)
|
raise IntervalTooShort(60)
|
||||||
SendAndVerifyCodeUtil(target, code, backend=form_type, **content).gen_and_send_async()
|
tooler = SendAndVerifyCodeUtil(
|
||||||
|
target, code, backend=form_type, user_info=user_info, **content
|
||||||
|
)
|
||||||
|
tooler.gen_and_send_async()
|
||||||
cache.set(token_sent_key, int(time.time()), 60)
|
cache.set(token_sent_key, int(time.time()), 60)
|
||||||
|
|
||||||
def prepare_code_data(self, user_info, serializer):
|
def prepare_code_data(self, user_info, serializer):
|
||||||
|
@ -61,7 +65,7 @@ class UserResetPasswordSendCodeApi(CreateAPIView):
|
||||||
if not user:
|
if not user:
|
||||||
raise ValueError(err)
|
raise ValueError(err)
|
||||||
|
|
||||||
code = random_string(6, lower=False, upper=False)
|
code = random_string(settings.SMS_CODE_LENGTH, lower=False, upper=False)
|
||||||
subject = '%s: %s' % (get_login_title(), _('Forgot password'))
|
subject = '%s: %s' % (get_login_title(), _('Forgot password'))
|
||||||
context = {
|
context = {
|
||||||
'user': user, 'title': subject, 'code': code,
|
'user': user, 'title': subject, 'code': code,
|
||||||
|
@ -82,7 +86,7 @@ class UserResetPasswordSendCodeApi(CreateAPIView):
|
||||||
code, target, form_type, content = self.prepare_code_data(user_info, serializer)
|
code, target, form_type, content = self.prepare_code_data(user_info, serializer)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return Response({'error': str(e)}, status=400)
|
return Response({'error': str(e)}, status=400)
|
||||||
self.safe_send_code(token, code, target, form_type, content)
|
self.safe_send_code(token, code, target, form_type, content, user_info)
|
||||||
return Response({'data': 'ok'}, status=200)
|
return Response({'data': 'ok'}, status=200)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.conf import settings
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from common.utils.verify_code import SendAndVerifyCodeUtil
|
from common.utils.verify_code import SendAndVerifyCodeUtil
|
||||||
|
from users.serializers import SmsUserSerializer
|
||||||
from .base import BaseMFA
|
from .base import BaseMFA
|
||||||
|
|
||||||
sms_failed_msg = _("SMS verify code invalid")
|
sms_failed_msg = _("SMS verify code invalid")
|
||||||
|
@ -14,8 +15,13 @@ class MFASms(BaseMFA):
|
||||||
|
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
super().__init__(user)
|
super().__init__(user)
|
||||||
phone = user.phone if self.is_authenticated() else ''
|
phone, user_info = '', None
|
||||||
self.sms = SendAndVerifyCodeUtil(phone, backend=self.name)
|
if self.is_authenticated():
|
||||||
|
phone = user.phone
|
||||||
|
user_info = SmsUserSerializer(user).data
|
||||||
|
self.sms = SendAndVerifyCodeUtil(
|
||||||
|
phone, backend=self.name, user_info=user_info
|
||||||
|
)
|
||||||
|
|
||||||
def check_code(self, code):
|
def check_code(self, code):
|
||||||
assert self.is_authenticated()
|
assert self.is_authenticated()
|
||||||
|
|
|
@ -43,7 +43,7 @@ class SMS:
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def send_verify_code(self, phone_number, code):
|
def send_verify_code(self, phone_number, code, **kwargs):
|
||||||
prefix = getattr(self.client, 'SIGN_AND_TMPL_SETTING_FIELD_PREFIX', '')
|
prefix = getattr(self.client, 'SIGN_AND_TMPL_SETTING_FIELD_PREFIX', '')
|
||||||
sign_name = getattr(settings, f'{prefix}_VERIFY_SIGN_NAME', None)
|
sign_name = getattr(settings, f'{prefix}_VERIFY_SIGN_NAME', None)
|
||||||
template_code = getattr(settings, f'{prefix}_VERIFY_TEMPLATE_CODE', None)
|
template_code = getattr(settings, f'{prefix}_VERIFY_TEMPLATE_CODE', None)
|
||||||
|
@ -53,4 +53,7 @@ class SMS:
|
||||||
code='verify_code_sign_tmpl_invalid',
|
code='verify_code_sign_tmpl_invalid',
|
||||||
detail=_('SMS verification code signature or template invalid')
|
detail=_('SMS verification code signature or template invalid')
|
||||||
)
|
)
|
||||||
return self.send_sms([phone_number], sign_name, template_code, OrderedDict(code=code))
|
return self.send_sms(
|
||||||
|
[phone_number], sign_name, template_code,
|
||||||
|
OrderedDict(code=code), **kwargs
|
||||||
|
)
|
||||||
|
|
|
@ -20,16 +20,20 @@ logger = get_logger(__file__)
|
||||||
be executed to send SMS messages"""
|
be executed to send SMS messages"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
def send_sms_async(target, code):
|
def send_sms_async(target, code, user_info):
|
||||||
SMS().send_verify_code(target, code)
|
SMS().send_verify_code(target, code, user_info=user_info)
|
||||||
|
|
||||||
|
|
||||||
class SendAndVerifyCodeUtil(object):
|
class SendAndVerifyCodeUtil(object):
|
||||||
KEY_TMPL = 'auth-verify-code-{}'
|
KEY_TMPL = 'auth-verify-code-{}'
|
||||||
|
|
||||||
def __init__(self, target, code=None, key=None, backend='email', timeout=None, **kwargs):
|
def __init__(
|
||||||
|
self, target, code=None, key=None, backend='email',
|
||||||
|
user_info=None, timeout=None, **kwargs
|
||||||
|
):
|
||||||
self.code = code
|
self.code = code
|
||||||
self.target = target
|
self.target = target
|
||||||
|
self.user_info = user_info
|
||||||
self.backend = backend
|
self.backend = backend
|
||||||
self.key = key or self.KEY_TMPL.format(target)
|
self.key = key or self.KEY_TMPL.format(target)
|
||||||
self.timeout = settings.VERIFY_CODE_TTL if timeout is None else timeout
|
self.timeout = settings.VERIFY_CODE_TTL if timeout is None else timeout
|
||||||
|
@ -78,7 +82,7 @@ class SendAndVerifyCodeUtil(object):
|
||||||
return code
|
return code
|
||||||
|
|
||||||
def __send_with_sms(self):
|
def __send_with_sms(self):
|
||||||
send_sms_async.apply_async(args=(self.target, self.code), priority=100)
|
send_sms_async.apply_async(args=(self.target, self.code, self.user_info), priority=100)
|
||||||
|
|
||||||
def __send_with_email(self):
|
def __send_with_email(self):
|
||||||
subject = self.other_args.get('subject', '')
|
subject = self.other_args.get('subject', '')
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from common.utils import validate_ssh_public_key
|
from common.utils import validate_ssh_public_key
|
||||||
|
@ -103,7 +104,9 @@ class UserForgotPasswordForm(forms.Form):
|
||||||
label=_('SMS'), required=False,
|
label=_('SMS'), required=False,
|
||||||
help_text=_('The phone number must contain an area code, for example, +86')
|
help_text=_('The phone number must contain an area code, for example, +86')
|
||||||
)
|
)
|
||||||
code = forms.CharField(label=_('Verify code'), max_length=6, required=False)
|
code = forms.CharField(
|
||||||
|
label=_('Verify code'), max_length=settings.SMS_CODE_LENGTH, required=False
|
||||||
|
)
|
||||||
form_type = forms.ChoiceField(
|
form_type = forms.ChoiceField(
|
||||||
choices=[('sms', _('SMS')), ('email', _('Email'))],
|
choices=[('sms', _('SMS')), ('email', _('Email'))],
|
||||||
widget=forms.HiddenInput({'value': 'email'})
|
widget=forms.HiddenInput({'value': 'email'})
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -27,6 +26,7 @@ from ..models import User
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"UserSerializer",
|
"UserSerializer",
|
||||||
|
"SmsUserSerializer",
|
||||||
"MiniUserSerializer",
|
"MiniUserSerializer",
|
||||||
"InviteSerializer",
|
"InviteSerializer",
|
||||||
"ServiceAccountSerializer",
|
"ServiceAccountSerializer",
|
||||||
|
@ -411,6 +411,14 @@ class UserRetrieveSerializer(UserSerializer):
|
||||||
fields = UserSerializer.Meta.fields + ["login_confirm_settings"]
|
fields = UserSerializer.Meta.fields + ["login_confirm_settings"]
|
||||||
|
|
||||||
|
|
||||||
|
class SmsUserSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = [
|
||||||
|
'id', 'username', 'name', 'email', 'phone', 'source', 'is_active', 'comment'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class MiniUserSerializer(serializers.ModelSerializer):
|
class MiniUserSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
|
|
@ -16,6 +16,7 @@ from authentication.utils import check_user_property_is_correct
|
||||||
from common.const.choices import COUNTRY_CALLING_CODES
|
from common.const.choices import COUNTRY_CALLING_CODES
|
||||||
from common.utils import FlashMessageUtil, get_object_or_none, random_string
|
from common.utils import FlashMessageUtil, get_object_or_none, random_string
|
||||||
from common.utils.verify_code import SendAndVerifyCodeUtil
|
from common.utils.verify_code import SendAndVerifyCodeUtil
|
||||||
|
from users.serializers import SmsUserSerializer
|
||||||
from users.notifications import ResetPasswordSuccessMsg
|
from users.notifications import ResetPasswordSuccessMsg
|
||||||
from ... import forms
|
from ... import forms
|
||||||
from ...models import User
|
from ...models import User
|
||||||
|
@ -51,8 +52,7 @@ class UserForgotPasswordPreviewingView(FormView):
|
||||||
if token_sent_at:
|
if token_sent_at:
|
||||||
raise IntervalTooShort(sent_ttl)
|
raise IntervalTooShort(sent_ttl)
|
||||||
token = random_string(36)
|
token = random_string(36)
|
||||||
user_info = {'username': user.username, 'phone': user.phone, 'email': user.email}
|
cache.set(token, SmsUserSerializer(user).data, 5 * 60)
|
||||||
cache.set(token, user_info, 5 * 60)
|
|
||||||
cache.set(token_sent_at_key, time.time(), sent_ttl)
|
cache.set(token_sent_at_key, time.time(), sent_ttl)
|
||||||
return token
|
return token
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue