diff --git a/apps/applications/serializers/attrs/application_type/chrome.py b/apps/applications/serializers/attrs/application_type/chrome.py index 08035bc31..ba491e2fe 100644 --- a/apps/applications/serializers/attrs/application_type/chrome.py +++ b/apps/applications/serializers/attrs/application_type/chrome.py @@ -23,7 +23,7 @@ class ChromeSerializer(RemoteAppSerializer): ) chrome_password = EncryptedField( max_length=128, allow_blank=True, required=False, - label=_('Chrome password'), allow_null=True + label=_('Chrome password'), allow_null=True, encrypted_key='chrome_password' ) diff --git a/apps/audits/const.py b/apps/audits/const.py index a49363e0a..17380311b 100644 --- a/apps/audits/const.py +++ b/apps/audits/const.py @@ -16,6 +16,8 @@ MODELS_NEED_RECORD = ( 'CommandFilter', 'Platform', 'Label', # applications 'Application', + # account + 'AuthBook', # orgs 'Organization', # settings diff --git a/apps/audits/utils.py b/apps/audits/utils.py index 8ce23a498..e62a40577 100644 --- a/apps/audits/utils.py +++ b/apps/audits/utils.py @@ -59,14 +59,15 @@ def get_resource_display(resource): def model_to_dict_for_operate_log( instance, include_model_fields=True, include_related_fields=True ): - need_continue_fields = ['date_updated'] + model_need_continue_fields = ['date_updated'] + m2m_need_continue_fields = ['history_passwords'] opts = instance._meta data = {} for f in chain(opts.concrete_fields, opts.private_fields): if isinstance(f, (models.FileField, models.ImageField)): continue - if getattr(f, 'attname', None) in need_continue_fields: + if getattr(f, 'attname', None) in model_need_continue_fields: continue value = getattr(instance, f.name) or getattr(instance, f.attname) @@ -75,11 +76,6 @@ def model_to_dict_for_operate_log( if getattr(f, 'primary_key', False): f.verbose_name = 'id' - elif isinstance(f, ( - fields.EncryptCharField, fields.EncryptTextField, - fields.EncryptJsonDictCharField, fields.EncryptJsonDictTextField - )) or getattr(f, 'attname', '') == 'password': - value = 'encrypt|%s' % value elif isinstance(value, list): value = [str(v) for v in value] @@ -91,11 +87,12 @@ def model_to_dict_for_operate_log( value = [] if instance.pk is not None: related_name = getattr(f, 'attname', '') or getattr(f, 'related_name', '') - if related_name: - try: - value = [str(i) for i in getattr(instance, related_name).all()] - except: - pass + if not related_name or related_name in m2m_need_continue_fields: + continue + try: + value = [str(i) for i in getattr(instance, related_name).all()] + except: + pass if not value: continue try: diff --git a/apps/authentication/api/password.py b/apps/authentication/api/password.py index 8c676117c..dbade3a33 100644 --- a/apps/authentication/api/password.py +++ b/apps/authentication/api/password.py @@ -3,6 +3,7 @@ from rest_framework.response import Response from rest_framework.permissions import AllowAny from django.utils.translation import ugettext as _ from django.template.loader import render_to_string +from django.core.cache import cache from common.utils.verify_code import SendAndVerifyCodeUtil from common.permissions import IsValidUser @@ -23,7 +24,7 @@ class UserResetPasswordSendCodeApi(CreateAPIView): serializer_class = ResetPasswordCodeSerializer @staticmethod - def is_valid_user( **kwargs): + def is_valid_user(**kwargs): user = get_object_or_none(User, **kwargs) if not user: err_msg = _('User does not exist: {}').format(_("No user matched")) @@ -38,33 +39,25 @@ class UserResetPasswordSendCodeApi(CreateAPIView): def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) + token = request.GET.get('token') + username = cache.get(token) form_type = serializer.validated_data['form_type'] - username = serializer.validated_data['username'] code = random_string(6, lower=False, upper=False) other_args = {} - if form_type == 'phone': - backend = 'sms' - target = serializer.validated_data['phone'] - user, err = self.is_valid_user(username=username, phone=target) - if not user: - return Response({'error': err}, status=400) - else: - backend = 'email' - target = serializer.validated_data['email'] - user, err = self.is_valid_user(username=username, email=target) - if not user: - return Response({'error': err}, status=400) + target = serializer.validated_data[form_type] + query_key = 'phone' if form_type == 'sms' else form_type + user, err = self.is_valid_user(username=username, **{query_key: target}) + if not user: + return Response({'error': err}, status=400) - subject = '%s: %s' % (get_login_title(), _('Forgot password')) - context = { - 'user': user, 'title': subject, 'code': code, - } - message = render_to_string('authentication/_msg_reset_password_code.html', context) - other_args['subject'] = subject - other_args['message'] = message - - SendAndVerifyCodeUtil(target, code, backend=backend, **other_args).gen_and_send_async() + subject = '%s: %s' % (get_login_title(), _('Forgot password')) + context = { + 'user': user, 'title': subject, 'code': code, + } + message = render_to_string('authentication/_msg_reset_password_code.html', context) + other_args['subject'], other_args['message'] = subject, message + SendAndVerifyCodeUtil(target, code, backend=form_type, **other_args).gen_and_send_async() return Response({'data': 'ok'}, status=200) diff --git a/apps/authentication/serializers/password_mfa.py b/apps/authentication/serializers/password_mfa.py index 152ba0fc6..7684c3f88 100644 --- a/apps/authentication/serializers/password_mfa.py +++ b/apps/authentication/serializers/password_mfa.py @@ -12,26 +12,23 @@ __all__ = [ class ResetPasswordCodeSerializer(serializers.Serializer): - form_type = serializers.CharField(default='email') - username = serializers.CharField() + form_type = serializers.ChoiceField( + choices=[('sms', _('SMS')), ('email', _('Email'))], default='email' + ) email = serializers.CharField(allow_blank=True) - phone = serializers.CharField(allow_blank=True) + sms = serializers.CharField(allow_blank=True) def create(self, attrs): error = [] + validate_backends = { + 'email': _('Email'), 'sms': _('SMS') + } form_type = attrs.get('form_type', 'email') - username = attrs.get('username') - if not username: - error.append(_('The {} cannot be empty').format(_('Username'))) - if form_type == 'phone': - phone = attrs.get('phone') - if not phone: - error.append(_('The {} cannot be empty').format(_('Phone'))) - else: - email = attrs.get('email') - if not email: - error.append(_('The {} cannot be empty').format(_('Email'))) - + validate_backend_input = attrs.get(form_type) + if not validate_backend_input: + error.append(_('The {} cannot be empty').format( + validate_backends.get(validate_backend_input)) + ) if error: raise serializers.ValidationError(error) diff --git a/apps/authentication/urls/view_urls.py b/apps/authentication/urls/view_urls.py index 2c97fe1ea..bad1ded62 100644 --- a/apps/authentication/urls/view_urls.py +++ b/apps/authentication/urls/view_urls.py @@ -18,6 +18,7 @@ urlpatterns = [ path('logout/', views.UserLogoutView.as_view(), name='logout'), # 原来在users中的 + path('password/forget/previewing/', users_view.UserForgotPasswordPreviewingView.as_view(), name='forgot-previewing'), path('password/forgot/', users_view.UserForgotPasswordView.as_view(), name='forgot-password'), path('password/reset/', users_view.UserResetPasswordView.as_view(), name='reset-password'), path('password/verify/', users_view.UserVerifyPasswordView.as_view(), name='user-verify-password'), diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 19720b587..1a2e9e244 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -115,7 +115,7 @@ class UserLoginContextMixin: @staticmethod def get_forgot_password_url(): - forgot_password_url = reverse('authentication:forgot-password') + forgot_password_url = reverse('authentication:forgot-previewing') forgot_password_url = settings.FORGOT_PASSWORD_URL or forgot_password_url return forgot_password_url diff --git a/apps/common/drf/fields.py b/apps/common/drf/fields.py index 353b3a75b..4370d0054 100644 --- a/apps/common/drf/fields.py +++ b/apps/common/drf/fields.py @@ -32,8 +32,9 @@ class EncryptedField(serializers.CharField): if write_only is None: write_only = True kwargs['write_only'] = write_only + encrypted_key = kwargs.pop('encrypted_key', None) super().__init__(**kwargs) - add_encrypted_field_set(self.label) + add_encrypted_field_set(encrypted_key or self.label) def to_internal_value(self, value): value = super().to_internal_value(value) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 55303e2e1..dd8d132a4 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -199,6 +199,13 @@ class Config(dict): 'REDIS_PORT': 6379, 'REDIS_PASSWORD': '', 'REDIS_USE_SSL': False, + 'REDIS_SSL_KEY': None, + 'REDIS_SSL_CERT': None, + 'REDIS_SSL_CA': None, + # Redis Sentinel + 'REDIS_SENTINEL_HOSTS': '', + 'REDIS_SENTINEL_PASSWORD': '', + 'REDIS_SENTINEL_SOCKET_TIMEOUT': None, # Default value 'REDIS_DB_CELERY': 3, 'REDIS_DB_CACHE': 4, @@ -491,6 +498,8 @@ class Config(dict): 'SERVER_REPLAY_STORAGE': {}, 'SECURITY_DATA_CRYPTO_ALGO': None, 'GMSSL_ENABLED': False, + # 操作日志变更字段的存储ES配置 + 'OPERATE_LOG_ELASTICSEARCH_CONFIG': {}, # Magnus 组件需要监听的端口范围 'MAGNUS_PORTS': '30000-30100', diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index ce9f73624..028ab7b56 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-10 10:55+0800\n" +"POT-Creation-Date: 2022-11-10 16:37+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -158,13 +158,11 @@ msgstr "コンマ区切り文字列の形式。* はすべて一致すること #: acls/serializers/login_asset_acl.py:51 assets/models/base.py:176 #: assets/models/gathered_user.py:15 audits/models.py:139 #: authentication/forms.py:25 authentication/forms.py:27 -#: authentication/models.py:260 authentication/serializers/password_mfa.py:25 +#: authentication/models.py:260 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 -#: ops/models/adhoc.py:159 users/forms/profile.py:32 users/forms/profile.py:102 +#: ops/models/adhoc.py:159 users/forms/profile.py:32 users/forms/profile.py:109 #: users/models/user.py:671 users/templates/users/_msg_user_created.html:12 -#: users/templates/users/forgot_password.html:51 -#: users/templates/users/forgot_password.html:98 #: xpack/plugins/change_auth_plan/models/asset.py:34 #: xpack/plugins/change_auth_plan/models/asset.py:195 #: xpack/plugins/cloud/serializers/account_attrs.py:26 @@ -226,7 +224,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "いずれのレビューアも組織 '{}' に属していません" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:24 +#: xpack/plugins/cloud/serializers/task.py:23 msgid "IP address invalid: `{}`" msgstr "IPアドレスが無効: '{}'" @@ -326,7 +324,7 @@ msgstr "タイプ" msgid "Domain" msgstr "ドメイン" -#: applications/models/application.py:230 xpack/plugins/cloud/models.py:32 +#: applications/models/application.py:230 xpack/plugins/cloud/models.py:33 #: xpack/plugins/cloud/serializers/account.py:64 msgid "Attrs" msgstr "ツールバーの" @@ -365,7 +363,7 @@ msgstr "タイプ表示" #: orgs/models.py:72 orgs/models.py:223 perms/models/base.py:92 #: users/models/group.py:18 users/models/user.py:937 #: xpack/plugins/change_auth_plan/models/base.py:45 -#: xpack/plugins/cloud/models.py:127 +#: xpack/plugins/cloud/models.py:125 msgid "Date created" msgstr "作成された日付" @@ -610,7 +608,7 @@ msgstr "ホスト名生" #: assets/models/asset.py:215 assets/serializers/account.py:16 #: assets/serializers/asset.py:65 perms/serializers/asset/user_permission.py:41 -#: xpack/plugins/cloud/models.py:106 xpack/plugins/cloud/serializers/task.py:44 +#: xpack/plugins/cloud/models.py:107 xpack/plugins/cloud/serializers/task.py:43 msgid "Protocols" msgstr "プロトコル" @@ -632,7 +630,7 @@ msgstr "アクティブです。" msgid "Admin user" msgstr "管理ユーザー" -#: assets/models/asset.py:225 xpack/plugins/cloud/const.py:32 +#: assets/models/asset.py:225 msgid "Public IP" msgstr "パブリックIP" @@ -651,7 +649,7 @@ msgstr "ラベル" #: orgs/models.py:225 perms/models/base.py:91 users/models/user.py:720 #: users/serializers/group.py:33 #: xpack/plugins/change_auth_plan/models/base.py:48 -#: xpack/plugins/cloud/models.py:124 xpack/plugins/gathered_user/models.py:30 +#: xpack/plugins/cloud/models.py:122 xpack/plugins/gathered_user/models.py:30 msgid "Created by" msgstr "によって作成された" @@ -763,7 +761,7 @@ msgstr "トリガーモード" #: xpack/plugins/change_auth_plan/models/base.py:201 #: xpack/plugins/change_auth_plan/serializers/app.py:66 #: xpack/plugins/change_auth_plan/serializers/asset.py:180 -#: xpack/plugins/cloud/models.py:181 +#: xpack/plugins/cloud/models.py:179 msgid "Reason" msgstr "理由" @@ -793,7 +791,7 @@ msgstr "OK" #: assets/models/base.py:32 audits/models.py:136 #: xpack/plugins/change_auth_plan/serializers/app.py:88 #: xpack/plugins/change_auth_plan/serializers/asset.py:199 -#: xpack/plugins/cloud/const.py:41 +#: xpack/plugins/cloud/const.py:36 msgid "Failed" msgstr "失敗しました" @@ -844,10 +842,10 @@ msgstr "帯域幅" msgid "Contact" msgstr "連絡先" -#: assets/models/cluster.py:22 authentication/serializers/password_mfa.py:29 -#: users/forms/profile.py:104 users/models/user.py:693 -#: users/templates/users/forgot_password.html:59 -#: users/templates/users/forgot_password.html:103 +#: assets/models/cluster.py:22 authentication/serializers/password_mfa.py:25 +#: users/forms/profile.py:103 users/models/user.py:693 +#: users/templates/users/forgot_password.html:55 +#: users/templates/users/forgot_password.html:121 msgid "Phone" msgstr "電話" @@ -1013,7 +1011,7 @@ msgid "Parent key" msgstr "親キー" #: assets/models/node.py:566 assets/serializers/system_user.py:267 -#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers/task.py:72 +#: xpack/plugins/cloud/models.py:96 xpack/plugins/cloud/serializers/task.py:70 msgid "Node" msgstr "ノード" @@ -1170,8 +1168,8 @@ msgstr "CPU情報" msgid "Actions" msgstr "アクション" -#: assets/serializers/backup.py:31 ops/mixin.py:26 ops/mixin.py:106 -#: ops/mixin.py:147 settings/serializers/auth/ldap.py:66 +#: assets/serializers/backup.py:31 ops/mixin.py:106 ops/mixin.py:147 +#: settings/serializers/auth/ldap.py:66 #: xpack/plugins/change_auth_plan/serializers/base.py:43 msgid "Periodic perform" msgstr "定期的なパフォーマンス" @@ -1581,8 +1579,8 @@ msgid "MFA" msgstr "MFA" #: audits/models.py:146 terminal/models/status.py:33 -#: tickets/models/ticket/general.py:281 xpack/plugins/cloud/models.py:177 -#: xpack/plugins/cloud/models.py:229 +#: tickets/models/ticket/general.py:281 xpack/plugins/cloud/models.py:175 +#: xpack/plugins/cloud/models.py:227 msgid "Status" msgstr "ステータス" @@ -1619,7 +1617,7 @@ msgid "Hosts display" msgstr "ホスト表示" #: audits/serializers.py:102 ops/models/command.py:27 -#: xpack/plugins/cloud/models.py:175 +#: xpack/plugins/cloud/models.py:173 msgid "Result" msgstr "結果" @@ -1683,16 +1681,16 @@ msgstr "この操作には、MFAを検証する必要があります" msgid "Current user not support mfa type: {}" msgstr "現在のユーザーはmfaタイプをサポートしていません: {}" -#: authentication/api/password.py:29 terminal/api/session.py:224 -#: users/views/profile/reset.py:74 +#: authentication/api/password.py:30 terminal/api/session.py:224 +#: users/views/profile/reset.py:45 users/views/profile/reset.py:107 msgid "User does not exist: {}" msgstr "ユーザーが存在しない: {}" -#: authentication/api/password.py:29 +#: authentication/api/password.py:30 msgid "No user matched" msgstr "ユーザーにマッチしなかった" -#: authentication/api/password.py:33 +#: authentication/api/password.py:34 msgid "" "The user is from {}, please go to the corresponding system to change the " "password" @@ -1700,10 +1698,12 @@ msgstr "" "ユーザーは {}からです。対応するシステムにアクセスしてパスワードを変更してくだ" "さい。" -#: authentication/api/password.py:59 +#: authentication/api/password.py:61 #: authentication/templates/authentication/login.html:256 #: users/templates/users/forgot_password.html:33 #: users/templates/users/forgot_password.html:34 +#: users/templates/users/forgot_password_previewing.html:13 +#: users/templates/users/forgot_password_previewing.html:14 msgid "Forgot password" msgstr "パスワードを忘れた" @@ -2015,6 +2015,7 @@ msgid "SMS verify code invalid" msgstr "SMS検証コードが無効" #: authentication/mfa/sms.py:12 settings/serializers/auth/sms.py:27 +#: users/templates/users/forgot_password.html:74 msgid "SMS" msgstr "SMS" @@ -2135,7 +2136,7 @@ msgid "binding reminder" msgstr "バインディングリマインダー" #: authentication/serializers/connection_token.py:23 -#: xpack/plugins/cloud/models.py:33 +#: xpack/plugins/cloud/models.py:34 msgid "Validity" msgstr "有効性" @@ -2149,16 +2150,16 @@ msgstr "アセットまたはアプリが必要" #: authentication/serializers/password_mfa.py:25 #: authentication/serializers/password_mfa.py:29 -#: authentication/serializers/password_mfa.py:33 -#: users/templates/users/forgot_password.html:95 +#: users/templates/users/forgot_password.html:117 msgid "The {} cannot be empty" msgstr "{} 空にしてはならない" -#: authentication/serializers/password_mfa.py:33 +#: authentication/serializers/password_mfa.py:29 #: notifications/backends/__init__.py:10 settings/serializers/email.py:19 -#: settings/serializers/email.py:50 users/forms/profile.py:103 -#: users/models/user.py:675 users/templates/users/forgot_password.html:55 -#: users/templates/users/forgot_password.html:108 +#: settings/serializers/email.py:50 users/forms/profile.py:102 +#: users/models/user.py:675 users/templates/users/forgot_password.html:51 +#: users/templates/users/forgot_password.html:70 +#: users/templates/users/forgot_password.html:126 msgid "Email" msgstr "メール" @@ -2312,7 +2313,7 @@ msgstr "新しいものを要求する" #: authentication/templates/authentication/_msg_reset_password_code.html:12 #: terminal/models/sharing.py:26 terminal/models/sharing.py:80 -#: users/forms/profile.py:105 users/templates/users/forgot_password.html:63 +#: users/forms/profile.py:104 users/templates/users/forgot_password.html:59 msgid "Verify code" msgstr "コードの確認" @@ -4760,17 +4761,17 @@ msgstr "" " " #: templates/_mfa_login_field.html:28 -#: users/templates/users/forgot_password.html:65 +#: users/templates/users/forgot_password.html:61 msgid "Send verification code" msgstr "確認コードを送信" #: templates/_mfa_login_field.html:106 -#: users/templates/users/forgot_password.html:122 +#: users/templates/users/forgot_password.html:139 msgid "Wait: " msgstr "待つ:" #: templates/_mfa_login_field.html:116 -#: users/templates/users/forgot_password.html:138 +#: users/templates/users/forgot_password.html:155 msgid "The verification code has been sent" msgstr "確認コードが送信されました" @@ -5247,7 +5248,7 @@ msgstr "アクセスキー" msgid "Access key secret" msgstr "アクセスキーシークレット" -#: terminal/serializers/storage.py:65 xpack/plugins/cloud/models.py:222 +#: terminal/serializers/storage.py:65 xpack/plugins/cloud/models.py:220 msgid "Region" msgstr "リージョン" @@ -5773,40 +5774,40 @@ msgstr "パスワードの確認" msgid "Password does not match" msgstr "パスワードが一致しない" -#: users/forms/profile.py:112 +#: users/forms/profile.py:115 msgid "Old password" msgstr "古いパスワード" -#: users/forms/profile.py:122 +#: users/forms/profile.py:125 msgid "Old password error" msgstr "古いパスワードエラー" -#: users/forms/profile.py:132 +#: users/forms/profile.py:135 msgid "Automatically configure and download the SSH key" msgstr "SSHキーの自動設定とダウンロード" -#: users/forms/profile.py:134 +#: users/forms/profile.py:137 msgid "ssh public key" msgstr "ssh公開キー" -#: users/forms/profile.py:135 +#: users/forms/profile.py:138 msgid "ssh-rsa AAAA..." msgstr "ssh-rsa AAAA.." -#: users/forms/profile.py:136 +#: users/forms/profile.py:139 msgid "Paste your id_rsa.pub here." msgstr "ここにid_rsa.pubを貼り付けます。" -#: users/forms/profile.py:149 +#: users/forms/profile.py:152 msgid "Public key should not be the same as your old one." msgstr "公開鍵は古いものと同じであってはなりません。" -#: users/forms/profile.py:153 users/serializers/profile.py:100 +#: users/forms/profile.py:156 users/serializers/profile.py:100 #: users/serializers/profile.py:183 users/serializers/profile.py:210 msgid "Not a valid ssh public key" msgstr "有効なssh公開鍵ではありません" -#: users/forms/profile.py:164 users/models/user.py:706 +#: users/forms/profile.py:167 users/models/user.py:706 msgid "Public key" msgstr "公開キー" @@ -5886,7 +5887,7 @@ msgstr "ユーザーパスワード履歴" msgid "Reset password" msgstr "パスワードのリセット" -#: users/notifications.py:85 users/views/profile/reset.py:139 +#: users/notifications.py:85 users/views/profile/reset.py:172 msgid "Reset password success" msgstr "パスワードのリセット成功" @@ -6082,17 +6083,14 @@ msgstr "ここをクリックしてパスワードを設定してください" msgid "Input your email, that will send a mail to your" msgstr "あなたのメールを入力し、それはあなたにメールを送信します" -#: users/templates/users/forgot_password.html:68 +#: users/templates/users/forgot_password.html:64 +#: users/templates/users/forgot_password_previewing.html:30 msgid "Submit" msgstr "送信" -#: users/templates/users/forgot_password.html:71 -msgid "Use the phone number to retrieve the password" -msgstr "携帯電話番号を使ってパスワードを探す" - -#: users/templates/users/forgot_password.html:72 -msgid "Use email to retrieve the password" -msgstr "メールアドレスを使ってパスワードを取り戻す" +#: users/templates/users/forgot_password_previewing.html:21 +msgid "Please enter the username for which you want to retrieve the password" +msgstr "パスワードを取り戻す必要があるユーザー名を入力してください" #: users/templates/users/mfa_setting.html:24 msgid "Enable MFA" @@ -6240,19 +6238,19 @@ msgstr "パスワード無効" msgid "Token invalid or expired" msgstr "トークンが無効または期限切れ" -#: users/views/profile/reset.py:112 +#: users/views/profile/reset.py:145 msgid "User auth from {}, go there change password" msgstr "ユーザー認証ソース {}, 対応するシステムにパスワードを変更してください" -#: users/views/profile/reset.py:119 +#: users/views/profile/reset.py:152 msgid "* Your password does not meet the requirements" msgstr "* パスワードが要件を満たしていない" -#: users/views/profile/reset.py:125 +#: users/views/profile/reset.py:158 msgid "* The new password cannot be the last {} passwords" msgstr "* 新しいパスワードを最後の {} パスワードにすることはできません" -#: users/views/profile/reset.py:140 +#: users/views/profile/reset.py:173 msgid "Reset password success, return to login page" msgstr "パスワードの成功をリセットし、ログインページに戻る" @@ -6523,27 +6521,27 @@ msgstr "プライベートIP" msgid "Instance name" msgstr "インスタンス名" -#: xpack/plugins/cloud/const.py:37 +#: xpack/plugins/cloud/const.py:32 msgid "Instance name and Partial IP" msgstr "インスタンス名と部分IP" -#: xpack/plugins/cloud/const.py:42 +#: xpack/plugins/cloud/const.py:37 msgid "Succeed" msgstr "成功" -#: xpack/plugins/cloud/const.py:46 +#: xpack/plugins/cloud/const.py:41 msgid "Unsync" msgstr "同期していません" -#: xpack/plugins/cloud/const.py:47 +#: xpack/plugins/cloud/const.py:42 msgid "New Sync" msgstr "新しい同期" -#: xpack/plugins/cloud/const.py:48 +#: xpack/plugins/cloud/const.py:43 msgid "Synced" msgstr "同期済み" -#: xpack/plugins/cloud/const.py:49 +#: xpack/plugins/cloud/const.py:44 msgid "Released" msgstr "リリース済み" @@ -6551,19 +6549,19 @@ msgstr "リリース済み" msgid "Cloud center" msgstr "クラウドセンター" -#: xpack/plugins/cloud/models.py:29 +#: xpack/plugins/cloud/models.py:30 msgid "Provider" msgstr "プロバイダー" -#: xpack/plugins/cloud/models.py:38 +#: xpack/plugins/cloud/models.py:39 msgid "Cloud account" msgstr "クラウドアカウント" -#: xpack/plugins/cloud/models.py:40 +#: xpack/plugins/cloud/models.py:41 msgid "Test cloud account" msgstr "クラウドアカウントのテスト" -#: xpack/plugins/cloud/models.py:84 xpack/plugins/cloud/serializers/task.py:69 +#: xpack/plugins/cloud/models.py:85 xpack/plugins/cloud/serializers/task.py:67 msgid "Account" msgstr "アカウント" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index dc81821c4..3bf37b362 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -157,13 +157,11 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. " #: acls/serializers/login_asset_acl.py:51 assets/models/base.py:176 #: assets/models/gathered_user.py:15 audits/models.py:139 #: authentication/forms.py:25 authentication/forms.py:27 -#: authentication/models.py:260 authentication/serializers/password_mfa.py:25 +#: authentication/models.py:260 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 -#: ops/models/adhoc.py:159 users/forms/profile.py:32 users/forms/profile.py:102 +#: ops/models/adhoc.py:159 users/forms/profile.py:32 users/forms/profile.py:109 #: users/models/user.py:671 users/templates/users/_msg_user_created.html:12 -#: users/templates/users/forgot_password.html:51 -#: users/templates/users/forgot_password.html:98 #: xpack/plugins/change_auth_plan/models/asset.py:34 #: xpack/plugins/change_auth_plan/models/asset.py:195 #: xpack/plugins/cloud/serializers/account_attrs.py:26 @@ -839,10 +837,10 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 authentication/serializers/password_mfa.py:29 -#: users/forms/profile.py:104 users/models/user.py:693 -#: users/templates/users/forgot_password.html:59 -#: users/templates/users/forgot_password.html:103 +#: assets/models/cluster.py:22 authentication/serializers/password_mfa.py:25 +#: users/forms/profile.py:103 users/models/user.py:693 +#: users/templates/users/forgot_password.html:55 +#: users/templates/users/forgot_password.html:121 msgid "Phone" msgstr "手机" @@ -1671,25 +1669,27 @@ msgstr "此操作需要验证您的 MFA" msgid "Current user not support mfa type: {}" msgstr "当前用户不支持 MFA 类型: {}" -#: authentication/api/password.py:29 terminal/api/session.py:224 -#: users/views/profile/reset.py:74 +#: authentication/api/password.py:30 terminal/api/session.py:224 +#: users/views/profile/reset.py:45 users/views/profile/reset.py:107 msgid "User does not exist: {}" msgstr "用户不存在: {}" -#: authentication/api/password.py:29 +#: authentication/api/password.py:30 msgid "No user matched" msgstr "没有匹配到用户" -#: authentication/api/password.py:33 +#: authentication/api/password.py:34 msgid "" "The user is from {}, please go to the corresponding system to change the " "password" msgstr "用户来自 {} 请去相应系统修改密码" -#: authentication/api/password.py:59 +#: authentication/api/password.py:61 #: authentication/templates/authentication/login.html:256 #: users/templates/users/forgot_password.html:33 #: users/templates/users/forgot_password.html:34 +#: users/templates/users/forgot_password_previewing.html:13 +#: users/templates/users/forgot_password_previewing.html:14 msgid "Forgot password" msgstr "忘记密码" @@ -1991,6 +1991,7 @@ msgid "SMS verify code invalid" msgstr "短信验证码校验失败" #: authentication/mfa/sms.py:12 settings/serializers/auth/sms.py:27 +#: users/templates/users/forgot_password.html:74 msgid "SMS" msgstr "短信" @@ -2121,16 +2122,16 @@ msgstr "资产或应用必填" #: authentication/serializers/password_mfa.py:25 #: authentication/serializers/password_mfa.py:29 -#: authentication/serializers/password_mfa.py:33 -#: users/templates/users/forgot_password.html:95 +#: users/templates/users/forgot_password.html:117 msgid "The {} cannot be empty" msgstr "{} 不能为空" -#: authentication/serializers/password_mfa.py:33 +#: authentication/serializers/password_mfa.py:29 #: notifications/backends/__init__.py:10 settings/serializers/email.py:19 -#: settings/serializers/email.py:50 users/forms/profile.py:103 -#: users/models/user.py:675 users/templates/users/forgot_password.html:55 -#: users/templates/users/forgot_password.html:108 +#: settings/serializers/email.py:50 users/forms/profile.py:102 +#: users/models/user.py:675 users/templates/users/forgot_password.html:51 +#: users/templates/users/forgot_password.html:70 +#: users/templates/users/forgot_password.html:126 msgid "Email" msgstr "邮件" @@ -2280,7 +2281,7 @@ msgstr "重新申请" #: authentication/templates/authentication/_msg_reset_password_code.html:12 #: terminal/models/sharing.py:26 terminal/models/sharing.py:80 -#: users/forms/profile.py:105 users/templates/users/forgot_password.html:63 +#: users/forms/profile.py:104 users/templates/users/forgot_password.html:59 msgid "Verify code" msgstr "验证码" @@ -4685,17 +4686,17 @@ msgstr "" " " #: templates/_mfa_login_field.html:28 -#: users/templates/users/forgot_password.html:65 +#: users/templates/users/forgot_password.html:61 msgid "Send verification code" msgstr "发送验证码" #: templates/_mfa_login_field.html:106 -#: users/templates/users/forgot_password.html:122 +#: users/templates/users/forgot_password.html:139 msgid "Wait: " msgstr "等待:" #: templates/_mfa_login_field.html:116 -#: users/templates/users/forgot_password.html:138 +#: users/templates/users/forgot_password.html:155 msgid "The verification code has been sent" msgstr "验证码已发送" @@ -5682,40 +5683,40 @@ msgstr "确认密码" msgid "Password does not match" msgstr "密码不一致" -#: users/forms/profile.py:112 +#: users/forms/profile.py:115 msgid "Old password" msgstr "原来密码" -#: users/forms/profile.py:122 +#: users/forms/profile.py:125 msgid "Old password error" msgstr "原来密码错误" -#: users/forms/profile.py:132 +#: users/forms/profile.py:135 msgid "Automatically configure and download the SSH key" msgstr "自动配置并下载SSH密钥" -#: users/forms/profile.py:134 +#: users/forms/profile.py:137 msgid "ssh public key" msgstr "SSH公钥" -#: users/forms/profile.py:135 +#: users/forms/profile.py:138 msgid "ssh-rsa AAAA..." msgstr "ssh-rsa AAAA..." -#: users/forms/profile.py:136 +#: users/forms/profile.py:139 msgid "Paste your id_rsa.pub here." msgstr "复制你的公钥到这里" -#: users/forms/profile.py:149 +#: users/forms/profile.py:152 msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms/profile.py:153 users/serializers/profile.py:100 +#: users/forms/profile.py:156 users/serializers/profile.py:100 #: users/serializers/profile.py:183 users/serializers/profile.py:210 msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/profile.py:164 users/models/user.py:706 +#: users/forms/profile.py:167 users/models/user.py:706 msgid "Public key" msgstr "SSH公钥" @@ -5795,7 +5796,7 @@ msgstr "用户密码历史" msgid "Reset password" msgstr "重置密码" -#: users/notifications.py:85 users/views/profile/reset.py:139 +#: users/notifications.py:85 users/views/profile/reset.py:172 msgid "Reset password success" msgstr "重置密码成功" @@ -5989,17 +5990,14 @@ msgstr "点击这里设置密码" msgid "Input your email, that will send a mail to your" msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中" -#: users/templates/users/forgot_password.html:68 +#: users/templates/users/forgot_password.html:64 +#: users/templates/users/forgot_password_previewing.html:30 msgid "Submit" msgstr "提交" -#: users/templates/users/forgot_password.html:71 -msgid "Use the phone number to retrieve the password" -msgstr "使用手机号找回密码" - -#: users/templates/users/forgot_password.html:72 -msgid "Use email to retrieve the password" -msgstr "使用邮箱找回密码" +#: users/templates/users/forgot_password_previewing.html:21 +msgid "Please enter the username for which you want to retrieve the password" +msgstr "请输入您需要找回密码的用户名" #: users/templates/users/mfa_setting.html:24 msgid "Enable MFA" @@ -6135,23 +6133,23 @@ msgstr "MFA(OTP) 禁用成功,返回登录页面" msgid "Password invalid" msgstr "用户名或密码无效" -#: users/views/profile/reset.py:96 users/views/profile/reset.py:107 +#: users/views/profile/reset.py:129 users/views/profile/reset.py:140 msgid "Token invalid or expired" msgstr "Token错误或失效" -#: users/views/profile/reset.py:112 +#: users/views/profile/reset.py:145 msgid "User auth from {}, go there change password" msgstr "用户认证源来自 {}, 请去相应系统修改密码" -#: users/views/profile/reset.py:119 +#: users/views/profile/reset.py:152 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/views/profile/reset.py:125 +#: users/views/profile/reset.py:158 msgid "* The new password cannot be the last {} passwords" msgstr "* 新密码不能是最近 {} 次的密码" -#: users/views/profile/reset.py:140 +#: users/views/profile/reset.py:173 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" diff --git a/apps/users/forms/profile.py b/apps/users/forms/profile.py index ca9db9e0c..56c496ce4 100644 --- a/apps/users/forms/profile.py +++ b/apps/users/forms/profile.py @@ -2,10 +2,9 @@ # from django import forms from django.utils.translation import gettext_lazy as _ -from captcha.fields import CaptchaField from common.utils import validate_ssh_public_key -from authentication.forms import EncryptedField +from authentication.forms import EncryptedField, CaptchaMixin from ..models import User @@ -13,7 +12,8 @@ __all__ = [ 'UserProfileForm', 'UserMFAForm', 'UserFirstLoginFinishForm', 'UserPasswordForm', 'UserPublicKeyForm', 'FileForm', 'UserTokenResetPasswordForm', 'UserForgotPasswordForm', - 'UserCheckPasswordForm', 'UserCheckOtpCodeForm' + 'UserCheckPasswordForm', 'UserCheckOtpCodeForm', + 'UserForgotPasswordPreviewingForm' ] @@ -99,11 +99,17 @@ class UserTokenResetPasswordForm(forms.Form): class UserForgotPasswordForm(forms.Form): - username = forms.CharField(label=_("Username")) email = forms.CharField(label=_("Email"), required=False) - phone = forms.CharField(label=_('Phone'), required=False, max_length=11) + sms = forms.CharField(label=_('SMS'), required=False, max_length=11) code = forms.CharField(label=_('Verify code'), max_length=6, required=False) - form_type = forms.CharField(widget=forms.HiddenInput({'value': 'email'})) + form_type = forms.ChoiceField( + choices=[('sms', _('SMS')), ('email', _('Email'))], + widget=forms.HiddenInput({'value': 'email'}) + ) + + +class UserForgotPasswordPreviewingForm(CaptchaMixin): + username = forms.CharField(label=_("Username")) class UserPasswordForm(UserTokenResetPasswordForm): diff --git a/apps/users/templates/users/forgot_password.html b/apps/users/templates/users/forgot_password.html index a5770fdcc..8aa2adc69 100644 --- a/apps/users/templates/users/forgot_password.html +++ b/apps/users/templates/users/forgot_password.html @@ -22,85 +22,93 @@ height: 100%; vertical-align: top; } - .display-fade { - display: none; - } - .display-show { - display: block; - } {% endblock %} {% block html_title %}{% trans 'Forgot password' %}{% endblock %} {% block title %} {% trans 'Forgot password' %}?{% endblock %} {% block content %} -

+

{% trans 'Input your email, that will send a mail to your' %}

-

+

Enter your phone number and a verification code will be sent to your phone

+ {% if 'code' in form.errors %} +

{{ form.code.errors.as_text }}

+ {% endif %} +
+
{% csrf_token %} {% bootstrap_field form.form_type layout="horizontal" %} -
- - +
+
-
- - +
+
-
- - -
-
- - +
+ + type='button' onclick="sendChallengeCode(this)"> + {% trans 'Send verification code' %} +
- {% if XPACK_ENABLED %} - {% trans 'Use the phone number to retrieve the password' %} - {% trans 'Use email to retrieve the password' %} - {% endif %}