From 5d4fa2205811e7aaa5034c2955f1090262ec1348 Mon Sep 17 00:00:00 2001 From: maninhill <41712985+maninhill@users.noreply.github.com> Date: Fri, 22 Sep 2023 09:45:22 +0800 Subject: [PATCH 001/114] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96=20README=20?= =?UTF-8?q?=E6=96=87=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index e83f8ee8f..c445740a2 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@

- JumpServer v3.0 正式发布。 -
9 年时间,倾情投入,用心做好一款开源堡垒机。

From 71285935028e41398f2782b6a1ada8fb8214058b Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 22 Sep 2023 15:06:58 +0800 Subject: [PATCH 002/114] =?UTF-8?q?perf:=20CeleryTaskExecution=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=BB=98=E8=AE=A4=E6=8E=92=E5=BA=8F=20(#11663)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- .../migrations/0004_auto_20230106_1507.py | 4 ++-- .../migrations/0015_auto_20230825_1120.py | 2 +- .../0027_alter_celerytaskexecution_options.py | 17 +++++++++++++++++ apps/ops/models/celery.py | 5 +++-- apps/terminal/migrations/0062_applet_edition.py | 2 +- 5 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 apps/ops/migrations/0027_alter_celerytaskexecution_options.py diff --git a/apps/accounts/migrations/0004_auto_20230106_1507.py b/apps/accounts/migrations/0004_auto_20230106_1507.py index 3be64ef5a..6392fce3b 100644 --- a/apps/accounts/migrations/0004_auto_20230106_1507.py +++ b/apps/accounts/migrations/0004_auto_20230106_1507.py @@ -13,11 +13,11 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='changesecretautomation', name='secret_strategy', - field=models.CharField(choices=[('specific', 'Specific password'), ('random', 'Random')], default='specific', max_length=16, verbose_name='Secret strategy'), + field=models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy'), ), migrations.AlterField( model_name='pushaccountautomation', name='secret_strategy', - field=models.CharField(choices=[('specific', 'Specific password'), ('random', 'Random')], default='specific', max_length=16, verbose_name='Secret strategy'), + field=models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy'), ), ] diff --git a/apps/accounts/migrations/0015_auto_20230825_1120.py b/apps/accounts/migrations/0015_auto_20230825_1120.py index 083c88634..533613855 100644 --- a/apps/accounts/migrations/0015_auto_20230825_1120.py +++ b/apps/accounts/migrations/0015_auto_20230825_1120.py @@ -29,6 +29,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='accounttemplate', name='secret_strategy', - field=models.CharField(choices=[('specific', 'Specific password'), ('random', 'Random')], default='specific', max_length=16, verbose_name='Secret strategy'), + field=models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy'), ), ] diff --git a/apps/ops/migrations/0027_alter_celerytaskexecution_options.py b/apps/ops/migrations/0027_alter_celerytaskexecution_options.py new file mode 100644 index 000000000..0b3e07308 --- /dev/null +++ b/apps/ops/migrations/0027_alter_celerytaskexecution_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.10 on 2023-09-22 06:52 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('ops', '0026_auto_20230810_1039'), + ] + + operations = [ + migrations.AlterModelOptions( + name='celerytaskexecution', + options={'ordering': ['-date_start'], 'verbose_name': 'Celery Task Execution'}, + ), + ] diff --git a/apps/ops/models/celery.py b/apps/ops/models/celery.py index e0f566ff1..f17ffe685 100644 --- a/apps/ops/models/celery.py +++ b/apps/ops/models/celery.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- # -import uuid import os +import uuid -from django.utils.translation import gettext_lazy as _ from django.conf import settings from django.db import models +from django.utils.translation import gettext_lazy as _ from ops.celery import app @@ -83,4 +83,5 @@ class CeleryTaskExecution(models.Model): return "{}: {}".format(self.name, self.id) class Meta: + ordering = ['-date_start'] verbose_name = _("Celery Task Execution") diff --git a/apps/terminal/migrations/0062_applet_edition.py b/apps/terminal/migrations/0062_applet_edition.py index bd5118edf..8ca93ce2f 100644 --- a/apps/terminal/migrations/0062_applet_edition.py +++ b/apps/terminal/migrations/0062_applet_edition.py @@ -12,7 +12,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='applet', name='edition', - field=models.CharField(choices=[('community', 'Community'), ('enterprise', 'Enterprise')], + field=models.CharField(choices=[('community', 'Community edition'), ('enterprise', 'Enterprise')], default='community', max_length=128, verbose_name='Edition'), ), ] From cefd9f4ab25709886d7476021637e07a108b8141 Mon Sep 17 00:00:00 2001 From: Bai Date: Fri, 22 Sep 2023 15:05:18 +0800 Subject: [PATCH 003/114] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E8=B5=84=E4=BA=A7=E6=95=B0=E9=87=8F=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E4=B8=8D=E5=87=86=E7=A1=AE=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 --- apps/assets/models/node.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 139295f69..d4fc8165d 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -402,12 +402,7 @@ class NodeAssetsMixin(NodeAllAssetsMappingMixin): return Asset.objects.filter(q).distinct() def get_assets_amount(self): - q = Q(node__key__startswith=f'{self.key}:') | Q(node__key=self.key) - return self.assets.through.objects.filter(q).count() - - def get_assets_account_by_children(self): - children = self.get_all_children().values_list() - return self.assets.through.objects.filter(node_id__in=children).count() + return self.get_all_assets().count() @classmethod def get_node_all_assets_by_key_v2(cls, key): From 43dbb4c22693b5bae8ffcfdcefd0c8a6b34ef6bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=B0=8F=E7=99=BD?= <296015668@qq.com> Date: Thu, 21 Sep 2023 17:43:37 +0800 Subject: [PATCH 004/114] =?UTF-8?q?perf:=20=E6=B7=BB=E5=8A=A0=20patch=20?= =?UTF-8?q?=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 2174df9c0..33ce70b25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,6 +39,7 @@ ARG TOOLS=" \ locales \ nmap \ openssh-client \ + patch \ sshpass \ telnet \ vim \ From 0cf17310e1492c5c4c1a5be6bc37cf281e8df9f7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 22 Sep 2023 17:07:52 +0800 Subject: [PATCH 005/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20DOMAINS=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=2080=E5=92=8C443=20=E4=B8=8D=E7=94=9F?= =?UTF-8?q?=E6=95=88=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/jumpserver/settings/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/jumpserver/settings/base.py b/apps/jumpserver/settings/base.py index 4cf1c3c77..4344660f7 100644 --- a/apps/jumpserver/settings/base.py +++ b/apps/jumpserver/settings/base.py @@ -79,6 +79,7 @@ if CONFIG.SITE_URL: ALLOWED_DOMAINS = DOMAINS.split(',') if DOMAINS else ['localhost:8080'] ALLOWED_DOMAINS = [host.strip() for host in ALLOWED_DOMAINS] ALLOWED_DOMAINS = [host.replace('http://', '').replace('https://', '') for host in ALLOWED_DOMAINS if host] +ALLOWED_DOMAINS = [host.replace(':80', '').replace(':443', '') for host in ALLOWED_DOMAINS] ALLOWED_DOMAINS = [host.split('/')[0] for host in ALLOWED_DOMAINS if host] DEBUG_HOSTS = ('127.0.0.1', 'localhost', 'core') From 138adeff763e7609580b4208941af82fe0a66e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=B0=8F=E7=99=BD?= <296015668@qq.com> Date: Fri, 22 Sep 2023 16:16:22 +0800 Subject: [PATCH 006/114] =?UTF-8?q?perf:=20=E6=B7=BB=E5=8A=A0=20ping=20?= =?UTF-8?q?=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 33ce70b25..581f40f18 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,6 +36,7 @@ ARG TOOLS=" \ curl \ default-libmysqlclient-dev \ default-mysql-client \ + iputils-ping \ locales \ nmap \ openssh-client \ From 0f3ddc3bf1e81bd701df54d42c09fd826d487422 Mon Sep 17 00:00:00 2001 From: Bai Date: Mon, 25 Sep 2023 16:19:02 +0800 Subject: [PATCH 007/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=94=A8=E6=88=B7=E5=90=8C=E6=AD=A5=E5=90=8C=E6=97=B6?= =?UTF-8?q?=E5=8C=85=E5=90=ABpwd/ssh-key=E5=AF=BC=E8=87=B4=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=B4=A6=E5=8F=B7id=E5=86=B2=E7=AA=81=E6=8A=A5?= =?UTF-8?q?=E9=94=99=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/assets/migrations/0100_auto_20220711_1413.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/migrations/0100_auto_20220711_1413.py b/apps/assets/migrations/0100_auto_20220711_1413.py index 4e57be633..8bb3309e4 100644 --- a/apps/assets/migrations/0100_auto_20220711_1413.py +++ b/apps/assets/migrations/0100_auto_20220711_1413.py @@ -25,7 +25,7 @@ def migrate_asset_accounts(apps, schema_editor): count += len(auth_books) # auth book 和 account 相同的属性 same_attrs = [ - 'id', 'username', 'comment', 'date_created', 'date_updated', + 'username', 'comment', 'date_created', 'date_updated', 'created_by', 'asset_id', 'org_id', ] # 认证的属性,可能是 auth_book 的,可能是 system_user 的 From 4065baf78511b83f466f606dd74efeb358efaad4 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:25:44 +0800 Subject: [PATCH 008/114] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=A0=A1=E5=9E=92=E6=9C=BA=E6=97=B6=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=91=98=20(#11686)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/acls/const.py | 1 + apps/acls/notifications.py | 38 ++ .../templates/acls/user_login_reminder.html | 14 + apps/audits/signal_handlers/login_log.py | 27 +- apps/authentication/mixins.py | 7 +- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 418 +++++++++-------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 419 ++++++++++-------- apps/tickets/handlers/base.py | 2 +- 10 files changed, 553 insertions(+), 381 deletions(-) create mode 100644 apps/acls/notifications.py create mode 100644 apps/acls/templates/acls/user_login_reminder.html diff --git a/apps/acls/const.py b/apps/acls/const.py index c2d2be586..cccc906f4 100644 --- a/apps/acls/const.py +++ b/apps/acls/const.py @@ -7,3 +7,4 @@ class ActionChoices(models.TextChoices): accept = 'accept', _('Accept') review = 'review', _('Review') warning = 'warning', _('Warning') + notice = 'notice', _('Notifications') diff --git a/apps/acls/notifications.py b/apps/acls/notifications.py new file mode 100644 index 000000000..57414c65d --- /dev/null +++ b/apps/acls/notifications.py @@ -0,0 +1,38 @@ +from django.template.loader import render_to_string +from django.utils.translation import gettext_lazy as _ + +from audits.models import UserLoginLog +from notifications.notifications import UserMessage +from users.models import User + + +class UserLoginReminderMsg(UserMessage): + subject = _('User login reminder') + + def __init__(self, user, user_log: UserLoginLog): + self.user_log = user_log + super().__init__(user) + + def get_html_msg(self) -> dict: + user_log = self.user_log + + context = { + 'ip': user_log.ip, + 'city': user_log.city, + 'username': user_log.username, + 'recipient': self.user.username, + 'user_agent': user_log.user_agent, + } + message = render_to_string('acls/user_login_reminder.html', context) + + print('message', message) + return { + 'subject': str(self.subject), + 'message': message + } + + @classmethod + def gen_test_msg(cls): + user = User.objects.first() + user_log = UserLoginLog.objects.first() + return cls(user, user_log) diff --git a/apps/acls/templates/acls/user_login_reminder.html b/apps/acls/templates/acls/user_login_reminder.html new file mode 100644 index 000000000..3af4fd52a --- /dev/null +++ b/apps/acls/templates/acls/user_login_reminder.html @@ -0,0 +1,14 @@ +{% load i18n %} + +

{% trans 'Respectful' %}{{ recipient }},

+
+

{% trans 'Username' %}: [{{ username }}]

+

IP: [{{ ip }}]

+

{% trans 'Login city' %}: [{{ city }}]

+

{% trans 'User agent' %}: [{{ user_agent }}]

+
+ +

{% trans 'The user has just successfully logged into the system. Please ensure that this is an authorized operation. If you suspect that this is an unauthorized access, please take appropriate measures immediately.' %}

+ +

{% trans 'Thank you' %}!

+ diff --git a/apps/audits/signal_handlers/login_log.py b/apps/audits/signal_handlers/login_log.py index fae32a44b..835e4842f 100644 --- a/apps/audits/signal_handlers/login_log.py +++ b/apps/audits/signal_handlers/login_log.py @@ -11,6 +11,9 @@ from django.utils.functional import LazyObject from django.utils.translation import gettext_lazy as _ from rest_framework.request import Request +from acls.const import ActionChoices +from acls.models import LoginACL +from acls.notifications import UserLoginReminderMsg from audits.models import UserLoginLog from authentication.signals import post_auth_failed, post_auth_success from authentication.utils import check_different_city_login_if_need @@ -102,21 +105,37 @@ def create_user_session(request, user_id, instance: UserLoginLog): request.session['user_session_id'] = user_session.id +def send_login_info_to_reviewers(instance: UserLoginLog, reviewers): + for reviewer in reviewers: + UserLoginReminderMsg(reviewer, instance).publish_async() + + @receiver(post_auth_success) def on_user_auth_success(sender, user, request, login_type=None, **kwargs): logger.debug('User login success: {}'.format(user.username)) check_different_city_login_if_need(user, request) - data = generate_data( - user.username, request, login_type=login_type - ) - request.session['login_time'] = data['datetime'].strftime("%Y-%m-%d %H:%M:%S") + data = generate_data(user.username, request, login_type=login_type) + request.session['login_time'] = data['datetime'].strftime('%Y-%m-%d %H:%M:%S') data.update({'mfa': int(user.mfa_enabled), 'status': True}) instance = write_login_log(**data) + # TODO 目前只记录 web 登录的 session if instance.type != LoginTypeChoices.web: return create_user_session(request, user.id, instance) + auth_notice_required = request.session.get('auth_notice_required') + if not auth_notice_required: + return + + auth_acl_id = request.session.get('auth_acl_id') + acl = LoginACL.objects.filter(id=auth_acl_id, action=ActionChoices.notice).first() + if not acl or not acl.reviewers.exists(): + return + + reviewers = acl.reviewers.all() + send_login_info_to_reviewers(instance, reviewers) + @receiver(post_auth_failed) def on_user_auth_failed(sender, username, request, reason='', **kwargs): diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index b301af6c3..9667e85c7 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -355,6 +355,11 @@ class AuthACLMixin: self.request.session['auth_acl_id'] = str(acl.id) return + if acl.is_action(acl.ActionChoices.notice): + self.request.session['auth_notice_required'] = '1' + self.request.session['auth_acl_id'] = str(acl.id) + return + def _check_third_party_login_acl(self): request = self.request error_message = getattr(request, 'error_message', None) @@ -513,7 +518,7 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost def clear_auth_mark(self): keys = [ 'auth_password', 'user_id', 'auth_confirm_required', - 'auth_ticket_id', 'auth_acl_id' + 'auth_notice_required', 'auth_ticket_id', 'auth_acl_id' ] for k in keys: self.request.session.pop(k, '') diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index e12251412..8e58c4d50 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ffdd50c364a510b4f5cfe7922a5f1a604e8bc7b03aa43ece1dff0250ccde6d6 -size 160575 +oid sha256:47cea504e9acfdcc94a45f2ea96216dddde065d134a92a0ef3e92815a12df6fc +size 162024 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index aa30a95b7..1c40efb67 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: 2023-09-19 15:41+0800\n" +"POT-Creation-Date: 2023-09-25 16:22+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -24,9 +24,9 @@ msgstr "パラメータ 'action' は [{}] でなければなりません。" #: accounts/const/account.py:6 #: accounts/serializers/automations/change_secret.py:32 -#: assets/models/_user.py:24 audits/signal_handlers/login_log.py:35 +#: assets/models/_user.py:24 audits/signal_handlers/login_log.py:37 #: authentication/confirm/password.py:9 authentication/forms.py:32 -#: authentication/templates/authentication/login.html:286 +#: authentication/templates/authentication/login.html:324 #: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47 #: users/forms/profile.py:22 users/serializers/user.py:105 #: users/templates/users/_msg_user_created.html:13 @@ -71,7 +71,7 @@ msgstr "動的コード" msgid "Anonymous account" msgstr "匿名ユーザー" -#: accounts/const/account.py:25 users/models/user.py:744 +#: accounts/const/account.py:25 users/models/user.py:741 msgid "Local" msgstr "ローカル" @@ -79,7 +79,7 @@ msgstr "ローカル" msgid "Collected" msgstr "集めました" -#: accounts/const/account.py:27 accounts/serializers/account/account.py:27 +#: accounts/const/account.py:27 accounts/serializers/account/account.py:28 #: settings/serializers/auth/sms.py:75 msgid "Template" msgstr "テンプレート" @@ -96,7 +96,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:155 audits/const.py:55 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:62 terminal/const.py:77 xpack/plugins/cloud/const.py:43 +#: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 msgid "Failed" msgstr "失敗しました" @@ -217,15 +217,15 @@ msgstr "ユーザー %s がパスワードを閲覧/導き出しました" #: accounts/models/account.py:48 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:205 -#: accounts/serializers/account/account.py:250 +#: accounts/serializers/account/account.py:209 +#: accounts/serializers/account/account.py:254 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:111 #: accounts/serializers/automations/change_secret.py:131 #: acls/serializers/base.py:123 assets/models/asset/common.py:93 #: assets/models/asset/common.py:334 assets/models/cmd_filter.py:36 #: assets/serializers/domain.py:19 assets/serializers/label.py:27 -#: audits/models.py:54 authentication/models/connection_token.py:36 +#: audits/models.py:56 authentication/models/connection_token.py:36 #: perms/models/asset_permission.py:64 perms/serializers/permission.py:34 #: terminal/backends/command/models.py:17 terminal/models/session/session.py:31 #: terminal/notifications.py:155 terminal/serializers/command.py:17 @@ -237,8 +237,8 @@ msgid "Asset" msgstr "資産" #: accounts/models/account.py:52 accounts/models/template.py:15 -#: accounts/serializers/account/account.py:212 -#: accounts/serializers/account/account.py:260 +#: accounts/serializers/account/account.py:216 +#: accounts/serializers/account/account.py:264 #: accounts/serializers/account/template.py:25 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" @@ -250,8 +250,8 @@ msgstr "から切り替え" msgid "Version" msgstr "バージョン" -#: accounts/models/account.py:56 accounts/serializers/account/account.py:207 -#: users/models/user.py:846 +#: accounts/models/account.py:56 accounts/serializers/account/account.py:211 +#: users/models/user.py:843 msgid "Source" msgstr "ソース" @@ -263,7 +263,7 @@ msgstr "ソース ID" #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 #: acls/serializers/base.py:124 assets/serializers/asset/common.py:125 -#: assets/serializers/gateway.py:28 audits/models.py:55 ops/models/base.py:18 +#: assets/serializers/gateway.py:28 audits/models.py:57 ops/models/base.py:18 #: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 #: terminal/backends/command/models.py:18 terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 @@ -306,7 +306,7 @@ msgid "Account backup plan" msgstr "アカウントバックアップ計画" #: accounts/models/automations/backup_account.py:91 -#: assets/models/automations/base.py:115 audits/models.py:61 +#: assets/models/automations/base.py:115 audits/models.py:63 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:228 #: ops/templates/ops/celery_task_log.html:75 #: perms/models/asset_permission.py:72 terminal/models/applet/host.py:140 @@ -334,7 +334,7 @@ msgstr "アカウントのバックアップスナップショット" msgid "Trigger mode" msgstr "トリガーモード" -#: accounts/models/automations/backup_account.py:105 audits/models.py:195 +#: accounts/models/automations/backup_account.py:105 audits/models.py:197 #: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "理由" @@ -420,10 +420,10 @@ msgid "Date finished" msgstr "終了日" #: accounts/models/automations/change_secret.py:44 -#: accounts/serializers/account/account.py:252 assets/const/automation.py:8 +#: accounts/serializers/account/account.py:256 assets/const/automation.py:8 #: authentication/templates/authentication/passkey.html:173 -#: authentication/views/base.py:26 authentication/views/base.py:27 -#: authentication/views/base.py:28 common/const/choices.py:20 +#: authentication/views/base.py:27 authentication/views/base.py:28 +#: authentication/views/base.py:29 common/const/choices.py:20 msgid "Error" msgstr "間違い" @@ -442,13 +442,13 @@ msgstr "最終ログイン日" #: accounts/models/automations/gather_account.py:17 #: accounts/models/automations/push_account.py:15 accounts/models/base.py:65 #: accounts/serializers/account/virtual.py:21 acls/serializers/base.py:19 -#: acls/serializers/base.py:50 assets/models/_user.py:23 audits/models.py:180 -#: authentication/forms.py:25 authentication/forms.py:27 -#: authentication/models/temp_token.py:9 +#: acls/serializers/base.py:50 acls/templates/acls/user_login_reminder.html:5 +#: assets/models/_user.py:23 audits/models.py:182 authentication/forms.py:25 +#: authentication/forms.py:27 authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: users/forms/profile.py:32 users/forms/profile.py:115 -#: users/models/user.py:796 users/templates/users/_msg_user_created.html:12 +#: users/models/user.py:793 users/templates/users/_msg_user_created.html:12 #: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" msgstr "ユーザー名" @@ -476,7 +476,7 @@ msgstr "トリガー方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:41 #: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 -#: audits/models.py:88 audits/serializers.py:84 +#: audits/models.py:90 audits/serializers.py:84 #: authentication/serializers/connect_token_secret.py:116 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" @@ -491,7 +491,7 @@ msgid "Verify asset account" msgstr "アカウントの確認" #: accounts/models/base.py:37 accounts/models/base.py:67 -#: accounts/serializers/account/account.py:432 +#: accounts/serializers/account/account.py:436 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:45 #: authentication/serializers/connect_token_secret.py:41 @@ -537,7 +537,7 @@ msgstr "パスワードルール" #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 users/forms/profile.py:33 #: users/models/group.py:13 users/models/preference.py:11 -#: users/models/user.py:798 xpack/plugins/cloud/models.py:32 +#: users/models/user.py:795 xpack/plugins/cloud/models.py:32 #: xpack/plugins/cloud/models.py:273 xpack/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名前" @@ -649,15 +649,15 @@ msgstr "" "{} -暗号化変更タスクが完了しました: 暗号化パスワードが設定されていません-個人" "情報にアクセスしてください-> ファイル暗号化パスワードを設定してください" -#: accounts/serializers/account/account.py:30 +#: accounts/serializers/account/account.py:31 msgid "Push now" msgstr "今すぐプッシュ" -#: accounts/serializers/account/account.py:37 +#: accounts/serializers/account/account.py:38 msgid "Exist policy" msgstr "アカウントの存在ポリシー" -#: accounts/serializers/account/account.py:185 applications/models.py:11 +#: accounts/serializers/account/account.py:189 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:89 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 #: assets/serializers/platform.py:133 assets/serializers/platform.py:229 @@ -666,7 +666,7 @@ msgstr "アカウントの存在ポリシー" msgid "Category" msgstr "カテゴリ" -#: accounts/serializers/account/account.py:186 +#: accounts/serializers/account/account.py:190 #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 #: acls/serializers/command_acl.py:18 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 @@ -686,26 +686,26 @@ msgstr "カテゴリ" msgid "Type" msgstr "タイプ" -#: accounts/serializers/account/account.py:201 +#: accounts/serializers/account/account.py:205 msgid "Asset not found" msgstr "資産が存在しません" -#: accounts/serializers/account/account.py:241 +#: accounts/serializers/account/account.py:245 msgid "Has secret" msgstr "エスクローされたパスワード" -#: accounts/serializers/account/account.py:251 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:255 ops/models/celery.py:60 #: tickets/models/comment.py:13 tickets/models/ticket/general.py:45 #: tickets/models/ticket/general.py:279 tickets/serializers/super_ticket.py:14 #: tickets/serializers/ticket/ticket.py:21 msgid "State" msgstr "状態" -#: accounts/serializers/account/account.py:253 +#: accounts/serializers/account/account.py:257 msgid "Changed" msgstr "編集済み" -#: accounts/serializers/account/account.py:263 +#: accounts/serializers/account/account.py:267 #: accounts/serializers/automations/base.py:22 acls/models/base.py:97 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 ops/models/base.py:17 @@ -714,29 +714,29 @@ msgstr "編集済み" msgid "Assets" msgstr "資産" -#: accounts/serializers/account/account.py:318 +#: accounts/serializers/account/account.py:322 msgid "Account already exists" msgstr "アカウントはすでに存在しています" -#: accounts/serializers/account/account.py:368 +#: accounts/serializers/account/account.py:372 #, python-format msgid "Asset does not support this secret type: %s" msgstr "アセットはアカウント タイプをサポートしていません: %s" -#: accounts/serializers/account/account.py:400 +#: accounts/serializers/account/account.py:404 msgid "Account has exist" msgstr "アカウントはすでに存在しています" -#: accounts/serializers/account/account.py:433 +#: accounts/serializers/account/account.py:437 #: authentication/serializers/connect_token_secret.py:156 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:31 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:440 acls/serializers/base.py:116 -#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:50 -#: audits/models.py:86 audits/models.py:164 audits/models.py:262 +#: accounts/serializers/account/account.py:447 acls/serializers/base.py:116 +#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:52 +#: audits/models.py:88 audits/models.py:166 audits/models.py:264 #: audits/serializers.py:171 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 @@ -748,12 +748,12 @@ msgstr "ID" #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:996 -#: users/models/user.py:1032 users/serializers/group.py:18 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:993 +#: users/models/user.py:1029 users/serializers/group.py:18 msgid "User" msgstr "ユーザー" -#: accounts/serializers/account/account.py:441 +#: accounts/serializers/account/account.py:448 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:158 terminal/notifications.py:207 msgid "Date" @@ -761,7 +761,7 @@ msgstr "日付" #: accounts/serializers/account/backup.py:31 #: accounts/serializers/automations/base.py:36 -#: assets/serializers/automations/base.py:34 ops/mixin.py:23 ops/mixin.py:103 +#: assets/serializers/automations/base.py:34 ops/mixin.py:23 ops/mixin.py:104 #: settings/serializers/auth/ldap.py:66 msgid "Periodic perform" msgstr "定期的なパフォーマンス" @@ -798,28 +798,24 @@ msgstr "" "場合は、`username@domain`のようになります。" #: accounts/serializers/account/template.py:11 -#, fuzzy -#| msgid "Password strength" msgid "Password length" -msgstr "パスワードの強さ" +msgstr "パスワードの長さ" #: accounts/serializers/account/template.py:12 -#, fuzzy -#| msgid "Powershell" msgid "Lowercase" -msgstr "PowerShell" +msgstr "小文字" #: accounts/serializers/account/template.py:13 msgid "Uppercase" -msgstr "" +msgstr "大文字" #: accounts/serializers/account/template.py:14 msgid "Digit" -msgstr "" +msgstr "数値#スウスウ#" #: accounts/serializers/account/template.py:15 msgid "Special symbol" -msgstr "" +msgstr "特殊記号" #: accounts/serializers/account/template.py:36 msgid "Secret generation strategy for account creation" @@ -844,7 +840,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:834 +#: tickets/models/ticket/general.py:297 users/models/user.py:831 #: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "コメント" @@ -895,8 +891,8 @@ msgid "Automation task execution" msgstr "自動タスク実行履歴" #: accounts/serializers/automations/change_secret.py:154 audits/const.py:54 -#: audits/models.py:60 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:60 ops/serializers/celery.py:40 +#: audits/models.py:62 audits/signal_handlers/activity_log.py:33 +#: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: terminal/const.py:76 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:119 msgid "Success" @@ -986,6 +982,10 @@ msgstr "レビュー担当者" msgid "Warning" msgstr "警告" +#: acls/const.py:10 notifications/apps.py:7 +msgid "Notifications" +msgstr "通知" + #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:97 #: xpack/plugins/cloud/models.py:275 @@ -1097,6 +1097,10 @@ msgstr "ログインasset acl" msgid "Login asset confirm" msgstr "ログイン資産の確認" +#: acls/notifications.py:10 +msgid "User login reminder" +msgstr "ユーザーログインのリマインダ" + #: acls/serializers/base.py:11 acls/serializers/login_acl.py:11 msgid "With * indicating a match all. " msgstr "* はすべて一致することを示します。" @@ -1152,6 +1156,36 @@ msgstr "IP" msgid "Time Period" msgstr "期間" +#: acls/templates/acls/user_login_reminder.html:3 +msgid "Respectful" +msgstr "尊敬する" + +#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:188 +#: audits/models.py:257 +#: authentication/templates/authentication/_msg_different_city.html:11 +#: tickets/models/ticket/login_confirm.py:11 +msgid "Login city" +msgstr "ログイン都市" + +#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:191 +#: audits/models.py:258 audits/serializers.py:65 +msgid "User agent" +msgstr "ユーザーエージェント" + +#: acls/templates/acls/user_login_reminder.html:11 +msgid "" +"The user has just successfully logged into the system. Please ensure that " +"this is an authorized operation. If you suspect that this is an unauthorized " +"access, please take appropriate measures immediately." +msgstr "" +"ユーザーはシステムに正常にログインしたばかりです。許可されたアクションである" +"ことを確認してください。不正アクセスが疑われる場合は、すぐに適切な措置を取っ" +"てください。" + +#: acls/templates/acls/user_login_reminder.html:13 +msgid "Thank you" +msgstr "ありがとうございます。" + #: applications/apps.py:9 msgid "Applications" msgstr "アプリケーション" @@ -1260,7 +1294,7 @@ msgstr "無効" #: assets/const/base.py:34 settings/serializers/basic.py:6 #: users/serializers/preference/koko.py:15 -#: users/serializers/preference/lina.py:32 +#: users/serializers/preference/lina.py:39 #: users/serializers/preference/luna.py:60 msgid "Basic" msgstr "基本" @@ -1473,19 +1507,19 @@ msgstr "SSHパブリックキー" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 -#: audits/models.py:259 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:227 users/models/user.py:1033 +#: audits/models.py:261 common/db/models.py:34 ops/models/base.py:54 +#: ops/models/job.py:227 users/models/user.py:1030 msgid "Date created" msgstr "作成された日付" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:855 +#: common/db/models.py:35 users/models/user.py:852 msgid "Date updated" msgstr "更新日" #: assets/models/_user.py:30 assets/models/cmd_filter.py:44 #: assets/models/cmd_filter.py:91 assets/models/group.py:18 -#: common/db/models.py:32 users/models/user.py:841 +#: common/db/models.py:32 users/models/user.py:838 #: users/serializers/group.py:29 msgid "Created by" msgstr "によって作成された" @@ -1521,7 +1555,7 @@ msgstr "プロトコル" msgid "Sudo" msgstr "すど" -#: assets/models/_user.py:55 ops/const.py:49 +#: assets/models/_user.py:55 ops/const.py:49 ops/const.py:59 msgid "Shell" msgstr "シェル" @@ -1650,7 +1684,7 @@ msgstr "自動化されたタスク" msgid "Asset automation task" msgstr "アセットの自動化タスク" -#: assets/models/automations/base.py:113 audits/models.py:200 +#: assets/models/automations/base.py:113 audits/models.py:202 #: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220 #: terminal/models/applet/applet.py:301 terminal/models/applet/host.py:139 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 @@ -1679,7 +1713,7 @@ msgstr "確認済みの日付" #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:61 #: perms/serializers/permission.py:32 users/models/group.py:25 -#: users/models/user.py:804 +#: users/models/user.py:801 msgid "User group" msgstr "ユーザーグループ" @@ -1729,11 +1763,11 @@ msgstr "デフォルト" msgid "Default asset group" msgstr "デフォルトアセットグループ" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1018 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1015 msgid "System" msgstr "システム" -#: assets/models/label.py:19 assets/models/node.py:544 +#: assets/models/label.py:19 assets/models/node.py:539 #: assets/serializers/cagegory.py:7 assets/serializers/cagegory.py:14 #: authentication/models/connection_token.py:29 #: authentication/serializers/connect_token_secret.py:122 @@ -1755,28 +1789,28 @@ msgstr "ラベル" msgid "New node" msgstr "新しいノード" -#: assets/models/node.py:472 audits/backends/db.py:55 audits/backends/db.py:56 +#: assets/models/node.py:467 audits/backends/db.py:55 audits/backends/db.py:56 msgid "empty" msgstr "空" -#: assets/models/node.py:543 perms/models/perm_node.py:28 +#: assets/models/node.py:538 perms/models/perm_node.py:28 msgid "Key" msgstr "キー" -#: assets/models/node.py:545 assets/serializers/node.py:20 +#: assets/models/node.py:540 assets/serializers/node.py:20 msgid "Full value" msgstr "フルバリュー" -#: assets/models/node.py:549 perms/models/perm_node.py:30 +#: assets/models/node.py:544 perms/models/perm_node.py:30 msgid "Parent key" msgstr "親キー" -#: assets/models/node.py:558 perms/serializers/permission.py:35 +#: assets/models/node.py:553 perms/serializers/permission.py:35 #: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 msgid "Node" msgstr "ノード" -#: assets/models/node.py:561 +#: assets/models/node.py:556 msgid "Can match node" msgstr "ノードを一致させることができます" @@ -2236,8 +2270,8 @@ msgstr "作成" msgid "Connect" msgstr "接続" -#: audits/const.py:30 authentication/templates/authentication/login.html:252 -#: authentication/templates/authentication/login.html:325 +#: audits/const.py:30 authentication/templates/authentication/login.html:290 +#: authentication/templates/authentication/login.html:363 #: templates/_header_bar.html:95 msgid "Login" msgstr "ログイン" @@ -2252,7 +2286,7 @@ msgstr "パスワードを変更する" msgid "Terminal" msgstr "ターミナル" -#: audits/const.py:41 audits/models.py:128 +#: audits/const.py:41 audits/models.py:130 msgid "Operate log" msgstr "ログの操作" @@ -2281,28 +2315,28 @@ msgstr "是" msgid "No" msgstr "否" -#: audits/models.py:43 +#: audits/models.py:45 msgid "Job audit log" msgstr "ジョブ監査ログ" -#: audits/models.py:52 audits/models.py:96 audits/models.py:167 +#: audits/models.py:54 audits/models.py:98 audits/models.py:169 #: terminal/models/session/session.py:38 terminal/models/session/sharing.py:113 msgid "Remote addr" msgstr "リモートaddr" -#: audits/models.py:57 audits/serializers.py:35 +#: audits/models.py:59 audits/serializers.py:35 msgid "Operate" msgstr "操作" -#: audits/models.py:59 +#: audits/models.py:61 msgid "Filename" msgstr "ファイル名" -#: audits/models.py:62 common/serializers/common.py:98 +#: audits/models.py:64 common/serializers/common.py:98 msgid "File" msgstr "書類" -#: audits/models.py:63 terminal/backends/command/models.py:21 +#: audits/models.py:65 terminal/backends/command/models.py:21 #: terminal/models/session/replay.py:9 terminal/models/session/sharing.py:20 #: terminal/models/session/sharing.py:95 #: terminal/templates/terminal/_msg_command_alert.html:10 @@ -2311,103 +2345,93 @@ msgstr "書類" msgid "Session" msgstr "セッション" -#: audits/models.py:66 +#: audits/models.py:68 msgid "File transfer log" msgstr "ファイル転送ログ" -#: audits/models.py:90 audits/serializers.py:86 +#: audits/models.py:92 audits/serializers.py:86 msgid "Resource Type" msgstr "リソースタイプ" -#: audits/models.py:91 audits/models.py:94 audits/models.py:140 +#: audits/models.py:93 audits/models.py:96 audits/models.py:142 #: audits/serializers.py:85 msgid "Resource" msgstr "リソース" -#: audits/models.py:97 audits/models.py:143 audits/models.py:169 +#: audits/models.py:99 audits/models.py:145 audits/models.py:171 #: terminal/serializers/command.py:75 msgid "Datetime" msgstr "時間" -#: audits/models.py:136 +#: audits/models.py:138 msgid "Activity type" msgstr "活動の種類" -#: audits/models.py:146 +#: audits/models.py:148 msgid "Detail" msgstr "詳細" -#: audits/models.py:149 +#: audits/models.py:151 msgid "Detail ID" msgstr "詳細 ID" -#: audits/models.py:153 +#: audits/models.py:155 msgid "Activity log" msgstr "活動記録" -#: audits/models.py:165 +#: audits/models.py:167 msgid "Change by" msgstr "による変更" -#: audits/models.py:175 +#: audits/models.py:177 msgid "Password change log" msgstr "パスワード変更ログ" -#: audits/models.py:182 audits/models.py:257 +#: audits/models.py:184 audits/models.py:259 msgid "Login type" msgstr "ログインタイプ" -#: audits/models.py:184 audits/models.py:253 +#: audits/models.py:186 audits/models.py:255 #: tickets/models/ticket/login_confirm.py:10 msgid "Login IP" msgstr "ログインIP" -#: audits/models.py:186 audits/models.py:255 -#: authentication/templates/authentication/_msg_different_city.html:11 -#: tickets/models/ticket/login_confirm.py:11 -msgid "Login city" -msgstr "ログイン都市" - -#: audits/models.py:189 audits/models.py:256 audits/serializers.py:65 -msgid "User agent" -msgstr "ユーザーエージェント" - -#: audits/models.py:192 audits/serializers.py:49 +#: audits/models.py:194 audits/serializers.py:49 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:821 +#: users/forms/profile.py:65 users/models/user.py:818 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" -#: audits/models.py:202 +#: audits/models.py:204 msgid "Date login" msgstr "日付ログイン" -#: audits/models.py:204 audits/models.py:258 audits/serializers.py:67 +#: audits/models.py:206 audits/models.py:260 audits/serializers.py:67 #: audits/serializers.py:183 msgid "Authentication backend" msgstr "認証バックエンド" -#: audits/models.py:248 +#: audits/models.py:250 msgid "User login log" msgstr "ユーザーログインログ" -#: audits/models.py:254 +#: audits/models.py:256 msgid "Session key" msgstr "セッションID" -#: audits/models.py:260 authentication/models/connection_token.py:47 +#: audits/models.py:262 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 #: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:839 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:836 msgid "Date expired" msgstr "期限切れの日付" -#: audits/models.py:278 +#: audits/models.py:288 msgid "User session" msgstr "ユーザーセッション" -#: audits/models.py:280 +#: audits/models.py:290 msgid "Offline ussr session" msgstr "ユーザー・セッションの下限" @@ -2435,46 +2459,46 @@ msgstr "ユーザー %s はシステム %s にログインしています" msgid "User %s perform a task for this resource: %s" msgstr "ユーザー %s が現在のリソースでタスク (%s) を実行しました" -#: audits/signal_handlers/login_log.py:34 +#: audits/signal_handlers/login_log.py:36 msgid "SSH Key" msgstr "SSHキー" -#: audits/signal_handlers/login_log.py:36 settings/serializers/auth/sso.py:13 +#: audits/signal_handlers/login_log.py:38 settings/serializers/auth/sso.py:13 msgid "SSO" msgstr "SSO" -#: audits/signal_handlers/login_log.py:37 +#: audits/signal_handlers/login_log.py:39 msgid "Auth Token" msgstr "認証トークン" -#: audits/signal_handlers/login_log.py:38 authentication/notifications.py:73 +#: audits/signal_handlers/login_log.py:40 authentication/notifications.py:73 #: authentication/views/login.py:77 authentication/views/wecom.py:159 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:751 users/models/user.py:856 +#: users/models/user.py:748 users/models/user.py:853 msgid "WeCom" msgstr "企業微信" -#: audits/signal_handlers/login_log.py:39 authentication/views/feishu.py:122 +#: audits/signal_handlers/login_log.py:41 authentication/views/feishu.py:122 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:753 -#: users/models/user.py:858 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:750 +#: users/models/user.py:855 msgid "FeiShu" msgstr "本を飛ばす" -#: audits/signal_handlers/login_log.py:40 authentication/views/dingtalk.py:159 +#: audits/signal_handlers/login_log.py:42 authentication/views/dingtalk.py:159 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:752 -#: users/models/user.py:857 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:749 +#: users/models/user.py:854 msgid "DingTalk" msgstr "DingTalk" -#: audits/signal_handlers/login_log.py:41 +#: audits/signal_handlers/login_log.py:43 #: authentication/models/temp_token.py:16 msgid "Temporary token" msgstr "仮パスワード" -#: audits/signal_handlers/login_log.py:42 authentication/views/login.py:95 +#: audits/signal_handlers/login_log.py:44 authentication/views/login.py:95 #: settings/serializers/auth/passkey.py:8 msgid "Passkey" msgstr "" @@ -2539,7 +2563,7 @@ msgstr "" "さい。" #: authentication/api/password.py:64 -#: authentication/templates/authentication/login.html:317 +#: authentication/templates/authentication/login.html:355 #: users/templates/users/forgot_password.html:27 #: users/templates/users/forgot_password.html:28 #: users/templates/users/forgot_password_previewing.html:13 @@ -3055,7 +3079,7 @@ msgstr "期限切れです" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 #: settings/serializers/msg.py:57 users/forms/profile.py:102 -#: users/forms/profile.py:109 users/models/user.py:800 +#: users/forms/profile.py:109 users/models/user.py:797 #: users/templates/users/forgot_password.html:117 #: users/views/profile/reset.py:73 msgid "Email" @@ -3093,13 +3117,13 @@ msgid "Show" msgstr "表示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:646 users/serializers/profile.py:92 +#: users/models/user.py:643 users/serializers/profile.py:92 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" msgstr "無効化" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:647 users/serializers/profile.py:93 +#: users/models/user.py:644 users/serializers/profile.py:93 #: users/templates/users/mfa_setting.html:26 #: users/templates/users/mfa_setting.html:68 msgid "Enable" @@ -3256,7 +3280,7 @@ msgstr "" msgid "Cancel" msgstr "キャンセル" -#: authentication/templates/authentication/login.html:237 +#: authentication/templates/authentication/login.html:270 msgid "" "Configuration file has problems and cannot be logged in. Please contact the " "administrator or view latest docs" @@ -3264,11 +3288,11 @@ msgstr "" "設定ファイルに問題があり、ログインできません。管理者に連絡するか、最新のド" "キュメントを参照してください。" -#: authentication/templates/authentication/login.html:238 +#: authentication/templates/authentication/login.html:271 msgid "If you are administrator, you can update the config resolve it, set" msgstr "管理者の場合は、configを更新して解決することができます。" -#: authentication/templates/authentication/login.html:332 +#: authentication/templates/authentication/login.html:370 msgid "More login options" msgstr "その他のログインオプション" @@ -3321,7 +3345,7 @@ msgstr "再試行しますか?" msgid "LAN" msgstr "ローカルエリアネットワーク" -#: authentication/views/base.py:61 +#: authentication/views/base.py:67 #: perms/templates/perms/_msg_permed_items_expire.html:21 msgid "If you have any question, please contact the administrator" msgstr "質問があったら、管理者に連絡して下さい" @@ -3477,7 +3501,7 @@ msgstr "の準備を" msgid "Pending" msgstr "未定" -#: common/const/choices.py:17 ops/const.py:59 +#: common/const/choices.py:17 ops/const.py:71 msgid "Running" msgstr "ランニング" @@ -3561,7 +3585,7 @@ msgstr "は破棄されます" msgid "discard time" msgstr "時間を捨てる" -#: common/db/models.py:33 users/models/user.py:842 +#: common/db/models.py:33 users/models/user.py:839 msgid "Updated by" msgstr "によって更新" @@ -3789,10 +3813,6 @@ msgstr "" "いる場合は、nginxリスニングポートにアクセスしていないことを証明してください。" "頑張ってください。" -#: notifications/apps.py:7 -msgid "Notifications" -msgstr "通知" - #: notifications/backends/__init__.py:13 msgid "Site message" msgstr "サイトメッセージ" @@ -3841,14 +3861,34 @@ msgstr "タスクは存在しません" msgid "Task {} args or kwargs error" msgstr "タスク実行パラメータエラー" -#: ops/api/playbook.py:37 +#: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" msgstr "現在プレイブックは1つのジョブで使用されています" -#: ops/api/playbook.py:91 +#: ops/api/playbook.py:93 msgid "Unsupported file content" msgstr "サポートされていないファイルの内容" +#: ops/api/playbook.py:95 ops/api/playbook.py:141 ops/api/playbook.py:189 +msgid "Invalid file path" +msgstr "無効なファイルパス" + +#: ops/api/playbook.py:167 +msgid "This file can not be rename" +msgstr "ファイル名を変更することはできません" + +#: ops/api/playbook.py:186 +msgid "File already exists" +msgstr "ファイルは既に存在します。" + +#: ops/api/playbook.py:204 +msgid "File key is required" +msgstr "ファイルキーこのフィールドは必須です" + +#: ops/api/playbook.py:207 +msgid "This file can not be delete" +msgstr "このファイルを削除できません" + #: ops/apps.py:9 ops/notifications.py:17 rbac/tree.py:55 msgid "App ops" msgstr "アプリ操作" @@ -3901,31 +3941,39 @@ msgstr "特権アカウントのみ" msgid "Privileged First" msgstr "特権アカウント優先" -#: ops/const.py:50 +#: ops/const.py:50 ops/const.py:60 msgid "Powershell" msgstr "PowerShell" -#: ops/const.py:51 +#: ops/const.py:51 ops/const.py:61 msgid "Python" msgstr "Python" -#: ops/const.py:52 +#: ops/const.py:52 ops/const.py:62 msgid "MySQL" msgstr "MySQL" -#: ops/const.py:53 +#: ops/const.py:53 ops/const.py:64 msgid "PostgreSQL" msgstr "PostgreSQL" -#: ops/const.py:54 +#: ops/const.py:54 ops/const.py:65 msgid "SQLServer" msgstr "SQLServer" -#: ops/const.py:55 +#: ops/const.py:55 ops/const.py:67 msgid "Raw" msgstr "" -#: ops/const.py:61 +#: ops/const.py:63 +msgid "MariaDB" +msgstr "MariaDB" + +#: ops/const.py:66 +msgid "Oracle" +msgstr "Oracle" + +#: ops/const.py:73 msgid "Timeout" msgstr "タイムアウト" @@ -3933,28 +3981,28 @@ msgstr "タイムアウト" msgid "no valid program entry found." msgstr "利用可能なプログラムポータルがありません" -#: ops/mixin.py:26 ops/mixin.py:89 settings/serializers/auth/ldap.py:73 +#: ops/mixin.py:26 ops/mixin.py:90 settings/serializers/auth/ldap.py:73 msgid "Cycle perform" msgstr "サイクル実行" -#: ops/mixin.py:30 ops/mixin.py:87 ops/mixin.py:106 +#: ops/mixin.py:30 ops/mixin.py:88 ops/mixin.py:107 #: settings/serializers/auth/ldap.py:70 msgid "Regularly perform" msgstr "定期的に実行する" -#: ops/mixin.py:109 +#: ops/mixin.py:110 msgid "Interval" msgstr "間隔" -#: ops/mixin.py:119 +#: ops/mixin.py:120 msgid "* Please enter a valid crontab expression" msgstr "* 有効なcrontab式を入力してください" -#: ops/mixin.py:126 +#: ops/mixin.py:127 msgid "Range {} to {}" msgstr "{} から {} までの範囲" -#: ops/mixin.py:137 +#: ops/mixin.py:138 msgid "Require periodic or regularly perform setting" msgstr "定期的または定期的に設定を行う必要があります" @@ -4023,7 +4071,7 @@ msgstr "終了" msgid "Date published" msgstr "発売日" -#: ops/models/celery.py:86 +#: ops/models/celery.py:87 msgid "Celery Task Execution" msgstr "Celery タスク実行" @@ -4123,23 +4171,23 @@ msgstr "時を過ごす" msgid "Run ansible task" msgstr "Ansible タスクを実行する" -#: ops/tasks.py:63 +#: ops/tasks.py:68 msgid "Run ansible task execution" msgstr "Ansible タスクの実行を開始する" -#: ops/tasks.py:85 +#: ops/tasks.py:90 msgid "Clear celery periodic tasks" msgstr "タスクログを定期的にクリアする" -#: ops/tasks.py:106 +#: ops/tasks.py:111 msgid "Create or update periodic tasks" msgstr "定期的なタスクの作成または更新" -#: ops/tasks.py:114 +#: ops/tasks.py:119 msgid "Periodic check service performance" msgstr "サービスのパフォーマンスを定期的に確認する" -#: ops/tasks.py:120 +#: ops/tasks.py:125 msgid "Clean up unexpected jobs" msgstr "例外ジョブのクリーンアップ" @@ -4425,7 +4473,7 @@ msgid "Scope" msgstr "スコープ" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:808 +#: users/models/user.py:805 msgid "Role" msgstr "ロール" @@ -7305,7 +7353,7 @@ msgstr "公開鍵は古いものと同じであってはなりません。" msgid "Not a valid ssh public key" msgstr "有効なssh公開鍵ではありません" -#: users/forms/profile.py:173 users/models/user.py:831 +#: users/forms/profile.py:173 users/models/user.py:828 #: xpack/plugins/cloud/serializers/account_attrs.py:203 msgid "Public key" msgstr "公開キー" @@ -7314,73 +7362,73 @@ msgstr "公開キー" msgid "Preference" msgstr "ユーザー設定" -#: users/models/user.py:648 users/serializers/profile.py:94 +#: users/models/user.py:645 users/serializers/profile.py:94 msgid "Force enable" msgstr "強制有効" -#: users/models/user.py:810 users/serializers/user.py:172 +#: users/models/user.py:807 users/serializers/user.py:172 msgid "Is service account" msgstr "サービスアカウントです" -#: users/models/user.py:812 +#: users/models/user.py:809 msgid "Avatar" msgstr "アバター" -#: users/models/user.py:815 +#: users/models/user.py:812 msgid "Wechat" msgstr "微信" -#: users/models/user.py:818 users/serializers/user.py:109 +#: users/models/user.py:815 users/serializers/user.py:109 msgid "Phone" msgstr "電話" -#: users/models/user.py:824 +#: users/models/user.py:821 msgid "OTP secret key" msgstr "OTP 秘密" -#: users/models/user.py:828 +#: users/models/user.py:825 #: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "Private key" msgstr "ssh秘密鍵" -#: users/models/user.py:836 users/serializers/profile.py:125 +#: users/models/user.py:833 users/serializers/profile.py:125 #: users/serializers/user.py:169 msgid "Is first login" msgstr "最初のログインです" -#: users/models/user.py:850 +#: users/models/user.py:847 msgid "Date password last updated" msgstr "最終更新日パスワード" -#: users/models/user.py:853 +#: users/models/user.py:850 msgid "Need update password" msgstr "更新パスワードが必要" -#: users/models/user.py:977 +#: users/models/user.py:974 msgid "Can not delete admin user" msgstr "管理者ユーザーを削除できませんでした" -#: users/models/user.py:1003 +#: users/models/user.py:1000 msgid "Can invite user" msgstr "ユーザーを招待できます" -#: users/models/user.py:1004 +#: users/models/user.py:1001 msgid "Can remove user" msgstr "ユーザーを削除できます" -#: users/models/user.py:1005 +#: users/models/user.py:1002 msgid "Can match user" msgstr "ユーザーに一致できます" -#: users/models/user.py:1014 +#: users/models/user.py:1011 msgid "Administrator" msgstr "管理者" -#: users/models/user.py:1017 +#: users/models/user.py:1014 msgid "Administrator is the super user of system" msgstr "管理者はシステムのスーパーユーザーです" -#: users/models/user.py:1042 +#: users/models/user.py:1039 msgid "User password history" msgstr "ユーザーパスワード履歴" @@ -7419,15 +7467,15 @@ msgstr "MFAのリセット" msgid "File name conflict resolution" msgstr "ファイル名競合ソリューション" -#: users/serializers/preference/lina.py:11 +#: users/serializers/preference/lina.py:13 msgid "New file encryption password" msgstr "新しいファイルの暗号化パスワード" -#: users/serializers/preference/lina.py:16 +#: users/serializers/preference/lina.py:18 msgid "Confirm file encryption password" msgstr "ファイルの暗号化パスワードを確認する" -#: users/serializers/preference/lina.py:24 users/serializers/profile.py:48 +#: users/serializers/preference/lina.py:31 users/serializers/profile.py:48 msgid "The newly set password is inconsistent" msgstr "新しく設定されたパスワードが一致しない" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 3f9d48c80..a173d346d 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:78fa10c674b853ebde73bbdef255beeb794a7a7b4bf5483ac1464c282aab0819 -size 131200 +oid sha256:096cdc44514bd9f43b5e0062d878625c220ed7826a57a27968db3cb97e7eb011 +size 132403 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index d6b011d9d..9bd900805 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: 2023-09-19 10:39+0800\n" +"POT-Creation-Date: 2023-09-25 16:22+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -23,9 +23,9 @@ msgstr "参数 'action' 必须是 [{}]" #: accounts/const/account.py:6 #: accounts/serializers/automations/change_secret.py:32 -#: assets/models/_user.py:24 audits/signal_handlers/login_log.py:35 +#: assets/models/_user.py:24 audits/signal_handlers/login_log.py:37 #: authentication/confirm/password.py:9 authentication/forms.py:32 -#: authentication/templates/authentication/login.html:286 +#: authentication/templates/authentication/login.html:324 #: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47 #: users/forms/profile.py:22 users/serializers/user.py:105 #: users/templates/users/_msg_user_created.html:13 @@ -70,7 +70,7 @@ msgstr "同名账号" msgid "Anonymous account" msgstr "匿名账号" -#: accounts/const/account.py:25 users/models/user.py:744 +#: accounts/const/account.py:25 users/models/user.py:741 msgid "Local" msgstr "数据库" @@ -78,7 +78,7 @@ msgstr "数据库" msgid "Collected" msgstr "收集" -#: accounts/const/account.py:27 accounts/serializers/account/account.py:27 +#: accounts/const/account.py:27 accounts/serializers/account/account.py:28 #: settings/serializers/auth/sms.py:75 msgid "Template" msgstr "模板" @@ -95,7 +95,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:155 audits/const.py:55 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:62 terminal/const.py:77 xpack/plugins/cloud/const.py:43 +#: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 msgid "Failed" msgstr "失败" @@ -216,15 +216,15 @@ msgstr "用户 %s 查看/导出 了密码" #: accounts/models/account.py:48 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:205 -#: accounts/serializers/account/account.py:250 +#: accounts/serializers/account/account.py:209 +#: accounts/serializers/account/account.py:254 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:111 #: accounts/serializers/automations/change_secret.py:131 #: acls/serializers/base.py:123 assets/models/asset/common.py:93 #: assets/models/asset/common.py:334 assets/models/cmd_filter.py:36 #: assets/serializers/domain.py:19 assets/serializers/label.py:27 -#: audits/models.py:54 authentication/models/connection_token.py:36 +#: audits/models.py:56 authentication/models/connection_token.py:36 #: perms/models/asset_permission.py:64 perms/serializers/permission.py:34 #: terminal/backends/command/models.py:17 terminal/models/session/session.py:31 #: terminal/notifications.py:155 terminal/serializers/command.py:17 @@ -236,8 +236,8 @@ msgid "Asset" msgstr "资产" #: accounts/models/account.py:52 accounts/models/template.py:15 -#: accounts/serializers/account/account.py:212 -#: accounts/serializers/account/account.py:260 +#: accounts/serializers/account/account.py:216 +#: accounts/serializers/account/account.py:264 #: accounts/serializers/account/template.py:25 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" @@ -249,8 +249,8 @@ msgstr "切换自" msgid "Version" msgstr "版本" -#: accounts/models/account.py:56 accounts/serializers/account/account.py:207 -#: users/models/user.py:846 +#: accounts/models/account.py:56 accounts/serializers/account/account.py:211 +#: users/models/user.py:843 msgid "Source" msgstr "来源" @@ -262,7 +262,7 @@ msgstr "来源 ID" #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 #: acls/serializers/base.py:124 assets/serializers/asset/common.py:125 -#: assets/serializers/gateway.py:28 audits/models.py:55 ops/models/base.py:18 +#: assets/serializers/gateway.py:28 audits/models.py:57 ops/models/base.py:18 #: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 #: terminal/backends/command/models.py:18 terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 @@ -305,7 +305,7 @@ msgid "Account backup plan" msgstr "账号备份计划" #: accounts/models/automations/backup_account.py:91 -#: assets/models/automations/base.py:115 audits/models.py:61 +#: assets/models/automations/base.py:115 audits/models.py:63 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:228 #: ops/templates/ops/celery_task_log.html:75 #: perms/models/asset_permission.py:72 terminal/models/applet/host.py:140 @@ -333,7 +333,7 @@ msgstr "账号备份快照" msgid "Trigger mode" msgstr "触发模式" -#: accounts/models/automations/backup_account.py:105 audits/models.py:195 +#: accounts/models/automations/backup_account.py:105 audits/models.py:197 #: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "原因" @@ -419,10 +419,10 @@ msgid "Date finished" msgstr "结束日期" #: accounts/models/automations/change_secret.py:44 -#: accounts/serializers/account/account.py:252 assets/const/automation.py:8 +#: accounts/serializers/account/account.py:256 assets/const/automation.py:8 #: authentication/templates/authentication/passkey.html:173 -#: authentication/views/base.py:26 authentication/views/base.py:27 -#: authentication/views/base.py:28 common/const/choices.py:20 +#: authentication/views/base.py:27 authentication/views/base.py:28 +#: authentication/views/base.py:29 common/const/choices.py:20 msgid "Error" msgstr "错误" @@ -441,13 +441,13 @@ msgstr "最后登录日期" #: accounts/models/automations/gather_account.py:17 #: accounts/models/automations/push_account.py:15 accounts/models/base.py:65 #: accounts/serializers/account/virtual.py:21 acls/serializers/base.py:19 -#: acls/serializers/base.py:50 assets/models/_user.py:23 audits/models.py:180 -#: authentication/forms.py:25 authentication/forms.py:27 -#: authentication/models/temp_token.py:9 +#: acls/serializers/base.py:50 acls/templates/acls/user_login_reminder.html:5 +#: assets/models/_user.py:23 audits/models.py:182 authentication/forms.py:25 +#: authentication/forms.py:27 authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: users/forms/profile.py:32 users/forms/profile.py:115 -#: users/models/user.py:796 users/templates/users/_msg_user_created.html:12 +#: users/models/user.py:793 users/templates/users/_msg_user_created.html:12 #: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" msgstr "用户名" @@ -475,7 +475,7 @@ msgstr "触发方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:41 #: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 -#: audits/models.py:88 audits/serializers.py:84 +#: audits/models.py:90 audits/serializers.py:84 #: authentication/serializers/connect_token_secret.py:116 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" @@ -490,7 +490,7 @@ msgid "Verify asset account" msgstr "账号验证" #: accounts/models/base.py:37 accounts/models/base.py:67 -#: accounts/serializers/account/account.py:432 +#: accounts/serializers/account/account.py:436 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:45 #: authentication/serializers/connect_token_secret.py:41 @@ -536,7 +536,7 @@ msgstr "密码规则" #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 users/forms/profile.py:33 #: users/models/group.py:13 users/models/preference.py:11 -#: users/models/user.py:798 xpack/plugins/cloud/models.py:32 +#: users/models/user.py:795 xpack/plugins/cloud/models.py:32 #: xpack/plugins/cloud/models.py:273 xpack/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名称" @@ -647,15 +647,15 @@ msgstr "" "{} - 改密任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设置加" "密密码" -#: accounts/serializers/account/account.py:30 +#: accounts/serializers/account/account.py:31 msgid "Push now" msgstr "立即推送" -#: accounts/serializers/account/account.py:37 +#: accounts/serializers/account/account.py:38 msgid "Exist policy" msgstr "账号存在策略" -#: accounts/serializers/account/account.py:185 applications/models.py:11 +#: accounts/serializers/account/account.py:189 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:89 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 #: assets/serializers/platform.py:133 assets/serializers/platform.py:229 @@ -664,7 +664,7 @@ msgstr "账号存在策略" msgid "Category" msgstr "类别" -#: accounts/serializers/account/account.py:186 +#: accounts/serializers/account/account.py:190 #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 #: acls/serializers/command_acl.py:18 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 @@ -684,26 +684,26 @@ msgstr "类别" msgid "Type" msgstr "类型" -#: accounts/serializers/account/account.py:201 +#: accounts/serializers/account/account.py:205 msgid "Asset not found" msgstr "资产不存在" -#: accounts/serializers/account/account.py:241 +#: accounts/serializers/account/account.py:245 msgid "Has secret" msgstr "已托管密码" -#: accounts/serializers/account/account.py:251 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:255 ops/models/celery.py:60 #: tickets/models/comment.py:13 tickets/models/ticket/general.py:45 #: tickets/models/ticket/general.py:279 tickets/serializers/super_ticket.py:14 #: tickets/serializers/ticket/ticket.py:21 msgid "State" msgstr "状态" -#: accounts/serializers/account/account.py:253 +#: accounts/serializers/account/account.py:257 msgid "Changed" msgstr "已修改" -#: accounts/serializers/account/account.py:263 +#: accounts/serializers/account/account.py:267 #: accounts/serializers/automations/base.py:22 acls/models/base.py:97 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 ops/models/base.py:17 @@ -712,29 +712,29 @@ msgstr "已修改" msgid "Assets" msgstr "资产" -#: accounts/serializers/account/account.py:318 +#: accounts/serializers/account/account.py:322 msgid "Account already exists" msgstr "账号已存在" -#: accounts/serializers/account/account.py:368 +#: accounts/serializers/account/account.py:372 #, python-format msgid "Asset does not support this secret type: %s" msgstr "资产不支持账号类型: %s" -#: accounts/serializers/account/account.py:400 +#: accounts/serializers/account/account.py:404 msgid "Account has exist" msgstr "账号已存在" -#: accounts/serializers/account/account.py:433 +#: accounts/serializers/account/account.py:437 #: authentication/serializers/connect_token_secret.py:156 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:31 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:440 acls/serializers/base.py:116 -#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:50 -#: audits/models.py:86 audits/models.py:164 audits/models.py:262 +#: accounts/serializers/account/account.py:447 acls/serializers/base.py:116 +#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:52 +#: audits/models.py:88 audits/models.py:166 audits/models.py:264 #: audits/serializers.py:171 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 @@ -746,12 +746,12 @@ msgstr "ID" #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:996 -#: users/models/user.py:1032 users/serializers/group.py:18 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:993 +#: users/models/user.py:1029 users/serializers/group.py:18 msgid "User" msgstr "用户" -#: accounts/serializers/account/account.py:441 +#: accounts/serializers/account/account.py:448 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:158 terminal/notifications.py:207 msgid "Date" @@ -759,7 +759,7 @@ msgstr "日期" #: accounts/serializers/account/backup.py:31 #: accounts/serializers/automations/base.py:36 -#: assets/serializers/automations/base.py:34 ops/mixin.py:23 ops/mixin.py:103 +#: assets/serializers/automations/base.py:34 ops/mixin.py:23 ops/mixin.py:104 #: settings/serializers/auth/ldap.py:66 msgid "Periodic perform" msgstr "定时执行" @@ -796,28 +796,24 @@ msgstr "" "username@domain" #: accounts/serializers/account/template.py:11 -#, fuzzy -#| msgid "Password strength" msgid "Password length" -msgstr "密码强度:" +msgstr "密码长度" #: accounts/serializers/account/template.py:12 -#, fuzzy -#| msgid "Powershell" msgid "Lowercase" -msgstr "PowerShell" +msgstr "小写字母" #: accounts/serializers/account/template.py:13 msgid "Uppercase" -msgstr "" +msgstr "大写字母" #: accounts/serializers/account/template.py:14 msgid "Digit" -msgstr "" +msgstr "数字" #: accounts/serializers/account/template.py:15 msgid "Special symbol" -msgstr "" +msgstr "特殊字符" #: accounts/serializers/account/template.py:36 msgid "Secret generation strategy for account creation" @@ -842,7 +838,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:834 +#: tickets/models/ticket/general.py:297 users/models/user.py:831 #: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "备注" @@ -892,8 +888,8 @@ msgid "Automation task execution" msgstr "自动化任务执行历史" #: accounts/serializers/automations/change_secret.py:154 audits/const.py:54 -#: audits/models.py:60 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:60 ops/serializers/celery.py:40 +#: audits/models.py:62 audits/signal_handlers/activity_log.py:33 +#: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: terminal/const.py:76 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:119 msgid "Success" @@ -983,6 +979,10 @@ msgstr "审批" msgid "Warning" msgstr "告警" +#: acls/const.py:10 notifications/apps.py:7 +msgid "Notifications" +msgstr "通知" + #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:97 #: xpack/plugins/cloud/models.py:275 @@ -1094,6 +1094,10 @@ msgstr "登录资产访问控制" msgid "Login asset confirm" msgstr "登录资产复核" +#: acls/notifications.py:10 +msgid "User login reminder" +msgstr "用户登录提醒" + #: acls/serializers/base.py:11 acls/serializers/login_acl.py:11 msgid "With * indicating a match all. " msgstr "* 表示匹配所有. " @@ -1148,6 +1152,35 @@ msgstr "IP" msgid "Time Period" msgstr "时段" +#: acls/templates/acls/user_login_reminder.html:3 +msgid "Respectful" +msgstr "尊敬的" + +#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:188 +#: audits/models.py:257 +#: authentication/templates/authentication/_msg_different_city.html:11 +#: tickets/models/ticket/login_confirm.py:11 +msgid "Login city" +msgstr "登录城市" + +#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:191 +#: audits/models.py:258 audits/serializers.py:65 +msgid "User agent" +msgstr "用户代理" + +#: acls/templates/acls/user_login_reminder.html:11 +msgid "" +"The user has just successfully logged into the system. Please ensure that " +"this is an authorized operation. If you suspect that this is an unauthorized " +"access, please take appropriate measures immediately." +msgstr "" +"用户刚刚成功登录到系统。请确保这是授权的操作。如果您怀疑这是一个未经授权的访" +"问,请立即采取适当的措施。" + +#: acls/templates/acls/user_login_reminder.html:13 +msgid "Thank you" +msgstr "谢谢" + #: applications/apps.py:9 msgid "Applications" msgstr "应用管理" @@ -1254,7 +1287,7 @@ msgstr "禁用" #: assets/const/base.py:34 settings/serializers/basic.py:6 #: users/serializers/preference/koko.py:15 -#: users/serializers/preference/lina.py:32 +#: users/serializers/preference/lina.py:39 #: users/serializers/preference/luna.py:60 msgid "Basic" msgstr "基本" @@ -1467,19 +1500,19 @@ msgstr "SSH公钥" # msgstr "备注" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 -#: audits/models.py:259 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:227 users/models/user.py:1033 +#: audits/models.py:261 common/db/models.py:34 ops/models/base.py:54 +#: ops/models/job.py:227 users/models/user.py:1030 msgid "Date created" msgstr "创建日期" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:855 +#: common/db/models.py:35 users/models/user.py:852 msgid "Date updated" msgstr "更新日期" #: assets/models/_user.py:30 assets/models/cmd_filter.py:44 #: assets/models/cmd_filter.py:91 assets/models/group.py:18 -#: common/db/models.py:32 users/models/user.py:841 +#: common/db/models.py:32 users/models/user.py:838 #: users/serializers/group.py:29 msgid "Created by" msgstr "创建者" @@ -1515,7 +1548,7 @@ msgstr "协议" msgid "Sudo" msgstr "Sudo" -#: assets/models/_user.py:55 ops/const.py:49 +#: assets/models/_user.py:55 ops/const.py:49 ops/const.py:59 msgid "Shell" msgstr "Shell" @@ -1644,7 +1677,7 @@ msgstr "自动化任务" msgid "Asset automation task" msgstr "资产自动化任务" -#: assets/models/automations/base.py:113 audits/models.py:200 +#: assets/models/automations/base.py:113 audits/models.py:202 #: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220 #: terminal/models/applet/applet.py:301 terminal/models/applet/host.py:139 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 @@ -1673,7 +1706,7 @@ msgstr "校验日期" #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:61 #: perms/serializers/permission.py:32 users/models/group.py:25 -#: users/models/user.py:804 +#: users/models/user.py:801 msgid "User group" msgstr "用户组" @@ -1723,11 +1756,11 @@ msgstr "默认" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1018 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1015 msgid "System" msgstr "系统" -#: assets/models/label.py:19 assets/models/node.py:544 +#: assets/models/label.py:19 assets/models/node.py:539 #: assets/serializers/cagegory.py:7 assets/serializers/cagegory.py:14 #: authentication/models/connection_token.py:29 #: authentication/serializers/connect_token_secret.py:122 @@ -1749,28 +1782,28 @@ msgstr "标签" msgid "New node" msgstr "新节点" -#: assets/models/node.py:472 audits/backends/db.py:55 audits/backends/db.py:56 +#: assets/models/node.py:467 audits/backends/db.py:55 audits/backends/db.py:56 msgid "empty" msgstr "空" -#: assets/models/node.py:543 perms/models/perm_node.py:28 +#: assets/models/node.py:538 perms/models/perm_node.py:28 msgid "Key" msgstr "键" -#: assets/models/node.py:545 assets/serializers/node.py:20 +#: assets/models/node.py:540 assets/serializers/node.py:20 msgid "Full value" msgstr "全称" -#: assets/models/node.py:549 perms/models/perm_node.py:30 +#: assets/models/node.py:544 perms/models/perm_node.py:30 msgid "Parent key" msgstr "ssh私钥" -#: assets/models/node.py:558 perms/serializers/permission.py:35 +#: assets/models/node.py:553 perms/serializers/permission.py:35 #: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 msgid "Node" msgstr "节点" -#: assets/models/node.py:561 +#: assets/models/node.py:556 msgid "Can match node" msgstr "可以匹配节点" @@ -2221,8 +2254,8 @@ msgstr "创建" msgid "Connect" msgstr "连接" -#: audits/const.py:30 authentication/templates/authentication/login.html:252 -#: authentication/templates/authentication/login.html:325 +#: audits/const.py:30 authentication/templates/authentication/login.html:290 +#: authentication/templates/authentication/login.html:363 #: templates/_header_bar.html:95 msgid "Login" msgstr "登录" @@ -2237,7 +2270,7 @@ msgstr "改密" msgid "Terminal" msgstr "终端" -#: audits/const.py:41 audits/models.py:128 +#: audits/const.py:41 audits/models.py:130 msgid "Operate log" msgstr "操作日志" @@ -2266,28 +2299,28 @@ msgstr "是" msgid "No" msgstr "否" -#: audits/models.py:43 +#: audits/models.py:45 msgid "Job audit log" msgstr "作业审计日志" -#: audits/models.py:52 audits/models.py:96 audits/models.py:167 +#: audits/models.py:54 audits/models.py:98 audits/models.py:169 #: terminal/models/session/session.py:38 terminal/models/session/sharing.py:113 msgid "Remote addr" msgstr "远端地址" -#: audits/models.py:57 audits/serializers.py:35 +#: audits/models.py:59 audits/serializers.py:35 msgid "Operate" msgstr "操作" -#: audits/models.py:59 +#: audits/models.py:61 msgid "Filename" msgstr "文件名" -#: audits/models.py:62 common/serializers/common.py:98 +#: audits/models.py:64 common/serializers/common.py:98 msgid "File" msgstr "文件" -#: audits/models.py:63 terminal/backends/command/models.py:21 +#: audits/models.py:65 terminal/backends/command/models.py:21 #: terminal/models/session/replay.py:9 terminal/models/session/sharing.py:20 #: terminal/models/session/sharing.py:95 #: terminal/templates/terminal/_msg_command_alert.html:10 @@ -2296,103 +2329,93 @@ msgstr "文件" msgid "Session" msgstr "会话" -#: audits/models.py:66 +#: audits/models.py:68 msgid "File transfer log" msgstr "文件管理" -#: audits/models.py:90 audits/serializers.py:86 +#: audits/models.py:92 audits/serializers.py:86 msgid "Resource Type" msgstr "资源类型" -#: audits/models.py:91 audits/models.py:94 audits/models.py:140 +#: audits/models.py:93 audits/models.py:96 audits/models.py:142 #: audits/serializers.py:85 msgid "Resource" msgstr "资源" -#: audits/models.py:97 audits/models.py:143 audits/models.py:169 +#: audits/models.py:99 audits/models.py:145 audits/models.py:171 #: terminal/serializers/command.py:75 msgid "Datetime" msgstr "日期" -#: audits/models.py:136 +#: audits/models.py:138 msgid "Activity type" msgstr "活动类型" -#: audits/models.py:146 +#: audits/models.py:148 msgid "Detail" msgstr "详情" -#: audits/models.py:149 +#: audits/models.py:151 msgid "Detail ID" msgstr "详情 ID" -#: audits/models.py:153 +#: audits/models.py:155 msgid "Activity log" msgstr "活动日志" -#: audits/models.py:165 +#: audits/models.py:167 msgid "Change by" msgstr "修改者" -#: audits/models.py:175 +#: audits/models.py:177 msgid "Password change log" msgstr "改密日志" -#: audits/models.py:182 audits/models.py:257 +#: audits/models.py:184 audits/models.py:259 msgid "Login type" msgstr "登录方式" -#: audits/models.py:184 audits/models.py:253 +#: audits/models.py:186 audits/models.py:255 #: tickets/models/ticket/login_confirm.py:10 msgid "Login IP" msgstr "登录 IP" -#: audits/models.py:186 audits/models.py:255 -#: authentication/templates/authentication/_msg_different_city.html:11 -#: tickets/models/ticket/login_confirm.py:11 -msgid "Login city" -msgstr "登录城市" - -#: audits/models.py:189 audits/models.py:256 audits/serializers.py:65 -msgid "User agent" -msgstr "用户代理" - -#: audits/models.py:192 audits/serializers.py:49 +#: audits/models.py:194 audits/serializers.py:49 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:821 +#: users/forms/profile.py:65 users/models/user.py:818 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" -#: audits/models.py:202 +#: audits/models.py:204 msgid "Date login" msgstr "登录日期" -#: audits/models.py:204 audits/models.py:258 audits/serializers.py:67 +#: audits/models.py:206 audits/models.py:260 audits/serializers.py:67 #: audits/serializers.py:183 msgid "Authentication backend" msgstr "认证方式" -#: audits/models.py:248 +#: audits/models.py:250 msgid "User login log" msgstr "用户登录日志" -#: audits/models.py:254 +#: audits/models.py:256 msgid "Session key" msgstr "会话标识" -#: audits/models.py:260 authentication/models/connection_token.py:47 +#: audits/models.py:262 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 #: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:839 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:836 msgid "Date expired" msgstr "失效日期" -#: audits/models.py:278 +#: audits/models.py:288 msgid "User session" msgstr "用户会话" -#: audits/models.py:280 +#: audits/models.py:290 msgid "Offline ussr session" msgstr "下限用户会话" @@ -2420,46 +2443,46 @@ msgstr "用户 %s 登录系统 %s" msgid "User %s perform a task for this resource: %s" msgstr "用户 %s 在当前资源, 执行了任务 (%s)" -#: audits/signal_handlers/login_log.py:34 +#: audits/signal_handlers/login_log.py:36 msgid "SSH Key" msgstr "SSH 密钥" -#: audits/signal_handlers/login_log.py:36 settings/serializers/auth/sso.py:13 +#: audits/signal_handlers/login_log.py:38 settings/serializers/auth/sso.py:13 msgid "SSO" msgstr "SSO" -#: audits/signal_handlers/login_log.py:37 +#: audits/signal_handlers/login_log.py:39 msgid "Auth Token" msgstr "认证令牌" -#: audits/signal_handlers/login_log.py:38 authentication/notifications.py:73 +#: audits/signal_handlers/login_log.py:40 authentication/notifications.py:73 #: authentication/views/login.py:77 authentication/views/wecom.py:159 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:751 users/models/user.py:856 +#: users/models/user.py:748 users/models/user.py:853 msgid "WeCom" msgstr "企业微信" -#: audits/signal_handlers/login_log.py:39 authentication/views/feishu.py:122 +#: audits/signal_handlers/login_log.py:41 authentication/views/feishu.py:122 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:753 -#: users/models/user.py:858 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:750 +#: users/models/user.py:855 msgid "FeiShu" msgstr "飞书" -#: audits/signal_handlers/login_log.py:40 authentication/views/dingtalk.py:159 +#: audits/signal_handlers/login_log.py:42 authentication/views/dingtalk.py:159 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:752 -#: users/models/user.py:857 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:749 +#: users/models/user.py:854 msgid "DingTalk" msgstr "钉钉" -#: audits/signal_handlers/login_log.py:41 +#: audits/signal_handlers/login_log.py:43 #: authentication/models/temp_token.py:16 msgid "Temporary token" msgstr "临时密码" -#: audits/signal_handlers/login_log.py:42 authentication/views/login.py:95 +#: audits/signal_handlers/login_log.py:44 authentication/views/login.py:95 #: settings/serializers/auth/passkey.py:8 msgid "Passkey" msgstr "" @@ -2520,7 +2543,7 @@ msgid "" msgstr "用户来自 {} 请去相应系统修改密码" #: authentication/api/password.py:64 -#: authentication/templates/authentication/login.html:317 +#: authentication/templates/authentication/login.html:355 #: users/templates/users/forgot_password.html:27 #: users/templates/users/forgot_password.html:28 #: users/templates/users/forgot_password_previewing.html:13 @@ -3024,7 +3047,7 @@ msgstr "已过期" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 #: settings/serializers/msg.py:57 users/forms/profile.py:102 -#: users/forms/profile.py:109 users/models/user.py:800 +#: users/forms/profile.py:109 users/models/user.py:797 #: users/templates/users/forgot_password.html:117 #: users/views/profile/reset.py:73 msgid "Email" @@ -3062,13 +3085,13 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:646 users/serializers/profile.py:92 +#: users/models/user.py:643 users/serializers/profile.py:92 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:647 users/serializers/profile.py:93 +#: users/models/user.py:644 users/serializers/profile.py:93 #: users/templates/users/mfa_setting.html:26 #: users/templates/users/mfa_setting.html:68 msgid "Enable" @@ -3217,17 +3240,17 @@ msgstr "如果这次公钥更新不是由你发起的,那么你的账号可能 msgid "Cancel" msgstr "取消" -#: authentication/templates/authentication/login.html:237 +#: authentication/templates/authentication/login.html:270 msgid "" "Configuration file has problems and cannot be logged in. Please contact the " "administrator or view latest docs" msgstr "配置文件有问题,无法登录,请联系管理员或查看最新文档" -#: authentication/templates/authentication/login.html:238 +#: authentication/templates/authentication/login.html:271 msgid "If you are administrator, you can update the config resolve it, set" msgstr "如果你是管理员,可以更新配置文件解决,设置配置项" -#: authentication/templates/authentication/login.html:332 +#: authentication/templates/authentication/login.html:370 msgid "More login options" msgstr "其他方式登录" @@ -3278,7 +3301,7 @@ msgstr "是否重试 ?" msgid "LAN" msgstr "局域网" -#: authentication/views/base.py:61 +#: authentication/views/base.py:67 #: perms/templates/perms/_msg_permed_items_expire.html:21 msgid "If you have any question, please contact the administrator" msgstr "如果有疑问或需求,请联系系统管理员" @@ -3434,7 +3457,7 @@ msgstr "准备" msgid "Pending" msgstr "待定的" -#: common/const/choices.py:17 ops/const.py:59 +#: common/const/choices.py:17 ops/const.py:71 msgid "Running" msgstr "运行中" @@ -3518,7 +3541,7 @@ msgstr "忽略的" msgid "discard time" msgstr "忽略时间" -#: common/db/models.py:33 users/models/user.py:842 +#: common/db/models.py:33 users/models/user.py:839 msgid "Updated by" msgstr "最后更新者" @@ -3739,10 +3762,6 @@ msgstr "" "div>
如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运" -#: notifications/apps.py:7 -msgid "Notifications" -msgstr "通知" - #: notifications/backends/__init__.py:13 msgid "Site message" msgstr "站内信" @@ -3791,14 +3810,34 @@ msgstr "任务 {} 不存在" msgid "Task {} args or kwargs error" msgstr "任务 {} 执行参数错误" -#: ops/api/playbook.py:37 +#: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" msgstr "当前 playbook 正在作业中使用" -#: ops/api/playbook.py:91 +#: ops/api/playbook.py:93 msgid "Unsupported file content" msgstr "不支持的文件内容" +#: ops/api/playbook.py:95 ops/api/playbook.py:141 ops/api/playbook.py:189 +msgid "Invalid file path" +msgstr "无效的文件路径" + +#: ops/api/playbook.py:167 +msgid "This file can not be rename" +msgstr "该文件不能重命名" + +#: ops/api/playbook.py:186 +msgid "File already exists" +msgstr "文件已存在" + +#: ops/api/playbook.py:204 +msgid "File key is required" +msgstr "文件密钥该字段是必填项。" + +#: ops/api/playbook.py:207 +msgid "This file can not be delete" +msgstr "无法删除此文件" + #: ops/apps.py:9 ops/notifications.py:17 rbac/tree.py:55 msgid "App ops" msgstr "作业中心" @@ -3851,31 +3890,39 @@ msgstr "仅限特权账号" msgid "Privileged First" msgstr "特权账号优先" -#: ops/const.py:50 +#: ops/const.py:50 ops/const.py:60 msgid "Powershell" msgstr "PowerShell" -#: ops/const.py:51 +#: ops/const.py:51 ops/const.py:61 msgid "Python" msgstr "Python" -#: ops/const.py:52 +#: ops/const.py:52 ops/const.py:62 msgid "MySQL" msgstr "MySQL" -#: ops/const.py:53 +#: ops/const.py:53 ops/const.py:64 msgid "PostgreSQL" msgstr "PostgreSQL" -#: ops/const.py:54 +#: ops/const.py:54 ops/const.py:65 msgid "SQLServer" msgstr "SQLServer" -#: ops/const.py:55 +#: ops/const.py:55 ops/const.py:67 msgid "Raw" -msgstr "" +msgstr "Raw" -#: ops/const.py:61 +#: ops/const.py:63 +msgid "MariaDB" +msgstr "MariaDB" + +#: ops/const.py:66 +msgid "Oracle" +msgstr "Oracle" + +#: ops/const.py:73 msgid "Timeout" msgstr "超时" @@ -3883,28 +3930,28 @@ msgstr "超时" msgid "no valid program entry found." msgstr "没有可用程序入口" -#: ops/mixin.py:26 ops/mixin.py:89 settings/serializers/auth/ldap.py:73 +#: ops/mixin.py:26 ops/mixin.py:90 settings/serializers/auth/ldap.py:73 msgid "Cycle perform" msgstr "周期执行" -#: ops/mixin.py:30 ops/mixin.py:87 ops/mixin.py:106 +#: ops/mixin.py:30 ops/mixin.py:88 ops/mixin.py:107 #: settings/serializers/auth/ldap.py:70 msgid "Regularly perform" msgstr "定期执行" -#: ops/mixin.py:109 +#: ops/mixin.py:110 msgid "Interval" msgstr "间隔" -#: ops/mixin.py:119 +#: ops/mixin.py:120 msgid "* Please enter a valid crontab expression" msgstr "* 请输入有效的 crontab 表达式" -#: ops/mixin.py:126 +#: ops/mixin.py:127 msgid "Range {} to {}" msgstr "输入在 {} - {} 范围之间" -#: ops/mixin.py:137 +#: ops/mixin.py:138 msgid "Require periodic or regularly perform setting" msgstr "需要周期或定期设置" @@ -3973,7 +4020,7 @@ msgstr "结束" msgid "Date published" msgstr "发布日期" -#: ops/models/celery.py:86 +#: ops/models/celery.py:87 msgid "Celery Task Execution" msgstr "Celery 任务执行" @@ -4073,23 +4120,23 @@ msgstr "花费时间" msgid "Run ansible task" msgstr "运行 Ansible 任务" -#: ops/tasks.py:63 +#: ops/tasks.py:68 msgid "Run ansible task execution" msgstr "开始执行 Ansible 任务" -#: ops/tasks.py:85 +#: ops/tasks.py:90 msgid "Clear celery periodic tasks" msgstr "清理周期任务" -#: ops/tasks.py:106 +#: ops/tasks.py:111 msgid "Create or update periodic tasks" msgstr "创建或更新周期任务" -#: ops/tasks.py:114 +#: ops/tasks.py:119 msgid "Periodic check service performance" msgstr "周期检测服务性能" -#: ops/tasks.py:120 +#: ops/tasks.py:125 msgid "Clean up unexpected jobs" msgstr "清理异常作业" @@ -4374,7 +4421,7 @@ msgid "Scope" msgstr "范围" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:808 +#: users/models/user.py:805 msgid "Role" msgstr "角色" @@ -7203,7 +7250,7 @@ msgstr "不能和原来的密钥相同" msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/profile.py:173 users/models/user.py:831 +#: users/forms/profile.py:173 users/models/user.py:828 #: xpack/plugins/cloud/serializers/account_attrs.py:203 msgid "Public key" msgstr "SSH公钥" @@ -7212,73 +7259,73 @@ msgstr "SSH公钥" msgid "Preference" msgstr "用户设置" -#: users/models/user.py:648 users/serializers/profile.py:94 +#: users/models/user.py:645 users/serializers/profile.py:94 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:810 users/serializers/user.py:172 +#: users/models/user.py:807 users/serializers/user.py:172 msgid "Is service account" msgstr "服务账号" -#: users/models/user.py:812 +#: users/models/user.py:809 msgid "Avatar" msgstr "头像" -#: users/models/user.py:815 +#: users/models/user.py:812 msgid "Wechat" msgstr "微信" -#: users/models/user.py:818 users/serializers/user.py:109 +#: users/models/user.py:815 users/serializers/user.py:109 msgid "Phone" msgstr "手机" -#: users/models/user.py:824 +#: users/models/user.py:821 msgid "OTP secret key" msgstr "OTP 密钥" -#: users/models/user.py:828 +#: users/models/user.py:825 #: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "Private key" msgstr "ssh私钥" -#: users/models/user.py:836 users/serializers/profile.py:125 +#: users/models/user.py:833 users/serializers/profile.py:125 #: users/serializers/user.py:169 msgid "Is first login" msgstr "首次登录" -#: users/models/user.py:850 +#: users/models/user.py:847 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:853 +#: users/models/user.py:850 msgid "Need update password" msgstr "需要更新密码" -#: users/models/user.py:977 +#: users/models/user.py:974 msgid "Can not delete admin user" msgstr "无法删除管理员用户" -#: users/models/user.py:1003 +#: users/models/user.py:1000 msgid "Can invite user" msgstr "可以邀请用户" -#: users/models/user.py:1004 +#: users/models/user.py:1001 msgid "Can remove user" msgstr "可以移除用户" -#: users/models/user.py:1005 +#: users/models/user.py:1002 msgid "Can match user" msgstr "可以匹配用户" -#: users/models/user.py:1014 +#: users/models/user.py:1011 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:1017 +#: users/models/user.py:1014 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/models/user.py:1042 +#: users/models/user.py:1039 msgid "User password history" msgstr "用户密码历史" @@ -7317,15 +7364,15 @@ msgstr "重置 MFA" msgid "File name conflict resolution" msgstr "文件名冲突解决方案" -#: users/serializers/preference/lina.py:11 +#: users/serializers/preference/lina.py:13 msgid "New file encryption password" msgstr "文件加密密码" -#: users/serializers/preference/lina.py:16 +#: users/serializers/preference/lina.py:18 msgid "Confirm file encryption password" msgstr "确认文件加密密码" -#: users/serializers/preference/lina.py:24 users/serializers/profile.py:48 +#: users/serializers/preference/lina.py:31 users/serializers/profile.py:48 msgid "The newly set password is inconsistent" msgstr "两次密码不一致" diff --git a/apps/tickets/handlers/base.py b/apps/tickets/handlers/base.py index fbf6a8e46..c1378da89 100644 --- a/apps/tickets/handlers/base.py +++ b/apps/tickets/handlers/base.py @@ -98,7 +98,7 @@ class BaseHandler: context = self._diff_prev_approve_context(state) context.update({'approve_info': approve_info}) body = self.safe_html_script( - render_to_string('tickets/ticket_approve_diff.html', context) + render_to_string('tickets/user_login_reminder.html', context) ) data = { 'body': body, From d64e77db30281af06f81fdc451446d34d857af02 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:37:00 +0800 Subject: [PATCH 009/114] =?UTF-8?q?perf:=20=E5=8E=BB=E6=8E=89print=20(#116?= =?UTF-8?q?87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/acls/notifications.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/acls/notifications.py b/apps/acls/notifications.py index 57414c65d..ec4d99cd1 100644 --- a/apps/acls/notifications.py +++ b/apps/acls/notifications.py @@ -25,7 +25,6 @@ class UserLoginReminderMsg(UserMessage): } message = render_to_string('acls/user_login_reminder.html', context) - print('message', message) return { 'subject': str(self.subject), 'message': message From 42bdb2cf140d39261a0ef5faa9562844473bac0a Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Mon, 25 Sep 2023 15:54:15 +0800 Subject: [PATCH 010/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=89=BE?= =?UTF-8?q?=E5=9B=9E=E5=AF=86=E7=A0=81=E6=97=B6=E5=8C=BA=E5=8F=B7=E5=B8=A6?= =?UTF-8?q?=E5=8A=A0=E5=8F=B7=E6=97=A0=E6=B3=95=E5=8C=B9=E9=85=8D=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/views/profile/reset.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/users/views/profile/reset.py b/apps/users/views/profile/reset.py index b8e18a647..cde1463ea 100644 --- a/apps/users/views/profile/reset.py +++ b/apps/users/views/profile/reset.py @@ -114,6 +114,11 @@ class UserForgotPasswordView(FormView): target = form.cleaned_data[form_type] code = form.cleaned_data['code'] + query_key = form_type + if form_type == 'sms': + query_key = 'phone' + target = target.lstrip('+') + try: sender_util = SendAndVerifyCodeUtil(target, backend=form_type) sender_util.verify(code) @@ -121,7 +126,6 @@ class UserForgotPasswordView(FormView): form.add_error('code', str(e)) return super().form_invalid(form) - query_key = 'phone' if form_type == 'sms' else form_type user = get_object_or_none(User, **{'username': username, query_key: target}) if not user: form.add_error('code', _('No user matched')) From 10363dcc5bfcfacd1e7e70dddfaae5245a65a5c2 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 25 Sep 2023 21:39:16 +0800 Subject: [PATCH 011/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=94=A8?= =?UTF-8?q?=E6=88=B7username=20=E4=B8=AD=E6=96=87=20=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98=20(#11692)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/authentication/views/login.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index a222aa4d8..6514e14eb 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -310,12 +310,6 @@ class UserLoginGuardView(mixins.AuthMixin, RedirectView): age = self.request.session.get_expiry_age() self.request.session.set_expiry(age) - def get(self, request, *args, **kwargs): - response = super().get(request, *args, **kwargs) - if request.user.is_authenticated: - response.set_cookie('jms_username', request.user.username) - return response - def get_redirect_url(self, *args, **kwargs): try: user = self.get_user_from_session() From c369b5478c9ba912bad71576a6920249766e0b83 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 25 Sep 2023 21:49:47 +0800 Subject: [PATCH 012/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9A=B4?= =?UTF-8?q?=E5=8A=9B=E6=A0=A1=E9=AA=8C=E9=AA=8C=E8=AF=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/utils/verify_code.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/common/utils/verify_code.py b/apps/common/utils/verify_code.py index b21e43b26..ab191af15 100644 --- a/apps/common/utils/verify_code.py +++ b/apps/common/utils/verify_code.py @@ -26,6 +26,7 @@ class SendAndVerifyCodeUtil(object): self.target = target self.backend = backend self.key = key or self.KEY_TMPL.format(target) + self.verify_key = self.key + '_verify' self.timeout = settings.VERIFY_CODE_TTL if timeout is None else timeout self.other_args = kwargs @@ -47,6 +48,11 @@ class SendAndVerifyCodeUtil(object): raise def verify(self, code): + times = cache.get(self.verify_key, 0) + if times >= 3: + self.__clear() + raise CodeError + cache.set(self.verify_key, times + 1, timeout=self.timeout) right = cache.get(self.key) if not right: raise CodeExpired From 1f00c00183fa70195061e8d4a82769388ac6823a Mon Sep 17 00:00:00 2001 From: Bai Date: Mon, 25 Sep 2023 22:58:43 +0800 Subject: [PATCH 013/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E6=A0=A1=E9=AA=8C=E9=80=BB=E8=BE=91=E5=92=8C?= =?UTF-8?q?=E6=8A=A5=E9=94=99=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/utils/verify_code.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/common/utils/verify_code.py b/apps/common/utils/verify_code.py index ab191af15..120618704 100644 --- a/apps/common/utils/verify_code.py +++ b/apps/common/utils/verify_code.py @@ -51,7 +51,7 @@ class SendAndVerifyCodeUtil(object): times = cache.get(self.verify_key, 0) if times >= 3: self.__clear() - raise CodeError + raise CodeExpired cache.set(self.verify_key, times + 1, timeout=self.timeout) right = cache.get(self.key) if not right: @@ -65,6 +65,7 @@ class SendAndVerifyCodeUtil(object): def __clear(self): cache.delete(self.key) + cache.delete(self.verify_key) def __ttl(self): return cache.ttl(self.key) From 9bde2ff6e1d8fe58ba070db61f098cafced515e9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 25 Sep 2023 22:58:12 +0800 Subject: [PATCH 014/114] fix: pubkey auth require svc sign --- apps/authentication/mixins.py | 6 ++++++ apps/common/permissions.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 9667e85c7..a9b7a1838 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -424,6 +424,7 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost key_prefix_captcha = "_LOGIN_INVALID_{}" def _check_auth_user_is_valid(self, username, password, public_key): + from common.permissions import ServiceAccountSignaturePermission user = authenticate( self.request, username=username, password=password, public_key=public_key @@ -431,6 +432,11 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost if not user: self.raise_credential_error(errors.reason_password_failed) + if public_key: + permission = ServiceAccountSignaturePermission() + if not permission.has_permission(self.request, self): + self.raise_credential_error(errors.reason_password_failed) + self.request.session['auth_backend'] = getattr(user, 'backend', settings.AUTH_BACKEND_MODEL) if user.is_expired: diff --git a/apps/common/permissions.py b/apps/common/permissions.py index 5c58de68e..37c51de47 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -86,3 +86,38 @@ class UserConfirmation(permissions.BasePermission): min_level = ConfirmType.values.index(confirm_type) + 1 name = 'UserConfirmationLevel{}TTL{}'.format(min_level, ttl) return type(name, (cls,), {'min_level': min_level, 'ttl': ttl, 'confirm_type': confirm_type}) + + +class ServiceAccountSignaturePermission(permissions.BasePermission): + def has_permission(self, request, view): + from authentication.models import AccessKey + from common.utils.crypto import get_aes_crypto + signature = request.META.get('HTTP_X_JMS_SVC', '') + if not signature or not signature.startswith('Sign'): + return False + data = signature[4:].strip() + if not data or ':' not in data: + return False + ak_id, time_sign = data.split(':', 1) + if not ak_id or not time_sign: + return False + ak = AccessKey.objects.filter(id=ak_id).first() + if not ak or not ak.is_active: + return False + if not ak.user or not ak.user.is_active or not ak.user.is_service_account: + return False + aes = get_aes_crypto(str(ak.secret).replace('-', ''), mode='ECB') + try: + timestamp = aes.decrypt(time_sign) + if not timestamp or not timestamp.isdigit(): + return False + timestamp = int(timestamp) + interval = abs(int(time.time()) - timestamp) + if interval > 30: + return False + return True + except Exception: + return False + + def has_object_permission(self, request, view, obj): + return False From 7c67d882aa884da6ea268427e5999db2560aa296 Mon Sep 17 00:00:00 2001 From: Bryan Date: Mon, 25 Sep 2023 23:18:50 +0800 Subject: [PATCH 015/114] Revert "fix: pubkey auth require svc sign" This reverts commit 9bde2ff6e1d8fe58ba070db61f098cafced515e9. --- apps/authentication/mixins.py | 6 ------ apps/common/permissions.py | 35 ----------------------------------- 2 files changed, 41 deletions(-) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index a9b7a1838..9667e85c7 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -424,7 +424,6 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost key_prefix_captcha = "_LOGIN_INVALID_{}" def _check_auth_user_is_valid(self, username, password, public_key): - from common.permissions import ServiceAccountSignaturePermission user = authenticate( self.request, username=username, password=password, public_key=public_key @@ -432,11 +431,6 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost if not user: self.raise_credential_error(errors.reason_password_failed) - if public_key: - permission = ServiceAccountSignaturePermission() - if not permission.has_permission(self.request, self): - self.raise_credential_error(errors.reason_password_failed) - self.request.session['auth_backend'] = getattr(user, 'backend', settings.AUTH_BACKEND_MODEL) if user.is_expired: diff --git a/apps/common/permissions.py b/apps/common/permissions.py index 37c51de47..5c58de68e 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -86,38 +86,3 @@ class UserConfirmation(permissions.BasePermission): min_level = ConfirmType.values.index(confirm_type) + 1 name = 'UserConfirmationLevel{}TTL{}'.format(min_level, ttl) return type(name, (cls,), {'min_level': min_level, 'ttl': ttl, 'confirm_type': confirm_type}) - - -class ServiceAccountSignaturePermission(permissions.BasePermission): - def has_permission(self, request, view): - from authentication.models import AccessKey - from common.utils.crypto import get_aes_crypto - signature = request.META.get('HTTP_X_JMS_SVC', '') - if not signature or not signature.startswith('Sign'): - return False - data = signature[4:].strip() - if not data or ':' not in data: - return False - ak_id, time_sign = data.split(':', 1) - if not ak_id or not time_sign: - return False - ak = AccessKey.objects.filter(id=ak_id).first() - if not ak or not ak.is_active: - return False - if not ak.user or not ak.user.is_active or not ak.user.is_service_account: - return False - aes = get_aes_crypto(str(ak.secret).replace('-', ''), mode='ECB') - try: - timestamp = aes.decrypt(time_sign) - if not timestamp or not timestamp.isdigit(): - return False - timestamp = int(timestamp) - interval = abs(int(time.time()) - timestamp) - if interval > 30: - return False - return True - except Exception: - return False - - def has_object_permission(self, request, view, obj): - return False From cbe3d66b397212db24f0f94d613b5665157b6c9e Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 25 Sep 2023 22:58:12 +0800 Subject: [PATCH 016/114] fix: pubkey auth require svc sign --- apps/authentication/backends/pubkey.py | 9 +++++-- apps/common/permissions.py | 35 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/apps/authentication/backends/pubkey.py b/apps/authentication/backends/pubkey.py index 1494d6b2e..bb9f91072 100644 --- a/apps/authentication/backends/pubkey.py +++ b/apps/authentication/backends/pubkey.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- # -from django.contrib.auth import get_user_model from django.conf import settings +from django.contrib.auth import get_user_model +from common.permissions import ServiceAccountSignaturePermission from .base import JMSBaseAuthBackend UserModel = get_user_model() @@ -18,6 +19,10 @@ class PublicKeyAuthBackend(JMSBaseAuthBackend): def authenticate(self, request, username=None, public_key=None, **kwargs): if not public_key: return None + + permission = ServiceAccountSignaturePermission() + if not permission.has_permission(request, None): + return None if username is None: username = kwargs.get(UserModel.USERNAME_FIELD) try: @@ -26,7 +31,7 @@ class PublicKeyAuthBackend(JMSBaseAuthBackend): return None else: if user.check_public_key(public_key) and \ - self.user_can_authenticate(user): + self.user_can_authenticate(user): return user def get_user(self, user_id): diff --git a/apps/common/permissions.py b/apps/common/permissions.py index 5c58de68e..37c51de47 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -86,3 +86,38 @@ class UserConfirmation(permissions.BasePermission): min_level = ConfirmType.values.index(confirm_type) + 1 name = 'UserConfirmationLevel{}TTL{}'.format(min_level, ttl) return type(name, (cls,), {'min_level': min_level, 'ttl': ttl, 'confirm_type': confirm_type}) + + +class ServiceAccountSignaturePermission(permissions.BasePermission): + def has_permission(self, request, view): + from authentication.models import AccessKey + from common.utils.crypto import get_aes_crypto + signature = request.META.get('HTTP_X_JMS_SVC', '') + if not signature or not signature.startswith('Sign'): + return False + data = signature[4:].strip() + if not data or ':' not in data: + return False + ak_id, time_sign = data.split(':', 1) + if not ak_id or not time_sign: + return False + ak = AccessKey.objects.filter(id=ak_id).first() + if not ak or not ak.is_active: + return False + if not ak.user or not ak.user.is_active or not ak.user.is_service_account: + return False + aes = get_aes_crypto(str(ak.secret).replace('-', ''), mode='ECB') + try: + timestamp = aes.decrypt(time_sign) + if not timestamp or not timestamp.isdigit(): + return False + timestamp = int(timestamp) + interval = abs(int(time.time()) - timestamp) + if interval > 30: + return False + return True + except Exception: + return False + + def has_object_permission(self, request, view, obj): + return False From 56a26481a4ca086fe8be58d6de06dabbfbb011aa Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 26 Sep 2023 12:06:54 +0800 Subject: [PATCH 017/114] =?UTF-8?q?perf:=20=E8=B4=A6=E5=8F=B7=E6=A8=A1?= =?UTF-8?q?=E7=89=88=20=E7=94=9F=E6=88=90=E9=9A=8F=E6=9C=BA=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E5=AF=86=E9=92=A5=E5=8F=8A=E8=B4=A6=E5=8F=B7=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=9B=B4=E6=96=B0500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/models/mixins/vault.py | 4 ++-- apps/accounts/serializers/account/template.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/accounts/models/mixins/vault.py b/apps/accounts/models/mixins/vault.py index b16927b62..b8865ab7d 100644 --- a/apps/accounts/models/mixins/vault.py +++ b/apps/accounts/models/mixins/vault.py @@ -37,8 +37,8 @@ class VaultManagerMixin(models.Manager): post_save.send(obj.__class__, instance=obj, created=True) return objs - def bulk_update(self, objs, batch_size=None, ignore_conflicts=False): - objs = super().bulk_update(objs, batch_size=batch_size, ignore_conflicts=ignore_conflicts) + def bulk_update(self, objs, fields, batch_size=None): + objs = super().bulk_update(objs, fields, batch_size=batch_size) for obj in objs: post_save.send(obj.__class__, instance=obj, created=False) return objs diff --git a/apps/accounts/serializers/account/template.py b/apps/accounts/serializers/account/template.py index 149760908..89045627c 100644 --- a/apps/accounts/serializers/account/template.py +++ b/apps/accounts/serializers/account/template.py @@ -1,7 +1,9 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from accounts.const import SecretStrategy, SecretType from accounts.models import AccountTemplate, Account +from accounts.utils import SecretGenerator from common.serializers import SecretReadableMixin from common.serializers.fields import ObjectRelatedField from .base import BaseAccountSerializer @@ -55,9 +57,20 @@ class AccountTemplateSerializer(BaseAccountSerializer): accounts = Account.objects.filter(**query_data) instance.bulk_sync_account_secret(accounts, self.context['request'].user.id) + @staticmethod + def generate_secret(attrs): + secret_type = attrs.get('secret_type', SecretType.PASSWORD) + secret_strategy = attrs.get('secret_strategy', SecretStrategy.custom) + password_rules = attrs.get('password_rules') + if secret_strategy != SecretStrategy.random: + return + generator = SecretGenerator(secret_strategy, secret_type, password_rules) + attrs['secret'] = generator.get_secret() + def validate(self, attrs): self._is_sync_account = attrs.pop('is_sync_account', None) attrs = super().validate(attrs) + self.generate_secret(attrs) return attrs def update(self, instance, validated_data): From 2069fee79553417368402230a2d43d8afa691932 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 27 Sep 2023 10:31:44 +0800 Subject: [PATCH 018/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=8F=91?= =?UTF-8?q?=E9=80=81=E9=82=AE=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/tasks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/common/tasks.py b/apps/common/tasks.py index a7d9aacdd..2daa5acb5 100644 --- a/apps/common/tasks.py +++ b/apps/common/tasks.py @@ -36,7 +36,9 @@ def send_mail_async(*args, **kwargs): args[0] = (settings.EMAIL_SUBJECT_PREFIX or '') + args[0] from_email = settings.EMAIL_FROM or settings.EMAIL_HOST_USER args.insert(2, from_email) - args = tuple(args) + + args[3] = [mail for mail in args[3] if mail != 'admin@mycomany.com'] + args = tuple(args) try: return send_mail(*args, **kwargs) @@ -50,6 +52,7 @@ def send_mail_attachment_async(subject, message, recipient_list, attachment_list attachment_list = [] from_email = settings.EMAIL_FROM or settings.EMAIL_HOST_USER subject = (settings.EMAIL_SUBJECT_PREFIX or '') + subject + recipient_list = [mail for mail in recipient_list if mail != 'admin@mycomany.com'] email = EmailMultiAlternatives( subject=subject, body=message, From 15a5dda9e0cdbe2ac618a6b2a09df8928f485c88 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 27 Sep 2023 15:12:24 +0800 Subject: [PATCH 019/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E7=9A=84=E9=82=AE=E7=AE=B1=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/tasks.py | 3 --- .../migrations/0046_auto_20230927_1456.py | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 apps/users/migrations/0046_auto_20230927_1456.py diff --git a/apps/common/tasks.py b/apps/common/tasks.py index 2daa5acb5..468fa2a51 100644 --- a/apps/common/tasks.py +++ b/apps/common/tasks.py @@ -37,9 +37,7 @@ def send_mail_async(*args, **kwargs): from_email = settings.EMAIL_FROM or settings.EMAIL_HOST_USER args.insert(2, from_email) - args[3] = [mail for mail in args[3] if mail != 'admin@mycomany.com'] args = tuple(args) - try: return send_mail(*args, **kwargs) except Exception as e: @@ -52,7 +50,6 @@ def send_mail_attachment_async(subject, message, recipient_list, attachment_list attachment_list = [] from_email = settings.EMAIL_FROM or settings.EMAIL_HOST_USER subject = (settings.EMAIL_SUBJECT_PREFIX or '') + subject - recipient_list = [mail for mail in recipient_list if mail != 'admin@mycomany.com'] email = EmailMultiAlternatives( subject=subject, body=message, diff --git a/apps/users/migrations/0046_auto_20230927_1456.py b/apps/users/migrations/0046_auto_20230927_1456.py new file mode 100644 index 000000000..d32569026 --- /dev/null +++ b/apps/users/migrations/0046_auto_20230927_1456.py @@ -0,0 +1,21 @@ +# Generated by Django 4.1.10 on 2023-09-27 06:56 + +from django.db import migrations + + +def migrate_user_default_email(apps, *args): + user_cls = apps.get_model('users', 'User') + user_cls.objects\ + .filter(email='admin@mycomany.com')\ + .update(email='admin@example.com') + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0045_delete_usersession'), + ] + + operations = [ + migrations.RunPython(migrate_user_default_email), + ] From f79084c2dff35702c77fe150c3249c2cd45fa13d Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 27 Sep 2023 14:13:06 +0800 Subject: [PATCH 020/114] =?UTF-8?q?fix:=20=E8=B4=A6=E5=8F=B7=E6=8E=88?= =?UTF-8?q?=E6=9D=83=E8=BF=87=E6=BB=A4=E6=8C=87=E5=AE=9A=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?api=20=E5=A4=B1=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/api/account/account.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/accounts/api/account/account.py b/apps/accounts/api/account/account.py index 740ccf041..ecf014b55 100644 --- a/apps/accounts/api/account/account.py +++ b/apps/accounts/api/account/account.py @@ -6,8 +6,8 @@ from rest_framework.status import HTTP_200_OK from accounts import serializers from accounts.filters import AccountFilterSet -from accounts.models import Account from accounts.mixins import AccountRecordViewLogMixin +from accounts.models import Account from assets.models import Asset, Node from common.api.mixin import ExtraFilterFieldsMixin from common.permissions import UserConfirmation, ConfirmType, IsValidUser @@ -57,19 +57,19 @@ class AccountViewSet(OrgBulkModelViewSet): permission_classes=[IsValidUser] ) def username_suggestions(self, request, *args, **kwargs): - asset_ids = request.data.get('assets') - node_ids = request.data.get('nodes') - username = request.data.get('username') + asset_ids = request.data.get('assets', []) + node_ids = request.data.get('nodes', []) + username = request.data.get('username', '') - assets = Asset.objects.all() - if asset_ids: - assets = assets.filter(id__in=asset_ids) + accounts = Account.objects.all() if node_ids: nodes = Node.objects.filter(id__in=node_ids) node_asset_ids = Node.get_nodes_all_assets(*nodes).values_list('id', flat=True) - assets = assets.filter(id__in=set(list(asset_ids) + list(node_asset_ids))) + asset_ids.extend(node_asset_ids) + + if asset_ids: + accounts = accounts.filter(asset_id__in=list(set(asset_ids))) - accounts = Account.objects.filter(asset__in=assets) if username: accounts = accounts.filter(username__icontains=username) usernames = list(accounts.values_list('username', flat=True).distinct()[:10]) From 896d42c53efc0d402c9982d868a42c1bbb4ac570 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Thu, 28 Sep 2023 16:32:41 +0800 Subject: [PATCH 021/114] =?UTF-8?q?perf:=20=E6=9B=B4=E6=96=B0jms-storage?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 18 ++++++------------ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/poetry.lock b/poetry.lock index d969ce3cf..674e0aa36 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "adal" @@ -2655,14 +2655,8 @@ files = [ [package.dependencies] google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" -grpcio = [ - {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, -] -grpcio-status = [ - {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""}, - {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, -] +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -3345,12 +3339,12 @@ reference = "tsinghua" [[package]] name = "jms-storage" -version = "0.0.51" +version = "0.0.52" description = "Jumpserver storage python sdk tools" optional = false python-versions = "*" files = [ - {file = "jms-storage-0.0.51.tar.gz", hash = "sha256:47a50ac4d952a21693b0e2f926f42fa0d02bc1fa8e507a8284059743b2b81911"}, + {file = "jms-storage-0.0.52.tar.gz", hash = "sha256:15303281a1d1a3ac24a5a9fb0d78abda3aa1f752590aab867923647a485ccfbd"}, ] [package.dependencies] @@ -7276,4 +7270,4 @@ reference = "tsinghua" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "b7d8e793f247e91e1bd22404559ed495e619fe691fa92712cacf7bd146c0eb8f" +content-hash = "bf72acdbac5e62c239033fd629835ad30788a3fcb07ac9a2dc2f15f321da6c30" diff --git a/pyproject.toml b/pyproject.toml index 160c0e974..5a1cd585b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ pynacl = "1.5.0" python-dateutil = "2.8.2" pyyaml = "6.0.1" requests = "2.31.0" -jms-storage = "0.0.51" +jms-storage = "0.0.52" simplejson = "3.19.1" six = "1.16.0" sshtunnel = "0.4.0" From 27c505853baa901eb19583443754e281e5e32511 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 7 Oct 2023 13:07:47 +0800 Subject: [PATCH 022/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=BF=98?= =?UTF-8?q?=E8=AE=B0=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/password.py | 45 ++- apps/authentication/errors/mfa.py | 8 + apps/common/exceptions.py | 2 +- apps/common/utils/verify_code.py | 7 - apps/locale/ja/LC_MESSAGES/django.po | 580 ++++++++++++++------------- apps/locale/zh/LC_MESSAGES/django.po | 580 ++++++++++++++------------- apps/users/models/user.py | 7 +- apps/users/views/profile/reset.py | 48 ++- 8 files changed, 678 insertions(+), 599 deletions(-) diff --git a/apps/authentication/api/password.py b/apps/authentication/api/password.py index 86801bc6c..cc1e6aff7 100644 --- a/apps/authentication/api/password.py +++ b/apps/authentication/api/password.py @@ -1,3 +1,5 @@ +import time + from django.core.cache import cache from django.http import HttpResponseRedirect from django.shortcuts import reverse @@ -7,7 +9,7 @@ from rest_framework.generics import CreateAPIView from rest_framework.permissions import AllowAny from rest_framework.response import Response -from authentication.errors import PasswordInvalid +from authentication.errors import PasswordInvalid, IntervalTooShort from authentication.mixins import AuthMixin from authentication.mixins import authenticate from authentication.serializers import ( @@ -38,18 +40,18 @@ class UserResetPasswordSendCodeApi(CreateAPIView): return None, err_msg return user, None - def create(self, request, *args, **kwargs): - token = request.GET.get('token') - userinfo = cache.get(token) - if not userinfo: - return HttpResponseRedirect(reverse('authentication:forgot-previewing')) + @staticmethod + def safe_send_code(token, code, target, form_type, content): + token_sent_key = '{}_send_at'.format(token) + token_send_at = cache.get(token_sent_key, 0) + if token_send_at: + raise IntervalTooShort(60) + SendAndVerifyCodeUtil(target, code, backend=form_type, **content).gen_and_send_async() + cache.set(token_sent_key, int(time.time()), 60) - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - username = userinfo.get('username') + def prepare_code_data(self, user_info, serializer): + username = user_info.get('username') form_type = serializer.validated_data['form_type'] - code = random_string(6, lower=False, upper=False) - other_args = {} target = serializer.validated_data[form_type] if form_type == 'sms': @@ -59,15 +61,30 @@ class UserResetPasswordSendCodeApi(CreateAPIView): query_key = form_type user, err = self.is_valid_user(username=username, **{query_key: target}) if not user: - return Response({'error': err}, status=400) + raise ValueError(err) + code = random_string(6, lower=False, upper=False) 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() + content = {'subject': subject, 'message': message} + return code, target, form_type, content + + def create(self, request, *args, **kwargs): + token = request.GET.get('token') + user_info = cache.get(token) + if not user_info: + return HttpResponseRedirect(reverse('authentication:forgot-previewing')) + + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + try: + code, target, form_type, content = self.prepare_code_data(user_info, serializer) + except ValueError as e: + return Response({'error': str(e)}, status=400) + self.safe_send_code(token, code, target, form_type, content) return Response({'data': 'ok'}, status=200) diff --git a/apps/authentication/errors/mfa.py b/apps/authentication/errors/mfa.py index 40b97df92..8a0844145 100644 --- a/apps/authentication/errors/mfa.py +++ b/apps/authentication/errors/mfa.py @@ -36,3 +36,11 @@ class FeiShuNotBound(JMSException): class PasswordInvalid(JMSException): default_code = 'passwd_invalid' default_detail = _('Your password is invalid') + + +class IntervalTooShort(JMSException): + default_code = 'interval_too_short' + default_detail = _('Please wait for %s seconds before retry') + + def __init__(self, interval, *args, **kwargs): + super().__init__(detail=self.default_detail % interval, *args, **kwargs) diff --git a/apps/common/exceptions.py b/apps/common/exceptions.py index 5fa8861b0..a8dfcafbd 100644 --- a/apps/common/exceptions.py +++ b/apps/common/exceptions.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # from django.utils.translation import gettext_lazy as _ -from rest_framework.exceptions import APIException from rest_framework import status +from rest_framework.exceptions import APIException class JMSException(APIException): diff --git a/apps/common/utils/verify_code.py b/apps/common/utils/verify_code.py index 120618704..b21e43b26 100644 --- a/apps/common/utils/verify_code.py +++ b/apps/common/utils/verify_code.py @@ -26,7 +26,6 @@ class SendAndVerifyCodeUtil(object): self.target = target self.backend = backend self.key = key or self.KEY_TMPL.format(target) - self.verify_key = self.key + '_verify' self.timeout = settings.VERIFY_CODE_TTL if timeout is None else timeout self.other_args = kwargs @@ -48,11 +47,6 @@ class SendAndVerifyCodeUtil(object): raise def verify(self, code): - times = cache.get(self.verify_key, 0) - if times >= 3: - self.__clear() - raise CodeExpired - cache.set(self.verify_key, times + 1, timeout=self.timeout) right = cache.get(self.key) if not right: raise CodeExpired @@ -65,7 +59,6 @@ class SendAndVerifyCodeUtil(object): def __clear(self): cache.delete(self.key) - cache.delete(self.verify_key) def __ttl(self): return cache.ttl(self.key) diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 1c40efb67..0a737c38d 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: 2023-09-25 16:22+0800\n" +"POT-Creation-Date: 2023-10-07 13:50+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -31,7 +31,7 @@ msgstr "パラメータ 'action' は [{}] でなければなりません。" #: users/forms/profile.py:22 users/serializers/user.py:105 #: users/templates/users/_msg_user_created.html:13 #: users/templates/users/user_password_verify.html:18 -#: xpack/plugins/cloud/serializers/account_attrs.py:28 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:28 msgid "Password" msgstr "パスワード" @@ -71,7 +71,7 @@ msgstr "動的コード" msgid "Anonymous account" msgstr "匿名ユーザー" -#: accounts/const/account.py:25 users/models/user.py:741 +#: accounts/const/account.py:25 users/models/user.py:738 msgid "Local" msgstr "ローカル" @@ -96,7 +96,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:155 audits/const.py:55 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 +#: ops/const.py:74 terminal/const.py:77 xpack.bak/plugins/cloud/const.py:43 msgid "Failed" msgstr "失敗しました" @@ -232,14 +232,15 @@ msgstr "ユーザー %s がパスワードを閲覧/導き出しました" #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 +#: tickets/models/ticket/apply_asset.py:16 +#: xpack.bak/plugins/cloud/models.py:253 msgid "Asset" msgstr "資産" #: accounts/models/account.py:52 accounts/models/template.py:15 #: accounts/serializers/account/account.py:216 #: accounts/serializers/account/account.py:264 -#: accounts/serializers/account/template.py:25 +#: accounts/serializers/account/template.py:27 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" msgstr "から切り替え" @@ -251,7 +252,7 @@ msgid "Version" msgstr "バージョン" #: accounts/models/account.py:56 accounts/serializers/account/account.py:211 -#: users/models/user.py:843 +#: users/models/user.py:840 msgid "Source" msgstr "ソース" @@ -268,7 +269,8 @@ msgstr "ソース ID" #: terminal/backends/command/models.py:18 terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 -#: tickets/models/ticket/command_confirm.py:13 xpack/plugins/cloud/models.py:89 +#: tickets/models/ticket/command_confirm.py:13 +#: xpack.bak/plugins/cloud/models.py:89 msgid "Account" msgstr "アカウント" @@ -335,7 +337,7 @@ msgid "Trigger mode" msgstr "トリガーモード" #: accounts/models/automations/backup_account.py:105 audits/models.py:197 -#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 +#: terminal/models/session/sharing.py:125 xpack.bak/plugins/cloud/models.py:205 msgid "Reason" msgstr "理由" @@ -448,8 +450,8 @@ msgstr "最終ログイン日" #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: users/forms/profile.py:32 users/forms/profile.py:115 -#: users/models/user.py:793 users/templates/users/_msg_user_created.html:12 -#: xpack/plugins/cloud/serializers/account_attrs.py:26 +#: users/models/user.py:790 users/templates/users/_msg_user_created.html:12 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" msgstr "ユーザー名" @@ -512,7 +514,7 @@ msgstr "ひみつ" msgid "Secret strategy" msgstr "鍵ポリシー" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:24 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "パスワードルール" @@ -537,8 +539,9 @@ msgstr "パスワードルール" #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 users/forms/profile.py:33 #: users/models/group.py:13 users/models/preference.py:11 -#: users/models/user.py:795 xpack/plugins/cloud/models.py:32 -#: xpack/plugins/cloud/models.py:273 xpack/plugins/cloud/serializers/task.py:68 +#: users/models/user.py:792 xpack.bak/plugins/cloud/models.py:32 +#: xpack.bak/plugins/cloud/models.py:273 +#: xpack.bak/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名前" @@ -567,7 +570,7 @@ msgstr "プラットフォーム" msgid "Push params" msgstr "パラメータをプッシュする" -#: accounts/models/template.py:25 xpack/plugins/cloud/models.py:325 +#: accounts/models/template.py:25 xpack.bak/plugins/cloud/models.py:325 msgid "Account template" msgstr "アカウント テンプレート" @@ -748,8 +751,8 @@ msgstr "ID" #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:993 -#: users/models/user.py:1029 users/serializers/group.py:18 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:990 +#: users/models/user.py:1026 users/serializers/group.py:18 msgid "User" msgstr "ユーザー" @@ -797,35 +800,35 @@ msgstr "" "ヒント: 認証にユーザー名が必要ない場合は、`null`を入力します。ADアカウントの" "場合は、`username@domain`のようになります。" -#: accounts/serializers/account/template.py:11 +#: accounts/serializers/account/template.py:13 msgid "Password length" msgstr "パスワードの長さ" -#: accounts/serializers/account/template.py:12 +#: accounts/serializers/account/template.py:14 msgid "Lowercase" msgstr "小文字" -#: accounts/serializers/account/template.py:13 +#: accounts/serializers/account/template.py:15 msgid "Uppercase" msgstr "大文字" -#: accounts/serializers/account/template.py:14 +#: accounts/serializers/account/template.py:16 msgid "Digit" msgstr "数値#スウスウ#" -#: accounts/serializers/account/template.py:15 +#: accounts/serializers/account/template.py:17 msgid "Special symbol" msgstr "特殊記号" -#: accounts/serializers/account/template.py:36 +#: accounts/serializers/account/template.py:38 msgid "Secret generation strategy for account creation" msgstr "账号创建时,密文生成策略" -#: accounts/serializers/account/template.py:37 +#: accounts/serializers/account/template.py:39 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:40 +#: accounts/serializers/account/template.py:42 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -840,8 +843,8 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:831 -#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 +#: tickets/models/ticket/general.py:297 users/models/user.py:828 +#: xpack.bak/plugins/cloud/models.py:39 xpack.bak/plugins/cloud/models.py:109 msgid "Comment" msgstr "コメント" @@ -988,13 +991,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:97 -#: xpack/plugins/cloud/models.py:275 +#: xpack.bak/plugins/cloud/models.py:275 msgid "Priority" msgstr "優先順位" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack/plugins/cloud/models.py:276 +#: xpack.bak/plugins/cloud/models.py:276 msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" @@ -1031,12 +1034,12 @@ msgid "Command" msgstr "コマンド" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack/plugins/cloud/models.py:291 +#: xpack.bak/plugins/cloud/models.py:291 msgid "Regex" msgstr "正規情報" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/feature.py:17 xpack/plugins/license/models.py:30 +#: settings/serializers/feature.py:17 xpack.bak/plugins/license/models.py:30 msgid "Content" msgstr "コンテンツ" @@ -1132,7 +1135,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "いずれのレビューアも組織 '{}' に属していません" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:137 +#: xpack.bak/plugins/cloud/serializers/task.py:137 msgid "IP address invalid: `{}`" msgstr "IPアドレスが無効: '{}'" @@ -1190,8 +1193,8 @@ msgstr "ありがとうございます。" msgid "Applications" msgstr "アプリケーション" -#: applications/models.py:16 xpack/plugins/cloud/models.py:37 -#: xpack/plugins/cloud/serializers/account.py:63 +#: applications/models.py:16 xpack.bak/plugins/cloud/models.py:37 +#: xpack.bak/plugins/cloud/serializers/account.py:63 msgid "Attrs" msgstr "ツールバーの" @@ -1256,7 +1259,7 @@ msgid "Unable to connect to port {port} on {address}" msgstr "{port} のポート {address} に接続できません" #: assets/automations/ping_gateway/manager.py:58 -#: authentication/middleware.py:92 xpack/plugins/cloud/providers/fc.py:47 +#: authentication/middleware.py:92 xpack.bak/plugins/cloud/providers/fc.py:47 msgid "Authentication failed" msgstr "認証に失敗しました" @@ -1308,7 +1311,7 @@ msgstr "脚本" #: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:67 #: settings/serializers/feature.py:47 terminal/models/component/endpoint.py:13 #: terminal/serializers/applet.py:17 -#: xpack/plugins/cloud/serializers/account_attrs.py:72 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:72 msgid "Host" msgstr "ホスト" @@ -1508,18 +1511,18 @@ msgstr "SSHパブリックキー" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:261 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:227 users/models/user.py:1030 +#: ops/models/job.py:227 users/models/user.py:1027 msgid "Date created" msgstr "作成された日付" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:852 +#: common/db/models.py:35 users/models/user.py:849 msgid "Date updated" msgstr "更新日" #: assets/models/_user.py:30 assets/models/cmd_filter.py:44 #: assets/models/cmd_filter.py:91 assets/models/group.py:18 -#: common/db/models.py:32 users/models/user.py:838 +#: common/db/models.py:32 users/models/user.py:835 #: users/serializers/group.py:29 msgid "Created by" msgstr "によって作成された" @@ -1597,7 +1600,7 @@ msgstr "クラウド サービス" #: assets/models/asset/common.py:92 assets/models/platform.py:16 #: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:68 -#: xpack/plugins/cloud/serializers/account_attrs.py:73 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:73 msgid "Port" msgstr "ポート" @@ -1608,13 +1611,15 @@ msgstr "アドレス" #: assets/models/asset/common.py:151 assets/models/platform.py:119 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:115 -#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 +#: perms/serializers/user_permission.py:24 +#: xpack.bak/plugins/cloud/models.py:321 msgid "Platform" msgstr "プラットフォーム" #: assets/models/asset/common.py:153 assets/models/domain.py:21 #: authentication/serializers/connect_token_secret.py:133 -#: perms/serializers/user_permission.py:29 xpack/plugins/cloud/models.py:323 +#: perms/serializers/user_permission.py:29 +#: xpack.bak/plugins/cloud/models.py:323 msgid "Domain" msgstr "ドメイン" @@ -1690,8 +1695,8 @@ msgstr "アセットの自動化タスク" #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:115 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 -#: xpack/plugins/cloud/models.py:257 +#: tickets/serializers/ticket/ticket.py:20 +#: xpack.bak/plugins/cloud/models.py:201 xpack.bak/plugins/cloud/models.py:257 msgid "Status" msgstr "ステータス" @@ -1713,7 +1718,7 @@ msgstr "確認済みの日付" #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:61 #: perms/serializers/permission.py:32 users/models/group.py:25 -#: users/models/user.py:801 +#: users/models/user.py:798 msgid "User group" msgstr "ユーザーグループ" @@ -1755,7 +1760,7 @@ msgstr "資産グループ" #: assets/models/group.py:31 assets/models/platform.py:19 #: assets/serializers/platform.py:114 -#: xpack/plugins/cloud/providers/nutanix.py:30 +#: xpack.bak/plugins/cloud/providers/nutanix.py:30 msgid "Default" msgstr "デフォルト" @@ -1763,7 +1768,7 @@ msgstr "デフォルト" msgid "Default asset group" msgstr "デフォルトアセットグループ" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1015 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1012 msgid "System" msgstr "システム" @@ -1806,7 +1811,8 @@ msgid "Parent key" msgstr "親キー" #: assets/models/node.py:553 perms/serializers/permission.py:35 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 +#: tickets/models/ticket/apply_asset.py:14 +#: xpack.bak/plugins/cloud/models.py:322 msgid "Node" msgstr "ノード" @@ -1948,8 +1954,9 @@ msgstr "" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:134 #: authentication/serializers/connect_token_secret.py:29 #: authentication/serializers/connect_token_secret.py:72 -#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:324 -#: xpack/plugins/cloud/serializers/task.py:31 +#: perms/serializers/user_permission.py:25 +#: xpack.bak/plugins/cloud/models.py:324 +#: xpack.bak/plugins/cloud/serializers/task.py:31 msgid "Protocols" msgstr "プロトコル" @@ -1982,9 +1989,9 @@ msgstr "デフォルト・データベース" #: assets/serializers/asset/database.py:28 common/db/fields.py:570 #: common/db/fields.py:575 common/serializers/fields.py:104 #: tickets/serializers/ticket/common.py:58 -#: xpack/plugins/cloud/serializers/account_attrs.py:56 -#: xpack/plugins/cloud/serializers/account_attrs.py:79 -#: xpack/plugins/cloud/serializers/account_attrs.py:143 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:56 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:79 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:143 msgid "This field is required." msgstr "このフィールドは必須です。" @@ -2398,7 +2405,7 @@ msgstr "ログインIP" #: audits/models.py:194 audits/serializers.py:49 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:818 +#: users/forms/profile.py:65 users/models/user.py:815 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" @@ -2423,7 +2430,7 @@ msgstr "セッションID" #: audits/models.py:262 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 #: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:836 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:833 msgid "Date expired" msgstr "期限切れの日付" @@ -2474,22 +2481,22 @@ msgstr "認証トークン" #: audits/signal_handlers/login_log.py:40 authentication/notifications.py:73 #: authentication/views/login.py:77 authentication/views/wecom.py:159 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:748 users/models/user.py:853 +#: users/models/user.py:745 users/models/user.py:850 msgid "WeCom" msgstr "企業微信" #: audits/signal_handlers/login_log.py:41 authentication/views/feishu.py:122 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:750 -#: users/models/user.py:855 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:747 +#: users/models/user.py:852 msgid "FeiShu" msgstr "本を飛ばす" #: audits/signal_handlers/login_log.py:42 authentication/views/dingtalk.py:159 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:749 -#: users/models/user.py:854 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:746 +#: users/models/user.py:851 msgid "DingTalk" msgstr "DingTalk" @@ -2545,16 +2552,16 @@ msgstr "ACL アクションはレビューです" msgid "Current user not support mfa type: {}" msgstr "現在のユーザーはmfaタイプをサポートしていません: {}" -#: authentication/api/password.py:32 terminal/api/session/session.py:300 -#: users/views/profile/reset.py:44 +#: authentication/api/password.py:34 terminal/api/session/session.py:300 +#: users/views/profile/reset.py:61 msgid "User does not exist: {}" msgstr "ユーザーが存在しない: {}" -#: authentication/api/password.py:32 users/views/profile/reset.py:127 +#: authentication/api/password.py:34 users/views/profile/reset.py:163 msgid "No user matched" msgstr "ユーザーにマッチしなかった" -#: authentication/api/password.py:36 +#: authentication/api/password.py:38 msgid "" "The user is from {}, please go to the corresponding system to change the " "password" @@ -2562,7 +2569,7 @@ msgstr "" "ユーザーは {}からです。対応するシステムにアクセスしてパスワードを変更してくだ" "さい。" -#: authentication/api/password.py:64 +#: authentication/api/password.py:67 #: authentication/templates/authentication/login.html:355 #: users/templates/users/forgot_password.html:27 #: users/templates/users/forgot_password.html:28 @@ -2811,6 +2818,10 @@ msgstr "本を飛ばすは拘束されていません" msgid "Your password is invalid" msgstr "パスワードが無効です" +#: authentication/errors/mfa.py:43 +msgid "Please wait for %s seconds before retry" +msgstr "%s 秒後に再試行してください" + #: authentication/errors/redirect.py:85 authentication/mixins.py:318 msgid "Your password is too simple, please change it for security" msgstr "パスワードがシンプルすぎるので、セキュリティのために変更してください" @@ -2902,7 +2913,7 @@ msgstr "メッセージ検証コードが無効" #: authentication/serializers/password_mfa.py:24 #: settings/serializers/auth/sms.py:28 users/forms/profile.py:104 #: users/forms/profile.py:109 users/templates/users/forgot_password.html:112 -#: users/views/profile/reset.py:79 +#: users/views/profile/reset.py:98 msgid "SMS" msgstr "メッセージ" @@ -3079,9 +3090,9 @@ msgstr "期限切れです" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 #: settings/serializers/msg.py:57 users/forms/profile.py:102 -#: users/forms/profile.py:109 users/models/user.py:797 +#: users/forms/profile.py:109 users/models/user.py:794 #: users/templates/users/forgot_password.html:117 -#: users/views/profile/reset.py:73 +#: users/views/profile/reset.py:92 msgid "Email" msgstr "メール" @@ -3117,13 +3128,13 @@ msgid "Show" msgstr "表示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:643 users/serializers/profile.py:92 +#: users/models/user.py:640 users/serializers/profile.py:92 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" msgstr "無効化" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:644 users/serializers/profile.py:93 +#: users/models/user.py:641 users/serializers/profile.py:93 #: users/templates/users/mfa_setting.html:26 #: users/templates/users/mfa_setting.html:68 msgid "Enable" @@ -3341,7 +3352,7 @@ msgid "Do you want to retry ?" msgstr "再試行しますか?" #: authentication/utils.py:28 common/utils/ip/geoip/utils.py:24 -#: xpack/plugins/cloud/const.py:29 +#: xpack.bak/plugins/cloud/const.py:29 msgid "LAN" msgstr "ローカルエリアネットワーク" @@ -3432,7 +3443,7 @@ msgstr "ログインタイムアウト、もう一度お試しください" msgid "User email already exists ({})" msgstr "ユーザー メールボックスは既に存在します ({})" -#: authentication/views/login.py:361 +#: authentication/views/login.py:355 msgid "" "Wait for {} confirm, You also can copy link to her/him
\n" " Don't close this page" @@ -3440,15 +3451,15 @@ msgstr "" "{} 確認を待ちます。彼女/彼へのリンクをコピーすることもできます
\n" " このページを閉じないでください" -#: authentication/views/login.py:366 +#: authentication/views/login.py:360 msgid "No ticket found" msgstr "チケットが見つかりません" -#: authentication/views/login.py:402 +#: authentication/views/login.py:396 msgid "Logout success" msgstr "ログアウト成功" -#: authentication/views/login.py:403 +#: authentication/views/login.py:397 msgid "Logout success, return login page" msgstr "ログアウト成功、ログインページを返す" @@ -3585,7 +3596,7 @@ msgstr "は破棄されます" msgid "discard time" msgstr "時間を捨てる" -#: common/db/models.py:33 users/models/user.py:839 +#: common/db/models.py:33 users/models/user.py:836 msgid "Updated by" msgstr "によって更新" @@ -3686,7 +3697,7 @@ msgstr "アリ雲" msgid "Tencent cloud" msgstr "テンセント雲" -#: common/sdk/sms/endpoint.py:18 xpack/plugins/cloud/const.py:13 +#: common/sdk/sms/endpoint.py:18 xpack.bak/plugins/cloud/const.py:13 msgid "Huawei Cloud" msgstr "華為雲" @@ -4038,7 +4049,7 @@ msgid "Date last run" msgstr "最終実行日" #: ops/models/base.py:51 ops/models/job.py:224 -#: xpack/plugins/cloud/models.py:199 +#: xpack.bak/plugins/cloud/models.py:199 msgid "Result" msgstr "結果" @@ -4473,7 +4484,7 @@ msgid "Scope" msgstr "スコープ" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:805 +#: users/models/user.py:802 msgid "Role" msgstr "ロール" @@ -4864,7 +4875,7 @@ msgid "Client Id" msgstr "クライアントID" #: settings/serializers/auth/oauth2.py:33 settings/serializers/auth/oidc.py:22 -#: xpack/plugins/cloud/serializers/account_attrs.py:38 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:38 msgid "Client Secret" msgstr "クライアント秘密" @@ -5132,7 +5143,7 @@ msgid "SSO auth key TTL" msgstr "Token有効期間" #: settings/serializers/auth/sso.py:20 -#: xpack/plugins/cloud/serializers/account_attrs.py:193 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:193 msgid "Unit: second" msgstr "単位: 秒" @@ -6232,7 +6243,7 @@ msgstr "一括作成非サポート" msgid "Storage is invalid" msgstr "ストレージが無効です" -#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:88 +#: terminal/models/applet/applet.py:30 xpack.bak/plugins/license/models.py:88 msgid "Community edition" msgstr "コミュニティ版" @@ -6759,16 +6770,16 @@ msgid "Bucket" msgstr "バケット" #: terminal/serializers/storage.py:32 -#: xpack/plugins/cloud/serializers/account_attrs.py:17 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:17 msgid "Access key id" msgstr "アクセスキー" #: terminal/serializers/storage.py:36 -#: xpack/plugins/cloud/serializers/account_attrs.py:20 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:20 msgid "Access key secret" msgstr "アクセスキーシークレット" -#: terminal/serializers/storage.py:67 xpack/plugins/cloud/models.py:250 +#: terminal/serializers/storage.py:67 xpack.bak/plugins/cloud/models.py:250 msgid "Region" msgstr "リージョン" @@ -7353,8 +7364,8 @@ msgstr "公開鍵は古いものと同じであってはなりません。" msgid "Not a valid ssh public key" msgstr "有効なssh公開鍵ではありません" -#: users/forms/profile.py:173 users/models/user.py:828 -#: xpack/plugins/cloud/serializers/account_attrs.py:203 +#: users/forms/profile.py:173 users/models/user.py:825 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:203 msgid "Public key" msgstr "公開キー" @@ -7362,73 +7373,73 @@ msgstr "公開キー" msgid "Preference" msgstr "ユーザー設定" -#: users/models/user.py:645 users/serializers/profile.py:94 +#: users/models/user.py:642 users/serializers/profile.py:94 msgid "Force enable" msgstr "強制有効" -#: users/models/user.py:807 users/serializers/user.py:172 +#: users/models/user.py:804 users/serializers/user.py:172 msgid "Is service account" msgstr "サービスアカウントです" -#: users/models/user.py:809 +#: users/models/user.py:806 msgid "Avatar" msgstr "アバター" -#: users/models/user.py:812 +#: users/models/user.py:809 msgid "Wechat" msgstr "微信" -#: users/models/user.py:815 users/serializers/user.py:109 +#: users/models/user.py:812 users/serializers/user.py:109 msgid "Phone" msgstr "電話" -#: users/models/user.py:821 +#: users/models/user.py:818 msgid "OTP secret key" msgstr "OTP 秘密" -#: users/models/user.py:825 -#: xpack/plugins/cloud/serializers/account_attrs.py:206 +#: users/models/user.py:822 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:206 msgid "Private key" msgstr "ssh秘密鍵" -#: users/models/user.py:833 users/serializers/profile.py:125 +#: users/models/user.py:830 users/serializers/profile.py:125 #: users/serializers/user.py:169 msgid "Is first login" msgstr "最初のログインです" -#: users/models/user.py:847 +#: users/models/user.py:844 msgid "Date password last updated" msgstr "最終更新日パスワード" -#: users/models/user.py:850 +#: users/models/user.py:847 msgid "Need update password" msgstr "更新パスワードが必要" -#: users/models/user.py:974 +#: users/models/user.py:971 msgid "Can not delete admin user" msgstr "管理者ユーザーを削除できませんでした" -#: users/models/user.py:1000 +#: users/models/user.py:997 msgid "Can invite user" msgstr "ユーザーを招待できます" -#: users/models/user.py:1001 +#: users/models/user.py:998 msgid "Can remove user" msgstr "ユーザーを削除できます" -#: users/models/user.py:1002 +#: users/models/user.py:999 msgid "Can match user" msgstr "ユーザーに一致できます" -#: users/models/user.py:1011 +#: users/models/user.py:1008 msgid "Administrator" msgstr "管理者" -#: users/models/user.py:1014 +#: users/models/user.py:1011 msgid "Administrator is the super user of system" msgstr "管理者はシステムのスーパーユーザーです" -#: users/models/user.py:1039 +#: users/models/user.py:1036 msgid "User password history" msgstr "ユーザーパスワード履歴" @@ -7439,7 +7450,7 @@ msgstr "ユーザーパスワード履歴" msgid "Reset password" msgstr "パスワードのリセット" -#: users/notifications.py:85 users/views/profile/reset.py:194 +#: users/notifications.py:85 users/views/profile/reset.py:230 msgid "Reset password success" msgstr "パスワードのリセット成功" @@ -7846,7 +7857,7 @@ msgstr "OTP無効化成功、ログインページを返す" msgid "Password invalid" msgstr "パスワード無効" -#: users/views/profile/reset.py:47 +#: users/views/profile/reset.py:64 msgid "" "Non-local users can log in only from third-party platforms and cannot change " "their passwords: {}" @@ -7854,557 +7865,561 @@ msgstr "" "ローカル以外のユーザーは、サードパーティ プラットフォームからのログインのみが" "許可され、パスワードの変更はサポートされていません: {}" -#: users/views/profile/reset.py:149 users/views/profile/reset.py:160 +#: users/views/profile/reset.py:185 users/views/profile/reset.py:196 msgid "Token invalid or expired" msgstr "トークンが無効または期限切れ" -#: users/views/profile/reset.py:165 +#: users/views/profile/reset.py:201 msgid "User auth from {}, go there change password" msgstr "ユーザー認証ソース {}, 対応するシステムにパスワードを変更してください" -#: users/views/profile/reset.py:172 +#: users/views/profile/reset.py:208 msgid "* Your password does not meet the requirements" msgstr "* パスワードが要件を満たしていない" -#: users/views/profile/reset.py:178 +#: users/views/profile/reset.py:214 msgid "* The new password cannot be the last {} passwords" msgstr "* 新しいパスワードを最後の {} パスワードにすることはできません" -#: users/views/profile/reset.py:195 +#: users/views/profile/reset.py:231 msgid "Reset password success, return to login page" msgstr "パスワードの成功をリセットし、ログインページに戻る" -#: xpack/apps.py:8 +#: xpack.bak/apps.py:8 msgid "XPACK" msgstr "XPack" -#: xpack/plugins/cloud/api.py:56 +#: xpack.bak/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "テスト接続成功" -#: xpack/plugins/cloud/api.py:58 +#: xpack.bak/plugins/cloud/api.py:58 msgid "Test connection failed: {}" msgstr "テスト接続に失敗しました: {}" -#: xpack/plugins/cloud/const.py:8 +#: xpack.bak/plugins/cloud/const.py:8 msgid "Alibaba Cloud" msgstr "アリ雲" -#: xpack/plugins/cloud/const.py:9 +#: xpack.bak/plugins/cloud/const.py:9 msgid "AWS (International)" msgstr "AWS (国際)" -#: xpack/plugins/cloud/const.py:10 +#: xpack.bak/plugins/cloud/const.py:10 msgid "AWS (China)" msgstr "AWS (中国)" -#: xpack/plugins/cloud/const.py:11 +#: xpack.bak/plugins/cloud/const.py:11 msgid "Azure (China)" msgstr "Azure (中国)" -#: xpack/plugins/cloud/const.py:12 +#: xpack.bak/plugins/cloud/const.py:12 msgid "Azure (International)" msgstr "Azure (国際)" -#: xpack/plugins/cloud/const.py:14 +#: xpack.bak/plugins/cloud/const.py:14 msgid "Baidu Cloud" msgstr "百度雲" -#: xpack/plugins/cloud/const.py:15 +#: xpack.bak/plugins/cloud/const.py:15 msgid "JD Cloud" msgstr "京東雲" -#: xpack/plugins/cloud/const.py:16 +#: xpack.bak/plugins/cloud/const.py:16 msgid "KingSoft Cloud" msgstr "金山雲" -#: xpack/plugins/cloud/const.py:17 +#: xpack.bak/plugins/cloud/const.py:17 msgid "Tencent Cloud" msgstr "テンセント雲" -#: xpack/plugins/cloud/const.py:18 +#: xpack.bak/plugins/cloud/const.py:18 msgid "Tencent Cloud (Lighthouse)" msgstr "テンセント雲(軽量アプリケーション)" -#: xpack/plugins/cloud/const.py:19 +#: xpack.bak/plugins/cloud/const.py:19 msgid "Google Cloud Platform" msgstr "谷歌雲" -#: xpack/plugins/cloud/const.py:20 +#: xpack.bak/plugins/cloud/const.py:20 msgid "UCloud" msgstr "ucloud" -#: xpack/plugins/cloud/const.py:22 +#: xpack.bak/plugins/cloud/const.py:22 msgid "VMware" msgstr "VMware" -#: xpack/plugins/cloud/const.py:23 xpack/plugins/cloud/providers/nutanix.py:15 +#: xpack.bak/plugins/cloud/const.py:23 +#: xpack.bak/plugins/cloud/providers/nutanix.py:15 msgid "Nutanix" msgstr "Nutanix" -#: xpack/plugins/cloud/const.py:24 +#: xpack.bak/plugins/cloud/const.py:24 msgid "Huawei Private Cloud" msgstr "華為私有雲" -#: xpack/plugins/cloud/const.py:25 +#: xpack.bak/plugins/cloud/const.py:25 msgid "Qingyun Private Cloud" msgstr "青雲私有雲" -#: xpack/plugins/cloud/const.py:26 +#: xpack.bak/plugins/cloud/const.py:26 msgid "CTYun Private Cloud" msgstr "スカイウィング私有雲" -#: xpack/plugins/cloud/const.py:27 +#: xpack.bak/plugins/cloud/const.py:27 msgid "OpenStack" msgstr "OpenStack" -#: xpack/plugins/cloud/const.py:28 +#: xpack.bak/plugins/cloud/const.py:28 msgid "Fusion Compute" msgstr "融合計算" -#: xpack/plugins/cloud/const.py:33 +#: xpack.bak/plugins/cloud/const.py:33 msgid "Private IP" msgstr "プライベートIP" -#: xpack/plugins/cloud/const.py:34 +#: xpack.bak/plugins/cloud/const.py:34 msgid "Public IP" msgstr "パブリックIP" -#: xpack/plugins/cloud/const.py:38 xpack/plugins/cloud/models.py:295 +#: xpack.bak/plugins/cloud/const.py:38 xpack.bak/plugins/cloud/models.py:295 msgid "Instance name" msgstr "インスタンス名" -#: xpack/plugins/cloud/const.py:39 +#: xpack.bak/plugins/cloud/const.py:39 msgid "Instance name and Partial IP" msgstr "インスタンス名と部分IP" -#: xpack/plugins/cloud/const.py:44 +#: xpack.bak/plugins/cloud/const.py:44 msgid "Succeed" msgstr "成功" -#: xpack/plugins/cloud/const.py:48 +#: xpack.bak/plugins/cloud/const.py:48 msgid "Unsync" msgstr "同期していません" -#: xpack/plugins/cloud/const.py:49 +#: xpack.bak/plugins/cloud/const.py:49 msgid "New Sync" msgstr "新しい同期" -#: xpack/plugins/cloud/const.py:50 +#: xpack.bak/plugins/cloud/const.py:50 msgid "Synced" msgstr "同期済み" -#: xpack/plugins/cloud/const.py:51 +#: xpack.bak/plugins/cloud/const.py:51 msgid "Released" msgstr "リリース済み" -#: xpack/plugins/cloud/manager.py:53 +#: xpack.bak/plugins/cloud/manager.py:53 msgid "Account unavailable" msgstr "利用できないアカウント" -#: xpack/plugins/cloud/meta.py:9 +#: xpack.bak/plugins/cloud/meta.py:9 msgid "Cloud center" msgstr "クラウドセンター" -#: xpack/plugins/cloud/models.py:34 +#: xpack.bak/plugins/cloud/models.py:34 msgid "Provider" msgstr "プロバイダー" -#: xpack/plugins/cloud/models.py:38 +#: xpack.bak/plugins/cloud/models.py:38 msgid "Validity" msgstr "有効性" -#: xpack/plugins/cloud/models.py:43 +#: xpack.bak/plugins/cloud/models.py:43 msgid "Cloud account" msgstr "クラウドアカウント" -#: xpack/plugins/cloud/models.py:45 +#: xpack.bak/plugins/cloud/models.py:45 msgid "Test cloud account" msgstr "クラウドアカウントのテスト" -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 +#: xpack.bak/plugins/cloud/models.py:92 +#: xpack.bak/plugins/cloud/serializers/task.py:151 msgid "Regions" msgstr "リージョン" -#: xpack/plugins/cloud/models.py:95 +#: xpack.bak/plugins/cloud/models.py:95 msgid "Hostname strategy" msgstr "ホスト名戦略" -#: xpack/plugins/cloud/models.py:100 -#: xpack/plugins/cloud/serializers/task.py:154 +#: xpack.bak/plugins/cloud/models.py:100 +#: xpack.bak/plugins/cloud/serializers/task.py:154 msgid "IP network segment group" msgstr "IPネットワークセグメントグループ" -#: xpack/plugins/cloud/models.py:103 -#: xpack/plugins/cloud/serializers/task.py:159 +#: xpack.bak/plugins/cloud/models.py:103 +#: xpack.bak/plugins/cloud/serializers/task.py:159 msgid "Sync IP type" msgstr "同期IPタイプ" -#: xpack/plugins/cloud/models.py:106 -#: xpack/plugins/cloud/serializers/task.py:177 +#: xpack.bak/plugins/cloud/models.py:106 +#: xpack.bak/plugins/cloud/serializers/task.py:177 msgid "Always update" msgstr "常に更新" -#: xpack/plugins/cloud/models.py:112 +#: xpack.bak/plugins/cloud/models.py:112 msgid "Date last sync" msgstr "最終同期日" -#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 -#: xpack/plugins/cloud/models.py:337 +#: xpack.bak/plugins/cloud/models.py:115 xpack.bak/plugins/cloud/models.py:313 +#: xpack.bak/plugins/cloud/models.py:337 msgid "Strategy" msgstr "戦略" -#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 +#: xpack.bak/plugins/cloud/models.py:120 xpack.bak/plugins/cloud/models.py:197 msgid "Sync instance task" msgstr "インスタンスの同期タスク" -#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 +#: xpack.bak/plugins/cloud/models.py:208 xpack.bak/plugins/cloud/models.py:260 msgid "Date sync" msgstr "日付の同期" -#: xpack/plugins/cloud/models.py:212 +#: xpack.bak/plugins/cloud/models.py:212 msgid "Sync instance snapshot" msgstr "インスタンススナップショットの同期" -#: xpack/plugins/cloud/models.py:216 +#: xpack.bak/plugins/cloud/models.py:216 msgid "Sync instance task execution" msgstr "インスタンスタスクの同期実行" -#: xpack/plugins/cloud/models.py:240 +#: xpack.bak/plugins/cloud/models.py:240 msgid "Sync task" msgstr "同期タスク" -#: xpack/plugins/cloud/models.py:244 +#: xpack.bak/plugins/cloud/models.py:244 msgid "Sync instance task history" msgstr "インスタンスタスク履歴の同期" -#: xpack/plugins/cloud/models.py:247 +#: xpack.bak/plugins/cloud/models.py:247 msgid "Instance" msgstr "インスタンス" -#: xpack/plugins/cloud/models.py:264 +#: xpack.bak/plugins/cloud/models.py:264 msgid "Sync instance detail" msgstr "同期インスタンスの詳細" -#: xpack/plugins/cloud/models.py:281 +#: xpack.bak/plugins/cloud/models.py:281 msgid "Task strategy" msgstr "ミッション戦略です" -#: xpack/plugins/cloud/models.py:285 +#: xpack.bak/plugins/cloud/models.py:285 msgid "Exact" msgstr "" -#: xpack/plugins/cloud/models.py:286 +#: xpack.bak/plugins/cloud/models.py:286 msgid "Not" msgstr "否" -#: xpack/plugins/cloud/models.py:287 +#: xpack.bak/plugins/cloud/models.py:287 msgid "In" msgstr "イン" -#: xpack/plugins/cloud/models.py:288 +#: xpack.bak/plugins/cloud/models.py:288 msgid "Contains" msgstr "含む" -#: xpack/plugins/cloud/models.py:289 +#: xpack.bak/plugins/cloud/models.py:289 msgid "Startswith" msgstr "始まる" -#: xpack/plugins/cloud/models.py:290 +#: xpack.bak/plugins/cloud/models.py:290 msgid "Endswith" msgstr "終わる" -#: xpack/plugins/cloud/models.py:296 +#: xpack.bak/plugins/cloud/models.py:296 msgid "Instance platform" msgstr "インスタンス名" -#: xpack/plugins/cloud/models.py:297 +#: xpack.bak/plugins/cloud/models.py:297 msgid "Instance address" msgstr "インスタンスアドレス" -#: xpack/plugins/cloud/models.py:304 +#: xpack.bak/plugins/cloud/models.py:304 msgid "Rule attr" msgstr "ルール属性" -#: xpack/plugins/cloud/models.py:308 +#: xpack.bak/plugins/cloud/models.py:308 msgid "Rule match" msgstr "ルール一致" -#: xpack/plugins/cloud/models.py:310 +#: xpack.bak/plugins/cloud/models.py:310 msgid "Rule value" msgstr "ルール値" -#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 +#: xpack.bak/plugins/cloud/models.py:317 +#: xpack.bak/plugins/cloud/serializers/task.py:70 msgid "Strategy rule" msgstr "戦略ルール" -#: xpack/plugins/cloud/models.py:332 +#: xpack.bak/plugins/cloud/models.py:332 msgid "Action attr" msgstr "アクション属性" -#: xpack/plugins/cloud/models.py:334 +#: xpack.bak/plugins/cloud/models.py:334 msgid "Action value" msgstr "アクション値" -#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 +#: xpack.bak/plugins/cloud/models.py:341 +#: xpack.bak/plugins/cloud/serializers/task.py:73 msgid "Strategy action" msgstr "戦略アクション" -#: xpack/plugins/cloud/providers/aws_international.py:18 +#: xpack.bak/plugins/cloud/providers/aws_international.py:18 msgid "China (Beijing)" msgstr "中国 (北京)" -#: xpack/plugins/cloud/providers/aws_international.py:19 +#: xpack.bak/plugins/cloud/providers/aws_international.py:19 msgid "China (Ningxia)" msgstr "中国 (寧夏)" -#: xpack/plugins/cloud/providers/aws_international.py:22 +#: xpack.bak/plugins/cloud/providers/aws_international.py:22 msgid "US East (Ohio)" msgstr "米国東部 (オハイオ州)" -#: xpack/plugins/cloud/providers/aws_international.py:23 +#: xpack.bak/plugins/cloud/providers/aws_international.py:23 msgid "US East (N. Virginia)" msgstr "米国東部 (N. バージニア州)" -#: xpack/plugins/cloud/providers/aws_international.py:24 +#: xpack.bak/plugins/cloud/providers/aws_international.py:24 msgid "US West (N. California)" msgstr "米国西部 (N. カリフォルニア州)" -#: xpack/plugins/cloud/providers/aws_international.py:25 +#: xpack.bak/plugins/cloud/providers/aws_international.py:25 msgid "US West (Oregon)" msgstr "米国西部 (オレゴン州)" -#: xpack/plugins/cloud/providers/aws_international.py:26 +#: xpack.bak/plugins/cloud/providers/aws_international.py:26 msgid "Africa (Cape Town)" msgstr "アフリカ (ケープタウン)" -#: xpack/plugins/cloud/providers/aws_international.py:27 +#: xpack.bak/plugins/cloud/providers/aws_international.py:27 msgid "Asia Pacific (Hong Kong)" msgstr "アジアパシフィック (香港)" -#: xpack/plugins/cloud/providers/aws_international.py:28 +#: xpack.bak/plugins/cloud/providers/aws_international.py:28 msgid "Asia Pacific (Mumbai)" msgstr "アジア太平洋 (ムンバイ)" -#: xpack/plugins/cloud/providers/aws_international.py:29 +#: xpack.bak/plugins/cloud/providers/aws_international.py:29 msgid "Asia Pacific (Osaka-Local)" msgstr "アジアパシフィック (大阪-ローカル)" -#: xpack/plugins/cloud/providers/aws_international.py:30 +#: xpack.bak/plugins/cloud/providers/aws_international.py:30 msgid "Asia Pacific (Seoul)" msgstr "アジア太平洋地域 (ソウル)" -#: xpack/plugins/cloud/providers/aws_international.py:31 +#: xpack.bak/plugins/cloud/providers/aws_international.py:31 msgid "Asia Pacific (Singapore)" msgstr "アジア太平洋 (シンガポール)" -#: xpack/plugins/cloud/providers/aws_international.py:32 +#: xpack.bak/plugins/cloud/providers/aws_international.py:32 msgid "Asia Pacific (Sydney)" msgstr "アジア太平洋 (シドニー)" -#: xpack/plugins/cloud/providers/aws_international.py:33 +#: xpack.bak/plugins/cloud/providers/aws_international.py:33 msgid "Asia Pacific (Tokyo)" msgstr "アジアパシフィック (東京)" -#: xpack/plugins/cloud/providers/aws_international.py:34 +#: xpack.bak/plugins/cloud/providers/aws_international.py:34 msgid "Canada (Central)" msgstr "カナダ (中央)" -#: xpack/plugins/cloud/providers/aws_international.py:35 +#: xpack.bak/plugins/cloud/providers/aws_international.py:35 msgid "Europe (Frankfurt)" msgstr "ヨーロッパ (フランクフルト)" -#: xpack/plugins/cloud/providers/aws_international.py:36 +#: xpack.bak/plugins/cloud/providers/aws_international.py:36 msgid "Europe (Ireland)" msgstr "ヨーロッパ (アイルランド)" -#: xpack/plugins/cloud/providers/aws_international.py:37 +#: xpack.bak/plugins/cloud/providers/aws_international.py:37 msgid "Europe (London)" msgstr "ヨーロッパ (ロンドン)" -#: xpack/plugins/cloud/providers/aws_international.py:38 +#: xpack.bak/plugins/cloud/providers/aws_international.py:38 msgid "Europe (Milan)" msgstr "ヨーロッパ (ミラノ)" -#: xpack/plugins/cloud/providers/aws_international.py:39 +#: xpack.bak/plugins/cloud/providers/aws_international.py:39 msgid "Europe (Paris)" msgstr "ヨーロッパ (パリ)" -#: xpack/plugins/cloud/providers/aws_international.py:40 +#: xpack.bak/plugins/cloud/providers/aws_international.py:40 msgid "Europe (Stockholm)" msgstr "ヨーロッパ (ストックホルム)" -#: xpack/plugins/cloud/providers/aws_international.py:41 +#: xpack.bak/plugins/cloud/providers/aws_international.py:41 msgid "Middle East (Bahrain)" msgstr "中东 (バーレーン)" -#: xpack/plugins/cloud/providers/aws_international.py:42 +#: xpack.bak/plugins/cloud/providers/aws_international.py:42 msgid "South America (São Paulo)" msgstr "南米 (サンパウロ)" -#: xpack/plugins/cloud/providers/baiducloud.py:54 -#: xpack/plugins/cloud/providers/jdcloud.py:125 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:54 +#: xpack.bak/plugins/cloud/providers/jdcloud.py:125 msgid "CN North-Beijing" msgstr "華北-北京" -#: xpack/plugins/cloud/providers/baiducloud.py:55 -#: xpack/plugins/cloud/providers/huaweicloud.py:42 -#: xpack/plugins/cloud/providers/jdcloud.py:128 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:55 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:42 +#: xpack.bak/plugins/cloud/providers/jdcloud.py:128 msgid "CN South-Guangzhou" msgstr "華南-広州" -#: xpack/plugins/cloud/providers/baiducloud.py:56 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:56 msgid "CN East-Suzhou" msgstr "華東-蘇州" -#: xpack/plugins/cloud/providers/baiducloud.py:57 -#: xpack/plugins/cloud/providers/huaweicloud.py:49 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:57 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:49 msgid "CN-Hong Kong" msgstr "中国-香港" -#: xpack/plugins/cloud/providers/baiducloud.py:58 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:58 msgid "CN Center-Wuhan" msgstr "華中-武漢" -#: xpack/plugins/cloud/providers/baiducloud.py:59 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:59 msgid "CN North-Baoding" msgstr "華北-保定" -#: xpack/plugins/cloud/providers/baiducloud.py:60 -#: xpack/plugins/cloud/providers/jdcloud.py:127 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:60 +#: xpack.bak/plugins/cloud/providers/jdcloud.py:127 msgid "CN East-Shanghai" msgstr "華東-上海" -#: xpack/plugins/cloud/providers/baiducloud.py:61 -#: xpack/plugins/cloud/providers/huaweicloud.py:51 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:61 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:51 msgid "AP-Singapore" msgstr "アジア太平洋-シンガポール" -#: xpack/plugins/cloud/providers/huaweicloud.py:39 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:39 msgid "CN North-Beijing1" msgstr "華北-北京1" -#: xpack/plugins/cloud/providers/huaweicloud.py:40 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:40 msgid "CN North-Beijing4" msgstr "華北-北京4" -#: xpack/plugins/cloud/providers/huaweicloud.py:41 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:41 msgid "CN North-Ulanqab1" msgstr "華北-ウランチャブ一" -#: xpack/plugins/cloud/providers/huaweicloud.py:43 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:43 msgid "CN South-Shenzhen" msgstr "華南-広州" -#: xpack/plugins/cloud/providers/huaweicloud.py:44 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:44 msgid "CN South-Guangzhou-InvitationOnly" msgstr "華南-広州-友好ユーザー環境" -#: xpack/plugins/cloud/providers/huaweicloud.py:45 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:45 msgid "CN East-Shanghai2" msgstr "華東-上海2" -#: xpack/plugins/cloud/providers/huaweicloud.py:46 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:46 msgid "CN East-Shanghai1" msgstr "華東-上海1" -#: xpack/plugins/cloud/providers/huaweicloud.py:48 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:48 msgid "CN Southwest-Guiyang1" msgstr "南西-貴陽1" -#: xpack/plugins/cloud/providers/huaweicloud.py:50 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:50 msgid "AP-Bangkok" msgstr "アジア太平洋-バンコク" -#: xpack/plugins/cloud/providers/huaweicloud.py:53 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:53 msgid "AF-Johannesburg" msgstr "アフリカ-ヨハネスブルク" -#: xpack/plugins/cloud/providers/huaweicloud.py:54 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:54 msgid "LA-Mexico City1" msgstr "LA-メキシコCity1" -#: xpack/plugins/cloud/providers/huaweicloud.py:55 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:55 msgid "LA-Santiago" msgstr "ラテンアメリカ-サンディエゴ" -#: xpack/plugins/cloud/providers/huaweicloud.py:56 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:56 msgid "LA-Sao Paulo1" msgstr "ラミー・サンパウロ1" -#: xpack/plugins/cloud/providers/huaweicloud.py:58 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:58 msgid "TR-Istanbul" msgstr "" -#: xpack/plugins/cloud/providers/jdcloud.py:126 +#: xpack.bak/plugins/cloud/providers/jdcloud.py:126 msgid "CN East-Suqian" msgstr "華東-宿遷" -#: xpack/plugins/cloud/serializers/account.py:64 +#: xpack.bak/plugins/cloud/serializers/account.py:64 msgid "Validity display" msgstr "有効表示" -#: xpack/plugins/cloud/serializers/account.py:65 +#: xpack.bak/plugins/cloud/serializers/account.py:65 msgid "Provider display" msgstr "プロバイダ表示" -#: xpack/plugins/cloud/serializers/account_attrs.py:35 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:35 msgid "Client ID" msgstr "クライアントID" -#: xpack/plugins/cloud/serializers/account_attrs.py:41 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:41 msgid "Tenant ID" msgstr "テナントID" -#: xpack/plugins/cloud/serializers/account_attrs.py:44 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:44 msgid "Subscription ID" msgstr "サブスクリプションID" -#: xpack/plugins/cloud/serializers/account_attrs.py:98 -#: xpack/plugins/cloud/serializers/account_attrs.py:103 -#: xpack/plugins/cloud/serializers/account_attrs.py:119 -#: xpack/plugins/cloud/serializers/account_attrs.py:149 -#: xpack/plugins/cloud/serializers/account_attrs.py:199 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:98 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:103 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:119 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:149 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:199 msgid "API Endpoint" msgstr "APIエンドポイント" -#: xpack/plugins/cloud/serializers/account_attrs.py:109 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:109 msgid "Auth url" msgstr "認証アドレス" -#: xpack/plugins/cloud/serializers/account_attrs.py:110 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:110 msgid "eg: http://openstack.example.com:5000/v3" msgstr "例えば: http://openstack.example.com:5000/v3" -#: xpack/plugins/cloud/serializers/account_attrs.py:113 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:113 msgid "User domain" msgstr "ユーザードメイン" -#: xpack/plugins/cloud/serializers/account_attrs.py:120 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:120 msgid "Cert File" msgstr "証明書ファイル" -#: xpack/plugins/cloud/serializers/account_attrs.py:121 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:121 msgid "Key File" msgstr "キーファイル" -#: xpack/plugins/cloud/serializers/account_attrs.py:137 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:137 msgid "Service account key" msgstr "サービスアカウントキー" -#: xpack/plugins/cloud/serializers/account_attrs.py:138 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:138 msgid "The file is in JSON format" msgstr "ファイルはJSON形式です。" -#: xpack/plugins/cloud/serializers/account_attrs.py:156 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:156 msgid "IP address invalid `{}`, {}" msgstr "IPアドレスが無効: '{}', {}" -#: xpack/plugins/cloud/serializers/account_attrs.py:172 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:172 msgid "Such as: 192.168.1.0/24, 10.0.0.0-10.0.0.255" msgstr "例:192.168.1.0/24、10.0.0.0.0-10.0.0.255" -#: xpack/plugins/cloud/serializers/account_attrs.py:175 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:175 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -8414,27 +8429,27 @@ msgstr "" "実行されると、有効な IP アドレスのみが同期されます。
ポートが0の場合、す" "べてのIPアドレスが有効です。" -#: xpack/plugins/cloud/serializers/account_attrs.py:183 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:183 msgid "Hostname prefix" msgstr "ホスト名プレフィックス" -#: xpack/plugins/cloud/serializers/account_attrs.py:186 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:186 msgid "IP segment" msgstr "IP セグメント" -#: xpack/plugins/cloud/serializers/account_attrs.py:190 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:190 msgid "Test port" msgstr "テストポート" -#: xpack/plugins/cloud/serializers/account_attrs.py:193 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:193 msgid "Test timeout" msgstr "テストタイムアウト" -#: xpack/plugins/cloud/serializers/account_attrs.py:209 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:209 msgid "Project" msgstr "project" -#: xpack/plugins/cloud/serializers/task.py:143 +#: xpack.bak/plugins/cloud/serializers/task.py:143 msgid "" "Only instances matching the IP range will be synced.
If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8448,83 +8463,84 @@ msgstr "" "ドレスをランダムに一致させることを意味します。
例: " "192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack/plugins/cloud/serializers/task.py:149 +#: xpack.bak/plugins/cloud/serializers/task.py:149 msgid "History count" msgstr "実行回数" -#: xpack/plugins/cloud/serializers/task.py:150 +#: xpack.bak/plugins/cloud/serializers/task.py:150 msgid "Instance count" msgstr "インスタンス数" -#: xpack/plugins/cloud/tasks.py:27 +#: xpack.bak/plugins/cloud/tasks.py:27 msgid "Run sync instance task" msgstr "同期インスタンス タスクを実行する" -#: xpack/plugins/cloud/tasks.py:41 +#: xpack.bak/plugins/cloud/tasks.py:41 msgid "Period clean sync instance task execution" msgstr "同期インスタンス タスクの実行記録を定期的にクリアする" -#: xpack/plugins/interface/api.py:52 +#: xpack.bak/plugins/interface/api.py:52 msgid "Restore default successfully." msgstr "デフォルトの復元に成功しました。" -#: xpack/plugins/interface/meta.py:9 +#: xpack.bak/plugins/interface/meta.py:9 msgid "Interface settings" msgstr "インターフェイスの設定" -#: xpack/plugins/interface/models.py:23 +#: xpack.bak/plugins/interface/models.py:23 msgid "Title of login page" msgstr "ログインページのタイトル" -#: xpack/plugins/interface/models.py:27 +#: xpack.bak/plugins/interface/models.py:27 msgid "Image of login page" msgstr "ログインページのイメージ" -#: xpack/plugins/interface/models.py:31 +#: xpack.bak/plugins/interface/models.py:31 msgid "Website icon" msgstr "ウェブサイトのアイコン" -#: xpack/plugins/interface/models.py:35 +#: xpack.bak/plugins/interface/models.py:35 msgid "Logo of management page" msgstr "管理ページのロゴ" -#: xpack/plugins/interface/models.py:39 +#: xpack.bak/plugins/interface/models.py:39 msgid "Logo of logout page" msgstr "ログアウトページのロゴ" -#: xpack/plugins/interface/models.py:41 +#: xpack.bak/plugins/interface/models.py:41 msgid "Theme" msgstr "テーマ" -#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 +#: xpack.bak/plugins/interface/models.py:44 +#: xpack.bak/plugins/interface/models.py:85 msgid "Interface setting" msgstr "インターフェイスの設定" -#: xpack/plugins/license/api.py:52 +#: xpack.bak/plugins/license/api.py:52 msgid "License import successfully" msgstr "ライセンスのインポートに成功" -#: xpack/plugins/license/api.py:53 +#: xpack.bak/plugins/license/api.py:53 msgid "License is invalid" msgstr "ライセンスが無効です" -#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 +#: xpack.bak/plugins/license/meta.py:10 xpack.bak/plugins/license/models.py:140 msgid "License" msgstr "ライセンス" -#: xpack/plugins/license/models.py:80 +#: xpack.bak/plugins/license/models.py:80 msgid "Basic edition" msgstr "エンタープライズ基本版" -#: xpack/plugins/license/models.py:82 +#: xpack.bak/plugins/license/models.py:82 msgid "Standard edition" msgstr "エンタープライズ標準版" -#: xpack/plugins/license/models.py:84 +#: xpack.bak/plugins/license/models.py:84 msgid "Professional edition" msgstr "エンタープライズプロフェッショナル版" -#: xpack/plugins/license/models.py:86 +#: xpack.bak/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 9bd900805..4345e5524 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: 2023-09-25 16:22+0800\n" +"POT-Creation-Date: 2023-10-07 13:50+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -30,7 +30,7 @@ msgstr "参数 'action' 必须是 [{}]" #: users/forms/profile.py:22 users/serializers/user.py:105 #: users/templates/users/_msg_user_created.html:13 #: users/templates/users/user_password_verify.html:18 -#: xpack/plugins/cloud/serializers/account_attrs.py:28 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:28 msgid "Password" msgstr "密码" @@ -70,7 +70,7 @@ msgstr "同名账号" msgid "Anonymous account" msgstr "匿名账号" -#: accounts/const/account.py:25 users/models/user.py:741 +#: accounts/const/account.py:25 users/models/user.py:738 msgid "Local" msgstr "数据库" @@ -95,7 +95,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:155 audits/const.py:55 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 +#: ops/const.py:74 terminal/const.py:77 xpack.bak/plugins/cloud/const.py:43 msgid "Failed" msgstr "失败" @@ -231,14 +231,15 @@ msgstr "用户 %s 查看/导出 了密码" #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 +#: tickets/models/ticket/apply_asset.py:16 +#: xpack.bak/plugins/cloud/models.py:253 msgid "Asset" msgstr "资产" #: accounts/models/account.py:52 accounts/models/template.py:15 #: accounts/serializers/account/account.py:216 #: accounts/serializers/account/account.py:264 -#: accounts/serializers/account/template.py:25 +#: accounts/serializers/account/template.py:27 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" msgstr "切换自" @@ -250,7 +251,7 @@ msgid "Version" msgstr "版本" #: accounts/models/account.py:56 accounts/serializers/account/account.py:211 -#: users/models/user.py:843 +#: users/models/user.py:840 msgid "Source" msgstr "来源" @@ -267,7 +268,8 @@ msgstr "来源 ID" #: terminal/backends/command/models.py:18 terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 -#: tickets/models/ticket/command_confirm.py:13 xpack/plugins/cloud/models.py:89 +#: tickets/models/ticket/command_confirm.py:13 +#: xpack.bak/plugins/cloud/models.py:89 msgid "Account" msgstr "账号" @@ -334,7 +336,7 @@ msgid "Trigger mode" msgstr "触发模式" #: accounts/models/automations/backup_account.py:105 audits/models.py:197 -#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 +#: terminal/models/session/sharing.py:125 xpack.bak/plugins/cloud/models.py:205 msgid "Reason" msgstr "原因" @@ -447,8 +449,8 @@ msgstr "最后登录日期" #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: users/forms/profile.py:32 users/forms/profile.py:115 -#: users/models/user.py:793 users/templates/users/_msg_user_created.html:12 -#: xpack/plugins/cloud/serializers/account_attrs.py:26 +#: users/models/user.py:790 users/templates/users/_msg_user_created.html:12 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" msgstr "用户名" @@ -511,7 +513,7 @@ msgstr "密钥" msgid "Secret strategy" msgstr "密文策略" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:24 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "密码规则" @@ -536,8 +538,9 @@ msgstr "密码规则" #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 users/forms/profile.py:33 #: users/models/group.py:13 users/models/preference.py:11 -#: users/models/user.py:795 xpack/plugins/cloud/models.py:32 -#: xpack/plugins/cloud/models.py:273 xpack/plugins/cloud/serializers/task.py:68 +#: users/models/user.py:792 xpack.bak/plugins/cloud/models.py:32 +#: xpack.bak/plugins/cloud/models.py:273 +#: xpack.bak/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名称" @@ -566,7 +569,7 @@ msgstr "系统平台" msgid "Push params" msgstr "账号推送参数" -#: accounts/models/template.py:25 xpack/plugins/cloud/models.py:325 +#: accounts/models/template.py:25 xpack.bak/plugins/cloud/models.py:325 msgid "Account template" msgstr "账号模版" @@ -746,8 +749,8 @@ msgstr "ID" #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:993 -#: users/models/user.py:1029 users/serializers/group.py:18 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:990 +#: users/models/user.py:1026 users/serializers/group.py:18 msgid "User" msgstr "用户" @@ -795,35 +798,35 @@ msgstr "" "提示: 如果认证时不需要用户名,可填写为 null, 如果是 AD 账号,格式为 " "username@domain" -#: accounts/serializers/account/template.py:11 +#: accounts/serializers/account/template.py:13 msgid "Password length" msgstr "密码长度" -#: accounts/serializers/account/template.py:12 +#: accounts/serializers/account/template.py:14 msgid "Lowercase" msgstr "小写字母" -#: accounts/serializers/account/template.py:13 +#: accounts/serializers/account/template.py:15 msgid "Uppercase" msgstr "大写字母" -#: accounts/serializers/account/template.py:14 +#: accounts/serializers/account/template.py:16 msgid "Digit" msgstr "数字" -#: accounts/serializers/account/template.py:15 +#: accounts/serializers/account/template.py:17 msgid "Special symbol" msgstr "特殊字符" -#: accounts/serializers/account/template.py:36 +#: accounts/serializers/account/template.py:38 msgid "Secret generation strategy for account creation" msgstr "密码生成策略,用于账号创建时,设置密码" -#: accounts/serializers/account/template.py:37 +#: accounts/serializers/account/template.py:39 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:40 +#: accounts/serializers/account/template.py:42 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -838,8 +841,8 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:831 -#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 +#: tickets/models/ticket/general.py:297 users/models/user.py:828 +#: xpack.bak/plugins/cloud/models.py:39 xpack.bak/plugins/cloud/models.py:109 msgid "Comment" msgstr "备注" @@ -985,13 +988,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:97 -#: xpack/plugins/cloud/models.py:275 +#: xpack.bak/plugins/cloud/models.py:275 msgid "Priority" msgstr "优先级" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack/plugins/cloud/models.py:276 +#: xpack.bak/plugins/cloud/models.py:276 msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" @@ -1028,12 +1031,12 @@ msgid "Command" msgstr "命令" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack/plugins/cloud/models.py:291 +#: xpack.bak/plugins/cloud/models.py:291 msgid "Regex" msgstr "正则表达式" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/feature.py:17 xpack/plugins/license/models.py:30 +#: settings/serializers/feature.py:17 xpack.bak/plugins/license/models.py:30 msgid "Content" msgstr "内容" @@ -1128,7 +1131,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "所有复核人都不属于组织 `{}`" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:137 +#: xpack.bak/plugins/cloud/serializers/task.py:137 msgid "IP address invalid: `{}`" msgstr "IP 地址无效: `{}`" @@ -1185,8 +1188,8 @@ msgstr "谢谢" msgid "Applications" msgstr "应用管理" -#: applications/models.py:16 xpack/plugins/cloud/models.py:37 -#: xpack/plugins/cloud/serializers/account.py:63 +#: applications/models.py:16 xpack.bak/plugins/cloud/models.py:37 +#: xpack.bak/plugins/cloud/serializers/account.py:63 msgid "Attrs" msgstr "属性" @@ -1249,7 +1252,7 @@ msgid "Unable to connect to port {port} on {address}" msgstr "无法连接到 {port} 上的端口 {address}" #: assets/automations/ping_gateway/manager.py:58 -#: authentication/middleware.py:92 xpack/plugins/cloud/providers/fc.py:47 +#: authentication/middleware.py:92 xpack.bak/plugins/cloud/providers/fc.py:47 msgid "Authentication failed" msgstr "认证失败" @@ -1301,7 +1304,7 @@ msgstr "脚本" #: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:67 #: settings/serializers/feature.py:47 terminal/models/component/endpoint.py:13 #: terminal/serializers/applet.py:17 -#: xpack/plugins/cloud/serializers/account_attrs.py:72 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:72 msgid "Host" msgstr "主机" @@ -1501,18 +1504,18 @@ msgstr "SSH公钥" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:261 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:227 users/models/user.py:1030 +#: ops/models/job.py:227 users/models/user.py:1027 msgid "Date created" msgstr "创建日期" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:852 +#: common/db/models.py:35 users/models/user.py:849 msgid "Date updated" msgstr "更新日期" #: assets/models/_user.py:30 assets/models/cmd_filter.py:44 #: assets/models/cmd_filter.py:91 assets/models/group.py:18 -#: common/db/models.py:32 users/models/user.py:838 +#: common/db/models.py:32 users/models/user.py:835 #: users/serializers/group.py:29 msgid "Created by" msgstr "创建者" @@ -1590,7 +1593,7 @@ msgstr "云服务" #: assets/models/asset/common.py:92 assets/models/platform.py:16 #: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:68 -#: xpack/plugins/cloud/serializers/account_attrs.py:73 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:73 msgid "Port" msgstr "端口" @@ -1601,13 +1604,15 @@ msgstr "地址" #: assets/models/asset/common.py:151 assets/models/platform.py:119 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:115 -#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 +#: perms/serializers/user_permission.py:24 +#: xpack.bak/plugins/cloud/models.py:321 msgid "Platform" msgstr "系统平台" #: assets/models/asset/common.py:153 assets/models/domain.py:21 #: authentication/serializers/connect_token_secret.py:133 -#: perms/serializers/user_permission.py:29 xpack/plugins/cloud/models.py:323 +#: perms/serializers/user_permission.py:29 +#: xpack.bak/plugins/cloud/models.py:323 msgid "Domain" msgstr "网域" @@ -1683,8 +1688,8 @@ msgstr "资产自动化任务" #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:115 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 -#: xpack/plugins/cloud/models.py:257 +#: tickets/serializers/ticket/ticket.py:20 +#: xpack.bak/plugins/cloud/models.py:201 xpack.bak/plugins/cloud/models.py:257 msgid "Status" msgstr "状态" @@ -1706,7 +1711,7 @@ msgstr "校验日期" #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:61 #: perms/serializers/permission.py:32 users/models/group.py:25 -#: users/models/user.py:801 +#: users/models/user.py:798 msgid "User group" msgstr "用户组" @@ -1748,7 +1753,7 @@ msgstr "资产组" #: assets/models/group.py:31 assets/models/platform.py:19 #: assets/serializers/platform.py:114 -#: xpack/plugins/cloud/providers/nutanix.py:30 +#: xpack.bak/plugins/cloud/providers/nutanix.py:30 msgid "Default" msgstr "默认" @@ -1756,7 +1761,7 @@ msgstr "默认" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1015 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1012 msgid "System" msgstr "系统" @@ -1799,7 +1804,8 @@ msgid "Parent key" msgstr "ssh私钥" #: assets/models/node.py:553 perms/serializers/permission.py:35 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 +#: tickets/models/ticket/apply_asset.py:14 +#: xpack.bak/plugins/cloud/models.py:322 msgid "Node" msgstr "节点" @@ -1939,8 +1945,9 @@ msgstr "资产中批量更新平台,不符合平台类型跳过的资产" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:134 #: authentication/serializers/connect_token_secret.py:29 #: authentication/serializers/connect_token_secret.py:72 -#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:324 -#: xpack/plugins/cloud/serializers/task.py:31 +#: perms/serializers/user_permission.py:25 +#: xpack.bak/plugins/cloud/models.py:324 +#: xpack.bak/plugins/cloud/serializers/task.py:31 msgid "Protocols" msgstr "协议组" @@ -1973,9 +1980,9 @@ msgstr "默认数据库" #: assets/serializers/asset/database.py:28 common/db/fields.py:570 #: common/db/fields.py:575 common/serializers/fields.py:104 #: tickets/serializers/ticket/common.py:58 -#: xpack/plugins/cloud/serializers/account_attrs.py:56 -#: xpack/plugins/cloud/serializers/account_attrs.py:79 -#: xpack/plugins/cloud/serializers/account_attrs.py:143 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:56 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:79 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:143 msgid "This field is required." msgstr "该字段是必填项。" @@ -2382,7 +2389,7 @@ msgstr "登录 IP" #: audits/models.py:194 audits/serializers.py:49 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:818 +#: users/forms/profile.py:65 users/models/user.py:815 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" @@ -2407,7 +2414,7 @@ msgstr "会话标识" #: audits/models.py:262 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 #: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:836 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:833 msgid "Date expired" msgstr "失效日期" @@ -2458,22 +2465,22 @@ msgstr "认证令牌" #: audits/signal_handlers/login_log.py:40 authentication/notifications.py:73 #: authentication/views/login.py:77 authentication/views/wecom.py:159 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:748 users/models/user.py:853 +#: users/models/user.py:745 users/models/user.py:850 msgid "WeCom" msgstr "企业微信" #: audits/signal_handlers/login_log.py:41 authentication/views/feishu.py:122 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:750 -#: users/models/user.py:855 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:747 +#: users/models/user.py:852 msgid "FeiShu" msgstr "飞书" #: audits/signal_handlers/login_log.py:42 authentication/views/dingtalk.py:159 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:749 -#: users/models/user.py:854 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:746 +#: users/models/user.py:851 msgid "DingTalk" msgstr "钉钉" @@ -2527,22 +2534,22 @@ msgstr "ACL 动作是复核" msgid "Current user not support mfa type: {}" msgstr "当前用户不支持 MFA 类型: {}" -#: authentication/api/password.py:32 terminal/api/session/session.py:300 -#: users/views/profile/reset.py:44 +#: authentication/api/password.py:34 terminal/api/session/session.py:300 +#: users/views/profile/reset.py:61 msgid "User does not exist: {}" msgstr "用户不存在: {}" -#: authentication/api/password.py:32 users/views/profile/reset.py:127 +#: authentication/api/password.py:34 users/views/profile/reset.py:163 msgid "No user matched" msgstr "没有匹配到用户" -#: authentication/api/password.py:36 +#: authentication/api/password.py:38 msgid "" "The user is from {}, please go to the corresponding system to change the " "password" msgstr "用户来自 {} 请去相应系统修改密码" -#: authentication/api/password.py:64 +#: authentication/api/password.py:67 #: authentication/templates/authentication/login.html:355 #: users/templates/users/forgot_password.html:27 #: users/templates/users/forgot_password.html:28 @@ -2783,6 +2790,10 @@ msgstr "没有绑定飞书" msgid "Your password is invalid" msgstr "您的密码无效" +#: authentication/errors/mfa.py:43 +msgid "Please wait for %s seconds before retry" +msgstr "请在 %s 秒后重试" + #: authentication/errors/redirect.py:85 authentication/mixins.py:318 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" @@ -2872,7 +2883,7 @@ msgstr "短信验证码校验失败" #: authentication/serializers/password_mfa.py:24 #: settings/serializers/auth/sms.py:28 users/forms/profile.py:104 #: users/forms/profile.py:109 users/templates/users/forgot_password.html:112 -#: users/views/profile/reset.py:79 +#: users/views/profile/reset.py:98 msgid "SMS" msgstr "短信" @@ -3047,9 +3058,9 @@ msgstr "已过期" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 #: settings/serializers/msg.py:57 users/forms/profile.py:102 -#: users/forms/profile.py:109 users/models/user.py:797 +#: users/forms/profile.py:109 users/models/user.py:794 #: users/templates/users/forgot_password.html:117 -#: users/views/profile/reset.py:73 +#: users/views/profile/reset.py:92 msgid "Email" msgstr "邮箱" @@ -3085,13 +3096,13 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:643 users/serializers/profile.py:92 +#: users/models/user.py:640 users/serializers/profile.py:92 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:644 users/serializers/profile.py:93 +#: users/models/user.py:641 users/serializers/profile.py:93 #: users/templates/users/mfa_setting.html:26 #: users/templates/users/mfa_setting.html:68 msgid "Enable" @@ -3297,7 +3308,7 @@ msgid "Do you want to retry ?" msgstr "是否重试 ?" #: authentication/utils.py:28 common/utils/ip/geoip/utils.py:24 -#: xpack/plugins/cloud/const.py:29 +#: xpack.bak/plugins/cloud/const.py:29 msgid "LAN" msgstr "局域网" @@ -3388,7 +3399,7 @@ msgstr "登录超时,请重新登录" msgid "User email already exists ({})" msgstr "用户邮箱已存在 ({})" -#: authentication/views/login.py:361 +#: authentication/views/login.py:355 msgid "" "Wait for {} confirm, You also can copy link to her/him
\n" " Don't close this page" @@ -3396,15 +3407,15 @@ msgstr "" "等待 {} 确认, 你也可以复制链接发给他/她
\n" " 不要关闭本页面" -#: authentication/views/login.py:366 +#: authentication/views/login.py:360 msgid "No ticket found" msgstr "没有发现工单" -#: authentication/views/login.py:402 +#: authentication/views/login.py:396 msgid "Logout success" msgstr "退出登录成功" -#: authentication/views/login.py:403 +#: authentication/views/login.py:397 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" @@ -3541,7 +3552,7 @@ msgstr "忽略的" msgid "discard time" msgstr "忽略时间" -#: common/db/models.py:33 users/models/user.py:839 +#: common/db/models.py:33 users/models/user.py:836 msgid "Updated by" msgstr "最后更新者" @@ -3640,7 +3651,7 @@ msgstr "阿里云" msgid "Tencent cloud" msgstr "腾讯云" -#: common/sdk/sms/endpoint.py:18 xpack/plugins/cloud/const.py:13 +#: common/sdk/sms/endpoint.py:18 xpack.bak/plugins/cloud/const.py:13 msgid "Huawei Cloud" msgstr "华为云" @@ -3987,7 +3998,7 @@ msgid "Date last run" msgstr "最后运行日期" #: ops/models/base.py:51 ops/models/job.py:224 -#: xpack/plugins/cloud/models.py:199 +#: xpack.bak/plugins/cloud/models.py:199 msgid "Result" msgstr "结果" @@ -4421,7 +4432,7 @@ msgid "Scope" msgstr "范围" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:805 +#: users/models/user.py:802 msgid "Role" msgstr "角色" @@ -4811,7 +4822,7 @@ msgid "Client Id" msgstr "客户端 ID" #: settings/serializers/auth/oauth2.py:33 settings/serializers/auth/oidc.py:22 -#: xpack/plugins/cloud/serializers/account_attrs.py:38 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:38 msgid "Client Secret" msgstr "客户端密钥" @@ -5076,7 +5087,7 @@ msgid "SSO auth key TTL" msgstr "令牌有效期" #: settings/serializers/auth/sso.py:20 -#: xpack/plugins/cloud/serializers/account_attrs.py:193 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:193 msgid "Unit: second" msgstr "单位: 秒" @@ -6141,7 +6152,7 @@ msgstr "不支持批量创建" msgid "Storage is invalid" msgstr "存储无效" -#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:88 +#: terminal/models/applet/applet.py:30 xpack.bak/plugins/license/models.py:88 msgid "Community edition" msgstr "社区版" @@ -6662,16 +6673,16 @@ msgid "Bucket" msgstr "桶名称" #: terminal/serializers/storage.py:32 -#: xpack/plugins/cloud/serializers/account_attrs.py:17 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:17 msgid "Access key id" msgstr "Access key ID(AK)" #: terminal/serializers/storage.py:36 -#: xpack/plugins/cloud/serializers/account_attrs.py:20 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:20 msgid "Access key secret" msgstr "Access key secret(SK)" -#: terminal/serializers/storage.py:67 xpack/plugins/cloud/models.py:250 +#: terminal/serializers/storage.py:67 xpack.bak/plugins/cloud/models.py:250 msgid "Region" msgstr "地域" @@ -7250,8 +7261,8 @@ msgstr "不能和原来的密钥相同" msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/profile.py:173 users/models/user.py:828 -#: xpack/plugins/cloud/serializers/account_attrs.py:203 +#: users/forms/profile.py:173 users/models/user.py:825 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:203 msgid "Public key" msgstr "SSH公钥" @@ -7259,73 +7270,73 @@ msgstr "SSH公钥" msgid "Preference" msgstr "用户设置" -#: users/models/user.py:645 users/serializers/profile.py:94 +#: users/models/user.py:642 users/serializers/profile.py:94 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:807 users/serializers/user.py:172 +#: users/models/user.py:804 users/serializers/user.py:172 msgid "Is service account" msgstr "服务账号" -#: users/models/user.py:809 +#: users/models/user.py:806 msgid "Avatar" msgstr "头像" -#: users/models/user.py:812 +#: users/models/user.py:809 msgid "Wechat" msgstr "微信" -#: users/models/user.py:815 users/serializers/user.py:109 +#: users/models/user.py:812 users/serializers/user.py:109 msgid "Phone" msgstr "手机" -#: users/models/user.py:821 +#: users/models/user.py:818 msgid "OTP secret key" msgstr "OTP 密钥" -#: users/models/user.py:825 -#: xpack/plugins/cloud/serializers/account_attrs.py:206 +#: users/models/user.py:822 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:206 msgid "Private key" msgstr "ssh私钥" -#: users/models/user.py:833 users/serializers/profile.py:125 +#: users/models/user.py:830 users/serializers/profile.py:125 #: users/serializers/user.py:169 msgid "Is first login" msgstr "首次登录" -#: users/models/user.py:847 +#: users/models/user.py:844 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:850 +#: users/models/user.py:847 msgid "Need update password" msgstr "需要更新密码" -#: users/models/user.py:974 +#: users/models/user.py:971 msgid "Can not delete admin user" msgstr "无法删除管理员用户" -#: users/models/user.py:1000 +#: users/models/user.py:997 msgid "Can invite user" msgstr "可以邀请用户" -#: users/models/user.py:1001 +#: users/models/user.py:998 msgid "Can remove user" msgstr "可以移除用户" -#: users/models/user.py:1002 +#: users/models/user.py:999 msgid "Can match user" msgstr "可以匹配用户" -#: users/models/user.py:1011 +#: users/models/user.py:1008 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:1014 +#: users/models/user.py:1011 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/models/user.py:1039 +#: users/models/user.py:1036 msgid "User password history" msgstr "用户密码历史" @@ -7336,7 +7347,7 @@ msgstr "用户密码历史" msgid "Reset password" msgstr "重置密码" -#: users/notifications.py:85 users/views/profile/reset.py:194 +#: users/notifications.py:85 users/views/profile/reset.py:230 msgid "Reset password success" msgstr "重置密码成功" @@ -7732,563 +7743,567 @@ msgstr "MFA(OTP) 禁用成功,返回登录页面" msgid "Password invalid" msgstr "用户名或密码无效" -#: users/views/profile/reset.py:47 +#: users/views/profile/reset.py:64 msgid "" "Non-local users can log in only from third-party platforms and cannot change " "their passwords: {}" msgstr "非本地用户仅允许从第三方平台登录,不支持修改密码: {}" -#: users/views/profile/reset.py:149 users/views/profile/reset.py:160 +#: users/views/profile/reset.py:185 users/views/profile/reset.py:196 msgid "Token invalid or expired" msgstr "令牌错误或失效" -#: users/views/profile/reset.py:165 +#: users/views/profile/reset.py:201 msgid "User auth from {}, go there change password" msgstr "用户认证源来自 {}, 请去相应系统修改密码" -#: users/views/profile/reset.py:172 +#: users/views/profile/reset.py:208 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/views/profile/reset.py:178 +#: users/views/profile/reset.py:214 msgid "* The new password cannot be the last {} passwords" msgstr "* 新密码不能是最近 {} 次的密码" -#: users/views/profile/reset.py:195 +#: users/views/profile/reset.py:231 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: xpack/apps.py:8 +#: xpack.bak/apps.py:8 msgid "XPACK" msgstr "XPack" -#: xpack/plugins/cloud/api.py:56 +#: xpack.bak/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "测试成功" -#: xpack/plugins/cloud/api.py:58 +#: xpack.bak/plugins/cloud/api.py:58 msgid "Test connection failed: {}" msgstr "测试连接失败:{}" -#: xpack/plugins/cloud/const.py:8 +#: xpack.bak/plugins/cloud/const.py:8 msgid "Alibaba Cloud" msgstr "阿里云" -#: xpack/plugins/cloud/const.py:9 +#: xpack.bak/plugins/cloud/const.py:9 msgid "AWS (International)" msgstr "AWS (国际)" -#: xpack/plugins/cloud/const.py:10 +#: xpack.bak/plugins/cloud/const.py:10 msgid "AWS (China)" msgstr "AWS (中国)" -#: xpack/plugins/cloud/const.py:11 +#: xpack.bak/plugins/cloud/const.py:11 msgid "Azure (China)" msgstr "Azure (中国)" -#: xpack/plugins/cloud/const.py:12 +#: xpack.bak/plugins/cloud/const.py:12 msgid "Azure (International)" msgstr "Azure (国际)" -#: xpack/plugins/cloud/const.py:14 +#: xpack.bak/plugins/cloud/const.py:14 msgid "Baidu Cloud" msgstr "百度云" -#: xpack/plugins/cloud/const.py:15 +#: xpack.bak/plugins/cloud/const.py:15 msgid "JD Cloud" msgstr "京东云" -#: xpack/plugins/cloud/const.py:16 +#: xpack.bak/plugins/cloud/const.py:16 msgid "KingSoft Cloud" msgstr "金山云" -#: xpack/plugins/cloud/const.py:17 +#: xpack.bak/plugins/cloud/const.py:17 msgid "Tencent Cloud" msgstr "腾讯云" -#: xpack/plugins/cloud/const.py:18 +#: xpack.bak/plugins/cloud/const.py:18 msgid "Tencent Cloud (Lighthouse)" msgstr "腾讯云(轻量服务器应用)" -#: xpack/plugins/cloud/const.py:19 +#: xpack.bak/plugins/cloud/const.py:19 msgid "Google Cloud Platform" msgstr "谷歌云" -#: xpack/plugins/cloud/const.py:20 +#: xpack.bak/plugins/cloud/const.py:20 msgid "UCloud" msgstr "ucloud" -#: xpack/plugins/cloud/const.py:22 +#: xpack.bak/plugins/cloud/const.py:22 msgid "VMware" msgstr "VMware" -#: xpack/plugins/cloud/const.py:23 xpack/plugins/cloud/providers/nutanix.py:15 +#: xpack.bak/plugins/cloud/const.py:23 +#: xpack.bak/plugins/cloud/providers/nutanix.py:15 msgid "Nutanix" msgstr "Nutanix" -#: xpack/plugins/cloud/const.py:24 +#: xpack.bak/plugins/cloud/const.py:24 msgid "Huawei Private Cloud" msgstr "华为私有云" -#: xpack/plugins/cloud/const.py:25 +#: xpack.bak/plugins/cloud/const.py:25 msgid "Qingyun Private Cloud" msgstr "青云私有云" -#: xpack/plugins/cloud/const.py:26 +#: xpack.bak/plugins/cloud/const.py:26 msgid "CTYun Private Cloud" msgstr "天翼私有云" -#: xpack/plugins/cloud/const.py:27 +#: xpack.bak/plugins/cloud/const.py:27 msgid "OpenStack" msgstr "OpenStack" -#: xpack/plugins/cloud/const.py:28 +#: xpack.bak/plugins/cloud/const.py:28 msgid "Fusion Compute" msgstr "融合计算" -#: xpack/plugins/cloud/const.py:33 +#: xpack.bak/plugins/cloud/const.py:33 msgid "Private IP" msgstr "私有IP" -#: xpack/plugins/cloud/const.py:34 +#: xpack.bak/plugins/cloud/const.py:34 msgid "Public IP" msgstr "公网IP" -#: xpack/plugins/cloud/const.py:38 xpack/plugins/cloud/models.py:295 +#: xpack.bak/plugins/cloud/const.py:38 xpack.bak/plugins/cloud/models.py:295 msgid "Instance name" msgstr "实例名称" -#: xpack/plugins/cloud/const.py:39 +#: xpack.bak/plugins/cloud/const.py:39 msgid "Instance name and Partial IP" msgstr "实例名称和部分IP" -#: xpack/plugins/cloud/const.py:44 +#: xpack.bak/plugins/cloud/const.py:44 msgid "Succeed" msgstr "成功" -#: xpack/plugins/cloud/const.py:48 +#: xpack.bak/plugins/cloud/const.py:48 msgid "Unsync" msgstr "未同步" -#: xpack/plugins/cloud/const.py:49 +#: xpack.bak/plugins/cloud/const.py:49 msgid "New Sync" msgstr "新同步" -#: xpack/plugins/cloud/const.py:50 +#: xpack.bak/plugins/cloud/const.py:50 msgid "Synced" msgstr "已同步" -#: xpack/plugins/cloud/const.py:51 +#: xpack.bak/plugins/cloud/const.py:51 msgid "Released" msgstr "已释放" -#: xpack/plugins/cloud/manager.py:53 +#: xpack.bak/plugins/cloud/manager.py:53 msgid "Account unavailable" msgstr "账号无效" -#: xpack/plugins/cloud/meta.py:9 +#: xpack.bak/plugins/cloud/meta.py:9 msgid "Cloud center" msgstr "云管中心" -#: xpack/plugins/cloud/models.py:34 +#: xpack.bak/plugins/cloud/models.py:34 msgid "Provider" msgstr "云服务商" -#: xpack/plugins/cloud/models.py:38 +#: xpack.bak/plugins/cloud/models.py:38 msgid "Validity" msgstr "有效" -#: xpack/plugins/cloud/models.py:43 +#: xpack.bak/plugins/cloud/models.py:43 msgid "Cloud account" msgstr "云账号" -#: xpack/plugins/cloud/models.py:45 +#: xpack.bak/plugins/cloud/models.py:45 msgid "Test cloud account" msgstr "测试云账号" -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 +#: xpack.bak/plugins/cloud/models.py:92 +#: xpack.bak/plugins/cloud/serializers/task.py:151 msgid "Regions" msgstr "地域" -#: xpack/plugins/cloud/models.py:95 +#: xpack.bak/plugins/cloud/models.py:95 msgid "Hostname strategy" msgstr "主机名策略" -#: xpack/plugins/cloud/models.py:100 -#: xpack/plugins/cloud/serializers/task.py:154 +#: xpack.bak/plugins/cloud/models.py:100 +#: xpack.bak/plugins/cloud/serializers/task.py:154 msgid "IP network segment group" msgstr "IP网段组" -#: xpack/plugins/cloud/models.py:103 -#: xpack/plugins/cloud/serializers/task.py:159 +#: xpack.bak/plugins/cloud/models.py:103 +#: xpack.bak/plugins/cloud/serializers/task.py:159 msgid "Sync IP type" msgstr "同步IP类型" -#: xpack/plugins/cloud/models.py:106 -#: xpack/plugins/cloud/serializers/task.py:177 +#: xpack.bak/plugins/cloud/models.py:106 +#: xpack.bak/plugins/cloud/serializers/task.py:177 msgid "Always update" msgstr "总是更新" -#: xpack/plugins/cloud/models.py:112 +#: xpack.bak/plugins/cloud/models.py:112 msgid "Date last sync" msgstr "最后同步日期" -#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 -#: xpack/plugins/cloud/models.py:337 +#: xpack.bak/plugins/cloud/models.py:115 xpack.bak/plugins/cloud/models.py:313 +#: xpack.bak/plugins/cloud/models.py:337 msgid "Strategy" msgstr "策略" -#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 +#: xpack.bak/plugins/cloud/models.py:120 xpack.bak/plugins/cloud/models.py:197 msgid "Sync instance task" msgstr "同步实例任务" -#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 +#: xpack.bak/plugins/cloud/models.py:208 xpack.bak/plugins/cloud/models.py:260 msgid "Date sync" msgstr "同步日期" -#: xpack/plugins/cloud/models.py:212 +#: xpack.bak/plugins/cloud/models.py:212 msgid "Sync instance snapshot" msgstr "同步实例快照" -#: xpack/plugins/cloud/models.py:216 +#: xpack.bak/plugins/cloud/models.py:216 msgid "Sync instance task execution" msgstr "同步实例任务执行" -#: xpack/plugins/cloud/models.py:240 +#: xpack.bak/plugins/cloud/models.py:240 msgid "Sync task" msgstr "同步任务" -#: xpack/plugins/cloud/models.py:244 +#: xpack.bak/plugins/cloud/models.py:244 msgid "Sync instance task history" msgstr "同步实例任务历史" -#: xpack/plugins/cloud/models.py:247 +#: xpack.bak/plugins/cloud/models.py:247 msgid "Instance" msgstr "实例" -#: xpack/plugins/cloud/models.py:264 +#: xpack.bak/plugins/cloud/models.py:264 msgid "Sync instance detail" msgstr "同步实例详情" -#: xpack/plugins/cloud/models.py:281 +#: xpack.bak/plugins/cloud/models.py:281 msgid "Task strategy" msgstr "任务策略" -#: xpack/plugins/cloud/models.py:285 +#: xpack.bak/plugins/cloud/models.py:285 msgid "Exact" msgstr "" -#: xpack/plugins/cloud/models.py:286 +#: xpack.bak/plugins/cloud/models.py:286 msgid "Not" msgstr "否" -#: xpack/plugins/cloud/models.py:287 +#: xpack.bak/plugins/cloud/models.py:287 msgid "In" msgstr "在..里面" -#: xpack/plugins/cloud/models.py:288 +#: xpack.bak/plugins/cloud/models.py:288 msgid "Contains" msgstr "包含" -#: xpack/plugins/cloud/models.py:289 +#: xpack.bak/plugins/cloud/models.py:289 msgid "Startswith" msgstr "以..开头" -#: xpack/plugins/cloud/models.py:290 +#: xpack.bak/plugins/cloud/models.py:290 msgid "Endswith" msgstr "以..结尾" -#: xpack/plugins/cloud/models.py:296 +#: xpack.bak/plugins/cloud/models.py:296 msgid "Instance platform" msgstr "实例平台" -#: xpack/plugins/cloud/models.py:297 +#: xpack.bak/plugins/cloud/models.py:297 msgid "Instance address" msgstr "实例地址" -#: xpack/plugins/cloud/models.py:304 +#: xpack.bak/plugins/cloud/models.py:304 msgid "Rule attr" msgstr "规则属性" -#: xpack/plugins/cloud/models.py:308 +#: xpack.bak/plugins/cloud/models.py:308 msgid "Rule match" msgstr "规则匹配" -#: xpack/plugins/cloud/models.py:310 +#: xpack.bak/plugins/cloud/models.py:310 msgid "Rule value" msgstr "规则值" -#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 +#: xpack.bak/plugins/cloud/models.py:317 +#: xpack.bak/plugins/cloud/serializers/task.py:70 msgid "Strategy rule" msgstr "条件" -#: xpack/plugins/cloud/models.py:332 +#: xpack.bak/plugins/cloud/models.py:332 msgid "Action attr" msgstr "动作属性" -#: xpack/plugins/cloud/models.py:334 +#: xpack.bak/plugins/cloud/models.py:334 msgid "Action value" msgstr "动作值" -#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 +#: xpack.bak/plugins/cloud/models.py:341 +#: xpack.bak/plugins/cloud/serializers/task.py:73 msgid "Strategy action" msgstr "动作" -#: xpack/plugins/cloud/providers/aws_international.py:18 +#: xpack.bak/plugins/cloud/providers/aws_international.py:18 msgid "China (Beijing)" msgstr "中国 (北京)" -#: xpack/plugins/cloud/providers/aws_international.py:19 +#: xpack.bak/plugins/cloud/providers/aws_international.py:19 msgid "China (Ningxia)" msgstr "中国 (宁夏)" -#: xpack/plugins/cloud/providers/aws_international.py:22 +#: xpack.bak/plugins/cloud/providers/aws_international.py:22 msgid "US East (Ohio)" msgstr "美国东部 (俄亥俄州)" -#: xpack/plugins/cloud/providers/aws_international.py:23 +#: xpack.bak/plugins/cloud/providers/aws_international.py:23 msgid "US East (N. Virginia)" msgstr "美国东部 (弗吉尼亚北部)" -#: xpack/plugins/cloud/providers/aws_international.py:24 +#: xpack.bak/plugins/cloud/providers/aws_international.py:24 msgid "US West (N. California)" msgstr "美国西部 (加利福尼亚北部)" -#: xpack/plugins/cloud/providers/aws_international.py:25 +#: xpack.bak/plugins/cloud/providers/aws_international.py:25 msgid "US West (Oregon)" msgstr "美国西部 (俄勒冈)" -#: xpack/plugins/cloud/providers/aws_international.py:26 +#: xpack.bak/plugins/cloud/providers/aws_international.py:26 msgid "Africa (Cape Town)" msgstr "非洲 (开普敦)" -#: xpack/plugins/cloud/providers/aws_international.py:27 +#: xpack.bak/plugins/cloud/providers/aws_international.py:27 msgid "Asia Pacific (Hong Kong)" msgstr "亚太地区 (香港)" -#: xpack/plugins/cloud/providers/aws_international.py:28 +#: xpack.bak/plugins/cloud/providers/aws_international.py:28 msgid "Asia Pacific (Mumbai)" msgstr "亚太地区 (孟买)" -#: xpack/plugins/cloud/providers/aws_international.py:29 +#: xpack.bak/plugins/cloud/providers/aws_international.py:29 msgid "Asia Pacific (Osaka-Local)" msgstr "亚太区域 (大阪当地)" -#: xpack/plugins/cloud/providers/aws_international.py:30 +#: xpack.bak/plugins/cloud/providers/aws_international.py:30 msgid "Asia Pacific (Seoul)" msgstr "亚太区域 (首尔)" -#: xpack/plugins/cloud/providers/aws_international.py:31 +#: xpack.bak/plugins/cloud/providers/aws_international.py:31 msgid "Asia Pacific (Singapore)" msgstr "亚太区域 (新加坡)" -#: xpack/plugins/cloud/providers/aws_international.py:32 +#: xpack.bak/plugins/cloud/providers/aws_international.py:32 msgid "Asia Pacific (Sydney)" msgstr "亚太区域 (悉尼)" -#: xpack/plugins/cloud/providers/aws_international.py:33 +#: xpack.bak/plugins/cloud/providers/aws_international.py:33 msgid "Asia Pacific (Tokyo)" msgstr "亚太区域 (东京)" -#: xpack/plugins/cloud/providers/aws_international.py:34 +#: xpack.bak/plugins/cloud/providers/aws_international.py:34 msgid "Canada (Central)" msgstr "加拿大 (中部)" -#: xpack/plugins/cloud/providers/aws_international.py:35 +#: xpack.bak/plugins/cloud/providers/aws_international.py:35 msgid "Europe (Frankfurt)" msgstr "欧洲 (法兰克福)" -#: xpack/plugins/cloud/providers/aws_international.py:36 +#: xpack.bak/plugins/cloud/providers/aws_international.py:36 msgid "Europe (Ireland)" msgstr "欧洲 (爱尔兰)" -#: xpack/plugins/cloud/providers/aws_international.py:37 +#: xpack.bak/plugins/cloud/providers/aws_international.py:37 msgid "Europe (London)" msgstr "欧洲 (伦敦)" -#: xpack/plugins/cloud/providers/aws_international.py:38 +#: xpack.bak/plugins/cloud/providers/aws_international.py:38 msgid "Europe (Milan)" msgstr "欧洲 (米兰)" -#: xpack/plugins/cloud/providers/aws_international.py:39 +#: xpack.bak/plugins/cloud/providers/aws_international.py:39 msgid "Europe (Paris)" msgstr "欧洲 (巴黎)" -#: xpack/plugins/cloud/providers/aws_international.py:40 +#: xpack.bak/plugins/cloud/providers/aws_international.py:40 msgid "Europe (Stockholm)" msgstr "欧洲 (斯德哥尔摩)" -#: xpack/plugins/cloud/providers/aws_international.py:41 +#: xpack.bak/plugins/cloud/providers/aws_international.py:41 msgid "Middle East (Bahrain)" msgstr "中东 (巴林)" -#: xpack/plugins/cloud/providers/aws_international.py:42 +#: xpack.bak/plugins/cloud/providers/aws_international.py:42 msgid "South America (São Paulo)" msgstr "南美洲 (圣保罗)" -#: xpack/plugins/cloud/providers/baiducloud.py:54 -#: xpack/plugins/cloud/providers/jdcloud.py:125 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:54 +#: xpack.bak/plugins/cloud/providers/jdcloud.py:125 msgid "CN North-Beijing" msgstr "华北-北京" -#: xpack/plugins/cloud/providers/baiducloud.py:55 -#: xpack/plugins/cloud/providers/huaweicloud.py:42 -#: xpack/plugins/cloud/providers/jdcloud.py:128 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:55 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:42 +#: xpack.bak/plugins/cloud/providers/jdcloud.py:128 msgid "CN South-Guangzhou" msgstr "华南-广州" -#: xpack/plugins/cloud/providers/baiducloud.py:56 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:56 msgid "CN East-Suzhou" msgstr "华东-苏州" -#: xpack/plugins/cloud/providers/baiducloud.py:57 -#: xpack/plugins/cloud/providers/huaweicloud.py:49 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:57 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:49 msgid "CN-Hong Kong" msgstr "中国-香港" -#: xpack/plugins/cloud/providers/baiducloud.py:58 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:58 msgid "CN Center-Wuhan" msgstr "华中-武汉" -#: xpack/plugins/cloud/providers/baiducloud.py:59 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:59 msgid "CN North-Baoding" msgstr "华北-保定" -#: xpack/plugins/cloud/providers/baiducloud.py:60 -#: xpack/plugins/cloud/providers/jdcloud.py:127 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:60 +#: xpack.bak/plugins/cloud/providers/jdcloud.py:127 msgid "CN East-Shanghai" msgstr "华东-上海" -#: xpack/plugins/cloud/providers/baiducloud.py:61 -#: xpack/plugins/cloud/providers/huaweicloud.py:51 +#: xpack.bak/plugins/cloud/providers/baiducloud.py:61 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:51 msgid "AP-Singapore" msgstr "亚太-新加坡" -#: xpack/plugins/cloud/providers/huaweicloud.py:39 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:39 msgid "CN North-Beijing1" msgstr "华北-北京1" -#: xpack/plugins/cloud/providers/huaweicloud.py:40 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:40 msgid "CN North-Beijing4" msgstr "华北-北京4" -#: xpack/plugins/cloud/providers/huaweicloud.py:41 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:41 msgid "CN North-Ulanqab1" msgstr "华北-乌兰察布一" -#: xpack/plugins/cloud/providers/huaweicloud.py:43 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:43 msgid "CN South-Shenzhen" msgstr "华南-广州" -#: xpack/plugins/cloud/providers/huaweicloud.py:44 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:44 msgid "CN South-Guangzhou-InvitationOnly" msgstr "华南-广州-友好用户环境" -#: xpack/plugins/cloud/providers/huaweicloud.py:45 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:45 msgid "CN East-Shanghai2" msgstr "华东-上海2" -#: xpack/plugins/cloud/providers/huaweicloud.py:46 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:46 msgid "CN East-Shanghai1" msgstr "华东-上海1" -#: xpack/plugins/cloud/providers/huaweicloud.py:48 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:48 msgid "CN Southwest-Guiyang1" msgstr "西南-贵阳1" -#: xpack/plugins/cloud/providers/huaweicloud.py:50 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:50 msgid "AP-Bangkok" msgstr "亚太-曼谷" -#: xpack/plugins/cloud/providers/huaweicloud.py:53 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:53 msgid "AF-Johannesburg" msgstr "非洲-约翰内斯堡" -#: xpack/plugins/cloud/providers/huaweicloud.py:54 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:54 msgid "LA-Mexico City1" msgstr "拉美-墨西哥城一" -#: xpack/plugins/cloud/providers/huaweicloud.py:55 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:55 msgid "LA-Santiago" msgstr "拉美-圣地亚哥" -#: xpack/plugins/cloud/providers/huaweicloud.py:56 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:56 msgid "LA-Sao Paulo1" msgstr "拉美-圣保罗一" -#: xpack/plugins/cloud/providers/huaweicloud.py:58 +#: xpack.bak/plugins/cloud/providers/huaweicloud.py:58 msgid "TR-Istanbul" msgstr "" -#: xpack/plugins/cloud/providers/jdcloud.py:126 +#: xpack.bak/plugins/cloud/providers/jdcloud.py:126 msgid "CN East-Suqian" msgstr "华东-宿迁" -#: xpack/plugins/cloud/serializers/account.py:64 +#: xpack.bak/plugins/cloud/serializers/account.py:64 msgid "Validity display" msgstr "有效性显示" -#: xpack/plugins/cloud/serializers/account.py:65 +#: xpack.bak/plugins/cloud/serializers/account.py:65 msgid "Provider display" msgstr "服务商显示" -#: xpack/plugins/cloud/serializers/account_attrs.py:35 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:35 msgid "Client ID" msgstr "客户端 ID" -#: xpack/plugins/cloud/serializers/account_attrs.py:41 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:41 msgid "Tenant ID" msgstr "租户 ID" -#: xpack/plugins/cloud/serializers/account_attrs.py:44 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:44 msgid "Subscription ID" msgstr "订阅 ID" -#: xpack/plugins/cloud/serializers/account_attrs.py:98 -#: xpack/plugins/cloud/serializers/account_attrs.py:103 -#: xpack/plugins/cloud/serializers/account_attrs.py:119 -#: xpack/plugins/cloud/serializers/account_attrs.py:149 -#: xpack/plugins/cloud/serializers/account_attrs.py:199 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:98 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:103 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:119 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:149 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:199 msgid "API Endpoint" msgstr "API 端点" -#: xpack/plugins/cloud/serializers/account_attrs.py:109 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:109 msgid "Auth url" msgstr "认证地址" -#: xpack/plugins/cloud/serializers/account_attrs.py:110 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:110 msgid "eg: http://openstack.example.com:5000/v3" msgstr "如: http://openstack.example.com:5000/v3" -#: xpack/plugins/cloud/serializers/account_attrs.py:113 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:113 msgid "User domain" msgstr "用户域" -#: xpack/plugins/cloud/serializers/account_attrs.py:120 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:120 msgid "Cert File" msgstr "证书文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:121 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:121 msgid "Key File" msgstr "密钥文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:137 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:137 msgid "Service account key" msgstr "服务账号密钥" -#: xpack/plugins/cloud/serializers/account_attrs.py:138 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:138 msgid "The file is in JSON format" msgstr "JSON 格式的文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:156 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:156 msgid "IP address invalid `{}`, {}" msgstr "IP 地址无效: `{}`, {}" -#: xpack/plugins/cloud/serializers/account_attrs.py:172 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:172 msgid "Such as: 192.168.1.0/24, 10.0.0.0-10.0.0.255" msgstr "例: 192.168.1.0/24,10.0.0.0-10.0.0.255" -#: xpack/plugins/cloud/serializers/account_attrs.py:175 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:175 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -8297,27 +8312,27 @@ msgstr "" "端口用来检测 IP 地址的有效性,在同步任务执行时,只会同步有效的 IP 地址。
" "如果端口为 0,则表示所有 IP 地址均有效。" -#: xpack/plugins/cloud/serializers/account_attrs.py:183 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:183 msgid "Hostname prefix" msgstr "主机名前缀" -#: xpack/plugins/cloud/serializers/account_attrs.py:186 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:186 msgid "IP segment" msgstr "IP 网段" -#: xpack/plugins/cloud/serializers/account_attrs.py:190 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:190 msgid "Test port" msgstr "测试端口" -#: xpack/plugins/cloud/serializers/account_attrs.py:193 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:193 msgid "Test timeout" msgstr "测试超时时间" -#: xpack/plugins/cloud/serializers/account_attrs.py:209 +#: xpack.bak/plugins/cloud/serializers/account_attrs.py:209 msgid "Project" msgstr "project" -#: xpack/plugins/cloud/serializers/task.py:143 +#: xpack.bak/plugins/cloud/serializers/task.py:143 msgid "" "Only instances matching the IP range will be synced.
If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8329,83 +8344,84 @@ msgstr "" "到的 IP 地址将被用作创建的资产的 IP。
默认值 * 表示同步所有实例和随机匹配 " "IP 地址。
例如: 192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack/plugins/cloud/serializers/task.py:149 +#: xpack.bak/plugins/cloud/serializers/task.py:149 msgid "History count" msgstr "执行次数" -#: xpack/plugins/cloud/serializers/task.py:150 +#: xpack.bak/plugins/cloud/serializers/task.py:150 msgid "Instance count" msgstr "实例个数" -#: xpack/plugins/cloud/tasks.py:27 +#: xpack.bak/plugins/cloud/tasks.py:27 msgid "Run sync instance task" msgstr "执行同步实例任务" -#: xpack/plugins/cloud/tasks.py:41 +#: xpack.bak/plugins/cloud/tasks.py:41 msgid "Period clean sync instance task execution" msgstr "定期清除同步实例任务执行记录" -#: xpack/plugins/interface/api.py:52 +#: xpack.bak/plugins/interface/api.py:52 msgid "Restore default successfully." msgstr "恢复默认成功!" -#: xpack/plugins/interface/meta.py:9 +#: xpack.bak/plugins/interface/meta.py:9 msgid "Interface settings" msgstr "界面设置" -#: xpack/plugins/interface/models.py:23 +#: xpack.bak/plugins/interface/models.py:23 msgid "Title of login page" msgstr "登录页面标题" -#: xpack/plugins/interface/models.py:27 +#: xpack.bak/plugins/interface/models.py:27 msgid "Image of login page" msgstr "登录页面图片" -#: xpack/plugins/interface/models.py:31 +#: xpack.bak/plugins/interface/models.py:31 msgid "Website icon" msgstr "网站图标" -#: xpack/plugins/interface/models.py:35 +#: xpack.bak/plugins/interface/models.py:35 msgid "Logo of management page" msgstr "管理页面logo" -#: xpack/plugins/interface/models.py:39 +#: xpack.bak/plugins/interface/models.py:39 msgid "Logo of logout page" msgstr "退出页面logo" -#: xpack/plugins/interface/models.py:41 +#: xpack.bak/plugins/interface/models.py:41 msgid "Theme" msgstr "主题" -#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 +#: xpack.bak/plugins/interface/models.py:44 +#: xpack.bak/plugins/interface/models.py:85 msgid "Interface setting" msgstr "界面设置" -#: xpack/plugins/license/api.py:52 +#: xpack.bak/plugins/license/api.py:52 msgid "License import successfully" msgstr "许可证导入成功" -#: xpack/plugins/license/api.py:53 +#: xpack.bak/plugins/license/api.py:53 msgid "License is invalid" msgstr "无效的许可证" -#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 +#: xpack.bak/plugins/license/meta.py:10 xpack.bak/plugins/license/models.py:140 msgid "License" msgstr "许可证" -#: xpack/plugins/license/models.py:80 +#: xpack.bak/plugins/license/models.py:80 msgid "Basic edition" msgstr "企业基础版" -#: xpack/plugins/license/models.py:82 +#: xpack.bak/plugins/license/models.py:82 msgid "Standard edition" msgstr "企业标准版" -#: xpack/plugins/license/models.py:84 +#: xpack.bak/plugins/license/models.py:84 msgid "Professional edition" msgstr "企业专业版" -#: xpack/plugins/license/models.py:86 +#: xpack.bak/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "企业旗舰版" diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 98facb346..94b05b331 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -606,7 +606,8 @@ class TokenMixin: def generate_reset_token(self): token = random_string(50) - self.set_cache(token) + key = self.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token) + cache.set(key, {'id': self.id, 'email': self.email}, 3600) return token @classmethod @@ -626,10 +627,6 @@ class TokenMixin: logger.error(e, exc_info=True) return None - def set_cache(self, token): - key = self.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token) - cache.set(key, {'id': self.id, 'email': self.email}, 3600) - @classmethod def expired_reset_password_token(cls, token): key = cls.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token) diff --git a/apps/users/views/profile/reset.py b/apps/users/views/profile/reset.py index cde1463ea..d33681b1b 100644 --- a/apps/users/views/profile/reset.py +++ b/apps/users/views/profile/reset.py @@ -2,6 +2,8 @@ from __future__ import unicode_literals +import time + from django.conf import settings from django.core.cache import cache from django.shortcuts import redirect, reverse @@ -9,6 +11,7 @@ from django.urls import reverse_lazy from django.utils.translation import gettext as _ from django.views.generic import FormView, RedirectView +from authentication.errors import IntervalTooShort from common.utils import FlashMessageUtil, get_object_or_none, random_string from common.utils.verify_code import SendAndVerifyCodeUtil from users.notifications import ResetPasswordSuccessMsg @@ -37,6 +40,20 @@ class UserForgotPasswordPreviewingView(FormView): def get_redirect_url(token): return reverse('authentication:forgot-password') + '?token=%s' % token + @staticmethod + def generate_previewing_token(user): + sent_ttl = 60 + token_sent_at_key = '{}_send_at'.format(user.username) + token_sent_at = cache.get(token_sent_at_key, 0) + + if token_sent_at: + raise IntervalTooShort(sent_ttl) + token = random_string(36) + user_info = {'username': user.username, 'phone': user.phone, 'email': user.email} + cache.set(token, user_info, 5 * 60) + cache.set(token_sent_at_key, time.time(), sent_ttl) + return token + def form_valid(self, form): username = form.cleaned_data['username'] user = get_object_or_none(User, username=username) @@ -49,9 +66,11 @@ class UserForgotPasswordPreviewingView(FormView): form.add_error('username', error) return super().form_invalid(form) - token = random_string(36) - user_map = {'username': user.username, 'phone': user.phone, 'email': user.email} - cache.set(token, user_map, 5 * 60) + try: + token = self.generate_previewing_token(user) + except IntervalTooShort as e: + form.add_error('username', e) + return super().form_invalid(form) return redirect(self.get_redirect_url(token)) @@ -103,13 +122,25 @@ class UserForgotPasswordView(FormView): reset_password_url = reverse('authentication:reset-password') return reset_password_url + query_params + @staticmethod + def safe_verify_code(token, target, form_type, code): + token_verified_key = '{}_verified'.format(token) + token_verified_times = cache.get(token_verified_key, 0) + + if token_verified_times >= 3: + cache.delete(token) + raise ValueError('Verification code has been used more than 3 times, please re-verify') + cache.set(token_verified_key, token_verified_times + 1, 5 * 60) + sender_util = SendAndVerifyCodeUtil(target, backend=form_type) + return sender_util.verify(code) + def form_valid(self, form): token = self.request.GET.get('token') - userinfo = cache.get(token) - if not userinfo: + user_info = cache.get(token) + if not user_info: return redirect(self.get_redirect_url(return_previewing=True)) - username = userinfo.get('username') + username = user_info.get('username') form_type = form.cleaned_data['form_type'] target = form.cleaned_data[form_type] code = form.cleaned_data['code'] @@ -120,8 +151,9 @@ class UserForgotPasswordView(FormView): target = target.lstrip('+') try: - sender_util = SendAndVerifyCodeUtil(target, backend=form_type) - sender_util.verify(code) + self.safe_verify_code(token, target, form_type, code) + except ValueError as e: + return redirect(self.get_redirect_url(return_previewing=True)) except Exception as e: form.add_error('code', str(e)) return super().form_invalid(form) From 28196573bb962a63f10d87e66585d8e687c83e7a Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 28 Sep 2023 11:24:34 +0800 Subject: [PATCH 023/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E9=9A=8F?= =?UTF-8?q?=E6=9C=BA=E4=BD=BF=E7=94=A8secrets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/utils/random.py | 42 ++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/apps/common/utils/random.py b/apps/common/utils/random.py index 267de8d22..166453da6 100644 --- a/apps/common/utils/random.py +++ b/apps/common/utils/random.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # import random +import secrets import socket import string import struct @@ -18,31 +19,24 @@ def random_ip(): def random_string(length: int, lower=True, upper=True, digit=True, special_char=False, symbols=string_punctuation): - random.seed() - args_names = ['lower', 'upper', 'digit'] - args_values = [lower, upper, digit] - args_string = [string.ascii_lowercase, string.ascii_uppercase, string.digits] - args_string_map = dict(zip(args_names, args_string)) - kwargs = dict(zip(args_names, args_values)) - kwargs_keys = list(kwargs.keys()) - kwargs_values = list(kwargs.values()) - args_true_count = len([i for i in kwargs_values if i]) + if not any([lower, upper, digit]): + raise ValueError('At least one of `lower`, `upper`, `digit` must be `True`') + if length < 4: + raise ValueError('The length of the string must be greater than 3') - assert any(kwargs_values), f'Parameters {kwargs_keys} must have at least one `True`' - assert length >= args_true_count, f'Expected length >= {args_true_count}, bug got {length}' - - chars = ''.join([args_string_map[k] for k, v in kwargs.items() if v]) - password = list(random.choice(chars) for i in range(length)) + chars_map = ( + (lower, string.ascii_lowercase), + (upper, string.ascii_uppercase), + (digit, string.digits), + ) + chars = ''.join([i[1] for i in chars_map if i[0]]) + texts = list(secrets.choice(chars) for __ in range(length)) if special_char: - special_num = length // 16 + 1 - special_index = [] - for i in range(special_num): - index = random.randint(1, length - 1) - if index not in special_index: - special_index.append(index) - for i in special_index: - password[i] = random.choice(symbols) + symbol_num = length // 16 + 1 + symbol_index = random.choices(list(range(1, length - 1)), k=symbol_num) + for i in symbol_index: + texts[i] = secrets.choice(symbols) - password = ''.join(password) - return password + text = ''.join(texts) + return text From 2aed3fcaea05bf7bbeca58a8f64a78913ef79409 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 7 Oct 2023 11:35:25 +0800 Subject: [PATCH 024/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E9=9A=8F?= =?UTF-8?q?=E6=9C=BA=E6=95=B0=E7=94=9F=E6=88=90=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20random=20=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/utils/random.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/common/utils/random.py b/apps/common/utils/random.py index 166453da6..505fbd041 100644 --- a/apps/common/utils/random.py +++ b/apps/common/utils/random.py @@ -18,6 +18,20 @@ def random_ip(): return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff))) +def random_replace_char(s, chars, length): + using_index = set() + seq = list(s) + + while length > 0: + index = secrets.randbelow(len(seq) - 1) + if index in using_index or index == 0: + continue + seq[index] = secrets.choice(chars) + using_index.add(index) + length -= 1 + return ''.join(seq) + + def random_string(length: int, lower=True, upper=True, digit=True, special_char=False, symbols=string_punctuation): if not any([lower, upper, digit]): raise ValueError('At least one of `lower`, `upper`, `digit` must be `True`') @@ -31,12 +45,10 @@ def random_string(length: int, lower=True, upper=True, digit=True, special_char= ) chars = ''.join([i[1] for i in chars_map if i[0]]) texts = list(secrets.choice(chars) for __ in range(length)) + texts = ''.join(texts) + # 控制一下特殊字符的数量, 别随机出来太多 if special_char: symbol_num = length // 16 + 1 - symbol_index = random.choices(list(range(1, length - 1)), k=symbol_num) - for i in symbol_index: - texts[i] = secrets.choice(symbols) - - text = ''.join(texts) - return text + texts = random_replace_char(texts, symbols, symbol_num) + return texts From 0c1f4d99f8858c5e6a555ed58c044f0438860c07 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Sat, 7 Oct 2023 15:28:40 +0800 Subject: [PATCH 025/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E5=BC=95=E5=85=A5html=E9=94=99=E8=AF=AF=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#11744)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/tickets/handlers/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tickets/handlers/base.py b/apps/tickets/handlers/base.py index c1378da89..fbf6a8e46 100644 --- a/apps/tickets/handlers/base.py +++ b/apps/tickets/handlers/base.py @@ -98,7 +98,7 @@ class BaseHandler: context = self._diff_prev_approve_context(state) context.update({'approve_info': approve_info}) body = self.safe_html_script( - render_to_string('tickets/user_login_reminder.html', context) + render_to_string('tickets/ticket_approve_diff.html', context) ) data = { 'body': body, From 42b4e7697d1941075b9393576b0b4f2b927c0b91 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Sat, 7 Oct 2023 15:50:28 +0800 Subject: [PATCH 026/114] =?UTF-8?q?feat:=20=E8=B5=84=E4=BA=A7=E7=99=BB?= =?UTF-8?q?=E5=BD=95acl=E5=8A=A8=E4=BD=9C=E5=A2=9E=E5=8A=A0=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=97=A5=E5=BF=97=20(#11741)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/backends/db.py | 2 +- apps/audits/const.py | 4 ++++ apps/authentication/api/connection_token.py | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/apps/audits/backends/db.py b/apps/audits/backends/db.py index ac589931d..bb2e241c1 100644 --- a/apps/audits/backends/db.py +++ b/apps/audits/backends/db.py @@ -58,7 +58,7 @@ class OperateLogStore(object): return diff_list def save(self, **kwargs): - log_id = kwargs.get('id', '') + log_id = kwargs.get('id') before = kwargs.pop('before') or {} after = kwargs.pop('after') or {} diff --git a/apps/audits/const.py b/apps/audits/const.py index 44d3a556f..77a76c804 100644 --- a/apps/audits/const.py +++ b/apps/audits/const.py @@ -29,6 +29,10 @@ class ActionChoices(TextChoices): connect = "connect", _("Connect") login = "login", _("Login") change_auth = "change_password", _("Change password") + # acls action + reject = 'reject', _('Reject') + accept = 'accept', _('Accept') + review = 'review', _('Review') class LoginTypeChoices(TextChoices): diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 955ca4028..2c1b0d0f1 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -374,6 +374,17 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView raise JMSException(code='perm_expired', detail=msg) return account + @staticmethod + def _record_operate_log(acl, asset): + from audits.handler import create_or_update_operate_log + after = {str(_('Assets')): str(asset)} + object_name = acl._meta.object_name + resource_type = acl._meta.verbose_name + create_or_update_operate_log( + acl.action, resource_type, resource=acl, + after=after, object_name=object_name + ) + def _validate_acl(self, user, asset, account): from acls.models import LoginAssetACL acls = LoginAssetACL.filter_queryset(user=user, asset=asset, account=account) @@ -382,15 +393,17 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView if not acl: return if acl.is_action(acl.ActionChoices.accept): + self._record_operate_log(acl, asset) return if acl.is_action(acl.ActionChoices.reject): + self._record_operate_log(acl, asset) msg = _('ACL action is reject: {}({})'.format(acl.name, acl.id)) raise JMSException(code='acl_reject', detail=msg) if acl.is_action(acl.ActionChoices.review): if not self.request.query_params.get('create_ticket'): msg = _('ACL action is review') raise JMSException(code='acl_review', detail=msg) - + self._record_operate_log(acl, asset) ticket = LoginAssetACL.create_login_asset_review_ticket( user=user, asset=asset, account_username=account.username, assignees=acl.reviewers.all(), org_id=asset.org_id From f206d963a094e77c0a343536e29618cc3f9213f5 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 7 Oct 2023 14:47:17 +0800 Subject: [PATCH 027/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | 4 ++-- apps/terminal/api/session/session.py | 7 ++++++- apps/terminal/api/session/task.py | 11 +++++------ apps/terminal/permissions.py | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index a173d346d..20a804924 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:096cdc44514bd9f43b5e0062d878625c220ed7826a57a27968db3cb97e7eb011 -size 132403 +oid sha256:e8df7ef115d111ee9a176fac269a41a87d6b9a76a521e0206fe29591ab16da69 +size 132482 diff --git a/apps/terminal/api/session/session.py b/apps/terminal/api/session/session.py index 756fb5a91..c286dee83 100644 --- a/apps/terminal/api/session/session.py +++ b/apps/terminal/api/session/session.py @@ -92,7 +92,12 @@ class SessionViewSet(RecordViewLogMixin, OrgBulkModelViewSet): rbac_perms = { 'download': ['terminal.download_sessionreplay'], } - permission_classes = [RBACPermission | IsSessionAssignee] + permission_classes = [RBACPermission] + + def get_permissions(self): + if self.action == 'retrieve': + self.permission_classes = [RBACPermission | IsSessionAssignee] + return super().get_permissions() @staticmethod def prepare_offline_file(session, local_path): diff --git a/apps/terminal/api/session/task.py b/apps/terminal/api/session/task.py index f9dc8a2f2..fd09fb2ae 100644 --- a/apps/terminal/api/session/task.py +++ b/apps/terminal/api/session/task.py @@ -50,14 +50,13 @@ class TaskViewSet(JMSBulkModelViewSet): serializer.is_valid(raise_exception=True) session_id = serializer.validated_data['session_id'] task_name = serializer.validated_data['task_name'] - session_ids = [session_id, ] user_id = request.user.id - for session_id in session_ids: - if not is_session_approver(session_id, user_id): - return Response({}, status=status.HTTP_403_FORBIDDEN) - with tmp_to_root_org(): - validated_session = create_sessions_tasks(session_ids, request.user, task_name=task_name) + if not is_session_approver(session_id, user_id): + return Response({}, status=status.HTTP_403_FORBIDDEN) + + with tmp_to_root_org(): + validated_session = create_sessions_tasks([session_id], request.user, task_name=task_name) return Response({"ok": validated_session}) diff --git a/apps/terminal/permissions.py b/apps/terminal/permissions.py index e2e72e572..288d839eb 100644 --- a/apps/terminal/permissions.py +++ b/apps/terminal/permissions.py @@ -9,7 +9,7 @@ __all__ = ['IsSessionAssignee'] class IsSessionAssignee(permissions.IsAuthenticated): def has_permission(self, request, view): - return False + return True def has_object_permission(self, request, view, obj): try: From 60399fae29131ae6f74199fe91e74646ff2eed86 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Sat, 7 Oct 2023 17:41:20 +0800 Subject: [PATCH 028/114] =?UTF-8?q?feat:=20=E7=99=BB=E5=BD=95=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E6=B6=88=E6=81=AF=E6=8F=90=E9=86=92=20(#11747)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/acls/notifications.py | 29 +++++++++++++++++++ .../templates/acls/asset_login_reminder.html | 12 ++++++++ apps/authentication/api/connection_token.py | 5 ++++ 3 files changed, 46 insertions(+) create mode 100644 apps/acls/templates/acls/asset_login_reminder.html diff --git a/apps/acls/notifications.py b/apps/acls/notifications.py index ec4d99cd1..5d3c59e13 100644 --- a/apps/acls/notifications.py +++ b/apps/acls/notifications.py @@ -1,6 +1,7 @@ from django.template.loader import render_to_string from django.utils.translation import gettext_lazy as _ +from assets.models import Asset from audits.models import UserLoginLog from notifications.notifications import UserMessage from users.models import User @@ -35,3 +36,31 @@ class UserLoginReminderMsg(UserMessage): user = User.objects.first() user_log = UserLoginLog.objects.first() return cls(user, user_log) + + +class AssetLoginReminderMsg(UserMessage): + subject = _('Asset login reminder') + + def __init__(self, user, asset: Asset, login_user: User): + self.asset = asset + self.login_user = login_user + super().__init__(user) + + def get_html_msg(self) -> dict: + context = { + 'recipient': self.user.username, + 'username': self.login_user.username, + 'asset': str(self.asset), + } + message = render_to_string('acls/asset_login_reminder.html', context) + + return { + 'subject': str(self.subject), + 'message': message + } + + @classmethod + def gen_test_msg(cls): + user = User.objects.first() + asset = Asset.objects.first() + return cls(user, asset, user) diff --git a/apps/acls/templates/acls/asset_login_reminder.html b/apps/acls/templates/acls/asset_login_reminder.html new file mode 100644 index 000000000..9a30cda49 --- /dev/null +++ b/apps/acls/templates/acls/asset_login_reminder.html @@ -0,0 +1,12 @@ +{% load i18n %} + +

{% trans 'Respectful' %}{{ recipient }},

+
+

{% trans 'Username' %}: [{{ username }}]

+

{% trans 'Assets' %}: [{{ asset }}]

+
+ +

{% trans 'The user has just successfully logged into the asset. Please ensure that this is an authorized operation. If you suspect that this is an unauthorized access, please take appropriate measures immediately.' %}

+ +

{% trans 'Thank you' %}!

+ diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 2c1b0d0f1..5b9481da0 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -15,6 +15,7 @@ from rest_framework.request import Request from rest_framework.response import Response from accounts.const import AliasAccount +from acls.notifications import AssetLoginReminderMsg from common.api import JMSModelViewSet from common.exceptions import JMSException from common.utils import random_string, get_logger, get_request_ip @@ -409,6 +410,10 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView assignees=acl.reviewers.all(), org_id=asset.org_id ) return ticket + if acl.is_action(acl.ActionChoices.notice): + reviewers = acl.reviewers.all() + for reviewer in reviewers: + AssetLoginReminderMsg(reviewer, asset, user).publish_async() class SuperConnectionTokenViewSet(ConnectionTokenViewSet): From 68707085fa0b5ab87d84e51703de69fcdbea751a Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Sat, 7 Oct 2023 17:41:45 +0800 Subject: [PATCH 029/114] =?UTF-8?q?perf:=20=E6=97=A5=E5=BF=97=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=97=B6=E9=97=B4=E4=B8=8D=E5=B0=91=E4=BA=8E6?= =?UTF-8?q?=E4=B8=AA=E6=9C=88=20(#11742)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/jumpserver/settings/custom.py | 15 +++++++++++++-- apps/settings/serializers/cleaning.py | 25 +++++++++++++++++++++---- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 18d0bccca..6733faa92 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -124,6 +124,8 @@ TASK_LOG_KEEP_DAYS = CONFIG.TASK_LOG_KEEP_DAYS OPERATE_LOG_KEEP_DAYS = CONFIG.OPERATE_LOG_KEEP_DAYS ACTIVITY_LOG_KEEP_DAYS = CONFIG.ACTIVITY_LOG_KEEP_DAYS FTP_LOG_KEEP_DAYS = CONFIG.FTP_LOG_KEEP_DAYS +CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = CONFIG.CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS + ORG_CHANGE_TO_URL = CONFIG.ORG_CHANGE_TO_URL WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD @@ -159,8 +161,6 @@ SECURITY_SESSION_SHARE = CONFIG.SECURITY_SESSION_SHARE LOGIN_REDIRECT_TO_BACKEND = CONFIG.LOGIN_REDIRECT_TO_BACKEND LOGIN_REDIRECT_MSG_ENABLED = CONFIG.LOGIN_REDIRECT_MSG_ENABLED -CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = CONFIG.CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS - TERMINAL_RAZOR_ENABLED = CONFIG.TERMINAL_RAZOR_ENABLED TERMINAL_OMNIDB_ENABLED = CONFIG.TERMINAL_OMNIDB_ENABLED TERMINAL_MAGNUS_ENABLED = CONFIG.TERMINAL_MAGNUS_ENABLED @@ -204,3 +204,14 @@ MAX_LIMIT_PER_PAGE = CONFIG.MAX_LIMIT_PER_PAGE # Magnus DB Port MAGNUS_ORACLE_PORTS = CONFIG.MAGNUS_ORACLE_PORTS LIMIT_SUPER_PRIV = CONFIG.LIMIT_SUPER_PRIV + +LOG_NAMES = [ + 'LOGIN_LOG_KEEP_DAYS', 'TASK_LOG_KEEP_DAYS', + 'OPERATE_LOG_KEEP_DAYS', 'FTP_LOG_KEEP_DAYS', + 'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS', + 'ACTIVITY_LOG_KEEP_DAYS', 'TERMINAL_SESSION_KEEP_DURATION' +] +if LIMIT_SUPER_PRIV: + for name in LOG_NAMES: + if globals()[name] < 180: + globals()[name] = 180 diff --git a/apps/settings/serializers/cleaning.py b/apps/settings/serializers/cleaning.py index eb1841561..03fda1f7b 100644 --- a/apps/settings/serializers/cleaning.py +++ b/apps/settings/serializers/cleaning.py @@ -1,5 +1,7 @@ +from django.conf import settings from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from rest_framework.serializers import ValidationError __all__ = ['CleaningSerializer'] @@ -27,12 +29,27 @@ class CleaningSerializer(serializers.Serializer): min_value=1, max_value=9999, label=_("Cloud sync record keep days (day)"), ) + ACTIVITY_LOG_KEEP_DAYS = serializers.IntegerField( + min_value=1, max_value=9999, + label=_("Activity log keep days (day)"), + ) TERMINAL_SESSION_KEEP_DURATION = serializers.IntegerField( min_value=1, max_value=99999, required=True, label=_('Session keep duration (day)'), help_text=_( 'Session, record, command will be delete if more than duration, only in database, OSS will not be affected.') ) - ACTIVITY_LOG_KEEP_DAYS = serializers.IntegerField( - min_value=1, max_value=9999, - label=_("Activity log keep days (day)"), - ) + MIN_DAYS_THRESHOLD = 180 + + def validate(self, attrs): + attrs = super().validate(attrs) + if not settings.LIMIT_SUPER_PRIV: + return attrs + + error_names = [ + name for name in settings.LOG_NAMES + if attrs.get(name, 0) < self.MIN_DAYS_THRESHOLD + ] + if error_names: + error_message = _('must be greater than {} days.').format(self.MIN_DAYS_THRESHOLD) + raise ValidationError({name: error_message for name in error_names}) + return attrs From dd50044b898ceca6a12c59347837028371fd7edf Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Sat, 7 Oct 2023 17:58:42 +0800 Subject: [PATCH 030/114] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91=20(#11748)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 556 ++++++++++++++------------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 555 +++++++++++++------------- 4 files changed, 571 insertions(+), 548 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 8e58c4d50..c0640b1af 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:47cea504e9acfdcc94a45f2ea96216dddde065d134a92a0ef3e92815a12df6fc -size 162024 +oid sha256:56db214d34b91e9f6d712cef124890264144b35b99e50d6833b4af0e935778b0 +size 162655 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 0a737c38d..0dcc50186 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: 2023-10-07 13:50+0800\n" +"POT-Creation-Date: 2023-10-07 17:56+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -31,7 +31,7 @@ msgstr "パラメータ 'action' は [{}] でなければなりません。" #: users/forms/profile.py:22 users/serializers/user.py:105 #: users/templates/users/_msg_user_created.html:13 #: users/templates/users/user_password_verify.html:18 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:28 +#: xpack/plugins/cloud/serializers/account_attrs.py:28 msgid "Password" msgstr "パスワード" @@ -94,9 +94,9 @@ msgid "Update" msgstr "更新" #: accounts/const/account.py:33 -#: accounts/serializers/automations/change_secret.py:155 audits/const.py:55 +#: accounts/serializers/automations/change_secret.py:155 audits/const.py:59 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:77 xpack.bak/plugins/cloud/const.py:43 +#: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 msgid "Failed" msgstr "失敗しました" @@ -232,8 +232,7 @@ msgstr "ユーザー %s がパスワードを閲覧/導き出しました" #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 -#: xpack.bak/plugins/cloud/models.py:253 +#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 msgid "Asset" msgstr "資産" @@ -269,8 +268,7 @@ msgstr "ソース ID" #: terminal/backends/command/models.py:18 terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 -#: tickets/models/ticket/command_confirm.py:13 -#: xpack.bak/plugins/cloud/models.py:89 +#: tickets/models/ticket/command_confirm.py:13 xpack/plugins/cloud/models.py:89 msgid "Account" msgstr "アカウント" @@ -337,7 +335,7 @@ msgid "Trigger mode" msgstr "トリガーモード" #: accounts/models/automations/backup_account.py:105 audits/models.py:197 -#: terminal/models/session/sharing.py:125 xpack.bak/plugins/cloud/models.py:205 +#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "理由" @@ -444,14 +442,15 @@ msgstr "最終ログイン日" #: accounts/models/automations/gather_account.py:17 #: accounts/models/automations/push_account.py:15 accounts/models/base.py:65 #: accounts/serializers/account/virtual.py:21 acls/serializers/base.py:19 -#: acls/serializers/base.py:50 acls/templates/acls/user_login_reminder.html:5 -#: assets/models/_user.py:23 audits/models.py:182 authentication/forms.py:25 -#: authentication/forms.py:27 authentication/models/temp_token.py:9 +#: acls/serializers/base.py:50 acls/templates/acls/asset_login_reminder.html:5 +#: acls/templates/acls/user_login_reminder.html:5 assets/models/_user.py:23 +#: audits/models.py:182 authentication/forms.py:25 authentication/forms.py:27 +#: authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: users/forms/profile.py:32 users/forms/profile.py:115 #: users/models/user.py:790 users/templates/users/_msg_user_created.html:12 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:26 +#: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" msgstr "ユーザー名" @@ -539,9 +538,8 @@ msgstr "パスワードルール" #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 users/forms/profile.py:33 #: users/models/group.py:13 users/models/preference.py:11 -#: users/models/user.py:792 xpack.bak/plugins/cloud/models.py:32 -#: xpack.bak/plugins/cloud/models.py:273 -#: xpack.bak/plugins/cloud/serializers/task.py:68 +#: users/models/user.py:792 xpack/plugins/cloud/models.py:32 +#: xpack/plugins/cloud/models.py:273 xpack/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名前" @@ -570,7 +568,7 @@ msgstr "プラットフォーム" msgid "Push params" msgstr "パラメータをプッシュする" -#: accounts/models/template.py:25 xpack.bak/plugins/cloud/models.py:325 +#: accounts/models/template.py:25 xpack/plugins/cloud/models.py:325 msgid "Account template" msgstr "アカウント テンプレート" @@ -710,8 +708,10 @@ msgstr "編集済み" #: accounts/serializers/account/account.py:267 #: accounts/serializers/automations/base.py:22 acls/models/base.py:97 +#: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 -#: assets/serializers/automations/base.py:20 ops/models/base.py:17 +#: assets/serializers/automations/base.py:20 +#: authentication/api/connection_token.py:381 ops/models/base.py:17 #: ops/models/job.py:139 ops/serializers/job.py:21 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" @@ -844,7 +844,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:828 -#: xpack.bak/plugins/cloud/models.py:39 xpack.bak/plugins/cloud/models.py:109 +#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "コメント" @@ -893,7 +893,7 @@ msgstr "* パスワードの長さの範囲6-30ビット" msgid "Automation task execution" msgstr "自動タスク実行履歴" -#: accounts/serializers/automations/change_secret.py:154 audits/const.py:54 +#: accounts/serializers/automations/change_secret.py:154 audits/const.py:58 #: audits/models.py:62 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: terminal/const.py:76 terminal/models/session/sharing.py:121 @@ -968,16 +968,16 @@ msgstr "秘密鍵が無効またはpassphraseエラー" msgid "Acls" msgstr "Acls" -#: acls/const.py:6 terminal/const.py:11 tickets/const.py:45 +#: acls/const.py:6 audits/const.py:33 terminal/const.py:11 tickets/const.py:45 #: tickets/templates/tickets/approve_check_password.html:49 msgid "Reject" msgstr "拒否" -#: acls/const.py:7 terminal/const.py:9 +#: acls/const.py:7 audits/const.py:34 terminal/const.py:9 msgid "Accept" msgstr "受け入れられる" -#: acls/const.py:8 +#: acls/const.py:8 audits/const.py:35 msgid "Review" msgstr "レビュー担当者" @@ -991,13 +991,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:97 -#: xpack.bak/plugins/cloud/models.py:275 +#: xpack/plugins/cloud/models.py:275 msgid "Priority" msgstr "優先順位" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack.bak/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/models.py:276 msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" @@ -1034,12 +1034,12 @@ msgid "Command" msgstr "コマンド" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack.bak/plugins/cloud/models.py:291 +#: xpack/plugins/cloud/models.py:291 msgid "Regex" msgstr "正規情報" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/feature.py:17 xpack.bak/plugins/license/models.py:30 +#: settings/serializers/feature.py:17 xpack/plugins/license/models.py:30 msgid "Content" msgstr "コンテンツ" @@ -1100,10 +1100,14 @@ msgstr "ログインasset acl" msgid "Login asset confirm" msgstr "ログイン資産の確認" -#: acls/notifications.py:10 +#: acls/notifications.py:11 msgid "User login reminder" msgstr "ユーザーログインのリマインダ" +#: acls/notifications.py:42 +msgid "Asset login reminder" +msgstr "資産ログインのリマインダ" + #: acls/serializers/base.py:11 acls/serializers/login_acl.py:11 msgid "With * indicating a match all. " msgstr "* はすべて一致することを示します。" @@ -1135,7 +1139,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "いずれのレビューアも組織 '{}' に属していません" #: acls/serializers/rules/rules.py:20 -#: xpack.bak/plugins/cloud/serializers/task.py:137 +#: xpack/plugins/cloud/serializers/task.py:137 msgid "IP address invalid: `{}`" msgstr "IPアドレスが無効: '{}'" @@ -1159,10 +1163,26 @@ msgstr "IP" msgid "Time Period" msgstr "期間" +#: acls/templates/acls/asset_login_reminder.html:3 #: acls/templates/acls/user_login_reminder.html:3 msgid "Respectful" msgstr "尊敬する" +#: acls/templates/acls/asset_login_reminder.html:9 +msgid "" +"The user has just successfully logged into the asset. Please ensure that " +"this is an authorized operation. If you suspect that this is an unauthorized " +"access, please take appropriate measures immediately." +msgstr "" +"ユーザーは資産に正常にログインしたばかりです。許可されたアクションであること" +"を確認してください。不正アクセスが疑われる場合は、すぐに適切な措置を取ってく" +"ださい。" + +#: acls/templates/acls/asset_login_reminder.html:11 +#: acls/templates/acls/user_login_reminder.html:13 +msgid "Thank you" +msgstr "ありがとうございます。" + #: acls/templates/acls/user_login_reminder.html:7 audits/models.py:188 #: audits/models.py:257 #: authentication/templates/authentication/_msg_different_city.html:11 @@ -1185,16 +1205,12 @@ msgstr "" "ことを確認してください。不正アクセスが疑われる場合は、すぐに適切な措置を取っ" "てください。" -#: acls/templates/acls/user_login_reminder.html:13 -msgid "Thank you" -msgstr "ありがとうございます。" - #: applications/apps.py:9 msgid "Applications" msgstr "アプリケーション" -#: applications/models.py:16 xpack.bak/plugins/cloud/models.py:37 -#: xpack.bak/plugins/cloud/serializers/account.py:63 +#: applications/models.py:16 xpack/plugins/cloud/models.py:37 +#: xpack/plugins/cloud/serializers/account.py:63 msgid "Attrs" msgstr "ツールバーの" @@ -1259,7 +1275,7 @@ msgid "Unable to connect to port {port} on {address}" msgstr "{port} のポート {address} に接続できません" #: assets/automations/ping_gateway/manager.py:58 -#: authentication/middleware.py:92 xpack.bak/plugins/cloud/providers/fc.py:47 +#: authentication/middleware.py:92 xpack/plugins/cloud/providers/fc.py:47 msgid "Authentication failed" msgstr "認証に失敗しました" @@ -1268,7 +1284,7 @@ msgstr "認証に失敗しました" msgid "Connect failed" msgstr "接続に失敗しました" -#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:37 +#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:41 #: audits/signal_handlers/activity_log.py:62 common/utils/ip/geoip/utils.py:31 #: common/utils/ip/geoip/utils.py:37 common/utils/ip/utils.py:104 msgid "Unknown" @@ -1290,7 +1306,7 @@ msgstr "テストゲートウェイ" msgid "Gather facts" msgstr "資産情報の収集" -#: assets/const/base.py:33 audits/const.py:48 +#: assets/const/base.py:33 audits/const.py:52 #: terminal/serializers/applet_host.py:32 msgid "Disabled" msgstr "無効" @@ -1311,7 +1327,7 @@ msgstr "脚本" #: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:67 #: settings/serializers/feature.py:47 terminal/models/component/endpoint.py:13 #: terminal/serializers/applet.py:17 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:72 +#: xpack/plugins/cloud/serializers/account_attrs.py:72 msgid "Host" msgstr "ホスト" @@ -1324,7 +1340,7 @@ msgid "Cloud service" msgstr "クラウド サービス" #: assets/const/category.py:14 assets/models/asset/gpt.py:11 -#: assets/models/asset/web.py:16 audits/const.py:35 +#: assets/models/asset/web.py:16 audits/const.py:39 #: terminal/models/applet/applet.py:27 users/const.py:47 msgid "Web" msgstr "Web" @@ -1600,7 +1616,7 @@ msgstr "クラウド サービス" #: assets/models/asset/common.py:92 assets/models/platform.py:16 #: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:68 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:73 +#: xpack/plugins/cloud/serializers/account_attrs.py:73 msgid "Port" msgstr "ポート" @@ -1611,15 +1627,13 @@ msgstr "アドレス" #: assets/models/asset/common.py:151 assets/models/platform.py:119 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:115 -#: perms/serializers/user_permission.py:24 -#: xpack.bak/plugins/cloud/models.py:321 +#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 msgid "Platform" msgstr "プラットフォーム" #: assets/models/asset/common.py:153 assets/models/domain.py:21 #: authentication/serializers/connect_token_secret.py:133 -#: perms/serializers/user_permission.py:29 -#: xpack.bak/plugins/cloud/models.py:323 +#: perms/serializers/user_permission.py:29 xpack/plugins/cloud/models.py:323 msgid "Domain" msgstr "ドメイン" @@ -1695,8 +1709,8 @@ msgstr "アセットの自動化タスク" #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:115 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 -#: xpack.bak/plugins/cloud/models.py:201 xpack.bak/plugins/cloud/models.py:257 +#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 +#: xpack/plugins/cloud/models.py:257 msgid "Status" msgstr "ステータス" @@ -1760,7 +1774,7 @@ msgstr "資産グループ" #: assets/models/group.py:31 assets/models/platform.py:19 #: assets/serializers/platform.py:114 -#: xpack.bak/plugins/cloud/providers/nutanix.py:30 +#: xpack/plugins/cloud/providers/nutanix.py:30 msgid "Default" msgstr "デフォルト" @@ -1811,8 +1825,7 @@ msgid "Parent key" msgstr "親キー" #: assets/models/node.py:553 perms/serializers/permission.py:35 -#: tickets/models/ticket/apply_asset.py:14 -#: xpack.bak/plugins/cloud/models.py:322 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 msgid "Node" msgstr "ノード" @@ -1838,7 +1851,7 @@ msgstr "開ける" msgid "Setting" msgstr "設定" -#: assets/models/platform.py:38 audits/const.py:49 +#: assets/models/platform.py:38 audits/const.py:53 #: authentication/backends/passkey/models.py:11 settings/models.py:36 #: terminal/serializers/applet_host.py:33 msgid "Enabled" @@ -1954,9 +1967,8 @@ msgstr "" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:134 #: authentication/serializers/connect_token_secret.py:29 #: authentication/serializers/connect_token_secret.py:72 -#: perms/serializers/user_permission.py:25 -#: xpack.bak/plugins/cloud/models.py:324 -#: xpack.bak/plugins/cloud/serializers/task.py:31 +#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:324 +#: xpack/plugins/cloud/serializers/task.py:31 msgid "Protocols" msgstr "プロトコル" @@ -1989,9 +2001,9 @@ msgstr "デフォルト・データベース" #: assets/serializers/asset/database.py:28 common/db/fields.py:570 #: common/db/fields.py:575 common/serializers/fields.py:104 #: tickets/serializers/ticket/common.py:58 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:56 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:79 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:143 +#: xpack/plugins/cloud/serializers/account_attrs.py:56 +#: xpack/plugins/cloud/serializers/account_attrs.py:79 +#: xpack/plugins/cloud/serializers/account_attrs.py:143 msgid "This field is required." msgstr "このフィールドは必須です。" @@ -2253,7 +2265,7 @@ msgid "Symlink" msgstr "Symlink" #: audits/const.py:18 audits/const.py:28 perms/const.py:14 -#: terminal/api/session/session.py:141 +#: terminal/api/session/session.py:146 msgid "Download" msgstr "ダウンロード" @@ -2261,7 +2273,7 @@ msgstr "ダウンロード" msgid "Rename dir" msgstr "マップディレクトリ" -#: audits/const.py:23 rbac/tree.py:233 terminal/api/session/session.py:252 +#: audits/const.py:23 rbac/tree.py:233 terminal/api/session/session.py:257 #: terminal/templates/terminal/_msg_command_warning.html:18 #: terminal/templates/terminal/_msg_session_sharing.html:10 msgid "View" @@ -2287,30 +2299,30 @@ msgstr "ログイン" msgid "Change password" msgstr "パスワードを変更する" -#: audits/const.py:36 settings/serializers/terminal.py:6 +#: audits/const.py:40 settings/serializers/terminal.py:6 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 #: terminal/serializers/session.py:49 terminal/serializers/session.py:63 msgid "Terminal" msgstr "ターミナル" -#: audits/const.py:41 audits/models.py:130 +#: audits/const.py:45 audits/models.py:130 msgid "Operate log" msgstr "ログの操作" -#: audits/const.py:42 +#: audits/const.py:46 msgid "Session log" msgstr "セッションログ" -#: audits/const.py:43 +#: audits/const.py:47 msgid "Login log" msgstr "ログインログ" -#: audits/const.py:44 terminal/models/applet/host.py:143 +#: audits/const.py:48 terminal/models/applet/host.py:143 #: terminal/models/component/task.py:22 msgid "Task" msgstr "タスク" -#: audits/const.py:50 +#: audits/const.py:54 msgid "-" msgstr "-" @@ -2522,29 +2534,29 @@ msgstr "外部ストレージへのFTPファイルのアップロード" msgid "This action require verify your MFA" msgstr "この操作には、MFAを検証する必要があります" -#: authentication/api/connection_token.py:260 +#: authentication/api/connection_token.py:261 msgid "Reusable connection token is not allowed, global setting not enabled" msgstr "" "再使用可能な接続トークンの使用は許可されていません。グローバル設定は有効に" "なっていません" -#: authentication/api/connection_token.py:351 +#: authentication/api/connection_token.py:352 msgid "Anonymous account is not supported for this asset" msgstr "匿名アカウントはこのプロパティではサポートされていません" -#: authentication/api/connection_token.py:370 +#: authentication/api/connection_token.py:371 msgid "Account not found" msgstr "アカウントが見つかりません" -#: authentication/api/connection_token.py:373 +#: authentication/api/connection_token.py:374 msgid "Permission expired" msgstr "承認の有効期限が切れています" -#: authentication/api/connection_token.py:387 +#: authentication/api/connection_token.py:401 msgid "ACL action is reject: {}({})" msgstr "ACL アクションは拒否です: {}({})" -#: authentication/api/connection_token.py:391 +#: authentication/api/connection_token.py:405 msgid "ACL action is review" msgstr "ACL アクションはレビューです" @@ -2552,7 +2564,7 @@ msgstr "ACL アクションはレビューです" msgid "Current user not support mfa type: {}" msgstr "現在のユーザーはmfaタイプをサポートしていません: {}" -#: authentication/api/password.py:34 terminal/api/session/session.py:300 +#: authentication/api/password.py:34 terminal/api/session/session.py:305 #: users/views/profile/reset.py:61 msgid "User does not exist: {}" msgstr "ユーザーが存在しない: {}" @@ -2819,6 +2831,7 @@ msgid "Your password is invalid" msgstr "パスワードが無効です" #: authentication/errors/mfa.py:43 +#, python-format msgid "Please wait for %s seconds before retry" msgstr "%s 秒後に再試行してください" @@ -3352,7 +3365,7 @@ msgid "Do you want to retry ?" msgstr "再試行しますか?" #: authentication/utils.py:28 common/utils/ip/geoip/utils.py:24 -#: xpack.bak/plugins/cloud/const.py:29 +#: xpack/plugins/cloud/const.py:29 msgid "LAN" msgstr "ローカルエリアネットワーク" @@ -3697,7 +3710,7 @@ msgstr "アリ雲" msgid "Tencent cloud" msgstr "テンセント雲" -#: common/sdk/sms/endpoint.py:18 xpack.bak/plugins/cloud/const.py:13 +#: common/sdk/sms/endpoint.py:18 xpack/plugins/cloud/const.py:13 msgid "Huawei Cloud" msgstr "華為雲" @@ -4049,7 +4062,7 @@ msgid "Date last run" msgstr "最終実行日" #: ops/models/base.py:51 ops/models/job.py:224 -#: xpack.bak/plugins/cloud/models.py:199 +#: xpack/plugins/cloud/models.py:199 msgid "Result" msgstr "結果" @@ -4875,7 +4888,7 @@ msgid "Client Id" msgstr "クライアントID" #: settings/serializers/auth/oauth2.py:33 settings/serializers/auth/oidc.py:22 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:38 +#: xpack/plugins/cloud/serializers/account_attrs.py:38 msgid "Client Secret" msgstr "クライアント秘密" @@ -5143,7 +5156,7 @@ msgid "SSO auth key TTL" msgstr "Token有効期間" #: settings/serializers/auth/sso.py:20 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:193 +#: xpack/plugins/cloud/serializers/account_attrs.py:193 msgid "Unit: second" msgstr "単位: 秒" @@ -5195,35 +5208,39 @@ msgstr "サポートリンク" msgid "default: http://www.jumpserver.org/support/" msgstr "デフォルト: http://www.jumpserver.org/support/" -#: settings/serializers/cleaning.py:8 +#: settings/serializers/cleaning.py:10 msgid "Period clean" msgstr "定時清掃" -#: settings/serializers/cleaning.py:12 +#: settings/serializers/cleaning.py:14 msgid "Login log keep days (day)" msgstr "ログインログは日数を保持します(天)" -#: settings/serializers/cleaning.py:16 +#: settings/serializers/cleaning.py:18 msgid "Task log keep days (day)" msgstr "タスクログは日数を保持します(天)" -#: settings/serializers/cleaning.py:20 +#: settings/serializers/cleaning.py:22 msgid "Operate log keep days (day)" msgstr "ログ管理日を操作する(天)" -#: settings/serializers/cleaning.py:24 +#: settings/serializers/cleaning.py:26 msgid "FTP log keep days (day)" msgstr "ダウンロードのアップロード(天)" -#: settings/serializers/cleaning.py:28 +#: settings/serializers/cleaning.py:30 msgid "Cloud sync record keep days (day)" msgstr "クラウド同期レコードは日数を保持します(天)" -#: settings/serializers/cleaning.py:31 +#: settings/serializers/cleaning.py:34 +msgid "Activity log keep days (day)" +msgstr "活動ログは日数を保持します(天)" + +#: settings/serializers/cleaning.py:37 msgid "Session keep duration (day)" msgstr "セッション維持期間(天)" -#: settings/serializers/cleaning.py:33 +#: settings/serializers/cleaning.py:39 msgid "" "Session, record, command will be delete if more than duration, only in " "database, OSS will not be affected." @@ -5231,9 +5248,9 @@ msgstr "" "この期間を超えるセッション、録音、およびコマンド レコードは削除されます (デー" "タベースのバックアップに影響し、OSS などには影響しません)" -#: settings/serializers/cleaning.py:37 -msgid "Activity log keep days (day)" -msgstr "活動ログは日数を保持します(天)" +#: settings/serializers/cleaning.py:53 +msgid "must be greater than {} days." +msgstr "" #: settings/serializers/feature.py:16 msgid "Subject" @@ -6117,15 +6134,15 @@ msgstr "オンラインセッションを持つ" msgid "User %s %s session %s replay" msgstr "ユーザー%s %sこのセッション %s の録画です" -#: terminal/api/session/session.py:292 +#: terminal/api/session/session.py:297 msgid "Session does not exist: {}" msgstr "セッションが存在しません: {}" -#: terminal/api/session/session.py:295 +#: terminal/api/session/session.py:300 msgid "Session is finished or the protocol not supported" msgstr "セッションが終了したか、プロトコルがサポートされていません" -#: terminal/api/session/session.py:308 +#: terminal/api/session/session.py:313 msgid "User does not have permission" msgstr "ユーザーに権限がありません" @@ -6243,7 +6260,7 @@ msgstr "一括作成非サポート" msgid "Storage is invalid" msgstr "ストレージが無効です" -#: terminal/models/applet/applet.py:30 xpack.bak/plugins/license/models.py:88 +#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:88 msgid "Community edition" msgstr "コミュニティ版" @@ -6770,16 +6787,16 @@ msgid "Bucket" msgstr "バケット" #: terminal/serializers/storage.py:32 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:17 +#: xpack/plugins/cloud/serializers/account_attrs.py:17 msgid "Access key id" msgstr "アクセスキー" #: terminal/serializers/storage.py:36 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:20 +#: xpack/plugins/cloud/serializers/account_attrs.py:20 msgid "Access key secret" msgstr "アクセスキーシークレット" -#: terminal/serializers/storage.py:67 xpack.bak/plugins/cloud/models.py:250 +#: terminal/serializers/storage.py:67 xpack/plugins/cloud/models.py:250 msgid "Region" msgstr "リージョン" @@ -7365,7 +7382,7 @@ msgid "Not a valid ssh public key" msgstr "有効なssh公開鍵ではありません" #: users/forms/profile.py:173 users/models/user.py:825 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:203 +#: xpack/plugins/cloud/serializers/account_attrs.py:203 msgid "Public key" msgstr "公開キー" @@ -7398,7 +7415,7 @@ msgid "OTP secret key" msgstr "OTP 秘密" #: users/models/user.py:822 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:206 +#: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "Private key" msgstr "ssh秘密鍵" @@ -7885,541 +7902,537 @@ msgstr "* 新しいパスワードを最後の {} パスワードにすること msgid "Reset password success, return to login page" msgstr "パスワードの成功をリセットし、ログインページに戻る" -#: xpack.bak/apps.py:8 +#: xpack/apps.py:8 msgid "XPACK" msgstr "XPack" -#: xpack.bak/plugins/cloud/api.py:56 +#: xpack/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "テスト接続成功" -#: xpack.bak/plugins/cloud/api.py:58 +#: xpack/plugins/cloud/api.py:58 msgid "Test connection failed: {}" msgstr "テスト接続に失敗しました: {}" -#: xpack.bak/plugins/cloud/const.py:8 +#: xpack/plugins/cloud/const.py:8 msgid "Alibaba Cloud" msgstr "アリ雲" -#: xpack.bak/plugins/cloud/const.py:9 +#: xpack/plugins/cloud/const.py:9 msgid "AWS (International)" msgstr "AWS (国際)" -#: xpack.bak/plugins/cloud/const.py:10 +#: xpack/plugins/cloud/const.py:10 msgid "AWS (China)" msgstr "AWS (中国)" -#: xpack.bak/plugins/cloud/const.py:11 +#: xpack/plugins/cloud/const.py:11 msgid "Azure (China)" msgstr "Azure (中国)" -#: xpack.bak/plugins/cloud/const.py:12 +#: xpack/plugins/cloud/const.py:12 msgid "Azure (International)" msgstr "Azure (国際)" -#: xpack.bak/plugins/cloud/const.py:14 +#: xpack/plugins/cloud/const.py:14 msgid "Baidu Cloud" msgstr "百度雲" -#: xpack.bak/plugins/cloud/const.py:15 +#: xpack/plugins/cloud/const.py:15 msgid "JD Cloud" msgstr "京東雲" -#: xpack.bak/plugins/cloud/const.py:16 +#: xpack/plugins/cloud/const.py:16 msgid "KingSoft Cloud" msgstr "金山雲" -#: xpack.bak/plugins/cloud/const.py:17 +#: xpack/plugins/cloud/const.py:17 msgid "Tencent Cloud" msgstr "テンセント雲" -#: xpack.bak/plugins/cloud/const.py:18 +#: xpack/plugins/cloud/const.py:18 msgid "Tencent Cloud (Lighthouse)" msgstr "テンセント雲(軽量アプリケーション)" -#: xpack.bak/plugins/cloud/const.py:19 +#: xpack/plugins/cloud/const.py:19 msgid "Google Cloud Platform" msgstr "谷歌雲" -#: xpack.bak/plugins/cloud/const.py:20 +#: xpack/plugins/cloud/const.py:20 msgid "UCloud" msgstr "ucloud" -#: xpack.bak/plugins/cloud/const.py:22 +#: xpack/plugins/cloud/const.py:22 msgid "VMware" msgstr "VMware" -#: xpack.bak/plugins/cloud/const.py:23 -#: xpack.bak/plugins/cloud/providers/nutanix.py:15 +#: xpack/plugins/cloud/const.py:23 xpack/plugins/cloud/providers/nutanix.py:15 msgid "Nutanix" msgstr "Nutanix" -#: xpack.bak/plugins/cloud/const.py:24 +#: xpack/plugins/cloud/const.py:24 msgid "Huawei Private Cloud" msgstr "華為私有雲" -#: xpack.bak/plugins/cloud/const.py:25 +#: xpack/plugins/cloud/const.py:25 msgid "Qingyun Private Cloud" msgstr "青雲私有雲" -#: xpack.bak/plugins/cloud/const.py:26 +#: xpack/plugins/cloud/const.py:26 msgid "CTYun Private Cloud" msgstr "スカイウィング私有雲" -#: xpack.bak/plugins/cloud/const.py:27 +#: xpack/plugins/cloud/const.py:27 msgid "OpenStack" msgstr "OpenStack" -#: xpack.bak/plugins/cloud/const.py:28 +#: xpack/plugins/cloud/const.py:28 msgid "Fusion Compute" msgstr "融合計算" -#: xpack.bak/plugins/cloud/const.py:33 +#: xpack/plugins/cloud/const.py:33 msgid "Private IP" msgstr "プライベートIP" -#: xpack.bak/plugins/cloud/const.py:34 +#: xpack/plugins/cloud/const.py:34 msgid "Public IP" msgstr "パブリックIP" -#: xpack.bak/plugins/cloud/const.py:38 xpack.bak/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/const.py:38 xpack/plugins/cloud/models.py:295 msgid "Instance name" msgstr "インスタンス名" -#: xpack.bak/plugins/cloud/const.py:39 +#: xpack/plugins/cloud/const.py:39 msgid "Instance name and Partial IP" msgstr "インスタンス名と部分IP" -#: xpack.bak/plugins/cloud/const.py:44 +#: xpack/plugins/cloud/const.py:44 msgid "Succeed" msgstr "成功" -#: xpack.bak/plugins/cloud/const.py:48 +#: xpack/plugins/cloud/const.py:48 msgid "Unsync" msgstr "同期していません" -#: xpack.bak/plugins/cloud/const.py:49 +#: xpack/plugins/cloud/const.py:49 msgid "New Sync" msgstr "新しい同期" -#: xpack.bak/plugins/cloud/const.py:50 +#: xpack/plugins/cloud/const.py:50 msgid "Synced" msgstr "同期済み" -#: xpack.bak/plugins/cloud/const.py:51 +#: xpack/plugins/cloud/const.py:51 msgid "Released" msgstr "リリース済み" -#: xpack.bak/plugins/cloud/manager.py:53 +#: xpack/plugins/cloud/manager.py:53 msgid "Account unavailable" msgstr "利用できないアカウント" -#: xpack.bak/plugins/cloud/meta.py:9 +#: xpack/plugins/cloud/meta.py:9 msgid "Cloud center" msgstr "クラウドセンター" -#: xpack.bak/plugins/cloud/models.py:34 +#: xpack/plugins/cloud/models.py:34 msgid "Provider" msgstr "プロバイダー" -#: xpack.bak/plugins/cloud/models.py:38 +#: xpack/plugins/cloud/models.py:38 msgid "Validity" msgstr "有効性" -#: xpack.bak/plugins/cloud/models.py:43 +#: xpack/plugins/cloud/models.py:43 msgid "Cloud account" msgstr "クラウドアカウント" -#: xpack.bak/plugins/cloud/models.py:45 +#: xpack/plugins/cloud/models.py:45 msgid "Test cloud account" msgstr "クラウドアカウントのテスト" -#: xpack.bak/plugins/cloud/models.py:92 -#: xpack.bak/plugins/cloud/serializers/task.py:151 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 msgid "Regions" msgstr "リージョン" -#: xpack.bak/plugins/cloud/models.py:95 +#: xpack/plugins/cloud/models.py:95 msgid "Hostname strategy" msgstr "ホスト名戦略" -#: xpack.bak/plugins/cloud/models.py:100 -#: xpack.bak/plugins/cloud/serializers/task.py:154 +#: xpack/plugins/cloud/models.py:100 +#: xpack/plugins/cloud/serializers/task.py:154 msgid "IP network segment group" msgstr "IPネットワークセグメントグループ" -#: xpack.bak/plugins/cloud/models.py:103 -#: xpack.bak/plugins/cloud/serializers/task.py:159 +#: xpack/plugins/cloud/models.py:103 +#: xpack/plugins/cloud/serializers/task.py:159 msgid "Sync IP type" msgstr "同期IPタイプ" -#: xpack.bak/plugins/cloud/models.py:106 -#: xpack.bak/plugins/cloud/serializers/task.py:177 +#: xpack/plugins/cloud/models.py:106 +#: xpack/plugins/cloud/serializers/task.py:177 msgid "Always update" msgstr "常に更新" -#: xpack.bak/plugins/cloud/models.py:112 +#: xpack/plugins/cloud/models.py:112 msgid "Date last sync" msgstr "最終同期日" -#: xpack.bak/plugins/cloud/models.py:115 xpack.bak/plugins/cloud/models.py:313 -#: xpack.bak/plugins/cloud/models.py:337 +#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 +#: xpack/plugins/cloud/models.py:337 msgid "Strategy" msgstr "戦略" -#: xpack.bak/plugins/cloud/models.py:120 xpack.bak/plugins/cloud/models.py:197 +#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 msgid "Sync instance task" msgstr "インスタンスの同期タスク" -#: xpack.bak/plugins/cloud/models.py:208 xpack.bak/plugins/cloud/models.py:260 +#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 msgid "Date sync" msgstr "日付の同期" -#: xpack.bak/plugins/cloud/models.py:212 +#: xpack/plugins/cloud/models.py:212 msgid "Sync instance snapshot" msgstr "インスタンススナップショットの同期" -#: xpack.bak/plugins/cloud/models.py:216 +#: xpack/plugins/cloud/models.py:216 msgid "Sync instance task execution" msgstr "インスタンスタスクの同期実行" -#: xpack.bak/plugins/cloud/models.py:240 +#: xpack/plugins/cloud/models.py:240 msgid "Sync task" msgstr "同期タスク" -#: xpack.bak/plugins/cloud/models.py:244 +#: xpack/plugins/cloud/models.py:244 msgid "Sync instance task history" msgstr "インスタンスタスク履歴の同期" -#: xpack.bak/plugins/cloud/models.py:247 +#: xpack/plugins/cloud/models.py:247 msgid "Instance" msgstr "インスタンス" -#: xpack.bak/plugins/cloud/models.py:264 +#: xpack/plugins/cloud/models.py:264 msgid "Sync instance detail" msgstr "同期インスタンスの詳細" -#: xpack.bak/plugins/cloud/models.py:281 +#: xpack/plugins/cloud/models.py:281 msgid "Task strategy" msgstr "ミッション戦略です" -#: xpack.bak/plugins/cloud/models.py:285 +#: xpack/plugins/cloud/models.py:285 msgid "Exact" msgstr "" -#: xpack.bak/plugins/cloud/models.py:286 +#: xpack/plugins/cloud/models.py:286 msgid "Not" msgstr "否" -#: xpack.bak/plugins/cloud/models.py:287 +#: xpack/plugins/cloud/models.py:287 msgid "In" msgstr "イン" -#: xpack.bak/plugins/cloud/models.py:288 +#: xpack/plugins/cloud/models.py:288 msgid "Contains" msgstr "含む" -#: xpack.bak/plugins/cloud/models.py:289 +#: xpack/plugins/cloud/models.py:289 msgid "Startswith" msgstr "始まる" -#: xpack.bak/plugins/cloud/models.py:290 +#: xpack/plugins/cloud/models.py:290 msgid "Endswith" msgstr "終わる" -#: xpack.bak/plugins/cloud/models.py:296 +#: xpack/plugins/cloud/models.py:296 msgid "Instance platform" msgstr "インスタンス名" -#: xpack.bak/plugins/cloud/models.py:297 +#: xpack/plugins/cloud/models.py:297 msgid "Instance address" msgstr "インスタンスアドレス" -#: xpack.bak/plugins/cloud/models.py:304 +#: xpack/plugins/cloud/models.py:304 msgid "Rule attr" msgstr "ルール属性" -#: xpack.bak/plugins/cloud/models.py:308 +#: xpack/plugins/cloud/models.py:308 msgid "Rule match" msgstr "ルール一致" -#: xpack.bak/plugins/cloud/models.py:310 +#: xpack/plugins/cloud/models.py:310 msgid "Rule value" msgstr "ルール値" -#: xpack.bak/plugins/cloud/models.py:317 -#: xpack.bak/plugins/cloud/serializers/task.py:70 +#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 msgid "Strategy rule" msgstr "戦略ルール" -#: xpack.bak/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/models.py:332 msgid "Action attr" msgstr "アクション属性" -#: xpack.bak/plugins/cloud/models.py:334 +#: xpack/plugins/cloud/models.py:334 msgid "Action value" msgstr "アクション値" -#: xpack.bak/plugins/cloud/models.py:341 -#: xpack.bak/plugins/cloud/serializers/task.py:73 +#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 msgid "Strategy action" msgstr "戦略アクション" -#: xpack.bak/plugins/cloud/providers/aws_international.py:18 +#: xpack/plugins/cloud/providers/aws_international.py:18 msgid "China (Beijing)" msgstr "中国 (北京)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:19 +#: xpack/plugins/cloud/providers/aws_international.py:19 msgid "China (Ningxia)" msgstr "中国 (寧夏)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:22 +#: xpack/plugins/cloud/providers/aws_international.py:22 msgid "US East (Ohio)" msgstr "米国東部 (オハイオ州)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:23 +#: xpack/plugins/cloud/providers/aws_international.py:23 msgid "US East (N. Virginia)" msgstr "米国東部 (N. バージニア州)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:24 +#: xpack/plugins/cloud/providers/aws_international.py:24 msgid "US West (N. California)" msgstr "米国西部 (N. カリフォルニア州)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:25 +#: xpack/plugins/cloud/providers/aws_international.py:25 msgid "US West (Oregon)" msgstr "米国西部 (オレゴン州)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:26 +#: xpack/plugins/cloud/providers/aws_international.py:26 msgid "Africa (Cape Town)" msgstr "アフリカ (ケープタウン)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:27 +#: xpack/plugins/cloud/providers/aws_international.py:27 msgid "Asia Pacific (Hong Kong)" msgstr "アジアパシフィック (香港)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:28 +#: xpack/plugins/cloud/providers/aws_international.py:28 msgid "Asia Pacific (Mumbai)" msgstr "アジア太平洋 (ムンバイ)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:29 +#: xpack/plugins/cloud/providers/aws_international.py:29 msgid "Asia Pacific (Osaka-Local)" msgstr "アジアパシフィック (大阪-ローカル)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:30 +#: xpack/plugins/cloud/providers/aws_international.py:30 msgid "Asia Pacific (Seoul)" msgstr "アジア太平洋地域 (ソウル)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:31 +#: xpack/plugins/cloud/providers/aws_international.py:31 msgid "Asia Pacific (Singapore)" msgstr "アジア太平洋 (シンガポール)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:32 +#: xpack/plugins/cloud/providers/aws_international.py:32 msgid "Asia Pacific (Sydney)" msgstr "アジア太平洋 (シドニー)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:33 +#: xpack/plugins/cloud/providers/aws_international.py:33 msgid "Asia Pacific (Tokyo)" msgstr "アジアパシフィック (東京)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:34 +#: xpack/plugins/cloud/providers/aws_international.py:34 msgid "Canada (Central)" msgstr "カナダ (中央)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:35 +#: xpack/plugins/cloud/providers/aws_international.py:35 msgid "Europe (Frankfurt)" msgstr "ヨーロッパ (フランクフルト)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:36 +#: xpack/plugins/cloud/providers/aws_international.py:36 msgid "Europe (Ireland)" msgstr "ヨーロッパ (アイルランド)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:37 +#: xpack/plugins/cloud/providers/aws_international.py:37 msgid "Europe (London)" msgstr "ヨーロッパ (ロンドン)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:38 +#: xpack/plugins/cloud/providers/aws_international.py:38 msgid "Europe (Milan)" msgstr "ヨーロッパ (ミラノ)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:39 +#: xpack/plugins/cloud/providers/aws_international.py:39 msgid "Europe (Paris)" msgstr "ヨーロッパ (パリ)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:40 +#: xpack/plugins/cloud/providers/aws_international.py:40 msgid "Europe (Stockholm)" msgstr "ヨーロッパ (ストックホルム)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:41 +#: xpack/plugins/cloud/providers/aws_international.py:41 msgid "Middle East (Bahrain)" msgstr "中东 (バーレーン)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:42 +#: xpack/plugins/cloud/providers/aws_international.py:42 msgid "South America (São Paulo)" msgstr "南米 (サンパウロ)" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:54 -#: xpack.bak/plugins/cloud/providers/jdcloud.py:125 +#: xpack/plugins/cloud/providers/baiducloud.py:54 +#: xpack/plugins/cloud/providers/jdcloud.py:125 msgid "CN North-Beijing" msgstr "華北-北京" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:55 -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:42 -#: xpack.bak/plugins/cloud/providers/jdcloud.py:128 +#: xpack/plugins/cloud/providers/baiducloud.py:55 +#: xpack/plugins/cloud/providers/huaweicloud.py:42 +#: xpack/plugins/cloud/providers/jdcloud.py:128 msgid "CN South-Guangzhou" msgstr "華南-広州" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:56 +#: xpack/plugins/cloud/providers/baiducloud.py:56 msgid "CN East-Suzhou" msgstr "華東-蘇州" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:57 -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:49 +#: xpack/plugins/cloud/providers/baiducloud.py:57 +#: xpack/plugins/cloud/providers/huaweicloud.py:49 msgid "CN-Hong Kong" msgstr "中国-香港" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:58 +#: xpack/plugins/cloud/providers/baiducloud.py:58 msgid "CN Center-Wuhan" msgstr "華中-武漢" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:59 +#: xpack/plugins/cloud/providers/baiducloud.py:59 msgid "CN North-Baoding" msgstr "華北-保定" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:60 -#: xpack.bak/plugins/cloud/providers/jdcloud.py:127 +#: xpack/plugins/cloud/providers/baiducloud.py:60 +#: xpack/plugins/cloud/providers/jdcloud.py:127 msgid "CN East-Shanghai" msgstr "華東-上海" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:61 -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:51 +#: xpack/plugins/cloud/providers/baiducloud.py:61 +#: xpack/plugins/cloud/providers/huaweicloud.py:51 msgid "AP-Singapore" msgstr "アジア太平洋-シンガポール" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:39 +#: xpack/plugins/cloud/providers/huaweicloud.py:39 msgid "CN North-Beijing1" msgstr "華北-北京1" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:40 +#: xpack/plugins/cloud/providers/huaweicloud.py:40 msgid "CN North-Beijing4" msgstr "華北-北京4" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:41 +#: xpack/plugins/cloud/providers/huaweicloud.py:41 msgid "CN North-Ulanqab1" msgstr "華北-ウランチャブ一" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:43 +#: xpack/plugins/cloud/providers/huaweicloud.py:43 msgid "CN South-Shenzhen" msgstr "華南-広州" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:44 +#: xpack/plugins/cloud/providers/huaweicloud.py:44 msgid "CN South-Guangzhou-InvitationOnly" msgstr "華南-広州-友好ユーザー環境" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:45 +#: xpack/plugins/cloud/providers/huaweicloud.py:45 msgid "CN East-Shanghai2" msgstr "華東-上海2" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:46 +#: xpack/plugins/cloud/providers/huaweicloud.py:46 msgid "CN East-Shanghai1" msgstr "華東-上海1" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:48 +#: xpack/plugins/cloud/providers/huaweicloud.py:48 msgid "CN Southwest-Guiyang1" msgstr "南西-貴陽1" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:50 +#: xpack/plugins/cloud/providers/huaweicloud.py:50 msgid "AP-Bangkok" msgstr "アジア太平洋-バンコク" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:53 +#: xpack/plugins/cloud/providers/huaweicloud.py:53 msgid "AF-Johannesburg" msgstr "アフリカ-ヨハネスブルク" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:54 +#: xpack/plugins/cloud/providers/huaweicloud.py:54 msgid "LA-Mexico City1" msgstr "LA-メキシコCity1" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:55 +#: xpack/plugins/cloud/providers/huaweicloud.py:55 msgid "LA-Santiago" msgstr "ラテンアメリカ-サンディエゴ" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:56 +#: xpack/plugins/cloud/providers/huaweicloud.py:56 msgid "LA-Sao Paulo1" msgstr "ラミー・サンパウロ1" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:58 +#: xpack/plugins/cloud/providers/huaweicloud.py:58 msgid "TR-Istanbul" msgstr "" -#: xpack.bak/plugins/cloud/providers/jdcloud.py:126 +#: xpack/plugins/cloud/providers/jdcloud.py:126 msgid "CN East-Suqian" msgstr "華東-宿遷" -#: xpack.bak/plugins/cloud/serializers/account.py:64 +#: xpack/plugins/cloud/serializers/account.py:64 msgid "Validity display" msgstr "有効表示" -#: xpack.bak/plugins/cloud/serializers/account.py:65 +#: xpack/plugins/cloud/serializers/account.py:65 msgid "Provider display" msgstr "プロバイダ表示" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:35 +#: xpack/plugins/cloud/serializers/account_attrs.py:35 msgid "Client ID" msgstr "クライアントID" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:41 +#: xpack/plugins/cloud/serializers/account_attrs.py:41 msgid "Tenant ID" msgstr "テナントID" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:44 +#: xpack/plugins/cloud/serializers/account_attrs.py:44 msgid "Subscription ID" msgstr "サブスクリプションID" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:98 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:103 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:119 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:149 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:199 +#: xpack/plugins/cloud/serializers/account_attrs.py:98 +#: xpack/plugins/cloud/serializers/account_attrs.py:103 +#: xpack/plugins/cloud/serializers/account_attrs.py:119 +#: xpack/plugins/cloud/serializers/account_attrs.py:149 +#: xpack/plugins/cloud/serializers/account_attrs.py:199 msgid "API Endpoint" msgstr "APIエンドポイント" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:109 +#: xpack/plugins/cloud/serializers/account_attrs.py:109 msgid "Auth url" msgstr "認証アドレス" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:110 +#: xpack/plugins/cloud/serializers/account_attrs.py:110 msgid "eg: http://openstack.example.com:5000/v3" msgstr "例えば: http://openstack.example.com:5000/v3" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:113 +#: xpack/plugins/cloud/serializers/account_attrs.py:113 msgid "User domain" msgstr "ユーザードメイン" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:120 +#: xpack/plugins/cloud/serializers/account_attrs.py:120 msgid "Cert File" msgstr "証明書ファイル" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:121 +#: xpack/plugins/cloud/serializers/account_attrs.py:121 msgid "Key File" msgstr "キーファイル" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:137 +#: xpack/plugins/cloud/serializers/account_attrs.py:137 msgid "Service account key" msgstr "サービスアカウントキー" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:138 +#: xpack/plugins/cloud/serializers/account_attrs.py:138 msgid "The file is in JSON format" msgstr "ファイルはJSON形式です。" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:156 +#: xpack/plugins/cloud/serializers/account_attrs.py:156 msgid "IP address invalid `{}`, {}" msgstr "IPアドレスが無効: '{}', {}" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:172 +#: xpack/plugins/cloud/serializers/account_attrs.py:172 msgid "Such as: 192.168.1.0/24, 10.0.0.0-10.0.0.255" msgstr "例:192.168.1.0/24、10.0.0.0.0-10.0.0.255" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:175 +#: xpack/plugins/cloud/serializers/account_attrs.py:175 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -8429,27 +8442,27 @@ msgstr "" "実行されると、有効な IP アドレスのみが同期されます。
ポートが0の場合、す" "べてのIPアドレスが有効です。" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:183 +#: xpack/plugins/cloud/serializers/account_attrs.py:183 msgid "Hostname prefix" msgstr "ホスト名プレフィックス" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:186 +#: xpack/plugins/cloud/serializers/account_attrs.py:186 msgid "IP segment" msgstr "IP セグメント" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:190 +#: xpack/plugins/cloud/serializers/account_attrs.py:190 msgid "Test port" msgstr "テストポート" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:193 +#: xpack/plugins/cloud/serializers/account_attrs.py:193 msgid "Test timeout" msgstr "テストタイムアウト" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:209 +#: xpack/plugins/cloud/serializers/account_attrs.py:209 msgid "Project" msgstr "project" -#: xpack.bak/plugins/cloud/serializers/task.py:143 +#: xpack/plugins/cloud/serializers/task.py:143 msgid "" "Only instances matching the IP range will be synced.
If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8463,84 +8476,83 @@ msgstr "" "ドレスをランダムに一致させることを意味します。
例: " "192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack.bak/plugins/cloud/serializers/task.py:149 +#: xpack/plugins/cloud/serializers/task.py:149 msgid "History count" msgstr "実行回数" -#: xpack.bak/plugins/cloud/serializers/task.py:150 +#: xpack/plugins/cloud/serializers/task.py:150 msgid "Instance count" msgstr "インスタンス数" -#: xpack.bak/plugins/cloud/tasks.py:27 +#: xpack/plugins/cloud/tasks.py:27 msgid "Run sync instance task" msgstr "同期インスタンス タスクを実行する" -#: xpack.bak/plugins/cloud/tasks.py:41 +#: xpack/plugins/cloud/tasks.py:41 msgid "Period clean sync instance task execution" msgstr "同期インスタンス タスクの実行記録を定期的にクリアする" -#: xpack.bak/plugins/interface/api.py:52 +#: xpack/plugins/interface/api.py:52 msgid "Restore default successfully." msgstr "デフォルトの復元に成功しました。" -#: xpack.bak/plugins/interface/meta.py:9 +#: xpack/plugins/interface/meta.py:9 msgid "Interface settings" msgstr "インターフェイスの設定" -#: xpack.bak/plugins/interface/models.py:23 +#: xpack/plugins/interface/models.py:23 msgid "Title of login page" msgstr "ログインページのタイトル" -#: xpack.bak/plugins/interface/models.py:27 +#: xpack/plugins/interface/models.py:27 msgid "Image of login page" msgstr "ログインページのイメージ" -#: xpack.bak/plugins/interface/models.py:31 +#: xpack/plugins/interface/models.py:31 msgid "Website icon" msgstr "ウェブサイトのアイコン" -#: xpack.bak/plugins/interface/models.py:35 +#: xpack/plugins/interface/models.py:35 msgid "Logo of management page" msgstr "管理ページのロゴ" -#: xpack.bak/plugins/interface/models.py:39 +#: xpack/plugins/interface/models.py:39 msgid "Logo of logout page" msgstr "ログアウトページのロゴ" -#: xpack.bak/plugins/interface/models.py:41 +#: xpack/plugins/interface/models.py:41 msgid "Theme" msgstr "テーマ" -#: xpack.bak/plugins/interface/models.py:44 -#: xpack.bak/plugins/interface/models.py:85 +#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 msgid "Interface setting" msgstr "インターフェイスの設定" -#: xpack.bak/plugins/license/api.py:52 +#: xpack/plugins/license/api.py:52 msgid "License import successfully" msgstr "ライセンスのインポートに成功" -#: xpack.bak/plugins/license/api.py:53 +#: xpack/plugins/license/api.py:53 msgid "License is invalid" msgstr "ライセンスが無効です" -#: xpack.bak/plugins/license/meta.py:10 xpack.bak/plugins/license/models.py:140 +#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 msgid "License" msgstr "ライセンス" -#: xpack.bak/plugins/license/models.py:80 +#: xpack/plugins/license/models.py:80 msgid "Basic edition" msgstr "エンタープライズ基本版" -#: xpack.bak/plugins/license/models.py:82 +#: xpack/plugins/license/models.py:82 msgid "Standard edition" msgstr "エンタープライズ標準版" -#: xpack.bak/plugins/license/models.py:84 +#: xpack/plugins/license/models.py:84 msgid "Professional edition" msgstr "エンタープライズプロフェッショナル版" -#: xpack.bak/plugins/license/models.py:86 +#: xpack/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 20a804924..213d99173 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:e8df7ef115d111ee9a176fac269a41a87d6b9a76a521e0206fe29591ab16da69 -size 132482 +oid sha256:06ff4e3474944be5a7f95106ecdeaa88d66be345fa69e1e62ec5d8c27be580ea +size 132912 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 4345e5524..904b5a1e1 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: 2023-10-07 13:50+0800\n" +"POT-Creation-Date: 2023-10-07 17:56+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -30,7 +30,7 @@ msgstr "参数 'action' 必须是 [{}]" #: users/forms/profile.py:22 users/serializers/user.py:105 #: users/templates/users/_msg_user_created.html:13 #: users/templates/users/user_password_verify.html:18 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:28 +#: xpack/plugins/cloud/serializers/account_attrs.py:28 msgid "Password" msgstr "密码" @@ -93,9 +93,9 @@ msgid "Update" msgstr "更新" #: accounts/const/account.py:33 -#: accounts/serializers/automations/change_secret.py:155 audits/const.py:55 +#: accounts/serializers/automations/change_secret.py:155 audits/const.py:59 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:77 xpack.bak/plugins/cloud/const.py:43 +#: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 msgid "Failed" msgstr "失败" @@ -231,8 +231,7 @@ msgstr "用户 %s 查看/导出 了密码" #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 -#: xpack.bak/plugins/cloud/models.py:253 +#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 msgid "Asset" msgstr "资产" @@ -268,8 +267,7 @@ msgstr "来源 ID" #: terminal/backends/command/models.py:18 terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 -#: tickets/models/ticket/command_confirm.py:13 -#: xpack.bak/plugins/cloud/models.py:89 +#: tickets/models/ticket/command_confirm.py:13 xpack/plugins/cloud/models.py:89 msgid "Account" msgstr "账号" @@ -336,7 +334,7 @@ msgid "Trigger mode" msgstr "触发模式" #: accounts/models/automations/backup_account.py:105 audits/models.py:197 -#: terminal/models/session/sharing.py:125 xpack.bak/plugins/cloud/models.py:205 +#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "原因" @@ -443,14 +441,15 @@ msgstr "最后登录日期" #: accounts/models/automations/gather_account.py:17 #: accounts/models/automations/push_account.py:15 accounts/models/base.py:65 #: accounts/serializers/account/virtual.py:21 acls/serializers/base.py:19 -#: acls/serializers/base.py:50 acls/templates/acls/user_login_reminder.html:5 -#: assets/models/_user.py:23 audits/models.py:182 authentication/forms.py:25 -#: authentication/forms.py:27 authentication/models/temp_token.py:9 +#: acls/serializers/base.py:50 acls/templates/acls/asset_login_reminder.html:5 +#: acls/templates/acls/user_login_reminder.html:5 assets/models/_user.py:23 +#: audits/models.py:182 authentication/forms.py:25 authentication/forms.py:27 +#: authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: users/forms/profile.py:32 users/forms/profile.py:115 #: users/models/user.py:790 users/templates/users/_msg_user_created.html:12 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:26 +#: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" msgstr "用户名" @@ -538,9 +537,8 @@ msgstr "密码规则" #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 users/forms/profile.py:33 #: users/models/group.py:13 users/models/preference.py:11 -#: users/models/user.py:792 xpack.bak/plugins/cloud/models.py:32 -#: xpack.bak/plugins/cloud/models.py:273 -#: xpack.bak/plugins/cloud/serializers/task.py:68 +#: users/models/user.py:792 xpack/plugins/cloud/models.py:32 +#: xpack/plugins/cloud/models.py:273 xpack/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名称" @@ -569,7 +567,7 @@ msgstr "系统平台" msgid "Push params" msgstr "账号推送参数" -#: accounts/models/template.py:25 xpack.bak/plugins/cloud/models.py:325 +#: accounts/models/template.py:25 xpack/plugins/cloud/models.py:325 msgid "Account template" msgstr "账号模版" @@ -708,8 +706,10 @@ msgstr "已修改" #: accounts/serializers/account/account.py:267 #: accounts/serializers/automations/base.py:22 acls/models/base.py:97 +#: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 -#: assets/serializers/automations/base.py:20 ops/models/base.py:17 +#: assets/serializers/automations/base.py:20 +#: authentication/api/connection_token.py:381 ops/models/base.py:17 #: ops/models/job.py:139 ops/serializers/job.py:21 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" @@ -842,7 +842,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:828 -#: xpack.bak/plugins/cloud/models.py:39 xpack.bak/plugins/cloud/models.py:109 +#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "备注" @@ -890,7 +890,7 @@ msgstr "* 密码长度范围 6-30 位" msgid "Automation task execution" msgstr "自动化任务执行历史" -#: accounts/serializers/automations/change_secret.py:154 audits/const.py:54 +#: accounts/serializers/automations/change_secret.py:154 audits/const.py:58 #: audits/models.py:62 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: terminal/const.py:76 terminal/models/session/sharing.py:121 @@ -965,16 +965,16 @@ msgstr "密钥不合法或密钥密码错误" msgid "Acls" msgstr "访问控制" -#: acls/const.py:6 terminal/const.py:11 tickets/const.py:45 +#: acls/const.py:6 audits/const.py:33 terminal/const.py:11 tickets/const.py:45 #: tickets/templates/tickets/approve_check_password.html:49 msgid "Reject" msgstr "拒绝" -#: acls/const.py:7 terminal/const.py:9 +#: acls/const.py:7 audits/const.py:34 terminal/const.py:9 msgid "Accept" msgstr "接受" -#: acls/const.py:8 +#: acls/const.py:8 audits/const.py:35 msgid "Review" msgstr "审批" @@ -988,13 +988,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:97 -#: xpack.bak/plugins/cloud/models.py:275 +#: xpack/plugins/cloud/models.py:275 msgid "Priority" msgstr "优先级" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack.bak/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/models.py:276 msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" @@ -1031,12 +1031,12 @@ msgid "Command" msgstr "命令" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack.bak/plugins/cloud/models.py:291 +#: xpack/plugins/cloud/models.py:291 msgid "Regex" msgstr "正则表达式" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/feature.py:17 xpack.bak/plugins/license/models.py:30 +#: settings/serializers/feature.py:17 xpack/plugins/license/models.py:30 msgid "Content" msgstr "内容" @@ -1097,10 +1097,14 @@ msgstr "登录资产访问控制" msgid "Login asset confirm" msgstr "登录资产复核" -#: acls/notifications.py:10 +#: acls/notifications.py:11 msgid "User login reminder" msgstr "用户登录提醒" +#: acls/notifications.py:42 +msgid "Asset login reminder" +msgstr "资产登录提醒" + #: acls/serializers/base.py:11 acls/serializers/login_acl.py:11 msgid "With * indicating a match all. " msgstr "* 表示匹配所有. " @@ -1131,7 +1135,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "所有复核人都不属于组织 `{}`" #: acls/serializers/rules/rules.py:20 -#: xpack.bak/plugins/cloud/serializers/task.py:137 +#: xpack/plugins/cloud/serializers/task.py:137 msgid "IP address invalid: `{}`" msgstr "IP 地址无效: `{}`" @@ -1155,10 +1159,25 @@ msgstr "IP" msgid "Time Period" msgstr "时段" +#: acls/templates/acls/asset_login_reminder.html:3 #: acls/templates/acls/user_login_reminder.html:3 msgid "Respectful" msgstr "尊敬的" +#: acls/templates/acls/asset_login_reminder.html:9 +msgid "" +"The user has just successfully logged into the asset. Please ensure that " +"this is an authorized operation. If you suspect that this is an unauthorized " +"access, please take appropriate measures immediately." +msgstr "" +"用户刚刚成功登录到资产。请确保这是授权的操作。如果您怀疑这是一个未经授权的访" +"问,请立即采取适当的措施。" + +#: acls/templates/acls/asset_login_reminder.html:11 +#: acls/templates/acls/user_login_reminder.html:13 +msgid "Thank you" +msgstr "谢谢" + #: acls/templates/acls/user_login_reminder.html:7 audits/models.py:188 #: audits/models.py:257 #: authentication/templates/authentication/_msg_different_city.html:11 @@ -1180,16 +1199,12 @@ msgstr "" "用户刚刚成功登录到系统。请确保这是授权的操作。如果您怀疑这是一个未经授权的访" "问,请立即采取适当的措施。" -#: acls/templates/acls/user_login_reminder.html:13 -msgid "Thank you" -msgstr "谢谢" - #: applications/apps.py:9 msgid "Applications" msgstr "应用管理" -#: applications/models.py:16 xpack.bak/plugins/cloud/models.py:37 -#: xpack.bak/plugins/cloud/serializers/account.py:63 +#: applications/models.py:16 xpack/plugins/cloud/models.py:37 +#: xpack/plugins/cloud/serializers/account.py:63 msgid "Attrs" msgstr "属性" @@ -1252,7 +1267,7 @@ msgid "Unable to connect to port {port} on {address}" msgstr "无法连接到 {port} 上的端口 {address}" #: assets/automations/ping_gateway/manager.py:58 -#: authentication/middleware.py:92 xpack.bak/plugins/cloud/providers/fc.py:47 +#: authentication/middleware.py:92 xpack/plugins/cloud/providers/fc.py:47 msgid "Authentication failed" msgstr "认证失败" @@ -1261,7 +1276,7 @@ msgstr "认证失败" msgid "Connect failed" msgstr "连接失败" -#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:37 +#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:41 #: audits/signal_handlers/activity_log.py:62 common/utils/ip/geoip/utils.py:31 #: common/utils/ip/geoip/utils.py:37 common/utils/ip/utils.py:104 msgid "Unknown" @@ -1283,7 +1298,7 @@ msgstr "测试网关" msgid "Gather facts" msgstr "收集资产信息" -#: assets/const/base.py:33 audits/const.py:48 +#: assets/const/base.py:33 audits/const.py:52 #: terminal/serializers/applet_host.py:32 msgid "Disabled" msgstr "禁用" @@ -1304,7 +1319,7 @@ msgstr "脚本" #: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:67 #: settings/serializers/feature.py:47 terminal/models/component/endpoint.py:13 #: terminal/serializers/applet.py:17 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:72 +#: xpack/plugins/cloud/serializers/account_attrs.py:72 msgid "Host" msgstr "主机" @@ -1317,7 +1332,7 @@ msgid "Cloud service" msgstr "云服务" #: assets/const/category.py:14 assets/models/asset/gpt.py:11 -#: assets/models/asset/web.py:16 audits/const.py:35 +#: assets/models/asset/web.py:16 audits/const.py:39 #: terminal/models/applet/applet.py:27 users/const.py:47 msgid "Web" msgstr "Web" @@ -1593,7 +1608,7 @@ msgstr "云服务" #: assets/models/asset/common.py:92 assets/models/platform.py:16 #: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:68 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:73 +#: xpack/plugins/cloud/serializers/account_attrs.py:73 msgid "Port" msgstr "端口" @@ -1604,15 +1619,13 @@ msgstr "地址" #: assets/models/asset/common.py:151 assets/models/platform.py:119 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:115 -#: perms/serializers/user_permission.py:24 -#: xpack.bak/plugins/cloud/models.py:321 +#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 msgid "Platform" msgstr "系统平台" #: assets/models/asset/common.py:153 assets/models/domain.py:21 #: authentication/serializers/connect_token_secret.py:133 -#: perms/serializers/user_permission.py:29 -#: xpack.bak/plugins/cloud/models.py:323 +#: perms/serializers/user_permission.py:29 xpack/plugins/cloud/models.py:323 msgid "Domain" msgstr "网域" @@ -1688,8 +1701,8 @@ msgstr "资产自动化任务" #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:115 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 -#: xpack.bak/plugins/cloud/models.py:201 xpack.bak/plugins/cloud/models.py:257 +#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 +#: xpack/plugins/cloud/models.py:257 msgid "Status" msgstr "状态" @@ -1753,7 +1766,7 @@ msgstr "资产组" #: assets/models/group.py:31 assets/models/platform.py:19 #: assets/serializers/platform.py:114 -#: xpack.bak/plugins/cloud/providers/nutanix.py:30 +#: xpack/plugins/cloud/providers/nutanix.py:30 msgid "Default" msgstr "默认" @@ -1804,8 +1817,7 @@ msgid "Parent key" msgstr "ssh私钥" #: assets/models/node.py:553 perms/serializers/permission.py:35 -#: tickets/models/ticket/apply_asset.py:14 -#: xpack.bak/plugins/cloud/models.py:322 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 msgid "Node" msgstr "节点" @@ -1831,7 +1843,7 @@ msgstr "开放的" msgid "Setting" msgstr "设置" -#: assets/models/platform.py:38 audits/const.py:49 +#: assets/models/platform.py:38 audits/const.py:53 #: authentication/backends/passkey/models.py:11 settings/models.py:36 #: terminal/serializers/applet_host.py:33 msgid "Enabled" @@ -1945,9 +1957,8 @@ msgstr "资产中批量更新平台,不符合平台类型跳过的资产" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:134 #: authentication/serializers/connect_token_secret.py:29 #: authentication/serializers/connect_token_secret.py:72 -#: perms/serializers/user_permission.py:25 -#: xpack.bak/plugins/cloud/models.py:324 -#: xpack.bak/plugins/cloud/serializers/task.py:31 +#: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:324 +#: xpack/plugins/cloud/serializers/task.py:31 msgid "Protocols" msgstr "协议组" @@ -1980,9 +1991,9 @@ msgstr "默认数据库" #: assets/serializers/asset/database.py:28 common/db/fields.py:570 #: common/db/fields.py:575 common/serializers/fields.py:104 #: tickets/serializers/ticket/common.py:58 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:56 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:79 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:143 +#: xpack/plugins/cloud/serializers/account_attrs.py:56 +#: xpack/plugins/cloud/serializers/account_attrs.py:79 +#: xpack/plugins/cloud/serializers/account_attrs.py:143 msgid "This field is required." msgstr "该字段是必填项。" @@ -2237,7 +2248,7 @@ msgid "Symlink" msgstr "建立软链接" #: audits/const.py:18 audits/const.py:28 perms/const.py:14 -#: terminal/api/session/session.py:141 +#: terminal/api/session/session.py:146 msgid "Download" msgstr "下载" @@ -2245,7 +2256,7 @@ msgstr "下载" msgid "Rename dir" msgstr "映射目录" -#: audits/const.py:23 rbac/tree.py:233 terminal/api/session/session.py:252 +#: audits/const.py:23 rbac/tree.py:233 terminal/api/session/session.py:257 #: terminal/templates/terminal/_msg_command_warning.html:18 #: terminal/templates/terminal/_msg_session_sharing.html:10 msgid "View" @@ -2271,30 +2282,30 @@ msgstr "登录" msgid "Change password" msgstr "改密" -#: audits/const.py:36 settings/serializers/terminal.py:6 +#: audits/const.py:40 settings/serializers/terminal.py:6 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 #: terminal/serializers/session.py:49 terminal/serializers/session.py:63 msgid "Terminal" msgstr "终端" -#: audits/const.py:41 audits/models.py:130 +#: audits/const.py:45 audits/models.py:130 msgid "Operate log" msgstr "操作日志" -#: audits/const.py:42 +#: audits/const.py:46 msgid "Session log" msgstr "会话日志" -#: audits/const.py:43 +#: audits/const.py:47 msgid "Login log" msgstr "登录日志" -#: audits/const.py:44 terminal/models/applet/host.py:143 +#: audits/const.py:48 terminal/models/applet/host.py:143 #: terminal/models/component/task.py:22 msgid "Task" msgstr "任务" -#: audits/const.py:50 +#: audits/const.py:54 msgid "-" msgstr "-" @@ -2506,27 +2517,27 @@ msgstr "上传 FTP 文件到外部存储" msgid "This action require verify your MFA" msgstr "该操作需要验证您的 MFA, 请先开启并配置" -#: authentication/api/connection_token.py:260 +#: authentication/api/connection_token.py:261 msgid "Reusable connection token is not allowed, global setting not enabled" msgstr "不允许使用可重复使用的连接令牌,未启用全局设置" -#: authentication/api/connection_token.py:351 +#: authentication/api/connection_token.py:352 msgid "Anonymous account is not supported for this asset" msgstr "匿名账号不支持当前资产" -#: authentication/api/connection_token.py:370 +#: authentication/api/connection_token.py:371 msgid "Account not found" msgstr "账号未找到" -#: authentication/api/connection_token.py:373 +#: authentication/api/connection_token.py:374 msgid "Permission expired" msgstr "授权已过期" -#: authentication/api/connection_token.py:387 +#: authentication/api/connection_token.py:401 msgid "ACL action is reject: {}({})" msgstr "ACL 动作是拒绝: {}({})" -#: authentication/api/connection_token.py:391 +#: authentication/api/connection_token.py:405 msgid "ACL action is review" msgstr "ACL 动作是复核" @@ -2534,7 +2545,7 @@ msgstr "ACL 动作是复核" msgid "Current user not support mfa type: {}" msgstr "当前用户不支持 MFA 类型: {}" -#: authentication/api/password.py:34 terminal/api/session/session.py:300 +#: authentication/api/password.py:34 terminal/api/session/session.py:305 #: users/views/profile/reset.py:61 msgid "User does not exist: {}" msgstr "用户不存在: {}" @@ -2791,6 +2802,7 @@ msgid "Your password is invalid" msgstr "您的密码无效" #: authentication/errors/mfa.py:43 +#, python-format msgid "Please wait for %s seconds before retry" msgstr "请在 %s 秒后重试" @@ -3308,7 +3320,7 @@ msgid "Do you want to retry ?" msgstr "是否重试 ?" #: authentication/utils.py:28 common/utils/ip/geoip/utils.py:24 -#: xpack.bak/plugins/cloud/const.py:29 +#: xpack/plugins/cloud/const.py:29 msgid "LAN" msgstr "局域网" @@ -3651,7 +3663,7 @@ msgstr "阿里云" msgid "Tencent cloud" msgstr "腾讯云" -#: common/sdk/sms/endpoint.py:18 xpack.bak/plugins/cloud/const.py:13 +#: common/sdk/sms/endpoint.py:18 xpack/plugins/cloud/const.py:13 msgid "Huawei Cloud" msgstr "华为云" @@ -3998,7 +4010,7 @@ msgid "Date last run" msgstr "最后运行日期" #: ops/models/base.py:51 ops/models/job.py:224 -#: xpack.bak/plugins/cloud/models.py:199 +#: xpack/plugins/cloud/models.py:199 msgid "Result" msgstr "结果" @@ -4822,7 +4834,7 @@ msgid "Client Id" msgstr "客户端 ID" #: settings/serializers/auth/oauth2.py:33 settings/serializers/auth/oidc.py:22 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:38 +#: xpack/plugins/cloud/serializers/account_attrs.py:38 msgid "Client Secret" msgstr "客户端密钥" @@ -5087,7 +5099,7 @@ msgid "SSO auth key TTL" msgstr "令牌有效期" #: settings/serializers/auth/sso.py:20 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:193 +#: xpack/plugins/cloud/serializers/account_attrs.py:193 msgid "Unit: second" msgstr "单位: 秒" @@ -5139,44 +5151,48 @@ msgstr "支持链接" msgid "default: http://www.jumpserver.org/support/" msgstr "默认: http://www.jumpserver.org/support/" -#: settings/serializers/cleaning.py:8 +#: settings/serializers/cleaning.py:10 msgid "Period clean" msgstr "定時清掃" -#: settings/serializers/cleaning.py:12 +#: settings/serializers/cleaning.py:14 msgid "Login log keep days (day)" msgstr "登录日志 (天)" -#: settings/serializers/cleaning.py:16 +#: settings/serializers/cleaning.py:18 msgid "Task log keep days (day)" msgstr "任务日志 (天)" -#: settings/serializers/cleaning.py:20 +#: settings/serializers/cleaning.py:22 msgid "Operate log keep days (day)" msgstr "操作日志 (天)" -#: settings/serializers/cleaning.py:24 +#: settings/serializers/cleaning.py:26 msgid "FTP log keep days (day)" msgstr "上传下载 (天)" -#: settings/serializers/cleaning.py:28 +#: settings/serializers/cleaning.py:30 msgid "Cloud sync record keep days (day)" msgstr "云同步记录 (天)" -#: settings/serializers/cleaning.py:31 +#: settings/serializers/cleaning.py:34 +msgid "Activity log keep days (day)" +msgstr "活动记录 (天)" + +#: settings/serializers/cleaning.py:37 msgid "Session keep duration (day)" msgstr "会话日志 (天)" -#: settings/serializers/cleaning.py:33 +#: settings/serializers/cleaning.py:39 msgid "" "Session, record, command will be delete if more than duration, only in " "database, OSS will not be affected." msgstr "" "会话、录像,命令记录超过该时长将会被清除 (影响数据库存储,OSS 等不受影响)" -#: settings/serializers/cleaning.py:37 -msgid "Activity log keep days (day)" -msgstr "活动记录 (天)" +#: settings/serializers/cleaning.py:53 +msgid "must be greater than {} days." +msgstr "" #: settings/serializers/feature.py:16 msgid "Subject" @@ -6026,15 +6042,15 @@ msgstr "有在线会话" msgid "User %s %s session %s replay" msgstr "用户 %s %s 了会话 %s 的录像" -#: terminal/api/session/session.py:292 +#: terminal/api/session/session.py:297 msgid "Session does not exist: {}" msgstr "会话不存在: {}" -#: terminal/api/session/session.py:295 +#: terminal/api/session/session.py:300 msgid "Session is finished or the protocol not supported" msgstr "会话已经完成或协议不支持" -#: terminal/api/session/session.py:308 +#: terminal/api/session/session.py:313 msgid "User does not have permission" msgstr "用户没有权限" @@ -6152,7 +6168,7 @@ msgstr "不支持批量创建" msgid "Storage is invalid" msgstr "存储无效" -#: terminal/models/applet/applet.py:30 xpack.bak/plugins/license/models.py:88 +#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:88 msgid "Community edition" msgstr "社区版" @@ -6673,16 +6689,16 @@ msgid "Bucket" msgstr "桶名称" #: terminal/serializers/storage.py:32 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:17 +#: xpack/plugins/cloud/serializers/account_attrs.py:17 msgid "Access key id" msgstr "Access key ID(AK)" #: terminal/serializers/storage.py:36 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:20 +#: xpack/plugins/cloud/serializers/account_attrs.py:20 msgid "Access key secret" msgstr "Access key secret(SK)" -#: terminal/serializers/storage.py:67 xpack.bak/plugins/cloud/models.py:250 +#: terminal/serializers/storage.py:67 xpack/plugins/cloud/models.py:250 msgid "Region" msgstr "地域" @@ -7262,7 +7278,7 @@ msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" #: users/forms/profile.py:173 users/models/user.py:825 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:203 +#: xpack/plugins/cloud/serializers/account_attrs.py:203 msgid "Public key" msgstr "SSH公钥" @@ -7295,7 +7311,7 @@ msgid "OTP secret key" msgstr "OTP 密钥" #: users/models/user.py:822 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:206 +#: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "Private key" msgstr "ssh私钥" @@ -7769,541 +7785,537 @@ msgstr "* 新密码不能是最近 {} 次的密码" msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: xpack.bak/apps.py:8 +#: xpack/apps.py:8 msgid "XPACK" msgstr "XPack" -#: xpack.bak/plugins/cloud/api.py:56 +#: xpack/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "测试成功" -#: xpack.bak/plugins/cloud/api.py:58 +#: xpack/plugins/cloud/api.py:58 msgid "Test connection failed: {}" msgstr "测试连接失败:{}" -#: xpack.bak/plugins/cloud/const.py:8 +#: xpack/plugins/cloud/const.py:8 msgid "Alibaba Cloud" msgstr "阿里云" -#: xpack.bak/plugins/cloud/const.py:9 +#: xpack/plugins/cloud/const.py:9 msgid "AWS (International)" msgstr "AWS (国际)" -#: xpack.bak/plugins/cloud/const.py:10 +#: xpack/plugins/cloud/const.py:10 msgid "AWS (China)" msgstr "AWS (中国)" -#: xpack.bak/plugins/cloud/const.py:11 +#: xpack/plugins/cloud/const.py:11 msgid "Azure (China)" msgstr "Azure (中国)" -#: xpack.bak/plugins/cloud/const.py:12 +#: xpack/plugins/cloud/const.py:12 msgid "Azure (International)" msgstr "Azure (国际)" -#: xpack.bak/plugins/cloud/const.py:14 +#: xpack/plugins/cloud/const.py:14 msgid "Baidu Cloud" msgstr "百度云" -#: xpack.bak/plugins/cloud/const.py:15 +#: xpack/plugins/cloud/const.py:15 msgid "JD Cloud" msgstr "京东云" -#: xpack.bak/plugins/cloud/const.py:16 +#: xpack/plugins/cloud/const.py:16 msgid "KingSoft Cloud" msgstr "金山云" -#: xpack.bak/plugins/cloud/const.py:17 +#: xpack/plugins/cloud/const.py:17 msgid "Tencent Cloud" msgstr "腾讯云" -#: xpack.bak/plugins/cloud/const.py:18 +#: xpack/plugins/cloud/const.py:18 msgid "Tencent Cloud (Lighthouse)" msgstr "腾讯云(轻量服务器应用)" -#: xpack.bak/plugins/cloud/const.py:19 +#: xpack/plugins/cloud/const.py:19 msgid "Google Cloud Platform" msgstr "谷歌云" -#: xpack.bak/plugins/cloud/const.py:20 +#: xpack/plugins/cloud/const.py:20 msgid "UCloud" msgstr "ucloud" -#: xpack.bak/plugins/cloud/const.py:22 +#: xpack/plugins/cloud/const.py:22 msgid "VMware" msgstr "VMware" -#: xpack.bak/plugins/cloud/const.py:23 -#: xpack.bak/plugins/cloud/providers/nutanix.py:15 +#: xpack/plugins/cloud/const.py:23 xpack/plugins/cloud/providers/nutanix.py:15 msgid "Nutanix" msgstr "Nutanix" -#: xpack.bak/plugins/cloud/const.py:24 +#: xpack/plugins/cloud/const.py:24 msgid "Huawei Private Cloud" msgstr "华为私有云" -#: xpack.bak/plugins/cloud/const.py:25 +#: xpack/plugins/cloud/const.py:25 msgid "Qingyun Private Cloud" msgstr "青云私有云" -#: xpack.bak/plugins/cloud/const.py:26 +#: xpack/plugins/cloud/const.py:26 msgid "CTYun Private Cloud" msgstr "天翼私有云" -#: xpack.bak/plugins/cloud/const.py:27 +#: xpack/plugins/cloud/const.py:27 msgid "OpenStack" msgstr "OpenStack" -#: xpack.bak/plugins/cloud/const.py:28 +#: xpack/plugins/cloud/const.py:28 msgid "Fusion Compute" msgstr "融合计算" -#: xpack.bak/plugins/cloud/const.py:33 +#: xpack/plugins/cloud/const.py:33 msgid "Private IP" msgstr "私有IP" -#: xpack.bak/plugins/cloud/const.py:34 +#: xpack/plugins/cloud/const.py:34 msgid "Public IP" msgstr "公网IP" -#: xpack.bak/plugins/cloud/const.py:38 xpack.bak/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/const.py:38 xpack/plugins/cloud/models.py:295 msgid "Instance name" msgstr "实例名称" -#: xpack.bak/plugins/cloud/const.py:39 +#: xpack/plugins/cloud/const.py:39 msgid "Instance name and Partial IP" msgstr "实例名称和部分IP" -#: xpack.bak/plugins/cloud/const.py:44 +#: xpack/plugins/cloud/const.py:44 msgid "Succeed" msgstr "成功" -#: xpack.bak/plugins/cloud/const.py:48 +#: xpack/plugins/cloud/const.py:48 msgid "Unsync" msgstr "未同步" -#: xpack.bak/plugins/cloud/const.py:49 +#: xpack/plugins/cloud/const.py:49 msgid "New Sync" msgstr "新同步" -#: xpack.bak/plugins/cloud/const.py:50 +#: xpack/plugins/cloud/const.py:50 msgid "Synced" msgstr "已同步" -#: xpack.bak/plugins/cloud/const.py:51 +#: xpack/plugins/cloud/const.py:51 msgid "Released" msgstr "已释放" -#: xpack.bak/plugins/cloud/manager.py:53 +#: xpack/plugins/cloud/manager.py:53 msgid "Account unavailable" msgstr "账号无效" -#: xpack.bak/plugins/cloud/meta.py:9 +#: xpack/plugins/cloud/meta.py:9 msgid "Cloud center" msgstr "云管中心" -#: xpack.bak/plugins/cloud/models.py:34 +#: xpack/plugins/cloud/models.py:34 msgid "Provider" msgstr "云服务商" -#: xpack.bak/plugins/cloud/models.py:38 +#: xpack/plugins/cloud/models.py:38 msgid "Validity" msgstr "有效" -#: xpack.bak/plugins/cloud/models.py:43 +#: xpack/plugins/cloud/models.py:43 msgid "Cloud account" msgstr "云账号" -#: xpack.bak/plugins/cloud/models.py:45 +#: xpack/plugins/cloud/models.py:45 msgid "Test cloud account" msgstr "测试云账号" -#: xpack.bak/plugins/cloud/models.py:92 -#: xpack.bak/plugins/cloud/serializers/task.py:151 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 msgid "Regions" msgstr "地域" -#: xpack.bak/plugins/cloud/models.py:95 +#: xpack/plugins/cloud/models.py:95 msgid "Hostname strategy" msgstr "主机名策略" -#: xpack.bak/plugins/cloud/models.py:100 -#: xpack.bak/plugins/cloud/serializers/task.py:154 +#: xpack/plugins/cloud/models.py:100 +#: xpack/plugins/cloud/serializers/task.py:154 msgid "IP network segment group" msgstr "IP网段组" -#: xpack.bak/plugins/cloud/models.py:103 -#: xpack.bak/plugins/cloud/serializers/task.py:159 +#: xpack/plugins/cloud/models.py:103 +#: xpack/plugins/cloud/serializers/task.py:159 msgid "Sync IP type" msgstr "同步IP类型" -#: xpack.bak/plugins/cloud/models.py:106 -#: xpack.bak/plugins/cloud/serializers/task.py:177 +#: xpack/plugins/cloud/models.py:106 +#: xpack/plugins/cloud/serializers/task.py:177 msgid "Always update" msgstr "总是更新" -#: xpack.bak/plugins/cloud/models.py:112 +#: xpack/plugins/cloud/models.py:112 msgid "Date last sync" msgstr "最后同步日期" -#: xpack.bak/plugins/cloud/models.py:115 xpack.bak/plugins/cloud/models.py:313 -#: xpack.bak/plugins/cloud/models.py:337 +#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 +#: xpack/plugins/cloud/models.py:337 msgid "Strategy" msgstr "策略" -#: xpack.bak/plugins/cloud/models.py:120 xpack.bak/plugins/cloud/models.py:197 +#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 msgid "Sync instance task" msgstr "同步实例任务" -#: xpack.bak/plugins/cloud/models.py:208 xpack.bak/plugins/cloud/models.py:260 +#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 msgid "Date sync" msgstr "同步日期" -#: xpack.bak/plugins/cloud/models.py:212 +#: xpack/plugins/cloud/models.py:212 msgid "Sync instance snapshot" msgstr "同步实例快照" -#: xpack.bak/plugins/cloud/models.py:216 +#: xpack/plugins/cloud/models.py:216 msgid "Sync instance task execution" msgstr "同步实例任务执行" -#: xpack.bak/plugins/cloud/models.py:240 +#: xpack/plugins/cloud/models.py:240 msgid "Sync task" msgstr "同步任务" -#: xpack.bak/plugins/cloud/models.py:244 +#: xpack/plugins/cloud/models.py:244 msgid "Sync instance task history" msgstr "同步实例任务历史" -#: xpack.bak/plugins/cloud/models.py:247 +#: xpack/plugins/cloud/models.py:247 msgid "Instance" msgstr "实例" -#: xpack.bak/plugins/cloud/models.py:264 +#: xpack/plugins/cloud/models.py:264 msgid "Sync instance detail" msgstr "同步实例详情" -#: xpack.bak/plugins/cloud/models.py:281 +#: xpack/plugins/cloud/models.py:281 msgid "Task strategy" msgstr "任务策略" -#: xpack.bak/plugins/cloud/models.py:285 +#: xpack/plugins/cloud/models.py:285 msgid "Exact" msgstr "" -#: xpack.bak/plugins/cloud/models.py:286 +#: xpack/plugins/cloud/models.py:286 msgid "Not" msgstr "否" -#: xpack.bak/plugins/cloud/models.py:287 +#: xpack/plugins/cloud/models.py:287 msgid "In" msgstr "在..里面" -#: xpack.bak/plugins/cloud/models.py:288 +#: xpack/plugins/cloud/models.py:288 msgid "Contains" msgstr "包含" -#: xpack.bak/plugins/cloud/models.py:289 +#: xpack/plugins/cloud/models.py:289 msgid "Startswith" msgstr "以..开头" -#: xpack.bak/plugins/cloud/models.py:290 +#: xpack/plugins/cloud/models.py:290 msgid "Endswith" msgstr "以..结尾" -#: xpack.bak/plugins/cloud/models.py:296 +#: xpack/plugins/cloud/models.py:296 msgid "Instance platform" msgstr "实例平台" -#: xpack.bak/plugins/cloud/models.py:297 +#: xpack/plugins/cloud/models.py:297 msgid "Instance address" msgstr "实例地址" -#: xpack.bak/plugins/cloud/models.py:304 +#: xpack/plugins/cloud/models.py:304 msgid "Rule attr" msgstr "规则属性" -#: xpack.bak/plugins/cloud/models.py:308 +#: xpack/plugins/cloud/models.py:308 msgid "Rule match" msgstr "规则匹配" -#: xpack.bak/plugins/cloud/models.py:310 +#: xpack/plugins/cloud/models.py:310 msgid "Rule value" msgstr "规则值" -#: xpack.bak/plugins/cloud/models.py:317 -#: xpack.bak/plugins/cloud/serializers/task.py:70 +#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 msgid "Strategy rule" msgstr "条件" -#: xpack.bak/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/models.py:332 msgid "Action attr" msgstr "动作属性" -#: xpack.bak/plugins/cloud/models.py:334 +#: xpack/plugins/cloud/models.py:334 msgid "Action value" msgstr "动作值" -#: xpack.bak/plugins/cloud/models.py:341 -#: xpack.bak/plugins/cloud/serializers/task.py:73 +#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 msgid "Strategy action" msgstr "动作" -#: xpack.bak/plugins/cloud/providers/aws_international.py:18 +#: xpack/plugins/cloud/providers/aws_international.py:18 msgid "China (Beijing)" msgstr "中国 (北京)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:19 +#: xpack/plugins/cloud/providers/aws_international.py:19 msgid "China (Ningxia)" msgstr "中国 (宁夏)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:22 +#: xpack/plugins/cloud/providers/aws_international.py:22 msgid "US East (Ohio)" msgstr "美国东部 (俄亥俄州)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:23 +#: xpack/plugins/cloud/providers/aws_international.py:23 msgid "US East (N. Virginia)" msgstr "美国东部 (弗吉尼亚北部)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:24 +#: xpack/plugins/cloud/providers/aws_international.py:24 msgid "US West (N. California)" msgstr "美国西部 (加利福尼亚北部)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:25 +#: xpack/plugins/cloud/providers/aws_international.py:25 msgid "US West (Oregon)" msgstr "美国西部 (俄勒冈)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:26 +#: xpack/plugins/cloud/providers/aws_international.py:26 msgid "Africa (Cape Town)" msgstr "非洲 (开普敦)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:27 +#: xpack/plugins/cloud/providers/aws_international.py:27 msgid "Asia Pacific (Hong Kong)" msgstr "亚太地区 (香港)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:28 +#: xpack/plugins/cloud/providers/aws_international.py:28 msgid "Asia Pacific (Mumbai)" msgstr "亚太地区 (孟买)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:29 +#: xpack/plugins/cloud/providers/aws_international.py:29 msgid "Asia Pacific (Osaka-Local)" msgstr "亚太区域 (大阪当地)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:30 +#: xpack/plugins/cloud/providers/aws_international.py:30 msgid "Asia Pacific (Seoul)" msgstr "亚太区域 (首尔)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:31 +#: xpack/plugins/cloud/providers/aws_international.py:31 msgid "Asia Pacific (Singapore)" msgstr "亚太区域 (新加坡)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:32 +#: xpack/plugins/cloud/providers/aws_international.py:32 msgid "Asia Pacific (Sydney)" msgstr "亚太区域 (悉尼)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:33 +#: xpack/plugins/cloud/providers/aws_international.py:33 msgid "Asia Pacific (Tokyo)" msgstr "亚太区域 (东京)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:34 +#: xpack/plugins/cloud/providers/aws_international.py:34 msgid "Canada (Central)" msgstr "加拿大 (中部)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:35 +#: xpack/plugins/cloud/providers/aws_international.py:35 msgid "Europe (Frankfurt)" msgstr "欧洲 (法兰克福)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:36 +#: xpack/plugins/cloud/providers/aws_international.py:36 msgid "Europe (Ireland)" msgstr "欧洲 (爱尔兰)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:37 +#: xpack/plugins/cloud/providers/aws_international.py:37 msgid "Europe (London)" msgstr "欧洲 (伦敦)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:38 +#: xpack/plugins/cloud/providers/aws_international.py:38 msgid "Europe (Milan)" msgstr "欧洲 (米兰)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:39 +#: xpack/plugins/cloud/providers/aws_international.py:39 msgid "Europe (Paris)" msgstr "欧洲 (巴黎)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:40 +#: xpack/plugins/cloud/providers/aws_international.py:40 msgid "Europe (Stockholm)" msgstr "欧洲 (斯德哥尔摩)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:41 +#: xpack/plugins/cloud/providers/aws_international.py:41 msgid "Middle East (Bahrain)" msgstr "中东 (巴林)" -#: xpack.bak/plugins/cloud/providers/aws_international.py:42 +#: xpack/plugins/cloud/providers/aws_international.py:42 msgid "South America (São Paulo)" msgstr "南美洲 (圣保罗)" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:54 -#: xpack.bak/plugins/cloud/providers/jdcloud.py:125 +#: xpack/plugins/cloud/providers/baiducloud.py:54 +#: xpack/plugins/cloud/providers/jdcloud.py:125 msgid "CN North-Beijing" msgstr "华北-北京" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:55 -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:42 -#: xpack.bak/plugins/cloud/providers/jdcloud.py:128 +#: xpack/plugins/cloud/providers/baiducloud.py:55 +#: xpack/plugins/cloud/providers/huaweicloud.py:42 +#: xpack/plugins/cloud/providers/jdcloud.py:128 msgid "CN South-Guangzhou" msgstr "华南-广州" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:56 +#: xpack/plugins/cloud/providers/baiducloud.py:56 msgid "CN East-Suzhou" msgstr "华东-苏州" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:57 -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:49 +#: xpack/plugins/cloud/providers/baiducloud.py:57 +#: xpack/plugins/cloud/providers/huaweicloud.py:49 msgid "CN-Hong Kong" msgstr "中国-香港" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:58 +#: xpack/plugins/cloud/providers/baiducloud.py:58 msgid "CN Center-Wuhan" msgstr "华中-武汉" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:59 +#: xpack/plugins/cloud/providers/baiducloud.py:59 msgid "CN North-Baoding" msgstr "华北-保定" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:60 -#: xpack.bak/plugins/cloud/providers/jdcloud.py:127 +#: xpack/plugins/cloud/providers/baiducloud.py:60 +#: xpack/plugins/cloud/providers/jdcloud.py:127 msgid "CN East-Shanghai" msgstr "华东-上海" -#: xpack.bak/plugins/cloud/providers/baiducloud.py:61 -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:51 +#: xpack/plugins/cloud/providers/baiducloud.py:61 +#: xpack/plugins/cloud/providers/huaweicloud.py:51 msgid "AP-Singapore" msgstr "亚太-新加坡" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:39 +#: xpack/plugins/cloud/providers/huaweicloud.py:39 msgid "CN North-Beijing1" msgstr "华北-北京1" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:40 +#: xpack/plugins/cloud/providers/huaweicloud.py:40 msgid "CN North-Beijing4" msgstr "华北-北京4" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:41 +#: xpack/plugins/cloud/providers/huaweicloud.py:41 msgid "CN North-Ulanqab1" msgstr "华北-乌兰察布一" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:43 +#: xpack/plugins/cloud/providers/huaweicloud.py:43 msgid "CN South-Shenzhen" msgstr "华南-广州" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:44 +#: xpack/plugins/cloud/providers/huaweicloud.py:44 msgid "CN South-Guangzhou-InvitationOnly" msgstr "华南-广州-友好用户环境" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:45 +#: xpack/plugins/cloud/providers/huaweicloud.py:45 msgid "CN East-Shanghai2" msgstr "华东-上海2" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:46 +#: xpack/plugins/cloud/providers/huaweicloud.py:46 msgid "CN East-Shanghai1" msgstr "华东-上海1" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:48 +#: xpack/plugins/cloud/providers/huaweicloud.py:48 msgid "CN Southwest-Guiyang1" msgstr "西南-贵阳1" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:50 +#: xpack/plugins/cloud/providers/huaweicloud.py:50 msgid "AP-Bangkok" msgstr "亚太-曼谷" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:53 +#: xpack/plugins/cloud/providers/huaweicloud.py:53 msgid "AF-Johannesburg" msgstr "非洲-约翰内斯堡" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:54 +#: xpack/plugins/cloud/providers/huaweicloud.py:54 msgid "LA-Mexico City1" msgstr "拉美-墨西哥城一" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:55 +#: xpack/plugins/cloud/providers/huaweicloud.py:55 msgid "LA-Santiago" msgstr "拉美-圣地亚哥" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:56 +#: xpack/plugins/cloud/providers/huaweicloud.py:56 msgid "LA-Sao Paulo1" msgstr "拉美-圣保罗一" -#: xpack.bak/plugins/cloud/providers/huaweicloud.py:58 +#: xpack/plugins/cloud/providers/huaweicloud.py:58 msgid "TR-Istanbul" msgstr "" -#: xpack.bak/plugins/cloud/providers/jdcloud.py:126 +#: xpack/plugins/cloud/providers/jdcloud.py:126 msgid "CN East-Suqian" msgstr "华东-宿迁" -#: xpack.bak/plugins/cloud/serializers/account.py:64 +#: xpack/plugins/cloud/serializers/account.py:64 msgid "Validity display" msgstr "有效性显示" -#: xpack.bak/plugins/cloud/serializers/account.py:65 +#: xpack/plugins/cloud/serializers/account.py:65 msgid "Provider display" msgstr "服务商显示" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:35 +#: xpack/plugins/cloud/serializers/account_attrs.py:35 msgid "Client ID" msgstr "客户端 ID" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:41 +#: xpack/plugins/cloud/serializers/account_attrs.py:41 msgid "Tenant ID" msgstr "租户 ID" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:44 +#: xpack/plugins/cloud/serializers/account_attrs.py:44 msgid "Subscription ID" msgstr "订阅 ID" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:98 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:103 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:119 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:149 -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:199 +#: xpack/plugins/cloud/serializers/account_attrs.py:98 +#: xpack/plugins/cloud/serializers/account_attrs.py:103 +#: xpack/plugins/cloud/serializers/account_attrs.py:119 +#: xpack/plugins/cloud/serializers/account_attrs.py:149 +#: xpack/plugins/cloud/serializers/account_attrs.py:199 msgid "API Endpoint" msgstr "API 端点" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:109 +#: xpack/plugins/cloud/serializers/account_attrs.py:109 msgid "Auth url" msgstr "认证地址" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:110 +#: xpack/plugins/cloud/serializers/account_attrs.py:110 msgid "eg: http://openstack.example.com:5000/v3" msgstr "如: http://openstack.example.com:5000/v3" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:113 +#: xpack/plugins/cloud/serializers/account_attrs.py:113 msgid "User domain" msgstr "用户域" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:120 +#: xpack/plugins/cloud/serializers/account_attrs.py:120 msgid "Cert File" msgstr "证书文件" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:121 +#: xpack/plugins/cloud/serializers/account_attrs.py:121 msgid "Key File" msgstr "密钥文件" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:137 +#: xpack/plugins/cloud/serializers/account_attrs.py:137 msgid "Service account key" msgstr "服务账号密钥" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:138 +#: xpack/plugins/cloud/serializers/account_attrs.py:138 msgid "The file is in JSON format" msgstr "JSON 格式的文件" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:156 +#: xpack/plugins/cloud/serializers/account_attrs.py:156 msgid "IP address invalid `{}`, {}" msgstr "IP 地址无效: `{}`, {}" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:172 +#: xpack/plugins/cloud/serializers/account_attrs.py:172 msgid "Such as: 192.168.1.0/24, 10.0.0.0-10.0.0.255" msgstr "例: 192.168.1.0/24,10.0.0.0-10.0.0.255" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:175 +#: xpack/plugins/cloud/serializers/account_attrs.py:175 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -8312,27 +8324,27 @@ msgstr "" "端口用来检测 IP 地址的有效性,在同步任务执行时,只会同步有效的 IP 地址。
" "如果端口为 0,则表示所有 IP 地址均有效。" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:183 +#: xpack/plugins/cloud/serializers/account_attrs.py:183 msgid "Hostname prefix" msgstr "主机名前缀" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:186 +#: xpack/plugins/cloud/serializers/account_attrs.py:186 msgid "IP segment" msgstr "IP 网段" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:190 +#: xpack/plugins/cloud/serializers/account_attrs.py:190 msgid "Test port" msgstr "测试端口" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:193 +#: xpack/plugins/cloud/serializers/account_attrs.py:193 msgid "Test timeout" msgstr "测试超时时间" -#: xpack.bak/plugins/cloud/serializers/account_attrs.py:209 +#: xpack/plugins/cloud/serializers/account_attrs.py:209 msgid "Project" msgstr "project" -#: xpack.bak/plugins/cloud/serializers/task.py:143 +#: xpack/plugins/cloud/serializers/task.py:143 msgid "" "Only instances matching the IP range will be synced.
If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8344,84 +8356,83 @@ msgstr "" "到的 IP 地址将被用作创建的资产的 IP。
默认值 * 表示同步所有实例和随机匹配 " "IP 地址。
例如: 192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack.bak/plugins/cloud/serializers/task.py:149 +#: xpack/plugins/cloud/serializers/task.py:149 msgid "History count" msgstr "执行次数" -#: xpack.bak/plugins/cloud/serializers/task.py:150 +#: xpack/plugins/cloud/serializers/task.py:150 msgid "Instance count" msgstr "实例个数" -#: xpack.bak/plugins/cloud/tasks.py:27 +#: xpack/plugins/cloud/tasks.py:27 msgid "Run sync instance task" msgstr "执行同步实例任务" -#: xpack.bak/plugins/cloud/tasks.py:41 +#: xpack/plugins/cloud/tasks.py:41 msgid "Period clean sync instance task execution" msgstr "定期清除同步实例任务执行记录" -#: xpack.bak/plugins/interface/api.py:52 +#: xpack/plugins/interface/api.py:52 msgid "Restore default successfully." msgstr "恢复默认成功!" -#: xpack.bak/plugins/interface/meta.py:9 +#: xpack/plugins/interface/meta.py:9 msgid "Interface settings" msgstr "界面设置" -#: xpack.bak/plugins/interface/models.py:23 +#: xpack/plugins/interface/models.py:23 msgid "Title of login page" msgstr "登录页面标题" -#: xpack.bak/plugins/interface/models.py:27 +#: xpack/plugins/interface/models.py:27 msgid "Image of login page" msgstr "登录页面图片" -#: xpack.bak/plugins/interface/models.py:31 +#: xpack/plugins/interface/models.py:31 msgid "Website icon" msgstr "网站图标" -#: xpack.bak/plugins/interface/models.py:35 +#: xpack/plugins/interface/models.py:35 msgid "Logo of management page" msgstr "管理页面logo" -#: xpack.bak/plugins/interface/models.py:39 +#: xpack/plugins/interface/models.py:39 msgid "Logo of logout page" msgstr "退出页面logo" -#: xpack.bak/plugins/interface/models.py:41 +#: xpack/plugins/interface/models.py:41 msgid "Theme" msgstr "主题" -#: xpack.bak/plugins/interface/models.py:44 -#: xpack.bak/plugins/interface/models.py:85 +#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 msgid "Interface setting" msgstr "界面设置" -#: xpack.bak/plugins/license/api.py:52 +#: xpack/plugins/license/api.py:52 msgid "License import successfully" msgstr "许可证导入成功" -#: xpack.bak/plugins/license/api.py:53 +#: xpack/plugins/license/api.py:53 msgid "License is invalid" msgstr "无效的许可证" -#: xpack.bak/plugins/license/meta.py:10 xpack.bak/plugins/license/models.py:140 +#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 msgid "License" msgstr "许可证" -#: xpack.bak/plugins/license/models.py:80 +#: xpack/plugins/license/models.py:80 msgid "Basic edition" msgstr "企业基础版" -#: xpack.bak/plugins/license/models.py:82 +#: xpack/plugins/license/models.py:82 msgid "Standard edition" msgstr "企业标准版" -#: xpack.bak/plugins/license/models.py:84 +#: xpack/plugins/license/models.py:84 msgid "Professional edition" msgstr "企业专业版" -#: xpack.bak/plugins/license/models.py:86 +#: xpack/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "企业旗舰版" From e71e8cd5956110c7576e49e3dc9cc732cb54a157 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Sun, 8 Oct 2023 10:32:51 +0800 Subject: [PATCH 031/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=B8=85?= =?UTF-8?q?=E9=99=A4=E6=97=A5=E5=BF=97=E5=A4=A9=E6=95=B0=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=A0=A1=E9=AA=8C=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/settings/custom.py | 11 --------- apps/settings/serializers/cleaning.py | 32 ++++++++------------------- 2 files changed, 9 insertions(+), 34 deletions(-) diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 6733faa92..e60b98dad 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -204,14 +204,3 @@ MAX_LIMIT_PER_PAGE = CONFIG.MAX_LIMIT_PER_PAGE # Magnus DB Port MAGNUS_ORACLE_PORTS = CONFIG.MAGNUS_ORACLE_PORTS LIMIT_SUPER_PRIV = CONFIG.LIMIT_SUPER_PRIV - -LOG_NAMES = [ - 'LOGIN_LOG_KEEP_DAYS', 'TASK_LOG_KEEP_DAYS', - 'OPERATE_LOG_KEEP_DAYS', 'FTP_LOG_KEEP_DAYS', - 'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS', - 'ACTIVITY_LOG_KEEP_DAYS', 'TERMINAL_SESSION_KEEP_DURATION' -] -if LIMIT_SUPER_PRIV: - for name in LOG_NAMES: - if globals()[name] < 180: - globals()[name] = 180 diff --git a/apps/settings/serializers/cleaning.py b/apps/settings/serializers/cleaning.py index 03fda1f7b..e5f572e77 100644 --- a/apps/settings/serializers/cleaning.py +++ b/apps/settings/serializers/cleaning.py @@ -1,55 +1,41 @@ from django.conf import settings from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from rest_framework.serializers import ValidationError __all__ = ['CleaningSerializer'] +MIN_VALUE = 180 if settings.LIMIT_SUPER_PRIV else 1 + class CleaningSerializer(serializers.Serializer): PREFIX_TITLE = _('Period clean') LOGIN_LOG_KEEP_DAYS = serializers.IntegerField( - min_value=1, max_value=9999, + min_value=MIN_VALUE, max_value=9999, label=_("Login log keep days (day)"), ) TASK_LOG_KEEP_DAYS = serializers.IntegerField( - min_value=1, max_value=9999, + min_value=MIN_VALUE, max_value=9999, label=_("Task log keep days (day)"), ) OPERATE_LOG_KEEP_DAYS = serializers.IntegerField( - min_value=1, max_value=9999, + min_value=MIN_VALUE, max_value=9999, label=_("Operate log keep days (day)"), ) FTP_LOG_KEEP_DAYS = serializers.IntegerField( - min_value=1, max_value=9999, + min_value=MIN_VALUE, max_value=9999, label=_("FTP log keep days (day)"), ) CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = serializers.IntegerField( - min_value=1, max_value=9999, + min_value=MIN_VALUE, max_value=9999, label=_("Cloud sync record keep days (day)"), ) ACTIVITY_LOG_KEEP_DAYS = serializers.IntegerField( - min_value=1, max_value=9999, + min_value=MIN_VALUE, max_value=9999, label=_("Activity log keep days (day)"), ) TERMINAL_SESSION_KEEP_DURATION = serializers.IntegerField( - min_value=1, max_value=99999, required=True, label=_('Session keep duration (day)'), + min_value=MIN_VALUE, max_value=99999, required=True, label=_('Session keep duration (day)'), help_text=_( 'Session, record, command will be delete if more than duration, only in database, OSS will not be affected.') ) - MIN_DAYS_THRESHOLD = 180 - - def validate(self, attrs): - attrs = super().validate(attrs) - if not settings.LIMIT_SUPER_PRIV: - return attrs - - error_names = [ - name for name in settings.LOG_NAMES - if attrs.get(name, 0) < self.MIN_DAYS_THRESHOLD - ] - if error_names: - error_message = _('must be greater than {} days.').format(self.MIN_DAYS_THRESHOLD) - raise ValidationError({name: error_message for name in error_names}) - return attrs From b42014d58ecd86183866cf726eb6309fdd32ff00 Mon Sep 17 00:00:00 2001 From: halo Date: Sun, 8 Oct 2023 20:03:15 +0800 Subject: [PATCH 032/114] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81DB2=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/const/database.py | 11 ++++++++ apps/assets/const/protocol.py | 7 +++++ .../migrations/0124_auto_20231007_1437.py | 26 +++++++++++++++++++ apps/terminal/connect_methods.py | 2 +- 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 apps/assets/migrations/0124_auto_20231007_1437.py diff --git a/apps/assets/const/database.py b/apps/assets/const/database.py index 86e86ed40..261373688 100644 --- a/apps/assets/const/database.py +++ b/apps/assets/const/database.py @@ -7,6 +7,7 @@ class DatabaseTypes(BaseType): POSTGRESQL = 'postgresql', 'PostgreSQL' ORACLE = 'oracle', 'Oracle' SQLSERVER = 'sqlserver', 'SQLServer' + DB2 = 'db2', 'DB2' CLICKHOUSE = 'clickhouse', 'ClickHouse' MONGODB = 'mongodb', 'MongoDB' REDIS = 'redis', 'Redis' @@ -45,6 +46,15 @@ class DatabaseTypes(BaseType): 'change_secret_enabled': False, 'push_account_enabled': False, }, + cls.DB2: { + 'ansible_enabled': False, + 'ping_enabled': False, + 'gather_facts_enabled': False, + 'gather_accounts_enabled': False, + 'verify_account_enabled': False, + 'change_secret_enabled': False, + 'push_account_enabled': False, + }, cls.CLICKHOUSE: { 'ansible_enabled': False, 'ping_enabled': False, @@ -73,6 +83,7 @@ class DatabaseTypes(BaseType): cls.POSTGRESQL: [{'name': 'PostgreSQL'}], cls.ORACLE: [{'name': 'Oracle'}], cls.SQLSERVER: [{'name': 'SQLServer'}], + cls.DB2: [{'name': 'DB2'}], cls.CLICKHOUSE: [{'name': 'ClickHouse'}], cls.MONGODB: [{'name': 'MongoDB'}], cls.REDIS: [ diff --git a/apps/assets/const/protocol.py b/apps/assets/const/protocol.py index f1c593522..9976596b2 100644 --- a/apps/assets/const/protocol.py +++ b/apps/assets/const/protocol.py @@ -22,6 +22,7 @@ class Protocol(ChoicesMixin, models.TextChoices): oracle = 'oracle', 'Oracle' postgresql = 'postgresql', 'PostgreSQL' sqlserver = 'sqlserver', 'SQLServer' + db2 = 'db2', 'DB2' clickhouse = 'clickhouse', 'ClickHouse' redis = 'redis', 'Redis' mongodb = 'mongodb', 'MongoDB' @@ -170,6 +171,12 @@ class Protocol(ChoicesMixin, models.TextChoices): } } }, + cls.db2: { + 'port': 5000, + 'required': True, + 'secret_types': ['password'], + 'xpack': True, + }, cls.clickhouse: { 'port': 9000, 'required': True, diff --git a/apps/assets/migrations/0124_auto_20231007_1437.py b/apps/assets/migrations/0124_auto_20231007_1437.py new file mode 100644 index 000000000..930327439 --- /dev/null +++ b/apps/assets/migrations/0124_auto_20231007_1437.py @@ -0,0 +1,26 @@ +# Generated by Django 4.1.10 on 2023-10-07 06:37 + +from django.db import migrations + + +def add_db2_platform(apps, schema_editor): + platform_cls = apps.get_model('assets', 'Platform') + automation_cls = apps.get_model('assets', 'PlatformAutomation') + platform = platform_cls.objects.create( + name='DB2', internal=True, category='database', type='db2', + domain_enabled=True, su_enabled=False, comment='DB2', + created_by='System', updated_by='System', + ) + platform.protocols.create(name='db2', port=5000, primary=True, setting={}) + automation_cls.objects.create(ansible_enabled=False, platform=platform) + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0123_device_automation_ansible_enabled'), + ] + + operations = [ + migrations.RunPython(add_db2_platform) + ] diff --git a/apps/terminal/connect_methods.py b/apps/terminal/connect_methods.py index 3f290a0c0..8672001c0 100644 --- a/apps/terminal/connect_methods.py +++ b/apps/terminal/connect_methods.py @@ -144,7 +144,7 @@ class ConnectMethodUtil: 'support': [ Protocol.mysql, Protocol.postgresql, Protocol.oracle, Protocol.sqlserver, - Protocol.mariadb + Protocol.mariadb, Protocol.db2 ], 'match': 'm2m' }, From ae5d4257ad642eb5c17305dfadf4087b183e3c70 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Sun, 8 Oct 2023 16:22:01 +0800 Subject: [PATCH 033/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E6=89=B9=E9=87=8F=E6=9B=B4=E6=96=B0=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/models/mixins/vault.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/accounts/models/mixins/vault.py b/apps/accounts/models/mixins/vault.py index b8865ab7d..c5d0935c5 100644 --- a/apps/accounts/models/mixins/vault.py +++ b/apps/accounts/models/mixins/vault.py @@ -38,6 +38,7 @@ class VaultManagerMixin(models.Manager): return objs def bulk_update(self, objs, fields, batch_size=None): + fields = ["_secret" if field == "secret" else field for field in fields] objs = super().bulk_update(objs, fields, batch_size=batch_size) for obj in objs: post_save.send(obj.__class__, instance=obj, created=False) From ef2b7b464ed3917d57e86b7e108095b0addc08b3 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 9 Oct 2023 14:35:21 +0800 Subject: [PATCH 034/114] =?UTF-8?q?perf:=20ansible=20=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E8=87=B3=20(#11766)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- .../change_secret/custom/ssh/main.yml | 6 ++++- .../change_secret/host/aix/main.yml | 7 +++-- .../change_secret/host/posix/main.yml | 7 +++-- .../push_account/host/aix/main.yml | 7 +++-- .../push_account/host/posix/main.yml | 7 +++-- .../verify_account/custom/ssh/main.yml | 10 +++---- .../verify_account/host/posix/main.yml | 16 +++++++++-- .../automations/verify_account/manager.py | 4 +-- apps/accounts/models/account.py | 27 +++++++++++++++++++ apps/ops/ansible/inventory.py | 19 +++++-------- .../ansible/modules_utils/custom_common.py | 14 +++++----- 11 files changed, 87 insertions(+), 37 deletions(-) diff --git a/apps/accounts/automations/change_secret/custom/ssh/main.yml b/apps/accounts/automations/change_secret/custom/ssh/main.yml index b35d2175a..7e30bf62b 100644 --- a/apps/accounts/automations/change_secret/custom/ssh/main.yml +++ b/apps/accounts/automations/change_secret/custom/ssh/main.yml @@ -47,4 +47,8 @@ login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - become: false + become: "{{ account.become.ansible_become | default(False) }}" + become_method: su + become_user: "{{ account.become.ansible_user | default('') }}" + become_password: "{{ account.become.ansible_password | default('') }}" + become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" diff --git a/apps/accounts/automations/change_secret/host/aix/main.yml b/apps/accounts/automations/change_secret/host/aix/main.yml index 56fde8a2c..e86319ce1 100644 --- a/apps/accounts/automations/change_secret/host/aix/main.yml +++ b/apps/accounts/automations/change_secret/host/aix/main.yml @@ -80,7 +80,11 @@ login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}" - become: false + become: "{{ account.become.ansible_become | default(False) }}" + become_method: su + become_user: "{{ account.become.ansible_user | default('') }}" + become_password: "{{ account.become.ansible_password | default('') }}" + become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" when: account.secret_type == "password" delegate_to: localhost @@ -91,6 +95,5 @@ login_user: "{{ account.username }}" login_private_key_path: "{{ account.private_key_path }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}" - become: false when: account.secret_type == "ssh_key" delegate_to: localhost diff --git a/apps/accounts/automations/change_secret/host/posix/main.yml b/apps/accounts/automations/change_secret/host/posix/main.yml index 1dca70a5a..6ac4b7aa9 100644 --- a/apps/accounts/automations/change_secret/host/posix/main.yml +++ b/apps/accounts/automations/change_secret/host/posix/main.yml @@ -80,7 +80,11 @@ login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}" - become: false + become: "{{ account.become.ansible_become | default(False) }}" + become_method: su + become_user: "{{ account.become.ansible_user | default('') }}" + become_password: "{{ account.become.ansible_password | default('') }}" + become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" when: account.secret_type == "password" delegate_to: localhost @@ -91,6 +95,5 @@ login_user: "{{ account.username }}" login_private_key_path: "{{ account.private_key_path }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}" - become: false when: account.secret_type == "ssh_key" delegate_to: localhost diff --git a/apps/accounts/automations/push_account/host/aix/main.yml b/apps/accounts/automations/push_account/host/aix/main.yml index 0e6fba5c5..b0256348c 100644 --- a/apps/accounts/automations/push_account/host/aix/main.yml +++ b/apps/accounts/automations/push_account/host/aix/main.yml @@ -80,7 +80,11 @@ login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}" - become: false + become: "{{ account.become.ansible_become | default(False) }}" + become_method: su + become_user: "{{ account.become.ansible_user | default('') }}" + become_password: "{{ account.become.ansible_password | default('') }}" + become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" when: account.secret_type == "password" delegate_to: localhost @@ -91,7 +95,6 @@ login_user: "{{ account.username }}" login_private_key_path: "{{ account.private_key_path }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}" - become: false when: account.secret_type == "ssh_key" delegate_to: localhost diff --git a/apps/accounts/automations/push_account/host/posix/main.yml b/apps/accounts/automations/push_account/host/posix/main.yml index ea5128b17..2d2cc8e3e 100644 --- a/apps/accounts/automations/push_account/host/posix/main.yml +++ b/apps/accounts/automations/push_account/host/posix/main.yml @@ -80,7 +80,11 @@ login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}" - become: false + become: "{{ account.become.ansible_become | default(False) }}" + become_method: su + become_user: "{{ account.become.ansible_user | default('') }}" + become_password: "{{ account.become.ansible_password | default('') }}" + become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" when: account.secret_type == "password" delegate_to: localhost @@ -91,7 +95,6 @@ login_user: "{{ account.username }}" login_private_key_path: "{{ account.private_key_path }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}" - become: false when: account.secret_type == "ssh_key" delegate_to: localhost diff --git a/apps/accounts/automations/verify_account/custom/ssh/main.yml b/apps/accounts/automations/verify_account/custom/ssh/main.yml index 4e35b9587..c565f6f83 100644 --- a/apps/accounts/automations/verify_account/custom/ssh/main.yml +++ b/apps/accounts/automations/verify_account/custom/ssh/main.yml @@ -13,8 +13,8 @@ login_password: "{{ account.secret }}" login_secret_type: "{{ account.secret_type }}" login_private_key_path: "{{ account.private_key_path }}" - become: "{{ custom_become | default(False) }}" - become_method: "{{ custom_become_method | default('su') }}" - become_user: "{{ custom_become_user | default('') }}" - become_password: "{{ custom_become_password | default('') }}" - become_private_key_path: "{{ custom_become_private_key_path | default(None) }}" + become: "{{ account.become.ansible_become | default(False) }}" + become_method: "{{ account.become.ansible_become_method | default('su') }}" + become_user: "{{ account.become.ansible_user | default('') }}" + become_password: "{{ account.become.ansible_password | default('') }}" + become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" diff --git a/apps/accounts/automations/verify_account/host/posix/main.yml b/apps/accounts/automations/verify_account/host/posix/main.yml index b096f9d84..49e84e5ee 100644 --- a/apps/accounts/automations/verify_account/host/posix/main.yml +++ b/apps/accounts/automations/verify_account/host/posix/main.yml @@ -1,11 +1,23 @@ - hosts: demo gather_facts: no tasks: - - name: Verify account connectivity - become: no + - name: Verify account connectivity(Do not switch) ansible.builtin.ping: vars: ansible_become: no ansible_user: "{{ account.username }}" ansible_password: "{{ account.secret }}" ansible_ssh_private_key_file: "{{ account.private_key_path }}" + when: not account.become.ansible_become + + - name: Verify account connectivity(Switch) + ansible.builtin.ping: + vars: + ansible_become: yes + ansible_user: "{{ account.become.ansible_user }}" + ansible_password: "{{ account.become.ansible_password }}" + ansible_ssh_private_key_file: "{{ account.become.ansible_ssh_private_key_file }}" + ansible_become_method: "{{ account.become.ansible_become_method }}" + ansible_become_user: "{{ account.become.ansible_become_user }}" + ansible_become_password: "{{ account.become.ansible_become_password }}" + when: account.become.ansible_become diff --git a/apps/accounts/automations/verify_account/manager.py b/apps/accounts/automations/verify_account/manager.py index b0e4a10ab..18478fb21 100644 --- a/apps/accounts/automations/verify_account/manager.py +++ b/apps/accounts/automations/verify_account/manager.py @@ -42,7 +42,6 @@ class VerifyAccountManager(AccountBasePlaybookManager): if host.get('error'): return host - # host['ssh_args'] = '-o ControlMaster=no -o ControlPersist=no' accounts = asset.accounts.all() accounts = self.get_accounts(account, accounts) inventory_hosts = [] @@ -64,7 +63,8 @@ class VerifyAccountManager(AccountBasePlaybookManager): 'username': account.username, 'secret_type': account.secret_type, 'secret': secret, - 'private_key_path': private_key_path + 'private_key_path': private_key_path, + 'become': account.get_ansible_become_auth(), } if account.platform.type == 'oracle': h['account']['mode'] = 'sysdba' if account.privileged else None diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 1bdf6e83d..644961ff7 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -95,6 +95,33 @@ class Account(AbsConnectivity, BaseAccount): """ 排除自己和以自己为 su-from 的账号 """ return self.asset.accounts.exclude(id=self.id).exclude(su_from=self) + @staticmethod + def make_account_ansible_vars(su_from): + var = { + 'ansible_user': su_from.username, + } + if not su_from.secret: + return var + var['ansible_password'] = su_from.secret + var['ansible_ssh_private_key_file'] = su_from.private_key_path + return var + + def get_ansible_become_auth(self): + su_from = self.su_from + platform = self.platform + auth = {'ansible_become': False} + if not (platform.su_enabled and su_from): + return auth + + auth.update(self.make_account_ansible_vars(su_from)) + become_method = 'sudo' if platform.su_method != 'su' else 'su' + password = su_from.secret if become_method == 'sudo' else self.secret + auth['ansible_become'] = True + auth['ansible_become_method'] = become_method + auth['ansible_become_user'] = self.username + auth['ansible_become_password'] = password + return auth + def replace_history_model_with_mixin(): """ diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index b07876224..1c5fd17fc 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -77,9 +77,11 @@ class JMSInventory: return var @staticmethod - def make_custom_become_ansible_vars(account, platform): + def make_custom_become_ansible_vars(account, su_from_auth): + su_method = su_from_auth['ansible_become_method'] var = { - 'custom_become': True, 'custom_become_method': platform.su_method, + 'custom_become': True, + 'custom_become_method': su_method, 'custom_become_user': account.su_from.username, 'custom_become_password': account.su_from.secret, 'custom_become_private_key_path': account.su_from.private_key_path @@ -98,16 +100,9 @@ class JMSInventory: su_from = account.su_from if platform.su_enabled and su_from: - host.update(self.make_account_ansible_vars(su_from)) - host.update(self.make_custom_become_ansible_vars(account, platform)) - become_method = 'sudo' if platform.su_method != 'su' else 'su' - host['ansible_become'] = True - host['ansible_become_method'] = 'sudo' - host['ansible_become_user'] = account.username - if become_method == 'sudo': - host['ansible_become_password'] = su_from.secret - else: - host['ansible_become_password'] = account.secret + su_from_auth = account.get_ansible_become_auth() + host.update(su_from_auth) + host.update(self.make_custom_become_ansible_vars(account, su_from_auth)) elif platform.su_enabled and not su_from and \ self.task_type in (AutomationTypes.change_secret, AutomationTypes.push_account): host.update(self.make_account_ansible_vars(account)) diff --git a/apps/ops/ansible/modules_utils/custom_common.py b/apps/ops/ansible/modules_utils/custom_common.py index 0eb454e5a..920ba6942 100644 --- a/apps/ops/ansible/modules_utils/custom_common.py +++ b/apps/ops/ansible/modules_utils/custom_common.py @@ -63,13 +63,13 @@ class SSHClient: @staticmethod def _is_match_user(user, content): # 正常命令切割后是[命令,用户名,交互前缀] - remote_user = content.split()[1] if len(content.split()) >= 3 else None - return remote_user and remote_user == user + content_list = content.split() if len(content.split()) >= 3 else None + return content_list and user in content_list def switch_user(self): self._get_channel() if not self.module.params['become']: - return None + return method = self.module.params['become_method'] username = self.module.params['login_user'] if method == 'sudo': @@ -85,7 +85,7 @@ class SSHClient: su_output, err_msg = self.execute(commands) if err_msg: return err_msg - i_output, err_msg = self.execute(['whoami']) + i_output, err_msg = self.execute(['whoami'], delay_time=1) if err_msg: return err_msg @@ -153,14 +153,14 @@ class SSHClient: output = self.channel.recv(size).decode(encoding) return output - def execute(self, commands): + def execute(self, commands, delay_time=0.3): if not self.is_connect: self.connect() output, error_msg = '', '' try: for command in commands: self.channel.send(command + '\n') - time.sleep(0.3) + time.sleep(delay_time) output = self._get_recv() except Exception as e: error_msg = str(e) @@ -170,5 +170,5 @@ class SSHClient: try: self.channel.close() self.client.close() - except: + except Exception: pass From e00c804a5a0a919084fbae5d02a510acf6b1a47a Mon Sep 17 00:00:00 2001 From: Bai Date: Mon, 9 Oct 2023 14:39:13 +0800 Subject: [PATCH 035/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E7=99=BB=E5=BD=95=E5=9F=8E=E5=B8=82=E5=90=8D1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/utils.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/authentication/utils.py b/apps/authentication/utils.py index b53afa423..9cbc95bf2 100644 --- a/apps/authentication/utils.py +++ b/apps/authentication/utils.py @@ -20,19 +20,22 @@ def check_different_city_login_if_need(user, request): return ip = get_request_ip(request) or '0.0.0.0' - if not (ip and validate_ip(ip)): - city = DEFAULT_CITY - else: - city = get_ip_city(ip) or DEFAULT_CITY - city_white = [_('LAN'), 'LAN'] is_private = ipaddress.ip_address(ip).is_private - if not is_private: - last_user_login = UserLoginLog.objects.exclude(city__in=city_white) \ - .filter(username=user.username, status=True).first() + if is_private: + return + last_user_login = UserLoginLog.objects.exclude( + city__in=city_white + ).filter(username=user.username, status=True).first() + if not last_user_login: + return - if last_user_login and last_user_login.city != city: - DifferentCityLoginMessage(user, ip, city).publish_async() + city = get_ip_city(ip) + last_city = get_ip_city(last_user_login.ip) + if city == last_city: + return + + DifferentCityLoginMessage(user, ip, city).publish_async() def build_absolute_uri(request, path=None): From c981e9cd9ff13d9522a384831eafbd6e496f0890 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 9 Oct 2023 16:05:42 +0800 Subject: [PATCH 036/114] =?UTF-8?q?perf:=20=E4=B8=BB=E6=9C=BA=E5=90=8D?= =?UTF-8?q?=E5=8C=85=E5=90=AB/=20=E5=8F=AF=E4=BB=A5=E6=89=A7=E8=A1=8Cansib?= =?UTF-8?q?le=E4=BB=BB=E5=8A=A1=20(#11772)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/utils.py | 2 +- apps/ops/ansible/inventory.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/accounts/utils.py b/apps/accounts/utils.py index fb3be63a7..ac896f8c0 100644 --- a/apps/accounts/utils.py +++ b/apps/accounts/utils.py @@ -16,7 +16,7 @@ class SecretGenerator: @staticmethod def generate_ssh_key(): - private_key, public_key = ssh_key_gen() + private_key, __ = ssh_key_gen() return private_key def generate_password(self): diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index 1c5fd17fc..1426ffc01 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -1,6 +1,7 @@ # ~*~ coding: utf-8 ~*~ import json import os +import re from collections import defaultdict from django.utils.translation import gettext as _ @@ -159,7 +160,7 @@ class JMSInventory: protocol = self.get_primary_protocol(ansible_config, protocols) tp, category = asset.type, asset.category - name = asset.name.replace(' ', '_').replace('[', '_').replace(']', '_') + name = re.sub(r'[ \[\]/]', '_', asset.name) secret_info = {k: v for k, v in asset.secret_info.items() if v} host = { 'name': name, From 3d27986c96b7fec39d03a88d9d53141df22e4b87 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 9 Oct 2023 17:16:38 +0800 Subject: [PATCH 037/114] =?UTF-8?q?perf:=20asset=20login=20=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E9=80=9A=E7=9F=A5=E6=B7=BB=E5=8A=A0=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95=20(#11774)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/const.py | 1 + apps/authentication/api/connection_token.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/audits/const.py b/apps/audits/const.py index 77a76c804..b08595a2f 100644 --- a/apps/audits/const.py +++ b/apps/audits/const.py @@ -33,6 +33,7 @@ class ActionChoices(TextChoices): reject = 'reject', _('Reject') accept = 'accept', _('Accept') review = 'review', _('Review') + notice = 'notice', _('Notifications') class LoginTypeChoices(TextChoices): diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 5b9481da0..8d7c753a9 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -412,6 +412,9 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView return ticket if acl.is_action(acl.ActionChoices.notice): reviewers = acl.reviewers.all() + if not reviewers: + return + self._record_operate_log(acl, asset) for reviewer in reviewers: AssetLoginReminderMsg(reviewer, asset, user).publish_async() From 3ac35eec68926df4c1238fa24328ccf90a0db21d Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Mon, 9 Oct 2023 17:55:44 +0800 Subject: [PATCH 038/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96OIDC=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=9C=AA=E6=BF=80=E6=B4=BB=E6=97=B6=EF=BC=8C=E4=BC=9A?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E8=B7=B3=E8=BD=AC=E7=99=BB=E5=BD=95=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/backends/oidc/views.py | 2 +- apps/authentication/middleware.py | 2 +- apps/authentication/mixins.py | 6 ++ apps/locale/ja/LC_MESSAGES/django.po | 110 ++++++++++----------- apps/locale/zh/LC_MESSAGES/django.po | 88 +++++++++-------- 5 files changed, 110 insertions(+), 98 deletions(-) diff --git a/apps/authentication/backends/oidc/views.py b/apps/authentication/backends/oidc/views.py index c638aeef6..98bd2ef2a 100644 --- a/apps/authentication/backends/oidc/views.py +++ b/apps/authentication/backends/oidc/views.py @@ -166,7 +166,7 @@ class OIDCAuthCallbackView(View): code_verifier = request.session.get('oidc_auth_code_verifier', None) logger.debug(log_prompt.format('Process authenticate')) user = auth.authenticate(nonce=nonce, request=request, code_verifier=code_verifier) - if user and user.is_valid: + if user: logger.debug(log_prompt.format('Login: {}'.format(user))) auth.login(self.request, user) # Stores an expiration timestamp in the user's session. This value will be used if diff --git a/apps/authentication/middleware.py b/apps/authentication/middleware.py index b4ee86ed4..99aabb2c3 100644 --- a/apps/authentication/middleware.py +++ b/apps/authentication/middleware.py @@ -92,7 +92,7 @@ class ThirdPartyLoginMiddleware(mixins.AuthMixin): 'title': _('Authentication failed'), 'message': _('Authentication failed (before login check failed): {}').format(e), 'interval': 10, - 'redirect_url': reverse('authentication:login'), + 'redirect_url': reverse('authentication:login') + '?admin=0', 'auto_redirect': True, } response = render(request, 'authentication/auth_fail_flash_message_standalone.html', context) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 9667e85c7..faa81736a 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -76,6 +76,12 @@ def authenticate(request=None, **credentials): if user is None: continue + if not user.is_valid: + temp_user = user + temp_user.backend = backend_path + request.error_message = _('User is not valid') + return temp_user + # 检查用户是否允许认证 if not backend.user_allow_authenticate(user): temp_user = user diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 0dcc50186..f27672f02 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: 2023-10-07 17:56+0800\n" +"POT-Creation-Date: 2023-10-09 17:52+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -244,7 +244,7 @@ msgstr "資産" msgid "Su from" msgstr "から切り替え" -#: accounts/models/account.py:54 assets/const/protocol.py:168 +#: accounts/models/account.py:54 assets/const/protocol.py:169 #: settings/serializers/auth/cas.py:20 settings/serializers/auth/feishu.py:20 #: terminal/models/applet/applet.py:35 msgid "Version" @@ -500,7 +500,7 @@ msgstr "アカウントの確認" msgid "Secret type" msgstr "鍵の種類" -#: accounts/models/base.py:39 accounts/models/mixins/vault.py:48 +#: accounts/models/base.py:39 accounts/models/mixins/vault.py:49 #: accounts/serializers/account/base.py:19 #: authentication/models/temp_token.py:10 #: authentication/templates/authentication/_access_key_modal.html:31 @@ -1318,7 +1318,7 @@ msgstr "無効" msgid "Basic" msgstr "基本" -#: assets/const/base.py:35 assets/const/protocol.py:245 +#: assets/const/base.py:35 assets/const/protocol.py:252 #: assets/models/asset/web.py:13 msgid "Script" msgstr "脚本" @@ -1386,11 +1386,11 @@ msgstr "ChatGPT" msgid "Other" msgstr "その他" -#: assets/const/protocol.py:48 +#: assets/const/protocol.py:49 msgid "SFTP root" msgstr "SFTPルート" -#: assets/const/protocol.py:50 +#: assets/const/protocol.py:51 #, python-brace-format msgid "" "SFTP root directory, Support variable:
- ${ACCOUNT} The connected " @@ -1401,81 +1401,81 @@ msgstr "" "ユーザー名
-${HOME}接続されたアカウントのホームディレクトリ
-${USER}" "ユーザーのユーザー名" -#: assets/const/protocol.py:65 +#: assets/const/protocol.py:66 msgid "Console" msgstr "Console" -#: assets/const/protocol.py:66 +#: assets/const/protocol.py:67 msgid "Connect to console session" msgstr "コンソールセッションに接続" -#: assets/const/protocol.py:70 +#: assets/const/protocol.py:71 msgid "Any" msgstr "任意" -#: assets/const/protocol.py:72 settings/serializers/security.py:228 +#: assets/const/protocol.py:73 settings/serializers/security.py:228 msgid "Security" msgstr "セキュリティ" -#: assets/const/protocol.py:73 +#: assets/const/protocol.py:74 msgid "Security layer to use for the connection" msgstr "接続に使用するセキュリティ レイヤー" -#: assets/const/protocol.py:79 +#: assets/const/protocol.py:80 msgid "AD domain" msgstr "AD ドメイン" -#: assets/const/protocol.py:94 +#: assets/const/protocol.py:95 msgid "Username prompt" msgstr "ユーザー名プロンプト" -#: assets/const/protocol.py:95 +#: assets/const/protocol.py:96 msgid "We will send username when we see this prompt" msgstr "このプロンプトが表示されたらユーザー名を送信します" -#: assets/const/protocol.py:100 +#: assets/const/protocol.py:101 msgid "Password prompt" msgstr "パスワードプロンプト" -#: assets/const/protocol.py:101 +#: assets/const/protocol.py:102 msgid "We will send password when we see this prompt" msgstr "このプロンプトが表示されたらパスワードを送信します" -#: assets/const/protocol.py:106 +#: assets/const/protocol.py:107 msgid "Success prompt" msgstr "成功プロンプト" -#: assets/const/protocol.py:107 +#: assets/const/protocol.py:108 msgid "We will consider login success when we see this prompt" msgstr "このプロンプトが表示されたらログイン成功とみなします" -#: assets/const/protocol.py:118 assets/models/asset/database.py:10 +#: assets/const/protocol.py:119 assets/models/asset/database.py:10 #: settings/serializers/msg.py:40 msgid "Use SSL" msgstr "SSLの使用" -#: assets/const/protocol.py:153 +#: assets/const/protocol.py:154 msgid "SYSDBA" msgstr "SYSDBA" -#: assets/const/protocol.py:154 +#: assets/const/protocol.py:155 msgid "Connect as SYSDBA" msgstr "SYSDBA として接続" -#: assets/const/protocol.py:169 +#: assets/const/protocol.py:170 msgid "" "SQL Server version, Different versions have different connection drivers" msgstr "SQL Server のバージョン。バージョンによって接続ドライバが異なります" -#: assets/const/protocol.py:192 +#: assets/const/protocol.py:199 msgid "Auth username" msgstr "ユーザー名で認証する" -#: assets/const/protocol.py:215 +#: assets/const/protocol.py:222 msgid "Safe mode" msgstr "安全モード" -#: assets/const/protocol.py:217 +#: assets/const/protocol.py:224 msgid "" "When safe mode is enabled, some operations will be disabled, such as: New " "tab, right click, visit other website, etc." @@ -1483,24 +1483,24 @@ msgstr "" "安全モードが有効になっている場合、新しいタブ、右クリック、他のウェブサイトへ" "のアクセスなど、一部の操作が無効になります" -#: assets/const/protocol.py:222 assets/models/asset/web.py:9 +#: assets/const/protocol.py:229 assets/models/asset/web.py:9 #: assets/serializers/asset/info/spec.py:16 msgid "Autofill" msgstr "自動充填" -#: assets/const/protocol.py:230 assets/models/asset/web.py:10 +#: assets/const/protocol.py:237 assets/models/asset/web.py:10 msgid "Username selector" msgstr "ユーザー名ピッカー" -#: assets/const/protocol.py:235 assets/models/asset/web.py:11 +#: assets/const/protocol.py:242 assets/models/asset/web.py:11 msgid "Password selector" msgstr "パスワードセレクター" -#: assets/const/protocol.py:240 assets/models/asset/web.py:12 +#: assets/const/protocol.py:247 assets/models/asset/web.py:12 msgid "Submit selector" msgstr "ボタンセレクターを確認する" -#: assets/const/protocol.py:263 +#: assets/const/protocol.py:270 msgid "API mode" msgstr "APIモード" @@ -2835,15 +2835,15 @@ msgstr "パスワードが無効です" msgid "Please wait for %s seconds before retry" msgstr "%s 秒後に再試行してください" -#: authentication/errors/redirect.py:85 authentication/mixins.py:318 +#: authentication/errors/redirect.py:85 authentication/mixins.py:324 msgid "Your password is too simple, please change it for security" msgstr "パスワードがシンプルすぎるので、セキュリティのために変更してください" -#: authentication/errors/redirect.py:93 authentication/mixins.py:325 +#: authentication/errors/redirect.py:93 authentication/mixins.py:331 msgid "You should to change your password before login" msgstr "ログインする前にパスワードを変更する必要があります" -#: authentication/errors/redirect.py:101 authentication/mixins.py:332 +#: authentication/errors/redirect.py:101 authentication/mixins.py:338 msgid "Your password has expired, please reset before logging in" msgstr "" "パスワードの有効期限が切れました。ログインする前にリセットしてください。" @@ -2946,7 +2946,11 @@ msgstr "無効にする電話番号をクリアする" msgid "Authentication failed (before login check failed): {}" msgstr "認証に失敗しました (ログインチェックが失敗する前): {}" -#: authentication/mixins.py:91 +#: authentication/mixins.py:82 +msgid "User is not valid" +msgstr "無効なユーザーです" + +#: authentication/mixins.py:97 msgid "" "The administrator has enabled 'Only allow login from user source'. \n" " The current user source is {}. Please contact the administrator." @@ -2954,11 +2958,11 @@ msgstr "" "管理者は「ユーザーソースからのみログインを許可」をオンにしており、現在のユー" "ザーソースは {} です。管理者に連絡してください。" -#: authentication/mixins.py:268 +#: authentication/mixins.py:274 msgid "The MFA type ({}) is not enabled" msgstr "MFAタイプ ({}) が有効になっていない" -#: authentication/mixins.py:308 +#: authentication/mixins.py:314 msgid "Please change your password" msgstr "パスワードを変更してください" @@ -3364,7 +3368,7 @@ msgstr "" msgid "Do you want to retry ?" msgstr "再試行しますか?" -#: authentication/utils.py:28 common/utils/ip/geoip/utils.py:24 +#: authentication/utils.py:23 common/utils/ip/geoip/utils.py:24 #: xpack/plugins/cloud/const.py:29 msgid "LAN" msgstr "ローカルエリアネットワーク" @@ -3861,15 +3865,15 @@ msgstr "システムメッセージ" msgid "Publish the station message" msgstr "投稿サイトニュース" -#: ops/ansible/inventory.py:92 ops/models/job.py:60 +#: ops/ansible/inventory.py:95 ops/models/job.py:60 msgid "No account available" msgstr "利用可能なアカウントがありません" -#: ops/ansible/inventory.py:263 +#: ops/ansible/inventory.py:259 msgid "Ansible disabled" msgstr "Ansible 無効" -#: ops/ansible/inventory.py:279 +#: ops/ansible/inventory.py:275 msgid "Skip hosts below:" msgstr "次のホストをスキップします: " @@ -5208,39 +5212,39 @@ msgstr "サポートリンク" msgid "default: http://www.jumpserver.org/support/" msgstr "デフォルト: http://www.jumpserver.org/support/" -#: settings/serializers/cleaning.py:10 +#: settings/serializers/cleaning.py:11 msgid "Period clean" msgstr "定時清掃" -#: settings/serializers/cleaning.py:14 +#: settings/serializers/cleaning.py:15 msgid "Login log keep days (day)" msgstr "ログインログは日数を保持します(天)" -#: settings/serializers/cleaning.py:18 +#: settings/serializers/cleaning.py:19 msgid "Task log keep days (day)" msgstr "タスクログは日数を保持します(天)" -#: settings/serializers/cleaning.py:22 +#: settings/serializers/cleaning.py:23 msgid "Operate log keep days (day)" msgstr "ログ管理日を操作する(天)" -#: settings/serializers/cleaning.py:26 +#: settings/serializers/cleaning.py:27 msgid "FTP log keep days (day)" msgstr "ダウンロードのアップロード(天)" -#: settings/serializers/cleaning.py:30 +#: settings/serializers/cleaning.py:31 msgid "Cloud sync record keep days (day)" msgstr "クラウド同期レコードは日数を保持します(天)" -#: settings/serializers/cleaning.py:34 +#: settings/serializers/cleaning.py:35 msgid "Activity log keep days (day)" msgstr "活動ログは日数を保持します(天)" -#: settings/serializers/cleaning.py:37 +#: settings/serializers/cleaning.py:38 msgid "Session keep duration (day)" msgstr "セッション維持期間(天)" -#: settings/serializers/cleaning.py:39 +#: settings/serializers/cleaning.py:40 msgid "" "Session, record, command will be delete if more than duration, only in " "database, OSS will not be affected." @@ -5248,10 +5252,6 @@ msgstr "" "この期間を超えるセッション、録音、およびコマンド レコードは削除されます (デー" "タベースのバックアップに影響し、OSS などには影響しません)" -#: settings/serializers/cleaning.py:53 -msgid "must be greater than {} days." -msgstr "" - #: settings/serializers/feature.py:16 msgid "Subject" msgstr "件名" @@ -6260,7 +6260,7 @@ msgstr "一括作成非サポート" msgid "Storage is invalid" msgstr "ストレージが無効です" -#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:88 +#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:86 msgid "Community edition" msgstr "コミュニティ版" @@ -8536,7 +8536,7 @@ msgstr "ライセンスのインポートに成功" msgid "License is invalid" msgstr "ライセンスが無効です" -#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 +#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:138 msgid "License" msgstr "ライセンス" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 904b5a1e1..d37f44618 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: 2023-10-07 17:56+0800\n" +"POT-Creation-Date: 2023-10-09 17:52+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -1428,69 +1428,69 @@ msgstr "当我们看到这个提示时,我们将发送用户名" msgid "Password prompt" msgstr "密码提示" -#: assets/const/protocol.py:101 +#: assets/const/protocol.py:102 msgid "We will send password when we see this prompt" msgstr "当我们看到这个提示时,我们将发送密码" -#: assets/const/protocol.py:106 +#: assets/const/protocol.py:107 msgid "Success prompt" msgstr "成功提示" -#: assets/const/protocol.py:107 +#: assets/const/protocol.py:108 msgid "We will consider login success when we see this prompt" msgstr "当我们看到这个提示时,我们将认为登录成功" -#: assets/const/protocol.py:118 assets/models/asset/database.py:10 +#: assets/const/protocol.py:119 assets/models/asset/database.py:10 #: settings/serializers/msg.py:40 msgid "Use SSL" msgstr "使用 SSL" -#: assets/const/protocol.py:153 +#: assets/const/protocol.py:154 msgid "SYSDBA" msgstr "SYSDBA" -#: assets/const/protocol.py:154 +#: assets/const/protocol.py:155 msgid "Connect as SYSDBA" msgstr "以 SYSDBA 角色连接" -#: assets/const/protocol.py:169 +#: assets/const/protocol.py:170 msgid "" "SQL Server version, Different versions have different connection drivers" msgstr "SQL Server 版本,不同版本有不同的连接驱动" -#: assets/const/protocol.py:192 +#: assets/const/protocol.py:199 msgid "Auth username" msgstr "使用用户名认证" -#: assets/const/protocol.py:215 +#: assets/const/protocol.py:222 msgid "Safe mode" msgstr "安全模式" -#: assets/const/protocol.py:217 +#: assets/const/protocol.py:224 msgid "" "When safe mode is enabled, some operations will be disabled, such as: New " "tab, right click, visit other website, etc." msgstr "" "当安全模式启用时,一些操作将被禁用,例如:新建标签页、右键、访问其它网站 等" -#: assets/const/protocol.py:222 assets/models/asset/web.py:9 +#: assets/const/protocol.py:229 assets/models/asset/web.py:9 #: assets/serializers/asset/info/spec.py:16 msgid "Autofill" msgstr "自动代填" -#: assets/const/protocol.py:230 assets/models/asset/web.py:10 +#: assets/const/protocol.py:237 assets/models/asset/web.py:10 msgid "Username selector" msgstr "用户名选择器" -#: assets/const/protocol.py:235 assets/models/asset/web.py:11 +#: assets/const/protocol.py:242 assets/models/asset/web.py:11 msgid "Password selector" msgstr "密码选择器" -#: assets/const/protocol.py:240 assets/models/asset/web.py:12 +#: assets/const/protocol.py:247 assets/models/asset/web.py:12 msgid "Submit selector" msgstr "确认按钮选择器" -#: assets/const/protocol.py:263 +#: assets/const/protocol.py:270 msgid "API mode" msgstr "API 模式" @@ -2806,15 +2806,15 @@ msgstr "您的密码无效" msgid "Please wait for %s seconds before retry" msgstr "请在 %s 秒后重试" -#: authentication/errors/redirect.py:85 authentication/mixins.py:318 +#: authentication/errors/redirect.py:85 authentication/mixins.py:324 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" -#: authentication/errors/redirect.py:93 authentication/mixins.py:325 +#: authentication/errors/redirect.py:93 authentication/mixins.py:331 msgid "You should to change your password before login" msgstr "登录完成前,请先修改密码" -#: authentication/errors/redirect.py:101 authentication/mixins.py:332 +#: authentication/errors/redirect.py:101 authentication/mixins.py:338 msgid "Your password has expired, please reset before logging in" msgstr "您的密码已过期,先修改再登录" @@ -2915,17 +2915,21 @@ msgstr "清空手机号码禁用" msgid "Authentication failed (before login check failed): {}" msgstr "认证失败 (登录前检查失败): {}" -#: authentication/mixins.py:91 +#: authentication/mixins.py:82 +msgid "User is not valid" +msgstr "无效的用户" + +#: authentication/mixins.py:97 msgid "" "The administrator has enabled 'Only allow login from user source'. \n" " The current user source is {}. Please contact the administrator." msgstr "管理员已开启'仅允许从用户来源登录',当前用户来源为{},请联系管理员。" -#: authentication/mixins.py:268 +#: authentication/mixins.py:274 msgid "The MFA type ({}) is not enabled" msgstr "该 MFA ({}) 方式没有启用" -#: authentication/mixins.py:308 +#: authentication/mixins.py:314 msgid "Please change your password" msgstr "请修改密码" @@ -3319,7 +3323,7 @@ msgstr "本页面未使用 HTTPS 协议,请使用 HTTPS 协议以确保您的 msgid "Do you want to retry ?" msgstr "是否重试 ?" -#: authentication/utils.py:28 common/utils/ip/geoip/utils.py:24 +#: authentication/utils.py:23 common/utils/ip/geoip/utils.py:24 #: xpack/plugins/cloud/const.py:29 msgid "LAN" msgstr "局域网" @@ -3809,15 +3813,15 @@ msgstr "系统信息" msgid "Publish the station message" msgstr "发布站内消息" -#: ops/ansible/inventory.py:92 ops/models/job.py:60 +#: ops/ansible/inventory.py:95 ops/models/job.py:60 msgid "No account available" msgstr "无可用账号" -#: ops/ansible/inventory.py:263 +#: ops/ansible/inventory.py:259 msgid "Ansible disabled" msgstr "Ansible 已禁用" -#: ops/ansible/inventory.py:279 +#: ops/ansible/inventory.py:275 msgid "Skip hosts below:" msgstr "跳过以下主机: " @@ -5151,49 +5155,45 @@ msgstr "支持链接" msgid "default: http://www.jumpserver.org/support/" msgstr "默认: http://www.jumpserver.org/support/" -#: settings/serializers/cleaning.py:10 +#: settings/serializers/cleaning.py:11 msgid "Period clean" msgstr "定時清掃" -#: settings/serializers/cleaning.py:14 +#: settings/serializers/cleaning.py:15 msgid "Login log keep days (day)" msgstr "登录日志 (天)" -#: settings/serializers/cleaning.py:18 +#: settings/serializers/cleaning.py:19 msgid "Task log keep days (day)" msgstr "任务日志 (天)" -#: settings/serializers/cleaning.py:22 +#: settings/serializers/cleaning.py:23 msgid "Operate log keep days (day)" msgstr "操作日志 (天)" -#: settings/serializers/cleaning.py:26 +#: settings/serializers/cleaning.py:27 msgid "FTP log keep days (day)" msgstr "上传下载 (天)" -#: settings/serializers/cleaning.py:30 +#: settings/serializers/cleaning.py:31 msgid "Cloud sync record keep days (day)" msgstr "云同步记录 (天)" -#: settings/serializers/cleaning.py:34 +#: settings/serializers/cleaning.py:35 msgid "Activity log keep days (day)" msgstr "活动记录 (天)" -#: settings/serializers/cleaning.py:37 +#: settings/serializers/cleaning.py:38 msgid "Session keep duration (day)" msgstr "会话日志 (天)" -#: settings/serializers/cleaning.py:39 +#: settings/serializers/cleaning.py:40 msgid "" "Session, record, command will be delete if more than duration, only in " "database, OSS will not be affected." msgstr "" "会话、录像,命令记录超过该时长将会被清除 (影响数据库存储,OSS 等不受影响)" -#: settings/serializers/cleaning.py:53 -msgid "must be greater than {} days." -msgstr "" - #: settings/serializers/feature.py:16 msgid "Subject" msgstr "主题" @@ -6168,7 +6168,7 @@ msgstr "不支持批量创建" msgid "Storage is invalid" msgstr "存储无效" -#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:88 +#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:86 msgid "Community edition" msgstr "社区版" @@ -8416,7 +8416,7 @@ msgstr "许可证导入成功" msgid "License is invalid" msgstr "无效的许可证" -#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 +#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:138 msgid "License" msgstr "许可证" @@ -8436,5 +8436,11 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" +#~ msgid "Basic edition" +#~ msgstr "企业基础版" + +#~ msgid "Professional edition" +#~ msgstr "企业专业版" + #~ msgid "Random" #~ msgstr "随机" From 814350ab804eb03e0ec74b3ee58c28483036a1a5 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Mon, 9 Oct 2023 17:58:23 +0800 Subject: [PATCH 039/114] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.po | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index d37f44618..28412b41c 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -8436,11 +8436,5 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" -#~ msgid "Basic edition" -#~ msgstr "企业基础版" - -#~ msgid "Professional edition" -#~ msgstr "企业专业版" - #~ msgid "Random" #~ msgstr "随机" From 95e7bde5d7cbfd4f06b0b440f28f56d996e91ac1 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Mon, 9 Oct 2023 18:34:23 +0800 Subject: [PATCH 040/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=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/authentication/mixins.py | 2 +- apps/locale/ja/LC_MESSAGES/django.po | 30 +++++++------- apps/locale/zh/LC_MESSAGES/django.po | 58 ++++++++++++++-------------- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index faa81736a..906fcbb97 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -79,7 +79,7 @@ def authenticate(request=None, **credentials): if not user.is_valid: temp_user = user temp_user.backend = backend_path - request.error_message = _('User is not valid') + request.error_message = _('User is invalid') return temp_user # 检查用户是否允许认证 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index f27672f02..99759dcae 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: 2023-10-09 17:52+0800\n" +"POT-Creation-Date: 2023-10-09 18:32+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -94,7 +94,7 @@ msgid "Update" msgstr "更新" #: accounts/const/account.py:33 -#: accounts/serializers/automations/change_secret.py:155 audits/const.py:59 +#: accounts/serializers/automations/change_secret.py:155 audits/const.py:60 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 #: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 msgid "Failed" @@ -893,7 +893,7 @@ msgstr "* パスワードの長さの範囲6-30ビット" msgid "Automation task execution" msgstr "自動タスク実行履歴" -#: accounts/serializers/automations/change_secret.py:154 audits/const.py:58 +#: accounts/serializers/automations/change_secret.py:154 audits/const.py:59 #: audits/models.py:62 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: terminal/const.py:76 terminal/models/session/sharing.py:121 @@ -985,7 +985,7 @@ msgstr "レビュー担当者" msgid "Warning" msgstr "警告" -#: acls/const.py:10 notifications/apps.py:7 +#: acls/const.py:10 audits/const.py:36 notifications/apps.py:7 msgid "Notifications" msgstr "通知" @@ -1284,7 +1284,7 @@ msgstr "認証に失敗しました" msgid "Connect failed" msgstr "接続に失敗しました" -#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:41 +#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:42 #: audits/signal_handlers/activity_log.py:62 common/utils/ip/geoip/utils.py:31 #: common/utils/ip/geoip/utils.py:37 common/utils/ip/utils.py:104 msgid "Unknown" @@ -1306,7 +1306,7 @@ msgstr "テストゲートウェイ" msgid "Gather facts" msgstr "資産情報の収集" -#: assets/const/base.py:33 audits/const.py:52 +#: assets/const/base.py:33 audits/const.py:53 #: terminal/serializers/applet_host.py:32 msgid "Disabled" msgstr "無効" @@ -1340,7 +1340,7 @@ msgid "Cloud service" msgstr "クラウド サービス" #: assets/const/category.py:14 assets/models/asset/gpt.py:11 -#: assets/models/asset/web.py:16 audits/const.py:39 +#: assets/models/asset/web.py:16 audits/const.py:40 #: terminal/models/applet/applet.py:27 users/const.py:47 msgid "Web" msgstr "Web" @@ -1851,7 +1851,7 @@ msgstr "開ける" msgid "Setting" msgstr "設定" -#: assets/models/platform.py:38 audits/const.py:53 +#: assets/models/platform.py:38 audits/const.py:54 #: authentication/backends/passkey/models.py:11 settings/models.py:36 #: terminal/serializers/applet_host.py:33 msgid "Enabled" @@ -2299,30 +2299,30 @@ msgstr "ログイン" msgid "Change password" msgstr "パスワードを変更する" -#: audits/const.py:40 settings/serializers/terminal.py:6 +#: audits/const.py:41 settings/serializers/terminal.py:6 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 #: terminal/serializers/session.py:49 terminal/serializers/session.py:63 msgid "Terminal" msgstr "ターミナル" -#: audits/const.py:45 audits/models.py:130 +#: audits/const.py:46 audits/models.py:130 msgid "Operate log" msgstr "ログの操作" -#: audits/const.py:46 +#: audits/const.py:47 msgid "Session log" msgstr "セッションログ" -#: audits/const.py:47 +#: audits/const.py:48 msgid "Login log" msgstr "ログインログ" -#: audits/const.py:48 terminal/models/applet/host.py:143 +#: audits/const.py:49 terminal/models/applet/host.py:143 #: terminal/models/component/task.py:22 msgid "Task" msgstr "タスク" -#: audits/const.py:54 +#: audits/const.py:55 msgid "-" msgstr "-" @@ -2947,7 +2947,7 @@ msgid "Authentication failed (before login check failed): {}" msgstr "認証に失敗しました (ログインチェックが失敗する前): {}" #: authentication/mixins.py:82 -msgid "User is not valid" +msgid "User is invalid" msgstr "無効なユーザーです" #: authentication/mixins.py:97 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 28412b41c..aeea7f4b5 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: 2023-10-09 17:52+0800\n" +"POT-Creation-Date: 2023-10-09 18:33+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -93,7 +93,7 @@ msgid "Update" msgstr "更新" #: accounts/const/account.py:33 -#: accounts/serializers/automations/change_secret.py:155 audits/const.py:59 +#: accounts/serializers/automations/change_secret.py:155 audits/const.py:60 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 #: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 msgid "Failed" @@ -243,7 +243,7 @@ msgstr "资产" msgid "Su from" msgstr "切换自" -#: accounts/models/account.py:54 assets/const/protocol.py:168 +#: accounts/models/account.py:54 assets/const/protocol.py:169 #: settings/serializers/auth/cas.py:20 settings/serializers/auth/feishu.py:20 #: terminal/models/applet/applet.py:35 msgid "Version" @@ -499,7 +499,7 @@ msgstr "账号验证" msgid "Secret type" msgstr "密文类型" -#: accounts/models/base.py:39 accounts/models/mixins/vault.py:48 +#: accounts/models/base.py:39 accounts/models/mixins/vault.py:49 #: accounts/serializers/account/base.py:19 #: authentication/models/temp_token.py:10 #: authentication/templates/authentication/_access_key_modal.html:31 @@ -890,7 +890,7 @@ msgstr "* 密码长度范围 6-30 位" msgid "Automation task execution" msgstr "自动化任务执行历史" -#: accounts/serializers/automations/change_secret.py:154 audits/const.py:58 +#: accounts/serializers/automations/change_secret.py:154 audits/const.py:59 #: audits/models.py:62 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: terminal/const.py:76 terminal/models/session/sharing.py:121 @@ -982,7 +982,7 @@ msgstr "审批" msgid "Warning" msgstr "告警" -#: acls/const.py:10 notifications/apps.py:7 +#: acls/const.py:10 audits/const.py:36 notifications/apps.py:7 msgid "Notifications" msgstr "通知" @@ -1276,7 +1276,7 @@ msgstr "认证失败" msgid "Connect failed" msgstr "连接失败" -#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:41 +#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:42 #: audits/signal_handlers/activity_log.py:62 common/utils/ip/geoip/utils.py:31 #: common/utils/ip/geoip/utils.py:37 common/utils/ip/utils.py:104 msgid "Unknown" @@ -1298,7 +1298,7 @@ msgstr "测试网关" msgid "Gather facts" msgstr "收集资产信息" -#: assets/const/base.py:33 audits/const.py:52 +#: assets/const/base.py:33 audits/const.py:53 #: terminal/serializers/applet_host.py:32 msgid "Disabled" msgstr "禁用" @@ -1310,7 +1310,7 @@ msgstr "禁用" msgid "Basic" msgstr "基本" -#: assets/const/base.py:35 assets/const/protocol.py:245 +#: assets/const/base.py:35 assets/const/protocol.py:252 #: assets/models/asset/web.py:13 msgid "Script" msgstr "脚本" @@ -1332,7 +1332,7 @@ msgid "Cloud service" msgstr "云服务" #: assets/const/category.py:14 assets/models/asset/gpt.py:11 -#: assets/models/asset/web.py:16 audits/const.py:39 +#: assets/models/asset/web.py:16 audits/const.py:40 #: terminal/models/applet/applet.py:27 users/const.py:47 msgid "Web" msgstr "Web" @@ -1378,11 +1378,11 @@ msgstr "ChatGPT" msgid "Other" msgstr "其它" -#: assets/const/protocol.py:48 +#: assets/const/protocol.py:49 msgid "SFTP root" msgstr "SFTP 根路径" -#: assets/const/protocol.py:50 +#: assets/const/protocol.py:51 #, python-brace-format msgid "" "SFTP root directory, Support variable:
- ${ACCOUNT} The connected " @@ -1392,39 +1392,39 @@ msgstr "" "SFTP根目录,支持变量:
-${ACCOUNT}已连接帐户用户名
-${HOME}连接帐户的主" "目录
-${USER}用户的用户名" -#: assets/const/protocol.py:65 +#: assets/const/protocol.py:66 msgid "Console" msgstr "控制台" -#: assets/const/protocol.py:66 +#: assets/const/protocol.py:67 msgid "Connect to console session" msgstr "连接到控制台会话" -#: assets/const/protocol.py:70 +#: assets/const/protocol.py:71 msgid "Any" msgstr "任意" -#: assets/const/protocol.py:72 settings/serializers/security.py:228 +#: assets/const/protocol.py:73 settings/serializers/security.py:228 msgid "Security" msgstr "安全" -#: assets/const/protocol.py:73 +#: assets/const/protocol.py:74 msgid "Security layer to use for the connection" msgstr "连接 RDP 使用的安全层" -#: assets/const/protocol.py:79 +#: assets/const/protocol.py:80 msgid "AD domain" msgstr "AD 网域" -#: assets/const/protocol.py:94 +#: assets/const/protocol.py:95 msgid "Username prompt" msgstr "用户名提示" -#: assets/const/protocol.py:95 +#: assets/const/protocol.py:96 msgid "We will send username when we see this prompt" msgstr "当我们看到这个提示时,我们将发送用户名" -#: assets/const/protocol.py:100 +#: assets/const/protocol.py:101 msgid "Password prompt" msgstr "密码提示" @@ -1843,7 +1843,7 @@ msgstr "开放的" msgid "Setting" msgstr "设置" -#: assets/models/platform.py:38 audits/const.py:53 +#: assets/models/platform.py:38 audits/const.py:54 #: authentication/backends/passkey/models.py:11 settings/models.py:36 #: terminal/serializers/applet_host.py:33 msgid "Enabled" @@ -2282,30 +2282,30 @@ msgstr "登录" msgid "Change password" msgstr "改密" -#: audits/const.py:40 settings/serializers/terminal.py:6 +#: audits/const.py:41 settings/serializers/terminal.py:6 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 #: terminal/serializers/session.py:49 terminal/serializers/session.py:63 msgid "Terminal" msgstr "终端" -#: audits/const.py:45 audits/models.py:130 +#: audits/const.py:46 audits/models.py:130 msgid "Operate log" msgstr "操作日志" -#: audits/const.py:46 +#: audits/const.py:47 msgid "Session log" msgstr "会话日志" -#: audits/const.py:47 +#: audits/const.py:48 msgid "Login log" msgstr "登录日志" -#: audits/const.py:48 terminal/models/applet/host.py:143 +#: audits/const.py:49 terminal/models/applet/host.py:143 #: terminal/models/component/task.py:22 msgid "Task" msgstr "任务" -#: audits/const.py:54 +#: audits/const.py:55 msgid "-" msgstr "-" @@ -2916,7 +2916,7 @@ msgid "Authentication failed (before login check failed): {}" msgstr "认证失败 (登录前检查失败): {}" #: authentication/mixins.py:82 -msgid "User is not valid" +msgid "User is invalid" msgstr "无效的用户" #: authentication/mixins.py:97 From 11157563ba0b5432fa406fcb2b68bf7820aefc90 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Mon, 9 Oct 2023 18:55:05 +0800 Subject: [PATCH 041/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E6=8E=A5=E5=8F=A3=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/authentication/middleware.py b/apps/authentication/middleware.py index 99aabb2c3..572ad39dc 100644 --- a/apps/authentication/middleware.py +++ b/apps/authentication/middleware.py @@ -92,7 +92,7 @@ class ThirdPartyLoginMiddleware(mixins.AuthMixin): 'title': _('Authentication failed'), 'message': _('Authentication failed (before login check failed): {}').format(e), 'interval': 10, - 'redirect_url': reverse('authentication:login') + '?admin=0', + 'redirect_url': reverse('authentication:login') + '?admin=1', 'auto_redirect': True, } response = render(request, 'authentication/auth_fail_flash_message_standalone.html', context) From d6c0139fef8b1b391808cb7259ae7ee96d843a3e Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 9 Oct 2023 15:40:54 +0800 Subject: [PATCH 042/114] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=E6=8C=81?= =?UTF-8?q?=E4=B9=85=E5=8C=96=E8=AE=BE=E7=BD=AE=E4=B8=AA=E4=BA=BA=E7=BB=88?= =?UTF-8?q?=E7=AB=AF=E4=B8=BB=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/connection_token.py | 17 +++++++++++------ apps/users/serializers/preference/koko.py | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 8d7c753a9..b0756d4aa 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -314,13 +314,18 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView return super().perform_create(serializer) def _insert_connect_options(self, data, user): - name = 'file_name_conflict_resolution' connect_options = data.pop('connect_options', {}) - preference = Preference.objects.filter( - name=name, user=user, category='koko' - ).first() - value = preference.value if preference else FileNameConflictResolution.REPLACE - connect_options[name] = value + default_name_opts = { + 'file_name_conflict_resolution': FileNameConflictResolution.REPLACE, + 'terminal_theme_name': 'Default', + } + preferences_query = Preference.objects.filter( + user=user, category='koko', name__in=default_name_opts.keys() + ).values_list('name', 'value') + preferences = dict(preferences_query) + for name in default_name_opts.keys(): + value = preferences.get(name, default_name_opts[name]) + connect_options[name] = value data['connect_options'] = connect_options def validate_serializer(self, serializer): diff --git a/apps/users/serializers/preference/koko.py b/apps/users/serializers/preference/koko.py index 5c7ac2e8d..9a37c13a6 100644 --- a/apps/users/serializers/preference/koko.py +++ b/apps/users/serializers/preference/koko.py @@ -9,6 +9,8 @@ class BasicSerializer(serializers.Serializer): FileNameConflictResolution.choices, default=FileNameConflictResolution.REPLACE, required=False, label=_('File name conflict resolution') ) + terminal_theme_name = serializers.CharField( + max_length=128, required=False, default='Default', label=_('Terminal theme name')) class KokoSerializer(serializers.Serializer): From a844ce23e4d04071d61acc7ff4fc61c50c148dd9 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 9 Oct 2023 19:14:43 +0800 Subject: [PATCH 043/114] =?UTF-8?q?perf:=20=E8=B0=83=E6=95=B4=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/serializers/preference/koko.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/users/serializers/preference/koko.py b/apps/users/serializers/preference/koko.py index 9a37c13a6..12c497d47 100644 --- a/apps/users/serializers/preference/koko.py +++ b/apps/users/serializers/preference/koko.py @@ -10,7 +10,9 @@ class BasicSerializer(serializers.Serializer): required=False, label=_('File name conflict resolution') ) terminal_theme_name = serializers.CharField( - max_length=128, required=False, default='Default', label=_('Terminal theme name')) + max_length=128, required=False, default='Default', + label=_('Terminal theme name') + ) class KokoSerializer(serializers.Serializer): From 30b19d31ebcc864f0920cec930c18adb932ebe6b Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:24:56 +0800 Subject: [PATCH 044/114] =?UTF-8?q?fix:=20=E8=B4=A6=E5=8F=B7=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=9B=B4=E6=96=B0=E5=A4=B1=E8=B4=A5=20(#11785)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/models/mixins/vault.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/accounts/models/mixins/vault.py b/apps/accounts/models/mixins/vault.py index c5d0935c5..d61bc18bb 100644 --- a/apps/accounts/models/mixins/vault.py +++ b/apps/accounts/models/mixins/vault.py @@ -39,7 +39,7 @@ class VaultManagerMixin(models.Manager): def bulk_update(self, objs, fields, batch_size=None): fields = ["_secret" if field == "secret" else field for field in fields] - objs = super().bulk_update(objs, fields, batch_size=batch_size) + super().bulk_update(objs, fields, batch_size=batch_size) for obj in objs: post_save.send(obj.__class__, instance=obj, created=False) return objs From 333746e7c4852799fc60f2ad7b6768f12f9ff2ef Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:52:52 +0800 Subject: [PATCH 045/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7=20access=20key=20=E7=9A=84=E4=BD=BF=E7=94=A8=E5=92=8C?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=20(#11776)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 优化用户 access key 的使用和创建 * perf: 优化 access key api --------- Co-authored-by: ibuler --- .../migrations/0023_auto_20230906_1322.py | 2 +- apps/authentication/api/access_key.py | 32 +++- apps/authentication/api/confirm.py | 8 +- apps/authentication/api/dingtalk.py | 2 +- apps/authentication/api/feishu.py | 2 +- apps/authentication/api/wecom.py | 2 +- apps/authentication/backends/drf.py | 136 +++---------- apps/authentication/confirm/base.py | 3 +- apps/authentication/confirm/password.py | 11 ++ apps/authentication/const.py | 7 +- .../migrations/0023_auto_20231010_1101.py | 57 ++++++ apps/authentication/models/access_key.py | 12 +- apps/authentication/models/private_token.py | 2 + apps/authentication/serializers/token.py | 11 +- apps/authentication/tests/__init__.py | 0 apps/authentication/tests/access_key.py | 34 ++++ apps/authentication/views/dingtalk.py | 2 +- apps/authentication/views/feishu.py | 2 +- apps/authentication/views/wecom.py | 2 +- apps/common/api/mixin.py | 17 +- apps/common/permissions.py | 4 +- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 179 +++++++++--------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 176 ++++++++--------- .../0047_user_date_api_key_last_used.py | 18 ++ apps/users/models/user.py | 7 +- apps/users/serializers/user.py | 11 +- 28 files changed, 417 insertions(+), 330 deletions(-) create mode 100644 apps/authentication/migrations/0023_auto_20231010_1101.py create mode 100644 apps/authentication/tests/__init__.py create mode 100644 apps/authentication/tests/access_key.py create mode 100644 apps/users/migrations/0047_user_date_api_key_last_used.py diff --git a/apps/audits/migrations/0023_auto_20230906_1322.py b/apps/audits/migrations/0023_auto_20230906_1322.py index 98447b7b3..286ff0df5 100644 --- a/apps/audits/migrations/0023_auto_20230906_1322.py +++ b/apps/audits/migrations/0023_auto_20230906_1322.py @@ -19,7 +19,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='operatelog', name='action', - field=models.CharField(choices=[('view', 'View'), ('update', 'Update'), ('delete', 'Delete'), ('create', 'Create'), ('download', 'Download'), ('connect', 'Connect'), ('login', 'Login'), ('change_password', 'Change password')], max_length=16, verbose_name='Action'), + field=models.CharField(choices=[('view', 'View'), ('update', 'Update'), ('delete', 'Delete'), ('create', 'Create'), ('download', 'Download'), ('connect', 'Connect'), ('login', 'Login'), ('change_password', 'Change password'), ('reject', 'Reject'), ('accept', 'Accept'), ('review', 'Review')], max_length=16, verbose_name='Action'), ), migrations.AlterField( model_name='userloginlog', diff --git a/apps/authentication/api/access_key.py b/apps/authentication/api/access_key.py index 9253f449d..86551953a 100644 --- a/apps/authentication/api/access_key.py +++ b/apps/authentication/api/access_key.py @@ -1,20 +1,44 @@ # -*- coding: utf-8 -*- # -from rest_framework.viewsets import ModelViewSet +from django.utils.translation import gettext as _ +from rest_framework import serializers +from rest_framework.response import Response +from common.api import JMSModelViewSet +from common.permissions import UserConfirmation from rbac.permissions import RBACPermission +from ..const import ConfirmType from ..serializers import AccessKeySerializer -class AccessKeyViewSet(ModelViewSet): +class AccessKeyViewSet(JMSModelViewSet): serializer_class = AccessKeySerializer - search_fields = ['^id', '^secret'] + search_fields = ['^id'] permission_classes = [RBACPermission] def get_queryset(self): return self.request.user.access_keys.all() + def get_permissions(self): + if self.is_swagger_request(): + return super().get_permissions() + + if self.action == 'create': + self.permission_classes = [ + RBACPermission, UserConfirmation.require(ConfirmType.PASSWORD) + ] + return super().get_permissions() + def perform_create(self, serializer): user = self.request.user - user.create_access_key() + if user.access_keys.count() >= 10: + raise serializers.ValidationError(_('Access keys can be created at most 10')) + key = user.create_access_key() + return key + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + key = self.perform_create(serializer) + return Response({'secret': key.secret, 'id': key.id}, status=201) diff --git a/apps/authentication/api/confirm.py b/apps/authentication/api/confirm.py index 3c0e670f0..92792d72d 100644 --- a/apps/authentication/api/confirm.py +++ b/apps/authentication/api/confirm.py @@ -13,7 +13,7 @@ from ..serializers import ConfirmSerializer class ConfirmBindORUNBindOAuth(RetrieveAPIView): - permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.ReLogin),) + permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.RELOGIN),) def retrieve(self, request, *args, **kwargs): return Response('ok') @@ -24,7 +24,7 @@ class ConfirmApi(RetrieveAPIView, CreateAPIView): serializer_class = ConfirmSerializer def get_confirm_backend(self, confirm_type): - backend_classes = ConfirmType.get_can_confirm_backend_classes(confirm_type) + backend_classes = ConfirmType.get_prop_backends(confirm_type) if not backend_classes: return for backend_cls in backend_classes: @@ -34,7 +34,7 @@ class ConfirmApi(RetrieveAPIView, CreateAPIView): return backend def retrieve(self, request, *args, **kwargs): - confirm_type = request.query_params.get('confirm_type') + confirm_type = request.query_params.get('confirm_type', 'password') backend = self.get_confirm_backend(confirm_type) if backend is None: msg = _('This action require verify your MFA') @@ -51,7 +51,7 @@ class ConfirmApi(RetrieveAPIView, CreateAPIView): serializer.is_valid(raise_exception=True) validated_data = serializer.validated_data - confirm_type = validated_data.get('confirm_type') + confirm_type = validated_data.get('confirm_type', 'password') mfa_type = validated_data.get('mfa_type') secret_key = validated_data.get('secret_key') diff --git a/apps/authentication/api/dingtalk.py b/apps/authentication/api/dingtalk.py index c8d9f7ce0..66f5a1d8c 100644 --- a/apps/authentication/api/dingtalk.py +++ b/apps/authentication/api/dingtalk.py @@ -27,7 +27,7 @@ class DingTalkQRUnBindBase(APIView): class DingTalkQRUnBindForUserApi(RoleUserMixin, DingTalkQRUnBindBase): - permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.ReLogin),) + permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.RELOGIN),) class DingTalkQRUnBindForAdminApi(RoleAdminMixin, DingTalkQRUnBindBase): diff --git a/apps/authentication/api/feishu.py b/apps/authentication/api/feishu.py index 148b99e51..336552087 100644 --- a/apps/authentication/api/feishu.py +++ b/apps/authentication/api/feishu.py @@ -27,7 +27,7 @@ class FeiShuQRUnBindBase(APIView): class FeiShuQRUnBindForUserApi(RoleUserMixin, FeiShuQRUnBindBase): - permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.ReLogin),) + permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.RELOGIN),) class FeiShuQRUnBindForAdminApi(RoleAdminMixin, FeiShuQRUnBindBase): diff --git a/apps/authentication/api/wecom.py b/apps/authentication/api/wecom.py index e704d3d4b..82476157b 100644 --- a/apps/authentication/api/wecom.py +++ b/apps/authentication/api/wecom.py @@ -27,7 +27,7 @@ class WeComQRUnBindBase(APIView): class WeComQRUnBindForUserApi(RoleUserMixin, WeComQRUnBindBase): - permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.ReLogin),) + permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.RELOGIN),) class WeComQRUnBindForAdminApi(RoleAdminMixin, WeComQRUnBindBase): diff --git a/apps/authentication/backends/drf.py b/apps/authentication/backends/drf.py index 8d7d60e99..4ba879cc2 100644 --- a/apps/authentication/backends/drf.py +++ b/apps/authentication/backends/drf.py @@ -1,119 +1,33 @@ # -*- coding: utf-8 -*- # -import time -import uuid - from django.contrib.auth import get_user_model from django.core.cache import cache +from django.utils import timezone from django.utils.translation import gettext as _ -from rest_framework import HTTP_HEADER_ENCODING from rest_framework import authentication, exceptions -from six import text_type from common.auth import signature -from common.utils import get_object_or_none, make_signature, http_to_unixtime -from .base import JMSBaseAuthBackend +from common.utils import get_object_or_none from ..models import AccessKey, PrivateToken -UserModel = get_user_model() + +def date_more_than(d, seconds): + return d is None or (timezone.now() - d).seconds > seconds -def get_request_date_header(request): - date = request.META.get('HTTP_DATE', b'') - if isinstance(date, text_type): - # Work around django test client oddness - date = date.encode(HTTP_HEADER_ENCODING) - return date +def after_authenticate_update_date(user, token=None): + if date_more_than(user.date_api_key_last_used, 60): + user.date_api_key_last_used = timezone.now() + user.save(update_fields=['date_api_key_last_used']) - -class AccessKeyAuthentication(authentication.BaseAuthentication): - """App使用Access key进行签名认证, 目前签名算法比较简单, - app注册或者手动建立后,会生成 access_key_id 和 access_key_secret, - 然后使用 如下算法生成签名: - Signature = md5(access_key_secret + '\n' + Date) - example: Signature = md5('d32d2b8b-9a10-4b8d-85bb-1a66976f6fdc' + '\n' + - 'Thu, 12 Jan 2017 08:19:41 GMT') - 请求时设置请求header - header['Authorization'] = 'Sign access_key_id:Signature' 如: - header['Authorization'] = - 'Sign d32d2b8b-9a10-4b8d-85bb-1a66976f6fdc:OKOlmdxgYPZ9+SddnUUDbQ==' - - 验证时根据相同算法进行验证, 取到access_key_id对应的access_key_id, 从request - headers取到Date, 然后进行md5, 判断得到的结果是否相同, 如果是认证通过, 否则 认证 - 失败 - """ - keyword = 'Sign' - - def authenticate(self, request): - auth = authentication.get_authorization_header(request).split() - if not auth or auth[0].lower() != self.keyword.lower().encode(): - return None - - if len(auth) == 1: - msg = _('Invalid signature header. No credentials provided.') - raise exceptions.AuthenticationFailed(msg) - elif len(auth) > 2: - msg = _('Invalid signature header. Signature ' - 'string should not contain spaces.') - raise exceptions.AuthenticationFailed(msg) - - try: - sign = auth[1].decode().split(':') - if len(sign) != 2: - msg = _('Invalid signature header. ' - 'Format like AccessKeyId:Signature') - raise exceptions.AuthenticationFailed(msg) - except UnicodeError: - msg = _('Invalid signature header. ' - 'Signature string should not contain invalid characters.') - raise exceptions.AuthenticationFailed(msg) - - access_key_id = sign[0] - try: - uuid.UUID(access_key_id) - except ValueError: - raise exceptions.AuthenticationFailed('Access key id invalid') - request_signature = sign[1] - - return self.authenticate_credentials( - request, access_key_id, request_signature - ) - - @staticmethod - def authenticate_credentials(request, access_key_id, request_signature): - access_key = get_object_or_none(AccessKey, id=access_key_id) - request_date = get_request_date_header(request) - if access_key is None or not access_key.user: - raise exceptions.AuthenticationFailed(_('Invalid signature.')) - access_key_secret = access_key.secret - - try: - request_unix_time = http_to_unixtime(request_date) - except ValueError: - raise exceptions.AuthenticationFailed( - _('HTTP header: Date not provide ' - 'or not %a, %d %b %Y %H:%M:%S GMT')) - - if int(time.time()) - request_unix_time > 15 * 60: - raise exceptions.AuthenticationFailed( - _('Expired, more than 15 minutes')) - - signature = make_signature(access_key_secret, request_date) - if not signature == request_signature: - raise exceptions.AuthenticationFailed(_('Invalid signature.')) - - if not access_key.user.is_active: - raise exceptions.AuthenticationFailed(_('User disabled.')) - return access_key.user, None - - def authenticate_header(self, request): - return 'Sign access_key_id:Signature' + if token and hasattr(token, 'date_last_used') and date_more_than(token.date_last_used, 60): + token.date_last_used = timezone.now() + token.save(update_fields=['date_last_used']) class AccessTokenAuthentication(authentication.BaseAuthentication): keyword = 'Bearer' - # expiration = settings.TOKEN_EXPIRATION or 3600 model = get_user_model() def authenticate(self, request): @@ -125,19 +39,20 @@ class AccessTokenAuthentication(authentication.BaseAuthentication): msg = _('Invalid token header. No credentials provided.') raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: - msg = _('Invalid token header. Sign string ' - 'should not contain spaces.') + msg = _('Invalid token header. Sign string should not contain spaces.') raise exceptions.AuthenticationFailed(msg) try: token = auth[1].decode() except UnicodeError: - msg = _('Invalid token header. Sign string ' - 'should not contain invalid characters.') + msg = _('Invalid token header. Sign string should not contain invalid characters.') raise exceptions.AuthenticationFailed(msg) - return self.authenticate_credentials(token) + user, header = self.authenticate_credentials(token) + after_authenticate_update_date(user) + return user, header - def authenticate_credentials(self, token): + @staticmethod + def authenticate_credentials(token): model = get_user_model() user_id = cache.get(token) user = get_object_or_none(model, id=user_id) @@ -151,15 +66,23 @@ class AccessTokenAuthentication(authentication.BaseAuthentication): return self.keyword -class PrivateTokenAuthentication(JMSBaseAuthBackend, authentication.TokenAuthentication): +class PrivateTokenAuthentication(authentication.TokenAuthentication): model = PrivateToken + def authenticate(self, request): + user_token = super().authenticate(request) + if not user_token: + return + user, token = user_token + after_authenticate_update_date(user, token) + return user, token + class SessionAuthentication(authentication.SessionAuthentication): def authenticate(self, request): """ Returns a `User` if the request session currently has a logged in user. - Otherwise returns `None`. + Otherwise, returns `None`. """ # Get the session-based user from the underlying HttpRequest object @@ -195,6 +118,7 @@ class SignatureAuthentication(signature.SignatureAuthentication): if not key.is_active: return None, None user, secret = key.user, str(key.secret) + after_authenticate_update_date(user, key) return user, secret except (AccessKey.DoesNotExist, exceptions.ValidationError): return None, None diff --git a/apps/authentication/confirm/base.py b/apps/authentication/confirm/base.py index 63258abce..5926da3e1 100644 --- a/apps/authentication/confirm/base.py +++ b/apps/authentication/confirm/base.py @@ -2,7 +2,6 @@ import abc class BaseConfirm(abc.ABC): - def __init__(self, user, request): self.user = user self.request = request @@ -23,7 +22,7 @@ class BaseConfirm(abc.ABC): @property def content(self): - return '' + return [] @abc.abstractmethod def authenticate(self, secret_key, mfa_type) -> tuple: diff --git a/apps/authentication/confirm/password.py b/apps/authentication/confirm/password.py index dc49576a3..e497bc261 100644 --- a/apps/authentication/confirm/password.py +++ b/apps/authentication/confirm/password.py @@ -15,3 +15,14 @@ class ConfirmPassword(BaseConfirm): ok = authenticate(self.request, username=self.user.username, password=secret_key) msg = '' if ok else _('Authentication failed password incorrect') return ok, msg + + @property + def content(self): + return [ + { + 'name': 'password', + 'display_name': _('Password'), + 'disabled': False, + 'placeholder': _('Password'), + } + ] diff --git a/apps/authentication/const.py b/apps/authentication/const.py index d7e0690db..1e06a4d35 100644 --- a/apps/authentication/const.py +++ b/apps/authentication/const.py @@ -11,7 +11,7 @@ CONFIRM_BACKEND_MAP = {backend.name: backend for backend in CONFIRM_BACKENDS} class ConfirmType(TextChoices): - ReLogin = ConfirmReLogin.name, ConfirmReLogin.display_name + RELOGIN = ConfirmReLogin.name, ConfirmReLogin.display_name PASSWORD = ConfirmPassword.name, ConfirmPassword.display_name MFA = ConfirmMFA.name, ConfirmMFA.display_name @@ -23,10 +23,11 @@ class ConfirmType(TextChoices): return types @classmethod - def get_can_confirm_backend_classes(cls, confirm_type): + def get_prop_backends(cls, confirm_type): types = cls.get_can_confirm_types(confirm_type) backend_classes = [ - CONFIRM_BACKEND_MAP[tp] for tp in types if tp in CONFIRM_BACKEND_MAP + CONFIRM_BACKEND_MAP[tp] + for tp in types if tp in CONFIRM_BACKEND_MAP ] return backend_classes diff --git a/apps/authentication/migrations/0023_auto_20231010_1101.py b/apps/authentication/migrations/0023_auto_20231010_1101.py new file mode 100644 index 000000000..81920dfd7 --- /dev/null +++ b/apps/authentication/migrations/0023_auto_20231010_1101.py @@ -0,0 +1,57 @@ +# Generated by Django 4.1.10 on 2023-10-10 02:47 + +import uuid +import authentication.models.access_key +from django.db import migrations, models + + +def migrate_access_key_secret(apps, schema_editor): + access_key_model = apps.get_model('authentication', 'AccessKey') + db_alias = schema_editor.connection.alias + + batch_size = 100 + count = 0 + + while True: + access_keys = access_key_model.objects.using(db_alias).all()[count:count + batch_size] + if not access_keys: + break + + count += len(access_keys) + access_keys_updated = [] + for access_key in access_keys: + s = access_key.secret + if len(s) != 32 or not s.islower(): + continue + try: + access_key.secret = '%s-%s-%s-%s-%s' % (s[:8], s[8:12], s[12:16], s[16:20], s[20:]) + access_keys_updated.append(access_key) + except (ValueError, IndexError): + pass + access_key_model.objects.bulk_update(access_keys_updated, fields=['secret']) + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0022_passkey'), + ] + + operations = [ + migrations.AddField( + model_name='accesskey', + name='date_last_used', + field=models.DateTimeField(blank=True, null=True, verbose_name='Date last used'), + ), + migrations.AddField( + model_name='privatetoken', + name='date_last_used', + field=models.DateTimeField(blank=True, null=True, verbose_name='Date last used'), + ), + migrations.AlterField( + model_name='accesskey', + name='secret', + field=models.CharField(default=authentication.models.access_key.default_secret, max_length=36, verbose_name='AccessKeySecret'), + ), + migrations.RunPython(migrate_access_key_secret), + ] diff --git a/apps/authentication/models/access_key.py b/apps/authentication/models/access_key.py index 77fa67c74..5d9571569 100644 --- a/apps/authentication/models/access_key.py +++ b/apps/authentication/models/access_key.py @@ -5,16 +5,20 @@ from django.db import models from django.utils.translation import gettext_lazy as _ import common.db.models +from common.utils.random import random_string + + +def default_secret(): + return random_string(36) class AccessKey(models.Model): - id = models.UUIDField(verbose_name='AccessKeyID', primary_key=True, - default=uuid.uuid4, editable=False) - secret = models.UUIDField(verbose_name='AccessKeySecret', - default=uuid.uuid4, editable=False) + id = models.UUIDField(verbose_name='AccessKeyID', primary_key=True, default=uuid.uuid4, editable=False) + secret = models.CharField(verbose_name='AccessKeySecret', default=default_secret, max_length=36) user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='User', on_delete=common.db.models.CASCADE_SIGNAL_SKIP, related_name='access_keys') is_active = models.BooleanField(default=True, verbose_name=_('Active')) + date_last_used = models.DateTimeField(null=True, blank=True, verbose_name=_('Date last used')) date_created = models.DateTimeField(auto_now_add=True) def get_id(self): diff --git a/apps/authentication/models/private_token.py b/apps/authentication/models/private_token.py index bb5f1da87..56669f018 100644 --- a/apps/authentication/models/private_token.py +++ b/apps/authentication/models/private_token.py @@ -1,9 +1,11 @@ +from django.db import models from django.utils.translation import gettext_lazy as _ from rest_framework.authtoken.models import Token class PrivateToken(Token): """Inherit from auth token, otherwise migration is boring""" + date_last_used = models.DateTimeField(null=True, blank=True, verbose_name=_('Date last used')) class Meta: verbose_name = _('Private Token') diff --git a/apps/authentication/serializers/token.py b/apps/authentication/serializers/token.py index d1e87c0c0..9101bc9c0 100644 --- a/apps/authentication/serializers/token.py +++ b/apps/authentication/serializers/token.py @@ -10,7 +10,7 @@ from users.serializers import UserProfileSerializer from ..models import AccessKey, TempToken __all__ = [ - 'AccessKeySerializer', 'BearerTokenSerializer', + 'AccessKeySerializer', 'BearerTokenSerializer', 'SSOTokenSerializer', 'TempTokenSerializer', ] @@ -18,8 +18,8 @@ __all__ = [ class AccessKeySerializer(serializers.ModelSerializer): class Meta: model = AccessKey - fields = ['id', 'secret', 'is_active', 'date_created'] - read_only_fields = ['id', 'secret', 'date_created'] + fields = ['id', 'is_active', 'date_created', 'date_last_used'] + read_only_fields = ['id', 'date_created', 'date_last_used'] class BearerTokenSerializer(serializers.Serializer): @@ -37,7 +37,8 @@ class BearerTokenSerializer(serializers.Serializer): def get_keyword(obj): return 'Bearer' - def update_last_login(self, user): + @staticmethod + def update_last_login(user): user.last_login = timezone.now() user.save(update_fields=['last_login']) @@ -96,7 +97,7 @@ class TempTokenSerializer(serializers.ModelSerializer): username = request.user.username kwargs = { 'username': username, 'secret': secret, - 'date_expired': timezone.now() + timezone.timedelta(seconds=5*60), + 'date_expired': timezone.now() + timezone.timedelta(seconds=5 * 60), } token = TempToken(**kwargs) token.save() diff --git a/apps/authentication/tests/__init__.py b/apps/authentication/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/authentication/tests/access_key.py b/apps/authentication/tests/access_key.py new file mode 100644 index 000000000..d0ec2842c --- /dev/null +++ b/apps/authentication/tests/access_key.py @@ -0,0 +1,34 @@ +# Python 示例 +# pip install requests drf-httpsig +import datetime +import json + +import requests +from httpsig.requests_auth import HTTPSignatureAuth + + +def get_auth(KeyID, SecretID): + signature_headers = ['(request-target)', 'accept', 'date'] + auth = HTTPSignatureAuth(key_id=KeyID, secret=SecretID, algorithm='hmac-sha256', headers=signature_headers) + return auth + + +def get_user_info(jms_url, auth): + url = jms_url + '/api/v1/users/users/?limit=1' + gmt_form = '%a, %d %b %Y %H:%M:%S GMT' + headers = { + 'Accept': 'application/json', + 'X-JMS-ORG': '00000000-0000-0000-0000-000000000002', + 'Date': datetime.datetime.utcnow().strftime(gmt_form) + } + + response = requests.get(url, auth=auth, headers=headers) + print(json.loads(response.text)) + + +if __name__ == '__main__': + jms_url = 'http://localhost:8080' + KeyID = '0753098d-810c-45fb-b42c-b27077147933' + SecretID = 'a58d2530-d7ee-4390-a204-3492e44dde84' + auth = get_auth(KeyID, SecretID) + get_user_info(jms_url, auth) diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index 819f1d055..c23827ca1 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -99,7 +99,7 @@ class DingTalkOAuthMixin(DingTalkBaseMixin, View): class DingTalkQRBindView(DingTalkQRMixin, View): - permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.ReLogin)) + permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.RELOGIN)) def get(self, request: HttpRequest): user = request.user diff --git a/apps/authentication/views/feishu.py b/apps/authentication/views/feishu.py index 0f62ef75d..ec483cdd4 100644 --- a/apps/authentication/views/feishu.py +++ b/apps/authentication/views/feishu.py @@ -69,7 +69,7 @@ class FeiShuQRMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashMe class FeiShuQRBindView(FeiShuQRMixin, View): - permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.ReLogin)) + permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.RELOGIN)) def get(self, request: HttpRequest): redirect_url = request.GET.get('redirect_url') diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index 238d0b3e6..7bb56202c 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -100,7 +100,7 @@ class WeComOAuthMixin(WeComBaseMixin, View): class WeComQRBindView(WeComQRMixin, View): - permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.ReLogin)) + permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.RELOGIN)) def get(self, request: HttpRequest): user = request.user diff --git a/apps/common/api/mixin.py b/apps/common/api/mixin.py index 4795673d7..d93459104 100644 --- a/apps/common/api/mixin.py +++ b/apps/common/api/mixin.py @@ -13,7 +13,7 @@ from common.drf.filters import ( IDSpmFilterBackend, CustomFilterBackend, IDInFilterBackend, IDNotFilterBackend, NotOrRelFilterBackend ) -from common.utils import get_logger +from common.utils import get_logger, lazyproperty from .action import RenderToJsonMixin from .serializer import SerializerMixin @@ -150,9 +150,9 @@ class OrderingFielderFieldsMixin: ordering_fields = None extra_ordering_fields = [] - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.ordering_fields = self._get_ordering_fields() + @lazyproperty + def ordering_fields(self): + return self._get_ordering_fields() def _get_ordering_fields(self): if isinstance(self.__class__.ordering_fields, (list, tuple)): @@ -179,7 +179,10 @@ class OrderingFielderFieldsMixin: model = self.queryset.model else: queryset = self.get_queryset() - model = queryset.model + if isinstance(queryset, list): + model = None + else: + model = queryset.model if not model: return [] @@ -201,4 +204,6 @@ class CommonApiMixin( SerializerMixin, ExtraFilterFieldsMixin, OrderingFielderFieldsMixin, QuerySetMixin, RenderToJsonMixin, PaginatedResponseMixin ): - pass + def is_swagger_request(self): + return getattr(self, 'swagger_fake_view', False) or \ + getattr(self, 'raw_action', '') == 'metadata' diff --git a/apps/common/permissions.py b/apps/common/permissions.py index 37c51de47..742759e1d 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -59,7 +59,7 @@ class WithBootstrapToken(permissions.BasePermission): class UserConfirmation(permissions.BasePermission): ttl = 60 * 5 min_level = 1 - confirm_type = ConfirmType.ReLogin + confirm_type = ConfirmType.RELOGIN def has_permission(self, request, view): if not settings.SECURITY_VIEW_AUTH_NEED_MFA: @@ -82,7 +82,7 @@ class UserConfirmation(permissions.BasePermission): return ttl @classmethod - def require(cls, confirm_type=ConfirmType.ReLogin, ttl=60 * 5): + def require(cls, confirm_type=ConfirmType.RELOGIN, ttl=60 * 5): min_level = ConfirmType.values.index(confirm_type) + 1 name = 'UserConfirmationLevel{}TTL{}'.format(min_level, ttl) return type(name, (cls,), {'min_level': min_level, 'ttl': ttl, 'confirm_type': confirm_type}) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index c0640b1af..c1648acb8 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56db214d34b91e9f6d712cef124890264144b35b99e50d6833b4af0e935778b0 -size 162655 +oid sha256:38bd8a6653f3f4dc63552b1c86379f82067f9f9daac227bacb3af3f9f62134f9 +size 161704 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 99759dcae..bcd757537 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: 2023-10-09 18:32+0800\n" +"POT-Creation-Date: 2023-10-10 11:13+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -25,10 +25,11 @@ msgstr "パラメータ 'action' は [{}] でなければなりません。" #: accounts/const/account.py:6 #: accounts/serializers/automations/change_secret.py:32 #: assets/models/_user.py:24 audits/signal_handlers/login_log.py:37 -#: authentication/confirm/password.py:9 authentication/forms.py:32 +#: authentication/confirm/password.py:9 authentication/confirm/password.py:24 +#: authentication/confirm/password.py:26 authentication/forms.py:32 #: authentication/templates/authentication/login.html:324 #: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47 -#: users/forms/profile.py:22 users/serializers/user.py:105 +#: users/forms/profile.py:22 users/serializers/user.py:104 #: users/templates/users/_msg_user_created.html:13 #: users/templates/users/user_password_verify.html:18 #: xpack/plugins/cloud/serializers/account_attrs.py:28 @@ -40,7 +41,7 @@ msgstr "パスワード" msgid "SSH key" msgstr "SSH キー" -#: accounts/const/account.py:8 authentication/models/access_key.py:33 +#: accounts/const/account.py:8 authentication/models/access_key.py:37 msgid "Access key" msgstr "アクセスキー" @@ -251,7 +252,7 @@ msgid "Version" msgstr "バージョン" #: accounts/models/account.py:56 accounts/serializers/account/account.py:211 -#: users/models/user.py:840 +#: users/models/user.py:837 msgid "Source" msgstr "ソース" @@ -552,7 +553,7 @@ msgstr "特権アカウント" #: assets/models/label.py:22 #: authentication/serializers/connect_token_secret.py:114 #: terminal/models/applet/applet.py:40 -#: terminal/models/component/endpoint.py:105 users/serializers/user.py:170 +#: terminal/models/component/endpoint.py:105 users/serializers/user.py:167 msgid "Is active" msgstr "アクティブです。" @@ -711,7 +712,7 @@ msgstr "編集済み" #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 -#: authentication/api/connection_token.py:381 ops/models/base.py:17 +#: authentication/api/connection_token.py:386 ops/models/base.py:17 #: ops/models/job.py:139 ops/serializers/job.py:21 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" @@ -751,8 +752,8 @@ msgstr "ID" #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:990 -#: users/models/user.py:1026 users/serializers/group.py:18 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:987 +#: users/models/user.py:1023 users/serializers/group.py:18 msgid "User" msgstr "ユーザー" @@ -1006,7 +1007,7 @@ msgstr "1-100、低い値は最初に一致します" msgid "Reviewers" msgstr "レビュー担当者" -#: acls/models/base.py:43 authentication/models/access_key.py:17 +#: acls/models/base.py:43 authentication/models/access_key.py:20 #: authentication/models/connection_token.py:53 #: authentication/templates/authentication/_access_key_modal.html:32 #: perms/models/asset_permission.py:76 terminal/models/session/sharing.py:29 @@ -1312,7 +1313,7 @@ msgid "Disabled" msgstr "無効" #: assets/const/base.py:34 settings/serializers/basic.py:6 -#: users/serializers/preference/koko.py:15 +#: users/serializers/preference/koko.py:19 #: users/serializers/preference/lina.py:39 #: users/serializers/preference/luna.py:60 msgid "Basic" @@ -1527,12 +1528,12 @@ msgstr "SSHパブリックキー" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:261 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:227 users/models/user.py:1027 +#: ops/models/job.py:227 users/models/user.py:1024 msgid "Date created" msgstr "作成された日付" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:849 +#: common/db/models.py:35 users/models/user.py:846 msgid "Date updated" msgstr "更新日" @@ -1782,7 +1783,7 @@ msgstr "デフォルト" msgid "Default asset group" msgstr "デフォルトアセットグループ" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1012 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1009 msgid "System" msgstr "システム" @@ -2493,7 +2494,7 @@ msgstr "認証トークン" #: audits/signal_handlers/login_log.py:40 authentication/notifications.py:73 #: authentication/views/login.py:77 authentication/views/wecom.py:159 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:745 users/models/user.py:850 +#: users/models/user.py:745 users/models/user.py:847 msgid "WeCom" msgstr "企業微信" @@ -2501,14 +2502,14 @@ msgstr "企業微信" #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 #: settings/serializers/auth/feishu.py:13 users/models/user.py:747 -#: users/models/user.py:852 +#: users/models/user.py:849 msgid "FeiShu" msgstr "本を飛ばす" #: audits/signal_handlers/login_log.py:42 authentication/views/dingtalk.py:159 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 #: settings/serializers/auth/dingtalk.py:10 users/models/user.py:746 -#: users/models/user.py:851 +#: users/models/user.py:848 msgid "DingTalk" msgstr "DingTalk" @@ -2540,23 +2541,23 @@ msgstr "" "再使用可能な接続トークンの使用は許可されていません。グローバル設定は有効に" "なっていません" -#: authentication/api/connection_token.py:352 +#: authentication/api/connection_token.py:357 msgid "Anonymous account is not supported for this asset" msgstr "匿名アカウントはこのプロパティではサポートされていません" -#: authentication/api/connection_token.py:371 +#: authentication/api/connection_token.py:376 msgid "Account not found" msgstr "アカウントが見つかりません" -#: authentication/api/connection_token.py:374 +#: authentication/api/connection_token.py:379 msgid "Permission expired" msgstr "承認の有効期限が切れています" -#: authentication/api/connection_token.py:401 +#: authentication/api/connection_token.py:406 msgid "ACL action is reject: {}({})" msgstr "ACL アクションは拒否です: {}({})" -#: authentication/api/connection_token.py:405 +#: authentication/api/connection_token.py:410 msgid "ACL action is review" msgstr "ACL アクションはレビューです" @@ -2599,56 +2600,21 @@ msgstr "認証" msgid "User invalid, disabled or expired" msgstr "ユーザーが無効、無効、または期限切れです" -#: authentication/backends/drf.py:54 -msgid "Invalid signature header. No credentials provided." -msgstr "署名ヘッダーが無効です。資格情報は提供されていません。" - -#: authentication/backends/drf.py:57 -msgid "Invalid signature header. Signature string should not contain spaces." -msgstr "署名ヘッダーが無効です。署名文字列にはスペースを含まないでください。" - -#: authentication/backends/drf.py:64 -msgid "Invalid signature header. Format like AccessKeyId:Signature" -msgstr "署名ヘッダーが無効です。AccessKeyIdのような形式: Signature" - -#: authentication/backends/drf.py:68 -msgid "" -"Invalid signature header. Signature string should not contain invalid " -"characters." -msgstr "" -"署名ヘッダーが無効です。署名文字列に無効な文字を含めることはできません。" - -#: authentication/backends/drf.py:88 authentication/backends/drf.py:104 -msgid "Invalid signature." -msgstr "署名が無効です。" - -#: authentication/backends/drf.py:95 -msgid "HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT" -msgstr "HTTP header: Date not provide or not" - -#: authentication/backends/drf.py:100 -msgid "Expired, more than 15 minutes" -msgstr "期限切れ、15分以上" - -#: authentication/backends/drf.py:107 -msgid "User disabled." -msgstr "ユーザーが無効になりました。" - -#: authentication/backends/drf.py:125 +#: authentication/backends/drf.py:39 msgid "Invalid token header. No credentials provided." msgstr "無効なトークンヘッダー。資格情報は提供されていません。" -#: authentication/backends/drf.py:128 +#: authentication/backends/drf.py:42 msgid "Invalid token header. Sign string should not contain spaces." msgstr "無効なトークンヘッダー。記号文字列にはスペースを含めないでください。" -#: authentication/backends/drf.py:135 +#: authentication/backends/drf.py:48 msgid "" "Invalid token header. Sign string should not contain invalid characters." msgstr "" "無効なトークンヘッダー。署名文字列に無効な文字を含めることはできません。" -#: authentication/backends/drf.py:146 +#: authentication/backends/drf.py:61 msgid "Invalid token or cache refreshed." msgstr "無効なトークンまたはキャッシュの更新。" @@ -2665,6 +2631,8 @@ msgid "Added on" msgstr "に追加" #: authentication/backends/passkey/models.py:14 +#: authentication/models/access_key.py:21 +#: authentication/models/private_token.py:8 msgid "Date last used" msgstr "最後に使用した日付" @@ -3041,7 +3009,7 @@ msgstr "スーパー接続トークンのシークレットを表示できます msgid "Super connection token" msgstr "スーパー接続トークン" -#: authentication/models/private_token.py:9 +#: authentication/models/private_token.py:11 msgid "Private Token" msgstr "プライベートトークン" @@ -3099,7 +3067,7 @@ msgstr "アクション" #: authentication/serializers/connection_token.py:42 #: perms/serializers/permission.py:38 perms/serializers/permission.py:57 -#: users/serializers/user.py:97 users/serializers/user.py:174 +#: users/serializers/user.py:97 users/serializers/user.py:171 msgid "Is expired" msgstr "期限切れです" @@ -3118,9 +3086,9 @@ msgstr "メール" msgid "The {} cannot be empty" msgstr "{} 空にしてはならない" -#: authentication/serializers/token.py:79 perms/serializers/permission.py:37 +#: authentication/serializers/token.py:80 perms/serializers/permission.py:37 #: perms/serializers/permission.py:58 users/serializers/user.py:98 -#: users/serializers/user.py:171 +#: users/serializers/user.py:168 msgid "Is valid" msgstr "有効です" @@ -6260,7 +6228,7 @@ msgstr "一括作成非サポート" msgid "Storage is invalid" msgstr "ストレージが無効です" -#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:86 +#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:88 msgid "Community edition" msgstr "コミュニティ版" @@ -7394,7 +7362,7 @@ msgstr "ユーザー設定" msgid "Force enable" msgstr "強制有効" -#: users/models/user.py:804 users/serializers/user.py:172 +#: users/models/user.py:804 users/serializers/user.py:169 msgid "Is service account" msgstr "サービスアカウントです" @@ -7406,7 +7374,7 @@ msgstr "アバター" msgid "Wechat" msgstr "微信" -#: users/models/user.py:812 users/serializers/user.py:109 +#: users/models/user.py:812 users/serializers/user.py:106 msgid "Phone" msgstr "電話" @@ -7420,43 +7388,47 @@ msgid "Private key" msgstr "ssh秘密鍵" #: users/models/user.py:830 users/serializers/profile.py:125 -#: users/serializers/user.py:169 +#: users/serializers/user.py:166 msgid "Is first login" msgstr "最初のログインです" -#: users/models/user.py:844 +#: users/models/user.py:840 msgid "Date password last updated" msgstr "最終更新日パスワード" -#: users/models/user.py:847 +#: users/models/user.py:843 msgid "Need update password" msgstr "更新パスワードが必要" -#: users/models/user.py:971 +#: users/models/user.py:845 +msgid "Date api key used" +msgstr "Api key 最後に使用した日付" + +#: users/models/user.py:968 msgid "Can not delete admin user" msgstr "管理者ユーザーを削除できませんでした" -#: users/models/user.py:997 +#: users/models/user.py:994 msgid "Can invite user" msgstr "ユーザーを招待できます" -#: users/models/user.py:998 +#: users/models/user.py:995 msgid "Can remove user" msgstr "ユーザーを削除できます" -#: users/models/user.py:999 +#: users/models/user.py:996 msgid "Can match user" msgstr "ユーザーに一致できます" -#: users/models/user.py:1008 +#: users/models/user.py:1005 msgid "Administrator" msgstr "管理者" -#: users/models/user.py:1011 +#: users/models/user.py:1008 msgid "Administrator is the super user of system" msgstr "管理者はシステムのスーパーユーザーです" -#: users/models/user.py:1036 +#: users/models/user.py:1033 msgid "User password history" msgstr "ユーザーパスワード履歴" @@ -7495,6 +7467,12 @@ msgstr "MFAのリセット" msgid "File name conflict resolution" msgstr "ファイル名競合ソリューション" +#: users/serializers/preference/koko.py:14 +#, fuzzy +#| msgid "Terminal setting" +msgid "Terminal theme name" +msgstr "ターミナル設定" + #: users/serializers/preference/lina.py:13 msgid "New file encryption password" msgstr "新しいファイルの暗号化パスワード" @@ -7583,7 +7561,7 @@ msgstr "MFAフォース有効化" msgid "Login blocked" msgstr "ログインがロックされました" -#: users/serializers/user.py:99 users/serializers/user.py:178 +#: users/serializers/user.py:99 users/serializers/user.py:175 msgid "Is OTP bound" msgstr "仮想MFAがバインドされているか" @@ -7591,27 +7569,27 @@ msgstr "仮想MFAがバインドされているか" msgid "Can public key authentication" msgstr "公開鍵認証が可能" -#: users/serializers/user.py:173 +#: users/serializers/user.py:170 msgid "Is org admin" msgstr "組織管理者です" -#: users/serializers/user.py:175 +#: users/serializers/user.py:172 msgid "Avatar url" msgstr "アバターURL" -#: users/serializers/user.py:179 +#: users/serializers/user.py:176 msgid "MFA level" msgstr "MFA レベル" -#: users/serializers/user.py:285 +#: users/serializers/user.py:282 msgid "Select users" msgstr "ユーザーの選択" -#: users/serializers/user.py:286 +#: users/serializers/user.py:283 msgid "For security, only list several users" msgstr "セキュリティのために、複数のユーザーのみをリストします" -#: users/serializers/user.py:319 +#: users/serializers/user.py:316 msgid "name not unique" msgstr "名前が一意ではない" @@ -8536,7 +8514,7 @@ msgstr "ライセンスのインポートに成功" msgid "License is invalid" msgstr "ライセンスが無効です" -#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:138 +#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 msgid "License" msgstr "ライセンス" @@ -8556,6 +8534,35 @@ msgstr "エンタープライズプロフェッショナル版" msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" +#~ msgid "Invalid signature header. No credentials provided." +#~ msgstr "署名ヘッダーが無効です。資格情報は提供されていません。" + +#~ msgid "" +#~ "Invalid signature header. Signature string should not contain spaces." +#~ msgstr "" +#~ "署名ヘッダーが無効です。署名文字列にはスペースを含まないでください。" + +#~ msgid "Invalid signature header. Format like AccessKeyId:Signature" +#~ msgstr "署名ヘッダーが無効です。AccessKeyIdのような形式: Signature" + +#~ msgid "" +#~ "Invalid signature header. Signature string should not contain invalid " +#~ "characters." +#~ msgstr "" +#~ "署名ヘッダーが無効です。署名文字列に無効な文字を含めることはできません。" + +#~ msgid "Invalid signature." +#~ msgstr "署名が無効です。" + +#~ msgid "HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT" +#~ msgstr "HTTP header: Date not provide or not" + +#~ msgid "Expired, more than 15 minutes" +#~ msgstr "期限切れ、15分以上" + +#~ msgid "User disabled." +#~ msgstr "ユーザーが無効になりました。" + #~ msgid "Random" #~ msgstr "ランダム" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 213d99173..bac130e24 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:06ff4e3474944be5a7f95106ecdeaa88d66be345fa69e1e62ec5d8c27be580ea -size 132912 +oid sha256:25c1d449875189c84c0d586792424d70651a1f86f55b93332287dfef44db2f2f +size 132289 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index aeea7f4b5..f42e27fd6 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: 2023-10-09 18:33+0800\n" +"POT-Creation-Date: 2023-10-10 11:13+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -24,10 +24,11 @@ msgstr "参数 'action' 必须是 [{}]" #: accounts/const/account.py:6 #: accounts/serializers/automations/change_secret.py:32 #: assets/models/_user.py:24 audits/signal_handlers/login_log.py:37 -#: authentication/confirm/password.py:9 authentication/forms.py:32 +#: authentication/confirm/password.py:9 authentication/confirm/password.py:24 +#: authentication/confirm/password.py:26 authentication/forms.py:32 #: authentication/templates/authentication/login.html:324 #: settings/serializers/auth/ldap.py:25 settings/serializers/auth/ldap.py:47 -#: users/forms/profile.py:22 users/serializers/user.py:105 +#: users/forms/profile.py:22 users/serializers/user.py:104 #: users/templates/users/_msg_user_created.html:13 #: users/templates/users/user_password_verify.html:18 #: xpack/plugins/cloud/serializers/account_attrs.py:28 @@ -39,7 +40,7 @@ msgstr "密码" msgid "SSH key" msgstr "SSH 密钥" -#: accounts/const/account.py:8 authentication/models/access_key.py:33 +#: accounts/const/account.py:8 authentication/models/access_key.py:37 msgid "Access key" msgstr "Access key" @@ -250,7 +251,7 @@ msgid "Version" msgstr "版本" #: accounts/models/account.py:56 accounts/serializers/account/account.py:211 -#: users/models/user.py:840 +#: users/models/user.py:837 msgid "Source" msgstr "来源" @@ -551,7 +552,7 @@ msgstr "特权账号" #: assets/models/label.py:22 #: authentication/serializers/connect_token_secret.py:114 #: terminal/models/applet/applet.py:40 -#: terminal/models/component/endpoint.py:105 users/serializers/user.py:170 +#: terminal/models/component/endpoint.py:105 users/serializers/user.py:167 msgid "Is active" msgstr "激活" @@ -709,7 +710,7 @@ msgstr "已修改" #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 -#: authentication/api/connection_token.py:381 ops/models/base.py:17 +#: authentication/api/connection_token.py:386 ops/models/base.py:17 #: ops/models/job.py:139 ops/serializers/job.py:21 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" @@ -749,8 +750,8 @@ msgstr "ID" #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:990 -#: users/models/user.py:1026 users/serializers/group.py:18 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:987 +#: users/models/user.py:1023 users/serializers/group.py:18 msgid "User" msgstr "用户" @@ -1003,7 +1004,7 @@ msgstr "优先级可选范围为 1-100 (数值越小越优先)" msgid "Reviewers" msgstr "审批人" -#: acls/models/base.py:43 authentication/models/access_key.py:17 +#: acls/models/base.py:43 authentication/models/access_key.py:20 #: authentication/models/connection_token.py:53 #: authentication/templates/authentication/_access_key_modal.html:32 #: perms/models/asset_permission.py:76 terminal/models/session/sharing.py:29 @@ -1304,7 +1305,7 @@ msgid "Disabled" msgstr "禁用" #: assets/const/base.py:34 settings/serializers/basic.py:6 -#: users/serializers/preference/koko.py:15 +#: users/serializers/preference/koko.py:19 #: users/serializers/preference/lina.py:39 #: users/serializers/preference/luna.py:60 msgid "Basic" @@ -1519,12 +1520,12 @@ msgstr "SSH公钥" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:261 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:227 users/models/user.py:1027 +#: ops/models/job.py:227 users/models/user.py:1024 msgid "Date created" msgstr "创建日期" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:849 +#: common/db/models.py:35 users/models/user.py:846 msgid "Date updated" msgstr "更新日期" @@ -1774,7 +1775,7 @@ msgstr "默认" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1012 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1009 msgid "System" msgstr "系统" @@ -2476,7 +2477,7 @@ msgstr "认证令牌" #: audits/signal_handlers/login_log.py:40 authentication/notifications.py:73 #: authentication/views/login.py:77 authentication/views/wecom.py:159 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:745 users/models/user.py:850 +#: users/models/user.py:745 users/models/user.py:847 msgid "WeCom" msgstr "企业微信" @@ -2484,14 +2485,14 @@ msgstr "企业微信" #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 #: settings/serializers/auth/feishu.py:13 users/models/user.py:747 -#: users/models/user.py:852 +#: users/models/user.py:849 msgid "FeiShu" msgstr "飞书" #: audits/signal_handlers/login_log.py:42 authentication/views/dingtalk.py:159 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 #: settings/serializers/auth/dingtalk.py:10 users/models/user.py:746 -#: users/models/user.py:851 +#: users/models/user.py:848 msgid "DingTalk" msgstr "钉钉" @@ -2521,23 +2522,23 @@ msgstr "该操作需要验证您的 MFA, 请先开启并配置" msgid "Reusable connection token is not allowed, global setting not enabled" msgstr "不允许使用可重复使用的连接令牌,未启用全局设置" -#: authentication/api/connection_token.py:352 +#: authentication/api/connection_token.py:357 msgid "Anonymous account is not supported for this asset" msgstr "匿名账号不支持当前资产" -#: authentication/api/connection_token.py:371 +#: authentication/api/connection_token.py:376 msgid "Account not found" msgstr "账号未找到" -#: authentication/api/connection_token.py:374 +#: authentication/api/connection_token.py:379 msgid "Permission expired" msgstr "授权已过期" -#: authentication/api/connection_token.py:401 +#: authentication/api/connection_token.py:406 msgid "ACL action is reject: {}({})" msgstr "ACL 动作是拒绝: {}({})" -#: authentication/api/connection_token.py:405 +#: authentication/api/connection_token.py:410 msgid "ACL action is review" msgstr "ACL 动作是复核" @@ -2578,54 +2579,20 @@ msgstr "认证" msgid "User invalid, disabled or expired" msgstr "用户无效,已禁用或已过期" -#: authentication/backends/drf.py:54 -msgid "Invalid signature header. No credentials provided." -msgstr "不合法的签名头" - -#: authentication/backends/drf.py:57 -msgid "Invalid signature header. Signature string should not contain spaces." -msgstr "不合法的签名头" - -#: authentication/backends/drf.py:64 -msgid "Invalid signature header. Format like AccessKeyId:Signature" -msgstr "不合法的签名头" - -#: authentication/backends/drf.py:68 -msgid "" -"Invalid signature header. Signature string should not contain invalid " -"characters." -msgstr "不合法的签名头" - -#: authentication/backends/drf.py:88 authentication/backends/drf.py:104 -msgid "Invalid signature." -msgstr "签名无效" - -#: authentication/backends/drf.py:95 -msgid "HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT" -msgstr "HTTP header not valid" - -#: authentication/backends/drf.py:100 -msgid "Expired, more than 15 minutes" -msgstr "已过期,超过15分钟" - -#: authentication/backends/drf.py:107 -msgid "User disabled." -msgstr "用户已禁用" - -#: authentication/backends/drf.py:125 +#: authentication/backends/drf.py:39 msgid "Invalid token header. No credentials provided." msgstr "无效的令牌头。没有提供任何凭据。" -#: authentication/backends/drf.py:128 +#: authentication/backends/drf.py:42 msgid "Invalid token header. Sign string should not contain spaces." msgstr "无效的令牌头。符号字符串不应包含空格。" -#: authentication/backends/drf.py:135 +#: authentication/backends/drf.py:48 msgid "" "Invalid token header. Sign string should not contain invalid characters." msgstr "无效的令牌头。符号字符串不应包含无效字符。" -#: authentication/backends/drf.py:146 +#: authentication/backends/drf.py:61 msgid "Invalid token or cache refreshed." msgstr "刷新的令牌或缓存无效。" @@ -2642,6 +2609,8 @@ msgid "Added on" msgstr "附加" #: authentication/backends/passkey/models.py:14 +#: authentication/models/access_key.py:21 +#: authentication/models/private_token.py:8 msgid "Date last used" msgstr "最后使用日期" @@ -3008,7 +2977,7 @@ msgstr "可以查看超级连接令牌密文" msgid "Super connection token" msgstr "超级连接令牌" -#: authentication/models/private_token.py:9 +#: authentication/models/private_token.py:11 msgid "Private Token" msgstr "私有令牌" @@ -3066,7 +3035,7 @@ msgstr "动作" #: authentication/serializers/connection_token.py:42 #: perms/serializers/permission.py:38 perms/serializers/permission.py:57 -#: users/serializers/user.py:97 users/serializers/user.py:174 +#: users/serializers/user.py:97 users/serializers/user.py:171 msgid "Is expired" msgstr "已过期" @@ -3085,9 +3054,9 @@ msgstr "邮箱" msgid "The {} cannot be empty" msgstr "{} 不能为空" -#: authentication/serializers/token.py:79 perms/serializers/permission.py:37 +#: authentication/serializers/token.py:80 perms/serializers/permission.py:37 #: perms/serializers/permission.py:58 users/serializers/user.py:98 -#: users/serializers/user.py:171 +#: users/serializers/user.py:168 msgid "Is valid" msgstr "是否有效" @@ -6168,7 +6137,7 @@ msgstr "不支持批量创建" msgid "Storage is invalid" msgstr "存储无效" -#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:86 +#: terminal/models/applet/applet.py:30 xpack/plugins/license/models.py:88 msgid "Community edition" msgstr "社区版" @@ -7290,7 +7259,7 @@ msgstr "用户设置" msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:804 users/serializers/user.py:172 +#: users/models/user.py:804 users/serializers/user.py:169 msgid "Is service account" msgstr "服务账号" @@ -7302,7 +7271,7 @@ msgstr "头像" msgid "Wechat" msgstr "微信" -#: users/models/user.py:812 users/serializers/user.py:109 +#: users/models/user.py:812 users/serializers/user.py:106 msgid "Phone" msgstr "手机" @@ -7316,43 +7285,47 @@ msgid "Private key" msgstr "ssh私钥" #: users/models/user.py:830 users/serializers/profile.py:125 -#: users/serializers/user.py:169 +#: users/serializers/user.py:166 msgid "Is first login" msgstr "首次登录" -#: users/models/user.py:844 +#: users/models/user.py:840 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:847 +#: users/models/user.py:843 msgid "Need update password" msgstr "需要更新密码" -#: users/models/user.py:971 +#: users/models/user.py:845 +msgid "Date api key used" +msgstr "Api key 最后使用日期" + +#: users/models/user.py:968 msgid "Can not delete admin user" msgstr "无法删除管理员用户" -#: users/models/user.py:997 +#: users/models/user.py:994 msgid "Can invite user" msgstr "可以邀请用户" -#: users/models/user.py:998 +#: users/models/user.py:995 msgid "Can remove user" msgstr "可以移除用户" -#: users/models/user.py:999 +#: users/models/user.py:996 msgid "Can match user" msgstr "可以匹配用户" -#: users/models/user.py:1008 +#: users/models/user.py:1005 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:1011 +#: users/models/user.py:1008 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/models/user.py:1036 +#: users/models/user.py:1033 msgid "User password history" msgstr "用户密码历史" @@ -7391,6 +7364,12 @@ msgstr "重置 MFA" msgid "File name conflict resolution" msgstr "文件名冲突解决方案" +#: users/serializers/preference/koko.py:14 +#, fuzzy +#| msgid "Terminal setting" +msgid "Terminal theme name" +msgstr "终端设置" + #: users/serializers/preference/lina.py:13 msgid "New file encryption password" msgstr "文件加密密码" @@ -7479,7 +7458,7 @@ msgstr "强制 MFA" msgid "Login blocked" msgstr "登录被锁定" -#: users/serializers/user.py:99 users/serializers/user.py:178 +#: users/serializers/user.py:99 users/serializers/user.py:175 msgid "Is OTP bound" msgstr "是否绑定了虚拟 MFA" @@ -7487,27 +7466,27 @@ msgstr "是否绑定了虚拟 MFA" msgid "Can public key authentication" msgstr "可以使用公钥认证" -#: users/serializers/user.py:173 +#: users/serializers/user.py:170 msgid "Is org admin" msgstr "组织管理员" -#: users/serializers/user.py:175 +#: users/serializers/user.py:172 msgid "Avatar url" msgstr "头像路径" -#: users/serializers/user.py:179 +#: users/serializers/user.py:176 msgid "MFA level" msgstr "MFA 级别" -#: users/serializers/user.py:285 +#: users/serializers/user.py:282 msgid "Select users" msgstr "选择用户" -#: users/serializers/user.py:286 +#: users/serializers/user.py:283 msgid "For security, only list several users" msgstr "为了安全,仅列出几个用户" -#: users/serializers/user.py:319 +#: users/serializers/user.py:316 msgid "name not unique" msgstr "名称重复" @@ -8416,7 +8395,7 @@ msgstr "许可证导入成功" msgid "License is invalid" msgstr "无效的许可证" -#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:138 +#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 msgid "License" msgstr "许可证" @@ -8436,5 +8415,32 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" +#~ msgid "Invalid signature header. No credentials provided." +#~ msgstr "不合法的签名头" + +#~ msgid "" +#~ "Invalid signature header. Signature string should not contain spaces." +#~ msgstr "不合法的签名头" + +#~ msgid "Invalid signature header. Format like AccessKeyId:Signature" +#~ msgstr "不合法的签名头" + +#~ msgid "" +#~ "Invalid signature header. Signature string should not contain invalid " +#~ "characters." +#~ msgstr "不合法的签名头" + +#~ msgid "Invalid signature." +#~ msgstr "签名无效" + +#~ msgid "HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT" +#~ msgstr "HTTP header not valid" + +#~ msgid "Expired, more than 15 minutes" +#~ msgstr "已过期,超过15分钟" + +#~ msgid "User disabled." +#~ msgstr "用户已禁用" + #~ msgid "Random" #~ msgstr "随机" diff --git a/apps/users/migrations/0047_user_date_api_key_last_used.py b/apps/users/migrations/0047_user_date_api_key_last_used.py new file mode 100644 index 000000000..21ccd13cb --- /dev/null +++ b/apps/users/migrations/0047_user_date_api_key_last_used.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.10 on 2023-10-08 04:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0046_auto_20230927_1456'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='date_api_key_last_used', + field=models.DateTimeField(blank=True, null=True, verbose_name='Date api key used'), + ), + ] diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 94b05b331..adac178a7 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -834,11 +834,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, JSONFilterMixin, Abstract ) created_by = models.CharField(max_length=30, default='', blank=True, verbose_name=_('Created by')) updated_by = models.CharField(max_length=30, default='', blank=True, verbose_name=_('Updated by')) - source = models.CharField( - max_length=30, default=Source.local, - choices=Source.choices, - verbose_name=_('Source') - ) + source = models.CharField(max_length=30, default=Source.local, choices=Source.choices, verbose_name=_('Source')) date_password_last_updated = models.DateTimeField( auto_now_add=True, blank=True, null=True, verbose_name=_('Date password last updated') @@ -846,6 +842,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, JSONFilterMixin, Abstract need_update_password = models.BooleanField( default=False, verbose_name=_('Need update password') ) + date_api_key_last_used = models.DateTimeField(null=True, blank=True, verbose_name=_('Date api key used')) date_updated = models.DateTimeField(auto_now=True, verbose_name=_('Date updated')) wecom_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('WeCom')) dingtalk_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('DingTalk')) diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index 82ce8f0da..7929483a5 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -101,10 +101,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer source="can_use_ssh_key_login", label=_("Can public key authentication"), read_only=True ) - password = EncryptedField( - label=_("Password"), required=False, allow_blank=True, - allow_null=True, max_length=1024, - ) + password = EncryptedField(label=_("Password"), required=False, allow_blank=True, allow_null=True, max_length=1024, ) phone = PhoneField( validators=[PhoneValidator()], required=False, allow_blank=True, allow_null=True, label=_("Phone") ) @@ -128,8 +125,8 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer "created_by", "updated_by", "comment", # 通用字段 ] fields_date = [ - "date_expired", "date_joined", - "last_login", "date_updated" # 日期字段 + "date_expired", "date_joined", "last_login", + "date_updated", "date_api_key_last_used", ] fields_bool = [ "is_superuser", "is_org_admin", @@ -155,7 +152,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer read_only_fields = [ "date_joined", "last_login", "created_by", "is_first_login", "wecom_id", "dingtalk_id", - "feishu_id", + "feishu_id", "date_api_key_last_used", ] disallow_self_update_fields = ["is_active", "system_roles", "org_roles"] extra_kwargs = { From 70f0f55ddb49d2b9fca07a71f1c404a9ce49de0e Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Tue, 10 Oct 2023 18:23:54 +0800 Subject: [PATCH 046/114] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E7=9F=AD=E4=BF=A1=E8=AE=A4=E8=AF=81(?= =?UTF-8?q?=E6=96=87=E4=BB=B6)=20(#11784)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 支持自定义短信认证(文件) * perf: 翻译 * perf: 还原注释 --- apps/authentication/mfa/custom.py | 2 +- apps/common/sdk/sms/custom.py | 2 +- apps/common/sdk/sms/custom_file.py | 50 ++++++++++++++++ apps/common/sdk/sms/endpoint.py | 3 +- apps/common/utils/verify_code.py | 2 +- apps/jumpserver/conf.py | 6 +- apps/jumpserver/settings/auth.py | 3 + apps/jumpserver/settings/custom.py | 6 ++ apps/locale/ja/LC_MESSAGES/django.po | 83 ++++++++++++++++----------- apps/locale/zh/LC_MESSAGES/django.po | 83 ++++++++++++++++----------- apps/settings/api/sms.py | 23 +++++--- apps/settings/serializers/auth/sms.py | 14 +++-- 12 files changed, 193 insertions(+), 84 deletions(-) create mode 100644 apps/common/sdk/sms/custom_file.py diff --git a/apps/authentication/mfa/custom.py b/apps/authentication/mfa/custom.py index 0819dcfaa..70ceaf34c 100644 --- a/apps/authentication/mfa/custom.py +++ b/apps/authentication/mfa/custom.py @@ -10,7 +10,7 @@ logger = get_logger(__file__) mfa_custom_method = None if settings.MFA_CUSTOM: - """ 保证自定义认证方法在服务运行时不能被更改,只在第一次调用时加载一次 """ + """ 保证自定义的方法在服务运行时不能被更改,只在第一次调用时加载一次 """ try: mfa_custom_method_path = 'data.mfa.main.check_code' mfa_custom_method = import_string(mfa_custom_method_path) diff --git a/apps/common/sdk/sms/custom.py b/apps/common/sdk/sms/custom.py index 64f9a0246..2cfaca6ca 100644 --- a/apps/common/sdk/sms/custom.py +++ b/apps/common/sdk/sms/custom.py @@ -30,7 +30,7 @@ class CustomSMS(BaseSMSClient): code=template_param.get('code'), phone_numbers=phone_numbers_str ) - logger.info(f'Custom sms send: phone_numbers={phone_numbers}param={params}') + logger.info(f'Custom sms send: phone_numbers={phone_numbers}, param={params}') if settings.CUSTOM_SMS_REQUEST_METHOD == 'post': action = requests.post kwargs = {'json': params} diff --git a/apps/common/sdk/sms/custom_file.py b/apps/common/sdk/sms/custom_file.py new file mode 100644 index 000000000..ee24a0d31 --- /dev/null +++ b/apps/common/sdk/sms/custom_file.py @@ -0,0 +1,50 @@ +import os + +from collections import OrderedDict + +from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from django.utils.module_loading import import_string + +from common.utils import get_logger +from common.exceptions import JMSException +from jumpserver.settings import get_file_md5 + +from .base import BaseSMSClient + + +logger = get_logger(__file__) + + +custom_sms_method = None +SMS_CUSTOM_FILE_MD5 = settings.SMS_CUSTOM_FILE_MD5 +SMS_CUSTOM_FILE_PATH = os.path.join(settings.PROJECT_DIR, 'data', 'sms', 'main.py') +if SMS_CUSTOM_FILE_MD5 == get_file_md5(SMS_CUSTOM_FILE_PATH): + try: + custom_sms_method_path = 'data.sms.main.send_sms' + custom_sms_method = import_string(custom_sms_method_path) + except Exception as e: + logger.warning('Import custom sms method failed: {}, Maybe not enabled'.format(e)) + + +class CustomFileSMS(BaseSMSClient): + @classmethod + def new_from_settings(cls): + return cls() + + @staticmethod + def need_pre_check(): + return False + + def send_sms(self, phone_numbers: list, template_param: OrderedDict, **kwargs): + if not callable(custom_sms_method): + raise JMSException(_('The custom sms file is invalid')) + + try: + logger.info(f'Custom file sms send: phone_numbers={phone_numbers}, param={template_param}') + custom_sms_method(phone_numbers, template_param, **kwargs) + except Exception as err: + raise JMSException(_('SMS sending failed[%s]: %s') % (f"{_('Custom type')}({_('File')})", err)) + + +client = CustomFileSMS diff --git a/apps/common/sdk/sms/endpoint.py b/apps/common/sdk/sms/endpoint.py index ce016888a..6f5433fa5 100644 --- a/apps/common/sdk/sms/endpoint.py +++ b/apps/common/sdk/sms/endpoint.py @@ -17,7 +17,8 @@ class BACKENDS(TextChoices): TENCENT = 'tencent', _('Tencent cloud') HUAWEI = 'huawei', _('Huawei Cloud') CMPP2 = 'cmpp2', _('CMPP v2.0') - Custom = 'custom', _('Custom type') + CUSTOM = 'custom', _('Custom type') + CUSTOM_FILE = 'custom_file', f"{_('Custom type')}({_('File')})" class SMS: diff --git a/apps/common/utils/verify_code.py b/apps/common/utils/verify_code.py index b21e43b26..f9a721c89 100644 --- a/apps/common/utils/verify_code.py +++ b/apps/common/utils/verify_code.py @@ -67,7 +67,7 @@ class SendAndVerifyCodeUtil(object): return cache.get(self.key) def __generate(self): - code = random_string(4, lower=False, upper=False) + code = random_string(settings.SMS_CODE_LENGTH, lower=False, upper=False) self.code = code return code diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index b24ef5746..cb913f4d8 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -247,10 +247,11 @@ class Config(dict): 'AUTH_CUSTOM': False, 'AUTH_CUSTOM_FILE_MD5': '', - # Custom Config 'MFA_CUSTOM': False, 'MFA_CUSTOM_FILE_MD5': '', + 'SMS_CUSTOM_FILE_MD5': '', + # 临时密码 'AUTH_TEMP_TOKEN': False, @@ -409,6 +410,7 @@ class Config(dict): 'SMS_ENABLED': False, 'SMS_BACKEND': '', + 'SMS_CODE_LENGTH': 4, 'SMS_TEST_PHONE': '', 'ALIBABA_ACCESS_KEY_ID': '', @@ -439,7 +441,7 @@ class Config(dict): 'CMPP2_VERIFY_TEMPLATE_CODE': '{code}', 'CUSTOM_SMS_URL': '', - 'CUSTOM_SMS_API_PARAMS': {'phone_numbers': '{phone_numbers}', 'code': '{code}'}, + 'CUSTOM_SMS_API_PARAMS': {'phone_numbers': '{phone_numbers}', 'content': _('The verification code is: {code}')}, 'CUSTOM_SMS_REQUEST_METHOD': 'get', # Email diff --git a/apps/jumpserver/settings/auth.py b/apps/jumpserver/settings/auth.py index 63a96fe1d..5b6a1cd35 100644 --- a/apps/jumpserver/settings/auth.py +++ b/apps/jumpserver/settings/auth.py @@ -259,6 +259,9 @@ if MFA_CUSTOM and MFA_CUSTOM_FILE_MD5 == get_file_md5(MFA_CUSTOM_FILE_PATH): # 自定义多因子认证模块 MFA_BACKENDS.append(MFA_BACKEND_CUSTOM) +SMS_CUSTOM_FILE_MD5 = CONFIG.SMS_CUSTOM_FILE_MD5 +SMS_CUSTOM_FILE_PATH = os.path.join(PROJECT_DIR, 'data', 'sms', 'main.py') + AUTHENTICATION_BACKENDS_THIRD_PARTY = [ AUTH_BACKEND_OIDC_CODE, AUTH_BACKEND_CAS, AUTH_BACKEND_SAML2, AUTH_BACKEND_OAUTH2 diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index e60b98dad..0ec5c54ff 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -169,6 +169,7 @@ TERMINAL_KOKO_SSH_ENABLED = CONFIG.TERMINAL_KOKO_SSH_ENABLED # SMS enabled SMS_ENABLED = CONFIG.SMS_ENABLED SMS_BACKEND = CONFIG.SMS_BACKEND +SMS_CODE_LENGTH = CONFIG.SMS_CODE_LENGTH SMS_TEST_PHONE = CONFIG.SMS_TEST_PHONE # Alibaba @@ -186,6 +187,11 @@ TENCENT_VERIFY_SIGN_NAME = CONFIG.TENCENT_VERIFY_SIGN_NAME TENCENT_VERIFY_TEMPLATE_CODE = CONFIG.TENCENT_VERIFY_TEMPLATE_CODE TENCENT_SMS_SIGN_AND_TEMPLATES = CONFIG.TENCENT_SMS_SIGN_AND_TEMPLATES +# CUSTOM_SMS +CUSTOM_SMS_URL = CONFIG.CUSTOM_SMS_URL +CUSTOM_SMS_API_PARAMS = CONFIG.CUSTOM_SMS_API_PARAMS +CUSTOM_SMS_REQUEST_METHOD = CONFIG.CUSTOM_SMS_REQUEST_METHOD + # 公告 ANNOUNCEMENT_ENABLED = CONFIG.ANNOUNCEMENT_ENABLED ANNOUNCEMENT = CONFIG.ANNOUNCEMENT diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index bcd757537..98a9685e6 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -81,7 +81,7 @@ msgid "Collected" msgstr "集めました" #: accounts/const/account.py:27 accounts/serializers/account/account.py:28 -#: settings/serializers/auth/sms.py:75 +#: settings/serializers/auth/sms.py:79 msgid "Template" msgstr "テンプレート" @@ -1325,7 +1325,7 @@ msgid "Script" msgstr "脚本" #: assets/const/category.py:10 assets/models/asset/host.py:8 -#: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:67 +#: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:71 #: settings/serializers/feature.py:47 terminal/models/component/endpoint.py:13 #: terminal/serializers/applet.py:17 #: xpack/plugins/cloud/serializers/account_attrs.py:72 @@ -1616,7 +1616,7 @@ msgid "Cloud" msgstr "クラウド サービス" #: assets/models/asset/common.py:92 assets/models/platform.py:16 -#: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:68 +#: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:72 #: xpack/plugins/cloud/serializers/account_attrs.py:73 msgid "Port" msgstr "ポート" @@ -1692,7 +1692,7 @@ msgid "Proxy" msgstr "プロキシー" #: assets/models/automations/base.py:22 ops/models/job.py:223 -#: settings/serializers/auth/sms.py:99 +#: settings/serializers/auth/sms.py:103 msgid "Parameters" msgstr "パラメータ" @@ -2892,7 +2892,7 @@ msgstr "メッセージ検証コードが無効" #: authentication/mfa/sms.py:12 authentication/serializers/password_mfa.py:16 #: authentication/serializers/password_mfa.py:24 -#: settings/serializers/auth/sms.py:28 users/forms/profile.py:104 +#: settings/serializers/auth/sms.py:32 users/forms/profile.py:104 #: users/forms/profile.py:109 users/templates/users/forgot_password.html:112 #: users/views/profile/reset.py:98 msgid "SMS" @@ -3164,7 +3164,7 @@ msgstr "コードエラー" #: authentication/templates/authentication/_msg_reset_password_code.html:9 #: authentication/templates/authentication/_msg_rest_password_success.html:2 #: authentication/templates/authentication/_msg_rest_public_key_success.html:2 -#: jumpserver/conf.py:447 +#: jumpserver/conf.py:449 #: perms/templates/perms/_msg_item_permissions_expire.html:3 #: perms/templates/perms/_msg_permed_items_expire.html:3 #: tickets/templates/tickets/approve_check_password.html:33 @@ -3674,6 +3674,15 @@ msgstr "SP idは6ビット" msgid "Failed to connect to the CMPP gateway server, err: {}" msgstr "接続ゲートウェイサーバエラー, 非: {}" +#: common/sdk/sms/custom_file.py:41 +msgid "The custom sms file is invalid" +msgstr "カスタムショートメッセージファイルが無効です" + +#: common/sdk/sms/custom_file.py:47 +#, python-format +msgid "SMS sending failed[%s]: %s" +msgstr "ショートメッセージの送信に失敗しました[%s]: %s" + #: common/sdk/sms/endpoint.py:16 msgid "Alibaba cloud" msgstr "アリ雲" @@ -3690,11 +3699,11 @@ msgstr "華為雲" msgid "CMPP v2.0" msgstr "CMPP v2.0" -#: common/sdk/sms/endpoint.py:31 +#: common/sdk/sms/endpoint.py:32 msgid "SMS provider not support: {}" msgstr "SMSプロバイダーはサポートしていません: {}" -#: common/sdk/sms/endpoint.py:53 +#: common/sdk/sms/endpoint.py:54 msgid "SMS verification code signature or template invalid" msgstr "SMS検証コードの署名またはテンプレートが無効" @@ -3765,11 +3774,15 @@ msgstr "特殊文字を含むべきではない" msgid "The mobile phone number format is incorrect" msgstr "携帯電話番号の形式が正しくありません" -#: jumpserver/conf.py:446 +#: jumpserver/conf.py:444 +msgid "The verification code is: {code}" +msgstr "認証コードは: {code}" + +#: jumpserver/conf.py:448 msgid "Create account successfully" msgstr "アカウントを正常に作成" -#: jumpserver/conf.py:448 +#: jumpserver/conf.py:450 msgid "Your account has been created successfully" msgstr "アカウントが正常に作成されました" @@ -4596,7 +4609,7 @@ msgid "View permission tree" msgstr "権限ツリーの表示" #: settings/api/dingtalk.py:31 settings/api/feishu.py:36 -#: settings/api/sms.py:153 settings/api/vault.py:40 settings/api/wecom.py:37 +#: settings/api/sms.py:160 settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" msgstr "テストの成功" @@ -4624,11 +4637,11 @@ msgstr "Ldapユーザーを取得するにはNone" msgid "Imported {} users successfully (Organization: {})" msgstr "{} 人のユーザーを正常にインポートしました (組織: {})" -#: settings/api/sms.py:135 +#: settings/api/sms.py:142 msgid "Invalid SMS platform" msgstr "無効なショートメッセージプラットフォーム" -#: settings/api/sms.py:141 +#: settings/api/sms.py:148 msgid "test_phone is required" msgstr "携帯番号をテストこのフィールドは必須です" @@ -5034,54 +5047,58 @@ msgstr "SP プライベートキー" msgid "SP cert" msgstr "SP 証明書" -#: settings/serializers/auth/sms.py:16 +#: settings/serializers/auth/sms.py:17 msgid "Enable SMS" msgstr "SMSの有効化" -#: settings/serializers/auth/sms.py:18 +#: settings/serializers/auth/sms.py:19 msgid "SMS provider / Protocol" msgstr "SMSプロバイダ / プロトコル" -#: settings/serializers/auth/sms.py:23 settings/serializers/auth/sms.py:45 -#: settings/serializers/auth/sms.py:53 settings/serializers/auth/sms.py:62 -#: settings/serializers/auth/sms.py:73 settings/serializers/msg.py:76 +#: settings/serializers/auth/sms.py:22 +msgid "SMS code length" +msgstr "認証コード長" + +#: settings/serializers/auth/sms.py:27 settings/serializers/auth/sms.py:49 +#: settings/serializers/auth/sms.py:57 settings/serializers/auth/sms.py:66 +#: settings/serializers/auth/sms.py:77 settings/serializers/msg.py:76 msgid "Signature" msgstr "署名" -#: settings/serializers/auth/sms.py:24 settings/serializers/auth/sms.py:46 -#: settings/serializers/auth/sms.py:54 settings/serializers/auth/sms.py:63 +#: settings/serializers/auth/sms.py:28 settings/serializers/auth/sms.py:50 +#: settings/serializers/auth/sms.py:58 settings/serializers/auth/sms.py:67 msgid "Template code" msgstr "テンプレートコード" -#: settings/serializers/auth/sms.py:31 +#: settings/serializers/auth/sms.py:35 msgid "Test phone" msgstr "テスト電話" -#: settings/serializers/auth/sms.py:60 +#: settings/serializers/auth/sms.py:64 msgid "App Access Address" msgstr "アプリケーションアドレス" -#: settings/serializers/auth/sms.py:61 +#: settings/serializers/auth/sms.py:65 msgid "Signature channel number" msgstr "署名チャネル番号" -#: settings/serializers/auth/sms.py:69 +#: settings/serializers/auth/sms.py:73 msgid "Enterprise code(SP id)" msgstr "企業コード(SP id)" -#: settings/serializers/auth/sms.py:70 +#: settings/serializers/auth/sms.py:74 msgid "Shared secret(Shared secret)" msgstr "パスワードを共有する(Shared secret)" -#: settings/serializers/auth/sms.py:71 +#: settings/serializers/auth/sms.py:75 msgid "Original number(Src id)" msgstr "元の番号(Src id)" -#: settings/serializers/auth/sms.py:72 +#: settings/serializers/auth/sms.py:76 msgid "Business type(Service id)" msgstr "ビジネス・タイプ(Service id)" -#: settings/serializers/auth/sms.py:76 +#: settings/serializers/auth/sms.py:80 #, python-brace-format msgid "" "Template need contain {code} and Signature + template length does not exceed " @@ -5092,24 +5109,24 @@ msgstr "" "満です。たとえば、認証コードは{code}で、有効期間は5分です。他の人には言わない" "でください。" -#: settings/serializers/auth/sms.py:85 +#: settings/serializers/auth/sms.py:89 #, python-brace-format msgid "The template needs to contain {code}" msgstr "テンプレートには{code}を含める必要があります" -#: settings/serializers/auth/sms.py:88 +#: settings/serializers/auth/sms.py:92 msgid "Signature + Template must not exceed 65 words" msgstr "署名+テンプレートの長さは65文字以内" -#: settings/serializers/auth/sms.py:97 +#: settings/serializers/auth/sms.py:101 msgid "URL" msgstr "URL" -#: settings/serializers/auth/sms.py:102 +#: settings/serializers/auth/sms.py:106 msgid "Request method" msgstr "請求方法です" -#: settings/serializers/auth/sms.py:111 +#: settings/serializers/auth/sms.py:117 #, python-format msgid "The value in the parameter must contain %s" msgstr "パラメータの値には必ず %s が含まれます" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index f42e27fd6..be805ccf0 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -80,7 +80,7 @@ msgid "Collected" msgstr "收集" #: accounts/const/account.py:27 accounts/serializers/account/account.py:28 -#: settings/serializers/auth/sms.py:75 +#: settings/serializers/auth/sms.py:79 msgid "Template" msgstr "模板" @@ -1317,7 +1317,7 @@ msgid "Script" msgstr "脚本" #: assets/const/category.py:10 assets/models/asset/host.py:8 -#: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:67 +#: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:71 #: settings/serializers/feature.py:47 terminal/models/component/endpoint.py:13 #: terminal/serializers/applet.py:17 #: xpack/plugins/cloud/serializers/account_attrs.py:72 @@ -1608,7 +1608,7 @@ msgid "Cloud" msgstr "云服务" #: assets/models/asset/common.py:92 assets/models/platform.py:16 -#: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:68 +#: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:72 #: xpack/plugins/cloud/serializers/account_attrs.py:73 msgid "Port" msgstr "端口" @@ -1684,7 +1684,7 @@ msgid "Proxy" msgstr "代理" #: assets/models/automations/base.py:22 ops/models/job.py:223 -#: settings/serializers/auth/sms.py:99 +#: settings/serializers/auth/sms.py:103 msgid "Parameters" msgstr "参数" @@ -2862,7 +2862,7 @@ msgstr "短信验证码校验失败" #: authentication/mfa/sms.py:12 authentication/serializers/password_mfa.py:16 #: authentication/serializers/password_mfa.py:24 -#: settings/serializers/auth/sms.py:28 users/forms/profile.py:104 +#: settings/serializers/auth/sms.py:32 users/forms/profile.py:104 #: users/forms/profile.py:109 users/templates/users/forgot_password.html:112 #: users/views/profile/reset.py:98 msgid "SMS" @@ -3132,7 +3132,7 @@ msgstr "代码错误" #: authentication/templates/authentication/_msg_reset_password_code.html:9 #: authentication/templates/authentication/_msg_rest_password_success.html:2 #: authentication/templates/authentication/_msg_rest_public_key_success.html:2 -#: jumpserver/conf.py:447 +#: jumpserver/conf.py:449 #: perms/templates/perms/_msg_item_permissions_expire.html:3 #: perms/templates/perms/_msg_permed_items_expire.html:3 #: tickets/templates/tickets/approve_check_password.html:33 @@ -3628,6 +3628,15 @@ msgstr "SP_id 为6位" msgid "Failed to connect to the CMPP gateway server, err: {}" msgstr "连接网关服务器错误,错误:{}" +#: common/sdk/sms/custom_file.py:41 +msgid "The custom sms file is invalid" +msgstr "自定义短信文件无效" + +#: common/sdk/sms/custom_file.py:47 +#, python-format +msgid "SMS sending failed[%s]: %s" +msgstr "短信发送失败[%s]: %s" + #: common/sdk/sms/endpoint.py:16 msgid "Alibaba cloud" msgstr "阿里云" @@ -3644,11 +3653,11 @@ msgstr "华为云" msgid "CMPP v2.0" msgstr "CMPP v2.0" -#: common/sdk/sms/endpoint.py:31 +#: common/sdk/sms/endpoint.py:32 msgid "SMS provider not support: {}" msgstr "短信服务商不支持:{}" -#: common/sdk/sms/endpoint.py:53 +#: common/sdk/sms/endpoint.py:54 msgid "SMS verification code signature or template invalid" msgstr "短信验证码签名或模版无效" @@ -3719,11 +3728,15 @@ msgstr "不能包含特殊字符" msgid "The mobile phone number format is incorrect" msgstr "手机号格式不正确" -#: jumpserver/conf.py:446 +#: jumpserver/conf.py:444 +msgid "The verification code is: {code}" +msgstr "验证码为: {code}" + +#: jumpserver/conf.py:448 msgid "Create account successfully" msgstr "创建账号成功" -#: jumpserver/conf.py:448 +#: jumpserver/conf.py:450 msgid "Your account has been created successfully" msgstr "你的账号已创建成功" @@ -4543,7 +4556,7 @@ msgid "View permission tree" msgstr "查看授权树" #: settings/api/dingtalk.py:31 settings/api/feishu.py:36 -#: settings/api/sms.py:153 settings/api/vault.py:40 settings/api/wecom.py:37 +#: settings/api/sms.py:160 settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" msgstr "测试成功" @@ -4571,11 +4584,11 @@ msgstr "获取 LDAP 用户为 None" msgid "Imported {} users successfully (Organization: {})" msgstr "成功导入 {} 个用户 ( 组织: {} )" -#: settings/api/sms.py:135 +#: settings/api/sms.py:142 msgid "Invalid SMS platform" msgstr "无效的短信平台" -#: settings/api/sms.py:141 +#: settings/api/sms.py:148 msgid "test_phone is required" msgstr "测试手机号 该字段是必填项。" @@ -4980,54 +4993,58 @@ msgstr "SP 密钥" msgid "SP cert" msgstr "SP 证书" -#: settings/serializers/auth/sms.py:16 +#: settings/serializers/auth/sms.py:17 msgid "Enable SMS" msgstr "启用 SMS" -#: settings/serializers/auth/sms.py:18 +#: settings/serializers/auth/sms.py:19 msgid "SMS provider / Protocol" msgstr "短信服务商 / 协议" -#: settings/serializers/auth/sms.py:23 settings/serializers/auth/sms.py:45 -#: settings/serializers/auth/sms.py:53 settings/serializers/auth/sms.py:62 -#: settings/serializers/auth/sms.py:73 settings/serializers/msg.py:76 +#: settings/serializers/auth/sms.py:22 +msgid "SMS code length" +msgstr "验证码长度" + +#: settings/serializers/auth/sms.py:27 settings/serializers/auth/sms.py:49 +#: settings/serializers/auth/sms.py:57 settings/serializers/auth/sms.py:66 +#: settings/serializers/auth/sms.py:77 settings/serializers/msg.py:76 msgid "Signature" msgstr "签名" -#: settings/serializers/auth/sms.py:24 settings/serializers/auth/sms.py:46 -#: settings/serializers/auth/sms.py:54 settings/serializers/auth/sms.py:63 +#: settings/serializers/auth/sms.py:28 settings/serializers/auth/sms.py:50 +#: settings/serializers/auth/sms.py:58 settings/serializers/auth/sms.py:67 msgid "Template code" msgstr "模板" -#: settings/serializers/auth/sms.py:31 +#: settings/serializers/auth/sms.py:35 msgid "Test phone" msgstr "测试手机号" -#: settings/serializers/auth/sms.py:60 +#: settings/serializers/auth/sms.py:64 msgid "App Access Address" msgstr "应用地址" -#: settings/serializers/auth/sms.py:61 +#: settings/serializers/auth/sms.py:65 msgid "Signature channel number" msgstr "签名通道号" -#: settings/serializers/auth/sms.py:69 +#: settings/serializers/auth/sms.py:73 msgid "Enterprise code(SP id)" msgstr "企业代码(SP id)" -#: settings/serializers/auth/sms.py:70 +#: settings/serializers/auth/sms.py:74 msgid "Shared secret(Shared secret)" msgstr "共享密码(Shared secret)" -#: settings/serializers/auth/sms.py:71 +#: settings/serializers/auth/sms.py:75 msgid "Original number(Src id)" msgstr "原始号码(Src id)" -#: settings/serializers/auth/sms.py:72 +#: settings/serializers/auth/sms.py:76 msgid "Business type(Service id)" msgstr "业务类型(Service id)" -#: settings/serializers/auth/sms.py:76 +#: settings/serializers/auth/sms.py:80 #, python-brace-format msgid "" "Template need contain {code} and Signature + template length does not exceed " @@ -5037,24 +5054,24 @@ msgstr "" "模板需要包含 {code},并且模板+签名长度不能超过67个字。例如, 您的验证码是 " "{code}, 有效期为5分钟。请不要泄露给其他人。" -#: settings/serializers/auth/sms.py:85 +#: settings/serializers/auth/sms.py:89 #, python-brace-format msgid "The template needs to contain {code}" msgstr "模板需要包含 {code}" -#: settings/serializers/auth/sms.py:88 +#: settings/serializers/auth/sms.py:92 msgid "Signature + Template must not exceed 65 words" msgstr "模板+签名不能超过65个字" -#: settings/serializers/auth/sms.py:97 +#: settings/serializers/auth/sms.py:101 msgid "URL" msgstr "URL" -#: settings/serializers/auth/sms.py:102 +#: settings/serializers/auth/sms.py:106 msgid "Request method" msgstr "请求方式" -#: settings/serializers/auth/sms.py:111 +#: settings/serializers/auth/sms.py:117 #, python-format msgid "The value in the parameter must contain %s" msgstr "参数中的值必须包含 %s" diff --git a/apps/settings/api/sms.py b/apps/settings/api/sms.py index ec767828d..ecf7bd343 100644 --- a/apps/settings/api/sms.py +++ b/apps/settings/api/sms.py @@ -2,6 +2,7 @@ import importlib from collections import OrderedDict from django.utils.translation import gettext_lazy as _ +from django.conf import settings from rest_framework import status from rest_framework.exceptions import APIException from rest_framework.generics import ListAPIView, GenericAPIView @@ -28,7 +29,6 @@ class SMSBackendAPI(ListAPIView): } for b in BACKENDS.choices ] - return Response(data) @@ -39,11 +39,16 @@ class SMSTestingAPI(GenericAPIView): 'huawei': serializers.HuaweiSMSSettingSerializer, 'cmpp2': serializers.CMPP2SMSSettingSerializer, 'custom': serializers.CustomSMSSettingSerializer, + 'custom_file': serializers.BaseSMSSettingSerializer, } rbac_perms = { 'POST': 'settings.change_sms' } + @property + def test_code(self): + return '6' * settings.SMS_CODE_LENGTH + @staticmethod def get_or_from_setting(key, value=''): if not value: @@ -63,7 +68,7 @@ class SMSTestingAPI(GenericAPIView): send_sms_params = { 'sign_name': data['ALIBABA_VERIFY_SIGN_NAME'], 'template_code': data['ALIBABA_VERIFY_TEMPLATE_CODE'], - 'template_param': {'code': '666666'} + 'template_param': {'code': self.test_code} } return init_params, send_sms_params @@ -78,7 +83,7 @@ class SMSTestingAPI(GenericAPIView): send_sms_params = { 'sign_name': data['TENCENT_VERIFY_SIGN_NAME'], 'template_code': data['TENCENT_VERIFY_TEMPLATE_CODE'], - 'template_param': OrderedDict(code='666666') + 'template_param': OrderedDict(code=self.test_code) } return init_params, send_sms_params @@ -94,7 +99,7 @@ class SMSTestingAPI(GenericAPIView): send_sms_params = { 'sign_name': data['HUAWEI_VERIFY_SIGN_NAME'], 'template_code': data['HUAWEI_VERIFY_TEMPLATE_CODE'], - 'template_param': OrderedDict(code='666666') + 'template_param': OrderedDict(code=self.test_code) } return init_params, send_sms_params @@ -110,16 +115,18 @@ class SMSTestingAPI(GenericAPIView): send_sms_params = { 'sign_name': data['CMPP2_VERIFY_SIGN_NAME'], 'template_code': data['CMPP2_VERIFY_TEMPLATE_CODE'], - 'template_param': OrderedDict(code='666666') + 'template_param': OrderedDict(code=self.test_code) } return init_params, send_sms_params - @staticmethod - def get_custom_params(data): + def get_custom_params(self, data): init_params = {} - send_sms_params = {'template_param': OrderedDict(code='666666')} + send_sms_params = {'template_param': OrderedDict(code=self.test_code)} return init_params, send_sms_params + def get_custom_file_params(self, data): + return self.get_custom_params(data) + def get_params_by_backend(self, backend, data): """ 返回两部分参数 diff --git a/apps/settings/serializers/auth/sms.py b/apps/settings/serializers/auth/sms.py index 40881e52a..1011c9aa6 100644 --- a/apps/settings/serializers/auth/sms.py +++ b/apps/settings/serializers/auth/sms.py @@ -7,6 +7,7 @@ from common.serializers.fields import EncryptedField, PhoneField from common.validators import PhoneValidator __all__ = [ + 'BaseSMSSettingSerializer', 'SMSSettingSerializer', 'AlibabaSMSSettingSerializer', 'TencentSMSSettingSerializer', 'HuaweiSMSSettingSerializer', 'CMPP2SMSSettingSerializer', 'CustomSMSSettingSerializer', ] @@ -17,6 +18,9 @@ class SMSSettingSerializer(serializers.Serializer): SMS_BACKEND = serializers.ChoiceField( choices=BACKENDS.choices, default=BACKENDS.ALIBABA, label=_('SMS provider / Protocol') ) + SMS_CODE_LENGTH = serializers.IntegerField( + default=4, min_value=4, max_value=16, label=_('SMS code length') + ) class SignTmplPairSerializer(serializers.Serializer): @@ -102,12 +106,14 @@ class CustomSMSSettingSerializer(BaseSMSSettingSerializer): default=RequestType.get, choices=RequestType.choices, label=_("Request method") ) - @staticmethod - def validate(attrs): + def validate(self, attrs): need_params = {'{phone_numbers}', '{code}'} params = attrs.get('CUSTOM_SMS_API_PARAMS', {}) - if len(set(params.values()) & need_params) != len(need_params): - raise serializers.ValidationError( + # 这里用逗号分隔是保证需要的参数必须是完整的,不能分开在不同的参数中首位相连 + params_string = ','.join(params.values()) + for param in need_params: + if param not in params_string: + raise serializers.ValidationError( _('The value in the parameter must contain %s') % ','.join(need_params) ) return attrs From 0d101bc5adb1b1076e13d98b5eadf61368101f8c Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:48:35 +0800 Subject: [PATCH 047/114] =?UTF-8?q?perf:=20=E4=B8=8D=E6=B4=BB=E8=B7=83?= =?UTF-8?q?=E4=BA=86=E7=94=A8=E6=88=B7=E9=BB=98=E8=AE=A490=E5=A4=A9?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E4=B8=80=E4=B8=8B=20(#11790)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/jumpserver/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index cb913f4d8..e540ef71e 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -497,7 +497,7 @@ class Config(dict): 'SECURITY_LUNA_REMEMBER_AUTH': True, 'SECURITY_WATERMARK_ENABLED': True, 'SECURITY_MFA_VERIFY_TTL': 3600, - 'SECURITY_UNCOMMON_USERS_TTL': 30, + 'SECURITY_UNCOMMON_USERS_TTL': 90, 'VERIFY_CODE_TTL': 60, 'SECURITY_SESSION_SHARE': True, 'SECURITY_CHECK_DIFFERENT_CITY_LOGIN': True, From affa562384de0c17c6301684687b27dbba4039b8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 10 Oct 2023 17:39:41 +0800 Subject: [PATCH 048/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=A6=81?= =?UTF-8?q?=E7=94=A8=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/asset/common.py | 2 +- apps/audits/api.py | 2 ++ apps/audits/models.py | 6 +++- apps/audits/signal_handlers/activity_log.py | 4 ++- apps/users/tasks.py | 36 ++++++++++++++++++--- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index b88d22ce7..ea2be442c 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -159,7 +159,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali return if isinstance(self.initial_data, list): return - accounts = self.initial_data.pop('accounts', None) + accounts = self.initial_data.get('accounts', None) self._accounts = accounts def _get_protocols_required_default(self): diff --git a/apps/audits/api.py b/apps/audits/api.py index 72c1d8a99..17a58b2f2 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -185,6 +185,8 @@ class ResourceActivityAPIView(generics.ListAPIView): 'r_user', 'r_action', 'r_type' ) org_q = Q(org_id=Organization.SYSTEM_ID) | Q(org_id=current_org.id) + if resource_id: + org_q |= Q(org_id='') | Q(org_id=Organization.ROOT_ID) with tmp_to_root_org(): qs1 = self.get_operate_log_qs(fields, limit, org_q, resource_id=resource_id) qs2 = self.get_activity_log_qs(fields, limit, org_q, resource_id=resource_id) diff --git a/apps/audits/models.py b/apps/audits/models.py index 34ec301f2..ff58f962a 100644 --- a/apps/audits/models.py +++ b/apps/audits/models.py @@ -10,7 +10,7 @@ from django.utils import timezone from django.utils.translation import gettext, gettext_lazy as _ from common.db.encoder import ModelJSONFieldEncoder -from common.utils import lazyproperty +from common.utils import lazyproperty, i18n_trans from ops.models import JobExecution from orgs.mixins.models import OrgModelMixin, Organization from orgs.utils import current_org @@ -155,6 +155,10 @@ class ActivityLog(OrgModelMixin): verbose_name = _("Activity log") ordering = ('-datetime',) + def __str__(self): + detail = i18n_trans(self.detail) + return "{} {}".format(detail, self.resource_id) + def save(self, *args, **kwargs): if current_org.is_root() and not self.org_id: self.org_id = Organization.ROOT_ID diff --git a/apps/audits/signal_handlers/activity_log.py b/apps/audits/signal_handlers/activity_log.py index 4b41d2d1d..a391f0007 100644 --- a/apps/audits/signal_handlers/activity_log.py +++ b/apps/audits/signal_handlers/activity_log.py @@ -69,7 +69,9 @@ class ActivityLogHandler: def create_activities(resource_ids, detail, detail_id, action, org_id): if not resource_ids: - return + raise ValueError('resource_ids is empty') + if not org_id: + org_id = Organization.ROOT_ID activities = [ ActivityLog( resource_id=getattr(resource_id, 'pk', resource_id), diff --git a/apps/users/tasks.py b/apps/users/tasks.py index add7a9ed7..ecc59c3bc 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -1,15 +1,17 @@ # -*- coding: utf-8 -*- # +import uuid from datetime import timedelta -from celery import shared_task +from celery import shared_task, current_task from django.conf import settings +from django.db.models import Q from django.utils import timezone -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, gettext_noop +from audits.const import ActivityChoices from common.const.crontab import CRONTAB_AT_AM_TEN, CRONTAB_AT_PM_TWO from common.utils import get_logger -from common.utils.timezone import utc_now from ops.celery.decorator import after_app_ready_start, register_as_period_task from ops.celery.utils import create_or_update_celery_periodic_tasks from orgs.utils import tmp_to_root_org @@ -85,5 +87,29 @@ def check_user_expired_periodic(): def check_unused_users(): uncommon_users_ttl = settings.SECURITY_UNCOMMON_USERS_TTL seconds_to_subtract = uncommon_users_ttl * 24 * 60 * 60 - t = utc_now() - timedelta(seconds=seconds_to_subtract) - User.objects.filter(last_login__lte=t).update(is_active=False) + t = timezone.now() - timedelta(seconds=seconds_to_subtract) + last_login_q = Q(last_login__lte=t) | Q(last_login__isnull=True) + api_key_q = Q(date_api_key_last_used__lte=t) | Q(date_api_key_last_used__isnull=True) + + users = User.objects \ + .filter(date_joined__lt=t) \ + .filter(is_active=True) \ + .filter(last_login_q) \ + .filter(api_key_q) + + if not users: + return + print("Some users are not used for a long time, and they will be disabled.") + resource_ids = [] + for user in users: + resource_ids.append(user.id) + print(' - {}'.format(user.name)) + + users.update(is_active=False) + from audits.signal_handlers import create_activities + if current_task: + task_id = current_task.request.id + else: + task_id = str(uuid.uuid4()) + detail = gettext_noop('The user has not logged in recently and has been disabled.') + create_activities(resource_ids, detail, task_id, action=ActivityChoices.task, org_id='') From 6017f804a67931137ab69a6b70ea5478366e0c26 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 10 Oct 2023 19:11:08 +0800 Subject: [PATCH 049/114] =?UTF-8?q?perf:=20=E7=94=A8=E6=88=B7=20phone=20we?= =?UTF-8?q?chat=20=E5=8A=A0=E5=AF=86=20(#11789)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- .../migrations/0048_wechat_phone_encrypt.py | 45 +++++++++++++++++++ apps/users/models/user.py | 6 +-- 2 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 apps/users/migrations/0048_wechat_phone_encrypt.py diff --git a/apps/users/migrations/0048_wechat_phone_encrypt.py b/apps/users/migrations/0048_wechat_phone_encrypt.py new file mode 100644 index 000000000..7db87e09e --- /dev/null +++ b/apps/users/migrations/0048_wechat_phone_encrypt.py @@ -0,0 +1,45 @@ +# Generated by Django 4.1.10 on 2023-10-10 06:57 + +from django.db import migrations + +import common.db.fields + +users_bulked = [] + + +def get_encrypt_fields_value(apps, *args): + global users_bulked + user_model = apps.get_model('users', 'User') + bulk_size = 2000 + users = user_model.objects.all() + users_bulked = [ + users[i:i + bulk_size] + for i in range(0, users.count(), bulk_size) + ] + + +def migrate_encrypt_fields(apps, *args): + user_model = apps.get_model('users', 'User') + for _users in users_bulked: + user_model.objects.bulk_update(_users, ['phone', 'wechat']) + + +class Migration(migrations.Migration): + dependencies = [ + ('users', '0047_user_date_api_key_last_used'), + ] + + operations = [ + migrations.RunPython(get_encrypt_fields_value), + migrations.AlterField( + model_name='user', + name='wechat', + field=common.db.fields.EncryptCharField(blank=True, max_length=128, verbose_name='Wechat'), + ), + migrations.AlterField( + model_name='user', + name='phone', + field=common.db.fields.EncryptCharField(blank=True, max_length=128, null=True, verbose_name='Phone'), + ), + migrations.RunPython(migrate_encrypt_fields), + ] diff --git a/apps/users/models/user.py b/apps/users/models/user.py index adac178a7..15054aa4f 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -805,11 +805,11 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, JSONFilterMixin, Abstract avatar = models.ImageField( upload_to="avatar", null=True, verbose_name=_('Avatar') ) - wechat = models.CharField( + wechat = fields.EncryptCharField( max_length=128, blank=True, verbose_name=_('Wechat') ) - phone = models.CharField( - max_length=20, blank=True, null=True, verbose_name=_('Phone') + phone = fields.EncryptCharField( + max_length=128, blank=True, null=True, verbose_name=_('Phone') ) mfa_level = models.SmallIntegerField( default=0, choices=MFAMixin.MFA_LEVEL_CHOICES, verbose_name=_('MFA') From 73db1bf50cc786cff107fd8a7df294999f949e9b Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Mon, 9 Oct 2023 13:55:55 +0800 Subject: [PATCH 050/114] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81LDAP=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=BB=84=E5=8F=98=E6=9B=B4=E6=97=B6=EF=BC=8CJS?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=8F=98=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/utils/ldap.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/settings/utils/ldap.py b/apps/settings/utils/ldap.py index d130ac41a..a91cb5662 100644 --- a/apps/settings/utils/ldap.py +++ b/apps/settings/utils/ldap.py @@ -426,7 +426,21 @@ class LDAPImportUtil(object): return errors @staticmethod - def bind_org(org, users, group_users_mapper): + def exit_user_group(user_groups_mapper): + # 通过对比查询本次导入用户需要移除的用户组 + group_remove_users_mapper = defaultdict(set) + for user, current_groups in user_groups_mapper.items(): + old_groups = set(user.groups.all()) + exit_groups = old_groups - current_groups + logger.debug(f'Ldap user {user} exits user groups {exit_groups}') + for g in exit_groups: + group_remove_users_mapper[g].add(user) + + # 根据用户组统一移除用户 + for g, rm_users in group_remove_users_mapper.items(): + g.users.remove(*rm_users) + + def bind_org(self, org, users, group_users_mapper): if not org: return if org.is_root(): @@ -436,11 +450,15 @@ class LDAPImportUtil(object): org.add_member(user) # add user to group with tmp_to_org(org): + user_groups_mapper = defaultdict(set) for group_name, users in group_users_mapper.items(): group, created = UserGroup.objects.get_or_create( name=group_name, defaults={'name': group_name} ) + for user in users: + user_groups_mapper[user].add(group) group.users.add(*users) + self.exit_user_group(user_groups_mapper) class LDAPTestUtil(object): From 09f71d80eb313b41298acc2285b73dbe729c29b6 Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 11 Oct 2023 16:59:51 +0800 Subject: [PATCH 051/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96LDAP=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=AF=BC=E5=85=A5=E5=88=97=E8=A1=A8=E6=97=B6=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=8A=B6=E6=80=81=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/utils/ldap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/settings/utils/ldap.py b/apps/settings/utils/ldap.py index a91cb5662..d5fba8ce9 100644 --- a/apps/settings/utils/ldap.py +++ b/apps/settings/utils/ldap.py @@ -350,12 +350,12 @@ class LDAPSyncUtil(object): try: self.pre_sync() self.sync() - self.post_sync() except Exception as e: error_msg = str(e) logger.error(error_msg) self.set_task_error_msg(error_msg) finally: + self.post_sync() logger.info('End perform sync ldap users from server to cache') close_old_connections() From 3ef8e9603a1d37dafe630ffeab56effd088ba8c4 Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 11 Oct 2023 14:06:56 +0800 Subject: [PATCH 052/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20otp=20wind?= =?UTF-8?q?ows=20=E6=9C=80=E5=B0=8F=E6=94=AF=E6=8C=81=E8=AE=BE=E7=BD=AE=20?= =?UTF-8?q?0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/serializers/security.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/settings/serializers/security.py b/apps/settings/serializers/security.py index 7c9bf676a..1fca3be3d 100644 --- a/apps/settings/serializers/security.py +++ b/apps/settings/serializers/security.py @@ -128,7 +128,7 @@ class SecurityAuthSerializer(serializers.Serializer): required=False, max_length=16, label=_('OTP issuer name'), ) OTP_VALID_WINDOW = serializers.IntegerField( - min_value=1, max_value=10, + min_value=0, max_value=10, label=_("OTP valid window") ) SECURITY_MFA_VERIFY_TTL = serializers.IntegerField( From ca7d1640342cf46202a163dee461dd48c210672e Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 11 Oct 2023 16:03:05 +0800 Subject: [PATCH 053/114] =?UTF-8?q?perf:=20=E8=B4=A6=E5=8F=B7=E6=A8=A1?= =?UTF-8?q?=E7=89=88=E4=BF=A1=E6=81=AF=E5=90=8C=E6=AD=A5=E5=88=B0=E6=89=80?= =?UTF-8?q?=E5=85=B3=E8=81=94=E7=9A=84=E8=B4=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/api/account/template.py | 12 +++- apps/accounts/models/template.py | 10 +--- apps/accounts/serializers/account/template.py | 31 +--------- apps/accounts/tasks/__init__.py | 7 ++- apps/accounts/tasks/template.py | 60 +++++++++++++++++++ 5 files changed, 80 insertions(+), 40 deletions(-) create mode 100644 apps/accounts/tasks/template.py diff --git a/apps/accounts/api/account/template.py b/apps/accounts/api/account/template.py index f9c3637f8..57d577b46 100644 --- a/apps/accounts/api/account/template.py +++ b/apps/accounts/api/account/template.py @@ -1,10 +1,12 @@ from django_filters import rest_framework as drf_filters +from rest_framework import status from rest_framework.decorators import action from rest_framework.response import Response from accounts import serializers -from accounts.models import AccountTemplate from accounts.mixins import AccountRecordViewLogMixin +from accounts.models import AccountTemplate +from accounts.tasks import template_sync_related_accounts from assets.const import Protocol from common.drf.filters import BaseFilterSet from common.permissions import UserConfirmation, ConfirmType @@ -44,6 +46,7 @@ class AccountTemplateViewSet(OrgBulkModelViewSet): } rbac_perms = { 'su_from_account_templates': 'accounts.view_accounttemplate', + 'sync_related_accounts': 'accounts.change_accounttemplate', } @action(methods=['get'], detail=False, url_path='su-from-account-templates') @@ -54,6 +57,13 @@ class AccountTemplateViewSet(OrgBulkModelViewSet): serializer = self.get_serializer(templates, many=True) return Response(data=serializer.data) + @action(methods=['patch'], detail=True, url_path='sync-related-accounts') + def sync_related_accounts(self, request, *args, **kwargs): + instance = self.get_object() + user_id = str(request.user.id) + task = template_sync_related_accounts.delay(str(instance.id), user_id) + return Response({'task': task.id}, status=status.HTTP_200_OK) + class AccountTemplateSecretsViewSet(AccountRecordViewLogMixin, AccountTemplateViewSet): serializer_classes = { diff --git a/apps/accounts/models/template.py b/apps/accounts/models/template.py index 0834dcb03..c56be1464 100644 --- a/apps/accounts/models/template.py +++ b/apps/accounts/models/template.py @@ -49,8 +49,7 @@ class AccountTemplate(BaseAccount, SecretWithRandomMixin): ).first() return account - @staticmethod - def bulk_update_accounts(accounts, data): + def bulk_update_accounts(self, accounts): history_model = Account.history.model account_ids = accounts.values_list('id', flat=True) history_accounts = history_model.objects.filter(id__in=account_ids) @@ -63,8 +62,7 @@ class AccountTemplate(BaseAccount, SecretWithRandomMixin): for account in accounts: account_id = str(account.id) account.version = account_id_count_map.get(account_id) + 1 - for k, v in data.items(): - setattr(account, k, v) + account.secret = self.get_secret() Account.objects.bulk_update(accounts, ['version', 'secret']) @staticmethod @@ -86,7 +84,5 @@ class AccountTemplate(BaseAccount, SecretWithRandomMixin): def bulk_sync_account_secret(self, accounts, user_id): """ 批量同步账号密码 """ - if not accounts: - return - self.bulk_update_accounts(accounts, {'secret': self.secret}) + self.bulk_update_accounts(accounts) self.bulk_create_history_accounts(accounts, user_id) diff --git a/apps/accounts/serializers/account/template.py b/apps/accounts/serializers/account/template.py index 89045627c..635c221bf 100644 --- a/apps/accounts/serializers/account/template.py +++ b/apps/accounts/serializers/account/template.py @@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from accounts.const import SecretStrategy, SecretType -from accounts.models import AccountTemplate, Account +from accounts.models import AccountTemplate from accounts.utils import SecretGenerator from common.serializers import SecretReadableMixin from common.serializers.fields import ObjectRelatedField @@ -18,9 +18,6 @@ class PasswordRulesSerializer(serializers.Serializer): class AccountTemplateSerializer(BaseAccountSerializer): - is_sync_account = serializers.BooleanField(default=False, write_only=True) - _is_sync_account = False - password_rules = PasswordRulesSerializer(required=False, label=_('Password rules')) su_from = ObjectRelatedField( required=False, queryset=AccountTemplate.objects, allow_null=True, @@ -32,7 +29,7 @@ class AccountTemplateSerializer(BaseAccountSerializer): fields = BaseAccountSerializer.Meta.fields + [ 'secret_strategy', 'password_rules', 'auto_push', 'push_params', 'platforms', - 'is_sync_account', 'su_from' + 'su_from' ] extra_kwargs = { 'secret_strategy': {'help_text': _('Secret generation strategy for account creation')}, @@ -46,17 +43,6 @@ class AccountTemplateSerializer(BaseAccountSerializer): }, } - def sync_accounts_secret(self, instance, diff): - if not self._is_sync_account or 'secret' not in diff: - return - query_data = { - 'source_id': instance.id, - 'username': instance.username, - 'secret_type': instance.secret_type - } - accounts = Account.objects.filter(**query_data) - instance.bulk_sync_account_secret(accounts, self.context['request'].user.id) - @staticmethod def generate_secret(attrs): secret_type = attrs.get('secret_type', SecretType.PASSWORD) @@ -68,23 +54,10 @@ class AccountTemplateSerializer(BaseAccountSerializer): attrs['secret'] = generator.get_secret() def validate(self, attrs): - self._is_sync_account = attrs.pop('is_sync_account', None) attrs = super().validate(attrs) self.generate_secret(attrs) return attrs - def update(self, instance, validated_data): - diff = { - k: v for k, v in validated_data.items() - if getattr(instance, k, None) != v - } - instance = super().update(instance, validated_data) - if {'username', 'secret_type'} & set(diff.keys()): - Account.objects.filter(source_id=instance.id).update(source_id=None) - else: - self.sync_accounts_secret(instance, diff) - return instance - class AccountTemplateSecretSerializer(SecretReadableMixin, AccountTemplateSerializer): class Meta(AccountTemplateSerializer.Meta): diff --git a/apps/accounts/tasks/__init__.py b/apps/accounts/tasks/__init__.py index 055508b24..8a79c1aa4 100644 --- a/apps/accounts/tasks/__init__.py +++ b/apps/accounts/tasks/__init__.py @@ -1,5 +1,6 @@ -from .backup_account import * from .automation import * -from .push_account import * -from .verify_account import * +from .backup_account import * from .gather_accounts import * +from .push_account import * +from .template import * +from .verify_account import * diff --git a/apps/accounts/tasks/template.py b/apps/accounts/tasks/template.py new file mode 100644 index 000000000..dbad2cf06 --- /dev/null +++ b/apps/accounts/tasks/template.py @@ -0,0 +1,60 @@ +from datetime import datetime + +from celery import shared_task +from django.shortcuts import get_object_or_404 +from django.utils.translation import gettext_lazy as _ + +from orgs.utils import tmp_to_root_org, tmp_to_org + + +@shared_task( + verbose_name=_('Template sync info to related accounts'), + activity_callback=lambda self, template_id, *args, **kwargs: (template_id, None) +) +def template_sync_related_accounts(template_id, user_id=None): + from accounts.models import Account, AccountTemplate + with tmp_to_root_org(): + template = get_object_or_404(AccountTemplate, id=template_id) + org_id = template.org_id + + with tmp_to_org(org_id): + accounts = Account.objects.filter(source_id=template_id) + if not accounts: + print('\033[35m>>> 没有需要同步的账号, 结束任务') + print('\033[0m') + return + + failed, succeeded = 0, 0 + succeeded_account_ids = [] + name = template.name + username = template.username + secret_type = template.secret_type + print(f'\033[32m>>> 开始同步模版名称、用户名、密钥类型到相关联的账号 ({datetime.now().strftime("%Y-%m-%d %H:%M:%S")})') + with tmp_to_org(org_id): + for account in accounts: + account.name = name + account.username = username + account.secret_type = secret_type + try: + account.save(update_fields=['name', 'username', 'secret_type']) + succeeded += 1 + succeeded_account_ids.append(account.id) + except Exception as e: + account.source_id = None + account.save(update_fields=['source_id']) + print(f'\033[31m- 同步失败: [{account}] 原因: [{e}]') + failed += 1 + accounts = Account.objects.filter(id__in=succeeded_account_ids) + if accounts: + print(f'\033[33m>>> 批量更新账号密文 ({datetime.now().strftime("%Y-%m-%d %H:%M:%S")})') + template.bulk_sync_account_secret(accounts, user_id) + + total = succeeded + failed + print( + f'\033[33m>>> 同步完成:, ' + f'共计: {total}, ' + f'成功: {succeeded}, ' + f'失败: {failed}, ' + f'({datetime.now().strftime("%Y-%m-%d %H:%M:%S")}) ' + ) + print('\033[0m') From 499eedd83ef6be737184f8d49be0a946eba595cb Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 9 Oct 2023 19:04:40 +0800 Subject: [PATCH 054/114] =?UTF-8?q?perf:=20=E4=BC=9A=E8=AF=9D=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20error=5Freason=20=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/const.py | 6 ++++++ .../migrations/0065_session_error_reason.py | 18 ++++++++++++++++++ apps/terminal/models/session/session.py | 1 + apps/terminal/serializers/session.py | 7 +++++-- 4 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 apps/terminal/migrations/0065_session_error_reason.py diff --git a/apps/terminal/const.py b/apps/terminal/const.py index c047343a4..87175d744 100644 --- a/apps/terminal/const.py +++ b/apps/terminal/const.py @@ -94,3 +94,9 @@ class TaskNameType(TextChoices): kill_session = "kill_session", _('Kill Session') lock_session = "lock_session", _('Lock Session') unlock_session = "unlock_session", _('Unlock Session') + + +class SessionErrorReason(TextChoices): + connect_failed = 'connect_failed', _('Connect Failed') + replay_create_failed = 'replay_create_failed', _('Replay Create Failed') + replay_upload_failed = 'replay_upload_failed', _('Replay Upload Failed') diff --git a/apps/terminal/migrations/0065_session_error_reason.py b/apps/terminal/migrations/0065_session_error_reason.py new file mode 100644 index 000000000..1a3bf8f67 --- /dev/null +++ b/apps/terminal/migrations/0065_session_error_reason.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.10 on 2023-10-10 06:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('terminal', '0064_auto_20230728_1001'), + ] + + operations = [ + migrations.AddField( + model_name='session', + name='error_reason', + field=models.CharField(blank=True, max_length=128, verbose_name='Error reason'), + ), + ] diff --git a/apps/terminal/models/session/session.py b/apps/terminal/models/session/session.py index 09f2df9e1..8530c3c63 100644 --- a/apps/terminal/models/session/session.py +++ b/apps/terminal/models/session/session.py @@ -45,6 +45,7 @@ class Session(OrgModelMixin): date_end = models.DateTimeField(verbose_name=_("Date end"), null=True) comment = models.TextField(blank=True, null=True, verbose_name=_("Comment")) cmd_amount = models.IntegerField(default=-1, verbose_name=_("Command amount")) + error_reason = models.CharField(max_length=128, blank=True, verbose_name=_("Error reason")) upload_to = 'replay' ACTIVE_CACHE_KEY_PREFIX = 'SESSION_ACTIVE_{}' diff --git a/apps/terminal/serializers/session.py b/apps/terminal/serializers/session.py index 6e5476c3e..ba8ecfdf8 100644 --- a/apps/terminal/serializers/session.py +++ b/apps/terminal/serializers/session.py @@ -5,7 +5,7 @@ from common.serializers.fields import LabeledChoiceField from common.utils import pretty_string from orgs.mixins.serializers import BulkOrgResourceModelSerializer from .terminal import TerminalSmallSerializer -from ..const import SessionType +from ..const import SessionType, SessionErrorReason from ..models import Session __all__ = [ @@ -24,6 +24,9 @@ class SessionSerializer(BulkOrgResourceModelSerializer): can_join = serializers.BooleanField(read_only=True, label=_("Can join")) can_terminate = serializers.BooleanField(read_only=True, label=_("Can terminate")) asset = serializers.CharField(label=_("Asset"), style={'base_template': 'textarea.html'}) + error_reason = LabeledChoiceField( + choices=SessionErrorReason.choices, label=_("Error reason"), required=False + ) class Meta: model = Session @@ -33,7 +36,7 @@ class SessionSerializer(BulkOrgResourceModelSerializer): "protocol", 'type', "login_from", "remote_addr", "is_success", "is_finished", "has_replay", "has_command", "date_start", "date_end", "comment", "terminal_display", "is_locked", - 'command_amount', + 'command_amount', 'error_reason' ] fields_fk = ["terminal", ] fields_custom = ["can_replay", "can_join", "can_terminate"] From fd6a8dd807df5217e02fc0d96181f77c909219ad Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 10 Oct 2023 18:07:35 +0800 Subject: [PATCH 055/114] =?UTF-8?q?perf:=20=E5=A2=9E=E5=8A=A0=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/const.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/terminal/const.py b/apps/terminal/const.py index 87175d744..0691db03a 100644 --- a/apps/terminal/const.py +++ b/apps/terminal/const.py @@ -100,3 +100,4 @@ class SessionErrorReason(TextChoices): connect_failed = 'connect_failed', _('Connect Failed') replay_create_failed = 'replay_create_failed', _('Replay Create Failed') replay_upload_failed = 'replay_upload_failed', _('Replay Upload Failed') + replay_convert_failed = 'replay_convert_failed', _('Replay Convert Failed') From e8652af054b71a74369e2263dc43bd5f700dd438 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 11 Oct 2023 10:31:57 +0800 Subject: [PATCH 056/114] =?UTF-8?q?perf:=20=E6=9B=B4=E6=96=B0=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/const.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/terminal/const.py b/apps/terminal/const.py index 0691db03a..9f0b8a947 100644 --- a/apps/terminal/const.py +++ b/apps/terminal/const.py @@ -86,18 +86,18 @@ class SessionType(TextChoices): class ActionPermission(TextChoices): - readonly = "readonly", _('Read Only') + readonly = "readonly", _('Read only') writable = "writable", _('Writable') class TaskNameType(TextChoices): - kill_session = "kill_session", _('Kill Session') - lock_session = "lock_session", _('Lock Session') - unlock_session = "unlock_session", _('Unlock Session') + kill_session = "kill_session", _('Kill session') + lock_session = "lock_session", _('Lock session') + unlock_session = "unlock_session", _('Unlock session') class SessionErrorReason(TextChoices): - connect_failed = 'connect_failed', _('Connect Failed') - replay_create_failed = 'replay_create_failed', _('Replay Create Failed') - replay_upload_failed = 'replay_upload_failed', _('Replay Upload Failed') - replay_convert_failed = 'replay_convert_failed', _('Replay Convert Failed') + connect_failed = 'connect_failed', _('Connect failed') + replay_create_failed = 'replay_create_failed', _('Replay create failed') + replay_upload_failed = 'replay_upload_failed', _('Replay upload failed') + replay_convert_failed = 'replay_convert_failed', _('Replay convert failed') From 443f6d25e8dfae53a8c4538e82fa6a51408d0e0f Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 11 Oct 2023 11:01:24 +0800 Subject: [PATCH 057/114] =?UTF-8?q?perf:=20Windows=20=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20rdping=20=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0125_auto_20231011_1053.py | 20 +++++++++++++++++++ apps/audits/api.py | 1 - 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 apps/assets/migrations/0125_auto_20231011_1053.py diff --git a/apps/assets/migrations/0125_auto_20231011_1053.py b/apps/assets/migrations/0125_auto_20231011_1053.py new file mode 100644 index 000000000..16313d0cb --- /dev/null +++ b/apps/assets/migrations/0125_auto_20231011_1053.py @@ -0,0 +1,20 @@ +# Generated by Django 4.1.10 on 2023-10-11 02:53 + +from django.db import migrations + + +def change_windows_ping_method(apps, schema_editor): + platform_automation_cls = apps.get_model('assets', 'PlatformAutomation') + automations = platform_automation_cls.objects.filter(platform__name__in=['Windows', 'Windows2016']) + automations.update(ping_method='ping_by_rdp') + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0124_auto_20231007_1437'), + ] + + operations = [ + migrations.RunPython(change_windows_ping_method) + ] diff --git a/apps/audits/api.py b/apps/audits/api.py index 17a58b2f2..9caed8a0c 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -259,7 +259,6 @@ class UserSessionViewSet(CommonApiMixin, viewsets.ModelViewSet): serializer_class = UserSessionSerializer filterset_fields = ['id', 'ip', 'city', 'type'] search_fields = ['id', 'ip', 'city'] - rbac_perms = { 'offline': ['users.offline_usersession'] } From 7cd2736e82edd2fa94493d31ff1b430102b92bf6 Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 11 Oct 2023 17:46:37 +0800 Subject: [PATCH 058/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BC=A0=E9=80=92=E7=9A=84=20phone=20=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/serializers/fields.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/common/serializers/fields.py b/apps/common/serializers/fields.py index 844c424e5..5fe019c65 100644 --- a/apps/common/serializers/fields.py +++ b/apps/common/serializers/fields.py @@ -218,12 +218,13 @@ class PhoneField(serializers.CharField): code = data.get('code') phone = data.get('phone', '') if code and phone: - data = '{}{}'.format(code, phone) + code = code.replace('+', '') + data = '+{}{}'.format(code, phone) else: data = phone try: phone = phonenumbers.parse(data, 'CN') - data = '{}{}'.format(phone.country_code, phone.national_number) + data = '+{}{}'.format(phone.country_code, phone.national_number) except phonenumbers.NumberParseException: data = '+86{}'.format(data) From 38aa828eb87e650cf1df468da3a35261eb52d6c7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 11 Oct 2023 17:48:58 +0800 Subject: [PATCH 059/114] =?UTF-8?q?perf:=20passkey=20=E5=8F=AA=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E6=9C=AC=E5=9C=B0=E7=94=A8=E6=88=B7=E5=BC=80=E5=90=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/backends/passkey/api.py | 2 ++ apps/authentication/backends/passkey/backends.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/apps/authentication/backends/passkey/api.py b/apps/authentication/backends/passkey/api.py index 8f5414122..644636dd2 100644 --- a/apps/authentication/backends/passkey/api.py +++ b/apps/authentication/backends/passkey/api.py @@ -22,6 +22,8 @@ class PasskeyViewSet(AuthMixin, FlashMessageMixin, ModelViewSet): @action(methods=['get', 'post'], detail=False, url_path='register') def register(self, request): + if request.user.source != 'local': + return JsonResponse({'error': _('Only register passkey for local user')}, status=400) if request.method == 'GET': register_data, state = register_begin(request) return JsonResponse(dict(register_data)) diff --git a/apps/authentication/backends/passkey/backends.py b/apps/authentication/backends/passkey/backends.py index dc7e1349b..4be1687d2 100644 --- a/apps/authentication/backends/passkey/backends.py +++ b/apps/authentication/backends/passkey/backends.py @@ -7,3 +7,6 @@ class PasskeyAuthBackend(JMSModelBackend): @staticmethod def is_enabled(): return settings.AUTH_PASSKEY + + def user_can_authenticate(self, user): + return user.source == 'local' From 614e019f147759cbe6b7129b333ffe41a20192f6 Mon Sep 17 00:00:00 2001 From: Bai Date: Thu, 12 Oct 2023 10:14:24 +0800 Subject: [PATCH 060/114] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E6=96=87=E4=BB=B6choices=E5=92=8C=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0023_auto_20230906_1322.py | 2 +- apps/locale/ja/LC_MESSAGES/django.po | 252 +++++++++--------- apps/locale/zh/LC_MESSAGES/django.po | 247 ++++++++--------- .../migrations/0064_auto_20230728_1001.py | 2 +- 4 files changed, 250 insertions(+), 253 deletions(-) diff --git a/apps/audits/migrations/0023_auto_20230906_1322.py b/apps/audits/migrations/0023_auto_20230906_1322.py index 286ff0df5..9f4d0553d 100644 --- a/apps/audits/migrations/0023_auto_20230906_1322.py +++ b/apps/audits/migrations/0023_auto_20230906_1322.py @@ -19,7 +19,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='operatelog', name='action', - field=models.CharField(choices=[('view', 'View'), ('update', 'Update'), ('delete', 'Delete'), ('create', 'Create'), ('download', 'Download'), ('connect', 'Connect'), ('login', 'Login'), ('change_password', 'Change password'), ('reject', 'Reject'), ('accept', 'Accept'), ('review', 'Review')], max_length=16, verbose_name='Action'), + field=models.CharField(choices=[('view', 'View'), ('update', 'Update'), ('delete', 'Delete'), ('create', 'Create'), ('download', 'Download'), ('connect', 'Connect'), ('login', 'Login'), ('change_password', 'Change password'), ('reject', 'Reject'), ('accept', 'Accept'), ('review', 'Review'), ('notice', 'Notifications')], max_length=16, verbose_name='Action'), ), migrations.AlterField( model_name='userloginlog', diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 98a9685e6..4ba81a5a4 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: 2023-10-10 11:13+0800\n" +"POT-Creation-Date: 2023-10-12 09:51+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -240,7 +240,7 @@ msgstr "資産" #: accounts/models/account.py:52 accounts/models/template.py:15 #: accounts/serializers/account/account.py:216 #: accounts/serializers/account/account.py:264 -#: accounts/serializers/account/template.py:27 +#: accounts/serializers/account/template.py:24 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" msgstr "から切り替え" @@ -335,7 +335,7 @@ msgstr "アカウントのバックアップスナップショット" msgid "Trigger mode" msgstr "トリガーモード" -#: accounts/models/automations/backup_account.py:105 audits/models.py:197 +#: accounts/models/automations/backup_account.py:105 audits/models.py:201 #: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "理由" @@ -343,7 +343,7 @@ msgstr "理由" #: accounts/models/automations/backup_account.py:107 #: accounts/serializers/automations/change_secret.py:110 #: accounts/serializers/automations/change_secret.py:133 -#: ops/serializers/job.py:56 terminal/serializers/session.py:46 +#: ops/serializers/job.py:56 terminal/serializers/session.py:49 msgid "Is success" msgstr "成功は" @@ -445,7 +445,7 @@ msgstr "最終ログイン日" #: accounts/serializers/account/virtual.py:21 acls/serializers/base.py:19 #: acls/serializers/base.py:50 acls/templates/acls/asset_login_reminder.html:5 #: acls/templates/acls/user_login_reminder.html:5 assets/models/_user.py:23 -#: audits/models.py:182 authentication/forms.py:25 authentication/forms.py:27 +#: audits/models.py:186 authentication/forms.py:25 authentication/forms.py:27 #: authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 @@ -514,7 +514,7 @@ msgstr "ひみつ" msgid "Secret strategy" msgstr "鍵ポリシー" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:24 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:21 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "パスワードルール" @@ -740,7 +740,7 @@ msgstr "ID" #: accounts/serializers/account/account.py:447 acls/serializers/base.py:116 #: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:52 -#: audits/models.py:88 audits/models.py:166 audits/models.py:264 +#: audits/models.py:88 audits/models.py:170 audits/models.py:268 #: audits/serializers.py:171 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 @@ -821,15 +821,15 @@ msgstr "数値#スウスウ#" msgid "Special symbol" msgstr "特殊記号" -#: accounts/serializers/account/template.py:38 +#: accounts/serializers/account/template.py:35 msgid "Secret generation strategy for account creation" msgstr "账号创建时,密文生成策略" -#: accounts/serializers/account/template.py:39 +#: accounts/serializers/account/template.py:36 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:42 +#: accounts/serializers/account/template.py:39 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -933,6 +933,10 @@ msgstr "資産の口座番号を収集する" msgid "Push accounts to assets" msgstr "アカウントをアセットにプッシュ:" +#: accounts/tasks/template.py:11 +msgid "Template sync info to related accounts" +msgstr "" + #: accounts/tasks/vault.py:31 msgid "Sync secret to vault" msgstr "秘密をVaultに同期する" @@ -1184,15 +1188,15 @@ msgstr "" msgid "Thank you" msgstr "ありがとうございます。" -#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:188 -#: audits/models.py:257 +#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:192 +#: audits/models.py:261 #: authentication/templates/authentication/_msg_different_city.html:11 #: tickets/models/ticket/login_confirm.py:11 msgid "Login city" msgstr "ログイン都市" -#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:191 -#: audits/models.py:258 audits/serializers.py:65 +#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:195 +#: audits/models.py:262 audits/serializers.py:65 msgid "User agent" msgstr "ユーザーエージェント" @@ -1281,7 +1285,7 @@ msgid "Authentication failed" msgstr "認証に失敗しました" #: assets/automations/ping_gateway/manager.py:60 -#: assets/automations/ping_gateway/manager.py:86 +#: assets/automations/ping_gateway/manager.py:86 terminal/const.py:100 msgid "Connect failed" msgstr "接続に失敗しました" @@ -1527,7 +1531,7 @@ msgstr "SSHパブリックキー" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 -#: audits/models.py:261 common/db/models.py:34 ops/models/base.py:54 +#: audits/models.py:265 common/db/models.py:34 ops/models/base.py:54 #: ops/models/job.py:227 users/models/user.py:1024 msgid "Date created" msgstr "作成された日付" @@ -1567,7 +1571,7 @@ msgstr "ユーザーと同じユーザー名" #: assets/models/_user.py:52 authentication/models/connection_token.py:41 #: authentication/serializers/connect_token_secret.py:111 #: terminal/models/applet/applet.py:42 terminal/serializers/session.py:19 -#: terminal/serializers/session.py:42 terminal/serializers/storage.py:70 +#: terminal/serializers/session.py:45 terminal/serializers/storage.py:70 msgid "Protocol" msgstr "プロトコル" @@ -1704,7 +1708,7 @@ msgstr "自動化されたタスク" msgid "Asset automation task" msgstr "アセットの自動化タスク" -#: assets/models/automations/base.py:113 audits/models.py:202 +#: assets/models/automations/base.py:113 audits/models.py:206 #: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220 #: terminal/models/applet/applet.py:301 terminal/models/applet/host.py:139 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 @@ -2302,7 +2306,7 @@ msgstr "パスワードを変更する" #: audits/const.py:41 settings/serializers/terminal.py:6 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 -#: terminal/serializers/session.py:49 terminal/serializers/session.py:63 +#: terminal/serializers/session.py:52 terminal/serializers/session.py:66 msgid "Terminal" msgstr "ターミナル" @@ -2339,7 +2343,7 @@ msgstr "否" msgid "Job audit log" msgstr "ジョブ監査ログ" -#: audits/models.py:54 audits/models.py:98 audits/models.py:169 +#: audits/models.py:54 audits/models.py:98 audits/models.py:173 #: terminal/models/session/session.py:38 terminal/models/session/sharing.py:113 msgid "Remote addr" msgstr "リモートaddr" @@ -2378,7 +2382,7 @@ msgstr "リソースタイプ" msgid "Resource" msgstr "リソース" -#: audits/models.py:99 audits/models.py:145 audits/models.py:171 +#: audits/models.py:99 audits/models.py:145 audits/models.py:175 #: terminal/serializers/command.py:75 msgid "Datetime" msgstr "時間" @@ -2399,59 +2403,59 @@ msgstr "詳細 ID" msgid "Activity log" msgstr "活動記録" -#: audits/models.py:167 +#: audits/models.py:171 msgid "Change by" msgstr "による変更" -#: audits/models.py:177 +#: audits/models.py:181 msgid "Password change log" msgstr "パスワード変更ログ" -#: audits/models.py:184 audits/models.py:259 +#: audits/models.py:188 audits/models.py:263 msgid "Login type" msgstr "ログインタイプ" -#: audits/models.py:186 audits/models.py:255 +#: audits/models.py:190 audits/models.py:259 #: tickets/models/ticket/login_confirm.py:10 msgid "Login IP" msgstr "ログインIP" -#: audits/models.py:194 audits/serializers.py:49 +#: audits/models.py:198 audits/serializers.py:49 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: users/forms/profile.py:65 users/models/user.py:815 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" -#: audits/models.py:204 +#: audits/models.py:208 msgid "Date login" msgstr "日付ログイン" -#: audits/models.py:206 audits/models.py:260 audits/serializers.py:67 +#: audits/models.py:210 audits/models.py:264 audits/serializers.py:67 #: audits/serializers.py:183 msgid "Authentication backend" msgstr "認証バックエンド" -#: audits/models.py:250 +#: audits/models.py:254 msgid "User login log" msgstr "ユーザーログインログ" -#: audits/models.py:256 +#: audits/models.py:260 msgid "Session key" msgstr "セッションID" -#: audits/models.py:262 authentication/models/connection_token.py:47 +#: audits/models.py:266 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 #: tickets/models/ticket/apply_application.py:31 #: tickets/models/ticket/apply_asset.py:20 users/models/user.py:833 msgid "Date expired" msgstr "期限切れの日付" -#: audits/models.py:288 +#: audits/models.py:292 msgid "User session" msgstr "ユーザーセッション" -#: audits/models.py:290 +#: audits/models.py:294 msgid "Offline ussr session" msgstr "ユーザー・セッションの下限" @@ -2531,6 +2535,10 @@ msgstr "監査セッション タスク ログのクリーンアップ" msgid "Upload FTP file to external storage" msgstr "外部ストレージへのFTPファイルのアップロード" +#: authentication/api/access_key.py:36 +msgid "Access keys can be created at most 10" +msgstr "" + #: authentication/api/confirm.py:40 msgid "This action require verify your MFA" msgstr "この操作には、MFAを検証する必要があります" @@ -2618,7 +2626,11 @@ msgstr "" msgid "Invalid token or cache refreshed." msgstr "無効なトークンまたはキャッシュの更新。" -#: authentication/backends/passkey/api.py:52 +#: authentication/backends/passkey/api.py:26 +msgid "Only register passkey for local user" +msgstr "" + +#: authentication/backends/passkey/api.py:54 msgid "Auth failed" msgstr "認証に失敗しました" @@ -2910,7 +2922,7 @@ msgstr "電話番号を設定して有効にする" msgid "Clear phone number to disable" msgstr "無効にする電話番号をクリアする" -#: authentication/middleware.py:93 settings/utils/ldap.py:661 +#: authentication/middleware.py:93 settings/utils/ldap.py:679 msgid "Authentication failed (before login check failed): {}" msgstr "認証に失敗しました (ログインチェックが失敗する前): {}" @@ -3775,6 +3787,7 @@ msgid "The mobile phone number format is incorrect" msgstr "携帯電話番号の形式が正しくありません" #: jumpserver/conf.py:444 +#, python-brace-format msgid "The verification code is: {code}" msgstr "認証コードは: {code}" @@ -4168,7 +4181,7 @@ msgstr "保存後に実行" msgid "Job type" msgstr "タスクの種類" -#: ops/serializers/job.py:57 terminal/serializers/session.py:50 +#: ops/serializers/job.py:57 terminal/serializers/session.py:53 msgid "Is finished" msgstr "終了しました" @@ -5766,100 +5779,100 @@ msgstr "LDAP ユーザーを定期的にインポートする" msgid "Registration periodic import ldap user task" msgstr "登録サイクルLDAPユーザータスクのインポート" -#: settings/utils/ldap.py:476 +#: settings/utils/ldap.py:494 msgid "ldap:// or ldaps:// protocol is used." msgstr "ldap:// または ldaps:// プロトコルが使用されます。" -#: settings/utils/ldap.py:487 +#: settings/utils/ldap.py:505 msgid "Host or port is disconnected: {}" msgstr "ホストまたはポートが切断されました: {}" -#: settings/utils/ldap.py:489 +#: settings/utils/ldap.py:507 msgid "The port is not the port of the LDAP service: {}" msgstr "ポートはLDAPサービスのポートではありません: {}" -#: settings/utils/ldap.py:491 +#: settings/utils/ldap.py:509 msgid "Please add certificate: {}" msgstr "証明書を追加してください: {}" -#: settings/utils/ldap.py:495 settings/utils/ldap.py:522 -#: settings/utils/ldap.py:552 settings/utils/ldap.py:580 +#: settings/utils/ldap.py:513 settings/utils/ldap.py:540 +#: settings/utils/ldap.py:570 settings/utils/ldap.py:598 msgid "Unknown error: {}" msgstr "不明なエラー: {}" -#: settings/utils/ldap.py:509 +#: settings/utils/ldap.py:527 msgid "Bind DN or Password incorrect" msgstr "DNまたはパスワードのバインドが正しくありません" -#: settings/utils/ldap.py:516 +#: settings/utils/ldap.py:534 msgid "Please enter Bind DN: {}" msgstr "バインドDN: {} を入力してください" -#: settings/utils/ldap.py:518 +#: settings/utils/ldap.py:536 msgid "Please enter Password: {}" msgstr "パスワードを入力してください: {}" -#: settings/utils/ldap.py:520 +#: settings/utils/ldap.py:538 msgid "Please enter correct Bind DN and Password: {}" msgstr "正しいバインドDNとパスワードを入力してください: {}" -#: settings/utils/ldap.py:538 +#: settings/utils/ldap.py:556 msgid "Invalid User OU or User search filter: {}" msgstr "無効なユーザー OU またはユーザー検索フィルター: {}" -#: settings/utils/ldap.py:569 +#: settings/utils/ldap.py:587 msgid "LDAP User attr map not include: {}" msgstr "LDAP ユーザーattrマップは含まれません: {}" -#: settings/utils/ldap.py:576 +#: settings/utils/ldap.py:594 msgid "LDAP User attr map is not dict" msgstr "LDAPユーザーattrマップはdictではありません" -#: settings/utils/ldap.py:595 +#: settings/utils/ldap.py:613 msgid "LDAP authentication is not enabled" msgstr "LDAP 認証が有効になっていない" -#: settings/utils/ldap.py:613 +#: settings/utils/ldap.py:631 msgid "Error (Invalid LDAP server): {}" msgstr "エラー (LDAPサーバーが無効): {}" -#: settings/utils/ldap.py:615 +#: settings/utils/ldap.py:633 msgid "Error (Invalid Bind DN): {}" msgstr "エラー (DNのバインドが無効): {}" -#: settings/utils/ldap.py:617 +#: settings/utils/ldap.py:635 msgid "Error (Invalid LDAP User attr map): {}" msgstr "エラー (LDAPユーザーattrマップが無効): {}" -#: settings/utils/ldap.py:619 +#: settings/utils/ldap.py:637 msgid "Error (Invalid User OU or User search filter): {}" msgstr "エラー (ユーザーOUまたはユーザー検索フィルターが無効): {}" -#: settings/utils/ldap.py:621 +#: settings/utils/ldap.py:639 msgid "Error (Not enabled LDAP authentication): {}" msgstr "エラー (LDAP認証が有効化されていません): {}" -#: settings/utils/ldap.py:623 +#: settings/utils/ldap.py:641 msgid "Error (Unknown): {}" msgstr "エラー (不明): {}" -#: settings/utils/ldap.py:626 +#: settings/utils/ldap.py:644 msgid "Succeed: Match {} s user" msgstr "成功: {} 人のユーザーに一致" -#: settings/utils/ldap.py:659 +#: settings/utils/ldap.py:677 msgid "Authentication failed (configuration incorrect): {}" msgstr "認証に失敗しました (設定が正しくありません): {}" -#: settings/utils/ldap.py:663 +#: settings/utils/ldap.py:681 msgid "Authentication failed (username or password incorrect): {}" msgstr "認証に失敗しました (ユーザー名またはパスワードが正しくありません): {}" -#: settings/utils/ldap.py:665 +#: settings/utils/ldap.py:683 msgid "Authentication failed (Unknown): {}" msgstr "認証に失敗しました (不明): {}" -#: settings/utils/ldap.py:668 +#: settings/utils/ldap.py:686 msgid "Authentication success: {}" msgstr "認証成功: {}" @@ -6218,7 +6231,7 @@ msgid "SFTP" msgstr "SFTP" #: terminal/const.py:89 -msgid "Read Only" +msgid "Read only" msgstr "読み取り専用" #: terminal/const.py:90 @@ -6226,17 +6239,29 @@ msgid "Writable" msgstr "書き込み可能" #: terminal/const.py:94 -msgid "Kill Session" +msgid "Kill session" msgstr "セッションを終了する" #: terminal/const.py:95 -msgid "Lock Session" +msgid "Lock session" msgstr "セッションをロックする" #: terminal/const.py:96 -msgid "Unlock Session" +msgid "Unlock session" msgstr "セッションのロックを解除する" +#: terminal/const.py:101 +msgid "Replay create failed" +msgstr "ビデオの作成に失敗しました" + +#: terminal/const.py:102 +msgid "Replay upload failed" +msgstr "動画のアップロードに失敗しました" + +#: terminal/const.py:103 +msgid "Replay convert failed" +msgstr "ビデオのトランスコーディングに失敗しました" + #: terminal/exceptions.py:8 msgid "Bulk create not support" msgstr "一括作成非サポート" @@ -6460,27 +6485,31 @@ msgstr "リプレイ" msgid "Date end" msgstr "終了日" -#: terminal/models/session/session.py:47 terminal/serializers/session.py:62 +#: terminal/models/session/session.py:47 terminal/serializers/session.py:65 msgid "Command amount" msgstr "コマンド量" -#: terminal/models/session/session.py:281 +#: terminal/models/session/session.py:48 terminal/serializers/session.py:28 +msgid "Error reason" +msgstr "間違った理由" + +#: terminal/models/session/session.py:282 msgid "Session record" msgstr "セッション記録" -#: terminal/models/session/session.py:283 +#: terminal/models/session/session.py:284 msgid "Can monitor session" msgstr "セッションを監視できます" -#: terminal/models/session/session.py:284 +#: terminal/models/session/session.py:285 msgid "Can share session" msgstr "セッションを共有できます" -#: terminal/models/session/session.py:285 +#: terminal/models/session/session.py:286 msgid "Can terminate session" msgstr "セッションを終了できます" -#: terminal/models/session/session.py:286 +#: terminal/models/session/session.py:287 msgid "Can validate session action perm" msgstr "セッションアクションのパーマを検証できます" @@ -6735,31 +6764,31 @@ msgstr "" msgid "Asset IP" msgstr "資産 IP" -#: terminal/serializers/session.py:23 terminal/serializers/session.py:47 +#: terminal/serializers/session.py:23 terminal/serializers/session.py:50 msgid "Can replay" msgstr "再生できます" -#: terminal/serializers/session.py:24 terminal/serializers/session.py:48 +#: terminal/serializers/session.py:24 terminal/serializers/session.py:51 msgid "Can join" msgstr "参加できます" -#: terminal/serializers/session.py:25 terminal/serializers/session.py:51 +#: terminal/serializers/session.py:25 terminal/serializers/session.py:54 msgid "Can terminate" msgstr "終了できます" -#: terminal/serializers/session.py:43 +#: terminal/serializers/session.py:46 msgid "User ID" msgstr "ユーザーID" -#: terminal/serializers/session.py:44 +#: terminal/serializers/session.py:47 msgid "Asset ID" msgstr "資産ID" -#: terminal/serializers/session.py:45 +#: terminal/serializers/session.py:48 msgid "Login from display" msgstr "表示からのログイン" -#: terminal/serializers/session.py:52 +#: terminal/serializers/session.py:55 msgid "Terminal display" msgstr "ターミナルディスプレイ" @@ -7485,10 +7514,8 @@ msgid "File name conflict resolution" msgstr "ファイル名競合ソリューション" #: users/serializers/preference/koko.py:14 -#, fuzzy -#| msgid "Terminal setting" msgid "Terminal theme name" -msgstr "ターミナル設定" +msgstr "ターミナルテーマ名" #: users/serializers/preference/lina.py:13 msgid "New file encryption password" @@ -7619,26 +7646,30 @@ msgstr "" "管理者は「既存のユーザーのみログインを許可」をオンにしており、現在のユーザー" "はユーザーリストにありません。管理者に連絡してください。" -#: users/tasks.py:23 +#: users/tasks.py:25 msgid "Check password expired" msgstr "パスワードの有効期限が切れていることを確認する" -#: users/tasks.py:37 +#: users/tasks.py:39 msgid "Periodic check password expired" msgstr "定期認証パスワードの有効期限" -#: users/tasks.py:51 +#: users/tasks.py:53 msgid "Check user expired" msgstr "ユーザーの有効期限が切れていることを確認する" -#: users/tasks.py:68 +#: users/tasks.py:70 msgid "Periodic check user expired" msgstr "ユーザーの有効期限の定期的な検出" -#: users/tasks.py:82 +#: users/tasks.py:84 msgid "Check unused users" msgstr "未使用のユーザーを確認する" +#: users/tasks.py:114 +msgid "The user has not logged in recently and has been disabled." +msgstr "ユーザーは最近ログインしておらず、無効になっています。" + #: users/templates/users/_msg_account_expire_reminder.html:7 msgid "Your account will expire in" msgstr "アカウントの有効期限は" @@ -8114,16 +8145,16 @@ msgid "Task strategy" msgstr "ミッション戦略です" #: xpack/plugins/cloud/models.py:285 -msgid "Exact" -msgstr "" +msgid "Equal" +msgstr "等しい" #: xpack/plugins/cloud/models.py:286 -msgid "Not" -msgstr "否" +msgid "Not equal" +msgstr "不等于" #: xpack/plugins/cloud/models.py:287 msgid "In" -msgstr "イン" +msgstr "で..." #: xpack/plugins/cloud/models.py:288 msgid "Contains" @@ -8131,11 +8162,11 @@ msgstr "含む" #: xpack/plugins/cloud/models.py:289 msgid "Startswith" -msgstr "始まる" +msgstr "始まる..." #: xpack/plugins/cloud/models.py:290 msgid "Endswith" -msgstr "終わる" +msgstr "終わる..." #: xpack/plugins/cloud/models.py:296 msgid "Instance platform" @@ -8550,38 +8581,3 @@ msgstr "エンタープライズプロフェッショナル版" #: xpack/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" - -#~ msgid "Invalid signature header. No credentials provided." -#~ msgstr "署名ヘッダーが無効です。資格情報は提供されていません。" - -#~ msgid "" -#~ "Invalid signature header. Signature string should not contain spaces." -#~ msgstr "" -#~ "署名ヘッダーが無効です。署名文字列にはスペースを含まないでください。" - -#~ msgid "Invalid signature header. Format like AccessKeyId:Signature" -#~ msgstr "署名ヘッダーが無効です。AccessKeyIdのような形式: Signature" - -#~ msgid "" -#~ "Invalid signature header. Signature string should not contain invalid " -#~ "characters." -#~ msgstr "" -#~ "署名ヘッダーが無効です。署名文字列に無効な文字を含めることはできません。" - -#~ msgid "Invalid signature." -#~ msgstr "署名が無効です。" - -#~ msgid "HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT" -#~ msgstr "HTTP header: Date not provide or not" - -#~ msgid "Expired, more than 15 minutes" -#~ msgstr "期限切れ、15分以上" - -#~ msgid "User disabled." -#~ msgstr "ユーザーが無効になりました。" - -#~ msgid "Random" -#~ msgstr "ランダム" - -#~ msgid "Enterprise edition" -#~ msgstr "エンタープライズ基本版" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index be805ccf0..5099f0a86 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: 2023-10-10 11:13+0800\n" +"POT-Creation-Date: 2023-10-12 09:51+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -239,7 +239,7 @@ msgstr "资产" #: accounts/models/account.py:52 accounts/models/template.py:15 #: accounts/serializers/account/account.py:216 #: accounts/serializers/account/account.py:264 -#: accounts/serializers/account/template.py:27 +#: accounts/serializers/account/template.py:24 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" msgstr "切换自" @@ -334,7 +334,7 @@ msgstr "账号备份快照" msgid "Trigger mode" msgstr "触发模式" -#: accounts/models/automations/backup_account.py:105 audits/models.py:197 +#: accounts/models/automations/backup_account.py:105 audits/models.py:201 #: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "原因" @@ -342,7 +342,7 @@ msgstr "原因" #: accounts/models/automations/backup_account.py:107 #: accounts/serializers/automations/change_secret.py:110 #: accounts/serializers/automations/change_secret.py:133 -#: ops/serializers/job.py:56 terminal/serializers/session.py:46 +#: ops/serializers/job.py:56 terminal/serializers/session.py:49 msgid "Is success" msgstr "是否成功" @@ -444,7 +444,7 @@ msgstr "最后登录日期" #: accounts/serializers/account/virtual.py:21 acls/serializers/base.py:19 #: acls/serializers/base.py:50 acls/templates/acls/asset_login_reminder.html:5 #: acls/templates/acls/user_login_reminder.html:5 assets/models/_user.py:23 -#: audits/models.py:182 authentication/forms.py:25 authentication/forms.py:27 +#: audits/models.py:186 authentication/forms.py:25 authentication/forms.py:27 #: authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 @@ -513,7 +513,7 @@ msgstr "密钥" msgid "Secret strategy" msgstr "密文策略" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:24 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:21 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "密码规则" @@ -738,7 +738,7 @@ msgstr "ID" #: accounts/serializers/account/account.py:447 acls/serializers/base.py:116 #: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:52 -#: audits/models.py:88 audits/models.py:166 audits/models.py:264 +#: audits/models.py:88 audits/models.py:170 audits/models.py:268 #: audits/serializers.py:171 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 @@ -819,15 +819,15 @@ msgstr "数字" msgid "Special symbol" msgstr "特殊字符" -#: accounts/serializers/account/template.py:38 +#: accounts/serializers/account/template.py:35 msgid "Secret generation strategy for account creation" msgstr "密码生成策略,用于账号创建时,设置密码" -#: accounts/serializers/account/template.py:39 +#: accounts/serializers/account/template.py:36 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:42 +#: accounts/serializers/account/template.py:39 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -930,6 +930,10 @@ msgstr "收集资产上的账号" msgid "Push accounts to assets" msgstr "推送账号到资产" +#: accounts/tasks/template.py:11 +msgid "Template sync info to related accounts" +msgstr "" + #: accounts/tasks/vault.py:31 msgid "Sync secret to vault" msgstr "同步密文到 vault" @@ -1179,15 +1183,15 @@ msgstr "" msgid "Thank you" msgstr "谢谢" -#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:188 -#: audits/models.py:257 +#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:192 +#: audits/models.py:261 #: authentication/templates/authentication/_msg_different_city.html:11 #: tickets/models/ticket/login_confirm.py:11 msgid "Login city" msgstr "登录城市" -#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:191 -#: audits/models.py:258 audits/serializers.py:65 +#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:195 +#: audits/models.py:262 audits/serializers.py:65 msgid "User agent" msgstr "用户代理" @@ -1273,7 +1277,7 @@ msgid "Authentication failed" msgstr "认证失败" #: assets/automations/ping_gateway/manager.py:60 -#: assets/automations/ping_gateway/manager.py:86 +#: assets/automations/ping_gateway/manager.py:86 terminal/const.py:100 msgid "Connect failed" msgstr "连接失败" @@ -1519,7 +1523,7 @@ msgstr "SSH公钥" # msgstr "备注" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 -#: audits/models.py:261 common/db/models.py:34 ops/models/base.py:54 +#: audits/models.py:265 common/db/models.py:34 ops/models/base.py:54 #: ops/models/job.py:227 users/models/user.py:1024 msgid "Date created" msgstr "创建日期" @@ -1559,7 +1563,7 @@ msgstr "用户名与用户相同" #: assets/models/_user.py:52 authentication/models/connection_token.py:41 #: authentication/serializers/connect_token_secret.py:111 #: terminal/models/applet/applet.py:42 terminal/serializers/session.py:19 -#: terminal/serializers/session.py:42 terminal/serializers/storage.py:70 +#: terminal/serializers/session.py:45 terminal/serializers/storage.py:70 msgid "Protocol" msgstr "协议" @@ -1696,7 +1700,7 @@ msgstr "自动化任务" msgid "Asset automation task" msgstr "资产自动化任务" -#: assets/models/automations/base.py:113 audits/models.py:202 +#: assets/models/automations/base.py:113 audits/models.py:206 #: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220 #: terminal/models/applet/applet.py:301 terminal/models/applet/host.py:139 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 @@ -2285,7 +2289,7 @@ msgstr "改密" #: audits/const.py:41 settings/serializers/terminal.py:6 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 -#: terminal/serializers/session.py:49 terminal/serializers/session.py:63 +#: terminal/serializers/session.py:52 terminal/serializers/session.py:66 msgid "Terminal" msgstr "终端" @@ -2322,7 +2326,7 @@ msgstr "否" msgid "Job audit log" msgstr "作业审计日志" -#: audits/models.py:54 audits/models.py:98 audits/models.py:169 +#: audits/models.py:54 audits/models.py:98 audits/models.py:173 #: terminal/models/session/session.py:38 terminal/models/session/sharing.py:113 msgid "Remote addr" msgstr "远端地址" @@ -2361,7 +2365,7 @@ msgstr "资源类型" msgid "Resource" msgstr "资源" -#: audits/models.py:99 audits/models.py:145 audits/models.py:171 +#: audits/models.py:99 audits/models.py:145 audits/models.py:175 #: terminal/serializers/command.py:75 msgid "Datetime" msgstr "日期" @@ -2382,59 +2386,59 @@ msgstr "详情 ID" msgid "Activity log" msgstr "活动日志" -#: audits/models.py:167 +#: audits/models.py:171 msgid "Change by" msgstr "修改者" -#: audits/models.py:177 +#: audits/models.py:181 msgid "Password change log" msgstr "改密日志" -#: audits/models.py:184 audits/models.py:259 +#: audits/models.py:188 audits/models.py:263 msgid "Login type" msgstr "登录方式" -#: audits/models.py:186 audits/models.py:255 +#: audits/models.py:190 audits/models.py:259 #: tickets/models/ticket/login_confirm.py:10 msgid "Login IP" msgstr "登录 IP" -#: audits/models.py:194 audits/serializers.py:49 +#: audits/models.py:198 audits/serializers.py:49 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: users/forms/profile.py:65 users/models/user.py:815 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" -#: audits/models.py:204 +#: audits/models.py:208 msgid "Date login" msgstr "登录日期" -#: audits/models.py:206 audits/models.py:260 audits/serializers.py:67 +#: audits/models.py:210 audits/models.py:264 audits/serializers.py:67 #: audits/serializers.py:183 msgid "Authentication backend" msgstr "认证方式" -#: audits/models.py:250 +#: audits/models.py:254 msgid "User login log" msgstr "用户登录日志" -#: audits/models.py:256 +#: audits/models.py:260 msgid "Session key" msgstr "会话标识" -#: audits/models.py:262 authentication/models/connection_token.py:47 +#: audits/models.py:266 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 #: tickets/models/ticket/apply_application.py:31 #: tickets/models/ticket/apply_asset.py:20 users/models/user.py:833 msgid "Date expired" msgstr "失效日期" -#: audits/models.py:288 +#: audits/models.py:292 msgid "User session" msgstr "用户会话" -#: audits/models.py:290 +#: audits/models.py:294 msgid "Offline ussr session" msgstr "下限用户会话" @@ -2514,6 +2518,10 @@ msgstr "清理审计会话任务日志" msgid "Upload FTP file to external storage" msgstr "上传 FTP 文件到外部存储" +#: authentication/api/access_key.py:36 +msgid "Access keys can be created at most 10" +msgstr "" + #: authentication/api/confirm.py:40 msgid "This action require verify your MFA" msgstr "该操作需要验证您的 MFA, 请先开启并配置" @@ -2596,7 +2604,11 @@ msgstr "无效的令牌头。符号字符串不应包含无效字符。" msgid "Invalid token or cache refreshed." msgstr "刷新的令牌或缓存无效。" -#: authentication/backends/passkey/api.py:52 +#: authentication/backends/passkey/api.py:26 +msgid "Only register passkey for local user" +msgstr "" + +#: authentication/backends/passkey/api.py:54 msgid "Auth failed" msgstr "认证失败" @@ -2880,7 +2892,7 @@ msgstr "设置手机号码启用" msgid "Clear phone number to disable" msgstr "清空手机号码禁用" -#: authentication/middleware.py:93 settings/utils/ldap.py:661 +#: authentication/middleware.py:93 settings/utils/ldap.py:679 msgid "Authentication failed (before login check failed): {}" msgstr "认证失败 (登录前检查失败): {}" @@ -3729,6 +3741,7 @@ msgid "The mobile phone number format is incorrect" msgstr "手机号格式不正确" #: jumpserver/conf.py:444 +#, python-brace-format msgid "The verification code is: {code}" msgstr "验证码为: {code}" @@ -4117,7 +4130,7 @@ msgstr "保存后执行" msgid "Job type" msgstr "任务类型" -#: ops/serializers/job.py:57 terminal/serializers/session.py:50 +#: ops/serializers/job.py:57 terminal/serializers/session.py:53 msgid "Is finished" msgstr "是否完成" @@ -5685,100 +5698,100 @@ msgstr "周期导入 LDAP 用户" msgid "Registration periodic import ldap user task" msgstr "注册周期导入 LDAP 用户 任务" -#: settings/utils/ldap.py:476 +#: settings/utils/ldap.py:494 msgid "ldap:// or ldaps:// protocol is used." msgstr "使用 ldap:// 或 ldaps:// 协议" -#: settings/utils/ldap.py:487 +#: settings/utils/ldap.py:505 msgid "Host or port is disconnected: {}" msgstr "主机或端口不可连接: {}" -#: settings/utils/ldap.py:489 +#: settings/utils/ldap.py:507 msgid "The port is not the port of the LDAP service: {}" msgstr "端口不是LDAP服务端口: {}" -#: settings/utils/ldap.py:491 +#: settings/utils/ldap.py:509 msgid "Please add certificate: {}" msgstr "请添加证书" -#: settings/utils/ldap.py:495 settings/utils/ldap.py:522 -#: settings/utils/ldap.py:552 settings/utils/ldap.py:580 +#: settings/utils/ldap.py:513 settings/utils/ldap.py:540 +#: settings/utils/ldap.py:570 settings/utils/ldap.py:598 msgid "Unknown error: {}" msgstr "未知错误: {}" -#: settings/utils/ldap.py:509 +#: settings/utils/ldap.py:527 msgid "Bind DN or Password incorrect" msgstr "绑定DN或密码错误" -#: settings/utils/ldap.py:516 +#: settings/utils/ldap.py:534 msgid "Please enter Bind DN: {}" msgstr "请输入绑定DN: {}" -#: settings/utils/ldap.py:518 +#: settings/utils/ldap.py:536 msgid "Please enter Password: {}" msgstr "请输入密码: {}" -#: settings/utils/ldap.py:520 +#: settings/utils/ldap.py:538 msgid "Please enter correct Bind DN and Password: {}" msgstr "请输入正确的绑定DN和密码: {}" -#: settings/utils/ldap.py:538 +#: settings/utils/ldap.py:556 msgid "Invalid User OU or User search filter: {}" msgstr "不合法的用户OU或用户过滤器: {}" -#: settings/utils/ldap.py:569 +#: settings/utils/ldap.py:587 msgid "LDAP User attr map not include: {}" msgstr "LDAP属性映射没有包含: {}" -#: settings/utils/ldap.py:576 +#: settings/utils/ldap.py:594 msgid "LDAP User attr map is not dict" msgstr "LDAP属性映射不合法" -#: settings/utils/ldap.py:595 +#: settings/utils/ldap.py:613 msgid "LDAP authentication is not enabled" msgstr "LDAP认证没有启用" -#: settings/utils/ldap.py:613 +#: settings/utils/ldap.py:631 msgid "Error (Invalid LDAP server): {}" msgstr "错误 (不合法的LDAP服务器地址): {}" -#: settings/utils/ldap.py:615 +#: settings/utils/ldap.py:633 msgid "Error (Invalid Bind DN): {}" msgstr "错误 (不合法的绑定DN): {}" -#: settings/utils/ldap.py:617 +#: settings/utils/ldap.py:635 msgid "Error (Invalid LDAP User attr map): {}" msgstr "错误 (不合法的LDAP属性映射): {}" -#: settings/utils/ldap.py:619 +#: settings/utils/ldap.py:637 msgid "Error (Invalid User OU or User search filter): {}" msgstr "错误 (不合法的用户OU或用户过滤器): {}" -#: settings/utils/ldap.py:621 +#: settings/utils/ldap.py:639 msgid "Error (Not enabled LDAP authentication): {}" msgstr "错误 (没有启用LDAP认证): {}" -#: settings/utils/ldap.py:623 +#: settings/utils/ldap.py:641 msgid "Error (Unknown): {}" msgstr "错误 (未知): {}" -#: settings/utils/ldap.py:626 +#: settings/utils/ldap.py:644 msgid "Succeed: Match {} s user" msgstr "成功匹配 {} 个用户" -#: settings/utils/ldap.py:659 +#: settings/utils/ldap.py:677 msgid "Authentication failed (configuration incorrect): {}" msgstr "认证失败 (配置错误): {}" -#: settings/utils/ldap.py:663 +#: settings/utils/ldap.py:681 msgid "Authentication failed (username or password incorrect): {}" msgstr "认证失败 (用户名或密码不正确): {}" -#: settings/utils/ldap.py:665 +#: settings/utils/ldap.py:683 msgid "Authentication failed (Unknown): {}" msgstr "认证失败: (未知): {}" -#: settings/utils/ldap.py:668 +#: settings/utils/ldap.py:686 msgid "Authentication success: {}" msgstr "认证成功: {}" @@ -6127,7 +6140,7 @@ msgid "SFTP" msgstr "SFTP" #: terminal/const.py:89 -msgid "Read Only" +msgid "Read only" msgstr "只读" #: terminal/const.py:90 @@ -6135,17 +6148,29 @@ msgid "Writable" msgstr "读写" #: terminal/const.py:94 -msgid "Kill Session" +msgid "Kill session" msgstr "终断会话" #: terminal/const.py:95 -msgid "Lock Session" +msgid "Lock session" msgstr "锁定会话" #: terminal/const.py:96 -msgid "Unlock Session" +msgid "Unlock session" msgstr "解锁会话" +#: terminal/const.py:101 +msgid "Replay create failed" +msgstr "录像创建失败" + +#: terminal/const.py:102 +msgid "Replay upload failed" +msgstr "录像上传失败" + +#: terminal/const.py:103 +msgid "Replay convert failed" +msgstr "录像转码失败" + #: terminal/exceptions.py:8 msgid "Bulk create not support" msgstr "不支持批量创建" @@ -6369,27 +6394,31 @@ msgstr "回放" msgid "Date end" msgstr "结束日期" -#: terminal/models/session/session.py:47 terminal/serializers/session.py:62 +#: terminal/models/session/session.py:47 terminal/serializers/session.py:65 msgid "Command amount" msgstr "命令数量" -#: terminal/models/session/session.py:281 +#: terminal/models/session/session.py:48 terminal/serializers/session.py:28 +msgid "Error reason" +msgstr "错误原因" + +#: terminal/models/session/session.py:282 msgid "Session record" msgstr "会话记录" -#: terminal/models/session/session.py:283 +#: terminal/models/session/session.py:284 msgid "Can monitor session" msgstr "可以监控会话" -#: terminal/models/session/session.py:284 +#: terminal/models/session/session.py:285 msgid "Can share session" msgstr "可以分享会话" -#: terminal/models/session/session.py:285 +#: terminal/models/session/session.py:286 msgid "Can terminate session" msgstr "可以终断会话" -#: terminal/models/session/session.py:286 +#: terminal/models/session/session.py:287 msgid "Can validate session action perm" msgstr "可以验证会话动作权限" @@ -6638,31 +6667,31 @@ msgstr "如果不同端点下的资产 IP 有冲突,使用资产标签实现" msgid "Asset IP" msgstr "资产 IP" -#: terminal/serializers/session.py:23 terminal/serializers/session.py:47 +#: terminal/serializers/session.py:23 terminal/serializers/session.py:50 msgid "Can replay" msgstr "是否可重放" -#: terminal/serializers/session.py:24 terminal/serializers/session.py:48 +#: terminal/serializers/session.py:24 terminal/serializers/session.py:51 msgid "Can join" msgstr "是否可加入" -#: terminal/serializers/session.py:25 terminal/serializers/session.py:51 +#: terminal/serializers/session.py:25 terminal/serializers/session.py:54 msgid "Can terminate" msgstr "是否可中断" -#: terminal/serializers/session.py:43 +#: terminal/serializers/session.py:46 msgid "User ID" msgstr "用户 ID" -#: terminal/serializers/session.py:44 +#: terminal/serializers/session.py:47 msgid "Asset ID" msgstr "资产 ID" -#: terminal/serializers/session.py:45 +#: terminal/serializers/session.py:48 msgid "Login from display" msgstr "登录来源名称" -#: terminal/serializers/session.py:52 +#: terminal/serializers/session.py:55 msgid "Terminal display" msgstr "终端显示" @@ -7382,10 +7411,8 @@ msgid "File name conflict resolution" msgstr "文件名冲突解决方案" #: users/serializers/preference/koko.py:14 -#, fuzzy -#| msgid "Terminal setting" msgid "Terminal theme name" -msgstr "终端设置" +msgstr "终端主题名称" #: users/serializers/preference/lina.py:13 msgid "New file encryption password" @@ -7515,26 +7542,30 @@ msgid "" msgstr "" "管理员已开启'仅允许已存在用户登录',当前用户不在用户列表中,请联系管理员。" -#: users/tasks.py:23 +#: users/tasks.py:25 msgid "Check password expired" msgstr "校验密码已过期" -#: users/tasks.py:37 +#: users/tasks.py:39 msgid "Periodic check password expired" msgstr "周期校验密码过期" -#: users/tasks.py:51 +#: users/tasks.py:53 msgid "Check user expired" msgstr "校验用户已过期" -#: users/tasks.py:68 +#: users/tasks.py:70 msgid "Periodic check user expired" msgstr "周期检测用户过期" -#: users/tasks.py:82 +#: users/tasks.py:84 msgid "Check unused users" msgstr "校验用户已过期" +#: users/tasks.py:114 +msgid "The user has not logged in recently and has been disabled." +msgstr "该用户最近未登录,已被禁用。" + #: users/templates/users/_msg_account_expire_reminder.html:7 msgid "Your account will expire in" msgstr "您的账号即将过期" @@ -7998,16 +8029,16 @@ msgid "Task strategy" msgstr "任务策略" #: xpack/plugins/cloud/models.py:285 -msgid "Exact" -msgstr "" +msgid "Equal" +msgstr "等于" #: xpack/plugins/cloud/models.py:286 -msgid "Not" -msgstr "否" +msgid "Not equal" +msgstr "不等于" #: xpack/plugins/cloud/models.py:287 msgid "In" -msgstr "在..里面" +msgstr "在...中" #: xpack/plugins/cloud/models.py:288 msgid "Contains" @@ -8015,11 +8046,11 @@ msgstr "包含" #: xpack/plugins/cloud/models.py:289 msgid "Startswith" -msgstr "以..开头" +msgstr "以...开头" #: xpack/plugins/cloud/models.py:290 msgid "Endswith" -msgstr "以..结尾" +msgstr "以...结尾" #: xpack/plugins/cloud/models.py:296 msgid "Instance platform" @@ -8431,33 +8462,3 @@ msgstr "企业专业版" #: xpack/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "企业旗舰版" - -#~ msgid "Invalid signature header. No credentials provided." -#~ msgstr "不合法的签名头" - -#~ msgid "" -#~ "Invalid signature header. Signature string should not contain spaces." -#~ msgstr "不合法的签名头" - -#~ msgid "Invalid signature header. Format like AccessKeyId:Signature" -#~ msgstr "不合法的签名头" - -#~ msgid "" -#~ "Invalid signature header. Signature string should not contain invalid " -#~ "characters." -#~ msgstr "不合法的签名头" - -#~ msgid "Invalid signature." -#~ msgstr "签名无效" - -#~ msgid "HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT" -#~ msgstr "HTTP header not valid" - -#~ msgid "Expired, more than 15 minutes" -#~ msgstr "已过期,超过15分钟" - -#~ msgid "User disabled." -#~ msgstr "用户已禁用" - -#~ msgid "Random" -#~ msgstr "随机" diff --git a/apps/terminal/migrations/0064_auto_20230728_1001.py b/apps/terminal/migrations/0064_auto_20230728_1001.py index 900abb5ba..93af187c4 100644 --- a/apps/terminal/migrations/0064_auto_20230728_1001.py +++ b/apps/terminal/migrations/0064_auto_20230728_1001.py @@ -33,6 +33,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='task', name='name', - field=models.CharField(choices=[('kill_session', 'Kill Session'), ('lock_session', 'Lock Session'), ('unlock_session', 'Unlock Session')], max_length=128, verbose_name='Name'), + field=models.CharField(choices=[('kill_session', 'Kill session'), ('lock_session', 'Lock session'), ('unlock_session', 'Unlock session')], max_length=128, verbose_name='Name'), ), ] From 23ccd6df8ce7ce04bd62dd2914a0ab7ab5ab8de9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 12 Oct 2023 10:45:38 +0800 Subject: [PATCH 061/114] =?UTF-8?q?perf:=20mysql=20mariadb=20=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E4=B8=8D=E5=86=8D=E5=BF=85=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/asset/database.py | 46 ++++++++++++++++++----- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/apps/assets/serializers/asset/database.py b/apps/assets/serializers/asset/database.py index 17a122fd6..633126995 100644 --- a/apps/assets/serializers/asset/database.py +++ b/apps/assets/serializers/asset/database.py @@ -1,8 +1,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from rest_framework.serializers import ValidationError -from assets.models import Database +from assets.models import Database, Platform from assets.serializers.gateway import GatewayWithAccountSecretSerializer from .common import AssetSerializer @@ -20,13 +19,42 @@ class DatabaseSerializer(AssetSerializer): ] fields = AssetSerializer.Meta.fields + extra_fields - def validate(self, attrs): - platform = attrs.get('platform') - db_type_required = ('mongodb', 'postgresql') - if platform and getattr(platform, 'type') in db_type_required \ - and not attrs.get('db_name'): - raise ValidationError({'db_name': _('This field is required.')}) - return attrs + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_db_name_required() + + def get_platform(self): + platform = None + platform_id = None + + if getattr(self, 'initial_data', None): + platform_id = self.initial_data.get('platform') + if isinstance(platform_id, dict): + platform_id = platform_id.get('id') or platform_id.get('pk') + if not platform_id and self.instance: + platform = self.instance.platform + elif getattr(self, 'instance', None): + if isinstance(self.instance, list): + return + platform = self.instance.platform + elif self.context.get('request'): + platform_id = self.context['request'].query_params.get('platform') + + if not platform and platform_id: + platform = Platform.objects.filter(id=platform_id).first() + return platform + + def set_db_name_required(self): + db_field = self.fields.get('db_name') + if not db_field: + return + + platform = self.get_platform() + if not platform: + return + + if platform.type in ['mysql', 'mariadb']: + db_field.required = False class DatabaseWithGatewaySerializer(DatabaseSerializer): From 8b3fd2c117cbc1b816ab8a5d64c1eb14171eae19 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 12 Oct 2023 14:45:45 +0800 Subject: [PATCH 062/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=20ip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/mixins.py | 11 +++++------ apps/common/utils/common.py | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 906fcbb97..3cf454101 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -19,7 +19,7 @@ from django.utils.translation import gettext as _ from rest_framework.request import Request from acls.models import LoginACL -from common.utils import get_request_ip, get_logger, bulk_get, FlashMessageUtil +from common.utils import get_request_ip_or_data, get_logger, bulk_get, FlashMessageUtil from users.models import User from users.utils import LoginBlockUtil, MFABlockUtils, LoginIpBlockUtil from . import errors @@ -107,13 +107,12 @@ auth.authenticate = authenticate class CommonMixin: request: Request + _ip = '' def get_request_ip(self): - ip = '' - if hasattr(self.request, 'data'): - ip = self.request.data.get('remote_addr', '') - ip = ip or get_request_ip(self.request) - return ip + if not self._ip: + self._ip = get_request_ip_or_data(self.request) + return self._ip def raise_credential_error(self, error): raise self.partial_credential_error(error=error) diff --git a/apps/common/utils/common.py b/apps/common/utils/common.py index 56f1dbd09..063bd1ebc 100644 --- a/apps/common/utils/common.py +++ b/apps/common/utils/common.py @@ -17,6 +17,8 @@ import psutil from django.conf import settings from django.templatetags.static import static +from common.permissions import ServiceAccountSignaturePermission + UUID_PATTERN = re.compile(r'\w{8}(-\w{4}){3}-\w{12}') ipip_db = None @@ -153,19 +155,26 @@ def is_uuid(seq): def get_request_ip(request): - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',') + x_real_ip = request.META.get('HTTP_X_REAL_IP', '') + if x_real_ip: + return x_real_ip + x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',') if x_forwarded_for and x_forwarded_for[0]: login_ip = x_forwarded_for[0] - else: - login_ip = request.META.get('REMOTE_ADDR', '') + return login_ip + + login_ip = request.META.get('REMOTE_ADDR', '') return login_ip def get_request_ip_or_data(request): ip = '' - if hasattr(request, 'data'): - ip = request.data.get('remote_addr', '') + + if hasattr(request, 'data') and request.data.get('remote_addr', ''): + permission = ServiceAccountSignaturePermission() + if permission.has_permission(request, None): + ip = request.data.get('remote_addr', '') ip = ip or get_request_ip(request) return ip From 9f042cfa04c2c7675a518ec6d9759bd1d8494c64 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 12 Oct 2023 16:17:32 +0800 Subject: [PATCH 063/114] Merge branch 'dev' into pr@dev@change_import (#11815) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 修改获取 ip * perf: 修改导入 --------- Co-authored-by: ibuler --- apps/accounts/api/account/account.py | 3 +- apps/accounts/api/account/template.py | 2 +- apps/authentication/api/access_key.py | 2 +- apps/authentication/api/confirm.py | 3 +- apps/authentication/api/dingtalk.py | 3 +- apps/authentication/api/feishu.py | 5 ++- apps/authentication/api/wecom.py | 3 +- apps/authentication/permissions.py | 58 +++++++++++++++++++++++++ apps/authentication/views/dingtalk.py | 2 +- apps/authentication/views/feishu.py | 2 +- apps/authentication/views/wecom.py | 2 +- apps/common/permissions.py | 53 ---------------------- apps/settings/api/public.py | 2 +- apps/terminal/api/component/endpoint.py | 2 +- apps/users/api/profile.py | 2 +- utils/add_china_db_platform.py | 8 ++++ 16 files changed, 85 insertions(+), 67 deletions(-) create mode 100644 apps/authentication/permissions.py create mode 100644 utils/add_china_db_platform.py diff --git a/apps/accounts/api/account/account.py b/apps/accounts/api/account/account.py index ecf014b55..8cfeeec8c 100644 --- a/apps/accounts/api/account/account.py +++ b/apps/accounts/api/account/account.py @@ -9,8 +9,9 @@ from accounts.filters import AccountFilterSet from accounts.mixins import AccountRecordViewLogMixin from accounts.models import Account from assets.models import Asset, Node +from authentication.permissions import UserConfirmation, ConfirmType from common.api.mixin import ExtraFilterFieldsMixin -from common.permissions import UserConfirmation, ConfirmType, IsValidUser +from common.permissions import IsValidUser from orgs.mixins.api import OrgBulkModelViewSet from rbac.permissions import RBACPermission diff --git a/apps/accounts/api/account/template.py b/apps/accounts/api/account/template.py index 57d577b46..4493cb227 100644 --- a/apps/accounts/api/account/template.py +++ b/apps/accounts/api/account/template.py @@ -8,8 +8,8 @@ from accounts.mixins import AccountRecordViewLogMixin from accounts.models import AccountTemplate from accounts.tasks import template_sync_related_accounts from assets.const import Protocol +from authentication.permissions import UserConfirmation, ConfirmType from common.drf.filters import BaseFilterSet -from common.permissions import UserConfirmation, ConfirmType from orgs.mixins.api import OrgBulkModelViewSet from rbac.permissions import RBACPermission diff --git a/apps/authentication/api/access_key.py b/apps/authentication/api/access_key.py index 86551953a..66026102c 100644 --- a/apps/authentication/api/access_key.py +++ b/apps/authentication/api/access_key.py @@ -5,8 +5,8 @@ from django.utils.translation import gettext as _ from rest_framework import serializers from rest_framework.response import Response +from authentication.permissions import UserConfirmation from common.api import JMSModelViewSet -from common.permissions import UserConfirmation from rbac.permissions import RBACPermission from ..const import ConfirmType from ..serializers import AccessKeySerializer diff --git a/apps/authentication/api/confirm.py b/apps/authentication/api/confirm.py index 92792d72d..4bd8d2c72 100644 --- a/apps/authentication/api/confirm.py +++ b/apps/authentication/api/confirm.py @@ -7,7 +7,8 @@ from rest_framework import status from rest_framework.generics import RetrieveAPIView, CreateAPIView from rest_framework.response import Response -from common.permissions import IsValidUser, UserConfirmation +from authentication.permissions import UserConfirmation +from common.permissions import IsValidUser from ..const import ConfirmType from ..serializers import ConfirmSerializer diff --git a/apps/authentication/api/dingtalk.py b/apps/authentication/api/dingtalk.py index 66f5a1d8c..ad3bd26b1 100644 --- a/apps/authentication/api/dingtalk.py +++ b/apps/authentication/api/dingtalk.py @@ -4,8 +4,9 @@ from rest_framework.views import APIView from authentication import errors from authentication.const import ConfirmType +from authentication.permissions import UserConfirmation from common.api import RoleUserMixin, RoleAdminMixin -from common.permissions import UserConfirmation, IsValidUser +from common.permissions import IsValidUser from common.utils import get_logger from users.models import User diff --git a/apps/authentication/api/feishu.py b/apps/authentication/api/feishu.py index 336552087..cf95bb6ea 100644 --- a/apps/authentication/api/feishu.py +++ b/apps/authentication/api/feishu.py @@ -4,12 +4,13 @@ from rest_framework.views import APIView from authentication import errors from authentication.const import ConfirmType +from authentication.permissions import UserConfirmation from common.api import RoleUserMixin, RoleAdminMixin -from common.permissions import UserConfirmation, IsValidUser +from common.permissions import IsValidUser from common.utils import get_logger from users.models import User -logger = get_logger(__file__) +logger = get_logger(__name__) class FeiShuQRUnBindBase(APIView): diff --git a/apps/authentication/api/wecom.py b/apps/authentication/api/wecom.py index 82476157b..6dcfe539c 100644 --- a/apps/authentication/api/wecom.py +++ b/apps/authentication/api/wecom.py @@ -4,8 +4,9 @@ from rest_framework.views import APIView from authentication import errors from authentication.const import ConfirmType +from authentication.permissions import UserConfirmation from common.api import RoleUserMixin, RoleAdminMixin -from common.permissions import UserConfirmation, IsValidUser +from common.permissions import IsValidUser from common.utils import get_logger from users.models import User diff --git a/apps/authentication/permissions.py b/apps/authentication/permissions.py new file mode 100644 index 000000000..e49f72796 --- /dev/null +++ b/apps/authentication/permissions.py @@ -0,0 +1,58 @@ +import time + +from django.conf import settings +from rest_framework import permissions + +from authentication.const import ConfirmType +from authentication.models import ConnectionToken +from common.exceptions import UserConfirmRequired +from common.permissions import IsValidUser +from common.utils import get_object_or_none +from orgs.utils import tmp_to_root_org + + +class UserConfirmation(permissions.BasePermission): + ttl = 60 * 5 + min_level = 1 + confirm_type = 'relogin' + + def has_permission(self, request, view): + if not settings.SECURITY_VIEW_AUTH_NEED_MFA: + return True + + confirm_level = request.session.get('CONFIRM_LEVEL') + confirm_time = request.session.get('CONFIRM_TIME') + ttl = self.get_ttl() + if not confirm_level or not confirm_time or \ + confirm_level < self.min_level or \ + confirm_time < time.time() - ttl: + raise UserConfirmRequired(code=self.confirm_type) + return True + + def get_ttl(self): + if self.confirm_type == ConfirmType.MFA: + ttl = settings.SECURITY_MFA_VERIFY_TTL + else: + ttl = self.ttl + return ttl + + @classmethod + def require(cls, confirm_type=ConfirmType.RELOGIN, ttl=60 * 5): + min_level = ConfirmType.values.index(confirm_type) + 1 + name = 'UserConfirmationLevel{}TTL{}'.format(min_level, ttl) + return type(name, (cls,), {'min_level': min_level, 'ttl': ttl, 'confirm_type': confirm_type}) + + +class IsValidUserOrConnectionToken(IsValidUser): + def has_permission(self, request, view): + return super().has_permission(request, view) \ + or self.is_valid_connection_token(request) + + @staticmethod + def is_valid_connection_token(request): + token_id = request.query_params.get('token') + if not token_id: + return False + with tmp_to_root_org(): + token = get_object_or_none(ConnectionToken, id=token_id) + return token and token.is_valid diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index c23827ca1..fcff66527 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -13,7 +13,7 @@ from authentication import errors from authentication.const import ConfirmType from authentication.mixins import AuthMixin from authentication.notifications import OAuthBindMessage -from common.permissions import UserConfirmation +from authentication.permissions import UserConfirmation from common.sdk.im.dingtalk import URL, DingTalk from common.utils import get_logger from common.utils.common import get_request_ip diff --git a/apps/authentication/views/feishu.py b/apps/authentication/views/feishu.py index ec483cdd4..5be872987 100644 --- a/apps/authentication/views/feishu.py +++ b/apps/authentication/views/feishu.py @@ -11,7 +11,7 @@ from rest_framework.permissions import AllowAny, IsAuthenticated from authentication.const import ConfirmType from authentication.notifications import OAuthBindMessage -from common.permissions import UserConfirmation +from authentication.permissions import UserConfirmation from common.sdk.im.feishu import URL, FeiShu from common.utils import get_logger from common.utils.common import get_request_ip diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index 7bb56202c..7bc814d3f 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -13,7 +13,7 @@ from authentication import errors from authentication.const import ConfirmType from authentication.mixins import AuthMixin from authentication.notifications import OAuthBindMessage -from common.permissions import UserConfirmation +from authentication.permissions import UserConfirmation from common.sdk.im.wecom import URL from common.sdk.im.wecom import WeCom from common.utils import get_logger diff --git a/apps/common/permissions.py b/apps/common/permissions.py index 742759e1d..ee2e24a8b 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -5,12 +5,6 @@ import time from django.conf import settings from rest_framework import permissions -from authentication.const import ConfirmType -from authentication.models import ConnectionToken -from common.exceptions import UserConfirmRequired -from common.utils import get_object_or_none -from orgs.utils import tmp_to_root_org - class IsValidUser(permissions.IsAuthenticated): """Allows access to valid user, is active and not expired""" @@ -20,21 +14,6 @@ class IsValidUser(permissions.IsAuthenticated): and request.user.is_valid -class IsValidUserOrConnectionToken(IsValidUser): - def has_permission(self, request, view): - return super().has_permission(request, view) \ - or self.is_valid_connection_token(request) - - @staticmethod - def is_valid_connection_token(request): - token_id = request.query_params.get('token') - if not token_id: - return False - with tmp_to_root_org(): - token = get_object_or_none(ConnectionToken, id=token_id) - return token and token.is_valid - - class OnlySuperUser(IsValidUser): def has_permission(self, request, view): return super().has_permission(request, view) \ @@ -56,38 +35,6 @@ class WithBootstrapToken(permissions.BasePermission): return settings.BOOTSTRAP_TOKEN == request_bootstrap_token -class UserConfirmation(permissions.BasePermission): - ttl = 60 * 5 - min_level = 1 - confirm_type = ConfirmType.RELOGIN - - def has_permission(self, request, view): - if not settings.SECURITY_VIEW_AUTH_NEED_MFA: - return True - - confirm_level = request.session.get('CONFIRM_LEVEL') - confirm_time = request.session.get('CONFIRM_TIME') - ttl = self.get_ttl() - if not confirm_level or not confirm_time or \ - confirm_level < self.min_level or \ - confirm_time < time.time() - ttl: - raise UserConfirmRequired(code=self.confirm_type) - return True - - def get_ttl(self): - if self.confirm_type == ConfirmType.MFA: - ttl = settings.SECURITY_MFA_VERIFY_TTL - else: - ttl = self.ttl - return ttl - - @classmethod - def require(cls, confirm_type=ConfirmType.RELOGIN, ttl=60 * 5): - min_level = ConfirmType.values.index(confirm_type) + 1 - name = 'UserConfirmationLevel{}TTL{}'.format(min_level, ttl) - return type(name, (cls,), {'min_level': min_level, 'ttl': ttl, 'confirm_type': confirm_type}) - - class ServiceAccountSignaturePermission(permissions.BasePermission): def has_permission(self, request, view): from authentication.models import AccessKey diff --git a/apps/settings/api/public.py b/apps/settings/api/public.py index f2628a5d4..85c56bdbf 100644 --- a/apps/settings/api/public.py +++ b/apps/settings/api/public.py @@ -2,7 +2,7 @@ from django.conf import settings from rest_framework import generics from rest_framework.permissions import AllowAny -from common.permissions import IsValidUserOrConnectionToken +from authentication.permissions import IsValidUserOrConnectionToken from common.utils import get_logger, lazyproperty from common.utils.timezone import local_now from jumpserver.utils import has_valid_xpack_license, get_xpack_license_info diff --git a/apps/terminal/api/component/endpoint.py b/apps/terminal/api/component/endpoint.py index e2eeaf548..b40aba9aa 100644 --- a/apps/terminal/api/component/endpoint.py +++ b/apps/terminal/api/component/endpoint.py @@ -6,8 +6,8 @@ from rest_framework.request import Request from rest_framework.response import Response from assets.models import Asset +from authentication.permissions import IsValidUserOrConnectionToken from common.api import JMSBulkModelViewSet -from common.permissions import IsValidUserOrConnectionToken from orgs.utils import tmp_to_root_org from terminal import serializers from terminal.models import Session, Endpoint, EndpointRule diff --git a/apps/users/api/profile.py b/apps/users/api/profile.py index 727c87314..974fc136c 100644 --- a/apps/users/api/profile.py +++ b/apps/users/api/profile.py @@ -5,7 +5,7 @@ from rest_framework import generics from rest_framework.permissions import IsAuthenticated from authentication.models import ConnectionToken -from common.permissions import IsValidUserOrConnectionToken +from authentication.permissions import IsValidUserOrConnectionToken from common.utils import get_object_or_none from orgs.utils import tmp_to_root_org from users.notifications import ( diff --git a/utils/add_china_db_platform.py b/utils/add_china_db_platform.py new file mode 100644 index 000000000..1a0d855bb --- /dev/null +++ b/utils/add_china_db_platform.py @@ -0,0 +1,8 @@ +#!/usr/bin/python +# +databases = [ + { + 'name': 'TiDB', + 'type': 'mysql', + } +] From 116d0ba5c665e86da579b8de6f7ade0654415759 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 12 Oct 2023 16:36:08 +0800 Subject: [PATCH 064/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=AE=B0=E5=BD=95=20activity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/audits/signal_handlers/activity_log.py | 2 ++ apps/authentication/mixins.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/audits/signal_handlers/activity_log.py b/apps/audits/signal_handlers/activity_log.py index a391f0007..0efc86386 100644 --- a/apps/audits/signal_handlers/activity_log.py +++ b/apps/audits/signal_handlers/activity_log.py @@ -94,6 +94,8 @@ def after_task_publish_for_activity_log(headers=None, body=None, **kwargs): logger.error(f'Get celery task info error: {e}', exc_info=True) else: logger.debug(f'Create activity log for celery task: {task_id}') + if not resource_ids: + return create_activities(resource_ids, detail, task_id, action=ActivityChoices.task, org_id=org_id) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 3cf454101..2446b4667 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -19,7 +19,7 @@ from django.utils.translation import gettext as _ from rest_framework.request import Request from acls.models import LoginACL -from common.utils import get_request_ip_or_data, get_logger, bulk_get, FlashMessageUtil +from common.utils import get_request_ip_or_data, get_request_ip, get_logger, bulk_get, FlashMessageUtil from users.models import User from users.utils import LoginBlockUtil, MFABlockUtils, LoginIpBlockUtil from . import errors From c0de27ff7a3a4c6fd25bb371d05d98538c7d5756 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 12 Oct 2023 18:11:51 +0800 Subject: [PATCH 065/114] =?UTF-8?q?perf:=20=E8=B5=84=E4=BA=A7=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=9B=B4=E6=96=B0=E5=B9=B3=E5=8F=B0=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=EF=BC=8C=E6=A0=B9=E6=8D=AE=E5=B9=B3=E5=8F=B0=E7=BA=A6=E6=9D=9F?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E8=87=AA=E5=8A=A8=E7=94=9F=E6=95=88=20(#1181?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/api/account/template.py | 2 +- apps/assets/api/asset/asset.py | 39 ++++++++++++++++++++++++- apps/assets/serializers/asset/common.py | 27 +++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/apps/accounts/api/account/template.py b/apps/accounts/api/account/template.py index 4493cb227..22d9508a7 100644 --- a/apps/accounts/api/account/template.py +++ b/apps/accounts/api/account/template.py @@ -46,7 +46,7 @@ class AccountTemplateViewSet(OrgBulkModelViewSet): } rbac_perms = { 'su_from_account_templates': 'accounts.view_accounttemplate', - 'sync_related_accounts': 'accounts.change_accounttemplate', + 'sync_related_accounts': 'accounts.change_account', } @action(methods=['get'], detail=False, url_path='su-from-account-templates') diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index f1573629e..4b36b4bbe 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -1,9 +1,12 @@ # -*- coding: utf-8 -*- # +from collections import defaultdict + import django_filters from django.db.models import Q from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _ +from rest_framework import status from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.status import HTTP_200_OK @@ -12,7 +15,7 @@ from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connect from assets import serializers from assets.exceptions import NotSupportedTemporarilyError from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend -from assets.models import Asset, Gateway, Platform +from assets.models import Asset, Gateway, Platform, Protocol from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual from common.api import SuggestionMixin from common.drf.filters import BaseFilterSet, AttrRulesFilterBackend @@ -115,6 +118,7 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): ("gateways", "assets.view_gateway"), ("spec_info", "assets.view_asset"), ("gathered_info", "assets.view_asset"), + ("sync_platform_protocols", "assets.change_asset"), ) extra_filter_backends = [ LabelFilterBackend, IpInFilterBackend, @@ -152,6 +156,39 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): gateways = asset.domain.gateways return self.get_paginated_response_from_queryset(gateways) + @action(methods=['post'], detail=False, url_path='sync-platform-protocols') + def sync_platform_protocols(self, request, *args, **kwargs): + platform_id = request.data.get('platform_id') + platform = get_object_or_404(Platform, pk=platform_id) + assets = platform.assets.all() + + platform_protocols = { + p['name']: p['port'] + for p in platform.protocols.values('name', 'port') + } + asset_protocols_map = defaultdict(set) + protocols = assets.prefetch_related('protocols').values_list( + 'id', 'protocols__name' + ) + for asset_id, protocol in protocols: + asset_id = str(asset_id) + asset_protocols_map[asset_id].add(protocol) + objs = [] + for asset_id, protocols in asset_protocols_map.items(): + protocol_names = set(platform_protocols) - protocols + if not protocol_names: + continue + for name in protocol_names: + objs.append( + Protocol( + name=name, + port=platform_protocols[name], + asset_id=asset_id, + ) + ) + Protocol.objects.bulk_create(objs) + return Response(status=status.HTTP_200_OK) + def create(self, request, *args, **kwargs): if request.path.find('/api/v1/assets/assets/') > -1: error = _('Cannot create asset directly, you should create a host or other') diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index ea2be442c..6d73287ee 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -298,10 +298,37 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali self.perform_nodes_display_create(instance, nodes_display) return instance + @staticmethod + def sync_platform_protocols(instance, old_platform): + platform = instance.platform + + if str(old_platform.id) == str(instance.platform_id): + return + + platform_protocols = { + p['name']: p['port'] + for p in platform.protocols.values('name', 'port') + } + + protocols = set(instance.protocols.values_list('name', flat=True)) + protocol_names = set(platform_protocols) - protocols + objs = [] + for name in protocol_names: + objs.append( + Protocol( + name=name, + port=platform_protocols[name], + asset_id=instance.id, + ) + ) + Protocol.objects.bulk_create(objs) + @atomic def update(self, instance, validated_data): + old_platform = instance.platform nodes_display = validated_data.pop('nodes_display', '') instance = super().update(instance, validated_data) + self.sync_platform_protocols(instance, old_platform) self.perform_nodes_display_create(instance, nodes_display) return instance From e1278360afec5c007cfab2814ffa151c50f01b80 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 12 Oct 2023 19:46:04 +0800 Subject: [PATCH 066/114] =?UTF-8?q?fix:=20=E8=B5=84=E4=BA=A7=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E5=A4=B1=E8=B4=A5=20(#11824)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/assets/serializers/asset/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 6d73287ee..78f691237 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -159,7 +159,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali return if isinstance(self.initial_data, list): return - accounts = self.initial_data.get('accounts', None) + accounts = self.initial_data.pop('accounts', None) self._accounts = accounts def _get_protocols_required_default(self): From 58fd578ddd1230f48fe1574e510723cecc7c8d54 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 12 Oct 2023 20:04:28 +0800 Subject: [PATCH 067/114] =?UTF-8?q?perf:=20=E8=B5=84=E4=BA=A7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=8F=90=E7=A4=BA=E6=B7=BB=E5=8A=A0=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=20(#11826)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/acls/notifications.py | 4 +++- apps/acls/templates/acls/asset_login_reminder.html | 1 + apps/authentication/api/connection_token.py | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/acls/notifications.py b/apps/acls/notifications.py index 5d3c59e13..cf19b7a51 100644 --- a/apps/acls/notifications.py +++ b/apps/acls/notifications.py @@ -41,9 +41,10 @@ class UserLoginReminderMsg(UserMessage): class AssetLoginReminderMsg(UserMessage): subject = _('Asset login reminder') - def __init__(self, user, asset: Asset, login_user: User): + def __init__(self, user, asset: Asset, login_user: User, account_username): self.asset = asset self.login_user = login_user + self.account_username = account_username super().__init__(user) def get_html_msg(self) -> dict: @@ -51,6 +52,7 @@ class AssetLoginReminderMsg(UserMessage): 'recipient': self.user.username, 'username': self.login_user.username, 'asset': str(self.asset), + 'account': self.account_username, } message = render_to_string('acls/asset_login_reminder.html', context) diff --git a/apps/acls/templates/acls/asset_login_reminder.html b/apps/acls/templates/acls/asset_login_reminder.html index 9a30cda49..8d63a2ff6 100644 --- a/apps/acls/templates/acls/asset_login_reminder.html +++ b/apps/acls/templates/acls/asset_login_reminder.html @@ -4,6 +4,7 @@

{% trans 'Username' %}: [{{ username }}]

{% trans 'Assets' %}: [{{ asset }}]

+

{% trans 'Account' %}: [{{ account }}]


{% trans 'The user has just successfully logged into the asset. Please ensure that this is an authorized operation. If you suspect that this is an unauthorized access, please take appropriate measures immediately.' %}

diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index b0756d4aa..6eb371827 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -419,9 +419,10 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView reviewers = acl.reviewers.all() if not reviewers: return + account_username = account.username self._record_operate_log(acl, asset) for reviewer in reviewers: - AssetLoginReminderMsg(reviewer, asset, user).publish_async() + AssetLoginReminderMsg(reviewer, asset, user, account_username).publish_async() class SuperConnectionTokenViewSet(ConnectionTokenViewSet): From 7eb497f9d30617f029c04c8cc5f75798e08a3e66 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 12 Oct 2023 20:13:21 +0800 Subject: [PATCH 068/114] =?UTF-8?q?fix:=20=E8=B5=84=E4=BA=A7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E8=A2=AB=E9=99=90=E5=88=B6=EF=BC=8C=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=88=B0=E5=BD=93=E5=89=8D=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E7=9A=84=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97=EF=BC=8C=E8=80=8C?= =?UTF-8?q?=E6=98=AF=E8=AE=B0=E5=BD=95=E5=88=B0=E5=85=A8=E5=B1=80=E7=BB=84?= =?UTF-8?q?=E7=BB=87=20(#11827)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/authentication/api/connection_token.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 6eb371827..f51351b67 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -22,6 +22,7 @@ from common.utils import random_string, get_logger, get_request_ip from common.utils.django import get_request_os from common.utils.http import is_true, is_false from orgs.mixins.api import RootOrgViewMixin +from orgs.utils import tmp_to_org from perms.models import ActionChoices from terminal.connect_methods import NativeClient, ConnectMethodUtil from terminal.models import EndpointRule, Endpoint @@ -383,13 +384,14 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView @staticmethod def _record_operate_log(acl, asset): from audits.handler import create_or_update_operate_log - after = {str(_('Assets')): str(asset)} - object_name = acl._meta.object_name - resource_type = acl._meta.verbose_name - create_or_update_operate_log( - acl.action, resource_type, resource=acl, - after=after, object_name=object_name - ) + with tmp_to_org(asset.org_id): + after = {str(_('Assets')): str(asset)} + object_name = acl._meta.object_name + resource_type = acl._meta.verbose_name + create_or_update_operate_log( + acl.action, resource_type, resource=acl, + after=after, object_name=object_name + ) def _validate_acl(self, user, asset, account): from acls.models import LoginAssetACL From 452ee1224cc364a01d27d7b084680560afe562b4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 13 Oct 2023 14:40:40 +0800 Subject: [PATCH 069/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=A1=AE=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/confirm.py | 17 +++++++++++++---- apps/authentication/backends/passkey/api.py | 15 +++++++++++++-- apps/authentication/confirm/relogin.py | 1 + apps/authentication/const.py | 1 + apps/authentication/serializers/confirm.py | 2 +- apps/authentication/urls/api_urls.py | 2 +- apps/common/exceptions.py | 3 +++ 7 files changed, 33 insertions(+), 8 deletions(-) diff --git a/apps/authentication/api/confirm.py b/apps/authentication/api/confirm.py index 4bd8d2c72..2bb371e48 100644 --- a/apps/authentication/api/confirm.py +++ b/apps/authentication/api/confirm.py @@ -4,10 +4,12 @@ import time from django.utils.translation import gettext_lazy as _ from rest_framework import status -from rest_framework.generics import RetrieveAPIView, CreateAPIView +from rest_framework.decorators import action +from rest_framework.generics import RetrieveAPIView from rest_framework.response import Response from authentication.permissions import UserConfirmation +from common.api import JMSGenericViewSet from common.permissions import IsValidUser from ..const import ConfirmType from ..serializers import ConfirmSerializer @@ -20,10 +22,17 @@ class ConfirmBindORUNBindOAuth(RetrieveAPIView): return Response('ok') -class ConfirmApi(RetrieveAPIView, CreateAPIView): +class UserConfirmationViewSet(JMSGenericViewSet): permission_classes = (IsValidUser,) serializer_class = ConfirmSerializer + @action(methods=['get'], detail=False) + def check(self, request): + confirm_type = request.query_params.get('confirm_type', 'password') + permission = UserConfirmation.require(confirm_type)() + permission.has_permission(request, self) + return Response('ok') + def get_confirm_backend(self, confirm_type): backend_classes = ConfirmType.get_prop_backends(confirm_type) if not backend_classes: @@ -34,12 +43,12 @@ class ConfirmApi(RetrieveAPIView, CreateAPIView): continue return backend - def retrieve(self, request, *args, **kwargs): + def list(self, request, *args, **kwargs): confirm_type = request.query_params.get('confirm_type', 'password') backend = self.get_confirm_backend(confirm_type) if backend is None: msg = _('This action require verify your MFA') - return Response(data={'error': msg}, status=status.HTTP_404_NOT_FOUND) + return Response(data={'error': msg}, status=status.HTTP_400_BAD_REQUEST) data = { 'confirm_type': backend.name, diff --git a/apps/authentication/backends/passkey/api.py b/apps/authentication/backends/passkey/api.py index 644636dd2..ace882a92 100644 --- a/apps/authentication/backends/passkey/api.py +++ b/apps/authentication/backends/passkey/api.py @@ -4,19 +4,30 @@ from django.shortcuts import render from django.utils.translation import gettext as _ from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated, AllowAny -from rest_framework.viewsets import ModelViewSet from authentication.mixins import AuthMixin +from common.api import JMSModelViewSet from .fido import register_begin, register_complete, auth_begin, auth_complete from .models import Passkey from .serializer import PasskeySerializer +from ...const import ConfirmType +from ...permissions import UserConfirmation from ...views import FlashMessageMixin -class PasskeyViewSet(AuthMixin, FlashMessageMixin, ModelViewSet): +class PasskeyViewSet(AuthMixin, FlashMessageMixin, JMSModelViewSet): serializer_class = PasskeySerializer permission_classes = (IsAuthenticated,) + def get_permissions(self): + if self.is_swagger_request(): + return super().get_permissions() + if self.action == 'register': + self.permission_classes = [ + IsAuthenticated, UserConfirmation.require(ConfirmType.PASSWORD) + ] + return super().get_permissions() + def get_queryset(self): return Passkey.objects.filter(user=self.request.user) diff --git a/apps/authentication/confirm/relogin.py b/apps/authentication/confirm/relogin.py index 2b27ef5fe..51e5d33c6 100644 --- a/apps/authentication/confirm/relogin.py +++ b/apps/authentication/confirm/relogin.py @@ -15,6 +15,7 @@ class ConfirmReLogin(BaseConfirm): display_name = 'Re-Login' def check(self): + return True return not self.user.is_password_authenticate() def authenticate(self, secret_key, mfa_type): diff --git a/apps/authentication/const.py b/apps/authentication/const.py index 1e06a4d35..bef4841b6 100644 --- a/apps/authentication/const.py +++ b/apps/authentication/const.py @@ -19,6 +19,7 @@ class ConfirmType(TextChoices): def get_can_confirm_types(cls, confirm_type): start = cls.values.index(confirm_type) types = cls.values[start:] + types = [tp for tp in types if tp != 'password'] types.reverse() return types diff --git a/apps/authentication/serializers/confirm.py b/apps/authentication/serializers/confirm.py index 5d747c656..dc7c997a6 100644 --- a/apps/authentication/serializers/confirm.py +++ b/apps/authentication/serializers/confirm.py @@ -7,4 +7,4 @@ from ..const import ConfirmType, MFAType class ConfirmSerializer(serializers.Serializer): confirm_type = serializers.ChoiceField(required=True, allow_blank=True, choices=ConfirmType.choices) mfa_type = serializers.ChoiceField(required=False, allow_blank=True, choices=MFAType.choices) - secret_key = EncryptedField(allow_blank=True) + secret_key = EncryptedField(allow_blank=True, required=False) diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py index 6d40f68e8..e7e561ffd 100644 --- a/apps/authentication/urls/api_urls.py +++ b/apps/authentication/urls/api_urls.py @@ -13,6 +13,7 @@ router.register('sso', api.SSOViewSet, 'sso') router.register('temp-tokens', api.TempTokenViewSet, 'temp-token') router.register('connection-token', api.ConnectionTokenViewSet, 'connection-token') router.register('super-connection-token', api.SuperConnectionTokenViewSet, 'super-connection-token') +router.register('confirm', api.UserConfirmationViewSet, 'confirm') urlpatterns = [ path('wecom/qr/unbind/', api.WeComQRUnBindForUserApi.as_view(), name='wecom-qr-unbind'), @@ -29,7 +30,6 @@ urlpatterns = [ name='feishu-event-subscription-callback'), path('auth/', api.TokenCreateApi.as_view(), name='user-auth'), - path('confirm/', api.ConfirmApi.as_view(), name='user-confirm'), path('confirm-oauth/', api.ConfirmBindORUNBindOAuth.as_view(), name='confirm-oauth'), path('tokens/', api.TokenCreateApi.as_view(), name='auth-token'), path('mfa/verify/', api.MFAChallengeVerifyApi.as_view(), name='mfa-verify'), diff --git a/apps/common/exceptions.py b/apps/common/exceptions.py index a8dfcafbd..0c22d6723 100644 --- a/apps/common/exceptions.py +++ b/apps/common/exceptions.py @@ -42,8 +42,11 @@ class ReferencedByOthers(JMSException): class UserConfirmRequired(JMSException): + status_code = status.HTTP_412_PRECONDITION_FAILED + def __init__(self, code=None): detail = { + 'type': 'user_confirm_required', 'code': code, 'detail': _('This action require confirm current user') } From 1ca912373fadbd5c80635156a15b53ff5bb654dc Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 13 Oct 2023 14:40:40 +0800 Subject: [PATCH 070/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=A1=AE=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/confirm.py | 17 +++++++++++++---- apps/authentication/backends/passkey/api.py | 15 +++++++++++++-- apps/authentication/serializers/confirm.py | 2 +- apps/authentication/urls/api_urls.py | 2 +- apps/common/exceptions.py | 3 +++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/apps/authentication/api/confirm.py b/apps/authentication/api/confirm.py index 4bd8d2c72..2bb371e48 100644 --- a/apps/authentication/api/confirm.py +++ b/apps/authentication/api/confirm.py @@ -4,10 +4,12 @@ import time from django.utils.translation import gettext_lazy as _ from rest_framework import status -from rest_framework.generics import RetrieveAPIView, CreateAPIView +from rest_framework.decorators import action +from rest_framework.generics import RetrieveAPIView from rest_framework.response import Response from authentication.permissions import UserConfirmation +from common.api import JMSGenericViewSet from common.permissions import IsValidUser from ..const import ConfirmType from ..serializers import ConfirmSerializer @@ -20,10 +22,17 @@ class ConfirmBindORUNBindOAuth(RetrieveAPIView): return Response('ok') -class ConfirmApi(RetrieveAPIView, CreateAPIView): +class UserConfirmationViewSet(JMSGenericViewSet): permission_classes = (IsValidUser,) serializer_class = ConfirmSerializer + @action(methods=['get'], detail=False) + def check(self, request): + confirm_type = request.query_params.get('confirm_type', 'password') + permission = UserConfirmation.require(confirm_type)() + permission.has_permission(request, self) + return Response('ok') + def get_confirm_backend(self, confirm_type): backend_classes = ConfirmType.get_prop_backends(confirm_type) if not backend_classes: @@ -34,12 +43,12 @@ class ConfirmApi(RetrieveAPIView, CreateAPIView): continue return backend - def retrieve(self, request, *args, **kwargs): + def list(self, request, *args, **kwargs): confirm_type = request.query_params.get('confirm_type', 'password') backend = self.get_confirm_backend(confirm_type) if backend is None: msg = _('This action require verify your MFA') - return Response(data={'error': msg}, status=status.HTTP_404_NOT_FOUND) + return Response(data={'error': msg}, status=status.HTTP_400_BAD_REQUEST) data = { 'confirm_type': backend.name, diff --git a/apps/authentication/backends/passkey/api.py b/apps/authentication/backends/passkey/api.py index 644636dd2..ace882a92 100644 --- a/apps/authentication/backends/passkey/api.py +++ b/apps/authentication/backends/passkey/api.py @@ -4,19 +4,30 @@ from django.shortcuts import render from django.utils.translation import gettext as _ from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated, AllowAny -from rest_framework.viewsets import ModelViewSet from authentication.mixins import AuthMixin +from common.api import JMSModelViewSet from .fido import register_begin, register_complete, auth_begin, auth_complete from .models import Passkey from .serializer import PasskeySerializer +from ...const import ConfirmType +from ...permissions import UserConfirmation from ...views import FlashMessageMixin -class PasskeyViewSet(AuthMixin, FlashMessageMixin, ModelViewSet): +class PasskeyViewSet(AuthMixin, FlashMessageMixin, JMSModelViewSet): serializer_class = PasskeySerializer permission_classes = (IsAuthenticated,) + def get_permissions(self): + if self.is_swagger_request(): + return super().get_permissions() + if self.action == 'register': + self.permission_classes = [ + IsAuthenticated, UserConfirmation.require(ConfirmType.PASSWORD) + ] + return super().get_permissions() + def get_queryset(self): return Passkey.objects.filter(user=self.request.user) diff --git a/apps/authentication/serializers/confirm.py b/apps/authentication/serializers/confirm.py index 5d747c656..dc7c997a6 100644 --- a/apps/authentication/serializers/confirm.py +++ b/apps/authentication/serializers/confirm.py @@ -7,4 +7,4 @@ from ..const import ConfirmType, MFAType class ConfirmSerializer(serializers.Serializer): confirm_type = serializers.ChoiceField(required=True, allow_blank=True, choices=ConfirmType.choices) mfa_type = serializers.ChoiceField(required=False, allow_blank=True, choices=MFAType.choices) - secret_key = EncryptedField(allow_blank=True) + secret_key = EncryptedField(allow_blank=True, required=False) diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py index 6d40f68e8..e7e561ffd 100644 --- a/apps/authentication/urls/api_urls.py +++ b/apps/authentication/urls/api_urls.py @@ -13,6 +13,7 @@ router.register('sso', api.SSOViewSet, 'sso') router.register('temp-tokens', api.TempTokenViewSet, 'temp-token') router.register('connection-token', api.ConnectionTokenViewSet, 'connection-token') router.register('super-connection-token', api.SuperConnectionTokenViewSet, 'super-connection-token') +router.register('confirm', api.UserConfirmationViewSet, 'confirm') urlpatterns = [ path('wecom/qr/unbind/', api.WeComQRUnBindForUserApi.as_view(), name='wecom-qr-unbind'), @@ -29,7 +30,6 @@ urlpatterns = [ name='feishu-event-subscription-callback'), path('auth/', api.TokenCreateApi.as_view(), name='user-auth'), - path('confirm/', api.ConfirmApi.as_view(), name='user-confirm'), path('confirm-oauth/', api.ConfirmBindORUNBindOAuth.as_view(), name='confirm-oauth'), path('tokens/', api.TokenCreateApi.as_view(), name='auth-token'), path('mfa/verify/', api.MFAChallengeVerifyApi.as_view(), name='mfa-verify'), diff --git a/apps/common/exceptions.py b/apps/common/exceptions.py index a8dfcafbd..0c22d6723 100644 --- a/apps/common/exceptions.py +++ b/apps/common/exceptions.py @@ -42,8 +42,11 @@ class ReferencedByOthers(JMSException): class UserConfirmRequired(JMSException): + status_code = status.HTTP_412_PRECONDITION_FAILED + def __init__(self, code=None): detail = { + 'type': 'user_confirm_required', 'code': code, 'detail': _('This action require confirm current user') } From 588723a76cccae7efd8b53ec68e6c7cae081ff71 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:08:56 +0800 Subject: [PATCH 071/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E7=99=BB=E5=BD=95=E9=80=9A=E7=9F=A5=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=20(#11834)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- .../templates/acls/asset_login_reminder.html | 2 +- apps/authentication/api/connection_token.py | 28 +++--- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 98 ++++++++++--------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 97 +++++++++--------- 6 files changed, 128 insertions(+), 105 deletions(-) diff --git a/apps/acls/templates/acls/asset_login_reminder.html b/apps/acls/templates/acls/asset_login_reminder.html index 8d63a2ff6..af836cab5 100644 --- a/apps/acls/templates/acls/asset_login_reminder.html +++ b/apps/acls/templates/acls/asset_login_reminder.html @@ -7,7 +7,7 @@

{% trans 'Account' %}: [{{ account }}]


-

{% trans 'The user has just successfully logged into the asset. Please ensure that this is an authorized operation. If you suspect that this is an unauthorized access, please take appropriate measures immediately.' %}

+

{% trans 'The user has just logged in to the asset. Please ensure that this is an authorized operation. If you suspect that this is an unauthorized access, please take appropriate measures immediately.' %}

{% trans 'Thank you' %}!

diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index f51351b67..c14df034d 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -361,9 +361,10 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView if account.has_secret: data['input_secret'] = '' + input_username = data.get('input_username', '') if account.username != AliasAccount.INPUT: data['input_username'] = '' - ticket = self._validate_acl(user, asset, account) + ticket = self._validate_acl(user, asset, account, input_username) if ticket: data['from_ticket'] = ticket data['is_active'] = False @@ -382,10 +383,13 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView return account @staticmethod - def _record_operate_log(acl, asset): + def _record_operate_log(acl, asset, input_username): from audits.handler import create_or_update_operate_log with tmp_to_org(asset.org_id): - after = {str(_('Assets')): str(asset)} + after = { + str(_('Assets')): str(asset), + str(_('Account')): input_username + } object_name = acl._meta.object_name resource_type = acl._meta.verbose_name create_or_update_operate_log( @@ -393,7 +397,7 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView after=after, object_name=object_name ) - def _validate_acl(self, user, asset, account): + def _validate_acl(self, user, asset, account, input_username): from acls.models import LoginAssetACL acls = LoginAssetACL.filter_queryset(user=user, asset=asset, account=account) ip = get_request_ip(self.request) @@ -401,19 +405,19 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView if not acl: return if acl.is_action(acl.ActionChoices.accept): - self._record_operate_log(acl, asset) + self._record_operate_log(acl, asset, input_username) return if acl.is_action(acl.ActionChoices.reject): - self._record_operate_log(acl, asset) + self._record_operate_log(acl, asset, input_username) msg = _('ACL action is reject: {}({})'.format(acl.name, acl.id)) raise JMSException(code='acl_reject', detail=msg) if acl.is_action(acl.ActionChoices.review): if not self.request.query_params.get('create_ticket'): msg = _('ACL action is review') raise JMSException(code='acl_review', detail=msg) - self._record_operate_log(acl, asset) + self._record_operate_log(acl, asset, input_username) ticket = LoginAssetACL.create_login_asset_review_ticket( - user=user, asset=asset, account_username=account.username, + user=user, asset=asset, account_username=input_username, assignees=acl.reviewers.all(), org_id=asset.org_id ) return ticket @@ -421,10 +425,12 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView reviewers = acl.reviewers.all() if not reviewers: return - account_username = account.username - self._record_operate_log(acl, asset) + + self._record_operate_log(acl, asset, input_username) for reviewer in reviewers: - AssetLoginReminderMsg(reviewer, asset, user, account_username).publish_async() + AssetLoginReminderMsg( + reviewer, asset, user, input_username + ).publish_async() class SuperConnectionTokenViewSet(ConnectionTokenViewSet): diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index c1648acb8..50a9d1152 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:38bd8a6653f3f4dc63552b1c86379f82067f9f9daac227bacb3af3f9f62134f9 -size 161704 +oid sha256:d7366743ab156daec72625cd241f76420c551c6a48330266a57e3d0b808d4164 +size 162675 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 4ba81a5a4..f3fa1ddb4 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: 2023-10-12 09:51+0800\n" +"POT-Creation-Date: 2023-10-13 15:03+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -263,10 +263,12 @@ msgstr "ソース ID" #: accounts/models/account.py:60 #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 -#: acls/serializers/base.py:124 assets/serializers/asset/common.py:125 -#: assets/serializers/gateway.py:28 audits/models.py:57 ops/models/base.py:18 -#: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 -#: terminal/backends/command/models.py:18 terminal/models/session/session.py:33 +#: acls/serializers/base.py:124 acls/templates/acls/asset_login_reminder.html:7 +#: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 +#: audits/models.py:57 authentication/api/connection_token.py:391 +#: ops/models/base.py:18 perms/models/asset_permission.py:70 +#: perms/serializers/permission.py:39 terminal/backends/command/models.py:18 +#: terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 #: tickets/models/ticket/command_confirm.py:13 xpack/plugins/cloud/models.py:89 @@ -712,7 +714,7 @@ msgstr "編集済み" #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 -#: authentication/api/connection_token.py:386 ops/models/base.py:17 +#: authentication/api/connection_token.py:390 ops/models/base.py:17 #: ops/models/job.py:139 ops/serializers/job.py:21 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" @@ -789,7 +791,7 @@ msgid "Key password" msgstr "キーパスワード" #: accounts/serializers/account/base.py:78 -#: assets/serializers/asset/common.py:311 +#: assets/serializers/asset/common.py:338 msgid "Spec info" msgstr "特別情報" @@ -1024,7 +1026,7 @@ msgid "Users" msgstr "ユーザー" #: acls/models/base.py:98 assets/models/automations/base.py:17 -#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:310 +#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:337 #: rbac/tree.py:35 msgid "Accounts" msgstr "アカウント" @@ -1173,17 +1175,16 @@ msgstr "期間" msgid "Respectful" msgstr "尊敬する" -#: acls/templates/acls/asset_login_reminder.html:9 +#: acls/templates/acls/asset_login_reminder.html:10 msgid "" -"The user has just successfully logged into the asset. Please ensure that " -"this is an authorized operation. If you suspect that this is an unauthorized " -"access, please take appropriate measures immediately." +"The user has just logged in to the asset. Please ensure that this is an " +"authorized operation. If you suspect that this is an unauthorized access, " +"please take appropriate measures immediately." msgstr "" -"ユーザーは資産に正常にログインしたばかりです。許可されたアクションであること" -"を確認してください。不正アクセスが疑われる場合は、すぐに適切な措置を取ってく" -"ださい。" +"ユーザーは資産にログインしています。許可されたアクションであることを確認して" +"ください。不正アクセスが疑われる場合は、すぐに適切な措置を取ってください。" -#: acls/templates/acls/asset_login_reminder.html:11 +#: acls/templates/acls/asset_login_reminder.html:12 #: acls/templates/acls/user_login_reminder.html:13 msgid "Thank you" msgstr "ありがとうございます。" @@ -1227,7 +1228,7 @@ msgstr "アプリケーション" msgid "Can match application" msgstr "アプリケーションを一致させることができます" -#: assets/api/asset/asset.py:157 +#: assets/api/asset/asset.py:194 msgid "Cannot create asset directly, you should create a host or other" msgstr "" "資産を直接作成することはできません。ホストまたはその他を作成する必要がありま" @@ -1646,7 +1647,7 @@ msgstr "ドメイン" msgid "Labels" msgstr "ラベル" -#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:312 +#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:339 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "資産ハードウェア情報の収集" @@ -1983,7 +1984,7 @@ msgid "Node path" msgstr "ノードパスです" #: assets/serializers/asset/common.py:145 -#: assets/serializers/asset/common.py:313 +#: assets/serializers/asset/common.py:340 msgid "Auto info" msgstr "自動情報" @@ -1999,19 +2000,10 @@ msgstr "ポート番号が範囲外です (0-65535)" msgid "Protocol is required: {}" msgstr "プロトコルが必要です: {}" -#: assets/serializers/asset/database.py:13 +#: assets/serializers/asset/database.py:12 msgid "Default database" msgstr "デフォルト・データベース" -#: assets/serializers/asset/database.py:28 common/db/fields.py:570 -#: common/db/fields.py:575 common/serializers/fields.py:104 -#: tickets/serializers/ticket/common.py:58 -#: xpack/plugins/cloud/serializers/account_attrs.py:56 -#: xpack/plugins/cloud/serializers/account_attrs.py:79 -#: xpack/plugins/cloud/serializers/account_attrs.py:143 -msgid "This field is required." -msgstr "このフィールドは必須です。" - #: assets/serializers/asset/gpt.py:20 msgid "" "If the server cannot directly connect to the API address, you need set up an " @@ -2539,33 +2531,33 @@ msgstr "外部ストレージへのFTPファイルのアップロード" msgid "Access keys can be created at most 10" msgstr "" -#: authentication/api/confirm.py:40 +#: authentication/api/confirm.py:41 msgid "This action require verify your MFA" msgstr "この操作には、MFAを検証する必要があります" -#: authentication/api/connection_token.py:261 +#: authentication/api/connection_token.py:262 msgid "Reusable connection token is not allowed, global setting not enabled" msgstr "" "再使用可能な接続トークンの使用は許可されていません。グローバル設定は有効に" "なっていません" -#: authentication/api/connection_token.py:357 +#: authentication/api/connection_token.py:358 msgid "Anonymous account is not supported for this asset" msgstr "匿名アカウントはこのプロパティではサポートされていません" -#: authentication/api/connection_token.py:376 +#: authentication/api/connection_token.py:378 msgid "Account not found" msgstr "アカウントが見つかりません" -#: authentication/api/connection_token.py:379 +#: authentication/api/connection_token.py:381 msgid "Permission expired" msgstr "承認の有効期限が切れています" -#: authentication/api/connection_token.py:406 +#: authentication/api/connection_token.py:412 msgid "ACL action is reject: {}({})" msgstr "ACL アクションは拒否です: {}({})" -#: authentication/api/connection_token.py:410 +#: authentication/api/connection_token.py:416 msgid "ACL action is review" msgstr "ACL アクションはレビューです" @@ -2815,15 +2807,15 @@ msgstr "パスワードが無効です" msgid "Please wait for %s seconds before retry" msgstr "%s 秒後に再試行してください" -#: authentication/errors/redirect.py:85 authentication/mixins.py:324 +#: authentication/errors/redirect.py:85 authentication/mixins.py:323 msgid "Your password is too simple, please change it for security" msgstr "パスワードがシンプルすぎるので、セキュリティのために変更してください" -#: authentication/errors/redirect.py:93 authentication/mixins.py:331 +#: authentication/errors/redirect.py:93 authentication/mixins.py:330 msgid "You should to change your password before login" msgstr "ログインする前にパスワードを変更する必要があります" -#: authentication/errors/redirect.py:101 authentication/mixins.py:338 +#: authentication/errors/redirect.py:101 authentication/mixins.py:337 msgid "Your password has expired, please reset before logging in" msgstr "" "パスワードの有効期限が切れました。ログインする前にリセットしてください。" @@ -2938,11 +2930,11 @@ msgstr "" "管理者は「ユーザーソースからのみログインを許可」をオンにしており、現在のユー" "ザーソースは {} です。管理者に連絡してください。" -#: authentication/mixins.py:274 +#: authentication/mixins.py:273 msgid "The MFA type ({}) is not enabled" msgstr "MFAタイプ ({}) が有効になっていない" -#: authentication/mixins.py:314 +#: authentication/mixins.py:313 msgid "Please change your password" msgstr "パスワードを変更してください" @@ -3577,6 +3569,14 @@ msgstr "無効なタイプです。all、ids、またはattrsでなければな msgid "Invalid ids for ids, should be a list" msgstr "無効なID、リストでなければなりません" +#: common/db/fields.py:570 common/db/fields.py:575 +#: common/serializers/fields.py:104 tickets/serializers/ticket/common.py:58 +#: xpack/plugins/cloud/serializers/account_attrs.py:56 +#: xpack/plugins/cloud/serializers/account_attrs.py:79 +#: xpack/plugins/cloud/serializers/account_attrs.py:143 +msgid "This field is required." +msgstr "このフィールドは必須です。" + #: common/db/fields.py:573 common/db/fields.py:578 msgid "Invalid attrs, should be a list of dict" msgstr "無効な属性、dictリストでなければなりません" @@ -8145,12 +8145,14 @@ msgid "Task strategy" msgstr "ミッション戦略です" #: xpack/plugins/cloud/models.py:285 -msgid "Equal" -msgstr "等しい" +msgid "Exact" +msgstr "" #: xpack/plugins/cloud/models.py:286 -msgid "Not equal" -msgstr "不等于" +#, fuzzy +#| msgid "No" +msgid "Not" +msgstr "否" #: xpack/plugins/cloud/models.py:287 msgid "In" @@ -8581,3 +8583,9 @@ msgstr "エンタープライズプロフェッショナル版" #: xpack/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" + +#~ msgid "Equal" +#~ msgstr "等しい" + +#~ msgid "Not equal" +#~ msgstr "不等于" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index bac130e24..98c61cf62 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:25c1d449875189c84c0d586792424d70651a1f86f55b93332287dfef44db2f2f -size 132289 +oid sha256:4be95c6d05beb08730dd3cee943e7f84aa94e8e711a41e2fce5b0aebf84e831b +size 133014 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 5099f0a86..5c375d6c1 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: 2023-10-12 09:51+0800\n" +"POT-Creation-Date: 2023-10-13 15:03+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -262,10 +262,12 @@ msgstr "来源 ID" #: accounts/models/account.py:60 #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 -#: acls/serializers/base.py:124 assets/serializers/asset/common.py:125 -#: assets/serializers/gateway.py:28 audits/models.py:57 ops/models/base.py:18 -#: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 -#: terminal/backends/command/models.py:18 terminal/models/session/session.py:33 +#: acls/serializers/base.py:124 acls/templates/acls/asset_login_reminder.html:7 +#: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 +#: audits/models.py:57 authentication/api/connection_token.py:391 +#: ops/models/base.py:18 perms/models/asset_permission.py:70 +#: perms/serializers/permission.py:39 terminal/backends/command/models.py:18 +#: terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 #: tickets/models/ticket/command_confirm.py:13 xpack/plugins/cloud/models.py:89 @@ -710,7 +712,7 @@ msgstr "已修改" #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 -#: authentication/api/connection_token.py:386 ops/models/base.py:17 +#: authentication/api/connection_token.py:390 ops/models/base.py:17 #: ops/models/job.py:139 ops/serializers/job.py:21 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" @@ -787,7 +789,7 @@ msgid "Key password" msgstr "密钥密码" #: accounts/serializers/account/base.py:78 -#: assets/serializers/asset/common.py:311 +#: assets/serializers/asset/common.py:338 msgid "Spec info" msgstr "特殊信息" @@ -1021,7 +1023,7 @@ msgid "Users" msgstr "用户管理" #: acls/models/base.py:98 assets/models/automations/base.py:17 -#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:310 +#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:337 #: rbac/tree.py:35 msgid "Accounts" msgstr "账号管理" @@ -1169,16 +1171,16 @@ msgstr "时段" msgid "Respectful" msgstr "尊敬的" -#: acls/templates/acls/asset_login_reminder.html:9 +#: acls/templates/acls/asset_login_reminder.html:10 msgid "" -"The user has just successfully logged into the asset. Please ensure that " -"this is an authorized operation. If you suspect that this is an unauthorized " -"access, please take appropriate measures immediately." +"The user has just logged in to the asset. Please ensure that this is an " +"authorized operation. If you suspect that this is an unauthorized access, " +"please take appropriate measures immediately." msgstr "" -"用户刚刚成功登录到资产。请确保这是授权的操作。如果您怀疑这是一个未经授权的访" -"问,请立即采取适当的措施。" +"用户刚刚在登录资产。请确保这是授权的操作。如果您怀疑这是一个未经授权的访问," +"请立即采取适当的措施。" -#: acls/templates/acls/asset_login_reminder.html:11 +#: acls/templates/acls/asset_login_reminder.html:12 #: acls/templates/acls/user_login_reminder.html:13 msgid "Thank you" msgstr "谢谢" @@ -1221,7 +1223,7 @@ msgstr "应用程序" msgid "Can match application" msgstr "匹配应用" -#: assets/api/asset/asset.py:157 +#: assets/api/asset/asset.py:194 msgid "Cannot create asset directly, you should create a host or other" msgstr "不能直接创建资产, 你应该创建主机或其他资产" @@ -1638,7 +1640,7 @@ msgstr "网域" msgid "Labels" msgstr "标签管理" -#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:312 +#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:339 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "收集资产硬件信息" @@ -1973,7 +1975,7 @@ msgid "Node path" msgstr "节点路径" #: assets/serializers/asset/common.py:145 -#: assets/serializers/asset/common.py:313 +#: assets/serializers/asset/common.py:340 msgid "Auto info" msgstr "自动化信息" @@ -1989,19 +1991,10 @@ msgstr "端口超出范围 (0-65535)" msgid "Protocol is required: {}" msgstr "协议是必填的: {}" -#: assets/serializers/asset/database.py:13 +#: assets/serializers/asset/database.py:12 msgid "Default database" msgstr "默认数据库" -#: assets/serializers/asset/database.py:28 common/db/fields.py:570 -#: common/db/fields.py:575 common/serializers/fields.py:104 -#: tickets/serializers/ticket/common.py:58 -#: xpack/plugins/cloud/serializers/account_attrs.py:56 -#: xpack/plugins/cloud/serializers/account_attrs.py:79 -#: xpack/plugins/cloud/serializers/account_attrs.py:143 -msgid "This field is required." -msgstr "该字段是必填项。" - #: assets/serializers/asset/gpt.py:20 msgid "" "If the server cannot directly connect to the API address, you need set up an " @@ -2522,31 +2515,31 @@ msgstr "上传 FTP 文件到外部存储" msgid "Access keys can be created at most 10" msgstr "" -#: authentication/api/confirm.py:40 +#: authentication/api/confirm.py:41 msgid "This action require verify your MFA" msgstr "该操作需要验证您的 MFA, 请先开启并配置" -#: authentication/api/connection_token.py:261 +#: authentication/api/connection_token.py:262 msgid "Reusable connection token is not allowed, global setting not enabled" msgstr "不允许使用可重复使用的连接令牌,未启用全局设置" -#: authentication/api/connection_token.py:357 +#: authentication/api/connection_token.py:358 msgid "Anonymous account is not supported for this asset" msgstr "匿名账号不支持当前资产" -#: authentication/api/connection_token.py:376 +#: authentication/api/connection_token.py:378 msgid "Account not found" msgstr "账号未找到" -#: authentication/api/connection_token.py:379 +#: authentication/api/connection_token.py:381 msgid "Permission expired" msgstr "授权已过期" -#: authentication/api/connection_token.py:406 +#: authentication/api/connection_token.py:412 msgid "ACL action is reject: {}({})" msgstr "ACL 动作是拒绝: {}({})" -#: authentication/api/connection_token.py:410 +#: authentication/api/connection_token.py:416 msgid "ACL action is review" msgstr "ACL 动作是复核" @@ -2787,15 +2780,15 @@ msgstr "您的密码无效" msgid "Please wait for %s seconds before retry" msgstr "请在 %s 秒后重试" -#: authentication/errors/redirect.py:85 authentication/mixins.py:324 +#: authentication/errors/redirect.py:85 authentication/mixins.py:323 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" -#: authentication/errors/redirect.py:93 authentication/mixins.py:331 +#: authentication/errors/redirect.py:93 authentication/mixins.py:330 msgid "You should to change your password before login" msgstr "登录完成前,请先修改密码" -#: authentication/errors/redirect.py:101 authentication/mixins.py:338 +#: authentication/errors/redirect.py:101 authentication/mixins.py:337 msgid "Your password has expired, please reset before logging in" msgstr "您的密码已过期,先修改再登录" @@ -2906,11 +2899,11 @@ msgid "" " The current user source is {}. Please contact the administrator." msgstr "管理员已开启'仅允许从用户来源登录',当前用户来源为{},请联系管理员。" -#: authentication/mixins.py:274 +#: authentication/mixins.py:273 msgid "The MFA type ({}) is not enabled" msgstr "该 MFA ({}) 方式没有启用" -#: authentication/mixins.py:314 +#: authentication/mixins.py:313 msgid "Please change your password" msgstr "请修改密码" @@ -3533,6 +3526,14 @@ msgstr "无效类型,应为 all、ids 或 attrs" msgid "Invalid ids for ids, should be a list" msgstr "无效的ID,应为列表" +#: common/db/fields.py:570 common/db/fields.py:575 +#: common/serializers/fields.py:104 tickets/serializers/ticket/common.py:58 +#: xpack/plugins/cloud/serializers/account_attrs.py:56 +#: xpack/plugins/cloud/serializers/account_attrs.py:79 +#: xpack/plugins/cloud/serializers/account_attrs.py:143 +msgid "This field is required." +msgstr "该字段是必填项。" + #: common/db/fields.py:573 common/db/fields.py:578 msgid "Invalid attrs, should be a list of dict" msgstr "无效的属性,应为dict列表" @@ -8029,12 +8030,14 @@ msgid "Task strategy" msgstr "任务策略" #: xpack/plugins/cloud/models.py:285 -msgid "Equal" -msgstr "等于" +msgid "Exact" +msgstr "" #: xpack/plugins/cloud/models.py:286 -msgid "Not equal" -msgstr "不等于" +#, fuzzy +#| msgid "No" +msgid "Not" +msgstr "否" #: xpack/plugins/cloud/models.py:287 msgid "In" @@ -8462,3 +8465,9 @@ msgstr "企业专业版" #: xpack/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "企业旗舰版" + +#~ msgid "Equal" +#~ msgstr "等于" + +#~ msgid "Not equal" +#~ msgstr "不等于" From ffc057f844a85cac63ab5c0596eb2f96dc0ceb1f Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:33:35 +0800 Subject: [PATCH 072/114] =?UTF-8?q?fix:=20=E8=B4=A6=E5=8F=B7=E6=94=B9?= =?UTF-8?q?=E5=AF=86-=E6=89=A7=E8=A1=8C=E5=88=97=E8=A1=A8-=E8=AF=A6?= =?UTF-8?q?=E6=83=85-=E4=BB=BB=E5=8A=A1=E8=AE=B0=E5=BD=95=EF=BC=9A?= =?UTF-8?q?=E6=A8=A1=E7=B3=8A=E6=90=9C=E7=B4=A2500=20(#11835)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/api/automations/change_secret.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/apps/accounts/api/automations/change_secret.py b/apps/accounts/api/automations/change_secret.py index 2af9afd09..e334403f5 100644 --- a/apps/accounts/api/automations/change_secret.py +++ b/apps/accounts/api/automations/change_secret.py @@ -5,8 +5,7 @@ from rest_framework import mixins from accounts import serializers from accounts.const import AutomationTypes -from accounts.models import ChangeSecretAutomation, ChangeSecretRecord, AutomationExecution -from common.utils import get_object_or_none +from accounts.models import ChangeSecretAutomation, ChangeSecretRecord from orgs.mixins.api import OrgBulkModelViewSet, OrgGenericViewSet from .base import ( AutomationAssetsListApi, AutomationRemoveAssetApi, AutomationAddAssetApi, @@ -30,8 +29,8 @@ class ChangeSecretAutomationViewSet(OrgBulkModelViewSet): class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet): serializer_class = serializers.ChangeSecretRecordSerializer - filter_fields = ['asset', 'execution_id'] - search_fields = ['asset__hostname'] + filter_fields = ('asset', 'execution_id') + search_fields = ('asset__address',) def get_queryset(self): return ChangeSecretRecord.objects.filter( @@ -41,10 +40,7 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet): def filter_queryset(self, queryset): queryset = super().filter_queryset(queryset) eid = self.request.query_params.get('execution_id') - execution = get_object_or_none(AutomationExecution, pk=eid) - if execution: - queryset = queryset.filter(execution=execution) - return queryset + return queryset.filter(execution_id=eid) class ChangSecretExecutionViewSet(AutomationExecutionViewSet): From ea0e85241253a3a274503057449a2e9922577385 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:21:20 +0800 Subject: [PATCH 073/114] =?UTF-8?q?fix:=20rdp=20=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=8F=AF=E8=BF=9E=E6=8E=A5=E6=80=A7=E5=A4=B1=E8=B4=A5=20(#1183?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/automations/verify_account/custom/rdp/main.yml | 2 +- apps/assets/automations/ping/custom/rdp/main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/accounts/automations/verify_account/custom/rdp/main.yml b/apps/accounts/automations/verify_account/custom/rdp/main.yml index b0c7cbe4f..017f4bab3 100644 --- a/apps/accounts/automations/verify_account/custom/rdp/main.yml +++ b/apps/accounts/automations/verify_account/custom/rdp/main.yml @@ -8,7 +8,7 @@ - name: Verify account (pyfreerdp) rdp_ping: login_host: "{{ jms_asset.address }}" - login_port: "{{ jms_asset.port }}" + login_port: "{{ jms_asset.protocols | selectattr('name', 'equalto', 'rdp') | map(attribute='port') | first }}" login_user: "{{ account.username }}" login_password: "{{ account.secret }}" login_secret_type: "{{ account.secret_type }}" diff --git a/apps/assets/automations/ping/custom/rdp/main.yml b/apps/assets/automations/ping/custom/rdp/main.yml index 75e40c027..a68670998 100644 --- a/apps/assets/automations/ping/custom/rdp/main.yml +++ b/apps/assets/automations/ping/custom/rdp/main.yml @@ -10,6 +10,6 @@ login_user: "{{ jms_account.username }}" login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" - login_port: "{{ jms_asset.port }}" + login_port: "{{ jms_asset.protocols | selectattr('name', 'equalto', 'rdp') | map(attribute='port') | first }}" login_secret_type: "{{ jms_account.secret_type }}" login_private_key_path: "{{ jms_account.private_key_path }}" From 1daf1acaf327fd2f7b1f77174a07a91987a6d134 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 13 Oct 2023 16:31:05 +0800 Subject: [PATCH 074/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20access=20k?= =?UTF-8?q?ey?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/access_key.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/authentication/api/access_key.py b/apps/authentication/api/access_key.py index 1ce992629..027fdf75f 100644 --- a/apps/authentication/api/access_key.py +++ b/apps/authentication/api/access_key.py @@ -7,6 +7,8 @@ from rest_framework.response import Response from common.api import JMSModelViewSet from rbac.permissions import RBACPermission +from ..const import ConfirmType +from ..permissions import UserConfirmation from ..serializers import AccessKeySerializer, AccessKeyCreateSerializer @@ -27,20 +29,20 @@ class AccessKeyViewSet(JMSModelViewSet): if self.action == 'create': self.permission_classes = [ - RBACPermission, + RBACPermission, UserConfirmation.require(ConfirmType.PASSWORD) ] return super().get_permissions() - def create(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - key = self.perform_create(serializer) - serializer = self.get_serializer(instance=key) - return Response(serializer.data, status=201) - def perform_create(self, serializer): user = self.request.user if user.access_keys.count() >= 10: raise serializers.ValidationError(_('Access keys can be created at most 10')) key = user.create_access_key() return key + + def create(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + key = self.perform_create(serializer) + serializer = self.get_serializer(instance=key) + return Response(serializer.data, status=201) From d6b450f32a76f9c2254e12f92c954eecba382aff Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 13 Oct 2023 16:33:25 +0800 Subject: [PATCH 075/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20ak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/confirm/relogin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/authentication/confirm/relogin.py b/apps/authentication/confirm/relogin.py index 51e5d33c6..2b27ef5fe 100644 --- a/apps/authentication/confirm/relogin.py +++ b/apps/authentication/confirm/relogin.py @@ -15,7 +15,6 @@ class ConfirmReLogin(BaseConfirm): display_name = 'Re-Login' def check(self): - return True return not self.user.is_password_authenticate() def authenticate(self, secret_key, mfa_type): From d7ac08f6d9c7e29bb4cfa9ddef9826f7f8e18ec6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 13 Oct 2023 16:36:23 +0800 Subject: [PATCH 076/114] =?UTF-8?q?perf:=20=E5=8E=BB=E6=8E=89=20debug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/const.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/authentication/const.py b/apps/authentication/const.py index bef4841b6..1e06a4d35 100644 --- a/apps/authentication/const.py +++ b/apps/authentication/const.py @@ -19,7 +19,6 @@ class ConfirmType(TextChoices): def get_can_confirm_types(cls, confirm_type): start = cls.values.index(confirm_type) types = cls.values[start:] - types = [tp for tp in types if tp != 'password'] types.reverse() return types From ac11790192c8731a8354b28c29768e4760ebca50 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:51:34 +0800 Subject: [PATCH 077/114] =?UTF-8?q?perf:=20=E6=9B=BF=E6=8D=A2iphone=20mfa?= =?UTF-8?q?=20=E4=BA=8C=E7=BB=B4=E7=A0=81=E5=9B=BE=E7=89=87=20(#11839)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/static/img/authenticator_iphone.png | Bin 6144 -> 30381 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/static/img/authenticator_iphone.png b/apps/static/img/authenticator_iphone.png index ed31d38b3205c7963dde9e51e8aa2a3821e0baf5..57f9e69104715be49c255fb5f94164ea746d064e 100644 GIT binary patch literal 30381 zcmZs@dpy(q|39wFb=B3?L0u)4;!;;hjM71Jsw72Gq!~tG$#Lb_W}~jo6e_(Wr%Eix zWo*kfb9E%IoX-|3nbRCHA zQ&Lh|cI@b32PLJSGQqFxUl)R}5FCz@mL3Dls@xZ$Om-A=x2`Syk@7h+fa6>~v(`Le3O4?j8>F_w9q%TM^f!`VFc zt~~I&VeKxh#p^1K7tgTEE26k0QX5>{QkYxjmoXDHGycggtz%DqF4mrmE+AG%&sr?s zc0b7@sy+CqgjDVC?s$nfW7>naG+(5Y@le~|Wu!STNqI%@>P6tK|M~XAHt@#--{1qA zw6=`vWlglI{9D*HXaj@{`?AbHCWUey8VkL0P1fwbo_5#O#9waqI%z>K<8OSxbwQ z2EiWF-D=qTg{8gg;f}&YhNZ_k>8xDDoQ6GS8-j;ceJLm?fK)Wihs1SM4-42jZo?&l z>+6fp1alMo*%aM}CL^!k(HOcO$>uGked+!(`?)cDlu(un+5d>{UX}5RB&jxH+rV|r z&4Md7C7r=rzzx3Q{dz69IR^|)PsCq1aYD9R-Q$?>kDZYd0qBBfmbY9q_V^{@$ctDS@l!RIhBYz+BPU0{LE3hy{jBfuvbtJ(W5FZ& zR&Zp)aaqE=z7?G}wdAc7I-Sn*9W3*v%GV3N$;qFE#Q3u1|8;-C`xTW*-C=5Er*K0< z>5q@xN0`MnPXL z5Cm0^ATQfKZphP-2&cpCyH#hZr`~Iue@mz#OFMmJ4{Y^|+zzXMSGX%X`pGI4kvtC+ z<1?kPVzz*q`es#TrNf|~A5qYhmW3K{lx(L?d{P>^E%1N%vdm@H?nmse|Mh7T2O{@% z@n-$B>={!w5>DL$%yrHRtrsN)jgt#AYYxF7C4{Em%Q1pP0dASrziw>OfX#OVe?G8T@!hR<&-SVFN=z2%oUa<&_Lpe!RC;cISdTQz=eue9KE)rml&GSl zWOFNM?Y(OX$MfAk9p*o$@Z!$-Gk^IB_i*SH{wi%d=O8~K&Peq~=((Z4-&!p_oG)n_ zHzSC>nbGQoCx#4P&Nnh>IJ!!Q5^6F>(|KODN4$#Gv2b@%>9Y6RiBE(1mrfgq{pPr` zN%yX2f%)Ke6;dPxk!HWOw{mOA6<+N%@4l>?uY9+=KPf5wk<8L-ESIE-N;o80FuJzn z&(8LCUs<2UT&ZC3s;#fn`}uP-eBCH__(4om65b;K6tvjdT)a~49eO*lHnPi$P@8U6 z=(5YFriA0yygiAza`O@AE+#%B_L;CStC_*r3l|9sv4(|Z6E1MHEGZiEBht(ERAcsu z5UZ&kZ3|xlQbqKDCuruw(_LbS>Dbf`ccEo{pdUMtr`jX5Tw;Yx!&#Jl?iU z@g_JX;il;Rtu9f$$v$pn^$pL?qi}wADB5P^{4GuG#Xhe3l2tVF)L0RRI6fFeQEB(E zj@O{)XEB}M-BMw?9Zq^wqq$GzjC= z9w%)R)Q3OD%}wb!zxw-8b4{8FiVDh~MulzkhP!E@0}im3y}j%R-*CnE1llKNHXcanXboVD9~B`wVoLd9~@!=U0pD#Uhrd+!{ksMGRiPKgY`ksWBXPD$>>*v7@WuYG z{WIe1igk8#fL5_b1p6} zdKn=##>nv-&fOC|OG;wiSfa+-G&6`Ur;$LrKb?AnNeMD)MuzpR~?pPyTj9%3l@H&t}+N+=!UuUBw?j?iOE3-m%i`PbHP1z zBbI4Ca!fT$aK%2g8uM@lz6K4nf{|fNJ+)r{^;hufW@9Hm_#W!0t-QZ^`KG4-E$SEep^)c z@S@NB#QsmO+}5~xokvc+v=j>3BaCd@&jFo1;k|fmN&3cu?6-@FWNV`Wus~>OPn}Dl}>O8~! zzB4vqVneWL!?K0AcmF=%-o2lby$F0R86pd`Q5n9;zLg1D5amQlZ>~VsYrgcUFVqd~ ztGzznEy{;A^D;Bv(e!DSQ?wJEmC3R_Z8}4e>xJk!IuCo1r!!NU>b%EEoU;BCQpwm z)<)vM{mL}uvoov}3(bvt%r(=f zOCC0m$wO^B7bPzK_c5_~BmKEdY)0Q`xPvWzw4R*dqq9?O=M@6##?k-{hrd4Od5a>~ z9jpv*86ey5^da~jst?6_dib4uuQbaQd=lPRZlybQV~Lf-OozJmi^lk03bPtO$xrX0 zAPG?|+`Qf`x{~zdyBPO~)lp)Kp7ZiWtrCFu7mrS2i{*6}` z{dW4joO)`bO)4E7n&Ol3lbN!@)yUR|$G~;3MMZ>%vxwQEM0P}Sfy&FQBC z-NM&zg9Cgo4|mX^ig-;?%vfW_Y?Bx?7B@Q54*@y(;$$nuR~lQ#FY#z$lzCMwN25C9 z0eF2yYIZ_nX4@*vdvoc5yuEN=Ml@HTmo?`B`X%I3{p1mB+HBJGV>z7UPRzlXePpv( z8&Xq@L$Z-Zv_Db(e?BA9G>3FmpY06fbItsS9`gF%pFxIn?~D%s(xUKPoYHQ?>yMF6 zMwU-tYJ@tyT(X=+TW{!n^uq>FW>|<0A5N~-?lAkrV2c@$J}PrYwNHYF%8J#7o6l zukE&3Osqht=YN1<6E4}@HhJMmE)6^6_^BxCGpA}Mal9DLOWd%@JGwbI<7T1(nO70P zS$*nBtKf59&!B765!^9d>TqpTzJbCCOg>aK_C>=W{>ZlPYp~)P!&evU1?4W#W64|D zw`PY&cZx}9NME-|V$f~+aaD_%dSCL8Uk9!da3ZBU|LM8E=`>2N;|WdjN@cD?8q`KB zG2*XcZx2_Wg4)r08$|a|Y7QD1IX{QZRc2L=TZ zS0zp^i38y0)~)yt`2^&Iim=|mz`*4L2*C_)x6v@_x|}HhILo!gkZ)^H=3NR%Bs7j~ zC_Z0JBUNmScx~fpdy6|j1%MLF>#8Q%g>_~Vi}?V0ot}1sFKS(r$x9dv7N;{9MnMrn zZQnfmiCE%57Q`RDY4qAu&!wcY`P#v23o+cUe=TvNFI>Lif~-oHT9V&s>IwglehG=6 zN4{&PuqRGXr%l7c!(BA?hrWcbY4|Tu(=T!BrM%LN9NGz6On*}WG`o%&AIe+4&hWih z+x*93^j7{<3sa9DDJok&&@T$ES8TN410xf6XXZrJ^zQC?aLSGlG4ch*sB?0XK-Z&) z^g$K<+E3QySPt+wjCA7IZ@LeC3FN`1oI)3u5f9GgOCNk`OYd6Xs#Yg4HK)HWg3$TS z;6t+RZZK{TZ%K0VG`3A1zq&}{;`rCEZ>~X_QIqEY%yiHl?iN$$rl|TVOnwww5GHIX z8!~S!CHOZQ~px)o#|H_SFIMZ95g7P=o)pVqOv)82J zEC%O78FM|c*U4Sfs^5)m))I;A#@)rI4=wzrGT{C@&+ZB_sddRdPmj+FR^R(~Yh8Aa zcDnZOsGo@%aYFHE7z$pZQ0g}g%}zdjZmX{J^ia*Naq(`mzRw*J`qk&sU;h1V>(?ur zD^u6FKFF)^vgY?K{{!{!?)v90{}w$s^T<6~zG#EAELPU@=^-Q)@$oXQd?ci^{=xI} zD4uV!7(f}wL&|C`HB2@qvjpJ&jH^;UiI+=OH~^Fa z0U+~iPIbB&yStA&9y3F#ezFHr*|6{Ar6<|hXgj#0CM+GHD}6O-u9v0~9M~EW+k5V~ z@OL{>?C7Rv|5iey(#jdPt|~8Lnf9|%!#jNSRdmnfBalEJrldZoC1;9Uo#Kso67>4p!bR^NTSRl=cg3~l{s_}orTlnw zg-#kx;ZJ`QBRUl*AG?MJWzZ<3j~%S)DHD9FKI{SuTM9x5?EdGmZW^Ij$iQPnd~s&H z)wS!@xoa>_)|Y|D)LIp7RtCd}ZK~(?`W4UD({?zqS6vbIWe+%JOeHE;RiOu+z?flf za4l@06kFOrgh%DVqSdPM!xx84;?$p+QidHfNb$oHU)6CxHQ(8|@~w4-d#!)EIc{yq zTz9MiBY*R~(VxvcFDU@WwPPA#2DSrki&R=2a;Wd2*00T0mHtl$+6;-6W8C{I)?=W+ zw+xgW6u8!Kzcn-?P=9Ye(Wdv8=*wDO+LgU-k+`)#uKfEDhZ(?*dE5slHz&o=5(7M$ zK9v_1YgK(AZ#q*MUg^)?W9VVh%1FeSs1w%4sjQ*~x{=)OcCi@WZcMa-x8gWnfdMrrFZu5#S^{dXX;ENw@ zvE8m89}k`Vcu(Dj&`!hYtMsLY#0ehhqFH`v(X%p0&A|Q3@E9v?+^q89))O?qtNp;S z?{l;_!x7qEohx*iI_*2uNHyP|fpTRyYwWxD6uwp{Ptbf9%r#bsdplAPt3EKRof$2TBI2CTS#>A1S0sClhjw8zDY4A0C$`_zBL0<9Jf$BhQ zDCR`Z&BpN6pF^S>QnP~@F(XMxc|#eOyBEW(&)jk+&B?zP=|vAZW(4C55s6`ny@G9E zE=jI+=61*E>Ip?&znZn^v-&roU*tGaT-=#KX@Z26+e77~p3Ry?B;>sJD)8yuWn&Gt z6DlaD_xr<#oIliB+Gubi7RYBOkTwG#Q$RbqOZ><-PSYYtR`<-Z&}DyyUZ}AL>4z1} z;uh^UF$qbIFZ^DgX^qvG8XNE;)IB{|n9m66^uzF61J>E`UQk1ue;spsHf%aEAP3sf z>1k7V6`y5lRp+G*M+}tO**1fMDDU?I#fM>h0}Vz`MZ>ghJQUOAFXD-&N;O)iTvr9V z3HMxaf0%fWVHRky-CKQ7bTq)Xi)p&QmNN+%_q>)2NAPC_3Lk8>c|Y0Re(Q6evvXXR z%^aW9ndM5Oidw>IhwQ%2*Gfu>)XWgJ z6;s?HpmefmgU9TJ@^pu$e9>?<%|qM~M5w0Jch!!VaA~Fjmw3Uhz}g~OK%QZpBybIA zvW2H`n210N!f-6D{`;X~jR>uZQje<6-Tz)DTZuRK=t8xnezvh#fOo z5RYZFg?~`sdAmvYo{qRnC&)jOCA&P`OPUZNgtqCmi@as&YJ_I z?rIK<0H$PYokbK!P~gfule*Qmb-9@ZXY}n7eHCKiT2KapwuWz47AOM^Vs6dBT4T#w z%ZI~>oe{*@c6J0+5K$(?LSDT1pgHglPN^T!G2@ShL(>6t4<$58jttEFl%GzMnvK@w&kgt5laLjQlAMV+1@A|2kOTcFHHPK z0o(k?#)ufjuJR&_39JHiYGs7b32&ll68^clD*Hr=p2b9)S7C$rdg3%vU5K!dkk?Y; z+vuh-<|{NRmsKwpNGdnSJI9e-PV)LDhIuGFk5RxYAFnse5=~Y+(AJrR=i@w#t~evD zC0~%+W;m15HMTX$sIy$lQ`(wCN%uq4=uSIo;1Sy>V&UblR~Na_OW=p;=;Cdj>3;Rk ztsqVE*eO_R*bUBc5x02lSb}n*x-x47!_$&l=8#DjIaFRkr(yu7kE9emQePjZMn!A5rL~vLt1@COL5BhS z|9{rjs!-}{?Xo4@`ox0NK3$&|+u=1@J(uer_(QVm(}C{19p2_zIBEKn-6&^w@M7+T zqRs&E{)LhUXx^{JbE~VFzxBA0t{L=w3?z9U5y8CIjn=7>74TgqS^smiE4T!M$peB-TacUlNz5b-$Uqvff z*Ex$}l)BHYHv8*jy`)c}ckaHo(WGVe(nx8a%komE!Yz01tkOP{%Dqhsu(rM>XtXn_ zrghOK9$Al10Qct|%H!gjUm(k>W&7t&Wgfvq_1w31D_h)deQn_~nrTSy@z$fEoqe?# zc;goV&jNGoGt43S^`?#vKjRmlDG0H_#NXf`tp`E2SdCB=qEmguk!mSv-42WHEogN(pR(+Y}9tLG&4+L%BPsOWc@w8!l zH)u$DwNLis@dm;y23bH+a*gV~qYdiSr5gzKs!3Yc3%?Jw7h{*s=ws8ZHOVEs0n6iQ z`QkmLGGTwj@_`m*?6L0Qnpj^CV zZx(5mY>>YnS?7~_PF7T*QJkOkF5Lz;i&ZlSZ*HJ{m~RlBtgu2C&_(H~0_V|s!vuyz z@BHV-=Lkq$ld=AZBAu_C)kVviK>?vG081=%K?Dl{Tg2rBZ#Kn&b{^d&j*p)9LKhr+ zxE0gF*y#EM-4S@8FS+%l0g*#>E_(Z+?z{io>P)W zzu8*PAH60cA*^*|L(1MjT0U>Dx5vCUGhI73T?@W%<|~~_F2I@D_lC`6x9(*{TlP|U z6X2YvQWVE;)67$;R+gD2s^a=*ZSRC{dd&B+8aEDn=yGWldu&Dso4m=WEezW0R}<# z?zTigpDE@v{oze6Rj_WOPl_tVtF?$49*dQ}kwn)DlLAu&*fw)6;X<6D}Unc2NtYaZ|d z0IN#5gZhD>0u6v1{&Oft*doS|t99qisTyHldSp$ot7{6*HR>FBq1@Rg0 zTfMa=eJBzgc{D|ac<2avR-M}mvtcmA14_%vItAa<$|r9K00m{O?Fd@5Y<-V-2foV` zW`_?4kAF4c%i;uja$HA=FQiEtJ(%QDX*}KGPo+<{FuBbFEvmjVIMfA?B}C0BP?D&* zMvIw;g6!-n3jtShF_|n$;m=!tYL>01YOy>)zqjTM^SUPX!i$z~=%2ucrS#)d-Fe#-Z(fjnBQoU0OAI()V_sy9 zB61wk^O!j%CMGCRdvQf@09hC)MAZ=YicJ05$2-0BZiOc0s!j1$ z%jJdTCR33{0GK7+0xG&o%S?)23h)+O^}C(x#`lvZU*XL9CMRM=>kmXzX3rA;J2rwG z9bP@rAB(T;bxJ=ug)PFZM&V_>tHhzjb{_Bd_gb~qqo1^fEpAU|Eh`>1Yrf6beRt(k z2gA$u?~W};c*kvRV{!Z_YSNqM@T*j=#QEsJ4)34u9&zktP%6&acSdl zdgvQzsg_kwq4GOgo1quouF?eD65m>LVo-h1p^lrDtXnu8L4}>8ZBd{lGX-CMJrw<` zvA*IEFI`5Y0yW$jr88wSs+Dz%(_%(-h}Dl5m6dRRkEfcd$;VRdp@?kdm&^EqP_NN@XA`pWGqHMO*s=rpo6>D+#NI{rLrZ?8oeft9W#%h(5TJS?K#xkg>hl%DH?a>}U+oeFBux6efUd=i8$TmS z_DT1Z(O~_&*QQm{Sz@)tRDHag#_X8=T$31q^Ypk4#-51mn7{Ad$mULSqDdJdbAnke z%FW^d5;~QL+j!FG1$@0>?<=3$^dL$+xqP(0J`)u^{arJnxq;Ai`TIS{hPsFrA7yoVrq?BJ)+C&YqV!hf8JI5+VnWEw!}A=sk_nVm5*6o z`3x_n(VM`$3`H2V#%iZuek=C(kh`G&G_x?BM*uEQqWhoSa--Xsjh}{V&E@$5TA7gZ zEAfl3Y!CeKt!HDl8nS{O!n`0qldbhO^?2t2Lk8<&ceLb@3zt9 zw30p`CAxOeaIu26!i|Ulhl(D{)C?Qn3h(URSXS}hlKfy)o!psAx$Cv4tX7C+7DcN& zc-Hxnlvh?%3GWOgNYyH!Q{wHBzJyAnHhlYJM}dCyQ2)e>K`kXzUc}6fy;)g&5mv6Sd;}+1lV`ASj%qH7N^?N`F98kP@qUC|S7Z&t0JB?p>JNLJvs zVWzs!cE)=O3^Tke7z#?BydQ|gu;tCAIs5KGzlIsp zk3W^~442WMV-fkVRnFpnM)T1g|@?ebhL7=&=A;BhD@34ceCe?YJ#3I9Y+9 zXaG}Krn&Fp-}8}x`=ma7K5MdL4pcPCKPBEwGfu;-L%gfjv~gJ6&m9>WD#;JLj6e+e z#IUf+CaF3iNG}DM=UrSf8#hxu*vXFgQ2U562iOeDus-Zdk=L!*XMJrma;dBNc#|Va zURABt(b1s?bn#cb0rKXCF=1^wGfh-pPh6TWr_ZL?mPw z1F^WErkz^^@oDAIC8NNO9?#V?e|GYxtNEutZ|{lJB=oF=)!Z&#h2J5>$=~;5g;D>^ z?UkGLs8}|mo8LR`dBZrQ_qE@Ep?|VFn~-=9`Uyz#{>Crf5ABMYz6=*t#JV7<%0-%_ zx({_YbIIpjsJ^g~D*w=x9_>QNHK=G>k}>y%mac8tKjxEq=_W2P&!wtqfNpzJpQ3Ti zG5h!>+#bY2wMw#)5D!gjTgdJRbg7&M8Z~V)&e}{m-+f%HOl7mtv$b@VMLfv%Ypbktd9KC~T4^hyvW6G@4GCAoNy_NdV zOGZL!p z8BaBQn$MV<@7M0H-L&fkcqZt!62b&OYjoVgc(1V{`51Oruy%8+Cc zj!3S}fwr$7F6$}6tt7{XB;PRIvfl8-O6B1}Q?kg6(lh0k)3s6*SsOXTZ+U|=_&}ptEM^j2c)CDE#WE&0Q$t7hoASpo?2&v^=PX3@q5g>s#wp}L$ z^4UGXJRcRbN=W(4@|j_1uE&1%MfYy{&>2dnsrlp^6_=>N9E#qp#1{a^@7xEVFS_8( zwMDKI!?DgpZby-q^#{Vuk_=9q2R1!pZn_UxVh!mY#DOg^h>FsOOwJbvxGD~XMgaNG z&b5IcACND*60|J!D8yW&>mr?9d{hLVp9>5uO!|&y2MpxaP%E)nSRqX9JAi6eJNNbPt59*` z!1&sxncwr~t>O*+k^6Z@R~SKk5VQuQ5Br6)&)`)KQ!39YY9N&Bc74lykos~4B1`cG z6OgS|Ev_ks>|_0TbwMiJ@0Dm;t3Y$t`MZKZ9;0;a{tR>S)M>YLz0S3vvzQuH7ct-% z%seQa4Vo_c(i2XHWqoUyLepO-dMe%C!(iY?n~$6{5DIl;Ct2RtIqYQVR!Y{jFkw`F z68UuKv3fLjj6U~#(NYWG4uEtgtPGJFU|PosE}lCfaqr77PDL4gpAkbMeaX)xHgdc)YJa3UDw1s5i??b z>Tu*X;;4+47HP?fTfNhn6)zI}gsHhjK>4^=P~&c%T8Lqw6q*NM;6Ho#nor~utsqo8 zW^^<O9dYc^(@`(>(8&GZ5(S;F*d*%N62cdtP%&- zY5i%^d8P<=Db1Z%Jj;DQ+j9NxM1qz3@SoK0%z(~ciofQ9)T+{_{}%@9dwP4pOUd@l z6QJaQ|Ehh?sq)GE@+#8%UM-hw*rmzAZgQJH0IicRtAC zxhAs{-!ps^4*X|-NDt#FB_LM2T&3(@K9fB@fGw*oy!N^fsK`!mL<$z(jcD+sp30SK zmUw@E0vTvk;F?HFo)$(ThaTbgrXaZBVh%WF9;9zy8TpW$wXC!JpZXsTZ#-dy(b#eJ zO5T|O<3C=MJd+?pR)A!xK8!v(Te3`Ze`uwXzqscUL>4~Ea?6rTe=vwThhf?eO_xtK zQ(e;c_(Qr5m4EQc<((qE=&#C}7p+*pp+ZT4B<$or`t=RsT7S(A11ZJ%I4#c1Xv*dx zckhmiUk|{T8Z1|T8DgY;DUXguqs$hDMGoTK0(enBKB%FDmxgtZRj@#yLT|1;V+I4t z`SY+`f2NZI0_Rxz7#pGT1cYdi`+PiP?$Qs4DB#&YyB;JT1-~p=^V$a9Ae_S*l-j^+ zT=i>(lWCwm2=HW~t3JLcGthg$@2!HAOQ?<}ju_nfGKij+5DAjr(oMjE=ZRxfb=~Ot zAj=6$`O)QwCivy`CD0&VJ?e_8HLP{Tmo#CKNuJ9G(?kBTFM~2QauEaAkP6z5Ve;ve~WPDGOELNJHB1VeIw%z)w6oQF1m zi$r@S$kZCgMSrSb7<%y2Q1+fZf^z@RGEaWFn}MKHFUy`!l(GC0&zW4;s|8@y?b4Ie zI8SW4i#Sl1KcRC7_;s*sS9r!$&Elf5NPpR(TWule%D(N4fGJ#FU9@LwcCU zZX+Y~v}+cu*-$9n?;f*{Iv9Sm*=qZXW&P&W4PBl|&;GSi0>U?dNpIX!)^((XSHpE} zK~A&j<s~&KowI-7rWmtMk1Q8OaUZpO-m48g-!z@-T6nPi!VR#xCS@ z+d#ryfW&^ALSQ+Yd1%h|!vX#5iu&P!OOE>^6TH2thD6@XRcEuk4}U zoy((U^Z<9i0fm)$+S;ZV;Tb^eg*5zGW|l113rO5Z@SHdC^x!qO2)MDi8HcG=_5<1+ zM4zc>V5kLmO+0!QmdpcqTF6-TY>RCgMMDi)5&~v7c6MSS99A(MJ@CQTlTA;Jni_z{ znV-<^@mb}2ki;;~1HFnboBp8YQ9^3L_&BB7A8iR+XGRSDAac;c-YJfus)%vJp=341JHH&nrMwQseBp-7vc7h zS)&LXng$UQfchUsuuNU-h`<8@Ot^ilrL1Px#Ia^A%bU*Jf$S8$TVFo5*7jAW_{e}Z z8rB?xT6aNqgjw1lqyE2NV>*J%EXw*P?M;FNcp!gAdvi>jpM*37~_`S)Ih;o0Km!|x2 z9#klw8W*zD%zFmD0@)PPGV-%lU&D%yK%EKZk)(s{*Bp?6jo3*N$bHfYA8Fx{qh?te z!b0{4jRe}DYYXEUc0H++RTBFausAn5+#4Aa4Okv<9>*dP)aNq$O(nWNH>+>WaLs)I z>1E&GiIlyVxQ&4ac%y*pu+!r0L6bxY=?C36PdELpSZ1N3Z0Ry{Dkj4d_Wyf2fwJ-u z)pR9{cKGbS!Z$4*Z)|C^G$*}A@<^Q|RAmQn_pXq4U(=%{jt>5q5fjQD|J`bCX>+DpBH4c3caBUU?<88-d%v*qEC@sUi27dHA|has;w!%5Ala{d<4h zJN`hx7W-*yWiGriYF4UA3xp?lZxlMR4mR1m2MQbdTSWNr=%!HciEhYA;slU}QB-ND z+P#^{$Rc^32HPm{;L1*Q@pdwF5USsC;bD12_=V#N@F}_B(SXp(j4OZiASgWGUGVdg zMNYlnOI9BFT=|z*=}twNU|XZI>dCl84TCuqXU&!$b+^T|r0Fjk|DTnx{)P4t&jT(z zR3iSaFu9Lop#efe>3*~E&M_1URd_V&X&j-slbdNWQPeKnCZ7J^X_sg9v$?Urd(4(_?)bwisfQ=Dy=61%i z#B)!Bu{Ya91xZ2LlvHt5*#ofDQ`+5kAYZS-&izSB3aT+ZI=%p)od36^2Rav!005w0 z{3*#|JjWh`?8wAE6Quh#W;=bJD#0IJjDLH5vAzl`4vF`YKvo)EV5?vHUW||!*kyek zEvJwgln3H|t_Tc(-yuXFq5uB?21Ly8yq*}a^~4iz8O35ICog=Km1cc!ee4{9%I|AP7$zWEWfZczl&s*rJ03k@ZOhW~%m{9# zTmYU223>&kBzJarAd@fr9!WHUqC|{9o*o_~Rh?{^&4`~;5qI<{Zpe(2}L#rrNg|wT!DJ$p=f(@V+F)09!+C=!ebrFi&ItTOKbwv3k4T6h-pu= zWH_)ZAur>8CxQv}hyr#Yl@4tBzpXHm5!sz&e_#5iOI(xdNRjfKA4bancUjzOM7#p6 z9TaR?;u44Nc{5EG1xn4b+;4_IwrZaUtNhW;vo>>b7dkT*G!*Bn8EnY0s}zM*GL|K8 z?ZH`c{TK6PEj+A(lDVFBT;(N^;A3?1{8Wj_m5=wcg#Q!qRQkhc+;HkJYEMW1f&+RA z6}%KT^>fw9>^mFBxGPVZTcXCBCeFkQKgd=bRcr!4R~~CRO%e@E5D>QOK^|`|Jc=iM zS4UNtm*xofARrfPc@^v{TmCg)G%rD^fKOzwCA1oT^BZWz$=Fe5xB z6~~vq4)YMQg!@~9Px^Omof--26amGHNtzrz*SZn)ZeYrfS0@TY^uPW(t)xY1K0ZE_ zycQ4)LRo-N8@Tx1$>k%`R_+8HAXLNoC>rpFIkC?I=j8?IP`g*Ve8dX`K)QN>J^YVI z*ns#|LT)2&Vq;1!o!%@4i5M(BRGlj#i$Vxs$Z*eLq_*y3rfVXDn)dEMmg86>m?O${ zVSK<`aWlhpdp8LGzN!q(D`rIV9GubOnLn!f%`(O#NpLIi??^UGBFHxdGWe~Zq}0NSSjDJDN(%(t#c5q(J4YQXS< zIuc|RfbbAI{jGF$L7}|rkuIt>c^9Rd|8YH%K+?mV=`?U*z`}vApe175%ZPNan3{+H zW&v3AW36Sma~TCCOZ#-=Yo6?ZY2jhp-p-?cApDo)_dQIQ8E>zRY7s9p`m$o2>G$#O zy7^G`;G49FE?;BYq*q`e%(;b;*7|gY>$L+WShix>Yu;;!I%dv;RUnXFEF;d=^95}| zSdXcOBt4Ia&M^I(8rDFpC;@9SQ!$M%6K1k1Zl_mx#I`Lk_$8|nE=Ftkr`>ym9%}`}Jk9k9~JxO|cs_7MVY4urAd7rMCaPMh?S=M8m zxs@h``+G`IUYoW<*pyzNy)+g&Gagbs6v=m~nECihtU^^Qg_oQ7`E3%0JrtyDmFh|oI)$1uh1wj!u}D%4O%>K$Z_oUBBJ@H zJX`zvOs=9aXxPlQtjb>cGX2a^W0Rgcl|YGPuRgSJX>Fc2=-WJwjRtEP6L+7}>u5mg zgW4!=VdnG=X`91$T=W2n%SKOLIlk%F55^lnQX-c;+$i<@h40v>RY4c+f3cJ#e9*eb zp)%IRuiffTn5c+?o*)X@+&qa*c#r@wff(XXXvxPcZ@;TuzsoK42 z>+y?(`OO^A!2mPWA08ftC?FVUJaM_+>qY;A7ND-_`Rkmp#{Wuq5{0CS!T`KO*Ln2; zAYbs>lzUY`#^UTOWRNpMUfvq}?zwbGa;CsV zkUJh1!FeYJCA%5s@+NJVbDQH$JqfeZGNk5_v=E3DN5NXy!=cF{j>HZY5o#kr4vPH| zz%|(&?evR}AQU{U9(Z#{m(Rk^O9#U$MZu> z5JgI#10kaC5S@2CU~w=AgFtjnx*$s%Br8(|ptqHBu;J}Eh(Ez#mih&Py-zX1&U|E@ z^+77?MnRzj_F!%v=qN95AFT000hbpEXX{m%@0kP4qfP7;ZwB^5E0V%}>q3sAs5{5l zg&eUB%(C}FxpUgYZ*MF?`K+_jR#oUh#~+Fq1T-4WjQ)JKQlKXOGe6#LmAr6*Guh#T z%J@z1C(%%09K#Z2^#R&S&ire2wG@l#sz?(IoSoq%sBOEx!5?#E?q!4(l`SG^$fG7U z)mU(Gx5;R>#%2uUXnm(1x9uG;Av5}^Y5^+&3i4sXG|YlSEN6;B4z z#<$RfQ9VE79sAZV%95co6YmgLMov$$1a&ovyyi#CMLH)>^sd=eIHl3WsS4DR=y=wR zCrH!fU7e`{vtpLM)#vi!;$ohtp_cCU97C}mZz{(>dA{gdZC>i?2FUZ#GtQ&dB2AjU z)!pYOZ${)K6?iA(cvD>&?$`Br1^rTnT9Kw#Z5TZO&Beu<6-xXp_GfUSHj_lJ?ZZA# z6}};ri;Db=I}~>xvj^~V6Zm0GF!Uo)LevN+y|M;=gm(9p72wH$8!+?1iE)GD&8J=p z5?n_RGjv{ucy>!8cqfV3u6g&q`u4X*n#nn^AQC8v=2z?VWiaA@CxXa z0ei?02BlB}wr_(upTSNnVq|{L&63oI__}u{hsvj^bK~WD1|Hlm!2xPFq(GE_sO*vb zu)>*2&IUS8hsExsc0`dAwH3YlQ0LkY@=h=J*$Z?fI%UHdH=@u$xrN^$$vt5pVC1Fa z62!r8d2j)oNU#{SgT-R$fi-KMlP%A_jC1$^?*&7X|8hwcx^NI&C}6A9P3-k;_!FK7 zR2@K5!lBy17^jjmEI?EN!JGcZ@WV<>|HKCHbP$0{xaw_z3FSuJa874!iSVfX`YUDq zVf8XO%jdoMMooFj?+s@xS%+@475l``+ZTQ(K&BxWDzpJx!qG{wuaIU%>{R7_9Gi4f z{IoYCjmn^RhpHBFuLh1wI8|fx9`$~6Z(x-N)INu@cJnKhiw@cF#w%MF##{y)wM*8M zNENgZe%tTo0^|lEOo&LpUoMMS=C_;sy#yN0?NEZmOYoyzv3 zQo`-Ucd!-2FIzE%1S0YTh!DkI_0H^s$kKNy{j_MfAOO~6o8p9rPTq9bxFulqvz14d z>qz=n{qLHulG2#--7}}66y6!)bSgd7I!QCoR(uJ^A$A9u`C53Xu66!-94kfn1}SY) zPhlU33k-%oA%62`a)&d4XCZ1HUiUnJSGqK-Fd^_c4)y-ha578Bd(~!OT8aXA6-@s4B&s{rF}}QB;SDBd-_nd~ zoBi?ino78Q5tmmELP*fuAa%A%)fa4x{PTo@W+Z8VB-|}onip~|47C*%=IfXC{0?&s zOCSR}>1iXdFu6S6P zrYA4KT3b%-`9CGVYF_wfmXy5yXW57Drs0o}0mOvFFr$yQ#)2glwu=ldpSFeZ#Ksgo z|47NkcviuD-XBv%>Nr0FWcV0K({MY8Q7Gwwt=snb#~}fF5l&DCYY;dy{H?mcyoz!D zOeY`HGJbFP?K6ofuqr?JhN^$uKU~tN=P&<}gDaiTocp1PYyr0i4^4rNQwkxS2E7#t z0(}8b62X9HM|B>(siGM~c=OjUg)U%aAm)j=Z6qL7Hw6gYpAS2fFCp61?fq(^tqAa* zjDSb!$SZ)ee++C?%Ka7(GQhM1s>A4xn&1NtzxSh#aXe_-q{d9QygmgcAL77hSwSq+3C8fH} zw*PvJ!3m5JV1Y{3W{ngrPSC^W(LV4W-kQzgeKDv;Ib=aqd*1|a+w`wT1)Z4s^R;MA z=+Skj`!chxr@O@6?KIzJw=yvQ_KeU(pJs}&FD;L1rr91CGMua$)7m1}6NI5m28*6n z9EFB^BO5HJE6<9F+xgbaJ0jBWhAW6ew>RO|h&SClO@dd+4S&gKyZ>7i$6hfIbAn6h zB?}%W(Cq5{j9&)dKBC{)=d|VySaw<_-XLZc+3QX9u5+N>!6xZNyV{OBi2ZkSLvpBx zH5}YOiya;$@Yj2&B{{p))Z{e@)f3GPSlo_1cGksr{=c%$Gp@563BC{n{|LQvm^C3<|sFu67!froP{PuHNU5 zU*=N{e!Tro`WPxlae`dVYNw`_6{*9u%UO~6S?9vzh2bl3yz8vyJryr@!ZXc*s&^0> z9;n+SLHMii)?av}-BGE!T3tubbBk+!UmR@R1ZE#ZaUvc46$x$F@bOa#7=ps?3TyaG zvAle%|_feGbtSttO=|OrshV3sECQ5MV732&h;Lh7;;mFxk+pwVZi?rcmPj* ziO3F;u;-#&ir{(WDm#@hWgi&)?aQx>C4WEt-q)&f2A!y6ScgmkyD~RY8E0fX!E8sn z8sg)c-w50w8R-W+xPI>@1iR{=w4 z!KAtBE;d!$u-0`Wdu4V1K9o`#Kl`)K_B+)JCjxq9{)N?*jCudV8Pc4$_t!0H&-2vz z-3_02a#e)KsRkV_?M5#~ zq*$|==+}e^ebPwwjvXQFbNd^Xv7|999(KH@Ucc?E;pG^=vx4m@r+u55jffM0dgLZM z1VO;fd6t^W;Mojqb9%wq9JhPiGfjUyV_lWs!3>#fk4Z`JwGsd4*}NYY&-icc3Fow= zW`FJ0vphUX74o#*6`&91HkVVzA|s*;gc3TP{*bgd{SZeFue&roF^bN4Br7Ccb7k*h zlxVm)*vHv1pE4SAwl*s4Zm@)YK0fD3LPFZdk00aOYIVQT@3Rq{7h7odh8+2Xf$(IN z=CK2h_SiF*^DD@y3ePGZd4C|aRrp3C4blA6XmEfk5N6AxMDe$yv7u`3T-Nw-f7=~G1?mS%$Yfz8CcR-0Eik96KJQ|hPevseOliV3_ z9dn@{+&DMRWJ5kF+G`t#yTBu%p?X8a>w{Xj@sNE>eC(AutVEq%CWC=C=p{*oRN)yM zm*c|Xjw_*J`ow_!(?e&iOQ&1yO+zdk{jKqLxN<)jE}YSTvx?2(V)T2%9HK>ux>!7L zl;o}mGyx&2z$)J&^a@qR6M+ZiUq>eRD8ObdBk|dG(f7-ZZbo&pqvztHIVvt1R0t;c zUhu8d2sc1XC#d%-L_uVb4NA75qP(!)+4V0 z&WbiT@3L{cRBEmV)B4$fm6N_BCqFwYHUYB((n9sO3;=Lu(GsFFDBl^%FSmE1=A92A zm!;rF4{PBTp5V5=hisD(xPi;O`I`0)gye4eaOmRM zJ#H9IRC7V&C!_x+9WI*)pp06kd>L=kso}jY!~W({l~V>qlkC>)jjT6IvN*aR%Jvr* z)wBa5uH?Kr7Hk-k>`s1cItaNKm%6ZHI}hGB>{-)mdEHnwRS`0`J(9ZOE~9X{G{D_% zMcsa$CaGs|vf`(q^J^6*lI+wX9ksZ&TM0AOU9Iqi6N_4AIjRu4ww6oXwba9IQF5W{ zN40OOjPLvQ7Y`YYXki!u3vr`^?VNkX!uG!+#5+T0VSP8z%*zO}Yv=sZrTYK99Xs-3 z<#?j4F8M{!(COC{`)Nq=Q-ojLkxG_4MufRU>)9vZ6+Q(D4xXhv=TpYZs+DxI=(&k# ziN~a!4nk1?&~rFH1Y|Z2@Ik^~O2$I0Tk_U|2}qLr2ZqC%3%Xl?()nw@^ZnTbqgJpC z0_CWG1Xg?cecQxJUqmkpqWbqkq*YG|I3@Y(6z7T+61G4J9w7lz_+kEi4NN}-1}gMd zKtTd1fZY?Uvt)?kQpYR@EDR=_h+NkTHwsQdAM(-#Szj|_^64yH*nW8b$*4~B8{>_HV0Sv;FWSl*fqMlJzyc?Nfg#z z1a}>JkTe8xDHL~1D0J=9a9ko2)UC9FGQX+t*Iixs^Ru6AS?*A-7*D(#$daedB@qfK zqIO&RTt_SUnV0h3gMobHlF;#z=s5)jsQ_ki-Jx#i0sbc-l>9eTB9#k?vwfNj<#@K0 zWsG!jSsg)Crw4QiEEDx{Z7(z^g`2J!M>y_6N`n+e@;+d3YGg(iLOAMFQ;;zTu;m2U(p+eO=G19C6a~N;PW|u1X4)FkWsB7qTfa@f43uTExiee zfNAo}-@xm)wEE(R2S?xk5SN4`TJUw;t1Kbz@mxCCUO+4_`+FMp$x2@bv@9^9#*8?) zh3#C~d!+4Nf$U86GKHDZ*M zG#P}lYpGuz3uny`UdT)R_&O9{i_wtgt3<)u8Rr+X!Zd^@{x=c0c8Wjx@J30hUqh@| z?RW2tvq!yD{+h7VJ7@Qf)RH=_U8{qu39s};orhRb%AM{j<7&U_s-96>+UB$x z2<<;r&HrRbd3?UfdhtFBLu=JJxea6}u^w`J3MGZn@ckHz!~v0P@!MigViA(y5nqZ} z4&4$jVB~sbK7huh6u^COiYtEO@pu>!>uRoOH(A;nClG-nf%bUWRf!iK8T75%$Q~1!629n4B}LRnF)K1E6MR&3u3p zENI*4>afG)Ev5pf{1`>)hI6o-(;xQKbnCi9_|9IRBjqMjmoy=b6Y=v3Nt^v$SiD(u zq6L~)EHiQszrEA{4x$`6XIRBhbIa_nQ*oX?%=zN+BrU!cxf2DrP{P;OXNjqOSSDag zpWr)Lk>M}bwNlF*tfX}tsev_PCt+-eMF~uDOTY7@S zi+Lz~dVRQr^^O8*|%VpWWC_4VP?UIPJr-=q2_D3n8? zQ)XfNRMS(_e`4jv%$wy4aQ5WXkFu?u-#;1F(<+H9OOf1lvmYm?CVTR1>Ws%+F?oGiS|0vxam8g<; zXiLepERx=FyjpE1WBrLgwhpso;E$&bE}^5TFC*2~)=F!B*_(E}YC%j&R0PR%2B!16 z(k(rwIzE`hSC5BdI3>kI%Kn5e1=I1TXv>TJpHTf-uWIqHM>8JOo~D|Fj+sgKx+$=n z5vSPklOIjCs62-0wC|sZ_1;(QaPYCh@J%aCBP4kmWDhK3#dQgvr7vTU%r`TVnT~EG zSAoS%&e6gAv{rvN{=*2F`oM}03f_i>nS+TZU)OCU_U@Q+*ckZSn1AeIe*3s-B_S>GNZTzplcdj9mo}ylJa|^TmEh9k6!)Pt7pr$1LBUgQm~xOCn6w1ky<>YqB)QD4CU4bu~GXN}i%R zST{Q*4-XDDkgW2qIoP8oB0pY8DLAf&4Klvs#JL!PHXC7lNQ8hvbS$EcG4%kKY>A4T3JxQRqLG z^+uw}m7MB8AP^v@vV?5Lck$>D7jo9^XDh|%5VoSLl(9~LsJ!qKz3}Mrx+WaGHUxb~&As!^utxN8yDC3?&yS}1)`8&A^<9eVqd`FO_tzJhpD)>|{x7iJP@ z!C(dBqMqw?ze`G>YD%}Z^^xk6H=!RtcwV^~X!w5l$$!y|E9WZEsb%X(+%VKtnfIthL#N)F?)7mi0B zilDP+DI6t4^61k4i>g5Z4b~$K;n9!tV{950=?fPM+T!&EI?pB+cx%6~s@LVX1TF0h zWBuA$TD`G*f~oH~)WNaWi(B1#(BRD781;F&t^ONB6{68%#|Lc-ltp z;9*VC`1dk`$V*#ouGs~0YpKDJj!u0qnSM7;wvMH;BYFBc>UVODF1O}=EG_G+rsnCc zIiZU(or6L%hxDLWz+#jh7yn)VC?31%DP z16!oVq~q=u#!x4^YVrft`S329v7#%iX^9-W2Xe3HPOxO3acYAnrpJExalIujuoPk4 zq;TZAV%rE)6a@t`D`0ncHB9Gxf+Z$E$yxT~T!!Vc0*cfbpj(nBY66(3@E#?tHz1~a zlfB^pU1i-YT|f`uRbL0VPLQACO=J#dCwgcr9mj6BUWh+(0V02R6UyA6)ga)#jc!%j zjZ>--_!X)%@5>r2q6G>^m~$5OPIt0URRvHPC#SoUF5IE1jWMk%#DLDK!oU7-r0VcXno-#@Bh`gr z0Bixtjwo(bH%WfeR`)#iHA=Ld>nt#kC(cc30(6mbs@UVQFezZ3epv=Uy?444LE8Y| zF>FM}>bo7-l?bv69>v_^?`^6GW63h9N-ltc$y)IVGK&@*6r?q3g}8^89GEfw%(gsA zt2O8N;GEQSu1ES#r*JmGV)*g&t*q&s9>EkXpo6T7rMy8CzojIV@;Vz$QBdNjk4iuER4f|Ddea zYJ8hAJ6u2=uFZzL07shdTwr`9!zlO;1dd!XXeuM=^Tm}R(DA#x*R8>F%|*wTJ0;q? z4R$5Gjm#91HXJN~SoUYXShdCb>vvZj;>D7S7*;oQpSf&ZW_ZuZiQzs}HKmHffEVVp zmYrqis+wg`EF^sK;O=}NK6)A)&~D1o7p;oJ9~1;V@O6yA-hh^4L|>dSZ{J+UMDlM*vedw- z<@!9!@h_Ja3q%%L0~21Iv)Hj8B0m3w-znFTf0ig$cbsjW61Zp$suH#Q8+u!oCAp-> zGICQY>c}YBDpYD9ya?RRlRe7eGzka{WEMi<%vfm$!+?wG5&23kZS8gK4<7w(GfE^b zn2*Ih6I%r=RQwbI&FblzgVr}hMsqXIt*0*u5i;+eFgkc30BT$%diIrR8S*EzY$k3F zpA(yyAt)nk#;f{1SOoU>p^{D^d|nZ%71RX81dT@^fjVni@&?psRf|KB2RkTq-(JclynqXiOD^!t;7YaYK;$2x0nJ zA;G1P_a9TZ0j8olQV|Ni@w2Ub8X6-CiQjr4x^TyguJk(w_0<&6Z*aUbo!;T$%1&L> zwcK20^`SK_{!ch(^a# zXs=Cvq*x{9_W5E3zPnarvZLt!3Yej(kjYU$IO^N{VV zg!d^^-y!mI@-y^6ivo0+36AP8p#Vz`23{FA%tlm|uNV{cZN(7Mk@#5>$hQd&4hDep zIA8e!gO)fz8Fn)I=3|e9=nYiDua!NPxhqy(+k*^^4Zew^wj~N#qtlz9VLqI4n%c0WU%~B7M}X2?kEJ!?8zGZvyS9* ztN%Z$rAV0_zn^G@9F@ou2Y6A4Q;(}!`GPutCRhRs{=xm(lLHl7vwvjN-V9YU`r`AS z&e{a(MIbcd8bVo;hIp$TlJMW2+>}?4YuHtmrUw(tmX~Bkw)L0DyDrp8uUyYRW-8v7 z*6DIbvvczl^I+Mp#xg%8((^48wRs&f|GGZAiTi=0x0A+-goh0e{?D%VsJ%q}y@GC> zyCEydWWK&=INZr9=fs;^{ojt`Tg8urle99ouBOOZ{7@e@ge^LsN#e)v&hr^y_sen; z>1lyv>6Al#N#@u(p~&p(k_<|BL71vsQdwQgyA|NDtK_Z0Pof&7f~)vBw6$ODi}_+U>sT^+(0%zz6A3(~YNNhHM39ePbI|tiQLJB<` z+)kqIs3qE4Q4M+w$`Ug+ym2*6%hPfYe5p2vyy^-R)FXn{Ru6dyBRsR91F;ZqmNiZ` z@hVO9Hy_FH#_>K&bHd#X=oUB(S8?m{jYFIttO3ig^~eXK(kWU?H8O1k05hgr;AlG= zIf$A1Pr&IGO>(i;!nH=pfKtS*gmSQ5s$vCmxZF zBu4)&jh?QJ4puPj;3a1bQU;9GhN-@f@b*Fa`|Yww;XnMk2QZ}=Q`p=}OfFFKo+@Jp z1FgA05ck*PS9I-w<`76YYp-MDm4#{`E)!jfloAs3O8v|^dq=v^C17vGqb$!pB{~Iy z3Kc!0q~AHyw2A845{CHl6Lgjdg4-OZszI0P-u1aF39)Sk&aZDrm^vJNWG92}s;2_q)X$s|e}MNTjGrOK^6^9yh&|HSwM0`RQ=7 z|CQ>6>CIB2enW*o)@4OH#YhVA>6Pj9jSu-gU8eepLU0fI#>1Oe6GHub2U*(S*6Rx^ z%+hX)a;G!%&mGukIgml(eQci{3x_-2QsT1CrboLutvAJ0?%5&;@3mJRvg{P<}*cCZyvhFd=x3$G=$+V~mf^q2fjSAYiz$8=7Q z5q~OZG2ir#&b{yYj9$Z>4y{KZ2>aHpoFVBv-M|^S%^jhUEGE15)=k8w953CHy?yBJ z*~8&RHd>ppJ0m&w6`MH4t}luoa31jAd-MsQoZQJ=uR20NT@kJ%_^H$7mM-t=j)>;L zHDsMGzZrXclE2;eE`GPmH~j9=b#IUR+`YD5d{3Q!>qK4Paku-9F=b+7w)>s2hyr1_ zdzv{>cZQ%k_k-XSEiUKcO?YkohwXWPAPE9|kUW4lI@-B`K8jL3LJz9`na2U*!T$hM z6@keS;S`bqP0k`T5-2e)(;k6asffjgz#9IS48EAW-AWmPl?#hu2d# zJ6R0*-akD{D9~460TQ7!7(vn<2V<5lM7eEeQJfL| z@w8x05goQISOE4$qg3hI7WeHPgEg>Gin?Qp%GHxyt<6#6-*Zq4WF4>QJYv(ZAqWjK zvN2JsY-UTj)*1*y+|MQOZM&j^*|n`^ z=Z1=J#nFsP3Keks$B7Y{vDhxnAFBFr-Xyu&ZoLgGP8f^wXjeSpez-H+yjy;cGY=(i z7Zp=Uqu0`ncM>z0H5Aj8%paE#VD?M=f(T=9Skz)gr0> z*PmL#-=s+Re?m4GX0G9P#*(!fH@OyRsr10r2g5G;Kx~J6uJ^)k!=~$RPoY53AvbX? z)3o95_J%NxCHHuLGnbVXR?*^vXNwhFgHWhPxitIiX*TX_&2D~P9L$H8lohA=oq|B5 z9kSY`D(NgWZBw(__4m)0lozTbV0@s@Mm7n=J8AC<$WXFFg!di|q}rI;@A?HwJ%p&X zyWPFg4gj@01*=YZr5ODf@j~Mcbg`Hhj(OqmqW~mrBZSZkWd?@c{R-UxaMoIZ)3UM8 zR?^8Fs7^z+)>Fqg@aKn)i?J-rB8<*?4mbm2boV3-3Ue*8GurBKYfwqWEo0!ZC1_#2 o%Gdu@aRUW~X{z77Y;L~GaWhJq&4Ohhjm`Vv;E@CL{ilEXf4m(|lK=n! literal 6144 zcmZu#dpwiv|G&1jvK1K{QgS{llAg>Ip_~ufuoXp4No_eL=R8`HkV9&Z$ysJYB}ECv zlH>E_5kn3sp>k%TR*B#3`}@6KzuzCf*ZsO)+v~nQ_vijx*ZscUpZDjJsIt(s#K!a}4aBaBgl7s5{;Iy{l23Q&)U2Ubq;?&Uax$jI=45%4 z;l)gmqjSYKNk@4=@0-PFlHa?Q>q+j=T{D>Lm;#1Q&)BlhsfzR?4E{{H5NHAW7e;-F zXui~{(4I!zFjA3)j4+x^EO#Jnn!8~!p`x;P?_I#rYYG3qCP<}g_@~8nEpW4~Z5Y{v zvtK%vTYvA()qndr#{TJ9y^>mPxVV*NErok_*fc#D3MrNoWE__uv;aU*Q8b(XYl(JD^H;FdAVP zw5Mqwu0B*gm&Ig#Uue5trHG)*xZ!@)Uf{B-inurA+`k+?}y->_R?R> zbM0e@VDnK%%sIx-QwAoQ%xlHw=JR-|o{FsbiLz5yF`sUk6f~_dj(&gN?D7lC>;Wkm@ItWa*CByZTOpwi)z0@08g@P@8-^AV5C3e#7m{~fc|LaI0b{n7yqAeF20t9U5{1zy zo6z=x1*J`vMmPZ?3NNcd98^3Ulomr3rH!1>0(jCu;FcX)Q+v%TH6)LV88TDHceJ~tlkyzF<54X=0FDTQc%}p2RSOqhsQC6_p&ms{Z>l-M0SvU`J4wY+yX(^mUJiCRiJDt{b0M+T>)azV*`|*PQBf zKi*7MYcV%0xe$eL74_Y5QT>kCajw(lRib>_5-Fc2_u_{$Ft}ni%0vHr8fe#~;H%Wc zo8Y>0_3YlN@5%-9?9AOT*C$jdX8}izG#+J{;U7X0k}Pj6lE#ZjrO6w>8cNSjESw4) zzCWz**5|lJP$JS!?iv#BY3e4EV$+w3!g#?pJLi+zIM`oUx1kn%z5;;~wVr=Z8f=xp zoq}c6YuY|?0fAzYv8wPl%nt)xyJzN4hVwL9@GV*^5iQ1L`@_Aew|XK+k)}@uEHir- z(h^jhDCtJk#auZ;xOfAa317^)M4bo}%Qi@Wk77pm&2y3qq;9sp)=q0h^B)WuA?|%V zA^lLA30-{Ck+=FZL3>$%^h4yA&*&rv*F_7sMUCV~T@JgzNh|FQ%_gheiIvK}du5nYJ zJcgDIGndCwY_ei!zxMT$gaHFNJ~ofYe}>g|5T2B{cl9g0ifHrP#5Ln6J%{DQI#{+e zjMd>ASTizl5`Ug3hl~QIZ-o@Z9D;4z4ePo4B+k;3g1Qev+X-Q|wH78gfGj+{G7_Cq zNCpdH=^tk>N?U41%6RiSHY)`^!gZZLtQ?`BMJ-QQzAgbYq}Rvw3hKtDjRut|DTl%* zxphgXBm?CoTY~9hP2I>Xj=2v@OX=N1VP%sRPbZ?XwOgj!m^F-)kM-jcO3DMtq@NkP zGJ;zomj0Cpp?u1gVg=`vPo+iT-&TF0Kc_A1l{VYd++2IdEE~eRmoE-z0>6&msrbh_ zTNYo+;=pGAa_&@`9flI1L4S@-mj*HRH_`8nU4YzSBp<~*F@{*grew-SE&9}%XHcNX zmC{yYO)N7%0Ta<(X|T!Qm_Q+}WpH<6M(Ye_G>tywPwH+rNX4taax=CNOa3t#B`vr^ zL3)iN^ehut@kTb^5{^u>mtso71g=Tu@!fZR(;ToDe?IRZv|NcvTU8RnCB)VB=oW)e zBT0h&-`BXhSV8Ys4HsnLKvG#DO-`!1c%`wY4!?QtH@vW8A?-`^b$#x#)(_pg((C)h zGSrcA{UjJC(z(%-K7!p;j)Jfw8TZAS*5m?neI{q$kv>|mTH+SHzdxmQ>e^1~vPiti z2Idu-2jq$c#Kd^CSuDn`M0pkmj=n)LF>xEWm~hA2{@y(ff4T?43oW?v$*W{z%Ecnr z>G=zMV(JQR&T1ZrC03VG)run{Qi`aZS@SZqs=4&dh^nI}SvM{e!hCo75Ld#^SrOv&o+?RvWb31Nz5cZt(Tj zxnj8qBh9RzG~hh3Hc%2%7_ANd43Kb9TEsDH)5lA_bAgO5jQUAq@i4q{T3?PU3ZV_| z#Hare6yfvA^CUzMX0INqyJ>>J*3HF!an1nCEYS$`V6@L6-Y9ileo+b?Q-xPD_aP`U zk=Z&gZUu@pzZ6LY@5yK9i{0^TY_WF8nLX^!|60S8xO~Ml z2ZCEw;OTnM_R57DURuT-e00L$(_QcmGvL%)=6Ak7z#j)1OwY;Bw;SLb%VkNfWDs8{ z&4(Y25=b85g?B3Cw9s@@mGEHeGP&aq%fORwQOq0J{jB5MAww9}h_Na5fKQ39ZO3Bf zSwVB^Uhh?ZIK|r{ibDwKheXoki5K{3S`}f&;A#9dcyOBIo^zd>!)jH{(D1n!{K1a2 zv5{lDl79_$X8k-rEA}h&t$%;Ol5K5I^RU=jy8@r~2g!TSU)$EVC??mu#0P9ko~kM! z%IP;3{7r>vT@K!lti)?&1n<#U%K5C13zDa(Hk7>~dE3iz28cp~qmzhAC0Dvrk`Ad` z&{~*f@+<1<5%bkDV`{~0(-A0}aihKIBd3}@(YlegFxPXX_vSn-nyO2i6_@P4dBE2U zPB-~rxmw0) zU;&O&i4tcmcCf@ZJDHgJWpBZ~xxjd*pPh%A5?9F%GJCgscEnDsBW-TxUP&K|fZ zpAKK-Ipf*#vu?W^ZoPnm`T*+dgm?(s6F6qV75{qxtEL)>1A#u!@l(nS>wF*$1U**&+ z=NjFbh*VlPS1zZkhEwDIT1ttwg2@@f9lLXZSkdb7yyN+d-m);fBBa)pkzS};KNt6q z;Kd%A`HP`Cd;%G4ZO7E2nvt^GJukq$COjTE@y5<@b~he*w<0!*sBGRV)lp&@$L7jU z_w|bxULraHnVYI1(urGjbHUM@|1lT;ID=3MJJjb`9Vow#5IG&e7}4))t?0PsFL)Nd zt4OQA%)M*X!0>sm?3ErX{~MV<)&JnkIGad!<+wvxp+-4&$Pqh3tJp|2@X8P}dOgAm zVd~DdF%&-E@DYh)?hFx^fGD@J_i}HuIEoFAT;X#SRrVvT%8Ygsq(6x&NRx7{S_ zz1M;QGXmJ>s`?Ps+|hg^rg@h9taSs*21#nvIijZ4Xx&9`ru^=yJ1gYMy?Ah^HD}B^ z9Cw5mr5re{to^EkLe{gZjJSo9jZaPFCLM&m%gBM7 zA`M59lEheNr`i3aHp`R5RCcgk0R*6lisQ1HOv@tst=6gKAeaiVXxv|cjuZPnVIB+{ ze-|6Y6n^Zw^P{vBw&uijsZ5#(v4USHxwcFpEw z$v3hD-Cwc4dKCu=qV6H-rjg$JFFjzchHy@N)}B2r0#V5m9P?s)xI+z^-*m>~WLCBE zklP2pEmTlfu&tmxN>DmgZQ~vc4~Cx;tqq4KzoQ4IGPx$U%RD_s|1oGs6Bf{X)5((C z8gso6nmjo+e+4sYAlG63<3XsWg6Eq=eb&v_GE~7_sZlE@ zG``NqoPajIcDT$%rMlerV_7Axr+_k5uP+jyAW z+AG<(Lo-6swE%DLHz)rd%Gm)}<}Y%{eqo-}+grM-pP^kp5t}Tzft8*C?D?l;aQP+`h%{d|^i(H` zM=dpdirsDoo7~s8A*+vJvCJiFea-9#hubL4`h-4=lfXS2+8k=_o4U@68u#X+5z{Hwcg&6_S204jm&U7jWd(@{T?v}V0xgTdNPUh+7_j;bU zHgxD)4)H_`bvu%TQ2la4r}MXiiimUy%OosABUj>sGZ6JGZS#?;sy0UR$@-l1YV=j7Q zpOw?WIx^VWoPd0XS*RFkLKS-dy_F0uPI;U^Q^&Wtyw*aPbq_e&_uKBZlhA-mHGz)a$EG&y7|+x_R)*#i=-Huzo?DFwy;0f%ItU zky~~btCWIj*eFIWVkIq|ILrBP!D+M|)vFq`JL(}QdD&?W|s&5P^ z+YquUR%9}UjlcppzGHZ{^`6PvpAUsmwfo>DN;gkk>{q*Dv+r3?82;^Y)Sl3ZhcEN{ zO`^#I+*)T{`hmb3-)CK};7h9wjb=-Or}oa=QTXv(zJX44JdE1%n36C3lwQHQ0l!I` zk@hbQMw_iwP171SUphIAD*S~zoes8cD;{Z9&_hbZqx)l-GlgJ(sjJnY%v- zaL($X5D!Mm5R&X;(Th49#rwq|Sd}M7ICDORD{d*A&kAqoL{#28C*GZ?XWhk%A$w=t zx;Zv@L1?qTgpl_*M=DR_eMUNrhc*Xf!s+PG#gT%roH%VjofOuID@Lx&iX)X?&DHd= zCLNMW;f^xT^;`gY@x23zU*_i!!AK^as)-lAF;*BaGoXeID&G?SCax&o2tk&Q)FfWrS`15AQB<6 zydA2k;jJvsjm1G<3Sm*+&{OW^15yL-nI`WVzBV!Zdt9gvFM4NKlCgB;ziPVFvAP=;%gy2WIE6vwo$ z4R)b6nxK*BFIeWz6f|E+tkln(*gaL7)^7HLlXu79^mKaS0;cfD(<=2es8EYr$!pK^ zI2o&%VzXQyzV_@1Jh<(?a*;HU-j{c%)H{*CYjB*{6WO8o$^2VaELn6A%1Or8F4E1N zUP)dNj0*mD1`jn*sD^9A6eBrSc05sSkZt zy1$DRFDLeSvBtjp5kC=A>DI;z>>6oL#80i-{FMgyqK?{ngaWd#&EV77g&&bfWT7!U z7sqp2xtC!Hn;c$b5TK&vEvr2*(*yQQx#p^fCg2+26~jl(9vy-rl(eo)nsRt?RfOg{ zpLnsUP=O)d+7?8pf1Sp)-B$?_Pp10=?ndYD{{9GWyCo_}Sh7a}-lYdb=QsnXxe9m5 z)j2m|(sZ}u48w=spI3(^pH_((H)7&LC=c#|*BdP@_C`bmk-?T(uC)RxsBr zy;R|7zSUybUU)s!Ob>T4TUV*Xz>r?5qB@x?BVt;hUc2xzW*`v~o|u?ZH~4{wHN{th WqnUN%kHw(QKCrbUTT~qNiu*5pqW;tX From f55c84ce3b9f86edf28048143e1284d30463b63e Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 13 Oct 2023 17:19:05 +0800 Subject: [PATCH 078/114] =?UTF-8?q?fix:=20=E5=8F=91=E5=B8=83=E6=9C=BA500?= =?UTF-8?q?=20(#11841)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/terminal/models/applet/applet.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index 99f4e1689..23450aff3 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -163,7 +163,7 @@ class Applet(JMSBaseModel): counts[host_id] += 1 hosts = list(sorted(hosts, key=lambda h: counts[h.id])) - return hosts[0] + return hosts[0] if hosts else None def select_host(self, user, asset): hosts = self.hosts.filter(is_active=True) @@ -186,6 +186,8 @@ class Applet(JMSBaseModel): host = pref_host[0] else: host = self._select_by_load(hosts) + if host is None: + return cache.set(prefer_key, str(host.id), timeout=None) return host From 0a28c5650cae8c75fb8b8af676044d2a68744e60 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:28:53 +0800 Subject: [PATCH 079/114] =?UTF-8?q?perf:=20=E4=B8=89=E6=96=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E5=BD=95=E9=80=9A=E7=9F=A5=20(#11846)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/signal_handlers/login_log.py | 32 ++++++++++++++---------- apps/authentication/middleware.py | 7 ++++++ apps/authentication/mixins.py | 3 ++- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/apps/audits/signal_handlers/login_log.py b/apps/audits/signal_handlers/login_log.py index 835e4842f..2ad9c0a7a 100644 --- a/apps/audits/signal_handlers/login_log.py +++ b/apps/audits/signal_handlers/login_log.py @@ -11,7 +11,6 @@ from django.utils.functional import LazyObject from django.utils.translation import gettext_lazy as _ from rest_framework.request import Request -from acls.const import ActionChoices from acls.models import LoginACL from acls.notifications import UserLoginReminderMsg from audits.models import UserLoginLog @@ -85,6 +84,9 @@ def generate_data(username, request, login_type=None): def create_user_session(request, user_id, instance: UserLoginLog): + # TODO 目前只记录 web 登录的 session + if instance.type != LoginTypeChoices.web: + return session_key = request.session.session_key or '-' session_store_cls = import_module(settings.SESSION_ENGINE).SessionStore session_store = session_store_cls(session_key=session_key) @@ -102,10 +104,21 @@ def create_user_session(request, user_id, instance: UserLoginLog): 'date_expired': instance.datetime + timedelta(seconds=ttl), } user_session = UserSession.objects.create(**online_session_data) - request.session['user_session_id'] = user_session.id + request.session['user_session_id'] = str(user_session.id) -def send_login_info_to_reviewers(instance: UserLoginLog, reviewers): +def send_login_info_to_reviewers(instance: UserLoginLog | str, auth_acl_id): + if isinstance(instance, str): + instance = UserLoginLog.objects.filter(id=instance).first() + + if not instance: + return + + acl = LoginACL.objects.filter(id=auth_acl_id).first() + if not acl or not acl.reviewers.exists(): + return + + reviewers = acl.reviewers.all() for reviewer in reviewers: UserLoginReminderMsg(reviewer, instance).publish_async() @@ -119,22 +132,15 @@ def on_user_auth_success(sender, user, request, login_type=None, **kwargs): data.update({'mfa': int(user.mfa_enabled), 'status': True}) instance = write_login_log(**data) - # TODO 目前只记录 web 登录的 session - if instance.type != LoginTypeChoices.web: - return create_user_session(request, user.id, instance) - + request.session['user_log_id'] = str(instance.id) + request.session['can_send_notifications'] = True auth_notice_required = request.session.get('auth_notice_required') if not auth_notice_required: return auth_acl_id = request.session.get('auth_acl_id') - acl = LoginACL.objects.filter(id=auth_acl_id, action=ActionChoices.notice).first() - if not acl or not acl.reviewers.exists(): - return - - reviewers = acl.reviewers.all() - send_login_info_to_reviewers(instance, reviewers) + send_login_info_to_reviewers(instance, auth_acl_id) @receiver(post_auth_failed) diff --git a/apps/authentication/middleware.py b/apps/authentication/middleware.py index 572ad39dc..9f245099d 100644 --- a/apps/authentication/middleware.py +++ b/apps/authentication/middleware.py @@ -8,6 +8,7 @@ from django.utils.deprecation import MiddlewareMixin from django.utils.translation import gettext as _ from apps.authentication import mixins +from audits.signal_handlers import send_login_info_to_reviewers from authentication.signals import post_auth_failed from common.utils import gen_key_pair from common.utils import get_request_ip @@ -105,6 +106,12 @@ class ThirdPartyLoginMiddleware(mixins.AuthMixin): guard_url = "%s?%s" % (guard_url, args) response = redirect(guard_url) finally: + if request.session.get('can_send_notifications') and \ + self.request.session['auth_notice_required']: + request.session['can_send_notifications'] = False + user_log_id = self.request.session.get('user_log_id') + auth_acl_id = self.request.session.get('auth_acl_id') + send_login_info_to_reviewers(user_log_id, auth_acl_id) return response diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 2446b4667..31cb1dc19 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -523,7 +523,8 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost def clear_auth_mark(self): keys = [ 'auth_password', 'user_id', 'auth_confirm_required', - 'auth_notice_required', 'auth_ticket_id', 'auth_acl_id' + 'auth_notice_required', 'auth_ticket_id', 'auth_acl_id', + 'user_session_id', 'user_log_id', 'can_send_notifications' ] for k in keys: self.request.session.pop(k, '') From 7dcae1e05a169d943120c71898373b3e54960f6a Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:27:37 +0800 Subject: [PATCH 080/114] =?UTF-8?q?perf:=20=E5=91=BD=E4=BB=A4=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E4=B8=AD=20=E5=8E=BB=E6=8E=89=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E5=8A=A8=E4=BD=9C=20(#11851)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/acls/serializers/command_acl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/acls/serializers/command_acl.py b/apps/acls/serializers/command_acl.py index dde953277..44fd8e0ed 100644 --- a/apps/acls/serializers/command_acl.py +++ b/apps/acls/serializers/command_acl.py @@ -8,6 +8,7 @@ from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.utils import tmp_to_root_org from terminal.models import Session from .base import BaseUserAssetAccountACLSerializer as BaseSerializer +from ..const import ActionChoices __all__ = ["CommandFilterACLSerializer", "CommandGroupSerializer", "CommandReviewSerializer"] @@ -31,8 +32,7 @@ class CommandFilterACLSerializer(BaseSerializer, BulkOrgResourceModelSerializer) class Meta(BaseSerializer.Meta): model = CommandFilterACL fields = BaseSerializer.Meta.fields + ['command_groups'] - # 默认都支持所有的 actions - action_choices_exclude = [] + action_choices_exclude = [ActionChoices.notice] class CommandReviewSerializer(serializers.Serializer): From 70661242c1202730303b99c47e2e3fdbde70cba0 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 14:22:20 +0800 Subject: [PATCH 081/114] =?UTF-8?q?fix:=20=E5=9C=A8=E7=BA=BF=E7=94=A8?= =?UTF-8?q?=E6=88=B7=20=E4=B8=8B=E7=BA=BF=E6=9D=83=E9=99=90=E9=94=99?= =?UTF-8?q?=E8=AF=AF=20=E5=AF=BC=E8=87=B4=E5=AE=A1=E8=AE=A1=E5=91=98?= =?UTF-8?q?=E6=97=A0=E6=9D=83=E9=99=90=E4=B8=8B=E7=BA=BF=E7=94=A8=E6=88=B7?= =?UTF-8?q?=20(#11853)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/audits/api.py b/apps/audits/api.py index 9caed8a0c..5150bcb8d 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -260,7 +260,7 @@ class UserSessionViewSet(CommonApiMixin, viewsets.ModelViewSet): filterset_fields = ['id', 'ip', 'city', 'type'] search_fields = ['id', 'ip', 'city'] rbac_perms = { - 'offline': ['users.offline_usersession'] + 'offline': ['audits.offline_usersession'] } @property From 86e8f3a80beae81879c566eb7edf348c47b7c6a9 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:06:47 +0800 Subject: [PATCH 082/114] =?UTF-8?q?fix:=20=E7=BB=84=E7=BB=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=98=E4=B8=8D=E8=83=BD=E5=88=9B=E5=BB=BA=E7=94=A8?= =?UTF-8?q?=E6=88=B7=20(#11856)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/rbac/const.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/rbac/const.py b/apps/rbac/const.py index fac97b5db..04bfb1617 100644 --- a/apps/rbac/const.py +++ b/apps/rbac/const.py @@ -79,7 +79,6 @@ exclude_permissions = ( ('rbac', 'systemrolebinding', 'change', 'systemrolebinding'), ('rbac', 'orgrolebinding', 'change', 'orgrolebinding'), ('rbac', 'menupermission', '*', 'menupermission'), - ('rbac', 'role', '*', '*'), ('ops', 'adhocexecution', 'view,add,delete,change', '*'), ('ops', 'jobexecution', 'change,delete', 'jobexecution'), ('ops', 'historicaljob', '*', '*'), @@ -132,7 +131,7 @@ only_system_permissions = ( ('rbac', 'systemrole', '*', '*'), ('rbac', 'rolebinding', '*', '*'), ('rbac', 'systemrolebinding', '*', '*'), - ('rbac', 'orgrole', 'delete,add,change', '*'), + ('rbac', 'orgrole', 'delete,add,change', 'orgrole'), ('orgs', 'organization', '*', '*'), ('xpack', 'license', '*', '*'), ('settings', 'setting', '*', '*'), From 8b6526211c5890a7d383aec2148b5c3688b6a84b Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 16:40:21 +0800 Subject: [PATCH 083/114] =?UTF-8?q?perf:=20=E5=B7=A5=E5=8D=95=E5=8A=A8?= =?UTF-8?q?=E4=BD=9C=E6=B7=BB=E5=8A=A0=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97?= =?UTF-8?q?=20(#11857)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/const.py | 6 ++++-- apps/tickets/api/ticket.py | 22 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/audits/const.py b/apps/audits/const.py index b08595a2f..4418e9e7f 100644 --- a/apps/audits/const.py +++ b/apps/audits/const.py @@ -29,11 +29,13 @@ class ActionChoices(TextChoices): connect = "connect", _("Connect") login = "login", _("Login") change_auth = "change_password", _("Change password") - # acls action - reject = 'reject', _('Reject') + accept = 'accept', _('Accept') review = 'review', _('Review') notice = 'notice', _('Notifications') + reject = 'reject', _('Reject') + approve = 'approve', _('Approve') + close = 'close', _('Close') class LoginTypeChoices(TextChoices): diff --git a/apps/tickets/api/ticket.py b/apps/tickets/api/ticket.py index 1e44c3818..da60aefe8 100644 --- a/apps/tickets/api/ticket.py +++ b/apps/tickets/api/ticket.py @@ -6,9 +6,10 @@ from rest_framework.decorators import action from rest_framework.exceptions import MethodNotAllowed from rest_framework.response import Response +from audits.handler import create_or_update_operate_log from common.api import CommonApiMixin from common.const.http import POST, PUT, PATCH -from orgs.utils import tmp_to_root_org +from orgs.utils import tmp_to_root_org, tmp_to_org from rbac.permissions import RBACPermission from tickets import filters from tickets import serializers @@ -17,6 +18,7 @@ from tickets.models import ( ApplyLoginAssetTicket, ApplyCommandTicket ) from tickets.permissions.ticket import IsAssignee, IsApplicant +from ..const import TicketAction __all__ = [ 'TicketViewSet', 'ApplyAssetTicketViewSet', @@ -77,6 +79,21 @@ class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet): with tmp_to_root_org(): return super().create(request, *args, **kwargs) + @staticmethod + def _record_operate_log(ticket, action): + with tmp_to_org(ticket.org_id): + after = { + 'ID': str(ticket.id), + str(_('Name')): ticket.title, + str(_('Applicant')): str(ticket.applicant), + } + object_name = ticket._meta.object_name + resource_type = ticket._meta.verbose_name + create_or_update_operate_log( + action, resource_type, resource=ticket, + after=after, object_name=object_name + ) + @action(detail=True, methods=[PUT, PATCH], permission_classes=[IsAssignee, ]) def approve(self, request, *args, **kwargs): self.ticket_not_allowed() @@ -88,18 +105,21 @@ class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet): serializer.is_valid(raise_exception=True) instance = serializer.save() instance.approve(processor=request.user) + self._record_operate_log(instance, TicketAction.approve) return Response('ok') @action(detail=True, methods=[PUT], permission_classes=[IsAssignee, ]) def reject(self, request, *args, **kwargs): instance = self.get_object() instance.reject(processor=request.user) + self._record_operate_log(instance, TicketAction.reject) return Response('ok') @action(detail=True, methods=[PUT], permission_classes=[IsAssignee | IsApplicant, ]) def close(self, request, *args, **kwargs): instance = self.get_object() instance.close() + self._record_operate_log(instance, TicketAction.close) return Response('ok') @action(detail=False, methods=[PUT], permission_classes=[RBACPermission, ]) From bda23b3d2ac4b0b284921d39387802667f4e0213 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:15:53 +0800 Subject: [PATCH 084/114] =?UTF-8?q?fix:=20=E8=B0=83API=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E4=B8=8E=E7=88=B6=E8=8A=82=E7=82=B9=E5=90=8C=E5=90=8D=E7=9A=84?= =?UTF-8?q?=E5=AD=90=E8=8A=82=E7=82=B9=E6=8A=A5=E9=94=99=EF=BC=9A=E5=90=8C?= =?UTF-8?q?=E7=BA=A7=E5=88=AB=E8=8A=82=E7=82=B9=E5=90=8D=E7=A7=B0=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E9=87=8D=E5=A4=8D=20(#11858)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/assets/serializers/node.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py index ac161318b..70da291c3 100644 --- a/apps/assets/serializers/node.py +++ b/apps/assets/serializers/node.py @@ -30,8 +30,9 @@ class NodeSerializer(BulkOrgResourceModelSerializer): if '/' in data: error = _("Can't contains: " + "/") raise serializers.ValidationError(error) - if self.instance: - instance = self.instance + view = self.context['view'] + instance = self.instance or getattr(view, 'instance', None) + if instance: siblings = instance.get_siblings() else: instance = Node.org_root() From 5c0905b3b5ff419898452a5a487d501156964f9a Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:51:26 +0800 Subject: [PATCH 085/114] =?UTF-8?q?fix:=20=E6=93=8D=E4=BD=9C=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=85=A8=E5=B1=80=E7=BB=84=E7=BB=87=E6=95=B0=E9=87=8F?= =?UTF-8?q?=E4=B8=8D=E5=AF=B9=20(#11859)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/api.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/audits/api.py b/apps/audits/api.py index 5150bcb8d..4b4a061b2 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -218,11 +218,10 @@ class OperateLogViewSet(OrgReadonlyModelViewSet): return super().get_serializer_class() def get_queryset(self): - org_q = Q(org_id=current_org.id) + org_q = Q() if self.is_action_detail: org_q |= Q(org_id=Organization.SYSTEM_ID) - with tmp_to_root_org(): - qs = OperateLog.objects.filter(org_q) + qs = OperateLog.objects.filter(org_q) es_config = settings.OPERATE_LOG_ELASTICSEARCH_CONFIG if es_config: engine_mod = import_module(TYPE_ENGINE_MAPPING['es']) From c917d8f346a0ab43e79fb9cef2faf6dd1cb480e3 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 19:33:13 +0800 Subject: [PATCH 086/114] =?UTF-8?q?fix:=20=E7=B3=BB=E7=BB=9F=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=20=E5=AE=89=E5=85=A8=E8=AE=BE=E7=BD=AE=E6=9D=83?= =?UTF-8?q?=E9=99=90=E4=B8=BA=E9=94=99=E8=AF=AF=20(#11860)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/settings/api/settings.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/settings/api/settings.py b/apps/settings/api/settings.py index 4a8f6cc3d..e4af8e147 100644 --- a/apps/settings/api/settings.py +++ b/apps/settings/api/settings.py @@ -65,6 +65,11 @@ class SettingsApi(generics.RetrieveUpdateAPIView): 'basic': 'settings.view_setting', 'terminal': 'settings.change_terminal', 'security': 'settings.change_security', + 'security_basic': 'settings.change_security', + 'security_auth': 'settings.change_security', + 'security_session': 'settings.change_security', + 'security_password': 'settings.change_security', + 'security_login_limit': 'settings.change_security', 'ldap': 'settings.change_auth', 'email': 'settings.change_email', 'email_content': 'settings.change_email', From f5176bcc6f592abf6cdb1cdc10a58dcbd70dbab7 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:04:05 +0800 Subject: [PATCH 087/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E6=96=87=E4=BB=B6=20(#11863)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/migrations/0023_auto_20230906_1322.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/audits/migrations/0023_auto_20230906_1322.py b/apps/audits/migrations/0023_auto_20230906_1322.py index 9f4d0553d..34998318c 100644 --- a/apps/audits/migrations/0023_auto_20230906_1322.py +++ b/apps/audits/migrations/0023_auto_20230906_1322.py @@ -1,7 +1,7 @@ # Generated by Django 4.1.10 on 2023-09-06 05:31 -from django.db import migrations, models import django.utils.timezone +from django.db import migrations, models class Migration(migrations.Migration): @@ -19,7 +19,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='operatelog', name='action', - field=models.CharField(choices=[('view', 'View'), ('update', 'Update'), ('delete', 'Delete'), ('create', 'Create'), ('download', 'Download'), ('connect', 'Connect'), ('login', 'Login'), ('change_password', 'Change password'), ('reject', 'Reject'), ('accept', 'Accept'), ('review', 'Review'), ('notice', 'Notifications')], max_length=16, verbose_name='Action'), + field=models.CharField(choices=[('view', 'View'), ('update', 'Update'), ('delete', 'Delete'), ('create', 'Create'), ('download', 'Download'), ('connect', 'Connect'), ('login', 'Login'), ('change_password', 'Change password'), ('accept', 'Accept'), ('review', 'Review'), ('notice', 'Notifications'), ('reject', 'Reject'), ('approve', 'Approve'), ('close', 'Close')], max_length=16, verbose_name='Action'), ), migrations.AlterField( model_name='userloginlog', From 741705b85b8c6d2ec946948177de98add2f3eeb7 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 17 Oct 2023 11:11:55 +0800 Subject: [PATCH 088/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E5=AE=9A?= =?UTF-8?q?=E6=9C=9F=E6=B8=85=E7=90=86=E6=97=A5=E5=BF=97=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=97=B6=E9=97=B4180=E5=A4=A9=20(#11864)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/jumpserver/conf.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index e540ef71e..6ba54732d 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -545,12 +545,12 @@ class Config(dict): 'MAGNUS_ORACLE_PORTS': '30000-30030', # 记录清理清理 - 'LOGIN_LOG_KEEP_DAYS': 200, - 'TASK_LOG_KEEP_DAYS': 90, - 'OPERATE_LOG_KEEP_DAYS': 200, - 'ACTIVITY_LOG_KEEP_DAYS': 200, - 'FTP_LOG_KEEP_DAYS': 200, - 'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS': 30, + 'LOGIN_LOG_KEEP_DAYS': 180, + 'TASK_LOG_KEEP_DAYS': 180, + 'OPERATE_LOG_KEEP_DAYS': 180, + 'ACTIVITY_LOG_KEEP_DAYS': 180, + 'FTP_LOG_KEEP_DAYS': 180, + 'CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS': 180, 'TICKETS_ENABLED': True, From 772684d24cdce0afbaf37e983d59ad9cb9f6421c Mon Sep 17 00:00:00 2001 From: Bai Date: Tue, 17 Oct 2023 11:19:13 +0800 Subject: [PATCH 089/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dcrontab?= =?UTF-8?q?=E8=AF=AD=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/const/crontab.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/common/const/crontab.py b/apps/common/const/crontab.py index bd9809176..e4de195eb 100644 --- a/apps/common/const/crontab.py +++ b/apps/common/const/crontab.py @@ -1,5 +1,5 @@ -CRONTAB_AT_AM_TWO = '0 14 * * *' +CRONTAB_AT_AM_TWO = '0 2 * * *' CRONTAB_AT_AM_TEN = '0 10 * * *' -CRONTAB_AT_PM_TWO = '0 2 * * *' +CRONTAB_AT_PM_TWO = '0 14 * * *' From 73b75df524bc08e52eee475d01039abbec5ed391 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 17 Oct 2023 14:28:19 +0800 Subject: [PATCH 090/114] =?UTF-8?q?perf:=20=E8=B5=84=E4=BA=A7acl=E6=8B=92?= =?UTF-8?q?=E7=BB=9D=E5=90=8E=E6=B2=A1=E8=AE=B0=E5=BD=95=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E6=97=A5=E5=BF=97=20(#11868)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/api.py | 6 +++--- apps/audits/backends/db.py | 2 +- apps/authentication/api/connection_token.py | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/audits/api.py b/apps/audits/api.py index 4b4a061b2..f332c5718 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -218,10 +218,10 @@ class OperateLogViewSet(OrgReadonlyModelViewSet): return super().get_serializer_class() def get_queryset(self): - org_q = Q() + qs = OperateLog.objects.all() if self.is_action_detail: - org_q |= Q(org_id=Organization.SYSTEM_ID) - qs = OperateLog.objects.filter(org_q) + with tmp_to_root_org(): + qs |= OperateLog.objects.filter(org_id=Organization.SYSTEM_ID) es_config = settings.OPERATE_LOG_ELASTICSEARCH_CONFIG if es_config: engine_mod = import_module(TYPE_ENGINE_MAPPING['es']) diff --git a/apps/audits/backends/db.py b/apps/audits/backends/db.py index bb2e241c1..870c25c9c 100644 --- a/apps/audits/backends/db.py +++ b/apps/audits/backends/db.py @@ -58,7 +58,7 @@ class OperateLogStore(object): return diff_list def save(self, **kwargs): - log_id = kwargs.get('id') + log_id = kwargs.pop('id', None) before = kwargs.pop('before') or {} after = kwargs.pop('after') or {} diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index c14df034d..8cefbe4e6 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -432,6 +432,14 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView reviewer, asset, user, input_username ).publish_async() + def create(self, request, *args, **kwargs): + try: + response = super().create(request, *args, **kwargs) + except JMSException as e: + data = {'code': e.detail.code, 'detail': e.detail} + return Response(data, status=e.status_code) + return response + class SuperConnectionTokenViewSet(ConnectionTokenViewSet): serializer_classes = { From e88e4438ba9694e371e192fba35d49dbd5944c3f Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:01:21 +0800 Subject: [PATCH 091/114] =?UTF-8?q?fix:=20acl=20=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97=20=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E4=B8=BA=E7=A9=BA=20(#11869)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/authentication/api/connection_token.py | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 8cefbe4e6..2f7238a38 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -300,6 +300,7 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView 'get_rdp_file': 'authentication.add_connectiontoken', 'get_client_protocol_url': 'authentication.add_connectiontoken', } + input_username = '' def get_queryset(self): queryset = ConnectionToken.objects \ @@ -335,6 +336,7 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView self._insert_connect_options(data, user) asset = data.get('asset') account_name = data.get('account') + self.input_username = data.get('input_username', '') _data = self._validate(user, asset, account_name) data.update(_data) return serializer @@ -361,10 +363,9 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView if account.has_secret: data['input_secret'] = '' - input_username = data.get('input_username', '') if account.username != AliasAccount.INPUT: data['input_username'] = '' - ticket = self._validate_acl(user, asset, account, input_username) + ticket = self._validate_acl(user, asset, account) if ticket: data['from_ticket'] = ticket data['is_active'] = False @@ -382,13 +383,12 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView raise JMSException(code='perm_expired', detail=msg) return account - @staticmethod - def _record_operate_log(acl, asset, input_username): + def _record_operate_log(self, acl, asset): from audits.handler import create_or_update_operate_log with tmp_to_org(asset.org_id): after = { str(_('Assets')): str(asset), - str(_('Account')): input_username + str(_('Account')): self.input_username } object_name = acl._meta.object_name resource_type = acl._meta.verbose_name @@ -397,7 +397,7 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView after=after, object_name=object_name ) - def _validate_acl(self, user, asset, account, input_username): + def _validate_acl(self, user, asset, account): from acls.models import LoginAssetACL acls = LoginAssetACL.filter_queryset(user=user, asset=asset, account=account) ip = get_request_ip(self.request) @@ -405,19 +405,19 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView if not acl: return if acl.is_action(acl.ActionChoices.accept): - self._record_operate_log(acl, asset, input_username) + self._record_operate_log(acl, asset) return if acl.is_action(acl.ActionChoices.reject): - self._record_operate_log(acl, asset, input_username) + self._record_operate_log(acl, asset) msg = _('ACL action is reject: {}({})'.format(acl.name, acl.id)) raise JMSException(code='acl_reject', detail=msg) if acl.is_action(acl.ActionChoices.review): if not self.request.query_params.get('create_ticket'): msg = _('ACL action is review') raise JMSException(code='acl_review', detail=msg) - self._record_operate_log(acl, asset, input_username) + self._record_operate_log(acl, asset) ticket = LoginAssetACL.create_login_asset_review_ticket( - user=user, asset=asset, account_username=input_username, + user=user, asset=asset, account_username=self.input_username, assignees=acl.reviewers.all(), org_id=asset.org_id ) return ticket @@ -426,10 +426,10 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView if not reviewers: return - self._record_operate_log(acl, asset, input_username) + self._record_operate_log(acl, asset) for reviewer in reviewers: AssetLoginReminderMsg( - reviewer, asset, user, input_username + reviewer, asset, user, self.input_username ).publish_async() def create(self, request, *args, **kwargs): From b5ef239c6ce99a9ee61cdebd0077875592dc9536 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 17 Oct 2023 15:57:15 +0800 Subject: [PATCH 092/114] =?UTF-8?q?perf:=20=E5=A2=9E=E5=8A=A0=E9=92=88?= =?UTF-8?q?=E5=AF=B9=20ip=20=E7=9A=84=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/connection_token.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 2f7238a38..bd2c01ef1 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -18,7 +18,7 @@ from accounts.const import AliasAccount from acls.notifications import AssetLoginReminderMsg from common.api import JMSModelViewSet from common.exceptions import JMSException -from common.utils import random_string, get_logger, get_request_ip +from common.utils import random_string, get_logger, get_request_ip_or_data from common.utils.django import get_request_os from common.utils.http import is_true, is_false from orgs.mixins.api import RootOrgViewMixin @@ -400,7 +400,7 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView def _validate_acl(self, user, asset, account): from acls.models import LoginAssetACL acls = LoginAssetACL.filter_queryset(user=user, asset=asset, account=account) - ip = get_request_ip(self.request) + ip = get_request_ip_or_data(self.request) acl = LoginAssetACL.get_match_rule_acls(user, ip, acls) if not acl: return From 3a118b6753e76c07bcc6d8f23d860b5f6a5886ef Mon Sep 17 00:00:00 2001 From: halo Date: Tue, 17 Oct 2023 17:13:30 +0800 Subject: [PATCH 093/114] =?UTF-8?q?perf:=20=E6=9B=B4=E6=96=B0clients?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/templates/resource_download.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/templates/resource_download.html b/apps/templates/resource_download.html index e69634564..e1e49f124 100644 --- a/apps/templates/resource_download.html +++ b/apps/templates/resource_download.html @@ -15,7 +15,7 @@ p {
-

JumpServer {% trans 'Client' %} v2.0.1

+

JumpServer {% trans 'Client' %} v2.0.2

{% trans 'JumpServer Client, currently used to launch the client, now only support launch RDP SSH client, The Telnet client will next' %}

From b313598227859a0ed3128f8715c93431e7297d3d Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 17 Oct 2023 19:15:46 +0800 Subject: [PATCH 094/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E6=89=B9=E9=87=8F=E6=B7=BB=E5=8A=A0=E6=A8=A1=E7=89=88?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E6=97=B6name=E6=B2=A1=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E8=BF=87=E6=9D=A5=EF=BC=8C=E8=B5=84=E4=BA=A7=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=97=B6=E4=BD=BF=E7=94=A8=E6=A8=A1=E7=89=88=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=88=87=E6=8D=A2=E8=87=AA=EF=BC=8C=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E5=85=8B=E9=9A=86=E6=97=B6=E7=94=9F=E6=88=90=E7=9A=84?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E6=B2=A1=E6=9C=89=E5=88=87=E6=8D=A2=E8=87=AA?= =?UTF-8?q?=20(#11877)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/serializers/account/account.py | 3 +- apps/assets/serializers/asset/common.py | 38 ++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index 0864499b6..8b8d9be36 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -78,7 +78,8 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer): def get_template_attr_for_account(template): # Set initial data from template field_names = [ - 'username', 'secret', 'secret_type', 'privileged', 'is_active' + 'name', 'username', 'secret', + 'secret_type', 'privileged', 'is_active' ] attrs = {} diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 78f691237..aae31c7ff 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -281,14 +281,48 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali return protocols_data_map.values() @staticmethod - def accounts_create(accounts_data, asset): + def update_account_su_from(accounts, include_su_from_accounts): + if not include_su_from_accounts: + return + name_map = {account.name: account for account in accounts} + username_secret_type_map = { + (account.username, account.secret_type): account for account in accounts + } + + for name, username_secret_type in include_su_from_accounts.items(): + account = name_map.get(name) + if not account: + continue + su_from_account = username_secret_type_map.get(username_secret_type) + if su_from_account: + account.su_from = su_from_account + account.save() + + def accounts_create(self, accounts_data, asset): + from accounts.models import AccountTemplate if not accounts_data: return + su_from_name_username_secret_type_map = {} for data in accounts_data: data['asset'] = asset.id + name = data.get('name') + su_from = data.pop('su_from', None) + template_id = data.get('template', None) + if template_id: + template = AccountTemplate.objects.get(id=template_id) + if template and template.su_from: + su_from_name_username_secret_type_map[template.name] = ( + template.su_from.username, template.su_from.secret_type + ) + elif isinstance(su_from, dict): + su_from = Account.objects.get(id=su_from.get('id')) + su_from_name_username_secret_type_map[name] = ( + su_from.username, su_from.secret_type + ) s = AssetAccountSerializer(data=accounts_data, many=True) s.is_valid(raise_exception=True) - s.save() + accounts = s.save() + self.update_account_su_from(accounts, su_from_name_username_secret_type_map) @atomic def create(self, validated_data): From f088bbce120cd0a7c6176880672a76e69ee96ab9 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 17 Oct 2023 19:21:59 +0800 Subject: [PATCH 095/114] =?UTF-8?q?perf:=20=E8=BF=9E=E6=8E=A5=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=8A=A8=E4=BD=9C=E5=8E=BB=E6=8E=89:=20?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=20(#11878)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/acls/serializers/connect_method.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/acls/serializers/connect_method.py b/apps/acls/serializers/connect_method.py index b1daf56ac..917933dcb 100644 --- a/apps/acls/serializers/connect_method.py +++ b/apps/acls/serializers/connect_method.py @@ -1,7 +1,7 @@ from orgs.mixins.serializers import BulkOrgResourceModelSerializer from .base import BaseUserAssetAccountACLSerializer as BaseSerializer -from ..models import ConnectMethodACL from ..const import ActionChoices +from ..models import ConnectMethodACL __all__ = ["ConnectMethodACLSerializer"] @@ -14,5 +14,5 @@ class ConnectMethodACLSerializer(BaseSerializer, BulkOrgResourceModelSerializer) if i not in ['assets', 'accounts'] ] action_choices_exclude = BaseSerializer.Meta.action_choices_exclude + [ - ActionChoices.review, ActionChoices.accept + ActionChoices.review, ActionChoices.accept, ActionChoices.notice ] From d3a283232f9404bdea3b1fd68d09d7f8a4c2cbcc Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 14:10:24 +0800 Subject: [PATCH 096/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20xpack=20li?= =?UTF-8?q?cense=20=E6=A3=80=E6=9F=A5=20(#11885)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ibuler --- .../models/automations/push_account.py | 4 ++-- apps/acls/serializers/base.py | 4 ++-- apps/assets/const/base.py | 5 ++-- apps/assets/const/protocol.py | 2 +- apps/jumpserver/settings/_xpack.py | 12 +++++++++- apps/jumpserver/utils.py | 23 ------------------- apps/settings/api/public.py | 5 ++-- apps/terminal/api/applet/applet.py | 2 +- apps/terminal/connect_methods.py | 4 ++-- apps/users/views/profile/reset.py | 2 +- 10 files changed, 24 insertions(+), 39 deletions(-) diff --git a/apps/accounts/models/automations/push_account.py b/apps/accounts/models/automations/push_account.py index 84aa1bb6e..99bbda01d 100644 --- a/apps/accounts/models/automations/push_account.py +++ b/apps/accounts/models/automations/push_account.py @@ -1,9 +1,9 @@ +from django.conf import settings from django.db import models from django.utils.translation import gettext_lazy as _ from accounts.const import AutomationTypes from accounts.models import Account -from jumpserver.utils import has_valid_xpack_license from .base import AccountBaseAutomation from .change_secret import ChangeSecretMixin @@ -41,7 +41,7 @@ class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation): def save(self, *args, **kwargs): self.type = AutomationTypes.push_account - if not has_valid_xpack_license(): + if not settings.XPACK_LICENSE_IS_VALID: self.is_periodic = False super().save(*args, **kwargs) diff --git a/apps/acls/serializers/base.py b/apps/acls/serializers/base.py index 4c2d80bfc..09f75bf42 100644 --- a/apps/acls/serializers/base.py +++ b/apps/acls/serializers/base.py @@ -1,9 +1,9 @@ +from django.conf import settings from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from acls.models.base import BaseACL from common.serializers.fields import JSONManyToManyField, LabeledChoiceField -from jumpserver.utils import has_valid_xpack_license from orgs.models import Organization from ..const import ActionChoices @@ -68,7 +68,7 @@ class ActionAclSerializer(serializers.Serializer): field_action = self.fields.get("action") if not field_action: return - if not has_valid_xpack_license(): + if not settings.XPACK_LICENSE_IS_VALID: field_action._choices.pop(ActionChoices.review, None) for choice in self.Meta.action_choices_exclude: field_action._choices.pop(choice, None) diff --git a/apps/assets/const/base.py b/apps/assets/const/base.py index 66c991caa..a77691c1b 100644 --- a/apps/assets/const/base.py +++ b/apps/assets/const/base.py @@ -1,9 +1,8 @@ +from django.conf import settings from django.db import models from django.db.models import TextChoices from django.utils.translation import gettext_lazy as _ -from jumpserver.utils import has_valid_xpack_license - class Type: def __init__(self, label, value): @@ -113,7 +112,7 @@ class BaseType(TextChoices): @classmethod def get_choices(cls): - if not has_valid_xpack_license(): + if not settings.XPACK_LICENSE_IS_VALID: return [ (tp.value, tp.label) for tp in cls.get_community_types() diff --git a/apps/assets/const/protocol.py b/apps/assets/const/protocol.py index 9976596b2..5aea2daec 100644 --- a/apps/assets/const/protocol.py +++ b/apps/assets/const/protocol.py @@ -276,7 +276,7 @@ class Protocol(ChoicesMixin, models.TextChoices): } } } - if settings.XPACK_ENABLED: + if settings.XPACK_LICENSE_IS_VALID: choices = protocols[cls.chatgpt]['setting']['api_mode']['choices'] choices.extend([ ('gpt-4', 'GPT-4'), diff --git a/apps/jumpserver/settings/_xpack.py b/apps/jumpserver/settings/_xpack.py index 9f4319a35..6e43c7501 100644 --- a/apps/jumpserver/settings/_xpack.py +++ b/apps/jumpserver/settings/_xpack.py @@ -1,17 +1,27 @@ # -*- coding: utf-8 -*- # +import datetime import os -from .. import const + from .base import INSTALLED_APPS, TEMPLATES +from .. import const + +current_year = datetime.datetime.now().year +corporation = f'FIT2CLOUD 飞致云 © 2014-{current_year}' XPACK_DIR = os.path.join(const.BASE_DIR, 'xpack') XPACK_ENABLED = os.path.isdir(XPACK_DIR) XPACK_TEMPLATES_DIR = [] XPACK_CONTEXT_PROCESSOR = [] +XPACK_LICENSE_IS_VALID = False +XPACK_LICENSE_INFO = { + 'corporation': corporation, +} if XPACK_ENABLED: from xpack.utils import get_xpack_templates_dir, get_xpack_context_processor + INSTALLED_APPS.insert(0, 'xpack.apps.XpackConfig') XPACK_TEMPLATES_DIR = get_xpack_templates_dir(const.BASE_DIR) XPACK_CONTEXT_PROCESSOR = get_xpack_context_processor() diff --git a/apps/jumpserver/utils.py b/apps/jumpserver/utils.py index 24f2b10d7..350c9b346 100644 --- a/apps/jumpserver/utils.py +++ b/apps/jumpserver/utils.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- # -from datetime import datetime from functools import partial -from django.conf import settings from werkzeug.local import LocalProxy from common.local import thread_local @@ -21,25 +19,4 @@ def get_current_request(): return _find('current_request') -def has_valid_xpack_license(): - if not settings.XPACK_ENABLED: - return False - from xpack.plugins.license.models import License - return License.has_valid_license() - - -def get_xpack_license_info() -> dict: - if has_valid_xpack_license(): - from xpack.plugins.license.models import License - info = License.get_license_detail() - corporation = info.get('corporation', '') - else: - current_year = datetime.now().year - corporation = f'FIT2CLOUD 飞致云 © 2014-{current_year}' - info = { - 'corporation': corporation - } - return info - - current_request = LocalProxy(partial(_find, 'current_request')) diff --git a/apps/settings/api/public.py b/apps/settings/api/public.py index 85c56bdbf..81abfeb99 100644 --- a/apps/settings/api/public.py +++ b/apps/settings/api/public.py @@ -5,7 +5,6 @@ from rest_framework.permissions import AllowAny from authentication.permissions import IsValidUserOrConnectionToken from common.utils import get_logger, lazyproperty from common.utils.timezone import local_now -from jumpserver.utils import has_valid_xpack_license, get_xpack_license_info from .. import serializers from ..utils import get_interface_setting_or_default @@ -36,8 +35,8 @@ class PublicSettingApi(OpenPublicSettingApi): def get_object(self): values = super().get_object() values.update({ - "XPACK_LICENSE_IS_VALID": has_valid_xpack_license(), - "XPACK_LICENSE_INFO": get_xpack_license_info(), + "XPACK_LICENSE_IS_VALID": settings.XPACK_LICENSE_IS_VALID, + "XPACK_LICENSE_INFO": settings.XPACK_LICENSE_INFO, "PASSWORD_RULE": { 'SECURITY_PASSWORD_MIN_LENGTH': settings.SECURITY_PASSWORD_MIN_LENGTH, 'SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH': settings.SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH, diff --git a/apps/terminal/api/applet/applet.py b/apps/terminal/api/applet/applet.py index 90b4f162c..9e656487b 100644 --- a/apps/terminal/api/applet/applet.py +++ b/apps/terminal/api/applet/applet.py @@ -61,7 +61,7 @@ class DownloadUploadMixin: update = request.query_params.get('update') is_enterprise = manifest.get('edition') == Applet.Edition.enterprise - if is_enterprise and not settings.XPACK_ENABLED: + if is_enterprise and not settings.XPACK_LICENSE_IS_VALID: raise ValidationError({'error': _('This is enterprise edition applet')}) instance = Applet.objects.filter(name=name).first() diff --git a/apps/terminal/connect_methods.py b/apps/terminal/connect_methods.py index 8672001c0..9c7ee989a 100644 --- a/apps/terminal/connect_methods.py +++ b/apps/terminal/connect_methods.py @@ -75,7 +75,7 @@ class NativeClient(TextChoices): xpack_protocols = Protocol.xpack_protocols() for protocol, _clients in clients_map.items(): - if not settings.XPACK_ENABLED and protocol in xpack_protocols: + if not settings.XPACK_LICENSE_IS_VALID and protocol in xpack_protocols: continue if isinstance(_clients, dict): if os == 'all': @@ -83,7 +83,7 @@ class NativeClient(TextChoices): else: _clients = _clients.get(os, _clients['default']) for client in _clients: - if not settings.XPACK_ENABLED and client in cls.xpack_methods(): + if not settings.XPACK_LICENSE_IS_VALID and client in cls.xpack_methods(): continue methods[protocol].append({ 'value': client.value, diff --git a/apps/users/views/profile/reset.py b/apps/users/views/profile/reset.py index d33681b1b..96d407302 100644 --- a/apps/users/views/profile/reset.py +++ b/apps/users/views/profile/reset.py @@ -90,7 +90,7 @@ class UserForgotPasswordView(FormView): @staticmethod def get_validate_backends_context(has_phone): validate_backends = [{'name': _('Email'), 'is_active': True, 'value': 'email'}] - if settings.XPACK_ENABLED: + if settings.XPACK_LICENSE_IS_VALID: if settings.SMS_ENABLED and has_phone: is_active = True else: From 71d70501d63f1168a518dbdafac864d7c51ef863 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 15:22:17 +0800 Subject: [PATCH 097/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E5=BF=85=E5=A1=AB=20(#11887)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ibuler --- apps/assets/serializers/asset/database.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/assets/serializers/asset/database.py b/apps/assets/serializers/asset/database.py index 633126995..ff3e7288a 100644 --- a/apps/assets/serializers/asset/database.py +++ b/apps/assets/serializers/asset/database.py @@ -55,6 +55,8 @@ class DatabaseSerializer(AssetSerializer): if platform.type in ['mysql', 'mariadb']: db_field.required = False + db_field.allow_blank = True + db_field.allow_null = True class DatabaseWithGatewaySerializer(DatabaseSerializer): From 9b019e45aee57c77f0f7d68f963d4e76a4554ba3 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 18 Oct 2023 15:40:52 +0800 Subject: [PATCH 098/114] =?UTF-8?q?perf:=20=E4=BF=AE=E5=A4=8D=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E4=B8=A2=E5=A4=B1=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/assets/serializers/asset/common.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index aae31c7ff..59799ba70 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -175,6 +175,8 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali protocols = self.initial_data.get('protocols') if protocols is not None: return + if getattr(self, 'instance', None): + return protocols_required, protocols_default = self._get_protocols_required_default() protocol_map = {str(protocol.id): protocol for protocol in protocols_required + protocols_default} From e0762573ae40309e1ec3d8d94062ef58376adc86 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 15:50:38 +0800 Subject: [PATCH 099/114] =?UTF-8?q?perf:=20=E5=9C=A8=E7=BA=BF=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=8A=A8=E6=80=81=E8=BF=87=E6=9C=9F=E6=97=B6=E9=97=B4?= =?UTF-8?q?=20(#11889)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/audits/api.py | 9 ++++----- apps/audits/models.py | 14 +++++++++++--- apps/audits/serializers.py | 1 + apps/audits/signal_handlers/login_log.py | 6 ------ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/apps/audits/api.py b/apps/audits/api.py index f332c5718..35315aa65 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -6,7 +6,6 @@ from importlib import import_module from django.conf import settings from django.db.models import F, Value, CharField, Q from django.http import HttpResponse, FileResponse -from django.utils import timezone from django.utils.encoding import escape_uri_path from rest_framework import generics from rest_framework import status @@ -269,9 +268,7 @@ class UserSessionViewSet(CommonApiMixin, viewsets.ModelViewSet): def get_queryset(self): keys = UserSession.get_keys() - queryset = UserSession.objects.filter( - date_expired__gt=timezone.now(), key__in=keys - ) + queryset = UserSession.objects.filter(key__in=keys) if current_org.is_root(): return queryset user_ids = self.org_user_ids @@ -281,7 +278,9 @@ class UserSessionViewSet(CommonApiMixin, viewsets.ModelViewSet): @action(['POST'], detail=False, url_path='offline') def offline(self, request, *args, **kwargs): ids = request.data.get('ids', []) - queryset = self.get_queryset().exclude(key=request.session.session_key).filter(id__in=ids) + queryset = self.get_queryset() + session_key = request.session.session_key + queryset = queryset.exclude(key=session_key).filter(id__in=ids) if not queryset.exists(): return Response(status=status.HTTP_200_OK) diff --git a/apps/audits/models.py b/apps/audits/models.py index ff58f962a..f7a669114 100644 --- a/apps/audits/models.py +++ b/apps/audits/models.py @@ -1,5 +1,6 @@ import os import uuid +from datetime import timedelta from importlib import import_module from django.conf import settings @@ -263,7 +264,6 @@ class UserSession(models.Model): type = models.CharField(choices=LoginTypeChoices.choices, max_length=2, verbose_name=_("Login type")) backend = models.CharField(max_length=32, default="", verbose_name=_("Authentication backend")) date_created = models.DateTimeField(null=True, blank=True, verbose_name=_('Date created')) - date_expired = models.DateTimeField(null=True, blank=True, verbose_name=_("Date expired"), db_index=True) user = models.ForeignKey( 'users.User', verbose_name=_('User'), related_name='sessions', on_delete=models.CASCADE ) @@ -275,6 +275,14 @@ class UserSession(models.Model): def backend_display(self): return gettext(self.backend) + @property + def date_expired(self): + session_store_cls = import_module(settings.SESSION_ENGINE).SessionStore + session_store = session_store_cls(session_key=self.key) + cache_key = session_store.cache_key + ttl = caches[settings.SESSION_CACHE_ALIAS].ttl(cache_key) + return timezone.now() + timedelta(seconds=ttl) + @staticmethod def get_keys(): session_store_cls = import_module(settings.SESSION_ENGINE).SessionStore @@ -284,8 +292,8 @@ class UserSession(models.Model): @classmethod def clear_expired_sessions(cls): - cls.objects.filter(date_expired__lt=timezone.now()).delete() - cls.objects.exclude(key__in=cls.get_keys()).delete() + keys = cls.get_keys() + cls.objects.exclude(key__in=keys).delete() class Meta: ordering = ['-date_created'] diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py index 829986297..472f6ae28 100644 --- a/apps/audits/serializers.py +++ b/apps/audits/serializers.py @@ -169,6 +169,7 @@ class FileSerializer(serializers.Serializer): class UserSessionSerializer(serializers.ModelSerializer): type = LabeledChoiceField(choices=LoginTypeChoices.choices, label=_("Type")) user = ObjectRelatedField(required=False, queryset=User.objects, label=_('User')) + date_expired = serializers.DateTimeField(format="%Y/%m/%d %H:%M:%S", label=_('Date expired')) is_current_user_session = serializers.SerializerMethodField() class Meta: diff --git a/apps/audits/signal_handlers/login_log.py b/apps/audits/signal_handlers/login_log.py index 2ad9c0a7a..2d8560412 100644 --- a/apps/audits/signal_handlers/login_log.py +++ b/apps/audits/signal_handlers/login_log.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # -from datetime import timedelta -from importlib import import_module from django.conf import settings from django.contrib.auth import BACKEND_SESSION_KEY @@ -88,9 +86,6 @@ def create_user_session(request, user_id, instance: UserLoginLog): if instance.type != LoginTypeChoices.web: return session_key = request.session.session_key or '-' - session_store_cls = import_module(settings.SESSION_ENGINE).SessionStore - session_store = session_store_cls(session_key=session_key) - ttl = session_store.get_expiry_age() online_session_data = { 'user_id': user_id, @@ -101,7 +96,6 @@ def create_user_session(request, user_id, instance: UserLoginLog): 'backend': instance.backend, 'user_agent': instance.user_agent, 'date_created': instance.datetime, - 'date_expired': instance.datetime + timedelta(seconds=ttl), } user_session = UserSession.objects.create(**online_session_data) request.session['user_session_id'] = str(user_session.id) From 78f1b2b002439f87be2c9fa1ceeee2847128fee5 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:04:02 +0800 Subject: [PATCH 100/114] =?UTF-8?q?perf:=20user=20session=20=E8=A1=A8?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E8=BF=87=E6=9C=9F=E6=97=B6=E9=97=B4=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=20(#11890)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- .../0025_remove_usersession_date_expired.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 apps/audits/migrations/0025_remove_usersession_date_expired.py diff --git a/apps/audits/migrations/0025_remove_usersession_date_expired.py b/apps/audits/migrations/0025_remove_usersession_date_expired.py new file mode 100644 index 000000000..0b495f72d --- /dev/null +++ b/apps/audits/migrations/0025_remove_usersession_date_expired.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.10 on 2023-10-18 08:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('audits', '0024_usersession'), + ] + + operations = [ + migrations.RemoveField( + model_name='usersession', + name='date_expired', + ), + ] From e713bdab0bbee3768d955c81f22bbf86ab8b934b Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 18 Oct 2023 16:09:17 +0800 Subject: [PATCH 101/114] =?UTF-8?q?perf:=20=E4=BC=81=E4=B8=9A=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E7=BB=91=E5=AE=9A=E5=90=8E=E9=80=80=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/dingtalk.py | 2 ++ apps/authentication/views/feishu.py | 2 ++ apps/authentication/views/wecom.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index fcff66527..536ef4155 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -1,6 +1,7 @@ from urllib.parse import urlencode from django.conf import settings +from django.contrib.auth import logout as auth_logout from django.db.utils import IntegrityError from django.http.request import HttpRequest from django.http.response import HttpResponseRedirect @@ -158,6 +159,7 @@ class DingTalkQRBindCallbackView(DingTalkQRMixin, View): ip = get_request_ip(request) OAuthBindMessage(user, ip, _('DingTalk'), user_id).publish_async() msg = _('Binding DingTalk successfully') + auth_logout(request) response = self.get_success_response(redirect_url, msg, msg) return response diff --git a/apps/authentication/views/feishu.py b/apps/authentication/views/feishu.py index 5be872987..4ceacc21b 100644 --- a/apps/authentication/views/feishu.py +++ b/apps/authentication/views/feishu.py @@ -1,6 +1,7 @@ from urllib.parse import urlencode from django.conf import settings +from django.contrib.auth import logout as auth_logout from django.db.utils import IntegrityError from django.http.request import HttpRequest from django.http.response import HttpResponseRedirect @@ -121,6 +122,7 @@ class FeiShuQRBindCallbackView(FeiShuQRMixin, View): ip = get_request_ip(request) OAuthBindMessage(user, ip, _('FeiShu'), user_id).publish_async() msg = _('Binding FeiShu successfully') + auth_logout(request) response = self.get_success_response(redirect_url, msg, msg) return response diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index 7bc814d3f..6817c84a0 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -1,6 +1,7 @@ from urllib.parse import urlencode from django.conf import settings +from django.contrib.auth import logout as auth_logout from django.db.utils import IntegrityError from django.http.request import HttpRequest from django.http.response import HttpResponseRedirect @@ -158,6 +159,7 @@ class WeComQRBindCallbackView(WeComQRMixin, View): ip = get_request_ip(request) OAuthBindMessage(user, ip, _('WeCom'), wecom_userid).publish_async() msg = _('Binding WeCom successfully') + auth_logout(request) response = self.get_success_response(redirect_url, msg, msg) return response From 3c3c112b073d1de9a9d7e91ff0fb64bb75592ff1 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 16:35:08 +0800 Subject: [PATCH 102/114] =?UTF-8?q?perf:=20=E6=9B=B4=E6=96=B0appletpublica?= =?UTF-8?q?tion=20applethostdeployment=20=E6=9D=83=E9=99=90=E4=BD=8D=20(#1?= =?UTF-8?q?1893)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/rbac/const.py | 4 ++-- apps/rbac/tree.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/rbac/const.py b/apps/rbac/const.py index 04bfb1617..01c8bd915 100644 --- a/apps/rbac/const.py +++ b/apps/rbac/const.py @@ -119,8 +119,6 @@ exclude_permissions = ( ('terminal', 'sessionsharing', 'view,add,change,delete', 'sessionsharing'), ('terminal', 'session', 'delete,share', 'session'), ('terminal', 'session', 'delete,change', 'command'), - ('terminal', 'appletpublication', '*', '*'), - ('terminal', 'applethostdeployment', '*', '*'), ('applications', '*', '*', '*'), ) @@ -152,6 +150,8 @@ only_system_permissions = ( ('orgs', 'organization', 'view', 'rootorg'), ('terminal', 'applet', '*', '*'), ('terminal', 'applethost', '*', '*'), + ('terminal', 'appletpublication', '*', '*'), + ('terminal', 'applethostdeployment', '*', '*'), ('acls', 'loginacl', '*', '*'), ('acls', 'connectmethodacl', '*', '*') ) diff --git a/apps/rbac/tree.py b/apps/rbac/tree.py index d608a869f..ef1571a1f 100644 --- a/apps/rbac/tree.py +++ b/apps/rbac/tree.py @@ -75,6 +75,8 @@ special_pid_mapper = { 'xpack.strategyrule': 'cloud_import', 'terminal.applet': 'remote_application', 'terminal.applethost': 'remote_application', + 'terminal.appletpublication': 'remote_application', + 'terminal.applethostdeployment': 'remote_application', 'accounts.accountbackupautomation': "backup_account_node", 'accounts.accountbackupexecution': "backup_account_node", "accounts.pushaccountautomation": "push_account_node", From 095c23ea4fec9be1a671d43024e8df25d07e3a78 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 18 Oct 2023 16:44:38 +0800 Subject: [PATCH 103/114] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9windows?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E8=B4=A6=E5=8F=B7=E7=9A=84=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/migrations/0125_auto_20231011_1053.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/assets/migrations/0125_auto_20231011_1053.py b/apps/assets/migrations/0125_auto_20231011_1053.py index 16313d0cb..11161c909 100644 --- a/apps/assets/migrations/0125_auto_20231011_1053.py +++ b/apps/assets/migrations/0125_auto_20231011_1053.py @@ -7,6 +7,7 @@ def change_windows_ping_method(apps, schema_editor): platform_automation_cls = apps.get_model('assets', 'PlatformAutomation') automations = platform_automation_cls.objects.filter(platform__name__in=['Windows', 'Windows2016']) automations.update(ping_method='ping_by_rdp') + automations.update(verify_account_method='verify_account_by_rdp') class Migration(migrations.Migration): From bdd885069fe1e772f741fee3841d4908ce5ce9eb Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 18 Oct 2023 17:09:14 +0800 Subject: [PATCH 104/114] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=97=B6=E5=88=9B=E5=BB=BA=20activity=20=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/audits/signal_handlers/activity_log.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/audits/signal_handlers/activity_log.py b/apps/audits/signal_handlers/activity_log.py index 0efc86386..0a525f038 100644 --- a/apps/audits/signal_handlers/activity_log.py +++ b/apps/audits/signal_handlers/activity_log.py @@ -114,6 +114,8 @@ def on_session_or_login_log_created(sender, instance=None, created=False, **kwar logger.error('Activity log handler not found: {}'.format(sender)) resource_ids, detail, act_type, org_id = func(instance) + if not resource_ids: + return return create_activities(resource_ids, detail, instance.id, act_type, org_id) From 00450121bc3a640402841c125eb182bd8d0ac544 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 18:46:49 +0800 Subject: [PATCH 105/114] =?UTF-8?q?perf:=20=E5=91=BD=E4=BB=A4=E7=BB=84?= =?UTF-8?q?=E5=8A=A0=E5=91=BD=E4=BB=A4=E8=BF=87=E6=BB=A4=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=20(#11898)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/acls/api/command_acl.py | 2 +- ...18_alter_commandfilteracl_command_groups.py | 18 ++++++++++++++++++ apps/acls/models/command_acl.py | 5 ++++- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 apps/acls/migrations/0018_alter_commandfilteracl_command_groups.py diff --git a/apps/acls/api/command_acl.py b/apps/acls/api/command_acl.py index 182b881eb..2043f274d 100644 --- a/apps/acls/api/command_acl.py +++ b/apps/acls/api/command_acl.py @@ -10,7 +10,7 @@ __all__ = ['CommandFilterACLViewSet', 'CommandGroupViewSet'] class CommandGroupViewSet(OrgBulkModelViewSet): model = models.CommandGroup - filterset_fields = ('name',) + filterset_fields = ('name', 'command_filters') search_fields = filterset_fields serializer_class = serializers.CommandGroupSerializer diff --git a/apps/acls/migrations/0018_alter_commandfilteracl_command_groups.py b/apps/acls/migrations/0018_alter_commandfilteracl_command_groups.py new file mode 100644 index 000000000..0dea952a8 --- /dev/null +++ b/apps/acls/migrations/0018_alter_commandfilteracl_command_groups.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.10 on 2023-10-18 10:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('acls', '0017_alter_connectmethodacl_options'), + ] + + operations = [ + migrations.AlterField( + model_name='commandfilteracl', + name='command_groups', + field=models.ManyToManyField(related_name='command_filters', to='acls.commandgroup', verbose_name='Command group'), + ), + ] diff --git a/apps/acls/models/command_acl.py b/apps/acls/models/command_acl.py index 4fbfd0829..2011cc60a 100644 --- a/apps/acls/models/command_acl.py +++ b/apps/acls/models/command_acl.py @@ -93,7 +93,10 @@ class CommandGroup(JMSOrgBaseModel): class CommandFilterACL(UserAssetAccountBaseACL): - command_groups = models.ManyToManyField(CommandGroup, verbose_name=_('Command group')) + command_groups = models.ManyToManyField( + CommandGroup, verbose_name=_('Command group'), + related_name='command_filters' + ) class Meta(UserAssetAccountBaseACL.Meta): abstract = False From a2fcc474369183bb7ada7c65c601571272657fc2 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 18 Oct 2023 18:51:28 +0800 Subject: [PATCH 106/114] =?UTF-8?q?fix:=20cas=20oidc=20=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=20(#11899)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/authentication/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/authentication/middleware.py b/apps/authentication/middleware.py index 9f245099d..9fce3dcb6 100644 --- a/apps/authentication/middleware.py +++ b/apps/authentication/middleware.py @@ -98,7 +98,7 @@ class ThirdPartyLoginMiddleware(mixins.AuthMixin): } response = render(request, 'authentication/auth_fail_flash_message_standalone.html', context) else: - if not self.request.session['auth_confirm_required']: + if not self.request.session.get('auth_confirm_required'): return response guard_url = reverse('authentication:login-guard') args = request.META.get('QUERY_STRING', '') From fef26c38fee01a600b1ccca0d04c9d7d4fb8edb0 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 18 Oct 2023 17:25:44 +0800 Subject: [PATCH 107/114] =?UTF-8?q?perf:=20=E5=8E=BB=E6=8E=89=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=AE=B0=E5=BD=95=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/audits/signal_handlers/activity_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/audits/signal_handlers/activity_log.py b/apps/audits/signal_handlers/activity_log.py index 0a525f038..4f564a3c4 100644 --- a/apps/audits/signal_handlers/activity_log.py +++ b/apps/audits/signal_handlers/activity_log.py @@ -69,7 +69,7 @@ class ActivityLogHandler: def create_activities(resource_ids, detail, detail_id, action, org_id): if not resource_ids: - raise ValueError('resource_ids is empty') + return if not org_id: org_id = Organization.ROOT_ID activities = [ From 25a19891577fac2d8848cd81439171034434688d Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 18 Oct 2023 19:13:35 +0800 Subject: [PATCH 108/114] =?UTF-8?q?perf:=20=E5=AE=8C=E5=96=84=E5=83=B5?= =?UTF-8?q?=E5=B0=B8=E4=BC=9A=E8=AF=9D=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/tasks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/terminal/tasks.py b/apps/terminal/tasks.py index 65fae3ded..38ca1941a 100644 --- a/apps/terminal/tasks.py +++ b/apps/terminal/tasks.py @@ -43,6 +43,7 @@ def delete_terminal_status_period(): @register_as_period_task(interval=600) @after_app_ready_start @after_app_shutdown_clean_periodic +@tmp_to_root_org() def clean_orphan_session(): active_sessions = Session.objects.filter(is_finished=False) for session in active_sessions: From 03d1a187dfaf196327c99227fa094f6a37439078 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Thu, 19 Oct 2023 11:21:08 +0800 Subject: [PATCH 109/114] =?UTF-8?q?perf:=20=E5=B7=A5=E5=8D=95=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=AE=A1=E6=89=B9=E8=80=85=E8=AE=BF=E9=97=AE=E9=93=BE?= =?UTF-8?q?=E6=8E=A5=E6=97=A0=E8=AE=A4=E8=AF=81=E8=B7=B3=E8=BD=AC=E5=88=B0?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=A1=B5=E9=9D=A2=20(#11902)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 工单直接审批者访问链接无认证跳转到登录页面 * perf: 修改重定向登录地址 * perf: 跳转字段标识更新 --- .../tickets/approve_check_password.html | 7 ----- apps/tickets/views/approve.py | 28 +++++++++---------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/apps/tickets/templates/tickets/approve_check_password.html b/apps/tickets/templates/tickets/approve_check_password.html index 9fd7e0862..fe256bc1c 100644 --- a/apps/tickets/templates/tickets/approve_check_password.html +++ b/apps/tickets/templates/tickets/approve_check_password.html @@ -39,7 +39,6 @@
{% csrf_token %}
diff --git a/apps/tickets/views/approve.py b/apps/tickets/views/approve.py index 8dcda595f..2f3e715b4 100644 --- a/apps/tickets/views/approve.py +++ b/apps/tickets/views/approve.py @@ -64,30 +64,28 @@ class TicketDirectApproveView(TemplateView): def get_context_data(self, **kwargs): # 放入工单信息 - token = kwargs.get('token') - content = cache.get(token, {}).get('content', []) - if self.request.user.is_authenticated: - prompt_msg = _('Click the button below to approve or reject') - else: - prompt_msg = _('After successful authentication, this ticket can be approved directly') kwargs.update({ - 'content': content, 'prompt_msg': prompt_msg, - 'login_url': '%s&next=%s' % ( - self.login_url, - reverse('tickets:direct-approve', kwargs={'token': token}) - ), + 'content': kwargs['ticket_info'].get('content', []), + 'prompt_msg': _('Click the button below to approve or reject'), }) return super().get_context_data(**kwargs) def get(self, request, *args, **kwargs): if not request.user.is_authenticated: - return HttpResponse(status=401) + direct_url = reverse('tickets:direct-approve', kwargs={'token': kwargs['token']}) + message_data = { + 'title': _('Ticket approval'), + 'message': _('After successful authentication, this ticket can be approved directly'), + 'redirect_url': f'{self.login_url}&{self.redirect_field_name}={direct_url}', + 'auto_redirect': True, + } + redirect_url = FlashMessageUtil.gen_message_url(message_data) + return redirect(redirect_url) - token = kwargs.get('token') - ticket_info = cache.get(token) + ticket_info = cache.get(kwargs['token']) if not ticket_info: return self.redirect_message_response(redirect_url=self.login_url) - return super().get(request, *args, **kwargs) + return super().get(request, ticket_info=ticket_info, *args, **kwargs) def post(self, request, **kwargs): user = request.user From 6870df6d75ac59c2d5ea61121b6802447e64ebc5 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 12:24:56 +0800 Subject: [PATCH 110/114] =?UTF-8?q?fix:=20cas=20ldap=20=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=20(#11908)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/authentication/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/authentication/middleware.py b/apps/authentication/middleware.py index 9fce3dcb6..10659a53a 100644 --- a/apps/authentication/middleware.py +++ b/apps/authentication/middleware.py @@ -107,7 +107,7 @@ class ThirdPartyLoginMiddleware(mixins.AuthMixin): response = redirect(guard_url) finally: if request.session.get('can_send_notifications') and \ - self.request.session['auth_notice_required']: + self.request.session.get('auth_notice_required'): request.session['can_send_notifications'] = False user_log_id = self.request.session.get('user_log_id') auth_acl_id = self.request.session.get('auth_acl_id') From 26a18a1f5cb24a9764492730e2ee88ed8a544a87 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:44:24 +0800 Subject: [PATCH 111/114] =?UTF-8?q?perf:=20=E6=89=B9=E9=87=8F=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=B5=84=E4=BA=A7=20=E8=B4=A6=E5=8F=B7=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E9=94=99=E8=AF=AF=E6=8F=90=E9=86=92=20(#11909)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/assets/serializers/asset/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 59799ba70..75d8c4c19 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -304,6 +304,10 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali from accounts.models import AccountTemplate if not accounts_data: return + + if not isinstance(accounts_data[0], dict): + raise serializers.ValidationError({'accounts': _("Invalid data")}) + su_from_name_username_secret_type_map = {} for data in accounts_data: data['asset'] = asset.id From 4aa4c6854b8f4bcf40c7eb8cac0c8c1970002686 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:15:02 +0800 Subject: [PATCH 112/114] =?UTF-8?q?perf:=20=E6=9B=B4=E6=96=B0ops=20ticket?= =?UTF-8?q?=20announcement=20settings=20=E6=9D=83=E9=99=90=20(#11911)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/settings/api/settings.py | 3 +++ .../migrations/0010_alter_setting_options.py | 17 +++++++++++++++++ apps/settings/models.py | 3 +++ 3 files changed, 23 insertions(+) create mode 100644 apps/settings/migrations/0010_alter_setting_options.py diff --git a/apps/settings/api/settings.py b/apps/settings/api/settings.py index e4af8e147..6b5f096cf 100644 --- a/apps/settings/api/settings.py +++ b/apps/settings/api/settings.py @@ -64,6 +64,9 @@ class SettingsApi(generics.RetrieveUpdateAPIView): rbac_category_permissions = { 'basic': 'settings.view_setting', 'terminal': 'settings.change_terminal', + 'ops': 'settings.change_ops', + 'ticket': 'settings.change_ticket', + 'announcement': 'settings.change_announcement', 'security': 'settings.change_security', 'security_basic': 'settings.change_security', 'security_auth': 'settings.change_security', diff --git a/apps/settings/migrations/0010_alter_setting_options.py b/apps/settings/migrations/0010_alter_setting_options.py new file mode 100644 index 000000000..3d52a64da --- /dev/null +++ b/apps/settings/migrations/0010_alter_setting_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.10 on 2023-10-19 07:08 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('settings', '0009_alter_cas_username_attribute'), + ] + + operations = [ + migrations.AlterModelOptions( + name='setting', + options={'permissions': [('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_ops', 'Can change auth ops'), ('change_ticket', 'Can change auth ticket'), ('change_announcement', 'Can change auth announcement'), ('change_vault', 'Can change vault setting'), ('change_systemmsgsubscription', 'Can change system msg sub setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_interface', 'Can change interface setting'), ('change_license', 'Can change license setting'), ('change_terminal', 'Can change terminal setting'), ('change_other', 'Can change other setting')], 'verbose_name': 'System setting'}, + ), + ] diff --git a/apps/settings/models.py b/apps/settings/models.py index 6aebf79b1..dc599bbe4 100644 --- a/apps/settings/models.py +++ b/apps/settings/models.py @@ -159,6 +159,9 @@ class Setting(models.Model): permissions = [ ('change_email', _('Can change email setting')), ('change_auth', _('Can change auth setting')), + ('change_ops', _('Can change auth ops')), + ('change_ticket', _('Can change auth ticket')), + ('change_announcement', _('Can change auth announcement')), ('change_vault', _('Can change vault setting')), ('change_systemmsgsubscription', _('Can change system msg sub setting')), ('change_sms', _('Can change sms setting')), From db2331521d09bc57a8481ac21cbb0d7d71b21583 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:56:14 +0800 Subject: [PATCH 113/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E5=A4=8D=E5=90=88=E9=80=9A=E7=9F=A5=E6=97=A0=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E4=BF=A1=E6=81=AF=20(#11912)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/authentication/api/connection_token.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index bd2c01ef1..e41b47f76 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -330,13 +330,28 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView connect_options[name] = value data['connect_options'] = connect_options + @staticmethod + def get_input_username(data): + input_username = data.get('input_username', '') + if input_username: + return input_username + + account = data.get('account', '') + if account == '@USER': + input_username = str(data.get('user', '')) + elif account == '@INPUT': + input_username = '@INPUT' + else: + input_username = account + return input_username + def validate_serializer(self, serializer): data = serializer.validated_data user = self.get_user(serializer) self._insert_connect_options(data, user) asset = data.get('asset') account_name = data.get('account') - self.input_username = data.get('input_username', '') + self.input_username = self.get_input_username(data) _data = self._validate(user, asset, account_name) data.update(_data) return serializer From db9ee71ab383cd9289c0f381673de30aeeec2dcf Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:24:25 +0800 Subject: [PATCH 114/114] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91=20(#11913)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 474 +++++++++--------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 474 +++++++++--------- apps/tickets/models/ticket/command_confirm.py | 3 + .../models/ticket/login_asset_confirm.py | 3 + apps/tickets/models/ticket/login_confirm.py | 3 + 7 files changed, 505 insertions(+), 460 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 50a9d1152..65199d7a6 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7366743ab156daec72625cd241f76420c551c6a48330266a57e3d0b808d4164 -size 162675 +oid sha256:67b3061a082605d0862007d0deb03dba0477b3eac85d99faf5755ee44e7453eb +size 163187 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index f3fa1ddb4..7eff444d0 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: 2023-10-13 15:03+0800\n" +"POT-Creation-Date: 2023-10-19 16:21+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,13 +18,13 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: accounts/api/automations/base.py:79 tickets/api/ticket.py:112 +#: accounts/api/automations/base.py:79 tickets/api/ticket.py:132 msgid "The parameter 'action' must be [{}]" msgstr "パラメータ 'action' は [{}] でなければなりません。" #: accounts/const/account.py:6 #: accounts/serializers/automations/change_secret.py:32 -#: assets/models/_user.py:24 audits/signal_handlers/login_log.py:37 +#: assets/models/_user.py:24 audits/signal_handlers/login_log.py:34 #: authentication/confirm/password.py:9 authentication/confirm/password.py:24 #: authentication/confirm/password.py:26 authentication/forms.py:32 #: authentication/templates/authentication/login.html:324 @@ -89,13 +89,13 @@ msgstr "テンプレート" msgid "Skip" msgstr "スキップ" -#: accounts/const/account.py:32 audits/const.py:24 rbac/tree.py:234 +#: accounts/const/account.py:32 audits/const.py:24 rbac/tree.py:236 #: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6 msgid "Update" msgstr "更新" #: accounts/const/account.py:33 -#: accounts/serializers/automations/change_secret.py:155 audits/const.py:60 +#: accounts/serializers/automations/change_secret.py:155 audits/const.py:62 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 #: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 msgid "Failed" @@ -218,15 +218,15 @@ msgstr "ユーザー %s がパスワードを閲覧/導き出しました" #: accounts/models/account.py:48 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:209 -#: accounts/serializers/account/account.py:254 +#: accounts/serializers/account/account.py:210 +#: accounts/serializers/account/account.py:255 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:111 #: accounts/serializers/automations/change_secret.py:131 #: acls/serializers/base.py:123 assets/models/asset/common.py:93 #: assets/models/asset/common.py:334 assets/models/cmd_filter.py:36 #: assets/serializers/domain.py:19 assets/serializers/label.py:27 -#: audits/models.py:56 authentication/models/connection_token.py:36 +#: audits/models.py:57 authentication/models/connection_token.py:36 #: perms/models/asset_permission.py:64 perms/serializers/permission.py:34 #: terminal/backends/command/models.py:17 terminal/models/session/session.py:31 #: terminal/notifications.py:155 terminal/serializers/command.py:17 @@ -238,8 +238,8 @@ msgid "Asset" msgstr "資産" #: accounts/models/account.py:52 accounts/models/template.py:15 -#: accounts/serializers/account/account.py:216 -#: accounts/serializers/account/account.py:264 +#: accounts/serializers/account/account.py:217 +#: accounts/serializers/account/account.py:265 #: accounts/serializers/account/template.py:24 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" @@ -251,7 +251,7 @@ msgstr "から切り替え" msgid "Version" msgstr "バージョン" -#: accounts/models/account.py:56 accounts/serializers/account/account.py:211 +#: accounts/models/account.py:56 accounts/serializers/account/account.py:212 #: users/models/user.py:837 msgid "Source" msgstr "ソース" @@ -265,7 +265,7 @@ msgstr "ソース ID" #: accounts/serializers/automations/change_secret.py:132 #: acls/serializers/base.py:124 acls/templates/acls/asset_login_reminder.html:7 #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 -#: audits/models.py:57 authentication/api/connection_token.py:391 +#: audits/models.py:58 authentication/api/connection_token.py:406 #: ops/models/base.py:18 perms/models/asset_permission.py:70 #: perms/serializers/permission.py:39 terminal/backends/command/models.py:18 #: terminal/models/session/session.py:33 @@ -309,7 +309,7 @@ msgid "Account backup plan" msgstr "アカウントバックアップ計画" #: accounts/models/automations/backup_account.py:91 -#: assets/models/automations/base.py:115 audits/models.py:63 +#: assets/models/automations/base.py:115 audits/models.py:64 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:228 #: ops/templates/ops/celery_task_log.html:75 #: perms/models/asset_permission.py:72 terminal/models/applet/host.py:140 @@ -337,7 +337,7 @@ msgstr "アカウントのバックアップスナップショット" msgid "Trigger mode" msgstr "トリガーモード" -#: accounts/models/automations/backup_account.py:105 audits/models.py:201 +#: accounts/models/automations/backup_account.py:105 audits/models.py:202 #: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "理由" @@ -423,7 +423,7 @@ msgid "Date finished" msgstr "終了日" #: accounts/models/automations/change_secret.py:44 -#: accounts/serializers/account/account.py:256 assets/const/automation.py:8 +#: accounts/serializers/account/account.py:257 assets/const/automation.py:8 #: authentication/templates/authentication/passkey.html:173 #: authentication/views/base.py:27 authentication/views/base.py:28 #: authentication/views/base.py:29 common/const/choices.py:20 @@ -447,7 +447,7 @@ msgstr "最終ログイン日" #: accounts/serializers/account/virtual.py:21 acls/serializers/base.py:19 #: acls/serializers/base.py:50 acls/templates/acls/asset_login_reminder.html:5 #: acls/templates/acls/user_login_reminder.html:5 assets/models/_user.py:23 -#: audits/models.py:186 authentication/forms.py:25 authentication/forms.py:27 +#: audits/models.py:187 authentication/forms.py:25 authentication/forms.py:27 #: authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 @@ -480,7 +480,7 @@ msgstr "トリガー方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:41 #: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 -#: audits/models.py:90 audits/serializers.py:84 +#: audits/models.py:91 audits/serializers.py:84 #: authentication/serializers/connect_token_secret.py:116 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" @@ -495,7 +495,7 @@ msgid "Verify asset account" msgstr "アカウントの確認" #: accounts/models/base.py:37 accounts/models/base.py:67 -#: accounts/serializers/account/account.py:436 +#: accounts/serializers/account/account.py:437 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:45 #: authentication/serializers/connect_token_secret.py:41 @@ -539,10 +539,11 @@ msgstr "パスワードルール" #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:94 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 -#: terminal/models/component/terminal.py:84 users/forms/profile.py:33 -#: users/models/group.py:13 users/models/preference.py:11 -#: users/models/user.py:792 xpack/plugins/cloud/models.py:32 -#: xpack/plugins/cloud/models.py:273 xpack/plugins/cloud/serializers/task.py:68 +#: terminal/models/component/terminal.py:84 tickets/api/ticket.py:87 +#: users/forms/profile.py:33 users/models/group.py:13 +#: users/models/preference.py:11 users/models/user.py:792 +#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 +#: xpack/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名前" @@ -661,7 +662,7 @@ msgstr "今すぐプッシュ" msgid "Exist policy" msgstr "アカウントの存在ポリシー" -#: accounts/serializers/account/account.py:189 applications/models.py:11 +#: accounts/serializers/account/account.py:190 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:89 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 #: assets/serializers/platform.py:133 assets/serializers/platform.py:229 @@ -670,9 +671,9 @@ msgstr "アカウントの存在ポリシー" msgid "Category" msgstr "カテゴリ" -#: accounts/serializers/account/account.py:190 +#: accounts/serializers/account/account.py:191 #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 -#: acls/serializers/command_acl.py:18 applications/models.py:14 +#: acls/serializers/command_acl.py:19 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 #: assets/models/cmd_filter.py:74 assets/models/platform.py:90 #: assets/serializers/asset/common.py:122 assets/serializers/platform.py:113 @@ -690,59 +691,59 @@ msgstr "カテゴリ" msgid "Type" msgstr "タイプ" -#: accounts/serializers/account/account.py:205 +#: accounts/serializers/account/account.py:206 msgid "Asset not found" msgstr "資産が存在しません" -#: accounts/serializers/account/account.py:245 +#: accounts/serializers/account/account.py:246 msgid "Has secret" msgstr "エスクローされたパスワード" -#: accounts/serializers/account/account.py:255 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:256 ops/models/celery.py:60 #: tickets/models/comment.py:13 tickets/models/ticket/general.py:45 #: tickets/models/ticket/general.py:279 tickets/serializers/super_ticket.py:14 #: tickets/serializers/ticket/ticket.py:21 msgid "State" msgstr "状態" -#: accounts/serializers/account/account.py:257 +#: accounts/serializers/account/account.py:258 msgid "Changed" msgstr "編集済み" -#: accounts/serializers/account/account.py:267 +#: accounts/serializers/account/account.py:268 #: accounts/serializers/automations/base.py:22 acls/models/base.py:97 #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 -#: authentication/api/connection_token.py:390 ops/models/base.py:17 +#: authentication/api/connection_token.py:405 ops/models/base.py:17 #: ops/models/job.py:139 ops/serializers/job.py:21 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" msgstr "資産" -#: accounts/serializers/account/account.py:322 +#: accounts/serializers/account/account.py:323 msgid "Account already exists" msgstr "アカウントはすでに存在しています" -#: accounts/serializers/account/account.py:372 +#: accounts/serializers/account/account.py:373 #, python-format msgid "Asset does not support this secret type: %s" msgstr "アセットはアカウント タイプをサポートしていません: %s" -#: accounts/serializers/account/account.py:404 +#: accounts/serializers/account/account.py:405 msgid "Account has exist" msgstr "アカウントはすでに存在しています" -#: accounts/serializers/account/account.py:437 +#: accounts/serializers/account/account.py:438 #: authentication/serializers/connect_token_secret.py:156 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:31 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:447 acls/serializers/base.py:116 -#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:52 -#: audits/models.py:88 audits/models.py:170 audits/models.py:268 +#: accounts/serializers/account/account.py:448 acls/serializers/base.py:116 +#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:53 +#: audits/models.py:89 audits/models.py:171 audits/models.py:268 #: audits/serializers.py:171 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 @@ -759,7 +760,7 @@ msgstr "ID" msgid "User" msgstr "ユーザー" -#: accounts/serializers/account/account.py:448 +#: accounts/serializers/account/account.py:449 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:158 terminal/notifications.py:207 msgid "Date" @@ -791,7 +792,7 @@ msgid "Key password" msgstr "キーパスワード" #: accounts/serializers/account/base.py:78 -#: assets/serializers/asset/common.py:338 +#: assets/serializers/asset/common.py:378 msgid "Spec info" msgstr "特別情報" @@ -842,7 +843,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 #: ops/models/job.py:145 ops/models/playbook.py:31 rbac/models/role.py:37 #: settings/models.py:37 terminal/models/applet/applet.py:45 -#: terminal/models/applet/applet.py:302 terminal/models/applet/host.py:142 +#: terminal/models/applet/applet.py:304 terminal/models/applet/host.py:142 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 @@ -896,11 +897,11 @@ msgstr "* パスワードの長さの範囲6-30ビット" msgid "Automation task execution" msgstr "自動タスク実行履歴" -#: accounts/serializers/automations/change_secret.py:154 audits/const.py:59 -#: audits/models.py:62 audits/signal_handlers/activity_log.py:33 +#: accounts/serializers/automations/change_secret.py:154 audits/const.py:61 +#: audits/models.py:63 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: terminal/const.py:76 terminal/models/session/sharing.py:121 -#: tickets/views/approve.py:119 +#: tickets/views/approve.py:117 msgid "Success" msgstr "成功" @@ -975,16 +976,16 @@ msgstr "秘密鍵が無効またはpassphraseエラー" msgid "Acls" msgstr "Acls" -#: acls/const.py:6 audits/const.py:33 terminal/const.py:11 tickets/const.py:45 -#: tickets/templates/tickets/approve_check_password.html:49 +#: acls/const.py:6 audits/const.py:36 terminal/const.py:11 tickets/const.py:45 +#: tickets/templates/tickets/approve_check_password.html:48 msgid "Reject" msgstr "拒否" -#: acls/const.py:7 audits/const.py:34 terminal/const.py:9 +#: acls/const.py:7 audits/const.py:33 terminal/const.py:9 msgid "Accept" msgstr "受け入れられる" -#: acls/const.py:8 audits/const.py:35 +#: acls/const.py:8 audits/const.py:34 msgid "Review" msgstr "レビュー担当者" @@ -992,7 +993,7 @@ msgstr "レビュー担当者" msgid "Warning" msgstr "警告" -#: acls/const.py:10 audits/const.py:36 notifications/apps.py:7 +#: acls/const.py:10 audits/const.py:35 notifications/apps.py:7 msgid "Notifications" msgstr "通知" @@ -1026,7 +1027,7 @@ msgid "Users" msgstr "ユーザー" #: acls/models/base.py:98 assets/models/automations/base.py:17 -#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:337 +#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:377 #: rbac/tree.py:35 msgid "Accounts" msgstr "アカウント" @@ -1058,8 +1059,8 @@ msgstr "1行1コマンド" msgid "Ignore case" msgstr "家を無視する" -#: acls/models/command_acl.py:33 acls/models/command_acl.py:96 -#: acls/serializers/command_acl.py:28 +#: acls/models/command_acl.py:33 acls/models/command_acl.py:97 +#: acls/serializers/command_acl.py:29 #: authentication/serializers/connect_token_secret.py:85 #: terminal/templates/terminal/_msg_command_warning.html:14 msgid "Command group" @@ -1069,12 +1070,12 @@ msgstr "コマンドグループ" msgid "The generated regular expression is incorrect: {}" msgstr "生成された正規表現が正しくありません: {}" -#: acls/models/command_acl.py:100 +#: acls/models/command_acl.py:103 #: terminal/templates/terminal/_msg_command_warning.html:12 msgid "Command acl" msgstr "コマンドフィルタリング" -#: acls/models/command_acl.py:109 tickets/const.py:11 +#: acls/models/command_acl.py:112 tickets/const.py:11 msgid "Command confirm" msgstr "コマンドの確認" @@ -1189,15 +1190,15 @@ msgstr "" msgid "Thank you" msgstr "ありがとうございます。" -#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:192 -#: audits/models.py:261 +#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:193 +#: audits/models.py:262 #: authentication/templates/authentication/_msg_different_city.html:11 #: tickets/models/ticket/login_confirm.py:11 msgid "Login city" msgstr "ログイン都市" -#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:195 -#: audits/models.py:262 audits/serializers.py:65 +#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:196 +#: audits/models.py:263 audits/serializers.py:65 msgid "User agent" msgstr "ユーザーエージェント" @@ -1250,7 +1251,7 @@ msgstr "ルートノード ({}) を削除できません。" msgid "Deletion failed and the node contains assets" msgstr "削除に失敗し、ノードにアセットが含まれています。" -#: assets/api/tree.py:49 assets/serializers/node.py:41 +#: assets/api/tree.py:49 assets/serializers/node.py:42 msgid "The same level node name cannot be the same" msgstr "同じレベルのノード名を同じにすることはできません。" @@ -1281,7 +1282,7 @@ msgid "Unable to connect to port {port} on {address}" msgstr "{port} のポート {address} に接続できません" #: assets/automations/ping_gateway/manager.py:58 -#: authentication/middleware.py:92 xpack/plugins/cloud/providers/fc.py:47 +#: authentication/middleware.py:93 xpack/plugins/cloud/providers/fc.py:47 msgid "Authentication failed" msgstr "認証に失敗しました" @@ -1290,7 +1291,7 @@ msgstr "認証に失敗しました" msgid "Connect failed" msgstr "接続に失敗しました" -#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:42 +#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:44 #: audits/signal_handlers/activity_log.py:62 common/utils/ip/geoip/utils.py:31 #: common/utils/ip/geoip/utils.py:37 common/utils/ip/utils.py:104 msgid "Unknown" @@ -1312,19 +1313,19 @@ msgstr "テストゲートウェイ" msgid "Gather facts" msgstr "資産情報の収集" -#: assets/const/base.py:33 audits/const.py:53 +#: assets/const/base.py:32 audits/const.py:55 #: terminal/serializers/applet_host.py:32 msgid "Disabled" msgstr "無効" -#: assets/const/base.py:34 settings/serializers/basic.py:6 +#: assets/const/base.py:33 settings/serializers/basic.py:6 #: users/serializers/preference/koko.py:19 #: users/serializers/preference/lina.py:39 #: users/serializers/preference/luna.py:60 msgid "Basic" msgstr "基本" -#: assets/const/base.py:35 assets/const/protocol.py:252 +#: assets/const/base.py:34 assets/const/protocol.py:252 #: assets/models/asset/web.py:13 msgid "Script" msgstr "脚本" @@ -1346,7 +1347,7 @@ msgid "Cloud service" msgstr "クラウド サービス" #: assets/const/category.py:14 assets/models/asset/gpt.py:11 -#: assets/models/asset/web.py:16 audits/const.py:40 +#: assets/models/asset/web.py:16 audits/const.py:42 #: terminal/models/applet/applet.py:27 users/const.py:47 msgid "Web" msgstr "Web" @@ -1532,7 +1533,7 @@ msgstr "SSHパブリックキー" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 -#: audits/models.py:265 common/db/models.py:34 ops/models/base.py:54 +#: audits/models.py:266 common/db/models.py:34 ops/models/base.py:54 #: ops/models/job.py:227 users/models/user.py:1024 msgid "Date created" msgstr "作成された日付" @@ -1647,7 +1648,7 @@ msgstr "ドメイン" msgid "Labels" msgstr "ラベル" -#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:339 +#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:379 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "資産ハードウェア情報の収集" @@ -1709,9 +1710,9 @@ msgstr "自動化されたタスク" msgid "Asset automation task" msgstr "アセットの自動化タスク" -#: assets/models/automations/base.py:113 audits/models.py:206 +#: assets/models/automations/base.py:113 audits/models.py:207 #: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220 -#: terminal/models/applet/applet.py:301 terminal/models/applet/host.py:139 +#: terminal/models/applet/applet.py:303 terminal/models/applet/host.py:139 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:115 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 @@ -1857,7 +1858,7 @@ msgstr "開ける" msgid "Setting" msgstr "設定" -#: assets/models/platform.py:38 audits/const.py:54 +#: assets/models/platform.py:38 audits/const.py:56 #: authentication/backends/passkey/models.py:11 settings/models.py:36 #: terminal/serializers/applet_host.py:33 msgid "Enabled" @@ -1984,22 +1985,26 @@ msgid "Node path" msgstr "ノードパスです" #: assets/serializers/asset/common.py:145 -#: assets/serializers/asset/common.py:340 +#: assets/serializers/asset/common.py:380 msgid "Auto info" msgstr "自動情報" -#: assets/serializers/asset/common.py:236 +#: assets/serializers/asset/common.py:238 msgid "Platform not exist" msgstr "プラットフォームが存在しません" -#: assets/serializers/asset/common.py:272 +#: assets/serializers/asset/common.py:274 msgid "port out of range (0-65535)" msgstr "ポート番号が範囲外です (0-65535)" -#: assets/serializers/asset/common.py:279 +#: assets/serializers/asset/common.py:281 msgid "Protocol is required: {}" msgstr "プロトコルが必要です: {}" +#: assets/serializers/asset/common.py:309 +msgid "Invalid data" +msgstr "無効なデータ" + #: assets/serializers/asset/database.py:12 msgid "Default database" msgstr "デフォルト・データベース" @@ -2245,7 +2250,7 @@ msgstr "Rmdir" #: audits/const.py:14 audits/const.py:25 #: authentication/templates/authentication/_access_key_modal.html:65 -#: perms/const.py:17 rbac/tree.py:235 +#: perms/const.py:17 rbac/tree.py:237 msgid "Delete" msgstr "削除" @@ -2270,7 +2275,7 @@ msgstr "ダウンロード" msgid "Rename dir" msgstr "マップディレクトリ" -#: audits/const.py:23 rbac/tree.py:233 terminal/api/session/session.py:257 +#: audits/const.py:23 rbac/tree.py:235 terminal/api/session/session.py:257 #: terminal/templates/terminal/_msg_command_warning.html:18 #: terminal/templates/terminal/_msg_session_sharing.html:10 msgid "View" @@ -2278,7 +2283,7 @@ msgstr "表示" #: audits/const.py:26 #: authentication/templates/authentication/_access_key_modal.html:22 -#: rbac/tree.py:232 +#: rbac/tree.py:234 msgid "Create" msgstr "作成" @@ -2296,30 +2301,41 @@ msgstr "ログイン" msgid "Change password" msgstr "パスワードを変更する" -#: audits/const.py:41 settings/serializers/terminal.py:6 +#: audits/const.py:37 tickets/const.py:46 +msgid "Approve" +msgstr "承認" + +#: audits/const.py:38 +#: authentication/templates/authentication/_access_key_modal.html:155 +#: authentication/templates/authentication/_mfa_confirm_modal.html:53 +#: templates/_modal.html:22 tickets/const.py:44 +msgid "Close" +msgstr "閉じる" + +#: audits/const.py:43 settings/serializers/terminal.py:6 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 #: terminal/serializers/session.py:52 terminal/serializers/session.py:66 msgid "Terminal" msgstr "ターミナル" -#: audits/const.py:46 audits/models.py:130 +#: audits/const.py:48 audits/models.py:131 msgid "Operate log" msgstr "ログの操作" -#: audits/const.py:47 +#: audits/const.py:49 msgid "Session log" msgstr "セッションログ" -#: audits/const.py:48 +#: audits/const.py:50 msgid "Login log" msgstr "ログインログ" -#: audits/const.py:49 terminal/models/applet/host.py:143 +#: audits/const.py:51 terminal/models/applet/host.py:143 #: terminal/models/component/task.py:22 msgid "Task" msgstr "タスク" -#: audits/const.py:55 +#: audits/const.py:57 msgid "-" msgstr "-" @@ -2331,28 +2347,28 @@ msgstr "是" msgid "No" msgstr "否" -#: audits/models.py:45 +#: audits/models.py:46 msgid "Job audit log" msgstr "ジョブ監査ログ" -#: audits/models.py:54 audits/models.py:98 audits/models.py:173 +#: audits/models.py:55 audits/models.py:99 audits/models.py:174 #: terminal/models/session/session.py:38 terminal/models/session/sharing.py:113 msgid "Remote addr" msgstr "リモートaddr" -#: audits/models.py:59 audits/serializers.py:35 +#: audits/models.py:60 audits/serializers.py:35 msgid "Operate" msgstr "操作" -#: audits/models.py:61 +#: audits/models.py:62 msgid "Filename" msgstr "ファイル名" -#: audits/models.py:64 common/serializers/common.py:98 +#: audits/models.py:65 common/serializers/common.py:98 msgid "File" msgstr "書類" -#: audits/models.py:65 terminal/backends/command/models.py:21 +#: audits/models.py:66 terminal/backends/command/models.py:21 #: terminal/models/session/replay.py:9 terminal/models/session/sharing.py:20 #: terminal/models/session/sharing.py:95 #: terminal/templates/terminal/_msg_command_alert.html:10 @@ -2361,93 +2377,86 @@ msgstr "書類" msgid "Session" msgstr "セッション" -#: audits/models.py:68 +#: audits/models.py:69 msgid "File transfer log" msgstr "ファイル転送ログ" -#: audits/models.py:92 audits/serializers.py:86 +#: audits/models.py:93 audits/serializers.py:86 msgid "Resource Type" msgstr "リソースタイプ" -#: audits/models.py:93 audits/models.py:96 audits/models.py:142 +#: audits/models.py:94 audits/models.py:97 audits/models.py:143 #: audits/serializers.py:85 msgid "Resource" msgstr "リソース" -#: audits/models.py:99 audits/models.py:145 audits/models.py:175 +#: audits/models.py:100 audits/models.py:146 audits/models.py:176 #: terminal/serializers/command.py:75 msgid "Datetime" msgstr "時間" -#: audits/models.py:138 +#: audits/models.py:139 msgid "Activity type" msgstr "活動の種類" -#: audits/models.py:148 +#: audits/models.py:149 msgid "Detail" msgstr "詳細" -#: audits/models.py:151 +#: audits/models.py:152 msgid "Detail ID" msgstr "詳細 ID" -#: audits/models.py:155 +#: audits/models.py:156 msgid "Activity log" msgstr "活動記録" -#: audits/models.py:171 +#: audits/models.py:172 msgid "Change by" msgstr "による変更" -#: audits/models.py:181 +#: audits/models.py:182 msgid "Password change log" msgstr "パスワード変更ログ" -#: audits/models.py:188 audits/models.py:263 +#: audits/models.py:189 audits/models.py:264 msgid "Login type" msgstr "ログインタイプ" -#: audits/models.py:190 audits/models.py:259 +#: audits/models.py:191 audits/models.py:260 #: tickets/models/ticket/login_confirm.py:10 msgid "Login IP" msgstr "ログインIP" -#: audits/models.py:198 audits/serializers.py:49 +#: audits/models.py:199 audits/serializers.py:49 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: users/forms/profile.py:65 users/models/user.py:815 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" -#: audits/models.py:208 +#: audits/models.py:209 msgid "Date login" msgstr "日付ログイン" -#: audits/models.py:210 audits/models.py:264 audits/serializers.py:67 -#: audits/serializers.py:183 +#: audits/models.py:211 audits/models.py:265 audits/serializers.py:67 +#: audits/serializers.py:184 msgid "Authentication backend" msgstr "認証バックエンド" -#: audits/models.py:254 +#: audits/models.py:255 msgid "User login log" msgstr "ユーザーログインログ" -#: audits/models.py:260 +#: audits/models.py:261 msgid "Session key" msgstr "セッションID" -#: audits/models.py:266 authentication/models/connection_token.py:47 -#: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 -#: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:833 -msgid "Date expired" -msgstr "期限切れの日付" - -#: audits/models.py:292 +#: audits/models.py:300 msgid "User session" msgstr "ユーザーセッション" -#: audits/models.py:294 +#: audits/models.py:302 msgid "Offline ussr session" msgstr "ユーザー・セッションの下限" @@ -2460,6 +2469,13 @@ msgstr "理由表示" msgid "User %s %s this resource" msgstr "ユーザー %s %s が現在のリソースをサブスクライブしました。" +#: audits/serializers.py:172 authentication/models/connection_token.py:47 +#: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 +#: tickets/models/ticket/apply_application.py:31 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:833 +msgid "Date expired" +msgstr "期限切れの日付" + #: audits/signal_handlers/activity_log.py:26 #, python-format msgid "User %s use account %s login asset %s" @@ -2475,26 +2491,26 @@ msgstr "ユーザー %s はシステム %s にログインしています" msgid "User %s perform a task for this resource: %s" msgstr "ユーザー %s が現在のリソースでタスク (%s) を実行しました" -#: audits/signal_handlers/login_log.py:36 +#: audits/signal_handlers/login_log.py:33 msgid "SSH Key" msgstr "SSHキー" -#: audits/signal_handlers/login_log.py:38 settings/serializers/auth/sso.py:13 +#: audits/signal_handlers/login_log.py:35 settings/serializers/auth/sso.py:13 msgid "SSO" msgstr "SSO" -#: audits/signal_handlers/login_log.py:39 +#: audits/signal_handlers/login_log.py:36 msgid "Auth Token" msgstr "認証トークン" -#: audits/signal_handlers/login_log.py:40 authentication/notifications.py:73 -#: authentication/views/login.py:77 authentication/views/wecom.py:159 +#: audits/signal_handlers/login_log.py:37 authentication/notifications.py:73 +#: authentication/views/login.py:77 authentication/views/wecom.py:160 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 #: users/models/user.py:745 users/models/user.py:847 msgid "WeCom" msgstr "企業微信" -#: audits/signal_handlers/login_log.py:41 authentication/views/feishu.py:122 +#: audits/signal_handlers/login_log.py:38 authentication/views/feishu.py:123 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 #: settings/serializers/auth/feishu.py:13 users/models/user.py:747 @@ -2502,19 +2518,19 @@ msgstr "企業微信" msgid "FeiShu" msgstr "本を飛ばす" -#: audits/signal_handlers/login_log.py:42 authentication/views/dingtalk.py:159 +#: audits/signal_handlers/login_log.py:39 authentication/views/dingtalk.py:160 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 #: settings/serializers/auth/dingtalk.py:10 users/models/user.py:746 #: users/models/user.py:848 msgid "DingTalk" msgstr "DingTalk" -#: audits/signal_handlers/login_log.py:43 +#: audits/signal_handlers/login_log.py:40 #: authentication/models/temp_token.py:16 msgid "Temporary token" msgstr "仮パスワード" -#: audits/signal_handlers/login_log.py:44 authentication/views/login.py:95 +#: audits/signal_handlers/login_log.py:41 authentication/views/login.py:95 #: settings/serializers/auth/passkey.py:8 msgid "Passkey" msgstr "" @@ -2527,11 +2543,11 @@ msgstr "監査セッション タスク ログのクリーンアップ" msgid "Upload FTP file to external storage" msgstr "外部ストレージへのFTPファイルのアップロード" -#: authentication/api/access_key.py:36 +#: authentication/api/access_key.py:39 msgid "Access keys can be created at most 10" msgstr "" -#: authentication/api/confirm.py:41 +#: authentication/api/confirm.py:50 msgid "This action require verify your MFA" msgstr "この操作には、MFAを検証する必要があります" @@ -2541,23 +2557,23 @@ msgstr "" "再使用可能な接続トークンの使用は許可されていません。グローバル設定は有効に" "なっていません" -#: authentication/api/connection_token.py:358 +#: authentication/api/connection_token.py:375 msgid "Anonymous account is not supported for this asset" msgstr "匿名アカウントはこのプロパティではサポートされていません" -#: authentication/api/connection_token.py:378 +#: authentication/api/connection_token.py:394 msgid "Account not found" msgstr "アカウントが見つかりません" -#: authentication/api/connection_token.py:381 +#: authentication/api/connection_token.py:397 msgid "Permission expired" msgstr "承認の有効期限が切れています" -#: authentication/api/connection_token.py:412 +#: authentication/api/connection_token.py:427 msgid "ACL action is reject: {}({})" msgstr "ACL アクションは拒否です: {}({})" -#: authentication/api/connection_token.py:416 +#: authentication/api/connection_token.py:431 msgid "ACL action is review" msgstr "ACL アクションはレビューです" @@ -2618,11 +2634,11 @@ msgstr "" msgid "Invalid token or cache refreshed." msgstr "無効なトークンまたはキャッシュの更新。" -#: authentication/backends/passkey/api.py:26 +#: authentication/backends/passkey/api.py:37 msgid "Only register passkey for local user" msgstr "" -#: authentication/backends/passkey/api.py:54 +#: authentication/backends/passkey/api.py:65 msgid "Auth failed" msgstr "認証に失敗しました" @@ -2780,21 +2796,21 @@ msgstr "電話が設定されていない" msgid "SSO auth closed" msgstr "SSO authは閉鎖されました" -#: authentication/errors/mfa.py:18 authentication/views/wecom.py:61 +#: authentication/errors/mfa.py:18 authentication/views/wecom.py:62 msgid "WeCom is already bound" msgstr "企業の微信はすでにバインドされています" -#: authentication/errors/mfa.py:23 authentication/views/wecom.py:202 -#: authentication/views/wecom.py:244 +#: authentication/errors/mfa.py:23 authentication/views/wecom.py:204 +#: authentication/views/wecom.py:246 msgid "WeCom is not bound" msgstr "企業の微信をバインドしていません" -#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:209 -#: authentication/views/dingtalk.py:251 +#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:211 +#: authentication/views/dingtalk.py:253 msgid "DingTalk is not bound" msgstr "DingTalkはバインドされていません" -#: authentication/errors/mfa.py:33 authentication/views/feishu.py:166 +#: authentication/errors/mfa.py:33 authentication/views/feishu.py:168 msgid "FeiShu is not bound" msgstr "本を飛ばすは拘束されていません" @@ -2914,7 +2930,7 @@ msgstr "電話番号を設定して有効にする" msgid "Clear phone number to disable" msgstr "無効にする電話番号をクリアする" -#: authentication/middleware.py:93 settings/utils/ldap.py:679 +#: authentication/middleware.py:94 settings/utils/ldap.py:679 msgid "Authentication failed (before login check failed): {}" msgstr "認証に失敗しました (ログインチェックが失敗する前): {}" @@ -3090,7 +3106,7 @@ msgstr "メール" msgid "The {} cannot be empty" msgstr "{} 空にしてはならない" -#: authentication/serializers/token.py:80 perms/serializers/permission.py:37 +#: authentication/serializers/token.py:86 perms/serializers/permission.py:37 #: perms/serializers/permission.py:58 users/serializers/user.py:98 #: users/serializers/user.py:168 msgid "Is valid" @@ -3133,12 +3149,6 @@ msgstr "有効化" msgid "Delete success" msgstr "削除成功" -#: authentication/templates/authentication/_access_key_modal.html:155 -#: authentication/templates/authentication/_mfa_confirm_modal.html:53 -#: templates/_modal.html:22 tickets/const.py:44 -msgid "Close" -msgstr "閉じる" - #: authentication/templates/authentication/_captcha_field.html:8 msgid "Play CAPTCHA as audio file" msgstr "CAPTCHAをオーディオファイルとして再生する" @@ -3350,69 +3360,69 @@ msgstr "ローカルエリアネットワーク" msgid "If you have any question, please contact the administrator" msgstr "質問があったら、管理者に連絡して下さい" -#: authentication/views/dingtalk.py:41 +#: authentication/views/dingtalk.py:42 msgid "DingTalk Error, Please contact your system administrator" msgstr "DingTalkエラー、システム管理者に連絡してください" -#: authentication/views/dingtalk.py:44 authentication/views/dingtalk.py:208 +#: authentication/views/dingtalk.py:45 authentication/views/dingtalk.py:210 msgid "DingTalk Error" msgstr "DingTalkエラー" -#: authentication/views/dingtalk.py:56 authentication/views/feishu.py:50 -#: authentication/views/wecom.py:57 +#: authentication/views/dingtalk.py:57 authentication/views/feishu.py:51 +#: authentication/views/wecom.py:58 msgid "" "The system configuration is incorrect. Please contact your administrator" msgstr "システム設定が正しくありません。管理者に連絡してください" -#: authentication/views/dingtalk.py:60 +#: authentication/views/dingtalk.py:61 msgid "DingTalk is already bound" msgstr "DingTalkはすでにバインドされています" -#: authentication/views/dingtalk.py:128 authentication/views/wecom.py:129 +#: authentication/views/dingtalk.py:129 authentication/views/wecom.py:130 msgid "Invalid user_id" msgstr "無効なuser_id" -#: authentication/views/dingtalk.py:144 +#: authentication/views/dingtalk.py:145 msgid "DingTalk query user failed" msgstr "DingTalkクエリユーザーが失敗しました" -#: authentication/views/dingtalk.py:153 +#: authentication/views/dingtalk.py:154 msgid "The DingTalk is already bound to another user" msgstr "DingTalkはすでに別のユーザーにバインドされています" -#: authentication/views/dingtalk.py:160 +#: authentication/views/dingtalk.py:161 msgid "Binding DingTalk successfully" msgstr "DingTalkのバインドに成功" -#: authentication/views/dingtalk.py:210 authentication/views/dingtalk.py:245 +#: authentication/views/dingtalk.py:212 authentication/views/dingtalk.py:247 msgid "Failed to get user from DingTalk" msgstr "DingTalkからユーザーを取得できませんでした" -#: authentication/views/dingtalk.py:252 +#: authentication/views/dingtalk.py:254 msgid "Please login with a password and then bind the DingTalk" msgstr "パスワードでログインし、DingTalkをバインドしてください" -#: authentication/views/feishu.py:38 authentication/views/feishu.py:165 +#: authentication/views/feishu.py:39 authentication/views/feishu.py:167 msgid "FeiShu Error" msgstr "FeiShuエラー" -#: authentication/views/feishu.py:66 +#: authentication/views/feishu.py:67 msgid "FeiShu is already bound" msgstr "FeiShuはすでにバインドされています" -#: authentication/views/feishu.py:107 +#: authentication/views/feishu.py:108 msgid "FeiShu query user failed" msgstr "FeiShuクエリユーザーが失敗しました" -#: authentication/views/feishu.py:116 +#: authentication/views/feishu.py:117 msgid "The FeiShu is already bound to another user" msgstr "FeiShuはすでに別のユーザーにバインドされています" -#: authentication/views/feishu.py:123 +#: authentication/views/feishu.py:124 msgid "Binding FeiShu successfully" msgstr "本を飛ばすのバインドに成功" -#: authentication/views/feishu.py:167 +#: authentication/views/feishu.py:169 msgid "Failed to get user from FeiShu" msgstr "本を飛ばすからユーザーを取得できませんでした" @@ -3452,31 +3462,31 @@ msgstr "ログアウト成功" msgid "Logout success, return login page" msgstr "ログアウト成功、ログインページを返す" -#: authentication/views/wecom.py:42 +#: authentication/views/wecom.py:43 msgid "WeCom Error, Please contact your system administrator" msgstr "企業微信エラー、システム管理者に連絡してください" -#: authentication/views/wecom.py:45 authentication/views/wecom.py:201 +#: authentication/views/wecom.py:46 authentication/views/wecom.py:203 msgid "WeCom Error" msgstr "企業微信エラー" -#: authentication/views/wecom.py:144 +#: authentication/views/wecom.py:145 msgid "WeCom query user failed" msgstr "企業微信ユーザーの問合せに失敗しました" -#: authentication/views/wecom.py:153 +#: authentication/views/wecom.py:154 msgid "The WeCom is already bound to another user" msgstr "この企業の微信はすでに他のユーザーをバインドしている。" -#: authentication/views/wecom.py:160 +#: authentication/views/wecom.py:161 msgid "Binding WeCom successfully" msgstr "企業の微信のバインドに成功" -#: authentication/views/wecom.py:203 authentication/views/wecom.py:238 +#: authentication/views/wecom.py:205 authentication/views/wecom.py:240 msgid "Failed to get user from WeCom" msgstr "企業の微信からユーザーを取得できませんでした" -#: authentication/views/wecom.py:245 +#: authentication/views/wecom.py:247 msgid "Please login with a password and then bind the WeCom" msgstr "パスワードでログインしてからWeComをバインドしてください" @@ -3650,11 +3660,11 @@ msgstr "M2Mリバースは許可されません" msgid "Is referenced by other objects and cannot be deleted" msgstr "他のオブジェクトによって参照され、削除できません。" -#: common/exceptions.py:48 +#: common/exceptions.py:51 msgid "This action require confirm current user" msgstr "このアクションでは、MFAの確認が必要です。" -#: common/exceptions.py:56 +#: common/exceptions.py:59 msgid "Unexpect error occur" msgstr "予期しないエラーが発生します" @@ -4272,7 +4282,7 @@ msgstr "" msgid "The organization have resource ({}) cannot be deleted" msgstr "組織のリソース ({}) は削除できません" -#: orgs/apps.py:7 rbac/tree.py:123 +#: orgs/apps.py:7 rbac/tree.py:125 msgid "App organizations" msgstr "アプリ組織" @@ -4599,25 +4609,25 @@ msgid "My assets" msgstr "私の資産" #: rbac/tree.py:56 terminal/models/applet/applet.py:52 -#: terminal/models/applet/applet.py:298 terminal/models/applet/host.py:29 +#: terminal/models/applet/applet.py:300 terminal/models/applet/host.py:29 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "リモートアプリケーション" -#: rbac/tree.py:124 +#: rbac/tree.py:126 msgid "Ticket comment" msgstr "チケットコメント" -#: rbac/tree.py:125 settings/serializers/feature.py:58 +#: rbac/tree.py:127 settings/serializers/feature.py:58 #: tickets/models/ticket/general.py:307 msgid "Ticket" msgstr "チケット" -#: rbac/tree.py:126 +#: rbac/tree.py:128 msgid "Common setting" msgstr "共通設定" -#: rbac/tree.py:127 +#: rbac/tree.py:129 msgid "View permission tree" msgstr "権限ツリーの表示" @@ -4675,38 +4685,50 @@ msgid "Can change auth setting" msgstr "資格認定の設定" #: settings/models.py:162 +msgid "Can change auth ops" +msgstr "タスクセンターの設定" + +#: settings/models.py:163 +msgid "Can change auth ticket" +msgstr "製造オーダ設定" + +#: settings/models.py:164 +msgid "Can change auth announcement" +msgstr "公告の設定" + +#: settings/models.py:165 msgid "Can change vault setting" msgstr "金庫の設定を変えることができます" -#: settings/models.py:163 +#: settings/models.py:166 msgid "Can change system msg sub setting" msgstr "システムmsgサブ设定を変更できます" -#: settings/models.py:164 +#: settings/models.py:167 msgid "Can change sms setting" msgstr "Smsの設定を変えることができます" -#: settings/models.py:165 +#: settings/models.py:168 msgid "Can change security setting" msgstr "セキュリティ設定を変更できます" -#: settings/models.py:166 +#: settings/models.py:169 msgid "Can change clean setting" msgstr "きれいな設定を変えることができます" -#: settings/models.py:167 +#: settings/models.py:170 msgid "Can change interface setting" msgstr "インターフェイスの設定を変えることができます" -#: settings/models.py:168 +#: settings/models.py:171 msgid "Can change license setting" msgstr "ライセンス設定を変更できます" -#: settings/models.py:169 +#: settings/models.py:172 msgid "Can change terminal setting" msgstr "ターミナルの設定を変えることができます" -#: settings/models.py:170 +#: settings/models.py:173 msgid "Can change other setting" msgstr "他の設定を変えることができます" @@ -6110,7 +6132,7 @@ msgstr "コマンドストア" msgid "Invalid" msgstr "無効" -#: terminal/api/component/storage.py:119 terminal/tasks.py:141 +#: terminal/api/component/storage.py:119 terminal/tasks.py:142 msgid "Test failure: {}" msgstr "テスト失敗: {}" @@ -6119,7 +6141,7 @@ msgid "Test successful" msgstr "テスト成功" #: terminal/api/component/storage.py:124 terminal/notifications.py:240 -#: terminal/tasks.py:145 +#: terminal/tasks.py:146 msgid "Test failure: Account invalid" msgstr "テスト失敗: アカウントが無効" @@ -6314,7 +6336,7 @@ msgstr "カスタムプラットフォームのみをサポート" msgid "Missing type in platform.yml" msgstr "platform.ymlにタイプがありません" -#: terminal/models/applet/applet.py:300 terminal/models/applet/host.py:35 +#: terminal/models/applet/applet.py:302 terminal/models/applet/host.py:35 #: terminal/models/applet/host.py:137 msgid "Hosting" msgstr "ホスト マシン" @@ -6874,23 +6896,23 @@ msgstr "端末の状態を定期的にクリーンアップする" msgid "Clean orphan session" msgstr "オフライン セッションをクリアする" -#: terminal/tasks.py:61 +#: terminal/tasks.py:62 msgid "Upload session replay to external storage" msgstr "セッションの記録を外部ストレージにアップロードする" -#: terminal/tasks.py:90 +#: terminal/tasks.py:91 msgid "Run applet host deployment" msgstr "アプリケーション マシンの展開を実行する" -#: terminal/tasks.py:100 +#: terminal/tasks.py:101 msgid "Install applet" msgstr "アプリをインストールする" -#: terminal/tasks.py:111 +#: terminal/tasks.py:112 msgid "Generate applet host accounts" msgstr "リモートアプリケーション上のアカウントを収集する" -#: terminal/tasks.py:123 +#: terminal/tasks.py:124 msgid "Check command replay storage connectivity" msgstr "チェックコマンドと録画ストレージの接続性" @@ -6920,6 +6942,10 @@ msgstr "" msgid "All available port count: {}, Already use port count: {}" msgstr "使用可能なすべてのポート数: {}、すでに使用しているポート数: {}" +#: tickets/api/ticket.py:88 tickets/models/ticket/general.py:288 +msgid "Applicant" +msgstr "応募者" + #: tickets/apps.py:7 msgid "Tickets" msgstr "チケット" @@ -6948,10 +6974,6 @@ msgstr "拒否" msgid "Closed" msgstr "クローズ" -#: tickets/const.py:46 -msgid "Approve" -msgstr "承認" - #: tickets/const.py:50 msgid "One level" msgstr "1つのレベル" @@ -7083,6 +7105,10 @@ msgstr "実行コマンド" msgid "Command filter acl" msgstr "コマンド フィルタ" +#: tickets/models/ticket/command_confirm.py:23 +msgid "Apply Command Ticket" +msgstr "製造オーダの検討を命令" + #: tickets/models/ticket/general.py:76 msgid "Ticket step" msgstr "チケットステップ" @@ -7095,10 +7121,6 @@ msgstr "割り当てられたチケット" msgid "Title" msgstr "タイトル" -#: tickets/models/ticket/general.py:288 -msgid "Applicant" -msgstr "応募者" - #: tickets/models/ticket/general.py:292 msgid "TicketFlow" msgstr "作業指示プロセス" @@ -7131,10 +7153,18 @@ msgstr "ログイン資産" msgid "Login account" msgstr "ログインアカウント" +#: tickets/models/ticket/login_asset_confirm.py:27 +msgid "Apply Login Asset Ticket" +msgstr "資産ログインレビュー製造オーダ" + #: tickets/models/ticket/login_confirm.py:12 msgid "Login datetime" msgstr "ログイン日時" +#: tickets/models/ticket/login_confirm.py:15 +msgid "Apply Login Ticket" +msgstr "ユーザーログインレビュー製造オーダ" + #: tickets/notifications.py:63 msgid "Ticket basic info" msgstr "チケット基本情報" @@ -7221,18 +7251,14 @@ msgid "Ticket information" msgstr "作業指示情報" #: tickets/templates/tickets/approve_check_password.html:29 -#: tickets/views/approve.py:40 +#: tickets/views/approve.py:40 tickets/views/approve.py:77 msgid "Ticket approval" msgstr "作業指示の承認" -#: tickets/templates/tickets/approve_check_password.html:45 +#: tickets/templates/tickets/approve_check_password.html:44 msgid "Approval" msgstr "承認" -#: tickets/templates/tickets/approve_check_password.html:54 -msgid "Go Login" -msgstr "ログイン" - #: tickets/views/approve.py:41 msgid "" "This ticket does not exist, the process has ended, or this link has expired" @@ -7240,19 +7266,19 @@ msgstr "" "このワークシートが存在しないか、ワークシートが終了したか、このリンクが無効に" "なっています" -#: tickets/views/approve.py:70 +#: tickets/views/approve.py:69 msgid "Click the button below to approve or reject" msgstr "下のボタンをクリックして同意または拒否。" -#: tickets/views/approve.py:72 +#: tickets/views/approve.py:78 msgid "After successful authentication, this ticket can be approved directly" msgstr "認証に成功した後、作業指示書は直接承認することができる。" -#: tickets/views/approve.py:97 +#: tickets/views/approve.py:95 msgid "Illegal approval action" msgstr "無効な承認アクション" -#: tickets/views/approve.py:110 +#: tickets/views/approve.py:108 msgid "This user is not authorized to approve this ticket" msgstr "このユーザーはこの作業指示を承認する権限がありません" @@ -8052,7 +8078,7 @@ msgstr "同期済み" msgid "Released" msgstr "リリース済み" -#: xpack/plugins/cloud/manager.py:53 +#: xpack/plugins/cloud/manager.py:54 msgid "Account unavailable" msgstr "利用できないアカウント" @@ -8145,14 +8171,12 @@ msgid "Task strategy" msgstr "ミッション戦略です" #: xpack/plugins/cloud/models.py:285 -msgid "Exact" -msgstr "" +msgid "Equal" +msgstr "等しい" #: xpack/plugins/cloud/models.py:286 -#, fuzzy -#| msgid "No" -msgid "Not" -msgstr "否" +msgid "Not Equal" +msgstr "不等于" #: xpack/plugins/cloud/models.py:287 msgid "In" @@ -8564,7 +8588,7 @@ msgstr "ライセンスのインポートに成功" msgid "License is invalid" msgstr "ライセンスが無効です" -#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 +#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:141 msgid "License" msgstr "ライセンス" @@ -8583,9 +8607,3 @@ msgstr "エンタープライズプロフェッショナル版" #: xpack/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" - -#~ msgid "Equal" -#~ msgstr "等しい" - -#~ msgid "Not equal" -#~ msgstr "不等于" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 98c61cf62..ca81a92bf 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:4be95c6d05beb08730dd3cee943e7f84aa94e8e711a41e2fce5b0aebf84e831b -size 133014 +oid sha256:9f331dc156c4e51b6a30249da9d342093c79da1cebe926474b7942a5d3b6a931 +size 133436 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 5c375d6c1..bdb3be1d1 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: 2023-10-13 15:03+0800\n" +"POT-Creation-Date: 2023-10-19 16:21+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -17,13 +17,13 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.3\n" -#: accounts/api/automations/base.py:79 tickets/api/ticket.py:112 +#: accounts/api/automations/base.py:79 tickets/api/ticket.py:132 msgid "The parameter 'action' must be [{}]" msgstr "参数 'action' 必须是 [{}]" #: accounts/const/account.py:6 #: accounts/serializers/automations/change_secret.py:32 -#: assets/models/_user.py:24 audits/signal_handlers/login_log.py:37 +#: assets/models/_user.py:24 audits/signal_handlers/login_log.py:34 #: authentication/confirm/password.py:9 authentication/confirm/password.py:24 #: authentication/confirm/password.py:26 authentication/forms.py:32 #: authentication/templates/authentication/login.html:324 @@ -88,13 +88,13 @@ msgstr "模板" msgid "Skip" msgstr "跳过" -#: accounts/const/account.py:32 audits/const.py:24 rbac/tree.py:234 +#: accounts/const/account.py:32 audits/const.py:24 rbac/tree.py:236 #: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6 msgid "Update" msgstr "更新" #: accounts/const/account.py:33 -#: accounts/serializers/automations/change_secret.py:155 audits/const.py:60 +#: accounts/serializers/automations/change_secret.py:155 audits/const.py:62 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 #: ops/const.py:74 terminal/const.py:77 xpack/plugins/cloud/const.py:43 msgid "Failed" @@ -217,15 +217,15 @@ msgstr "用户 %s 查看/导出 了密码" #: accounts/models/account.py:48 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:209 -#: accounts/serializers/account/account.py:254 +#: accounts/serializers/account/account.py:210 +#: accounts/serializers/account/account.py:255 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:111 #: accounts/serializers/automations/change_secret.py:131 #: acls/serializers/base.py:123 assets/models/asset/common.py:93 #: assets/models/asset/common.py:334 assets/models/cmd_filter.py:36 #: assets/serializers/domain.py:19 assets/serializers/label.py:27 -#: audits/models.py:56 authentication/models/connection_token.py:36 +#: audits/models.py:57 authentication/models/connection_token.py:36 #: perms/models/asset_permission.py:64 perms/serializers/permission.py:34 #: terminal/backends/command/models.py:17 terminal/models/session/session.py:31 #: terminal/notifications.py:155 terminal/serializers/command.py:17 @@ -237,8 +237,8 @@ msgid "Asset" msgstr "资产" #: accounts/models/account.py:52 accounts/models/template.py:15 -#: accounts/serializers/account/account.py:216 -#: accounts/serializers/account/account.py:264 +#: accounts/serializers/account/account.py:217 +#: accounts/serializers/account/account.py:265 #: accounts/serializers/account/template.py:24 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" @@ -250,7 +250,7 @@ msgstr "切换自" msgid "Version" msgstr "版本" -#: accounts/models/account.py:56 accounts/serializers/account/account.py:211 +#: accounts/models/account.py:56 accounts/serializers/account/account.py:212 #: users/models/user.py:837 msgid "Source" msgstr "来源" @@ -264,7 +264,7 @@ msgstr "来源 ID" #: accounts/serializers/automations/change_secret.py:132 #: acls/serializers/base.py:124 acls/templates/acls/asset_login_reminder.html:7 #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 -#: audits/models.py:57 authentication/api/connection_token.py:391 +#: audits/models.py:58 authentication/api/connection_token.py:406 #: ops/models/base.py:18 perms/models/asset_permission.py:70 #: perms/serializers/permission.py:39 terminal/backends/command/models.py:18 #: terminal/models/session/session.py:33 @@ -308,7 +308,7 @@ msgid "Account backup plan" msgstr "账号备份计划" #: accounts/models/automations/backup_account.py:91 -#: assets/models/automations/base.py:115 audits/models.py:63 +#: assets/models/automations/base.py:115 audits/models.py:64 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:228 #: ops/templates/ops/celery_task_log.html:75 #: perms/models/asset_permission.py:72 terminal/models/applet/host.py:140 @@ -336,7 +336,7 @@ msgstr "账号备份快照" msgid "Trigger mode" msgstr "触发模式" -#: accounts/models/automations/backup_account.py:105 audits/models.py:201 +#: accounts/models/automations/backup_account.py:105 audits/models.py:202 #: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "原因" @@ -422,7 +422,7 @@ msgid "Date finished" msgstr "结束日期" #: accounts/models/automations/change_secret.py:44 -#: accounts/serializers/account/account.py:256 assets/const/automation.py:8 +#: accounts/serializers/account/account.py:257 assets/const/automation.py:8 #: authentication/templates/authentication/passkey.html:173 #: authentication/views/base.py:27 authentication/views/base.py:28 #: authentication/views/base.py:29 common/const/choices.py:20 @@ -446,7 +446,7 @@ msgstr "最后登录日期" #: accounts/serializers/account/virtual.py:21 acls/serializers/base.py:19 #: acls/serializers/base.py:50 acls/templates/acls/asset_login_reminder.html:5 #: acls/templates/acls/user_login_reminder.html:5 assets/models/_user.py:23 -#: audits/models.py:186 authentication/forms.py:25 authentication/forms.py:27 +#: audits/models.py:187 authentication/forms.py:25 authentication/forms.py:27 #: authentication/models/temp_token.py:9 #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 @@ -479,7 +479,7 @@ msgstr "触发方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:41 #: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 -#: audits/models.py:90 audits/serializers.py:84 +#: audits/models.py:91 audits/serializers.py:84 #: authentication/serializers/connect_token_secret.py:116 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" @@ -494,7 +494,7 @@ msgid "Verify asset account" msgstr "账号验证" #: accounts/models/base.py:37 accounts/models/base.py:67 -#: accounts/serializers/account/account.py:436 +#: accounts/serializers/account/account.py:437 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:45 #: authentication/serializers/connect_token_secret.py:41 @@ -538,10 +538,11 @@ msgstr "密码规则" #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:94 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 -#: terminal/models/component/terminal.py:84 users/forms/profile.py:33 -#: users/models/group.py:13 users/models/preference.py:11 -#: users/models/user.py:792 xpack/plugins/cloud/models.py:32 -#: xpack/plugins/cloud/models.py:273 xpack/plugins/cloud/serializers/task.py:68 +#: terminal/models/component/terminal.py:84 tickets/api/ticket.py:87 +#: users/forms/profile.py:33 users/models/group.py:13 +#: users/models/preference.py:11 users/models/user.py:792 +#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 +#: xpack/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名称" @@ -659,7 +660,7 @@ msgstr "立即推送" msgid "Exist policy" msgstr "账号存在策略" -#: accounts/serializers/account/account.py:189 applications/models.py:11 +#: accounts/serializers/account/account.py:190 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:89 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 #: assets/serializers/platform.py:133 assets/serializers/platform.py:229 @@ -668,9 +669,9 @@ msgstr "账号存在策略" msgid "Category" msgstr "类别" -#: accounts/serializers/account/account.py:190 +#: accounts/serializers/account/account.py:191 #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 -#: acls/serializers/command_acl.py:18 applications/models.py:14 +#: acls/serializers/command_acl.py:19 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 #: assets/models/cmd_filter.py:74 assets/models/platform.py:90 #: assets/serializers/asset/common.py:122 assets/serializers/platform.py:113 @@ -688,59 +689,59 @@ msgstr "类别" msgid "Type" msgstr "类型" -#: accounts/serializers/account/account.py:205 +#: accounts/serializers/account/account.py:206 msgid "Asset not found" msgstr "资产不存在" -#: accounts/serializers/account/account.py:245 +#: accounts/serializers/account/account.py:246 msgid "Has secret" msgstr "已托管密码" -#: accounts/serializers/account/account.py:255 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:256 ops/models/celery.py:60 #: tickets/models/comment.py:13 tickets/models/ticket/general.py:45 #: tickets/models/ticket/general.py:279 tickets/serializers/super_ticket.py:14 #: tickets/serializers/ticket/ticket.py:21 msgid "State" msgstr "状态" -#: accounts/serializers/account/account.py:257 +#: accounts/serializers/account/account.py:258 msgid "Changed" msgstr "已修改" -#: accounts/serializers/account/account.py:267 +#: accounts/serializers/account/account.py:268 #: accounts/serializers/automations/base.py:22 acls/models/base.py:97 #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 -#: authentication/api/connection_token.py:390 ops/models/base.py:17 +#: authentication/api/connection_token.py:405 ops/models/base.py:17 #: ops/models/job.py:139 ops/serializers/job.py:21 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" msgstr "资产" -#: accounts/serializers/account/account.py:322 +#: accounts/serializers/account/account.py:323 msgid "Account already exists" msgstr "账号已存在" -#: accounts/serializers/account/account.py:372 +#: accounts/serializers/account/account.py:373 #, python-format msgid "Asset does not support this secret type: %s" msgstr "资产不支持账号类型: %s" -#: accounts/serializers/account/account.py:404 +#: accounts/serializers/account/account.py:405 msgid "Account has exist" msgstr "账号已存在" -#: accounts/serializers/account/account.py:437 +#: accounts/serializers/account/account.py:438 #: authentication/serializers/connect_token_secret.py:156 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:31 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:447 acls/serializers/base.py:116 -#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:52 -#: audits/models.py:88 audits/models.py:170 audits/models.py:268 +#: accounts/serializers/account/account.py:448 acls/serializers/base.py:116 +#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:53 +#: audits/models.py:89 audits/models.py:171 audits/models.py:268 #: audits/serializers.py:171 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 @@ -757,7 +758,7 @@ msgstr "ID" msgid "User" msgstr "用户" -#: accounts/serializers/account/account.py:448 +#: accounts/serializers/account/account.py:449 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:158 terminal/notifications.py:207 msgid "Date" @@ -789,7 +790,7 @@ msgid "Key password" msgstr "密钥密码" #: accounts/serializers/account/base.py:78 -#: assets/serializers/asset/common.py:338 +#: assets/serializers/asset/common.py:378 msgid "Spec info" msgstr "特殊信息" @@ -840,7 +841,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 #: ops/models/job.py:145 ops/models/playbook.py:31 rbac/models/role.py:37 #: settings/models.py:37 terminal/models/applet/applet.py:45 -#: terminal/models/applet/applet.py:302 terminal/models/applet/host.py:142 +#: terminal/models/applet/applet.py:304 terminal/models/applet/host.py:142 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 @@ -893,11 +894,11 @@ msgstr "* 密码长度范围 6-30 位" msgid "Automation task execution" msgstr "自动化任务执行历史" -#: accounts/serializers/automations/change_secret.py:154 audits/const.py:59 -#: audits/models.py:62 audits/signal_handlers/activity_log.py:33 +#: accounts/serializers/automations/change_secret.py:154 audits/const.py:61 +#: audits/models.py:63 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 #: terminal/const.py:76 terminal/models/session/sharing.py:121 -#: tickets/views/approve.py:119 +#: tickets/views/approve.py:117 msgid "Success" msgstr "成功" @@ -972,16 +973,16 @@ msgstr "密钥不合法或密钥密码错误" msgid "Acls" msgstr "访问控制" -#: acls/const.py:6 audits/const.py:33 terminal/const.py:11 tickets/const.py:45 -#: tickets/templates/tickets/approve_check_password.html:49 +#: acls/const.py:6 audits/const.py:36 terminal/const.py:11 tickets/const.py:45 +#: tickets/templates/tickets/approve_check_password.html:48 msgid "Reject" msgstr "拒绝" -#: acls/const.py:7 audits/const.py:34 terminal/const.py:9 +#: acls/const.py:7 audits/const.py:33 terminal/const.py:9 msgid "Accept" msgstr "接受" -#: acls/const.py:8 audits/const.py:35 +#: acls/const.py:8 audits/const.py:34 msgid "Review" msgstr "审批" @@ -989,7 +990,7 @@ msgstr "审批" msgid "Warning" msgstr "告警" -#: acls/const.py:10 audits/const.py:36 notifications/apps.py:7 +#: acls/const.py:10 audits/const.py:35 notifications/apps.py:7 msgid "Notifications" msgstr "通知" @@ -1023,7 +1024,7 @@ msgid "Users" msgstr "用户管理" #: acls/models/base.py:98 assets/models/automations/base.py:17 -#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:337 +#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:377 #: rbac/tree.py:35 msgid "Accounts" msgstr "账号管理" @@ -1055,8 +1056,8 @@ msgstr "每行一个命令" msgid "Ignore case" msgstr "忽略大小写" -#: acls/models/command_acl.py:33 acls/models/command_acl.py:96 -#: acls/serializers/command_acl.py:28 +#: acls/models/command_acl.py:33 acls/models/command_acl.py:97 +#: acls/serializers/command_acl.py:29 #: authentication/serializers/connect_token_secret.py:85 #: terminal/templates/terminal/_msg_command_warning.html:14 msgid "Command group" @@ -1066,12 +1067,12 @@ msgstr "命令组" msgid "The generated regular expression is incorrect: {}" msgstr "生成的正则表达式有误" -#: acls/models/command_acl.py:100 +#: acls/models/command_acl.py:103 #: terminal/templates/terminal/_msg_command_warning.html:12 msgid "Command acl" msgstr "命令过滤" -#: acls/models/command_acl.py:109 tickets/const.py:11 +#: acls/models/command_acl.py:112 tickets/const.py:11 msgid "Command confirm" msgstr "命令复核" @@ -1185,15 +1186,15 @@ msgstr "" msgid "Thank you" msgstr "谢谢" -#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:192 -#: audits/models.py:261 +#: acls/templates/acls/user_login_reminder.html:7 audits/models.py:193 +#: audits/models.py:262 #: authentication/templates/authentication/_msg_different_city.html:11 #: tickets/models/ticket/login_confirm.py:11 msgid "Login city" msgstr "登录城市" -#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:195 -#: audits/models.py:262 audits/serializers.py:65 +#: acls/templates/acls/user_login_reminder.html:8 audits/models.py:196 +#: audits/models.py:263 audits/serializers.py:65 msgid "User agent" msgstr "用户代理" @@ -1243,7 +1244,7 @@ msgstr "不能删除根节点 ({})" msgid "Deletion failed and the node contains assets" msgstr "删除失败,节点包含资产" -#: assets/api/tree.py:49 assets/serializers/node.py:41 +#: assets/api/tree.py:49 assets/serializers/node.py:42 msgid "The same level node name cannot be the same" msgstr "同级别节点名字不能重复" @@ -1274,7 +1275,7 @@ msgid "Unable to connect to port {port} on {address}" msgstr "无法连接到 {port} 上的端口 {address}" #: assets/automations/ping_gateway/manager.py:58 -#: authentication/middleware.py:92 xpack/plugins/cloud/providers/fc.py:47 +#: authentication/middleware.py:93 xpack/plugins/cloud/providers/fc.py:47 msgid "Authentication failed" msgstr "认证失败" @@ -1283,7 +1284,7 @@ msgstr "认证失败" msgid "Connect failed" msgstr "连接失败" -#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:42 +#: assets/const/automation.py:6 audits/const.py:6 audits/const.py:44 #: audits/signal_handlers/activity_log.py:62 common/utils/ip/geoip/utils.py:31 #: common/utils/ip/geoip/utils.py:37 common/utils/ip/utils.py:104 msgid "Unknown" @@ -1305,19 +1306,19 @@ msgstr "测试网关" msgid "Gather facts" msgstr "收集资产信息" -#: assets/const/base.py:33 audits/const.py:53 +#: assets/const/base.py:32 audits/const.py:55 #: terminal/serializers/applet_host.py:32 msgid "Disabled" msgstr "禁用" -#: assets/const/base.py:34 settings/serializers/basic.py:6 +#: assets/const/base.py:33 settings/serializers/basic.py:6 #: users/serializers/preference/koko.py:19 #: users/serializers/preference/lina.py:39 #: users/serializers/preference/luna.py:60 msgid "Basic" msgstr "基本" -#: assets/const/base.py:35 assets/const/protocol.py:252 +#: assets/const/base.py:34 assets/const/protocol.py:252 #: assets/models/asset/web.py:13 msgid "Script" msgstr "脚本" @@ -1339,7 +1340,7 @@ msgid "Cloud service" msgstr "云服务" #: assets/const/category.py:14 assets/models/asset/gpt.py:11 -#: assets/models/asset/web.py:16 audits/const.py:40 +#: assets/models/asset/web.py:16 audits/const.py:42 #: terminal/models/applet/applet.py:27 users/const.py:47 msgid "Web" msgstr "Web" @@ -1525,7 +1526,7 @@ msgstr "SSH公钥" # msgstr "备注" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 -#: audits/models.py:265 common/db/models.py:34 ops/models/base.py:54 +#: audits/models.py:266 common/db/models.py:34 ops/models/base.py:54 #: ops/models/job.py:227 users/models/user.py:1024 msgid "Date created" msgstr "创建日期" @@ -1640,7 +1641,7 @@ msgstr "网域" msgid "Labels" msgstr "标签管理" -#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:339 +#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:379 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "收集资产硬件信息" @@ -1702,9 +1703,9 @@ msgstr "自动化任务" msgid "Asset automation task" msgstr "资产自动化任务" -#: assets/models/automations/base.py:113 audits/models.py:206 +#: assets/models/automations/base.py:113 audits/models.py:207 #: audits/serializers.py:51 ops/models/base.py:49 ops/models/job.py:220 -#: terminal/models/applet/applet.py:301 terminal/models/applet/host.py:139 +#: terminal/models/applet/applet.py:303 terminal/models/applet/host.py:139 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:115 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 @@ -1850,7 +1851,7 @@ msgstr "开放的" msgid "Setting" msgstr "设置" -#: assets/models/platform.py:38 audits/const.py:54 +#: assets/models/platform.py:38 audits/const.py:56 #: authentication/backends/passkey/models.py:11 settings/models.py:36 #: terminal/serializers/applet_host.py:33 msgid "Enabled" @@ -1975,22 +1976,26 @@ msgid "Node path" msgstr "节点路径" #: assets/serializers/asset/common.py:145 -#: assets/serializers/asset/common.py:340 +#: assets/serializers/asset/common.py:380 msgid "Auto info" msgstr "自动化信息" -#: assets/serializers/asset/common.py:236 +#: assets/serializers/asset/common.py:238 msgid "Platform not exist" msgstr "平台不存在" -#: assets/serializers/asset/common.py:272 +#: assets/serializers/asset/common.py:274 msgid "port out of range (0-65535)" msgstr "端口超出范围 (0-65535)" -#: assets/serializers/asset/common.py:279 +#: assets/serializers/asset/common.py:281 msgid "Protocol is required: {}" msgstr "协议是必填的: {}" +#: assets/serializers/asset/common.py:309 +msgid "Invalid data" +msgstr "无效的数据" + #: assets/serializers/asset/database.py:12 msgid "Default database" msgstr "默认数据库" @@ -2229,7 +2234,7 @@ msgstr "删除目录" #: audits/const.py:14 audits/const.py:25 #: authentication/templates/authentication/_access_key_modal.html:65 -#: perms/const.py:17 rbac/tree.py:235 +#: perms/const.py:17 rbac/tree.py:237 msgid "Delete" msgstr "删除" @@ -2254,7 +2259,7 @@ msgstr "下载" msgid "Rename dir" msgstr "映射目录" -#: audits/const.py:23 rbac/tree.py:233 terminal/api/session/session.py:257 +#: audits/const.py:23 rbac/tree.py:235 terminal/api/session/session.py:257 #: terminal/templates/terminal/_msg_command_warning.html:18 #: terminal/templates/terminal/_msg_session_sharing.html:10 msgid "View" @@ -2262,7 +2267,7 @@ msgstr "查看" #: audits/const.py:26 #: authentication/templates/authentication/_access_key_modal.html:22 -#: rbac/tree.py:232 +#: rbac/tree.py:234 msgid "Create" msgstr "创建" @@ -2280,30 +2285,41 @@ msgstr "登录" msgid "Change password" msgstr "改密" -#: audits/const.py:41 settings/serializers/terminal.py:6 +#: audits/const.py:37 tickets/const.py:46 +msgid "Approve" +msgstr "同意" + +#: audits/const.py:38 +#: authentication/templates/authentication/_access_key_modal.html:155 +#: authentication/templates/authentication/_mfa_confirm_modal.html:53 +#: templates/_modal.html:22 tickets/const.py:44 +msgid "Close" +msgstr "关闭" + +#: audits/const.py:43 settings/serializers/terminal.py:6 #: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 #: terminal/serializers/session.py:52 terminal/serializers/session.py:66 msgid "Terminal" msgstr "终端" -#: audits/const.py:46 audits/models.py:130 +#: audits/const.py:48 audits/models.py:131 msgid "Operate log" msgstr "操作日志" -#: audits/const.py:47 +#: audits/const.py:49 msgid "Session log" msgstr "会话日志" -#: audits/const.py:48 +#: audits/const.py:50 msgid "Login log" msgstr "登录日志" -#: audits/const.py:49 terminal/models/applet/host.py:143 +#: audits/const.py:51 terminal/models/applet/host.py:143 #: terminal/models/component/task.py:22 msgid "Task" msgstr "任务" -#: audits/const.py:55 +#: audits/const.py:57 msgid "-" msgstr "-" @@ -2315,28 +2331,28 @@ msgstr "是" msgid "No" msgstr "否" -#: audits/models.py:45 +#: audits/models.py:46 msgid "Job audit log" msgstr "作业审计日志" -#: audits/models.py:54 audits/models.py:98 audits/models.py:173 +#: audits/models.py:55 audits/models.py:99 audits/models.py:174 #: terminal/models/session/session.py:38 terminal/models/session/sharing.py:113 msgid "Remote addr" msgstr "远端地址" -#: audits/models.py:59 audits/serializers.py:35 +#: audits/models.py:60 audits/serializers.py:35 msgid "Operate" msgstr "操作" -#: audits/models.py:61 +#: audits/models.py:62 msgid "Filename" msgstr "文件名" -#: audits/models.py:64 common/serializers/common.py:98 +#: audits/models.py:65 common/serializers/common.py:98 msgid "File" msgstr "文件" -#: audits/models.py:65 terminal/backends/command/models.py:21 +#: audits/models.py:66 terminal/backends/command/models.py:21 #: terminal/models/session/replay.py:9 terminal/models/session/sharing.py:20 #: terminal/models/session/sharing.py:95 #: terminal/templates/terminal/_msg_command_alert.html:10 @@ -2345,93 +2361,86 @@ msgstr "文件" msgid "Session" msgstr "会话" -#: audits/models.py:68 +#: audits/models.py:69 msgid "File transfer log" msgstr "文件管理" -#: audits/models.py:92 audits/serializers.py:86 +#: audits/models.py:93 audits/serializers.py:86 msgid "Resource Type" msgstr "资源类型" -#: audits/models.py:93 audits/models.py:96 audits/models.py:142 +#: audits/models.py:94 audits/models.py:97 audits/models.py:143 #: audits/serializers.py:85 msgid "Resource" msgstr "资源" -#: audits/models.py:99 audits/models.py:145 audits/models.py:175 +#: audits/models.py:100 audits/models.py:146 audits/models.py:176 #: terminal/serializers/command.py:75 msgid "Datetime" msgstr "日期" -#: audits/models.py:138 +#: audits/models.py:139 msgid "Activity type" msgstr "活动类型" -#: audits/models.py:148 +#: audits/models.py:149 msgid "Detail" msgstr "详情" -#: audits/models.py:151 +#: audits/models.py:152 msgid "Detail ID" msgstr "详情 ID" -#: audits/models.py:155 +#: audits/models.py:156 msgid "Activity log" msgstr "活动日志" -#: audits/models.py:171 +#: audits/models.py:172 msgid "Change by" msgstr "修改者" -#: audits/models.py:181 +#: audits/models.py:182 msgid "Password change log" msgstr "改密日志" -#: audits/models.py:188 audits/models.py:263 +#: audits/models.py:189 audits/models.py:264 msgid "Login type" msgstr "登录方式" -#: audits/models.py:190 audits/models.py:259 +#: audits/models.py:191 audits/models.py:260 #: tickets/models/ticket/login_confirm.py:10 msgid "Login IP" msgstr "登录 IP" -#: audits/models.py:198 audits/serializers.py:49 +#: audits/models.py:199 audits/serializers.py:49 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: users/forms/profile.py:65 users/models/user.py:815 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" -#: audits/models.py:208 +#: audits/models.py:209 msgid "Date login" msgstr "登录日期" -#: audits/models.py:210 audits/models.py:264 audits/serializers.py:67 -#: audits/serializers.py:183 +#: audits/models.py:211 audits/models.py:265 audits/serializers.py:67 +#: audits/serializers.py:184 msgid "Authentication backend" msgstr "认证方式" -#: audits/models.py:254 +#: audits/models.py:255 msgid "User login log" msgstr "用户登录日志" -#: audits/models.py:260 +#: audits/models.py:261 msgid "Session key" msgstr "会话标识" -#: audits/models.py:266 authentication/models/connection_token.py:47 -#: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 -#: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:833 -msgid "Date expired" -msgstr "失效日期" - -#: audits/models.py:292 +#: audits/models.py:300 msgid "User session" msgstr "用户会话" -#: audits/models.py:294 +#: audits/models.py:302 msgid "Offline ussr session" msgstr "下限用户会话" @@ -2444,6 +2453,13 @@ msgstr "原因描述" msgid "User %s %s this resource" msgstr "用户 %s %s 了当前资源" +#: audits/serializers.py:172 authentication/models/connection_token.py:47 +#: authentication/models/temp_token.py:13 perms/models/asset_permission.py:74 +#: tickets/models/ticket/apply_application.py:31 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:833 +msgid "Date expired" +msgstr "失效日期" + #: audits/signal_handlers/activity_log.py:26 #, python-format msgid "User %s use account %s login asset %s" @@ -2459,26 +2475,26 @@ msgstr "用户 %s 登录系统 %s" msgid "User %s perform a task for this resource: %s" msgstr "用户 %s 在当前资源, 执行了任务 (%s)" -#: audits/signal_handlers/login_log.py:36 +#: audits/signal_handlers/login_log.py:33 msgid "SSH Key" msgstr "SSH 密钥" -#: audits/signal_handlers/login_log.py:38 settings/serializers/auth/sso.py:13 +#: audits/signal_handlers/login_log.py:35 settings/serializers/auth/sso.py:13 msgid "SSO" msgstr "SSO" -#: audits/signal_handlers/login_log.py:39 +#: audits/signal_handlers/login_log.py:36 msgid "Auth Token" msgstr "认证令牌" -#: audits/signal_handlers/login_log.py:40 authentication/notifications.py:73 -#: authentication/views/login.py:77 authentication/views/wecom.py:159 +#: audits/signal_handlers/login_log.py:37 authentication/notifications.py:73 +#: authentication/views/login.py:77 authentication/views/wecom.py:160 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 #: users/models/user.py:745 users/models/user.py:847 msgid "WeCom" msgstr "企业微信" -#: audits/signal_handlers/login_log.py:41 authentication/views/feishu.py:122 +#: audits/signal_handlers/login_log.py:38 authentication/views/feishu.py:123 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 #: settings/serializers/auth/feishu.py:13 users/models/user.py:747 @@ -2486,19 +2502,19 @@ msgstr "企业微信" msgid "FeiShu" msgstr "飞书" -#: audits/signal_handlers/login_log.py:42 authentication/views/dingtalk.py:159 +#: audits/signal_handlers/login_log.py:39 authentication/views/dingtalk.py:160 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 #: settings/serializers/auth/dingtalk.py:10 users/models/user.py:746 #: users/models/user.py:848 msgid "DingTalk" msgstr "钉钉" -#: audits/signal_handlers/login_log.py:43 +#: audits/signal_handlers/login_log.py:40 #: authentication/models/temp_token.py:16 msgid "Temporary token" msgstr "临时密码" -#: audits/signal_handlers/login_log.py:44 authentication/views/login.py:95 +#: audits/signal_handlers/login_log.py:41 authentication/views/login.py:95 #: settings/serializers/auth/passkey.py:8 msgid "Passkey" msgstr "" @@ -2511,11 +2527,11 @@ msgstr "清理审计会话任务日志" msgid "Upload FTP file to external storage" msgstr "上传 FTP 文件到外部存储" -#: authentication/api/access_key.py:36 +#: authentication/api/access_key.py:39 msgid "Access keys can be created at most 10" msgstr "" -#: authentication/api/confirm.py:41 +#: authentication/api/confirm.py:50 msgid "This action require verify your MFA" msgstr "该操作需要验证您的 MFA, 请先开启并配置" @@ -2523,23 +2539,23 @@ msgstr "该操作需要验证您的 MFA, 请先开启并配置" msgid "Reusable connection token is not allowed, global setting not enabled" msgstr "不允许使用可重复使用的连接令牌,未启用全局设置" -#: authentication/api/connection_token.py:358 +#: authentication/api/connection_token.py:375 msgid "Anonymous account is not supported for this asset" msgstr "匿名账号不支持当前资产" -#: authentication/api/connection_token.py:378 +#: authentication/api/connection_token.py:394 msgid "Account not found" msgstr "账号未找到" -#: authentication/api/connection_token.py:381 +#: authentication/api/connection_token.py:397 msgid "Permission expired" msgstr "授权已过期" -#: authentication/api/connection_token.py:412 +#: authentication/api/connection_token.py:427 msgid "ACL action is reject: {}({})" msgstr "ACL 动作是拒绝: {}({})" -#: authentication/api/connection_token.py:416 +#: authentication/api/connection_token.py:431 msgid "ACL action is review" msgstr "ACL 动作是复核" @@ -2597,11 +2613,11 @@ msgstr "无效的令牌头。符号字符串不应包含无效字符。" msgid "Invalid token or cache refreshed." msgstr "刷新的令牌或缓存无效。" -#: authentication/backends/passkey/api.py:26 +#: authentication/backends/passkey/api.py:37 msgid "Only register passkey for local user" msgstr "" -#: authentication/backends/passkey/api.py:54 +#: authentication/backends/passkey/api.py:65 msgid "Auth failed" msgstr "认证失败" @@ -2753,21 +2769,21 @@ msgstr "手机号没有设置" msgid "SSO auth closed" msgstr "SSO 认证关闭了" -#: authentication/errors/mfa.py:18 authentication/views/wecom.py:61 +#: authentication/errors/mfa.py:18 authentication/views/wecom.py:62 msgid "WeCom is already bound" msgstr "企业微信已经绑定" -#: authentication/errors/mfa.py:23 authentication/views/wecom.py:202 -#: authentication/views/wecom.py:244 +#: authentication/errors/mfa.py:23 authentication/views/wecom.py:204 +#: authentication/views/wecom.py:246 msgid "WeCom is not bound" msgstr "没有绑定企业微信" -#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:209 -#: authentication/views/dingtalk.py:251 +#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:211 +#: authentication/views/dingtalk.py:253 msgid "DingTalk is not bound" msgstr "钉钉没有绑定" -#: authentication/errors/mfa.py:33 authentication/views/feishu.py:166 +#: authentication/errors/mfa.py:33 authentication/views/feishu.py:168 msgid "FeiShu is not bound" msgstr "没有绑定飞书" @@ -2885,7 +2901,7 @@ msgstr "设置手机号码启用" msgid "Clear phone number to disable" msgstr "清空手机号码禁用" -#: authentication/middleware.py:93 settings/utils/ldap.py:679 +#: authentication/middleware.py:94 settings/utils/ldap.py:679 msgid "Authentication failed (before login check failed): {}" msgstr "认证失败 (登录前检查失败): {}" @@ -3059,7 +3075,7 @@ msgstr "邮箱" msgid "The {} cannot be empty" msgstr "{} 不能为空" -#: authentication/serializers/token.py:80 perms/serializers/permission.py:37 +#: authentication/serializers/token.py:86 perms/serializers/permission.py:37 #: perms/serializers/permission.py:58 users/serializers/user.py:98 #: users/serializers/user.py:168 msgid "Is valid" @@ -3102,12 +3118,6 @@ msgstr "启用" msgid "Delete success" msgstr "删除成功" -#: authentication/templates/authentication/_access_key_modal.html:155 -#: authentication/templates/authentication/_mfa_confirm_modal.html:53 -#: templates/_modal.html:22 tickets/const.py:44 -msgid "Close" -msgstr "关闭" - #: authentication/templates/authentication/_captcha_field.html:8 msgid "Play CAPTCHA as audio file" msgstr "语言播放验证码" @@ -3307,69 +3317,69 @@ msgstr "局域网" msgid "If you have any question, please contact the administrator" msgstr "如果有疑问或需求,请联系系统管理员" -#: authentication/views/dingtalk.py:41 +#: authentication/views/dingtalk.py:42 msgid "DingTalk Error, Please contact your system administrator" msgstr "钉钉错误,请联系系统管理员" -#: authentication/views/dingtalk.py:44 authentication/views/dingtalk.py:208 +#: authentication/views/dingtalk.py:45 authentication/views/dingtalk.py:210 msgid "DingTalk Error" msgstr "钉钉错误" -#: authentication/views/dingtalk.py:56 authentication/views/feishu.py:50 -#: authentication/views/wecom.py:57 +#: authentication/views/dingtalk.py:57 authentication/views/feishu.py:51 +#: authentication/views/wecom.py:58 msgid "" "The system configuration is incorrect. Please contact your administrator" msgstr "企业配置错误,请联系系统管理员" -#: authentication/views/dingtalk.py:60 +#: authentication/views/dingtalk.py:61 msgid "DingTalk is already bound" msgstr "钉钉已经绑定" -#: authentication/views/dingtalk.py:128 authentication/views/wecom.py:129 +#: authentication/views/dingtalk.py:129 authentication/views/wecom.py:130 msgid "Invalid user_id" msgstr "无效的 user_id" -#: authentication/views/dingtalk.py:144 +#: authentication/views/dingtalk.py:145 msgid "DingTalk query user failed" msgstr "钉钉查询用户失败" -#: authentication/views/dingtalk.py:153 +#: authentication/views/dingtalk.py:154 msgid "The DingTalk is already bound to another user" msgstr "该钉钉已经绑定其他用户" -#: authentication/views/dingtalk.py:160 +#: authentication/views/dingtalk.py:161 msgid "Binding DingTalk successfully" msgstr "绑定 钉钉 成功" -#: authentication/views/dingtalk.py:210 authentication/views/dingtalk.py:245 +#: authentication/views/dingtalk.py:212 authentication/views/dingtalk.py:247 msgid "Failed to get user from DingTalk" msgstr "从钉钉获取用户失败" -#: authentication/views/dingtalk.py:252 +#: authentication/views/dingtalk.py:254 msgid "Please login with a password and then bind the DingTalk" msgstr "请使用密码登录,然后绑定钉钉" -#: authentication/views/feishu.py:38 authentication/views/feishu.py:165 +#: authentication/views/feishu.py:39 authentication/views/feishu.py:167 msgid "FeiShu Error" msgstr "飞书错误" -#: authentication/views/feishu.py:66 +#: authentication/views/feishu.py:67 msgid "FeiShu is already bound" msgstr "飞书已经绑定" -#: authentication/views/feishu.py:107 +#: authentication/views/feishu.py:108 msgid "FeiShu query user failed" msgstr "飞书查询用户失败" -#: authentication/views/feishu.py:116 +#: authentication/views/feishu.py:117 msgid "The FeiShu is already bound to another user" msgstr "该飞书已经绑定其他用户" -#: authentication/views/feishu.py:123 +#: authentication/views/feishu.py:124 msgid "Binding FeiShu successfully" msgstr "绑定 飞书 成功" -#: authentication/views/feishu.py:167 +#: authentication/views/feishu.py:169 msgid "Failed to get user from FeiShu" msgstr "从飞书获取用户失败" @@ -3409,31 +3419,31 @@ msgstr "退出登录成功" msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: authentication/views/wecom.py:42 +#: authentication/views/wecom.py:43 msgid "WeCom Error, Please contact your system administrator" msgstr "企业微信错误,请联系系统管理员" -#: authentication/views/wecom.py:45 authentication/views/wecom.py:201 +#: authentication/views/wecom.py:46 authentication/views/wecom.py:203 msgid "WeCom Error" msgstr "企业微信错误" -#: authentication/views/wecom.py:144 +#: authentication/views/wecom.py:145 msgid "WeCom query user failed" msgstr "企业微信查询用户失败" -#: authentication/views/wecom.py:153 +#: authentication/views/wecom.py:154 msgid "The WeCom is already bound to another user" msgstr "该企业微信已经绑定其他用户" -#: authentication/views/wecom.py:160 +#: authentication/views/wecom.py:161 msgid "Binding WeCom successfully" msgstr "绑定 企业微信 成功" -#: authentication/views/wecom.py:203 authentication/views/wecom.py:238 +#: authentication/views/wecom.py:205 authentication/views/wecom.py:240 msgid "Failed to get user from WeCom" msgstr "从企业微信获取用户失败" -#: authentication/views/wecom.py:245 +#: authentication/views/wecom.py:247 msgid "Please login with a password and then bind the WeCom" msgstr "请使用密码登录,然后绑定企业微信" @@ -3605,11 +3615,11 @@ msgstr "多对多反向是不被允许的" msgid "Is referenced by other objects and cannot be deleted" msgstr "被其他对象关联,不能删除" -#: common/exceptions.py:48 +#: common/exceptions.py:51 msgid "This action require confirm current user" msgstr "此操作需要确认当前用户" -#: common/exceptions.py:56 +#: common/exceptions.py:59 msgid "Unexpect error occur" msgstr "发生意外错误" @@ -4221,7 +4231,7 @@ msgstr "LDAP 同步设置组织为当前组织,请切换其他组织后再进 msgid "The organization have resource ({}) cannot be deleted" msgstr "组织存在资源 ({}) 不能被删除" -#: orgs/apps.py:7 rbac/tree.py:123 +#: orgs/apps.py:7 rbac/tree.py:125 msgid "App organizations" msgstr "组织管理" @@ -4547,25 +4557,25 @@ msgid "My assets" msgstr "我的资产" #: rbac/tree.py:56 terminal/models/applet/applet.py:52 -#: terminal/models/applet/applet.py:298 terminal/models/applet/host.py:29 +#: terminal/models/applet/applet.py:300 terminal/models/applet/host.py:29 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "远程应用" -#: rbac/tree.py:124 +#: rbac/tree.py:126 msgid "Ticket comment" msgstr "工单评论" -#: rbac/tree.py:125 settings/serializers/feature.py:58 +#: rbac/tree.py:127 settings/serializers/feature.py:58 #: tickets/models/ticket/general.py:307 msgid "Ticket" msgstr "工单管理" -#: rbac/tree.py:126 +#: rbac/tree.py:128 msgid "Common setting" msgstr "一般设置" -#: rbac/tree.py:127 +#: rbac/tree.py:129 msgid "View permission tree" msgstr "查看授权树" @@ -4623,38 +4633,50 @@ msgid "Can change auth setting" msgstr "认证设置" #: settings/models.py:162 +msgid "Can change auth ops" +msgstr "任务中心设置" + +#: settings/models.py:163 +msgid "Can change auth ticket" +msgstr "工单设置" + +#: settings/models.py:164 +msgid "Can change auth announcement" +msgstr "公告设置" + +#: settings/models.py:165 msgid "Can change vault setting" msgstr "可以更改 vault 设置" -#: settings/models.py:163 +#: settings/models.py:166 msgid "Can change system msg sub setting" msgstr "消息订阅设置" -#: settings/models.py:164 +#: settings/models.py:167 msgid "Can change sms setting" msgstr "短信设置" -#: settings/models.py:165 +#: settings/models.py:168 msgid "Can change security setting" msgstr "安全设置" -#: settings/models.py:166 +#: settings/models.py:169 msgid "Can change clean setting" msgstr "定期清理" -#: settings/models.py:167 +#: settings/models.py:170 msgid "Can change interface setting" msgstr "界面设置" -#: settings/models.py:168 +#: settings/models.py:171 msgid "Can change license setting" msgstr "许可证设置" -#: settings/models.py:169 +#: settings/models.py:172 msgid "Can change terminal setting" msgstr "终端设置" -#: settings/models.py:170 +#: settings/models.py:173 msgid "Can change other setting" msgstr "其它设置" @@ -6020,7 +6042,7 @@ msgstr "命令存储" msgid "Invalid" msgstr "无效" -#: terminal/api/component/storage.py:119 terminal/tasks.py:141 +#: terminal/api/component/storage.py:119 terminal/tasks.py:142 msgid "Test failure: {}" msgstr "测试失败: {}" @@ -6029,7 +6051,7 @@ msgid "Test successful" msgstr "测试成功" #: terminal/api/component/storage.py:124 terminal/notifications.py:240 -#: terminal/tasks.py:145 +#: terminal/tasks.py:146 msgid "Test failure: Account invalid" msgstr "测试失败: 账号无效" @@ -6224,7 +6246,7 @@ msgstr "只支持自定义平台" msgid "Missing type in platform.yml" msgstr "在 platform.yml 中缺少类型" -#: terminal/models/applet/applet.py:300 terminal/models/applet/host.py:35 +#: terminal/models/applet/applet.py:302 terminal/models/applet/host.py:35 #: terminal/models/applet/host.py:137 msgid "Hosting" msgstr "宿主机" @@ -6778,23 +6800,23 @@ msgstr "周期清理终端状态" msgid "Clean orphan session" msgstr "清除离线会话" -#: terminal/tasks.py:61 +#: terminal/tasks.py:62 msgid "Upload session replay to external storage" msgstr "上传会话录像到外部存储" -#: terminal/tasks.py:90 +#: terminal/tasks.py:91 msgid "Run applet host deployment" msgstr "运行应用机部署" -#: terminal/tasks.py:100 +#: terminal/tasks.py:101 msgid "Install applet" msgstr "安装应用" -#: terminal/tasks.py:111 +#: terminal/tasks.py:112 msgid "Generate applet host accounts" msgstr "收集远程应用上的账号" -#: terminal/tasks.py:123 +#: terminal/tasks.py:124 msgid "Check command replay storage connectivity" msgstr "检查命令及录像存储可连接性 " @@ -6821,6 +6843,10 @@ msgstr "没有端口可以使用,检查并修改配置文件中 Magnus 监听 msgid "All available port count: {}, Already use port count: {}" msgstr "所有可用端口数量:{},已使用端口数量:{}" +#: tickets/api/ticket.py:88 tickets/models/ticket/general.py:288 +msgid "Applicant" +msgstr "申请人" + #: tickets/apps.py:7 msgid "Tickets" msgstr "工单管理" @@ -6849,10 +6875,6 @@ msgstr "已拒绝" msgid "Closed" msgstr "关闭的" -#: tickets/const.py:46 -msgid "Approve" -msgstr "同意" - #: tickets/const.py:50 msgid "One level" msgstr "1 级" @@ -6983,6 +7005,10 @@ msgstr "运行的命令" msgid "Command filter acl" msgstr "命令过滤器" +#: tickets/models/ticket/command_confirm.py:23 +msgid "Apply Command Ticket" +msgstr "命令复核工单" + #: tickets/models/ticket/general.py:76 msgid "Ticket step" msgstr "工单步骤" @@ -6995,10 +7021,6 @@ msgstr "工单受理人" msgid "Title" msgstr "标题" -#: tickets/models/ticket/general.py:288 -msgid "Applicant" -msgstr "申请人" - #: tickets/models/ticket/general.py:292 msgid "TicketFlow" msgstr "工单流程" @@ -7031,10 +7053,18 @@ msgstr "登录资产" msgid "Login account" msgstr "登录账号" +#: tickets/models/ticket/login_asset_confirm.py:27 +msgid "Apply Login Asset Ticket" +msgstr "资产登录复核工单" + #: tickets/models/ticket/login_confirm.py:12 msgid "Login datetime" msgstr "登录日期" +#: tickets/models/ticket/login_confirm.py:15 +msgid "Apply Login Ticket" +msgstr "用户登录复核工单" + #: tickets/notifications.py:63 msgid "Ticket basic info" msgstr "工单基本信息" @@ -7121,36 +7151,32 @@ msgid "Ticket information" msgstr "工单信息" #: tickets/templates/tickets/approve_check_password.html:29 -#: tickets/views/approve.py:40 +#: tickets/views/approve.py:40 tickets/views/approve.py:77 msgid "Ticket approval" msgstr "工单审批" -#: tickets/templates/tickets/approve_check_password.html:45 +#: tickets/templates/tickets/approve_check_password.html:44 msgid "Approval" msgstr "同意" -#: tickets/templates/tickets/approve_check_password.html:54 -msgid "Go Login" -msgstr "去登录" - #: tickets/views/approve.py:41 msgid "" "This ticket does not exist, the process has ended, or this link has expired" msgstr "工单不存在,或者工单流程已经结束,或者此链接已经过期" -#: tickets/views/approve.py:70 +#: tickets/views/approve.py:69 msgid "Click the button below to approve or reject" msgstr "点击下方按钮同意或者拒绝" -#: tickets/views/approve.py:72 +#: tickets/views/approve.py:78 msgid "After successful authentication, this ticket can be approved directly" msgstr "认证成功后,工单可直接审批" -#: tickets/views/approve.py:97 +#: tickets/views/approve.py:95 msgid "Illegal approval action" msgstr "无效的审批动作" -#: tickets/views/approve.py:110 +#: tickets/views/approve.py:108 msgid "This user is not authorized to approve this ticket" msgstr "此用户无权审批此工单" @@ -7937,7 +7963,7 @@ msgstr "已同步" msgid "Released" msgstr "已释放" -#: xpack/plugins/cloud/manager.py:53 +#: xpack/plugins/cloud/manager.py:54 msgid "Account unavailable" msgstr "账号无效" @@ -8030,14 +8056,12 @@ msgid "Task strategy" msgstr "任务策略" #: xpack/plugins/cloud/models.py:285 -msgid "Exact" -msgstr "" +msgid "Equal" +msgstr "等于" #: xpack/plugins/cloud/models.py:286 -#, fuzzy -#| msgid "No" -msgid "Not" -msgstr "否" +msgid "Not Equal" +msgstr "不等于" #: xpack/plugins/cloud/models.py:287 msgid "In" @@ -8446,7 +8470,7 @@ msgstr "许可证导入成功" msgid "License is invalid" msgstr "无效的许可证" -#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:140 +#: xpack/plugins/license/meta.py:10 xpack/plugins/license/models.py:141 msgid "License" msgstr "许可证" @@ -8465,9 +8489,3 @@ msgstr "企业专业版" #: xpack/plugins/license/models.py:86 msgid "Ultimate edition" msgstr "企业旗舰版" - -#~ msgid "Equal" -#~ msgstr "等于" - -#~ msgid "Not equal" -#~ msgstr "不等于" diff --git a/apps/tickets/models/ticket/command_confirm.py b/apps/tickets/models/ticket/command_confirm.py index f261bb147..62103eb1e 100644 --- a/apps/tickets/models/ticket/command_confirm.py +++ b/apps/tickets/models/ticket/command_confirm.py @@ -18,3 +18,6 @@ class ApplyCommandTicket(Ticket): 'acls.CommandFilterACL', on_delete=models.SET_NULL, null=True, verbose_name=_('Command filter acl') ) + + class Meta: + verbose_name = _('Apply Command Ticket') diff --git a/apps/tickets/models/ticket/login_asset_confirm.py b/apps/tickets/models/ticket/login_asset_confirm.py index ffcbf869e..ce2f7f6fd 100644 --- a/apps/tickets/models/ticket/login_asset_confirm.py +++ b/apps/tickets/models/ticket/login_asset_confirm.py @@ -22,3 +22,6 @@ class ApplyLoginAssetTicket(Ticket): return self.connection_token.is_active = True self.connection_token.save() + + class Meta: + verbose_name = _('Apply Login Asset Ticket') diff --git a/apps/tickets/models/ticket/login_confirm.py b/apps/tickets/models/ticket/login_confirm.py index 024761ac7..7b17dd1f8 100644 --- a/apps/tickets/models/ticket/login_confirm.py +++ b/apps/tickets/models/ticket/login_confirm.py @@ -10,3 +10,6 @@ class ApplyLoginTicket(Ticket): apply_login_ip = models.GenericIPAddressField(verbose_name=_('Login IP'), null=True) apply_login_city = models.CharField(max_length=64, verbose_name=_('Login city'), null=True) apply_login_datetime = models.DateTimeField(verbose_name=_('Login datetime'), null=True) + + class Meta: + verbose_name = _('Apply Login Ticket')