From 353b66bf8f80281db57fb1eeeacbe66d3cf202c7 Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Thu, 11 Nov 2021 19:07:13 +0800 Subject: [PATCH 01/25] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=88=9B?= =?UTF-8?q?=E5=BB=BAtoken=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/token.py | 2 +- apps/authentication/serializers.py | 10 ++++++++-- apps/locale/zh/LC_MESSAGES/django.mo | 4 ++-- apps/locale/zh/LC_MESSAGES/django.po | 8 ++++---- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/authentication/api/token.py b/apps/authentication/api/token.py index d8e8eb6fc..c46c4d5e2 100644 --- a/apps/authentication/api/token.py +++ b/apps/authentication/api/token.py @@ -33,8 +33,8 @@ class TokenCreateApi(AuthMixin, CreateAPIView): self.check_user_mfa_if_need(user) self.check_user_login_confirm_if_need(user) self.send_auth_signal(success=True, user=user) - self.clear_auth_mark() resp = super().create(request, *args, **kwargs) + self.clear_auth_mark() return resp except errors.AuthFailedError as e: return Response(e.as_data(), status=400) diff --git a/apps/authentication/serializers.py b/apps/authentication/serializers.py index a87e1e942..44fea3242 100644 --- a/apps/authentication/serializers.py +++ b/apps/authentication/serializers.py @@ -54,9 +54,9 @@ class BearerTokenSerializer(serializers.Serializer): user.last_login = timezone.now() user.save(update_fields=['last_login']) - def create(self, validated_data): + def get_request_user(self): request = self.context.get('request') - if request.user and not request.user.is_anonymous: + if request.user and request.user.is_authenticated: user = request.user else: user_id = request.session.get('user_id') @@ -65,6 +65,12 @@ class BearerTokenSerializer(serializers.Serializer): raise serializers.ValidationError( "user id {} not exist".format(user_id) ) + return user + + def create(self, validated_data): + request = self.context.get('request') + user = self.get_request_user() + token, date_expired = user.create_bearer_token(request) self.update_last_login(user) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 03f88c57e..2aec9545b 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:925c5a219a4ee6835ad59e3b8e9f7ea5074ee3df6527c0f73ef1a50eaedaf59c -size 91777 +oid sha256:fd23c3a1a8f833e99937d38db94b5e32b308c9c1912f8b258ff009d96f9bf2bb +size 92755 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 4305720a7..e5071bf14 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -3240,7 +3240,7 @@ msgstr "" #: settings/serializers/other.py:24 msgid "Shell (Windows)" -msgstr "Shell(Windows 资产)" +msgstr "Windows shell" #: settings/serializers/other.py:25 msgid "The shell type used when Windows assets perform ansible tasks" @@ -3248,15 +3248,15 @@ msgstr "windows 资产执行 Ansible 任务时,使用的 Shell 类型。" #: settings/serializers/other.py:29 msgid "Perm ungroup node" -msgstr "授权未分组节点" +msgstr "显示未分组节点" #: settings/serializers/other.py:30 msgid "Perm single to ungroup node" -msgstr "授权未分组节点" +msgstr "放置单独授权的资产到未分组节点, 避免能看到资产所在节点,但该节点未被授权的问题" #: settings/serializers/other.py:34 msgid "Help Docs URL" -msgstr "" +msgstr "文档链接" #: settings/serializers/other.py:35 msgid "default: http://docs.jumpserver.org" From 90477146edd5cecf61299df451119a35ced2c8a4 Mon Sep 17 00:00:00 2001 From: feng626 <1304903146@qq.com> Date: Thu, 11 Nov 2021 19:03:01 +0800 Subject: [PATCH 02/25] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=85=A8?= =?UTF-8?q?=E5=B1=80ip=E9=BB=91=E5=90=8D=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/errors.py | 8 +++++ apps/authentication/mixins.py | 14 ++++++--- apps/authentication/views/login.py | 3 +- apps/jumpserver/conf.py | 1 + apps/jumpserver/settings/custom.py | 1 + apps/locale/zh/LC_MESSAGES/django.po | 10 ++++++ apps/settings/serializers/security.py | 45 ++++++++++++++++++++++----- apps/users/utils.py | 35 +++++++++++++++++++-- 8 files changed, 103 insertions(+), 14 deletions(-) diff --git a/apps/authentication/errors.py b/apps/authentication/errors.py index 19b13ab8e..1e9000e67 100644 --- a/apps/authentication/errors.py +++ b/apps/authentication/errors.py @@ -180,6 +180,14 @@ class BlockLoginError(AuthFailedNeedBlockMixin, AuthFailedError): super().__init__(username=username, ip=ip) +class BlockGlobalIpLoginError(AuthFailedError): + error = 'block_global_ip_login' + + def __init__(self, username, ip): + self.msg = _("IP is not allowed") + super().__init__(username=username, ip=ip) + + class SessionEmptyError(AuthFailedError): msg = session_empty_msg error = 'session_empty' diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index a7d845662..69daf6330 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -8,7 +8,6 @@ from typing import Callable from django.utils.http import urlencode from django.core.cache import cache from django.conf import settings -from django.urls import reverse_lazy from django.contrib import auth from django.utils.translation import ugettext as _ from rest_framework.request import Request @@ -18,10 +17,10 @@ from django.contrib.auth import ( ) from django.shortcuts import reverse, redirect, get_object_or_404 -from common.utils import get_object_or_none, get_request_ip, get_logger, bulk_get, FlashMessageUtil +from common.utils import get_request_ip, get_logger, bulk_get, FlashMessageUtil from acls.models import LoginACL from users.models import User -from users.utils import LoginBlockUtil, MFABlockUtils +from users.utils import LoginBlockUtil, MFABlockUtils, LoginIpBlockUtil from . import errors from .utils import rsa_decrypt, gen_key_pair from .signals import post_auth_success, post_auth_failed @@ -76,7 +75,9 @@ def authenticate(request=None, **credentials): return user # The credentials supplied are invalid to all backends, fire signal - user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request) + user_login_failed.send( + sender=__name__, credentials=_clean_credentials(credentials), request=request + ) auth.authenticate = authenticate @@ -209,6 +210,10 @@ class AuthPreCheckMixin: def _check_is_block(self, username, raise_exception=True): ip = self.get_request_ip() + + if LoginIpBlockUtil(ip).is_block(): + raise errors.BlockGlobalIpLoginError(username=username, ip=ip) + is_block = LoginBlockUtil(username, ip).is_block() if not is_block: return @@ -224,6 +229,7 @@ class AuthPreCheckMixin: username = self.request.data.get("username") else: username = self.request.POST.get("username") + self._check_is_block(username, raise_exception) def _check_only_allow_exists_user_auth(self, username): diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index e14f47256..79b6839b4 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -133,7 +133,8 @@ class UserLoginView(mixins.AuthMixin, FormView): errors.BlockMFAError, errors.MFACodeRequiredError, errors.SMSCodeRequiredError, - errors.UserPhoneNotSet + errors.UserPhoneNotSet, + errors.BlockGlobalIpLoginError ) as e: form.add_error('code', e.msg) return super().form_invalid(form) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index c634ca723..9d95609d2 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -292,6 +292,7 @@ class Config(dict): 'SECURITY_SERVICE_ACCOUNT_REGISTRATION': True, 'SECURITY_VIEW_AUTH_NEED_MFA': True, 'SECURITY_LOGIN_LIMIT_COUNT': 7, + 'SECURITY_LOGIN_IP_BLACK_LIST': [], 'SECURITY_LOGIN_LIMIT_TIME': 30, 'SECURITY_MAX_IDLE_TIME': 30, 'SECURITY_PASSWORD_EXPIRATION_TIME': 9999, diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index edc46c795..a4be80f5b 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -34,6 +34,7 @@ TERMINAL_REPLAY_STORAGE = CONFIG.TERMINAL_REPLAY_STORAGE SECURITY_MFA_AUTH = CONFIG.SECURITY_MFA_AUTH SECURITY_COMMAND_EXECUTION = CONFIG.SECURITY_COMMAND_EXECUTION SECURITY_LOGIN_LIMIT_COUNT = CONFIG.SECURITY_LOGIN_LIMIT_COUNT +SECURITY_LOGIN_IP_BLACK_LIST = CONFIG.SECURITY_LOGIN_IP_BLACK_LIST SECURITY_LOGIN_LIMIT_TIME = CONFIG.SECURITY_LOGIN_LIMIT_TIME # Unit: minute SECURITY_MAX_IDLE_TIME = CONFIG.SECURITY_MAX_IDLE_TIME # Unit: minute SECURITY_PASSWORD_EXPIRATION_TIME = CONFIG.SECURITY_PASSWORD_EXPIRATION_TIME # Unit: day diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index e5071bf14..2b057ff8f 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -171,6 +171,16 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. " msgid "Username" msgstr "用户名" +msgid "IP Black List" +msgstr "IP 黑名单" + +msgid "" +"Format for comma-delimited string. Such as: " +"192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64" +msgstr "" +"格式为逗号分隔的字符串。例如: 192.168.10.1, 192.168.1.0/24, " +"10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 (支持网域)" + #: acls/serializers/login_asset_acl.py:24 msgid "" "Format for comma-delimited string, with * indicating a match all. Such as: " diff --git a/apps/settings/serializers/security.py b/apps/settings/serializers/security.py index 51d03eab5..70b015948 100644 --- a/apps/settings/serializers/security.py +++ b/apps/settings/serializers/security.py @@ -1,6 +1,8 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers +from common.utils.ip import is_ip_address, is_ip_network, is_ip_segment + class SecurityPasswordRuleSerializer(serializers.Serializer): SECURITY_PASSWORD_MIN_LENGTH = serializers.IntegerField( @@ -14,9 +16,24 @@ class SecurityPasswordRuleSerializer(serializers.Serializer): SECURITY_PASSWORD_UPPER_CASE = serializers.BooleanField( required=False, label=_('Must contain capital') ) - SECURITY_PASSWORD_LOWER_CASE = serializers.BooleanField(required=False, label=_('Must contain lowercase')) - SECURITY_PASSWORD_NUMBER = serializers.BooleanField(required=False, label=_('Must contain numeric')) - SECURITY_PASSWORD_SPECIAL_CHAR = serializers.BooleanField(required=False, label=_('Must contain special')) + SECURITY_PASSWORD_LOWER_CASE = serializers.BooleanField( + required=False, label=_('Must contain lowercase') + ) + SECURITY_PASSWORD_NUMBER = serializers.BooleanField( + required=False, label=_('Must contain numeric') + ) + SECURITY_PASSWORD_SPECIAL_CHAR = serializers.BooleanField( + required=False, label=_('Must contain special') + ) + + +def ip_child_validator(ip_child): + is_valid = is_ip_address(ip_child) \ + or is_ip_network(ip_child) \ + or is_ip_segment(ip_child) + if not is_valid: + error = _('IP address invalid: `{}`').format(ip_child) + raise serializers.ValidationError(error) class SecurityAuthSerializer(serializers.Serializer): @@ -40,6 +57,14 @@ class SecurityAuthSerializer(serializers.Serializer): 'no login is allowed during this time interval.' ) ) + SECURITY_LOGIN_IP_BLACK_LIST = serializers.ListField( + default=[], label=_('IP Black List'), allow_empty=True, + child=serializers.CharField(max_length=1024, validators=[ip_child_validator]), + help_text=_( + 'Format for comma-delimited string. Such as: ' + '192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64' + ) + ) SECURITY_PASSWORD_EXPIRATION_TIME = serializers.IntegerField( min_value=1, max_value=99999, required=True, label=_('User password expiration'), @@ -72,7 +97,9 @@ class SecurityAuthSerializer(serializers.Serializer): SECURITY_MFA_VERIFY_TTL = serializers.IntegerField( min_value=5, max_value=60 * 60 * 10, label=_("MFA verify TTL"), - help_text=_("Unit: second, The verification MFA takes effect only when you view the account password"), + help_text=_( + "Unit: second, The verification MFA takes effect only when you view the account password" + ) ) SECURITY_LOGIN_CHALLENGE_ENABLED = serializers.BooleanField( required=False, default=False, @@ -108,7 +135,9 @@ class SecurityAuthSerializer(serializers.Serializer): class SecuritySettingSerializer(SecurityPasswordRuleSerializer, SecurityAuthSerializer): SECURITY_SERVICE_ACCOUNT_REGISTRATION = serializers.BooleanField( required=True, label=_('Enable terminal register'), - help_text=_("Allow terminal register, after all terminal setup, you should disable this for security") + help_text=_( + "Allow terminal register, after all terminal setup, you should disable this for security" + ) ) SECURITY_WATERMARK_ENABLED = serializers.BooleanField( required=True, label=_('Enable watermark'), @@ -142,6 +171,8 @@ class SecuritySettingSerializer(SecurityPasswordRuleSerializer, SecurityAuthSeri ) SECURITY_CHECK_DIFFERENT_CITY_LOGIN = serializers.BooleanField( required=False, label=_('Remote Login Protection'), - help_text=_('The system determines whether the login IP address belongs to a common login city. ' - 'If the account is logged in from a common login city, the system sends a remote login reminder') + help_text=_( + 'The system determines whether the login IP address belongs to a common login city. ' + 'If the account is logged in from a common login city, the system sends a remote login reminder' + ) ) diff --git a/apps/users/utils.py b/apps/users/utils.py index f00e363a6..033bc1081 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -14,7 +14,6 @@ from common.tasks import send_mail_async from common.utils import reverse, get_object_or_none from .models import User - logger = logging.getLogger('jumpserver') @@ -101,7 +100,7 @@ def check_password_rules(password, is_org_admin=False): min_length = settings.SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH else: min_length = settings.SECURITY_PASSWORD_MIN_LENGTH - pattern += '.{' + str(min_length-1) + ',}$' + pattern += '.{' + str(min_length - 1) + ',}$' match_obj = re.match(pattern, password) return bool(match_obj) @@ -173,6 +172,33 @@ class BlockUtilBase: return bool(cache.get(self.block_key)) +class BlockGlobalIpUtilBase: + LIMIT_KEY_TMPL: str + BLOCK_KEY_TMPL: str + + def __init__(self, ip): + self.ip = ip + self.limit_key = self.LIMIT_KEY_TMPL.format(ip) + self.block_key = self.BLOCK_KEY_TMPL.format(ip) + self.key_ttl = int(settings.SECURITY_LOGIN_LIMIT_TIME) * 60 + + def sign_limit_key_and_block_key(self): + count = cache.get(self.limit_key, 0) + count += 1 + cache.set(self.limit_key, count, self.key_ttl) + + limit_count = settings.SECURITY_LOGIN_LIMIT_COUNT + if count >= limit_count: + cache.set(self.block_key, True, self.key_ttl) + + def is_block(self): + if self.ip in settings.SECURITY_LOGIN_IP_BLACK_LIST: + self.sign_limit_key_and_block_key() + return bool(cache.get(self.block_key)) + else: + return False + + class LoginBlockUtil(BlockUtilBase): LIMIT_KEY_TMPL = "_LOGIN_LIMIT_{}_{}" BLOCK_KEY_TMPL = "_LOGIN_BLOCK_{}" @@ -183,6 +209,11 @@ class MFABlockUtils(BlockUtilBase): BLOCK_KEY_TMPL = "_MFA_BLOCK_{}" +class LoginIpBlockUtil(BlockGlobalIpUtilBase): + LIMIT_KEY_TMPL = "_LOGIN_LIMIT_{}" + BLOCK_KEY_TMPL = "_LOGIN_BLOCK_{}" + + def construct_user_email(username, email): if '@' not in email: if '@' in username: From e9dc1ad86a8af50761dd5fddc36a7d91690e295e Mon Sep 17 00:00:00 2001 From: Eric <xplzv@126.com> Date: Fri, 12 Nov 2021 14:49:25 +0800 Subject: [PATCH 03/25] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20ssh=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=BF=9E=E6=8E=A5=E6=97=A0=E6=B3=95=E4=BA=8C?= =?UTF-8?q?=E7=BA=A7=E7=99=BB=E5=BD=95=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/perms/serializers/asset/user_permission.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/perms/serializers/asset/user_permission.py b/apps/perms/serializers/asset/user_permission.py index f844b7d4d..0603a0e08 100644 --- a/apps/perms/serializers/asset/user_permission.py +++ b/apps/perms/serializers/asset/user_permission.py @@ -28,7 +28,7 @@ class AssetSystemUserSerializer(serializers.ModelSerializer): model = SystemUser only_fields = ( 'id', 'name', 'username', 'priority', 'protocol', 'login_mode', - 'sftp_root', 'username_same_with_user', + 'sftp_root', 'username_same_with_user', 'su_enabled', 'su_from', ) fields = list(only_fields) + ["actions"] read_only_fields = fields From 43cbf4f6a97cac33058896381c199a14175c9a39 Mon Sep 17 00:00:00 2001 From: xinwen <coderWen@126.com> Date: Fri, 12 Nov 2021 16:27:15 +0800 Subject: [PATCH 04/25] =?UTF-8?q?fix:=20=E5=88=9B=E5=BB=BA=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E6=97=B6=E5=90=8D=E7=A7=B0=E9=87=8D=E5=A4=8D=E6=8A=A5?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/serializers/storage.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/terminal/serializers/storage.py b/apps/terminal/serializers/storage.py index 61c018b37..3e4e99bc0 100644 --- a/apps/terminal/serializers/storage.py +++ b/apps/terminal/serializers/storage.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # -import copy from rest_framework import serializers from urllib.parse import urlparse from django.utils.translation import ugettext_lazy as _ @@ -9,6 +8,7 @@ from common.drf.serializers import MethodSerializer from common.drf.fields import ReadableHiddenField from ..models import ReplayStorage, CommandStorage from .. import const +from rest_framework.validators import UniqueValidator # Replay storage serializers @@ -220,6 +220,9 @@ class CommandStorageSerializer(BaseStorageSerializer): class Meta(BaseStorageSerializer.Meta): model = CommandStorage + extra_kwargs = { + 'name': {'validators': [UniqueValidator(queryset=CommandStorage.objects.all())]} + } # ReplayStorageSerializer @@ -230,4 +233,6 @@ class ReplayStorageSerializer(BaseStorageSerializer): class Meta(BaseStorageSerializer.Meta): model = ReplayStorage - + extra_kwargs = { + 'name': {'validators': [UniqueValidator(queryset=ReplayStorage.objects.all())]} + } From 9a7919f3aca8f15d7ba572dcb5f300d5f7576b70 Mon Sep 17 00:00:00 2001 From: xinwen <coderWen@126.com> Date: Fri, 12 Nov 2021 15:33:33 +0800 Subject: [PATCH 05/25] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0=E5=85=AC?= =?UTF-8?q?=E9=92=A5=E6=B6=88=E6=81=AF=E4=B8=8D=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_msg_rest_public_key_success.html | 14 ++ apps/locale/zh/LC_MESSAGES/django.po | 219 ++++++++++-------- apps/users/api/profile.py | 7 +- apps/users/notifications.py | 32 +++ 4 files changed, 169 insertions(+), 103 deletions(-) create mode 100644 apps/authentication/templates/authentication/_msg_rest_public_key_success.html diff --git a/apps/authentication/templates/authentication/_msg_rest_public_key_success.html b/apps/authentication/templates/authentication/_msg_rest_public_key_success.html new file mode 100644 index 000000000..a95bfdd9b --- /dev/null +++ b/apps/authentication/templates/authentication/_msg_rest_public_key_success.html @@ -0,0 +1,14 @@ +{% load i18n %} +<p>{% trans 'Hello' %} {{ name }},</p> + +<p> + {% trans 'Your public key has just been successfully updated' %} +</p> +<p> + <b>{% trans 'IP' %}:</b> {{ ip_address }} <br /> + <b>{% trans 'Browser' %}:</b> {{ browser }} +</p> +<p> + {% trans 'If the public key update was not initiated by you, your account may have security issues' %} <br /> + {% trans 'If you have any questions, you can contact the administrator' %} +</p> diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 2b057ff8f..0b4d4b4be 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-10 17:18+0800\n" +"POT-Creation-Date: 2021-11-12 15:28+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler <ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n" @@ -52,7 +52,7 @@ msgid "Active" msgstr "激活中" #: acls/models/base.py:32 applications/models/application.py:179 -#: assets/models/asset.py:144 assets/models/asset.py:220 +#: assets/models/asset.py:144 assets/models/asset.py:232 #: assets/models/base.py:180 assets/models/cluster.py:29 #: assets/models/cmd_filter.py:23 assets/models/cmd_filter.py:64 #: assets/models/domain.py:25 assets/models/domain.py:65 @@ -128,7 +128,7 @@ msgstr "系统用户" #: acls/models/login_asset_acl.py:22 #: applications/serializers/attrs/application_category/remote_app.py:37 -#: assets/models/asset.py:357 assets/models/authbook.py:18 +#: assets/models/asset.py:350 assets/models/authbook.py:18 #: assets/models/gathered_user.py:14 assets/serializers/system_user.py:258 #: audits/models.py:38 perms/models/asset_permission.py:99 #: templates/index.html:82 terminal/backends/command/models.py:19 @@ -192,16 +192,17 @@ msgstr "" #: acls/serializers/login_asset_acl.py:31 acls/serializers/rules/rules.py:32 #: applications/serializers/attrs/application_type/mysql_workbench.py:18 -#: assets/models/asset.py:180 assets/models/domain.py:61 +#: assets/models/asset.py:211 assets/models/domain.py:61 #: assets/serializers/account.py:12 #: authentication/templates/authentication/_msg_rest_password_success.html:8 +#: authentication/templates/authentication/_msg_rest_public_key_success.html:8 #: settings/serializers/terminal.py:8 #: users/templates/users/_granted_assets.html:26 #: users/templates/users/user_asset_permission.html:156 msgid "IP" msgstr "IP" -#: acls/serializers/login_asset_acl.py:35 assets/models/asset.py:181 +#: acls/serializers/login_asset_acl.py:35 assets/models/asset.py:212 #: assets/serializers/account.py:13 assets/serializers/gathered_user.py:23 #: settings/serializers/terminal.py:7 #: users/templates/users/_granted_assets.html:25 @@ -215,7 +216,7 @@ msgid "" "options: {}" msgstr "格式为逗号分隔的字符串, * 表示匹配所有. 可选的协议有: {}" -#: acls/serializers/login_asset_acl.py:55 assets/models/asset.py:184 +#: acls/serializers/login_asset_acl.py:55 assets/models/asset.py:214 #: assets/models/domain.py:63 assets/models/user.py:200 #: terminal/serializers/session.py:30 terminal/serializers/storage.py:69 msgid "Protocol" @@ -323,7 +324,7 @@ msgstr "类别" msgid "Type" msgstr "类型" -#: applications/models/application.py:175 assets/models/asset.py:188 +#: applications/models/application.py:175 assets/models/asset.py:218 #: assets/models/domain.py:30 assets/models/domain.py:64 msgid "Domain" msgstr "网域" @@ -375,7 +376,7 @@ msgstr "主机" #: applications/serializers/attrs/application_type/mysql_workbench.py:22 #: applications/serializers/attrs/application_type/oracle.py:11 #: applications/serializers/attrs/application_type/pgsql.py:11 -#: assets/models/asset.py:185 assets/models/domain.py:62 +#: assets/models/asset.py:215 assets/models/domain.py:62 #: settings/serializers/auth/radius.py:15 #: xpack/plugins/cloud/serializers/account_attrs.py:61 msgid "Port" @@ -463,103 +464,103 @@ msgstr "元数据" msgid "Internal" msgstr "内部的" -#: assets/models/asset.py:163 assets/models/asset.py:187 +#: assets/models/asset.py:163 assets/models/asset.py:217 #: assets/serializers/asset.py:65 perms/serializers/asset/user_permission.py:43 msgid "Platform" msgstr "系统平台" -#: assets/models/asset.py:186 assets/serializers/asset.py:67 +#: assets/models/asset.py:169 +msgid "Vendor" +msgstr "制造商" + +#: assets/models/asset.py:170 +msgid "Model" +msgstr "型号" + +#: assets/models/asset.py:171 +msgid "Serial number" +msgstr "序列号" + +#: assets/models/asset.py:173 +msgid "CPU model" +msgstr "CPU型号" + +#: assets/models/asset.py:174 +msgid "CPU count" +msgstr "CPU数量" + +#: assets/models/asset.py:175 +msgid "CPU cores" +msgstr "CPU核数" + +#: assets/models/asset.py:176 +msgid "CPU vcpus" +msgstr "CPU总数" + +#: assets/models/asset.py:177 +msgid "Memory" +msgstr "内存" + +#: assets/models/asset.py:178 +msgid "Disk total" +msgstr "硬盘大小" + +#: assets/models/asset.py:179 +msgid "Disk info" +msgstr "硬盘信息" + +#: assets/models/asset.py:181 +msgid "OS" +msgstr "操作系统" + +#: assets/models/asset.py:182 +msgid "OS version" +msgstr "系统版本" + +#: assets/models/asset.py:183 +msgid "OS arch" +msgstr "系统架构" + +#: assets/models/asset.py:184 +msgid "Hostname raw" +msgstr "主机名原始" + +#: assets/models/asset.py:216 assets/serializers/asset.py:67 #: perms/serializers/asset/user_permission.py:41 #: xpack/plugins/cloud/models.py:104 xpack/plugins/cloud/serializers/task.py:42 msgid "Protocols" msgstr "协议组" -#: assets/models/asset.py:189 assets/models/user.py:190 +#: assets/models/asset.py:219 assets/models/user.py:190 #: perms/models/asset_permission.py:100 #: xpack/plugins/change_auth_plan/models/asset.py:44 #: xpack/plugins/gathered_user/models.py:24 msgid "Nodes" msgstr "节点" -#: assets/models/asset.py:190 assets/models/cmd_filter.py:22 +#: assets/models/asset.py:220 assets/models/cmd_filter.py:22 #: assets/models/domain.py:66 assets/models/label.py:22 msgid "Is active" msgstr "激活" -#: assets/models/asset.py:193 assets/models/cluster.py:19 +#: assets/models/asset.py:223 assets/models/cluster.py:19 #: assets/models/user.py:187 assets/models/user.py:340 templates/_nav.html:44 msgid "Admin user" msgstr "特权用户" -#: assets/models/asset.py:196 +#: assets/models/asset.py:226 msgid "Public IP" msgstr "公网IP" -#: assets/models/asset.py:197 +#: assets/models/asset.py:227 msgid "Asset number" msgstr "资产编号" -#: assets/models/asset.py:200 -msgid "Vendor" -msgstr "制造商" - -#: assets/models/asset.py:201 -msgid "Model" -msgstr "型号" - -#: assets/models/asset.py:202 -msgid "Serial number" -msgstr "序列号" - -#: assets/models/asset.py:204 -msgid "CPU model" -msgstr "CPU型号" - -#: assets/models/asset.py:205 -msgid "CPU count" -msgstr "CPU数量" - -#: assets/models/asset.py:206 -msgid "CPU cores" -msgstr "CPU核数" - -#: assets/models/asset.py:207 -msgid "CPU vcpus" -msgstr "CPU总数" - -#: assets/models/asset.py:208 -msgid "Memory" -msgstr "内存" - -#: assets/models/asset.py:209 -msgid "Disk total" -msgstr "硬盘大小" - -#: assets/models/asset.py:210 -msgid "Disk info" -msgstr "硬盘信息" - -#: assets/models/asset.py:212 -msgid "OS" -msgstr "操作系统" - -#: assets/models/asset.py:213 -msgid "OS version" -msgstr "系统版本" - -#: assets/models/asset.py:214 -msgid "OS arch" -msgstr "系统架构" - -#: assets/models/asset.py:215 -msgid "Hostname raw" -msgstr "主机名原始" - -#: assets/models/asset.py:217 templates/_nav.html:46 +#: assets/models/asset.py:229 templates/_nav.html:46 msgid "Labels" msgstr "标签管理" -#: assets/models/asset.py:218 assets/models/base.py:183 +#: assets/models/asset.py:230 assets/models/base.py:183 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:26 #: assets/models/cmd_filter.py:67 assets/models/group.py:21 #: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:25 @@ -570,7 +571,7 @@ msgstr "标签管理" msgid "Created by" msgstr "创建者" -#: assets/models/asset.py:219 assets/models/base.py:181 +#: assets/models/asset.py:231 assets/models/base.py:181 #: assets/models/cluster.py:26 assets/models/domain.py:27 #: assets/models/gathered_user.py:19 assets/models/group.py:22 #: assets/models/label.py:25 common/db/models.py:72 common/mixins/models.py:50 @@ -875,19 +876,14 @@ msgstr "协议重复: {}" msgid "Domain name" msgstr "网域名称" -#: assets/serializers/asset.py:69 +#: assets/serializers/asset.py:70 msgid "Nodes name" msgstr "节点名称" -#: assets/serializers/asset.py:103 +#: assets/serializers/asset.py:104 msgid "Hardware info" msgstr "硬件信息" -#: assets/serializers/asset.py:104 assets/serializers/system_user.py:276 -#: orgs/mixins/serializers.py:26 -msgid "Org name" -msgstr "组织名称" - #: assets/serializers/asset.py:105 msgid "Admin user display" msgstr "特权用户名称" @@ -988,6 +984,10 @@ msgstr "仅允许自动登录的系统用户" msgid "System user name" msgstr "系统用户名称" +#: assets/serializers/system_user.py:276 orgs/mixins/serializers.py:26 +msgid "Org name" +msgstr "组织名称" + #: assets/serializers/system_user.py:285 msgid "Asset hostname" msgstr "资产主机名" @@ -1501,7 +1501,7 @@ msgstr "{ApplicationPermission} 添加 {SystemUser}" msgid "{ApplicationPermission} REMOVE {SystemUser}" msgstr "{ApplicationPermission} 移除 {SystemUser}" -#: authentication/api/connection_token.py:239 +#: authentication/api/connection_token.py:248 msgid "Invalid token" msgstr "无效的令牌" @@ -1880,6 +1880,7 @@ msgstr "代码错误" #: authentication/templates/authentication/_msg_different_city.html:3 #: authentication/templates/authentication/_msg_reset_password.html:3 #: authentication/templates/authentication/_msg_rest_password_success.html:2 +#: authentication/templates/authentication/_msg_rest_public_key_success.html:2 #: jumpserver/conf.py:269 #: perms/templates/perms/_msg_item_permissions_expire.html:3 #: perms/templates/perms/_msg_permed_items_expire.html:3 @@ -1929,6 +1930,7 @@ msgid "Your password has just been successfully updated" msgstr "你的密码刚刚成功更新" #: authentication/templates/authentication/_msg_rest_password_success.html:9 +#: authentication/templates/authentication/_msg_rest_public_key_success.html:9 msgid "Browser" msgstr "浏览器" @@ -1939,9 +1941,20 @@ msgid "" msgstr "如果这次密码更新不是由你发起的,那么你的账号可能存在安全问题" #: authentication/templates/authentication/_msg_rest_password_success.html:13 +#: authentication/templates/authentication/_msg_rest_public_key_success.html:13 msgid "If you have any questions, you can contact the administrator" msgstr "如果有疑问或需求,请联系系统管理员" +#: authentication/templates/authentication/_msg_rest_public_key_success.html:5 +msgid "Your public key has just been successfully updated" +msgstr "你的公钥刚刚成功更新" + +#: authentication/templates/authentication/_msg_rest_public_key_success.html:12 +msgid "" +"If the public key update was not initiated by you, your account may have " +"security issues" +msgstr "如果这次公钥更新不是由你发起的,那么你的账号可能存在安全问题" + #: authentication/templates/authentication/login.html:143 msgid "Welcome back, please enter username and password to login" msgstr "欢迎回来,请输入用户名和密码登录" @@ -3262,7 +3275,9 @@ msgstr "显示未分组节点" #: settings/serializers/other.py:30 msgid "Perm single to ungroup node" -msgstr "放置单独授权的资产到未分组节点, 避免能看到资产所在节点,但该节点未被授权的问题" +msgstr "" +"放置单独授权的资产到未分组节点, 避免能看到资产所在节点,但该节点未被授权的问" +"题" #: settings/serializers/other.py:34 msgid "Help Docs URL" @@ -3280,14 +3295,6 @@ msgstr "支持链接" msgid "default: http://www.jumpserver.org/support/" msgstr "默认: http://www.jumpserver.org/support/" -#: settings/serializers/other.py:44 -msgid "Help Website URL" -msgstr "官网链接" - -#: settings/serializers/other.py:45 -msgid "default: http://www.jumpserver.org" -msgstr "如: http://dev.jumpserver.org:8080" - #: settings/serializers/security.py:8 msgid "Password minimum length" msgstr "密码最小长度" @@ -5064,19 +5071,23 @@ msgstr "重置密码" msgid "Reset password success" msgstr "重置密码成功" -#: users/notifications.py:111 +#: users/notifications.py:117 +msgid "Reset public key success" +msgstr "重置公钥成功" + +#: users/notifications.py:143 msgid "Password is about expire" msgstr "密码即将过期" -#: users/notifications.py:139 +#: users/notifications.py:171 msgid "Account is about expire" msgstr "账号即将过期" -#: users/notifications.py:161 +#: users/notifications.py:193 msgid "Reset SSH Key" msgstr "重置 SSH 密钥" -#: users/notifications.py:182 +#: users/notifications.py:214 msgid "Reset MFA" msgstr "重置 MFA" @@ -5523,8 +5534,8 @@ msgstr "* 新密码不能是最近 {} 次的密码" msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: xpack/plugins/change_auth_plan/api/app.py:113 -#: xpack/plugins/change_auth_plan/api/asset.py:100 +#: xpack/plugins/change_auth_plan/api/app.py:114 +#: xpack/plugins/change_auth_plan/api/asset.py:101 msgid "The parameter 'action' must be [{}]" msgstr "参数 'action' 必须是 [{}]" @@ -5655,15 +5666,15 @@ msgstr "* 请输入正确的密码长度" msgid "* Password length range 6-30 bits" msgstr "* 密码长度范围 6-30 位" -#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:248 +#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:249 msgid "Invalid/incorrect password" msgstr "无效/错误 密码" -#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:250 +#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:251 msgid "Failed to connect to the host" msgstr "连接主机失败" -#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:252 +#: xpack/plugins/change_auth_plan/task_handlers/base/handler.py:253 msgid "Data could not be sent to remote" msgstr "无法将数据发送到远程" @@ -6021,7 +6032,7 @@ msgstr "执行次数" msgid "Instance count" msgstr "实例个数" -#: xpack/plugins/cloud/utils.py:65 +#: xpack/plugins/cloud/utils.py:68 msgid "Account unavailable" msgstr "账户无效" @@ -6109,6 +6120,12 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#~ msgid "Help Website URL" +#~ msgstr "官网链接" + +#~ msgid "default: http://www.jumpserver.org" +#~ msgstr "如: http://dev.jumpserver.org:8080" + #~ msgid "One-time password invalid, or ntp sync server time" #~ msgstr "MFA 验证码不正确,或者服务器端时间不对" diff --git a/apps/users/api/profile.py b/apps/users/api/profile.py index 372ed5809..14aa1d8f6 100644 --- a/apps/users/api/profile.py +++ b/apps/users/api/profile.py @@ -5,7 +5,10 @@ from rest_framework import generics from common.permissions import IsOrgAdmin from rest_framework.permissions import IsAuthenticated -from users.notifications import ResetPasswordMsg, ResetPasswordSuccessMsg, ResetSSHKeyMsg +from users.notifications import ( + ResetPasswordMsg, ResetPasswordSuccessMsg, ResetSSHKeyMsg, + ResetPublicKeySuccessMsg, +) from common.permissions import ( IsCurrentUserOrReadOnly ) @@ -87,4 +90,4 @@ class UserPublicKeyApi(generics.RetrieveUpdateAPIView): def perform_update(self, serializer): super().perform_update(serializer) - ResetPasswordSuccessMsg(self.get_object(), self.request).publish_async() + ResetPublicKeySuccessMsg(self.get_object(), self.request).publish_async() diff --git a/apps/users/notifications.py b/apps/users/notifications.py index d1fbad546..c9e5a0521 100644 --- a/apps/users/notifications.py +++ b/apps/users/notifications.py @@ -105,6 +105,38 @@ class ResetPasswordSuccessMsg(UserMessage): return cls(user, request) +class ResetPublicKeySuccessMsg(UserMessage): + def __init__(self, user, request): + super().__init__(user) + self.ip_address = get_request_ip_or_data(request) + self.browser = get_request_user_agent(request) + + def get_html_msg(self) -> dict: + user = self.user + + subject = _('Reset public key success') + context = { + 'name': user.name, + 'ip_address': self.ip_address, + 'browser': self.browser, + } + message = render_to_string('authentication/_msg_rest_public_key_success.html', context) + return { + 'subject': subject, + 'message': message + } + + @classmethod + def gen_test_msg(cls): + from users.models import User + from rest_framework.test import APIRequestFactory + from rest_framework.request import Request + factory = APIRequestFactory() + request = Request(factory.get('/notes/')) + user = User.objects.first() + return cls(user, request) + + class PasswordExpirationReminderMsg(UserMessage): def get_html_msg(self) -> dict: user = self.user From 7286b1b09ea23cdfb5e55fad5fa9ce62bc9f65ac Mon Sep 17 00:00:00 2001 From: Michael Bai <baijiangjie@gmail.com> Date: Fri, 12 Nov 2021 11:04:38 +0800 Subject: [PATCH 06/25] =?UTF-8?q?fix:=20=E7=A7=BB=E5=8A=A8=E3=80=90?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E8=AE=BE=E7=BD=AE=E4=BF=9D=E7=95=99=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E3=80=91=E8=87=B3=E5=AE=9A=E6=9C=9F=E6=B8=85=E7=90=86?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/serializers/cleaning.py | 5 +++++ apps/settings/serializers/terminal.py | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/settings/serializers/cleaning.py b/apps/settings/serializers/cleaning.py index 39aae4a80..68e8092cb 100644 --- a/apps/settings/serializers/cleaning.py +++ b/apps/settings/serializers/cleaning.py @@ -25,3 +25,8 @@ class CleaningSerializer(serializers.Serializer): min_value=1, max_value=9999, label=_("Cloud sync record keep days"), help_text=_("Unit: day") ) + TERMINAL_SESSION_KEEP_DURATION = serializers.IntegerField( + min_value=1, max_value=99999, required=True, label=_('Session keep duration'), + help_text=_('Unit: days, Session, record, command will be delete if more than duration, only in database') + ) + diff --git a/apps/settings/serializers/terminal.py b/apps/settings/serializers/terminal.py index 0410a7517..12858913d 100644 --- a/apps/settings/serializers/terminal.py +++ b/apps/settings/serializers/terminal.py @@ -25,10 +25,6 @@ class TerminalSettingSerializer(serializers.Serializer): TERMINAL_ASSET_LIST_SORT_BY = serializers.ChoiceField(SORT_BY_CHOICES, required=False, label=_('List sort by')) TERMINAL_ASSET_LIST_PAGE_SIZE = serializers.ChoiceField(PAGE_SIZE_CHOICES, required=False, label=_('List page size')) - TERMINAL_SESSION_KEEP_DURATION = serializers.IntegerField( - min_value=1, max_value=99999, required=True, label=_('Session keep duration'), - help_text=_('Unit: days, Session, record, command will be delete if more than duration, only in database') - ) TERMINAL_TELNET_REGEX = serializers.CharField( allow_blank=True, max_length=1024, required=False, label=_('Telnet login regex'), help_text=_("The login success message varies with devices. " From 4260fe14249af90fc968802c113925e36e780855 Mon Sep 17 00:00:00 2001 From: xinwen <coderWen@126.com> Date: Fri, 12 Nov 2021 15:54:09 +0800 Subject: [PATCH 07/25] =?UTF-8?q?fix:=20=E8=B5=84=E4=BA=A7=20cpu=5Finfo=20?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E4=B8=8D=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/asset.py | 1 + apps/locale/zh/LC_MESSAGES/django.po | 168 ++++++++++++++------------- 2 files changed, 88 insertions(+), 81 deletions(-) diff --git a/apps/assets/serializers/asset.py b/apps/assets/serializers/asset.py index b13eda715..0182dfc94 100644 --- a/apps/assets/serializers/asset.py +++ b/apps/assets/serializers/asset.py @@ -103,6 +103,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer): 'port': {'write_only': True}, 'hardware_info': {'label': _('Hardware info'), 'read_only': True}, 'admin_user_display': {'label': _('Admin user display'), 'read_only': True}, + 'cpu_info': {'label': _('CPU Info')}, } def get_fields(self): diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 0b4d4b4be..57dae93a7 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-12 15:28+0800\n" +"POT-Creation-Date: 2021-11-12 16:51+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler <ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n" @@ -171,16 +171,6 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. " msgid "Username" msgstr "用户名" -msgid "IP Black List" -msgstr "IP 黑名单" - -msgid "" -"Format for comma-delimited string. Such as: " -"192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64" -msgstr "" -"格式为逗号分隔的字符串。例如: 192.168.10.1, 192.168.1.0/24, " -"10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 (支持网域)" - #: acls/serializers/login_asset_acl.py:24 msgid "" "Format for comma-delimited string, with * indicating a match all. Such as: " @@ -235,7 +225,7 @@ msgstr "组织 `{}` 不存在" msgid "None of the reviewers belong to Organization `{}`" msgstr "所有复核人都不属于组织 `{}`" -#: acls/serializers/rules/rules.py:20 +#: acls/serializers/rules/rules.py:20 settings/serializers/security.py:35 #: xpack/plugins/cloud/serializers/task.py:23 msgid "IP address invalid: `{}`" msgstr "IP 地址无效: `{}`" @@ -455,7 +445,7 @@ msgstr "基础" msgid "Charset" msgstr "编码" -#: assets/models/asset.py:142 assets/serializers/asset.py:174 +#: assets/models/asset.py:142 assets/serializers/asset.py:175 #: tickets/models/ticket.py:50 msgid "Meta" msgstr "元数据" @@ -888,6 +878,10 @@ msgstr "硬件信息" msgid "Admin user display" msgstr "特权用户名称" +#: assets/serializers/asset.py:106 +msgid "CPU Info" +msgstr "CPU信息" + #: assets/serializers/base.py:41 msgid "private key invalid" msgstr "密钥不合法" @@ -1308,12 +1302,12 @@ msgstr "" msgid "Auth Token" msgstr "认证令牌" -#: audits/signals_handler.py:68 authentication/views/login.py:169 +#: audits/signals_handler.py:68 authentication/views/login.py:170 #: notifications/backends/__init__.py:11 users/models/user.py:596 msgid "WeCom" msgstr "企业微信" -#: audits/signals_handler.py:69 authentication/views/login.py:175 +#: audits/signals_handler.py:69 authentication/views/login.py:176 #: notifications/backends/__init__.py:12 users/models/user.py:597 msgid "DingTalk" msgstr "钉钉" @@ -1662,47 +1656,47 @@ msgstr "等待登录复核处理" msgid "Login confirm ticket was {}" msgstr "登录复核 {}" -#: authentication/errors.py:243 +#: authentication/errors.py:187 authentication/errors.py:251 msgid "IP is not allowed" msgstr "来源 IP 不被允许登录" -#: authentication/errors.py:250 +#: authentication/errors.py:258 msgid "Time Period is not allowed" msgstr "该 时间段 不被允许登录" -#: authentication/errors.py:283 +#: authentication/errors.py:291 msgid "SSO auth closed" msgstr "SSO 认证关闭了" -#: authentication/errors.py:288 authentication/mixins.py:344 +#: authentication/errors.py:296 authentication/mixins.py:350 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" -#: authentication/errors.py:297 authentication/mixins.py:351 +#: authentication/errors.py:305 authentication/mixins.py:357 msgid "You should to change your password before login" msgstr "登录完成前,请先修改密码" -#: authentication/errors.py:306 authentication/mixins.py:358 +#: authentication/errors.py:314 authentication/mixins.py:364 msgid "Your password has expired, please reset before logging in" msgstr "您的密码已过期,先修改再登录" -#: authentication/errors.py:340 +#: authentication/errors.py:348 msgid "Your password is invalid" msgstr "您的密码无效" -#: authentication/errors.py:346 +#: authentication/errors.py:354 msgid "No upload or download permission" msgstr "没有上传下载权限" -#: authentication/errors.py:358 +#: authentication/errors.py:366 msgid "Please enter MFA code" msgstr "请输入6位动态安全码" -#: authentication/errors.py:362 +#: authentication/errors.py:370 msgid "Please enter SMS code" msgstr "请输入短信验证码" -#: authentication/errors.py:366 users/exceptions.py:15 +#: authentication/errors.py:374 users/exceptions.py:15 msgid "Phone not set" msgstr "手机号没有设置" @@ -1774,11 +1768,11 @@ msgstr "设置手机号码启用" msgid "Clear phone number to disable" msgstr "清空手机号码禁用" -#: authentication/mixins.py:305 +#: authentication/mixins.py:311 msgid "The MFA type({}) is not supported" msgstr "该 MFA 方法 ({}) 不被支持" -#: authentication/mixins.py:334 +#: authentication/mixins.py:340 msgid "Please change your password" msgstr "请修改密码" @@ -1827,7 +1821,7 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: settings/serializers/security.py:25 users/models/user.py:458 +#: settings/serializers/security.py:42 users/models/user.py:458 #: users/serializers/profile.py:99 users/templates/users/mfa_setting.html:60 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" @@ -2097,12 +2091,12 @@ msgstr "正在跳转到 {} 认证" msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: authentication/views/login.py:181 notifications/backends/__init__.py:14 +#: authentication/views/login.py:182 notifications/backends/__init__.py:14 #: users/models/user.py:598 msgid "FeiShu" msgstr "飞书" -#: authentication/views/login.py:270 +#: authentication/views/login.py:271 msgid "" "Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n" " Don't close this page" @@ -2110,15 +2104,15 @@ msgstr "" "等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n" " 不要关闭本页面" -#: authentication/views/login.py:275 +#: authentication/views/login.py:276 msgid "No ticket found" msgstr "没有发现工单" -#: authentication/views/login.py:307 +#: authentication/views/login.py:308 msgid "Logout success" msgstr "退出登录成功" -#: authentication/views/login.py:308 +#: authentication/views/login.py:309 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" @@ -3295,61 +3289,73 @@ msgstr "支持链接" msgid "default: http://www.jumpserver.org/support/" msgstr "默认: http://www.jumpserver.org/support/" -#: settings/serializers/security.py:8 +#: settings/serializers/security.py:10 msgid "Password minimum length" msgstr "密码最小长度" -#: settings/serializers/security.py:12 +#: settings/serializers/security.py:14 msgid "Admin user password minimum length" msgstr "管理员密码最小长度" -#: settings/serializers/security.py:15 +#: settings/serializers/security.py:17 msgid "Must contain capital" msgstr "必须包含大写字符" -#: settings/serializers/security.py:17 +#: settings/serializers/security.py:20 msgid "Must contain lowercase" msgstr "必须包含小写字符" -#: settings/serializers/security.py:18 +#: settings/serializers/security.py:23 msgid "Must contain numeric" msgstr "必须包含数字" -#: settings/serializers/security.py:19 +#: settings/serializers/security.py:26 msgid "Must contain special" msgstr "必须包含特殊字符" -#: settings/serializers/security.py:26 +#: settings/serializers/security.py:43 msgid "All users" msgstr "所有用户" -#: settings/serializers/security.py:27 +#: settings/serializers/security.py:44 msgid "Only admin users" msgstr "仅管理员" -#: settings/serializers/security.py:29 +#: settings/serializers/security.py:46 msgid "Global MFA auth" msgstr "全局启用 MFA 认证" -#: settings/serializers/security.py:33 +#: settings/serializers/security.py:50 msgid "Limit the number of login failures" msgstr "限制登录失败次数" -#: settings/serializers/security.py:37 +#: settings/serializers/security.py:54 msgid "Block logon interval" msgstr "禁止登录时间间隔" -#: settings/serializers/security.py:39 +#: settings/serializers/security.py:56 msgid "" "Unit: minute, If the user has failed to log in for a limited number of " "times, no login is allowed during this time interval." msgstr "单位:分, 当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录" -#: settings/serializers/security.py:45 +#: settings/serializers/security.py:61 +msgid "IP Black List" +msgstr "IP 黑名单" + +#: settings/serializers/security.py:64 +msgid "" +"Format for comma-delimited string. Such as: 192.168.10.1, 192.168.1.0/24, " +"10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64" +msgstr "" +"格式为逗号分隔的字符串。例如: 192.168.10.1, 192.168.1.0/24, " +"10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 (支持网域)" + +#: settings/serializers/security.py:70 msgid "User password expiration" msgstr "用户密码过期时间" -#: settings/serializers/security.py:47 +#: settings/serializers/security.py:72 msgid "" "Unit: day, If the user does not update the password during the time, the " "user password will expire failure;The password expiration reminder mail will " @@ -3359,55 +3365,55 @@ msgstr "" "单位:天, 如果用户在此期间没有更新密码,用户密码将过期失效; 密码过期提醒邮件" "将在密码过期前5天内由系统(每天)自动发送给用户" -#: settings/serializers/security.py:54 +#: settings/serializers/security.py:79 msgid "Number of repeated historical passwords" msgstr "不能设置近几次密码" -#: settings/serializers/security.py:56 +#: settings/serializers/security.py:81 msgid "" "Tip: When the user resets the password, it cannot be the previous n " "historical passwords of the user" msgstr "提示:用户重置密码时,不能为该用户前几次使用过的密码" -#: settings/serializers/security.py:61 +#: settings/serializers/security.py:86 msgid "Only single device login" msgstr "仅一台设备登录" -#: settings/serializers/security.py:62 +#: settings/serializers/security.py:87 msgid "Next device login, pre login will be logout" msgstr "下个设备登录,上次登录会被顶掉" -#: settings/serializers/security.py:65 +#: settings/serializers/security.py:90 msgid "Only exist user login" msgstr "仅已存在用户登录" -#: settings/serializers/security.py:66 +#: settings/serializers/security.py:91 msgid "If enable, CAS、OIDC auth will be failed, if user not exist yet" msgstr "开启后,如果系统中不存在该用户,CAS、OIDC 登录将会失败" -#: settings/serializers/security.py:69 +#: settings/serializers/security.py:94 msgid "Only from source login" msgstr "仅从用户来源登录" -#: settings/serializers/security.py:70 +#: settings/serializers/security.py:95 msgid "Only log in from the user source property" msgstr "开启后,如果用户来源为本地,CAS、OIDC 登录将会失败" -#: settings/serializers/security.py:74 +#: settings/serializers/security.py:99 msgid "MFA verify TTL" msgstr "MFA 校验有效期" -#: settings/serializers/security.py:75 +#: settings/serializers/security.py:101 msgid "" "Unit: second, The verification MFA takes effect only when you view the " "account password" msgstr "单位: 秒, 目前仅在查看账号密码校验 MFA 时生效" -#: settings/serializers/security.py:79 +#: settings/serializers/security.py:106 msgid "Enable Login dynamic code" msgstr "启用登录附加码" -#: settings/serializers/security.py:80 +#: settings/serializers/security.py:107 msgid "" "The password and additional code are sent to a third party authentication " "system for verification" @@ -3415,89 +3421,89 @@ msgstr "" "密码和附加码一并发送给第三方认证系统进行校验, 如:有的第三方认证系统,需要 密" "码+6位数字 完成认证" -#: settings/serializers/security.py:85 +#: settings/serializers/security.py:112 msgid "MFA in login page" msgstr "MFA 在登录页面" -#: settings/serializers/security.py:86 +#: settings/serializers/security.py:113 msgid "Eu security regulations(GDPR) require MFA to be on the login page" msgstr "欧盟数据安全法规(GDPR) 要求 MFA 在登录页面,来确保系统登录安全" -#: settings/serializers/security.py:89 +#: settings/serializers/security.py:116 msgid "Enable Login captcha" msgstr "启用登录验证码" -#: settings/serializers/security.py:90 +#: settings/serializers/security.py:117 msgid "Enable captcha to prevent robot authentication" msgstr "开启验证码,防止机器人登录" -#: settings/serializers/security.py:110 +#: settings/serializers/security.py:137 msgid "Enable terminal register" msgstr "终端注册" -#: settings/serializers/security.py:111 +#: settings/serializers/security.py:139 msgid "" "Allow terminal register, after all terminal setup, you should disable this " "for security" msgstr "是否允许终端注册,当所有终端启动后,为了安全应该关闭" -#: settings/serializers/security.py:114 +#: settings/serializers/security.py:143 msgid "Enable watermark" msgstr "开启水印" -#: settings/serializers/security.py:115 +#: settings/serializers/security.py:144 msgid "Enabled, the web session and replay contains watermark information" msgstr "启用后,Web 会话和录像将包含水印信息" -#: settings/serializers/security.py:119 +#: settings/serializers/security.py:148 msgid "Connection max idle time" msgstr "连接最大空闲时间" -#: settings/serializers/security.py:120 +#: settings/serializers/security.py:149 msgid "If idle time more than it, disconnect connection Unit: minute" msgstr "提示:如果超过该配置没有操作,连接会被断开 (单位:分)" -#: settings/serializers/security.py:123 +#: settings/serializers/security.py:152 msgid "Remember manual auth" msgstr "保存手动输入密码" -#: settings/serializers/security.py:126 +#: settings/serializers/security.py:155 msgid "Enable change auth secure mode" msgstr "启用改密安全模式" -#: settings/serializers/security.py:129 +#: settings/serializers/security.py:158 msgid "Insecure command alert" msgstr "危险命令告警" -#: settings/serializers/security.py:132 +#: settings/serializers/security.py:161 msgid "Email recipient" msgstr "邮件收件人" -#: settings/serializers/security.py:133 +#: settings/serializers/security.py:162 msgid "Multiple user using , split" msgstr "多个用户,使用 , 分割" -#: settings/serializers/security.py:136 +#: settings/serializers/security.py:165 msgid "Batch command execution" msgstr "批量命令执行" -#: settings/serializers/security.py:137 +#: settings/serializers/security.py:166 msgid "Allow user run batch command or not using ansible" msgstr "是否允许用户使用 ansible 执行批量命令" -#: settings/serializers/security.py:140 +#: settings/serializers/security.py:169 msgid "Session share" msgstr "会话分享" -#: settings/serializers/security.py:141 +#: settings/serializers/security.py:170 msgid "Enabled, Allows user active session to be shared with other users" msgstr "开启后允许用户分享已连接的资产会话给它人,协同工作" -#: settings/serializers/security.py:144 +#: settings/serializers/security.py:173 msgid "Remote Login Protection" msgstr "异地登录保护" -#: settings/serializers/security.py:145 +#: settings/serializers/security.py:175 msgid "" "The system determines whether the login IP address belongs to a common login " "city. If the account is logged in from a common login city, the system sends " From 187977c04a25d169b80bcfd9339fe01c14dd9c72 Mon Sep 17 00:00:00 2001 From: xinwen <coderWen@126.com> Date: Fri, 12 Nov 2021 16:59:56 +0800 Subject: [PATCH 08/25] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=20CPU=20info=20?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/asset.py | 2 +- apps/locale/zh/LC_MESSAGES/django.po | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/assets/serializers/asset.py b/apps/assets/serializers/asset.py index 0182dfc94..1bb5afe24 100644 --- a/apps/assets/serializers/asset.py +++ b/apps/assets/serializers/asset.py @@ -103,7 +103,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer): 'port': {'write_only': True}, 'hardware_info': {'label': _('Hardware info'), 'read_only': True}, 'admin_user_display': {'label': _('Admin user display'), 'read_only': True}, - 'cpu_info': {'label': _('CPU Info')}, + 'cpu_info': {'label': _('CPU info')}, } def get_fields(self): diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 57dae93a7..9c34f0e40 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -879,7 +879,7 @@ msgid "Admin user display" msgstr "特权用户名称" #: assets/serializers/asset.py:106 -msgid "CPU Info" +msgid "CPU info" msgstr "CPU信息" #: assets/serializers/base.py:41 From 4a786baf4edb688477f701e8d9b12b5d12c96166 Mon Sep 17 00:00:00 2001 From: feng626 <1304903146@qq.com> Date: Fri, 12 Nov 2021 17:32:22 +0800 Subject: [PATCH 09/25] =?UTF-8?q?perf:=20=E5=B7=A5=E5=8D=95=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E4=BF=A1=E6=81=AF=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ticket/meta/ticket_type/apply_application.py | 11 ++++++++--- .../ticket/meta/ticket_type/apply_asset.py | 7 +++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/tickets/serializers/ticket/meta/ticket_type/apply_application.py b/apps/tickets/serializers/ticket/meta/ticket_type/apply_application.py index c3f75ed6d..98e303b1c 100644 --- a/apps/tickets/serializers/ticket/meta/ticket_type/apply_application.py +++ b/apps/tickets/serializers/ticket/meta/ticket_type/apply_application.py @@ -77,14 +77,19 @@ class ApplySerializer(serializers.Serializer): type = self.root.initial_data['meta'].get('apply_type') org_id = self.root.initial_data.get('org_id') with tmp_to_org(org_id): - applications = Application.objects.filter(id__in=apply_applications, type=type).values_list('id', flat=True) + applications = Application.objects.filter( + id__in=apply_applications, type=type + ).values_list('id', flat=True) return list(applications) def validate(self, attrs): - apply_date_start = attrs['apply_date_start'] - apply_date_expired = attrs['apply_date_expired'] + apply_date_start = attrs['apply_date_start'].strftime('%Y-%m-%d %H:%M:%S') + apply_date_expired = attrs['apply_date_expired'].strftime('%Y-%m-%d %H:%M:%S') if apply_date_expired <= apply_date_start: error = _('The expiration date should be greater than the start date') raise serializers.ValidationError({'apply_date_expired': error}) + + attrs['apply_date_start'] = apply_date_start + attrs['apply_date_expired'] = apply_date_expired return attrs diff --git a/apps/tickets/serializers/ticket/meta/ticket_type/apply_asset.py b/apps/tickets/serializers/ticket/meta/ticket_type/apply_asset.py index a4b78e7d5..19da551ec 100644 --- a/apps/tickets/serializers/ticket/meta/ticket_type/apply_asset.py +++ b/apps/tickets/serializers/ticket/meta/ticket_type/apply_asset.py @@ -64,10 +64,13 @@ class ApplySerializer(serializers.Serializer): )) def validate(self, attrs): - apply_date_start = attrs['apply_date_start'] - apply_date_expired = attrs['apply_date_expired'] + apply_date_start = attrs['apply_date_start'].strftime('%Y-%m-%d %H:%M:%S') + apply_date_expired = attrs['apply_date_expired'].strftime('%Y-%m-%d %H:%M:%S') if apply_date_expired <= apply_date_start: error = _('The expiration date should be greater than the start date') raise serializers.ValidationError({'apply_date_expired': error}) + + attrs['apply_date_start'] = apply_date_start + attrs['apply_date_expired'] = apply_date_expired return attrs From 454d3cba969fe2ed15fabcdcd26b71a8750bec49 Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Mon, 15 Nov 2021 16:00:11 +0800 Subject: [PATCH 10/25] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=99=BB?= =?UTF-8?q?=E5=BD=95mfa=E6=97=B6=EF=BC=8C=E9=80=89=E6=8B=A9=E4=BA=86?= =?UTF-8?q?=E7=A6=81=E7=94=A8=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.po | 2 +- apps/templates/_mfa_login_field.html | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 9c34f0e40..1ad622d52 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -1690,7 +1690,7 @@ msgstr "没有上传下载权限" #: authentication/errors.py:366 msgid "Please enter MFA code" -msgstr "请输入6位动态安全码" +msgstr "请输入验证码" #: authentication/errors.py:370 msgid "Please enter SMS code" diff --git a/apps/templates/_mfa_login_field.html b/apps/templates/_mfa_login_field.html index 6164e0a4c..8a9a1eb71 100644 --- a/apps/templates/_mfa_login_field.html +++ b/apps/templates/_mfa_login_field.html @@ -54,9 +54,13 @@ $(document).ready(function () { const mfaSelectRef = document.getElementById('mfa-select'); const preferMFA = localStorage.getItem(preferMFAKey); - if (preferMFA) { + const valueSelector = "value=" + preferMFA + const preferMFADisabled = $(`#mfa-select option[${valueSelector}]`).attr('disabled') + + if (preferMFA && !preferMFADisabled) { mfaSelectRef.value = preferMFA; } + const mfaSelect = mfaSelectRef.value; if (mfaSelect !== null) { selectChange(mfaSelect, true); @@ -73,7 +77,10 @@ $('.input-style').each(function (i, ele){ $(ele).attr('name', '').attr('required', false) }) - $('#mfa-' + name + ' .input-style').attr('name', 'code').attr('required', true) + $('#mfa-' + name + ' .input-style') + .attr('name', 'code') + .attr('required', true) + .focus() } function sendChallengeCode(currentBtn) { From a315e8888b6478cfea0c8fb0639c7dce073a6985 Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Mon, 15 Nov 2021 14:51:49 +0800 Subject: [PATCH 11/25] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E4=B8=80?= =?UTF-8?q?=E4=B8=8B=20win2016=20=E4=B8=8D=E5=86=8D=E5=86=85=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/migrations/0079_auto_20211102_1922.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/assets/migrations/0079_auto_20211102_1922.py b/apps/assets/migrations/0079_auto_20211102_1922.py index f0a05dc06..592df0259 100644 --- a/apps/assets/migrations/0079_auto_20211102_1922.py +++ b/apps/assets/migrations/0079_auto_20211102_1922.py @@ -16,6 +16,11 @@ def create_internal_platform(apps, schema_editor): name=name, defaults=defaults ) + win2016 = model.objects.filter(name='Windows2016').first() + if win2016: + win2016.internal = False + win2016.save(update_fields=['internal']) + class Migration(migrations.Migration): From 12a00969635f31fe75eaa6d9b06fccd0e2097807 Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Mon, 15 Nov 2021 14:27:11 +0800 Subject: [PATCH 12/25] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=87=8D?= =?UTF-8?q?=E7=BD=AEmfa=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/api/user.py | 13 +++++++------ apps/users/urls/api_urls.py | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/users/api/user.py b/apps/users/api/user.py index 2499e7b78..fe902e8c5 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -28,7 +28,7 @@ from ..filters import OrgRoleUserFilterBackend, UserFilter logger = get_logger(__name__) __all__ = [ 'UserViewSet', 'UserChangePasswordApi', - 'UserUnblockPKApi', 'UserResetOTPApi', + 'UserUnblockPKApi', 'UserResetMFAApi', ] @@ -199,7 +199,7 @@ class UserUnblockPKApi(UserQuerysetMixin, generics.UpdateAPIView): MFABlockUtils.unblock_user(username) -class UserResetOTPApi(UserQuerysetMixin, generics.RetrieveAPIView): +class UserResetMFAApi(UserQuerysetMixin, generics.RetrieveAPIView): permission_classes = (IsOrgAdmin,) serializer_class = serializers.ResetOTPSerializer @@ -209,9 +209,10 @@ class UserResetOTPApi(UserQuerysetMixin, generics.RetrieveAPIView): msg = _("Could not reset self otp, use profile reset instead") return Response({"error": msg}, status=401) - if user.mfa_enabled: - user.reset_mfa() - user.save() + backends = user.active_mfa_backends_mapper + for backend in backends: + if backend.can_disable(): + backend.disable() - ResetMFAMsg(user).publish_async() + ResetMFAMsg(user).publish_async() return Response({"msg": "success"}) diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py index 8b9c538bd..af24fc147 100644 --- a/apps/users/urls/api_urls.py +++ b/apps/users/urls/api_urls.py @@ -23,8 +23,8 @@ urlpatterns = [ path('profile/', api.UserProfileApi.as_view(), name='user-profile'), path('profile/password/', api.UserPasswordApi.as_view(), name='user-password'), path('profile/public-key/', api.UserPublicKeyApi.as_view(), name='user-public-key'), - path('otp/reset/', api.UserResetOTPApi.as_view(), name='my-otp-reset'), - path('users/<uuid:pk>/otp/reset/', api.UserResetOTPApi.as_view(), name='user-reset-otp'), + path('profile/mfa/reset/', api.UserResetMFAApi.as_view(), name='my-mfa-reset'), + path('users/<uuid:pk>/mfa/reset/', api.UserResetMFAApi.as_view(), name='user-reset-mfa'), path('users/<uuid:pk>/password/', api.UserChangePasswordApi.as_view(), name='change-user-password'), path('users/<uuid:pk>/password/reset/', api.UserResetPasswordApi.as_view(), name='user-reset-password'), path('users/<uuid:pk>/pubkey/reset/', api.UserResetPKApi.as_view(), name='user-public-key-reset'), From cb1c906db4ce58055422e737724bd1b46845a512 Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Mon, 15 Nov 2021 14:00:52 +0800 Subject: [PATCH 13/25] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=97=B6=E6=B2=A1=E6=9C=89=E7=BB=91=E5=AE=9Amfa?= =?UTF-8?q?=EF=BC=8C=E6=B2=A1=E6=9C=89=E8=B7=B3=E8=BD=AC=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: 首页登录如果没有则后续登录 --- apps/authentication/errors.py | 16 ++++------------ apps/authentication/mixins.py | 15 +++++++++++---- apps/authentication/views/login.py | 4 ++-- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/apps/authentication/errors.py b/apps/authentication/errors.py index 1e9000e67..f8bf7f32d 100644 --- a/apps/authentication/errors.py +++ b/apps/authentication/errors.py @@ -66,7 +66,6 @@ mfa_error_msg = _( ) mfa_required_msg = _("MFA required") mfa_unset_msg = _("MFA not set, please set it first") -otp_unset_msg = _("OTP not set, please set it first") login_confirm_required_msg = _("Login confirm required") login_confirm_wait_msg = _("Wait login confirm ticket for accept") login_confirm_error_msg = _("Login confirm ticket was {}") @@ -162,13 +161,11 @@ class BlockMFAError(AuthFailedNeedLogMixin, AuthFailedError): super().__init__(username=username, request=request, ip=ip) -class MFAUnsetError(AuthFailedNeedLogMixin, AuthFailedError): +class MFAUnsetError(Exception): error = reason_mfa_unset msg = mfa_unset_msg def __init__(self, user, request, url): - super().__init__(username=user.username, request=request) - self.user = user self.url = url @@ -354,21 +351,16 @@ class NotHaveUpDownLoadPerm(JMSException): default_detail = _('No upload or download permission') -class OTPBindRequiredError(JMSException): - default_detail = otp_unset_msg - - def __init__(self, url, *args, **kwargs): - super().__init__(*args, **kwargs) - self.url = url - - class MFACodeRequiredError(AuthFailedError): + error = 'mfa_code_required' msg = _("Please enter MFA code") class SMSCodeRequiredError(AuthFailedError): + error = 'sms_code_required' msg = _("Please enter SMS code") class UserPhoneNotSet(AuthFailedError): + error = 'phone_not_set' msg = _('Phone not set') diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 69daf6330..8ec2fc391 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -248,16 +248,24 @@ class MFAMixin: get_user_from_session: Callable get_request_ip: Callable + def _check_if_no_active_mfa(self, user): + active_mfa_mapper = user.active_mfa_backends_mapper + if not active_mfa_mapper: + url = reverse('authentication:user-otp-enable-start') + raise errors.MFAUnsetError(user, self.request, url) + def _check_login_page_mfa_if_need(self, user): if not settings.SECURITY_MFA_IN_LOGIN_PAGE: return + self._check_if_no_active_mfa(user) request = self.request data = request.data if hasattr(request, 'data') else request.POST code = data.get('code') mfa_type = data.get('mfa_type', 'otp') + if not code: - raise errors.MFACodeRequiredError + return self._do_check_user_mfa(code, mfa_type, user=user) def check_user_mfa_if_need(self, user): @@ -266,10 +274,9 @@ class MFAMixin: if not user.mfa_enabled: return + self._check_if_no_active_mfa(user) + active_mfa_mapper = user.active_mfa_backends_mapper - if not active_mfa_mapper: - url = reverse('authentication:user-otp-enable-start') - raise errors.MFAUnsetError(user, self.request, url) raise errors.MFARequiredError(mfa_types=tuple(active_mfa_mapper.keys())) def mark_mfa_ok(self, mfa_type): diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 79b6839b4..f8c760b43 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -122,10 +122,10 @@ class UserLoginView(mixins.AuthMixin, FormView): self.request.session.set_test_cookie() return self.render_to_response(context) except ( + errors.MFAUnsetError, errors.PasswordTooSimple, errors.PasswordRequireResetError, - errors.PasswordNeedUpdate, - errors.OTPBindRequiredError + errors.PasswordNeedUpdate ) as e: return redirect(e.url) except ( From 9bfbdea508b2047e8de42bdd2a0b4c27e96afe65 Mon Sep 17 00:00:00 2001 From: xinwen <coderWen@126.com> Date: Mon, 15 Nov 2021 16:29:22 +0800 Subject: [PATCH 14/25] =?UTF-8?q?fix:=20=E7=BB=88=E7=AB=AF=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=90=8E=EF=BC=8C=E5=BD=95=E5=83=8F=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E5=88=A0=E9=99=A4=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/models/storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/terminal/models/storage.py b/apps/terminal/models/storage.py index 7d4efcfbc..bb2495c7e 100644 --- a/apps/terminal/models/storage.py +++ b/apps/terminal/models/storage.py @@ -164,4 +164,4 @@ class ReplayStorage(CommonStorageModelMixin, CommonModelMixin): return storage.is_valid(src, target) def is_use(self): - return Terminal.objects.filter(replay_storage=self.name).exists() + return Terminal.objects.filter(replay_storage=self.name, is_deleted=False).exists() From d6395b64fab812112fbfcd904cebbce8f447a2b1 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 15 Nov 2021 16:51:05 +0800 Subject: [PATCH 15/25] merge: with dev (#7195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复登录mfa时,选择了禁用的 * fix: mfa focus Co-authored-by: ibuler <ibuler@qq.com> --- apps/templates/_mfa_login_field.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/templates/_mfa_login_field.html b/apps/templates/_mfa_login_field.html index 8a9a1eb71..31d59ec24 100644 --- a/apps/templates/_mfa_login_field.html +++ b/apps/templates/_mfa_login_field.html @@ -77,6 +77,14 @@ $('.input-style').each(function (i, ele){ $(ele).attr('name', '').attr('required', false) }) + + const currentMFAInput = $('#mfa-' + name + ' .input-style') + currentMFAInput.attr('name', 'code').attr('required', true) + + // 登录页时,不应该默认focus + if ($('input[name="username"]').length == 0) { + currentMFAInput.focus() + } $('#mfa-' + name + ' .input-style') .attr('name', 'code') .attr('required', true) From 7d9da9ff66991aaaed5c3c59769e923340577bfc Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Mon, 15 Nov 2021 16:53:35 +0800 Subject: [PATCH 16/25] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E5=90=8D?= =?UTF-8?q?=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/templates/_mfa_login_field.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/templates/_mfa_login_field.html b/apps/templates/_mfa_login_field.html index 31d59ec24..f4811a11a 100644 --- a/apps/templates/_mfa_login_field.html +++ b/apps/templates/_mfa_login_field.html @@ -78,17 +78,15 @@ $(ele).attr('name', '').attr('required', false) }) - const currentMFAInput = $('#mfa-' + name + ' .input-style') - currentMFAInput.attr('name', 'code').attr('required', true) + const currentMFAInputRef = $('#mfa-' + name + ' .input-style') + currentMFAInputRef.attr('name', 'code').attr('required', true) // 登录页时,不应该默认focus - if ($('input[name="username"]').length == 0) { - currentMFAInput.focus() + const usernameRef = $('input[name="username"]') + if (!usernameRef || usernameRef.length === 0) { + currentMFAInputRef.focus() } - $('#mfa-' + name + ' .input-style') - .attr('name', 'code') - .attr('required', true) - .focus() + currentMFAInputRef.attr('name', 'code').attr('required', true) } function sendChallengeCode(currentBtn) { From c5ff0d972bdf5602da5fffb64cf5fe29079c03e6 Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Mon, 15 Nov 2021 16:56:56 +0800 Subject: [PATCH 17/25] =?UTF-8?q?perf:=20=E5=8E=BB=E6=8E=89mfa=20input=20?= =?UTF-8?q?=E7=9A=84required=EF=BC=8C=E4=BB=A5=E4=B8=BA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E6=B2=A1=E6=9C=89=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/templates/_mfa_login_field.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/templates/_mfa_login_field.html b/apps/templates/_mfa_login_field.html index f4811a11a..52918acfd 100644 --- a/apps/templates/_mfa_login_field.html +++ b/apps/templates/_mfa_login_field.html @@ -75,7 +75,7 @@ } $('.input-style').each(function (i, ele){ - $(ele).attr('name', '').attr('required', false) + $(ele).attr('name', '') }) const currentMFAInputRef = $('#mfa-' + name + ' .input-style') @@ -86,7 +86,7 @@ if (!usernameRef || usernameRef.length === 0) { currentMFAInputRef.focus() } - currentMFAInputRef.attr('name', 'code').attr('required', true) + currentMFAInputRef.attr('name', 'code') } function sendChallengeCode(currentBtn) { From ed01f2f1fb3d7ca277805e296a416c30517971af Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 Nov 2021 11:32:25 +0800 Subject: [PATCH 18/25] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dxrdp=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E6=97=B6=E6=8A=A5=E9=94=99=20(#7202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复xrdp连接时报错 perf: 添加注释 * perf: 去掉import Co-authored-by: ibuler <ibuler@qq.com> --- apps/authentication/api/connection_token.py | 33 +++++++++++++-------- apps/authentication/errors.py | 6 ---- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 2ff4fa71c..4083edb90 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -26,7 +26,6 @@ from orgs.mixins.api import RootOrgViewMixin from common.http import is_true from perms.utils.asset.permission import get_asset_system_user_ids_with_actions_by_user from perms.models.asset_permission import Action -from authentication.errors import NotHaveUpDownLoadPerm from ..serializers import ( ConnectionTokenSerializer, ConnectionTokenSecretSerializer, @@ -96,22 +95,26 @@ class ClientProtocolMixin: drives_redirect = is_true(self.request.query_params.get('drives_redirect')) token = self.create_token(user, asset, application, system_user) + # 设置磁盘挂载 if drives_redirect and asset: systemuser_actions_mapper = get_asset_system_user_ids_with_actions_by_user(user, asset) - actions = systemuser_actions_mapper.get(system_user.id, []) + actions = systemuser_actions_mapper.get(system_user.id, 0) if actions & Action.UPDOWNLOAD: options['drivestoredirect:s'] = '*' - else: - raise NotHaveUpDownLoadPerm + # 全屏 options['screen mode id:i'] = '2' if full_screen else '1' + + # RDP Server 地址 address = settings.TERMINAL_RDP_ADDR if not address or address == 'localhost:3389': address = self.request.get_host().split(':')[0] + ':3389' options['full address:s'] = address + # 用户名 options['username:s'] = '{}|{}'.format(user.username, token) if system_user.ad_domain: options['domain:s'] = system_user.ad_domain + # 宽高 if width and height: options['desktopwidth:i'] = width options['desktopheight:i'] = height @@ -160,13 +163,16 @@ class ClientProtocolMixin: asset, application, system_user, user = self.get_request_resource(serializer) protocol = system_user.protocol username = user.username - name = '' + if protocol == 'rdp': name, config = self.get_rdp_file_content(serializer) - elif protocol == 'vnc': - raise HttpResponse(status=404, data={"error": "VNC not support"}) - else: + elif protocol == 'ssh': + # Todo: + name = '' config = 'ssh://system_user@asset@user@jumpserver-ssh' + else: + raise ValueError('Protocol not support: {}'.format(protocol)) + filename = "{}-{}-jumpserver".format(username, name) data = { "filename": filename, @@ -179,8 +185,13 @@ class ClientProtocolMixin: @action(methods=['POST', 'GET'], detail=False, url_path='client-url', permission_classes=[IsValidUser]) def get_client_protocol_url(self, request, *args, **kwargs): serializer = self.get_valid_serializer() - protocol_data = self.get_client_protocol_data(serializer) - protocol_data = base64.b64encode(json.dumps(protocol_data).encode()).decode() + try: + protocol_data = self.get_client_protocol_data(serializer) + except ValueError as e: + return Response({'error': str(e)}, status=401) + + protocol_data = json.dumps(protocol_data).encode() + protocol_data = base64.b64encode(protocol_data).decode() data = { 'url': 'jms://{}'.format(protocol_data), } @@ -348,14 +359,12 @@ class UserConnectionTokenViewSet( raise serializers.ValidationError("User not valid, disabled or expired") system_user = get_object_or_404(SystemUser, id=value.get('system_user')) - asset = None app = None if value.get('type') == 'asset': asset = get_object_or_404(Asset, id=value.get('asset')) if not asset.is_active: raise serializers.ValidationError("Asset disabled") - has_perm, expired_at = asset_validate_permission(user, asset, system_user, 'connect') else: app = get_object_or_404(Application, id=value.get('application')) diff --git a/apps/authentication/errors.py b/apps/authentication/errors.py index f8bf7f32d..c17a1bccb 100644 --- a/apps/authentication/errors.py +++ b/apps/authentication/errors.py @@ -345,12 +345,6 @@ class PasswordInvalid(JMSException): default_detail = _('Your password is invalid') -class NotHaveUpDownLoadPerm(JMSException): - status_code = status.HTTP_403_FORBIDDEN - code = 'not_have_up_down_load_perm' - default_detail = _('No upload or download permission') - - class MFACodeRequiredError(AuthFailedError): error = 'mfa_code_required' msg = _("Please enter MFA code") From 22c9dfc0f21ee70dcff673fc43a5971f14d8fe02 Mon Sep 17 00:00:00 2001 From: xinwen <coderWen@126.com> Date: Tue, 16 Nov 2021 11:12:59 +0800 Subject: [PATCH 19/25] =?UTF-8?q?fix:=20=E7=B3=BB=E7=BB=9F=E7=94=A8?= =?UTF-8?q?=E6=88=B7=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/perms/utils/asset/permission.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/perms/utils/asset/permission.py b/apps/perms/utils/asset/permission.py index 949ee6873..349040b99 100644 --- a/apps/perms/utils/asset/permission.py +++ b/apps/perms/utils/asset/permission.py @@ -86,7 +86,7 @@ def get_asset_system_user_ids_with_actions_by_user(user: User, asset: Asset): def has_asset_system_permission(user: User, asset: Asset, system_user: SystemUser): systemuser_actions_mapper = get_asset_system_user_ids_with_actions_by_user(user, asset) - actions = systemuser_actions_mapper.get(system_user.id, []) + actions = systemuser_actions_mapper.get(system_user.id, 0) if actions: return True return False From cc2d47e6dc66ab33144aca04a63ea2e6717cd33e Mon Sep 17 00:00:00 2001 From: ibuler <ibuler@qq.com> Date: Mon, 15 Nov 2021 18:36:22 +0800 Subject: [PATCH 20/25] =?UTF-8?q?perf:=20=E4=BF=AE=E5=A4=8D=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E7=99=BB=E5=BD=95mfa=E9=94=99=E8=AF=AF=E6=8F=90?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/mfa.py | 2 +- apps/authentication/errors.py | 2 +- apps/authentication/mfa/otp.py | 1 + apps/authentication/mfa/radius.py | 3 +- apps/authentication/mfa/sms.py | 8 +- apps/authentication/mixins.py | 9 +- .../templates/authentication/login.html | 2 +- .../templates/authentication/login_mfa.html | 2 +- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 163 +++++++++--------- apps/users/models/user.py | 8 +- 11 files changed, 113 insertions(+), 91 deletions(-) diff --git a/apps/authentication/api/mfa.py b/apps/authentication/api/mfa.py index 751231819..1387de8fe 100644 --- a/apps/authentication/api/mfa.py +++ b/apps/authentication/api/mfa.py @@ -44,7 +44,7 @@ class MFASendCodeApi(AuthMixin, CreateAPIView): else: user = get_object_or_404(User, username=username) - mfa_backend = user.get_mfa_backend_by_type(mfa_type) + mfa_backend = user.get_active_mfa_backend_by_type(mfa_type) if not mfa_backend or not mfa_backend.challenge_required: raise ValidationError('MFA type not support: {} {}'.format(mfa_type, mfa_backend)) mfa_backend.send_challenge() diff --git a/apps/authentication/errors.py b/apps/authentication/errors.py index c17a1bccb..015ca19ad 100644 --- a/apps/authentication/errors.py +++ b/apps/authentication/errors.py @@ -60,7 +60,7 @@ block_mfa_msg = _( "(please contact admin to unlock it or try again after {} minutes)" ) mfa_error_msg = _( - "{error}," + "{error}, " "You can also try {times_try} times " "(The account will be temporarily locked for {block_time} minutes)" ) diff --git a/apps/authentication/mfa/otp.py b/apps/authentication/mfa/otp.py index 9d67c4ae2..912978d25 100644 --- a/apps/authentication/mfa/otp.py +++ b/apps/authentication/mfa/otp.py @@ -10,6 +10,7 @@ otp_failed_msg = _("OTP code invalid, or server time error") class MFAOtp(BaseMFA): name = 'otp' display_name = _('OTP') + placeholder = _('OTP verification code') def check_code(self, code): from users.utils import check_otp_code diff --git a/apps/authentication/mfa/radius.py b/apps/authentication/mfa/radius.py index ad20456c1..9f1250234 100644 --- a/apps/authentication/mfa/radius.py +++ b/apps/authentication/mfa/radius.py @@ -9,7 +9,8 @@ mfa_failed_msg = _("Radius verify code invalid") class MFARadius(BaseMFA): name = 'otp_radius' - display_name = _('Radius MFA') + display_name = 'Radius' + placeholder = _("Radius verification code") def check_code(self, code): assert self.is_authenticated() diff --git a/apps/authentication/mfa/sms.py b/apps/authentication/mfa/sms.py index cc2855cfd..670e49378 100644 --- a/apps/authentication/mfa/sms.py +++ b/apps/authentication/mfa/sms.py @@ -19,8 +19,12 @@ class MFASms(BaseMFA): def check_code(self, code): assert self.is_authenticated() - ok = self.sms.verify(code) - msg = '' if ok else sms_failed_msg + ok = False + msg = '' + try: + ok = self.sms.verify(code) + except Exception as e: + msg = str(e) return ok, msg def is_active(self): diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 8ec2fc391..4d70439f4 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -312,10 +312,13 @@ class MFAMixin: ok = False mfa_backend = user.get_mfa_backend_by_type(mfa_type) - if mfa_backend: - ok, msg = mfa_backend.check_code(code) + backend_error = _('The MFA type ({}) is not enabled') + if not mfa_backend: + msg = backend_error.format(mfa_type) + elif not mfa_backend.is_active(): + msg = backend_error.format(mfa_backend.display_name) else: - msg = _('The MFA type({}) is not supported'.format(mfa_type)) + ok, msg = mfa_backend.check_code(code) if ok: self.mark_mfa_ok(mfa_type) diff --git a/apps/authentication/templates/authentication/login.html b/apps/authentication/templates/authentication/login.html index a6550e725..6e053b877 100644 --- a/apps/authentication/templates/authentication/login.html +++ b/apps/authentication/templates/authentication/login.html @@ -109,7 +109,7 @@ } .select-con { - width: 22%; + width: 30%; } .mfa-div { diff --git a/apps/authentication/templates/authentication/login_mfa.html b/apps/authentication/templates/authentication/login_mfa.html index cde6e367a..4e33af224 100644 --- a/apps/authentication/templates/authentication/login_mfa.html +++ b/apps/authentication/templates/authentication/login_mfa.html @@ -3,7 +3,7 @@ {% load i18n %} {% block title %} - {% trans 'MFA' %} + {% trans 'MFA Auth' %} {% endblock %} {% block content %} diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 2aec9545b..1fcdb6691 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd23c3a1a8f833e99937d38db94b5e32b308c9c1912f8b258ff009d96f9bf2bb -size 92755 +oid sha256:0cbfc40871a5626f0798771004e92949986e1b5f845c03783590baf38aa43c2e +size 93462 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 1ad622d52..14e729bb1 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-12 16:51+0800\n" +"POT-Creation-Date: 2021-11-15 17:52+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler <ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n" @@ -1223,11 +1223,10 @@ msgstr "用户代理" #: audits/models.py:110 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: authentication/templates/authentication/login_mfa.html:6 #: users/forms/profile.py:64 users/models/user.py:563 #: users/serializers/profile.py:102 msgid "MFA" -msgstr "多因子认证" +msgstr "MFA" #: audits/models.py:111 audits/serializers.py:45 terminal/models/sharing.py:88 #: xpack/plugins/change_auth_plan/models/base.py:187 @@ -1258,7 +1257,7 @@ msgstr "状态名称" #: audits/serializers.py:31 msgid "MFA display" -msgstr "多因子认证" +msgstr "MFA名称" #: audits/serializers.py:76 audits/serializers.py:91 ops/models/adhoc.py:248 #: terminal/serializers/session.py:35 @@ -1500,8 +1499,6 @@ msgid "Invalid token" msgstr "无效的令牌" #: authentication/api/mfa.py:103 -#, fuzzy -#| msgid "Code is invalid, " msgid "Code is invalid" msgstr "验证码无效" @@ -1566,11 +1563,11 @@ msgstr "密码解密失败" #: authentication/errors.py:28 msgid "MFA failed" -msgstr "多因子认证失败" +msgstr "MFA 校验失败" #: authentication/errors.py:29 msgid "MFA unset" -msgstr "多因子认证没有设定" +msgstr "MFA 没有设定" #: authentication/errors.py:30 msgid "Username does not exist" @@ -1627,76 +1624,72 @@ msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)" #: authentication/errors.py:63 #, python-brace-format msgid "" -"{error},You can also try {times_try} times (The account will be temporarily " +"{error}, You can also try {times_try} times (The account will be temporarily " "locked for {block_time} minutes)" msgstr "" -"{error},您还可以尝试 {times_try} 次(账号将被临时锁定 {block_time} 分钟)" +"{error},您还可以尝试 {times_try} 次(账号将被临时锁定 {block_time} 分钟)" #: authentication/errors.py:67 msgid "MFA required" -msgstr "需要多因子认证" +msgstr "需要 MFA 认证" #: authentication/errors.py:68 msgid "MFA not set, please set it first" -msgstr "多因子认证没有设置,请先完成设置" +msgstr "MFA 没有设置,请先完成设置" #: authentication/errors.py:69 -msgid "OTP not set, please set it first" -msgstr "OTP认证没有设置,请先完成设置" - -#: authentication/errors.py:70 msgid "Login confirm required" msgstr "需要登录复核" -#: authentication/errors.py:71 +#: authentication/errors.py:70 msgid "Wait login confirm ticket for accept" msgstr "等待登录复核处理" -#: authentication/errors.py:72 +#: authentication/errors.py:71 msgid "Login confirm ticket was {}" msgstr "登录复核 {}" -#: authentication/errors.py:187 authentication/errors.py:251 +#: authentication/errors.py:184 authentication/errors.py:248 msgid "IP is not allowed" msgstr "来源 IP 不被允许登录" -#: authentication/errors.py:258 +#: authentication/errors.py:255 msgid "Time Period is not allowed" msgstr "该 时间段 不被允许登录" -#: authentication/errors.py:291 +#: authentication/errors.py:288 msgid "SSO auth closed" msgstr "SSO 认证关闭了" -#: authentication/errors.py:296 authentication/mixins.py:350 +#: authentication/errors.py:293 authentication/mixins.py:359 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" -#: authentication/errors.py:305 authentication/mixins.py:357 +#: authentication/errors.py:302 authentication/mixins.py:366 msgid "You should to change your password before login" msgstr "登录完成前,请先修改密码" -#: authentication/errors.py:314 authentication/mixins.py:364 +#: authentication/errors.py:311 authentication/mixins.py:373 msgid "Your password has expired, please reset before logging in" msgstr "您的密码已过期,先修改再登录" -#: authentication/errors.py:348 +#: authentication/errors.py:345 msgid "Your password is invalid" msgstr "您的密码无效" -#: authentication/errors.py:354 +#: authentication/errors.py:351 msgid "No upload or download permission" msgstr "没有上传下载权限" -#: authentication/errors.py:366 +#: authentication/errors.py:356 msgid "Please enter MFA code" -msgstr "请输入验证码" +msgstr "请输入 MFA 验证码" -#: authentication/errors.py:370 +#: authentication/errors.py:361 msgid "Please enter SMS code" msgstr "请输入短信验证码" -#: authentication/errors.py:374 users/exceptions.py:15 +#: authentication/errors.py:366 users/exceptions.py:15 msgid "Phone not set" msgstr "手机号没有设置" @@ -1714,7 +1707,7 @@ msgstr "MFA 类型" #: authentication/forms.py:60 users/forms/profile.py:27 msgid "MFA code" -msgstr "多因子认证验证码" +msgstr "MFA 验证码" #: authentication/forms.py:62 msgid "Dynamic code" @@ -1722,29 +1715,33 @@ msgstr "动态码" #: authentication/mfa/base.py:7 msgid "Please input security code" -msgstr "请输入 6 位动态安全码" +msgstr "请输入动态安全码" #: authentication/mfa/otp.py:7 msgid "OTP code invalid, or server time error" -msgstr "MFA (OTP) 验证码错误,或者服务器端时间不对" +msgstr "虚拟 MFA 验证码错误,或者服务器端时间不对" #: authentication/mfa/otp.py:12 msgid "OTP" -msgstr "MFA (OTP)" +msgstr "MFA" -#: authentication/mfa/otp.py:47 +#: authentication/mfa/otp.py:13 +msgid "OTP verification code" +msgstr "虚拟 MFA 验证码" + +#: authentication/mfa/otp.py:48 msgid "Virtual OTP based MFA" -msgstr "虚拟 MFA (OTP)" +msgstr "虚拟 MFA(OTP)" #: authentication/mfa/radius.py:7 msgid "Radius verify code invalid" msgstr "Radius 校验失败" -#: authentication/mfa/radius.py:12 -msgid "Radius MFA" -msgstr "Radius MFA" +#: authentication/mfa/radius.py:13 +msgid "Radius verification code" +msgstr "Radius 动态安全码" -#: authentication/mfa/radius.py:43 +#: authentication/mfa/radius.py:44 msgid "Radius global enabled, cannot disable" msgstr "Radius MFA 全局开启,无法被禁用" @@ -1760,19 +1757,19 @@ msgstr "短信" msgid "SMS verification code" msgstr "短信验证码" -#: authentication/mfa/sms.py:53 +#: authentication/mfa/sms.py:57 msgid "Set phone number to enable" msgstr "设置手机号码启用" -#: authentication/mfa/sms.py:57 +#: authentication/mfa/sms.py:61 msgid "Clear phone number to disable" msgstr "清空手机号码禁用" -#: authentication/mixins.py:311 -msgid "The MFA type({}) is not supported" -msgstr "该 MFA 方法 ({}) 不被支持" +#: authentication/mixins.py:316 authentication/mixins.py:318 +msgid "The MFA type ({}) is not enabled" +msgstr "该 MFA ({}) 方式没有启用" -#: authentication/mixins.py:340 +#: authentication/mixins.py:349 msgid "Please change your password" msgstr "请修改密码" @@ -1855,11 +1852,11 @@ msgstr "验证码" #: authentication/templates/authentication/_mfa_confirm_modal.html:5 msgid "MFA confirm" -msgstr "多因子认证校验" +msgstr "MFA 认证校验" #: authentication/templates/authentication/_mfa_confirm_modal.html:17 msgid "Need MFA for view auth" -msgstr "需要多因子认证来查看账号信息" +msgstr "需要 MFA 认证来查看账号信息" #: authentication/templates/authentication/_mfa_confirm_modal.html:20 #: templates/_modal.html:23 templates/flash_message_standalone.html:34 @@ -1968,6 +1965,10 @@ msgstr "登录" msgid "More login options" msgstr "更多登录方式" +#: authentication/templates/authentication/login_mfa.html:6 +msgid "MFA Auth" +msgstr "MFA 多因子认证" + #: authentication/templates/authentication/login_mfa.html:19 #: users/templates/users/user_otp_check_password.html:18 #: users/templates/users/user_otp_enable_bind.html:24 @@ -1978,7 +1979,7 @@ msgstr "下一步" #: authentication/templates/authentication/login_mfa.html:22 msgid "Can't provide security? Please contact the administrator!" -msgstr "如果不能提供多因子认证验证码,请联系管理员!" +msgstr "如果不能提供 MFA 验证码,请联系管理员!" #: authentication/templates/authentication/login_wait_confirm.html:41 msgid "Refresh" @@ -3140,6 +3141,18 @@ msgstr "上传下载" msgid "Cloud sync record keep days" msgstr "云同步记录" +#: settings/serializers/cleaning.py:29 +msgid "Session keep duration" +msgstr "会话日志保存时间" + +#: settings/serializers/cleaning.py:30 +msgid "" +"Unit: days, Session, record, command will be delete if more than duration, " +"only in database" +msgstr "" +"单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不" +"受影响)" + #: settings/serializers/email.py:18 msgid "SMTP host" msgstr "SMTP 主机" @@ -3423,7 +3436,7 @@ msgstr "" #: settings/serializers/security.py:112 msgid "MFA in login page" -msgstr "MFA 在登录页面" +msgstr "MFA 在登录页面输入" #: settings/serializers/security.py:113 msgid "Eu security regulations(GDPR) require MFA to be on the login page" @@ -3545,36 +3558,24 @@ msgid "List page size" msgstr "资产列表每页数量" #: settings/serializers/terminal.py:29 -msgid "Session keep duration" -msgstr "会话日志保存时间" - -#: settings/serializers/terminal.py:30 -msgid "" -"Unit: days, Session, record, command will be delete if more than duration, " -"only in database" -msgstr "" -"单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不" -"受影响)" - -#: settings/serializers/terminal.py:33 msgid "Telnet login regex" msgstr "Telnet 成功正则表达式" -#: settings/serializers/terminal.py:34 +#: settings/serializers/terminal.py:30 msgid "" "The login success message varies with devices. if you cannot log in to the " "device through Telnet, set this parameter" msgstr "不同设备登录成功提示不一样,所以如果 telnet 不能正常登录,可以这里设置" -#: settings/serializers/terminal.py:38 +#: settings/serializers/terminal.py:34 msgid "RDP address" msgstr "RDP 地址" -#: settings/serializers/terminal.py:39 +#: settings/serializers/terminal.py:35 msgid "RDP visit address, eg: dev.jumpserver.org:3389" msgstr "RDP 访问地址, 如: dev.jumpserver.org:3389" -#: settings/serializers/terminal.py:42 +#: settings/serializers/terminal.py:38 msgid "Enable XRDP" msgstr "启用 XRDP 服务" @@ -3826,11 +3827,11 @@ msgstr "" msgid "Send verification code" msgstr "发送验证码" -#: templates/_mfa_login_field.html:92 +#: templates/_mfa_login_field.html:107 msgid "Wait: " msgstr "等待:" -#: templates/_mfa_login_field.html:102 +#: templates/_mfa_login_field.html:117 msgid "The verification code has been sent" msgstr "验证码已发送" @@ -4828,7 +4829,7 @@ msgstr "批准的系统用户名称" msgid "Permission named `{}` already exists" msgstr "授权名称 `{}` 已存在" -#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:88 +#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:90 #: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:71 msgid "The expiration date should be greater than the start date" msgstr "过期时间要大于开始时间" @@ -4924,7 +4925,7 @@ msgstr "点击查看" #: users/api/user.py:209 msgid "Could not reset self otp, use profile reset instead" -msgstr "不能在该页面重置多因子认证, 请去个人信息页面重置" +msgstr "不能在该页面重置 MFA 多因子认证, 请去个人信息页面重置" #: users/const.py:10 users/models/user.py:167 msgid "System administrator" @@ -4944,11 +4945,11 @@ msgstr "设置密码" #: users/exceptions.py:10 msgid "MFA not enabled" -msgstr "MFA没有开启" +msgstr "MFA 多因子认证没有开启" #: users/exceptions.py:20 msgid "MFA method not support" -msgstr "MFA 方法不支持" +msgstr "不支持该 MFA 方式" #: users/forms/profile.py:49 msgid "" @@ -4957,11 +4958,11 @@ msgid "" "modification -> change MFA Settings\"!" msgstr "" "启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在(个人信息->快速" -"修改->更改多因子设置)中直接绑定!" +"修改->设置 MFA 多因子认证)中直接绑定!" #: users/forms/profile.py:60 msgid "* Enable MFA to make the account more secure." -msgstr "* 启用多因子认证,使账号更加安全。" +msgstr "* 启用 MFA 多因子认证,使账号更加安全。" #: users/forms/profile.py:69 msgid "" @@ -4970,7 +4971,7 @@ msgid "" "password, enabling MFA)" msgstr "" "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:" -"设置复杂密码,并启用多因子认证)" +"设置复杂密码,并启用 MFA 多因子认证)" #: users/forms/profile.py:76 msgid "Finish" @@ -5189,7 +5190,7 @@ msgstr "是否绑定了飞书" #: users/serializers/user.py:92 msgid "Is OTP bound" -msgstr "是否绑定了虚拟MFA" +msgstr "是否绑定了虚拟 MFA" #: users/serializers/user.py:116 msgid "Role limit to {}" @@ -5463,7 +5464,7 @@ msgstr "账号保护已开启,请根据提示完成以下操作" #: users/templates/users/user_verify_mfa.html:17 msgid "Open MFA Authenticator and enter the 6-bit dynamic code" -msgstr "请打开MFA验证器,输入6位动态码" +msgstr "请打开 MFA 验证器,输入 6 位动态码" #: users/views/profile/otp.py:80 msgid "Already bound" @@ -5483,7 +5484,7 @@ msgstr "MFA (OTP) 启用成功,返回到登录页面" #: users/views/profile/otp.py:151 msgid "Disable OTP" -msgstr "禁用 MFA (OTP)" +msgstr "禁用虚拟 MFA(OTP)" #: users/views/profile/otp.py:157 msgid "OTP disable success" @@ -6126,6 +6127,12 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#~ msgid "OTP not set, please set it first" +#~ msgstr "OTP认证没有设置,请先完成设置" + +#~ msgid "Radius MFA" +#~ msgstr "Radius MFA" + #~ msgid "Help Website URL" #~ msgstr "官网链接" diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 0611d67cb..59c5b9a03 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -507,8 +507,14 @@ class MFAMixin: backends = [cls(user) for cls in MFA_BACKENDS if cls.global_enabled()] return backends + def get_active_mfa_backend_by_type(self, mfa_type): + backend = self.get_mfa_backend_by_type(mfa_type) + if not backend or not backend.is_active(): + return None + return backend + def get_mfa_backend_by_type(self, mfa_type): - mfa_mapper = self.active_mfa_backends_mapper + mfa_mapper = {b.name: b for b in self.get_user_mfa_backends(self)} backend = mfa_mapper.get(mfa_type) if not backend: return None From 2a9f0f8dcf7632f806dfbaebed6da8b55408e193 Mon Sep 17 00:00:00 2001 From: feng626 <1304903146@qq.com> Date: Wed, 17 Nov 2021 15:40:00 +0800 Subject: [PATCH 21/25] =?UTF-8?q?perf:=20help=E4=BF=A1=E6=81=AF=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E5=88=B0public=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/settings/custom.py | 8 +++++--- apps/settings/api/public.py | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index a4be80f5b..d5088ea48 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -37,7 +37,7 @@ SECURITY_LOGIN_LIMIT_COUNT = CONFIG.SECURITY_LOGIN_LIMIT_COUNT SECURITY_LOGIN_IP_BLACK_LIST = CONFIG.SECURITY_LOGIN_IP_BLACK_LIST SECURITY_LOGIN_LIMIT_TIME = CONFIG.SECURITY_LOGIN_LIMIT_TIME # Unit: minute SECURITY_MAX_IDLE_TIME = CONFIG.SECURITY_MAX_IDLE_TIME # Unit: minute -SECURITY_PASSWORD_EXPIRATION_TIME = CONFIG.SECURITY_PASSWORD_EXPIRATION_TIME # Unit: day +SECURITY_PASSWORD_EXPIRATION_TIME = CONFIG.SECURITY_PASSWORD_EXPIRATION_TIME # Unit: day SECURITY_PASSWORD_MIN_LENGTH = CONFIG.SECURITY_PASSWORD_MIN_LENGTH # Unit: bit SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH = CONFIG.SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH # Unit: bit OLD_PASSWORD_HISTORY_LIMIT_COUNT = CONFIG.OLD_PASSWORD_HISTORY_LIMIT_COUNT @@ -119,7 +119,6 @@ REFERER_CHECK_ENABLED = CONFIG.REFERER_CHECK_ENABLED CONNECTION_TOKEN_ENABLED = CONFIG.CONNECTION_TOKEN_ENABLED FORGOT_PASSWORD_URL = CONFIG.FORGOT_PASSWORD_URL - # 自定义默认组织名 GLOBAL_ORG_DISPLAY_NAME = CONFIG.GLOBAL_ORG_DISPLAY_NAME HEALTH_CHECK_TOKEN = CONFIG.HEALTH_CHECK_TOKEN @@ -136,7 +135,6 @@ CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = CONFIG.CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS XRDP_ENABLED = CONFIG.XRDP_ENABLED - # SMS enabled SMS_ENABLED = CONFIG.SMS_ENABLED SMS_BACKEND = CONFIG.SMS_BACKEND @@ -156,3 +154,7 @@ TENCENT_SMS_SIGN_AND_TEMPLATES = CONFIG.TENCENT_SMS_SIGN_AND_TEMPLATES # 公告 ANNOUNCEMENT_ENABLED = CONFIG.ANNOUNCEMENT_ENABLED ANNOUNCEMENT = CONFIG.ANNOUNCEMENT + +# help +HELP_DOCUMENT_URL = CONFIG.HELP_DOCUMENT_URL +HELP_SUPPORT_URL = CONFIG.HELP_SUPPORT_URL diff --git a/apps/settings/api/public.py b/apps/settings/api/public.py index ad9c1fbe6..48542a1ea 100644 --- a/apps/settings/api/public.py +++ b/apps/settings/api/public.py @@ -59,6 +59,8 @@ class PublicSettingApi(generics.RetrieveAPIView): "XRDP_ENABLED": settings.XRDP_ENABLED, "ANNOUNCEMENT_ENABLED": settings.ANNOUNCEMENT_ENABLED, "ANNOUNCEMENT": settings.ANNOUNCEMENT, + "HELP_DOCUMENT_URL": settings.HELP_DOCUMENT_URL, + "HELP_SUPPORT_URL": settings.HELP_SUPPORT_URL, } } return instance From 8af88cd2c6c93433ce72c4818202d6342af19e00 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 17 Nov 2021 16:43:04 +0800 Subject: [PATCH 22/25] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dotp=20verify=20m?= =?UTF-8?q?sg=E5=BC=95=E8=B5=B7=E7=9A=84500=20(#7210)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ibuler <ibuler@qq.com> --- apps/authentication/api/mfa.py | 2 +- apps/locale/zh/LC_MESSAGES/django.po | 87 ++++++++++++++-------------- 2 files changed, 44 insertions(+), 45 deletions(-) diff --git a/apps/authentication/api/mfa.py b/apps/authentication/api/mfa.py index 1387de8fe..52795c630 100644 --- a/apps/authentication/api/mfa.py +++ b/apps/authentication/api/mfa.py @@ -100,7 +100,7 @@ class UserOtpVerifyApi(CreateAPIView): request.session["MFA_VERIFY_TIME"] = int(time.time()) return Response({"ok": "1"}) else: - return Response({"error": _("Code is invalid") + ", " + error}, status=400) + return Response({"error": _("Code is invalid, {}").format(error)}, status=400) def get_permissions(self): if self.request.method.lower() == 'get' \ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 14e729bb1..74a9084aa 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-15 17:52+0800\n" +"POT-Creation-Date: 2021-11-17 16:39+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler <ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n" @@ -25,7 +25,7 @@ msgstr "" #: orgs/models.py:24 perms/models/base.py:44 settings/models.py:29 #: settings/serializers/sms.py:6 terminal/models/storage.py:23 #: terminal/models/task.py:16 terminal/models/terminal.py:100 -#: users/forms/profile.py:32 users/models/group.py:15 users/models/user.py:541 +#: users/forms/profile.py:32 users/models/group.py:15 users/models/user.py:547 #: users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_asset_permission.html:37 #: users/templates/users/user_asset_permission.html:154 @@ -60,7 +60,7 @@ msgstr "激活中" #: orgs/models.py:27 perms/models/base.py:53 settings/models.py:34 #: terminal/models/storage.py:26 terminal/models/terminal.py:114 #: tickets/models/ticket.py:71 users/models/group.py:16 -#: users/models/user.py:574 xpack/plugins/change_auth_plan/models/base.py:41 +#: users/models/user.py:580 xpack/plugins/change_auth_plan/models/base.py:41 #: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:113 #: xpack/plugins/gathered_user/models.py:26 msgid "Comment" @@ -87,7 +87,7 @@ msgstr "登录复核" #: terminal/backends/command/serializers.py:12 terminal/models/session.py:38 #: terminal/notifications.py:90 terminal/notifications.py:138 #: tickets/models/comment.py:17 users/const.py:14 users/models/user.py:169 -#: users/models/user.py:745 users/models/user.py:771 +#: users/models/user.py:751 users/models/user.py:777 #: users/serializers/group.py:19 #: users/templates/users/user_asset_permission.html:38 #: users/templates/users/user_asset_permission.html:64 @@ -162,7 +162,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. " #: assets/models/base.py:176 assets/models/gathered_user.py:15 #: audits/models.py:105 authentication/forms.py:15 authentication/forms.py:17 #: authentication/templates/authentication/_msg_different_city.html:9 -#: ops/models/adhoc.py:148 users/forms/profile.py:31 users/models/user.py:539 +#: ops/models/adhoc.py:148 users/forms/profile.py:31 users/models/user.py:545 #: users/templates/users/_msg_user_created.html:12 #: users/templates/users/_select_user_modal.html:14 #: xpack/plugins/change_auth_plan/models/asset.py:35 @@ -554,7 +554,7 @@ msgstr "标签管理" #: assets/models/cluster.py:28 assets/models/cmd_filter.py:26 #: assets/models/cmd_filter.py:67 assets/models/group.py:21 #: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:25 -#: orgs/models.py:437 perms/models/base.py:51 users/models/user.py:582 +#: orgs/models.py:437 perms/models/base.py:51 users/models/user.py:588 #: users/serializers/group.py:33 #: xpack/plugins/change_auth_plan/models/base.py:45 #: xpack/plugins/cloud/models.py:119 xpack/plugins/gathered_user/models.py:30 @@ -567,7 +567,7 @@ msgstr "创建者" #: assets/models/label.py:25 common/db/models.py:72 common/mixins/models.py:50 #: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:26 #: orgs/models.py:435 perms/models/base.py:52 users/models/group.py:18 -#: users/models/user.py:772 xpack/plugins/cloud/models.py:122 +#: users/models/user.py:778 xpack/plugins/cloud/models.py:122 msgid "Date created" msgstr "创建日期" @@ -622,7 +622,7 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 users/models/user.py:560 +#: assets/models/cluster.py:22 users/models/user.py:566 msgid "Phone" msgstr "手机" @@ -648,7 +648,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:14 -#: users/models/user.py:757 +#: users/models/user.py:763 msgid "System" msgstr "系统" @@ -1223,7 +1223,7 @@ msgstr "用户代理" #: audits/models.py:110 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:64 users/models/user.py:563 +#: users/forms/profile.py:64 users/models/user.py:569 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" @@ -1302,12 +1302,12 @@ msgid "Auth Token" msgstr "认证令牌" #: audits/signals_handler.py:68 authentication/views/login.py:170 -#: notifications/backends/__init__.py:11 users/models/user.py:596 +#: notifications/backends/__init__.py:11 users/models/user.py:602 msgid "WeCom" msgstr "企业微信" #: audits/signals_handler.py:69 authentication/views/login.py:176 -#: notifications/backends/__init__.py:12 users/models/user.py:597 +#: notifications/backends/__init__.py:12 users/models/user.py:603 msgid "DingTalk" msgstr "钉钉" @@ -1494,13 +1494,13 @@ msgstr "{ApplicationPermission} 添加 {SystemUser}" msgid "{ApplicationPermission} REMOVE {SystemUser}" msgstr "{ApplicationPermission} 移除 {SystemUser}" -#: authentication/api/connection_token.py:248 +#: authentication/api/connection_token.py:259 msgid "Invalid token" msgstr "无效的令牌" #: authentication/api/mfa.py:103 -msgid "Code is invalid" -msgstr "验证码无效" +msgid "Code is invalid, {}" +msgstr "验证码无效: {}" #: authentication/backends/api.py:67 msgid "Invalid signature header. No credentials provided." @@ -1661,15 +1661,15 @@ msgstr "该 时间段 不被允许登录" msgid "SSO auth closed" msgstr "SSO 认证关闭了" -#: authentication/errors.py:293 authentication/mixins.py:359 +#: authentication/errors.py:293 authentication/mixins.py:360 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" -#: authentication/errors.py:302 authentication/mixins.py:366 +#: authentication/errors.py:302 authentication/mixins.py:367 msgid "You should to change your password before login" msgstr "登录完成前,请先修改密码" -#: authentication/errors.py:311 authentication/mixins.py:373 +#: authentication/errors.py:311 authentication/mixins.py:374 msgid "Your password has expired, please reset before logging in" msgstr "您的密码已过期,先修改再登录" @@ -1677,19 +1677,15 @@ msgstr "您的密码已过期,先修改再登录" msgid "Your password is invalid" msgstr "您的密码无效" -#: authentication/errors.py:351 -msgid "No upload or download permission" -msgstr "没有上传下载权限" - -#: authentication/errors.py:356 +#: authentication/errors.py:350 msgid "Please enter MFA code" msgstr "请输入 MFA 验证码" -#: authentication/errors.py:361 +#: authentication/errors.py:355 msgid "Please enter SMS code" msgstr "请输入短信验证码" -#: authentication/errors.py:366 users/exceptions.py:15 +#: authentication/errors.py:360 users/exceptions.py:15 msgid "Phone not set" msgstr "手机号没有设置" @@ -1765,11 +1761,11 @@ msgstr "设置手机号码启用" msgid "Clear phone number to disable" msgstr "清空手机号码禁用" -#: authentication/mixins.py:316 authentication/mixins.py:318 +#: authentication/mixins.py:315 msgid "The MFA type ({}) is not enabled" msgstr "该 MFA ({}) 方式没有启用" -#: authentication/mixins.py:349 +#: authentication/mixins.py:350 msgid "Please change your password" msgstr "请修改密码" @@ -2093,7 +2089,7 @@ msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" #: authentication/views/login.py:182 notifications/backends/__init__.py:14 -#: users/models/user.py:598 +#: users/models/user.py:604 msgid "FeiShu" msgstr "飞书" @@ -2352,7 +2348,7 @@ msgstr "" "div>" #: notifications/backends/__init__.py:10 users/forms/profile.py:101 -#: users/models/user.py:543 +#: users/models/user.py:549 msgid "Email" msgstr "邮件" @@ -2567,7 +2563,7 @@ msgstr "组织审计员" msgid "GLOBAL" msgstr "全局组织" -#: orgs/models.py:434 users/models/user.py:551 users/serializers/user.py:37 +#: orgs/models.py:434 users/models/user.py:557 users/serializers/user.py:37 #: users/templates/users/_select_user_modal.html:15 msgid "Role" msgstr "角色" @@ -2628,7 +2624,7 @@ msgid "Favorite" msgstr "收藏夹" #: perms/models/base.py:47 templates/_nav.html:21 users/models/group.py:31 -#: users/models/user.py:547 users/templates/users/_select_user_modal.html:16 +#: users/models/user.py:553 users/templates/users/_select_user_modal.html:16 #: users/templates/users/user_asset_permission.html:39 #: users/templates/users/user_asset_permission.html:67 #: users/templates/users/user_database_app_permission.html:38 @@ -2639,7 +2635,7 @@ msgstr "用户组" #: perms/models/base.py:50 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:60 #: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:50 -#: users/models/user.py:579 +#: users/models/user.py:585 msgid "Date expired" msgstr "失效日期" @@ -3827,11 +3823,11 @@ msgstr "" msgid "Send verification code" msgstr "发送验证码" -#: templates/_mfa_login_field.html:107 +#: templates/_mfa_login_field.html:105 msgid "Wait: " msgstr "等待:" -#: templates/_mfa_login_field.html:117 +#: templates/_mfa_login_field.html:115 msgid "The verification code has been sent" msgstr "验证码已发送" @@ -5022,7 +5018,7 @@ msgstr "不能和原来的密钥相同" msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/profile.py:160 users/models/user.py:571 +#: users/forms/profile.py:160 users/models/user.py:577 #: users/templates/users/user_password_update.html:48 msgid "Public key" msgstr "SSH公钥" @@ -5031,39 +5027,39 @@ msgstr "SSH公钥" msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:520 +#: users/models/user.py:526 msgid "Local" msgstr "数据库" -#: users/models/user.py:554 +#: users/models/user.py:560 msgid "Avatar" msgstr "头像" -#: users/models/user.py:557 +#: users/models/user.py:563 msgid "Wechat" msgstr "微信" -#: users/models/user.py:568 +#: users/models/user.py:574 msgid "Private key" msgstr "ssh私钥" -#: users/models/user.py:587 +#: users/models/user.py:593 msgid "Source" msgstr "来源" -#: users/models/user.py:591 +#: users/models/user.py:597 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:594 +#: users/models/user.py:600 msgid "Need update password" msgstr "需要更新密码" -#: users/models/user.py:753 +#: users/models/user.py:759 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:756 +#: users/models/user.py:762 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -6127,6 +6123,9 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#~ msgid "No upload or download permission" +#~ msgstr "没有上传下载权限" + #~ msgid "OTP not set, please set it first" #~ msgstr "OTP认证没有设置,请先完成设置" From 0ed0f69be0b63c8f1e5b69bef176ccb552fec6e1 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 17 Nov 2021 19:28:58 +0800 Subject: [PATCH 23/25] =?UTF-8?q?fix:=20=E7=99=BB=E5=BD=95=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=AD=97=E6=AE=B5=E7=BF=BB=E8=AF=91=20(#7211)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: xinwen <coderWen@126.com> Co-authored-by: Jiangjie.Bai <32935519+BaiJiangJie@users.noreply.github.com> --- apps/audits/serializers.py | 2 +- apps/locale/zh/LC_MESSAGES/django.po | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py index 32e8ad20d..2c7911f14 100644 --- a/apps/audits/serializers.py +++ b/apps/audits/serializers.py @@ -42,7 +42,7 @@ class UserLoginLogSerializer(serializers.ModelSerializer): fields = fields_small extra_kwargs = { "user_agent": {'label': _('User agent')}, - "reason_display": {'label': _('Reason')} + "reason_display": {'label': _('Reason display')} } diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 74a9084aa..d72d23fac 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -1228,7 +1228,7 @@ msgstr "用户代理" msgid "MFA" msgstr "MFA" -#: audits/models.py:111 audits/serializers.py:45 terminal/models/sharing.py:88 +#: audits/models.py:111 terminal/models/sharing.py:88 #: xpack/plugins/change_auth_plan/models/base.py:187 #: xpack/plugins/cloud/models.py:176 msgid "Reason" @@ -1259,6 +1259,10 @@ msgstr "状态名称" msgid "MFA display" msgstr "MFA名称" +#: audits/serializers.py:45 +msgid "Reason display" +msgstr "原因显示" + #: audits/serializers.py:76 audits/serializers.py:91 ops/models/adhoc.py:248 #: terminal/serializers/session.py:35 msgid "Is success" From 096b2cf8b845b04775569f02c395b53ade68c0eb Mon Sep 17 00:00:00 2001 From: Michael Bai <baijiangjie@gmail.com> Date: Wed, 17 Nov 2021 19:31:15 +0800 Subject: [PATCH 24/25] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E7=BF=BB?= =?UTF-8?q?=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | 4 ++-- apps/locale/zh/LC_MESSAGES/django.po | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 1fcdb6691..2b95a8070 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0cbfc40871a5626f0798771004e92949986e1b5f845c03783590baf38aa43c2e -size 93462 +oid sha256:4fea2cdf5a5477757cb95ff36016ed754fd65f839c12adbac9247ebdcca138ef +size 93440 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index d72d23fac..59c2bf499 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -1261,7 +1261,7 @@ msgstr "MFA名称" #: audits/serializers.py:45 msgid "Reason display" -msgstr "原因显示" +msgstr "原因描述" #: audits/serializers.py:76 audits/serializers.py:91 ops/models/adhoc.py:248 #: terminal/serializers/session.py:35 From dac45f234a60e34e29ad6f4795ad804277bebd0f Mon Sep 17 00:00:00 2001 From: Michael Bai <baijiangjie@gmail.com> Date: Wed, 17 Nov 2021 18:54:32 +0800 Subject: [PATCH 25/25] =?UTF-8?q?fix:=20=E7=BB=88=E7=AB=AF=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=8C=87=E6=A0=87=E6=95=B0=E5=80=BC=E4=BF=9D=E7=95=99?= =?UTF-8?q?=E4=B8=80=E4=BD=8D=E5=B0=8F=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/models/status.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/terminal/models/status.py b/apps/terminal/models/status.py index cece6b5ce..bc195d857 100644 --- a/apps/terminal/models/status.py +++ b/apps/terminal/models/status.py @@ -55,8 +55,18 @@ class Status(models.Model): stat = cls(**data) stat.terminal = terminal stat.is_alive = terminal.is_alive + stat.keep_one_decimal_place() return stat + def keep_one_decimal_place(self): + keys = ['cpu_load', 'memory_used', 'disk_used'] + for key in keys: + value = getattr(self, key, 0) + if not isinstance(value, (int, float)): + continue + value = '%.1f' % value + setattr(self, key, float(value)) + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): self.terminal.set_alive(ttl=120)