mirror of https://github.com/jumpserver/jumpserver
perf: 优化短信 (#6826)
* perf: 优化短信 * refactor: 适配新的短信模板配置 Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: xinwen <coderWen@126.com>pull/6835/head
parent
55a5dd1e34
commit
7a45f4d129
|
@ -11,34 +11,6 @@ from common.exceptions import JMSException
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
class SMS_MESSAGE(TextChoices):
|
|
||||||
"""
|
|
||||||
定义短信的各种消息类型,会存到类似 `ALIBABA_SMS_SIGN_AND_TEMPLATES` settings 里
|
|
||||||
|
|
||||||
{
|
|
||||||
'verification_code': {'sign_name': 'Jumpserver', 'template_code': 'SMS_222870834'},
|
|
||||||
...
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
"""
|
|
||||||
验证码签名和模板。模板例子:
|
|
||||||
`您的验证码:${code},您正进行身份验证,打死不告诉别人!`
|
|
||||||
其中必须包含 `code` 变量
|
|
||||||
"""
|
|
||||||
VERIFICATION_CODE = 'verification_code'
|
|
||||||
|
|
||||||
def get_sign_and_tmpl(self, config: dict):
|
|
||||||
try:
|
|
||||||
data = config[self]
|
|
||||||
return data['sign_name'], data['template_code']
|
|
||||||
except KeyError as e:
|
|
||||||
raise JMSException(
|
|
||||||
code=f'{settings.SMS_BACKEND}_sign_and_tmpl_bad',
|
|
||||||
detail=_('Invalid SMS sign and template: {}').format(e)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BACKENDS(TextChoices):
|
class BACKENDS(TextChoices):
|
||||||
ALIBABA = 'alibaba', _('Alibaba cloud')
|
ALIBABA = 'alibaba', _('Alibaba cloud')
|
||||||
TENCENT = 'tencent', _('Tencent cloud')
|
TENCENT = 'tencent', _('Tencent cloud')
|
||||||
|
@ -49,11 +21,7 @@ class BaseSMSClient:
|
||||||
短信终端的基类
|
短信终端的基类
|
||||||
"""
|
"""
|
||||||
|
|
||||||
SIGN_AND_TMPL_SETTING_FIELD: str
|
SIGN_AND_TMPL_SETTING_FIELD_PREFIX: str
|
||||||
|
|
||||||
@property
|
|
||||||
def sign_and_tmpl(self):
|
|
||||||
return getattr(settings, self.SIGN_AND_TMPL_SETTING_FIELD, {})
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def new_from_settings(cls):
|
def new_from_settings(cls):
|
||||||
|
@ -86,5 +54,12 @@ class SMS:
|
||||||
)
|
)
|
||||||
|
|
||||||
def send_verify_code(self, phone_number, code):
|
def send_verify_code(self, phone_number, code):
|
||||||
sign_name, template_code = SMS_MESSAGE.VERIFICATION_CODE.get_sign_and_tmpl(self.client.sign_and_tmpl)
|
sign_name = getattr(settings, f'{self.client.SIGN_AND_TMPL_SETTING_FIELD_PREFIX}_VERIFY_SIGN_NAME')
|
||||||
|
template_code = getattr(settings, f'{self.client.SIGN_AND_TMPL_SETTING_FIELD_PREFIX}_VERIFY_TEMPLATE_CODE')
|
||||||
|
|
||||||
|
if not (sign_name and template_code):
|
||||||
|
raise JMSException(
|
||||||
|
code='verify_code_sign_tmpl_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))
|
||||||
|
|
|
@ -15,7 +15,7 @@ logger = get_logger(__file__)
|
||||||
|
|
||||||
|
|
||||||
class AlibabaSMS(BaseSMSClient):
|
class AlibabaSMS(BaseSMSClient):
|
||||||
SIGN_AND_TMPL_SETTING_FIELD = 'ALIBABA_SMS_SIGN_AND_TEMPLATES'
|
SIGN_AND_TMPL_SETTING_FIELD_PREFIX = 'ALIBABA'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def new_from_settings(cls):
|
def new_from_settings(cls):
|
||||||
|
|
|
@ -20,7 +20,7 @@ class TencentSMS(BaseSMSClient):
|
||||||
"""
|
"""
|
||||||
https://cloud.tencent.com/document/product/382/43196#.E5.8F.91.E9.80.81.E7.9F.AD.E4.BF.A1
|
https://cloud.tencent.com/document/product/382/43196#.E5.8F.91.E9.80.81.E7.9F.AD.E4.BF.A1
|
||||||
"""
|
"""
|
||||||
SIGN_AND_TMPL_SETTING_FIELD = 'TENCENT_SMS_SIGN_AND_TEMPLATES'
|
SIGN_AND_TMPL_SETTING_FIELD_PREFIX = 'TENCENT'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def new_from_settings(cls):
|
def new_from_settings(cls):
|
||||||
|
|
|
@ -249,12 +249,14 @@ class Config(dict):
|
||||||
|
|
||||||
'ALIBABA_ACCESS_KEY_ID': '',
|
'ALIBABA_ACCESS_KEY_ID': '',
|
||||||
'ALIBABA_ACCESS_KEY_SECRET': '',
|
'ALIBABA_ACCESS_KEY_SECRET': '',
|
||||||
'ALIBABA_SMS_SIGN_AND_TEMPLATES': {},
|
'ALIBABA_VERIFY_SIGN_NAME': '',
|
||||||
|
'ALIBABA_VERIFY_TEMPLATE_CODE': '',
|
||||||
|
|
||||||
'TENCENT_SECRET_ID': '',
|
'TENCENT_SECRET_ID': '',
|
||||||
'TENCENT_SECRET_KEY': '',
|
'TENCENT_SECRET_KEY': '',
|
||||||
'TENCENT_SDKAPPID': '',
|
'TENCENT_SDKAPPID': '',
|
||||||
'TENCENT_SMS_SIGN_AND_TEMPLATES': {},
|
'TENCENT_VERIFY_SIGN_NAME': '',
|
||||||
|
'TENCENT_VERIFY_TEMPLATE_CODE': '',
|
||||||
|
|
||||||
'OTP_VALID_WINDOW': 2,
|
'OTP_VALID_WINDOW': 2,
|
||||||
'OTP_ISSUER_NAME': 'JumpServer',
|
'OTP_ISSUER_NAME': 'JumpServer',
|
||||||
|
|
|
@ -122,21 +122,7 @@ AUTH_FEISHU = CONFIG.AUTH_FEISHU
|
||||||
FEISHU_APP_ID = CONFIG.FEISHU_APP_ID
|
FEISHU_APP_ID = CONFIG.FEISHU_APP_ID
|
||||||
FEISHU_APP_SECRET = CONFIG.FEISHU_APP_SECRET
|
FEISHU_APP_SECRET = CONFIG.FEISHU_APP_SECRET
|
||||||
|
|
||||||
# SMS auth
|
|
||||||
SMS_ENABLED = CONFIG.SMS_ENABLED
|
|
||||||
SMS_BACKEND = CONFIG.SMS_BACKEND
|
|
||||||
SMS_TEST_PHONE = CONFIG.SMS_TEST_PHONE
|
|
||||||
|
|
||||||
# Alibaba
|
|
||||||
ALIBABA_ACCESS_KEY_ID = CONFIG.ALIBABA_ACCESS_KEY_ID
|
|
||||||
ALIBABA_ACCESS_KEY_SECRET = CONFIG.ALIBABA_ACCESS_KEY_SECRET
|
|
||||||
ALIBABA_SMS_SIGN_AND_TEMPLATES = CONFIG.ALIBABA_SMS_SIGN_AND_TEMPLATES
|
|
||||||
|
|
||||||
# TENCENT
|
|
||||||
TENCENT_SECRET_ID = CONFIG.TENCENT_SECRET_ID
|
|
||||||
TENCENT_SECRET_KEY = CONFIG.TENCENT_SECRET_KEY
|
|
||||||
TENCENT_SDKAPPID = CONFIG.TENCENT_SDKAPPID
|
|
||||||
TENCENT_SMS_SIGN_AND_TEMPLATES = CONFIG.TENCENT_SMS_SIGN_AND_TEMPLATES
|
|
||||||
|
|
||||||
# Other setting
|
# Other setting
|
||||||
TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION
|
TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION
|
||||||
|
|
|
@ -132,3 +132,20 @@ LOGIN_REDIRECT_MSG_ENABLED = CONFIG.LOGIN_REDIRECT_MSG_ENABLED
|
||||||
CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = CONFIG.CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS
|
CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = CONFIG.CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS
|
||||||
|
|
||||||
XRDP_ENABLED = CONFIG.XRDP_ENABLED
|
XRDP_ENABLED = CONFIG.XRDP_ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
# SMS enabled
|
||||||
|
SMS_ENABLED = CONFIG.SMS_ENABLED
|
||||||
|
SMS_BACKEND = CONFIG.SMS_BACKEND
|
||||||
|
SMS_TEST_PHONE = CONFIG.SMS_TEST_PHONE
|
||||||
|
|
||||||
|
# Alibaba
|
||||||
|
ALIBABA_ACCESS_KEY_ID = CONFIG.ALIBABA_ACCESS_KEY_ID
|
||||||
|
ALIBABA_ACCESS_KEY_SECRET = CONFIG.ALIBABA_ACCESS_KEY_SECRET
|
||||||
|
ALIBABA_SMS_SIGN_AND_TEMPLATES = CONFIG.ALIBABA_SMS_SIGN_AND_TEMPLATES
|
||||||
|
|
||||||
|
# TENCENT
|
||||||
|
TENCENT_SECRET_ID = CONFIG.TENCENT_SECRET_ID
|
||||||
|
TENCENT_SECRET_KEY = CONFIG.TENCENT_SECRET_KEY
|
||||||
|
TENCENT_SDKAPPID = CONFIG.TENCENT_SDKAPPID
|
||||||
|
TENCENT_SMS_SIGN_AND_TEMPLATES = CONFIG.TENCENT_SMS_SIGN_AND_TEMPLATES
|
||||||
|
|
Binary file not shown.
|
@ -2789,7 +2789,7 @@ msgstr "短信服务商"
|
||||||
|
|
||||||
#: settings/serializers/auth/sms.py:15 settings/serializers/email.py:69
|
#: settings/serializers/auth/sms.py:15 settings/serializers/email.py:69
|
||||||
msgid "Signature"
|
msgid "Signature"
|
||||||
msgstr "署名"
|
msgstr "签名"
|
||||||
|
|
||||||
#: settings/serializers/auth/sms.py:16
|
#: settings/serializers/auth/sms.py:16
|
||||||
msgid "Template"
|
msgid "Template"
|
||||||
|
|
|
@ -4,7 +4,6 @@ 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 common.message.backends.sms import SMS_MESSAGE
|
|
||||||
from common.message.backends.sms.alibaba import AlibabaSMS
|
from common.message.backends.sms.alibaba import AlibabaSMS
|
||||||
from settings.models import Setting
|
from settings.models import Setting
|
||||||
from common.permissions import IsSuperUser
|
from common.permissions import IsSuperUser
|
||||||
|
@ -23,7 +22,8 @@ class AlibabaSMSTestingAPI(GenericAPIView):
|
||||||
|
|
||||||
alibaba_access_key_id = serializer.validated_data['ALIBABA_ACCESS_KEY_ID']
|
alibaba_access_key_id = serializer.validated_data['ALIBABA_ACCESS_KEY_ID']
|
||||||
alibaba_access_key_secret = serializer.validated_data.get('ALIBABA_ACCESS_KEY_SECRET')
|
alibaba_access_key_secret = serializer.validated_data.get('ALIBABA_ACCESS_KEY_SECRET')
|
||||||
alibaba_sms_sign_and_tmpl = serializer.validated_data['ALIBABA_SMS_SIGN_AND_TEMPLATES']
|
alibaba_verify_sign_name = serializer.validated_data['ALIBABA_VERIFY_SIGN_NAME']
|
||||||
|
alibaba_verify_template_code = serializer.validated_data['ALIBABA_VERIFY_TEMPLATE_CODE']
|
||||||
test_phone = serializer.validated_data.get('SMS_TEST_PHONE')
|
test_phone = serializer.validated_data.get('SMS_TEST_PHONE')
|
||||||
|
|
||||||
if not test_phone:
|
if not test_phone:
|
||||||
|
@ -41,12 +41,11 @@ class AlibabaSMSTestingAPI(GenericAPIView):
|
||||||
access_key_id=alibaba_access_key_id,
|
access_key_id=alibaba_access_key_id,
|
||||||
access_key_secret=alibaba_access_key_secret
|
access_key_secret=alibaba_access_key_secret
|
||||||
)
|
)
|
||||||
sign, tmpl = SMS_MESSAGE.VERIFICATION_CODE.get_sign_and_tmpl(alibaba_sms_sign_and_tmpl)
|
|
||||||
|
|
||||||
client.send_sms(
|
client.send_sms(
|
||||||
phone_numbers=[test_phone],
|
phone_numbers=[test_phone],
|
||||||
sign_name=sign,
|
sign_name=alibaba_verify_sign_name,
|
||||||
template_code=tmpl,
|
template_code=alibaba_verify_template_code,
|
||||||
template_param={'code': 'test'}
|
template_param={'code': 'test'}
|
||||||
)
|
)
|
||||||
return Response(status=status.HTTP_200_OK, data={'msg': _('Test success')})
|
return Response(status=status.HTTP_200_OK, data={'msg': _('Test success')})
|
||||||
|
|
|
@ -6,7 +6,6 @@ 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 common.message.backends.sms import SMS_MESSAGE
|
|
||||||
from common.message.backends.sms.tencent import TencentSMS
|
from common.message.backends.sms.tencent import TencentSMS
|
||||||
from settings.models import Setting
|
from settings.models import Setting
|
||||||
from common.permissions import IsSuperUser
|
from common.permissions import IsSuperUser
|
||||||
|
@ -25,7 +24,8 @@ class TencentSMSTestingAPI(GenericAPIView):
|
||||||
|
|
||||||
tencent_secret_id = serializer.validated_data['TENCENT_SECRET_ID']
|
tencent_secret_id = serializer.validated_data['TENCENT_SECRET_ID']
|
||||||
tencent_secret_key = serializer.validated_data.get('TENCENT_SECRET_KEY')
|
tencent_secret_key = serializer.validated_data.get('TENCENT_SECRET_KEY')
|
||||||
tencent_sms_sign_and_tmpl = serializer.validated_data['TENCENT_SMS_SIGN_AND_TEMPLATES']
|
tencent_verify_sign_name = serializer.validated_data['TENCENT_VERIFY_SIGN_NAME']
|
||||||
|
tencent_verify_template_code = serializer.validated_data['TENCENT_VERIFY_TEMPLATE_CODE']
|
||||||
tencent_sdkappid = serializer.validated_data.get('TENCENT_SDKAPPID')
|
tencent_sdkappid = serializer.validated_data.get('TENCENT_SDKAPPID')
|
||||||
|
|
||||||
test_phone = serializer.validated_data.get('SMS_TEST_PHONE')
|
test_phone = serializer.validated_data.get('SMS_TEST_PHONE')
|
||||||
|
@ -46,12 +46,11 @@ class TencentSMSTestingAPI(GenericAPIView):
|
||||||
secret_key=tencent_secret_key,
|
secret_key=tencent_secret_key,
|
||||||
sdkappid=tencent_sdkappid
|
sdkappid=tencent_sdkappid
|
||||||
)
|
)
|
||||||
sign, tmpl = SMS_MESSAGE.VERIFICATION_CODE.get_sign_and_tmpl(tencent_sms_sign_and_tmpl)
|
|
||||||
|
|
||||||
client.send_sms(
|
client.send_sms(
|
||||||
phone_numbers=[test_phone],
|
phone_numbers=[test_phone],
|
||||||
sign_name=sign,
|
sign_name=tencent_verify_sign_name,
|
||||||
template_code=tmpl,
|
template_code=tencent_verify_template_code,
|
||||||
template_param=OrderedDict(code='test')
|
template_param=OrderedDict(code='test')
|
||||||
)
|
)
|
||||||
return Response(status=status.HTTP_200_OK, data={'msg': _('Test success')})
|
return Response(status=status.HTTP_200_OK, data={'msg': _('Test success')})
|
||||||
|
|
|
@ -8,12 +8,14 @@ __all__ = ['SMSSettingSerializer', 'AlibabaSMSSettingSerializer', 'TencentSMSSet
|
||||||
|
|
||||||
class SMSSettingSerializer(serializers.Serializer):
|
class SMSSettingSerializer(serializers.Serializer):
|
||||||
SMS_ENABLED = serializers.BooleanField(default=False, label=_('Enable SMS'))
|
SMS_ENABLED = serializers.BooleanField(default=False, label=_('Enable SMS'))
|
||||||
SMS_BACKEND = serializers.ChoiceField(choices=BACKENDS.choices, default=BACKENDS.ALIBABA, label=_('SMS provider'))
|
SMS_BACKEND = serializers.ChoiceField(
|
||||||
|
choices=BACKENDS.choices, default=BACKENDS.ALIBABA, label=_('SMS provider')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SignAndTmplPairSerializer(serializers.Serializer):
|
class SignTmplPairSerializer(serializers.Serializer):
|
||||||
sign_name = serializers.CharField(max_length=256, required=True, label=_('Signature'))
|
SIGN_NAME = serializers.CharField(max_length=256, required=True, label=_('Signature'))
|
||||||
template_code = serializers.CharField(max_length=256, required=True, label=_('Template'))
|
TEMPLATE_CODE = serializers.CharField(max_length=256, required=True, label=_('Template code'))
|
||||||
|
|
||||||
|
|
||||||
class BaseSMSSettingSerializer(serializers.Serializer):
|
class BaseSMSSettingSerializer(serializers.Serializer):
|
||||||
|
@ -25,23 +27,18 @@ class BaseSMSSettingSerializer(serializers.Serializer):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class SignAndTmplSerializer(serializers.Serializer):
|
|
||||||
verification_code = SignAndTmplPairSerializer(default={'sign_name': '', 'template_code': ''})
|
|
||||||
|
|
||||||
|
|
||||||
class AlibabaSMSSettingSerializer(BaseSMSSettingSerializer):
|
class AlibabaSMSSettingSerializer(BaseSMSSettingSerializer):
|
||||||
ALIBABA_ACCESS_KEY_ID = serializers.CharField(max_length=256, required=True, label='AccessKeyId')
|
ALIBABA_ACCESS_KEY_ID = serializers.CharField(max_length=256, required=True, label='AccessKeyId')
|
||||||
ALIBABA_ACCESS_KEY_SECRET = serializers.CharField(
|
ALIBABA_ACCESS_KEY_SECRET = serializers.CharField(
|
||||||
max_length=256, required=False, label='AccessKeySecret', write_only=True)
|
max_length=256, required=False, label='AccessKeySecret', write_only=True
|
||||||
ALIBABA_SMS_SIGN_AND_TEMPLATES = SignAndTmplSerializer(
|
|
||||||
label=_('Signatures and Templates'), required=True
|
|
||||||
)
|
)
|
||||||
|
ALIBABA_VERIFY_SIGN_NAME = serializers.CharField(max_length=256, required=True, label=_('Signature'))
|
||||||
|
ALIBABA_VERIFY_TEMPLATE_CODE = serializers.CharField(max_length=256, required=True, label=_('Template code'))
|
||||||
|
|
||||||
|
|
||||||
class TencentSMSSettingSerializer(BaseSMSSettingSerializer):
|
class TencentSMSSettingSerializer(BaseSMSSettingSerializer):
|
||||||
TENCENT_SECRET_ID = serializers.CharField(max_length=256, required=True, label='Secret id')
|
TENCENT_SECRET_ID = serializers.CharField(max_length=256, required=True, label='Secret id')
|
||||||
TENCENT_SECRET_KEY = serializers.CharField(max_length=256, required=False, label='Secret key', write_only=True)
|
TENCENT_SECRET_KEY = serializers.CharField(max_length=256, required=False, label='Secret key', write_only=True)
|
||||||
TENCENT_SDKAPPID = serializers.CharField(max_length=256, required=True, label='SDK app id')
|
TENCENT_SDKAPPID = serializers.CharField(max_length=256, required=True, label='SDK app id')
|
||||||
TENCENT_SMS_SIGN_AND_TEMPLATES = SignAndTmplSerializer(
|
TENCENT_VERIFY_SIGN_NAME = serializers.CharField(max_length=256, required=True, label=_('Signature'))
|
||||||
label=_('Signatures and Templates'), required=True
|
TENCENT_VERIFY_TEMPLATE_CODE = serializers.CharField(max_length=256, required=True, label=_('Template code'))
|
||||||
)
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from abc import ABCMeta
|
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
@ -17,8 +15,12 @@ class OtherSettingSerializer(serializers.Serializer):
|
||||||
OTP_VALID_WINDOW = serializers.IntegerField(label=_("OTP valid window"))
|
OTP_VALID_WINDOW = serializers.IntegerField(label=_("OTP valid window"))
|
||||||
|
|
||||||
PERIOD_TASK_ENABLED = serializers.BooleanField(required=False, label=_("Enable period task"))
|
PERIOD_TASK_ENABLED = serializers.BooleanField(required=False, label=_("Enable period task"))
|
||||||
WINDOWS_SSH_DEFAULT_SHELL = serializers.CharField(
|
WINDOWS_SSH_DEFAULT_SHELL = serializers.ChoiceField(
|
||||||
required=False, max_length=1024, label=_('Ansible windows default shell'),
|
choices=[
|
||||||
|
('cmd', _("CMD")),
|
||||||
|
('powershell', _("PowerShell"))
|
||||||
|
],
|
||||||
|
label=_('Ansible windows default shell'),
|
||||||
help_text=_('The shell type used when Windows assets perform ansible tasks')
|
help_text=_('The shell type used when Windows assets perform ansible tasks')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue