From 14e572813fa614159787c0fea32afd9156223cdd Mon Sep 17 00:00:00 2001 From: Bai Date: Fri, 21 Apr 2023 11:20:51 +0800 Subject: [PATCH 01/88] fix: util add jobauditlog --- utils/clean_db_content_types.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/clean_db_content_types.py b/utils/clean_db_content_types.py index 646a9d051..d67436091 100644 --- a/utils/clean_db_content_types.py +++ b/utils/clean_db_content_types.py @@ -85,6 +85,11 @@ def clean_db_content_types(): ('assets', 'systemuser', 'view_systemuserasset'), ('assets', 'systemuser', 'add_systemuserasset'), ('assets', 'systemuser', 'remove_systemuserasset'), + + ('ops', 'jobauditlog', 'view_jobauditlog'), + ('ops', 'jobauditlog', 'add_jobauditlog'), + ('ops', 'jobauditlog', 'change_jobauditlog'), + ('ops', 'jobauditlog', 'delete_jobauditlog'), ] for app, model, codename in permissions_delete_required: print('delete {}.{} ({})'.format(app, codename, model)) From 06052b85a2f6f59ae0391032e1922d5ec387bccb Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 21 Apr 2023 11:18:04 +0800 Subject: [PATCH 02/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20=E8=87=AA=E5=AE=9A=E4=B9=89=20applet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit perf: 优化平台 --- apps/assets/api/mixin.py | 16 ++++++++++++++-- apps/assets/const/types.py | 13 ++++++++----- apps/assets/serializers/platform.py | 11 ++--------- apps/common/serializers/dynamic.py | 3 +++ apps/locale/zh/LC_MESSAGES/django.mo | 4 ++-- apps/locale/zh/LC_MESSAGES/django.po | 2 +- apps/terminal/models/applet/applet.py | 2 +- 7 files changed, 31 insertions(+), 20 deletions(-) diff --git a/apps/assets/api/mixin.py b/apps/assets/api/mixin.py index d7cb90121..13ae0f283 100644 --- a/apps/assets/api/mixin.py +++ b/apps/assets/api/mixin.py @@ -57,11 +57,23 @@ class SerializeToTreeNodeMixin: ] return data + @lazyproperty + def support_types(self): + from assets.const import AllTypes + return AllTypes.get_types_values(exclude_custom=True) + + def get_icon(self, asset): + if asset.type in self.support_types: + return asset.type + else: + return 'file' + @timeit def serialize_assets(self, assets, node_key=None): sftp_enabled_platform = PlatformProtocol.objects \ .filter(name='ssh', setting__sftp_enabled=True) \ - .values_list('platform', flat=True).distinct() + .values_list('platform', flat=True) \ + .distinct() if node_key is None: get_pid = lambda asset: getattr(asset, 'parent_key', '') else: @@ -75,7 +87,7 @@ class SerializeToTreeNodeMixin: 'pId': get_pid(asset), 'isParent': False, 'open': False, - 'iconSkin': asset.type, + 'iconSkin': self.get_icon(asset), 'chkDisabled': not asset.is_active, 'meta': { 'type': 'asset', diff --git a/apps/assets/const/types.py b/apps/assets/const/types.py index ad26968a5..8194c5c14 100644 --- a/apps/assets/const/types.py +++ b/apps/assets/const/types.py @@ -151,15 +151,18 @@ class AllTypes(ChoicesMixin): ) @classmethod - def get_types(cls): + def get_types(cls, exclude_custom=False): choices = [] - for i in dict(cls.category_types()).values(): - choices.extend(i.get_types()) + + for name, tp in dict(cls.category_types()).items(): + if name == Category.CUSTOM and exclude_custom: + continue + choices.extend(tp.get_types()) return choices @classmethod - def get_types_values(cls): - choices = cls.get_types() + def get_types_values(cls, exclude_custom=False): + choices = cls.get_types(exclude_custom=exclude_custom) return [c.value for c in choices] @staticmethod diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index b010a6ed0..3313168cc 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from assets.const.web import FillType -from common.serializers import WritableNestedModelSerializer +from common.serializers import WritableNestedModelSerializer, type_field_map from common.serializers.fields import LabeledChoiceField from common.utils import lazyproperty from ..const import Category, AllTypes @@ -88,14 +88,7 @@ class PlatformProtocolSerializer(serializers.ModelSerializer): class PlatformCustomField(serializers.Serializer): - TYPE_CHOICES = [ - ("str", "str"), - ("text", "text"), - ("int", "int"), - ("bool", "bool"), - ("choice", "choice"), - ("list", "list"), - ] + TYPE_CHOICES = [(t, t) for t, c in type_field_map.items()] name = serializers.CharField(label=_("Name"), max_length=128) label = serializers.CharField(label=_("Label"), max_length=128) type = serializers.ChoiceField(choices=TYPE_CHOICES, label=_("Type"), default='str') diff --git a/apps/common/serializers/dynamic.py b/apps/common/serializers/dynamic.py index 9ab26b9bb..2a8ffe0c0 100644 --- a/apps/common/serializers/dynamic.py +++ b/apps/common/serializers/dynamic.py @@ -7,6 +7,7 @@ example_info = [ type_field_map = { "str": serializers.CharField, + "password": serializers.CharField, "int": serializers.IntegerField, "bool": serializers.BooleanField, "text": serializers.CharField, @@ -27,6 +28,8 @@ def set_default_if_need(data, i): def set_default_by_type(tp, data, field_info): if tp == 'str': data['max_length'] = 4096 + elif tp == 'password': + data['write_only'] = True elif tp == 'choice': choices = field_info.pop('choices', []) if isinstance(choices, str): diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index a4ec5564d..20231b06d 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:2dd0610d610c2660f35d50dc2871ac08cc09080d2503e1080a57d97c47fea471 -size 114418 +oid sha256:591b458d6f8ea8d125bd584ca57768cd5aa5a7103b42e345eaadac744a73d475 +size 114412 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 4d3396700..fce65d116 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -1060,7 +1060,7 @@ msgstr "Web" #: assets/const/category.py:15 msgid "Custom type" -msgstr "自定义类型" +msgstr "自定义" #: assets/const/cloud.py:7 msgid "Public cloud" diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index 786bc35de..c7296eda6 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -98,7 +98,7 @@ class Applet(JMSBaseModel): return try: with open(os.path.join(d, 'platform.yml')) as f: - data = yaml.safe_load(f) + data = yaml_load_with_i18n(f) except Exception as e: raise ValidationError({'error': _('Load platform.yml failed: {}').format(e)}) From eb9ac213d585f7f5208f66f231707815bc898ec8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 20 Apr 2023 22:32:22 +0800 Subject: [PATCH 03/88] =?UTF-8?q?perf:=20=E5=8E=BB=E6=8E=89=20debug=20msg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/models/automations/change_secret.py | 1 - apps/assets/api/category.py | 3 ++- apps/assets/automations/methods.py | 1 - apps/perms/serializers/permission.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/accounts/models/automations/change_secret.py b/apps/accounts/models/automations/change_secret.py index d4ad77608..2226040a2 100644 --- a/apps/accounts/models/automations/change_secret.py +++ b/apps/accounts/models/automations/change_secret.py @@ -28,7 +28,6 @@ class ChangeSecretMixin(models.Model): default=SSHKeyStrategy.add, verbose_name=_('SSH key change strategy') ) - accounts: list[str] # account usernames get_all_assets: callable # get all assets class Meta: diff --git a/apps/assets/api/category.py b/apps/assets/api/category.py index 17deba4d3..8c14e352d 100644 --- a/apps/assets/api/category.py +++ b/apps/assets/api/category.py @@ -3,6 +3,7 @@ from rest_framework.decorators import action from rest_framework.response import Response from common.api import JMSGenericViewSet +from common.permissions import IsValidUser from assets.serializers import CategorySerializer, TypeSerializer from assets.const import AllTypes @@ -14,7 +15,7 @@ class CategoryViewSet(ListModelMixin, JMSGenericViewSet): 'default': CategorySerializer, 'types': TypeSerializer } - permission_classes = () + permission_classes = (IsValidUser,) def get_queryset(self): return AllTypes.categories() diff --git a/apps/assets/automations/methods.py b/apps/assets/automations/methods.py index 5b80bb547..332923534 100644 --- a/apps/assets/automations/methods.py +++ b/apps/assets/automations/methods.py @@ -40,7 +40,6 @@ def get_platform_automation_methods(path): continue with open(path, 'r') as f: - print("path: ", path) manifest = yaml_load_with_i18n(f) check_platform_method(manifest, path) manifest['dir'] = os.path.dirname(path) diff --git a/apps/perms/serializers/permission.py b/apps/perms/serializers/permission.py index 5fc19be29..0e5f3b80a 100644 --- a/apps/perms/serializers/permission.py +++ b/apps/perms/serializers/permission.py @@ -123,7 +123,7 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer): for i in range(0, len(account_ids), slice_count): push_accounts_to_assets_task.delay(account_ids[i:i + slice_count]) - def validate_accounts(self, usernames: list[str]): + def validate_accounts(self, usernames): template_ids = [] account_usernames = [] for username in usernames: From ede53d3b6bd97742f61397e344b34aebc7aabcf8 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 21 Apr 2023 14:08:59 +0800 Subject: [PATCH 04/88] perf: ssh key strategy translate (#10295) Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/const/automation.py | 2 +- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 108 +++++++++++++-------------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 105 +++++++++++++------------- apps/ops/const.py | 2 +- 6 files changed, 112 insertions(+), 113 deletions(-) diff --git a/apps/accounts/const/automation.py b/apps/accounts/const/automation.py index 5671f50f5..791527078 100644 --- a/apps/accounts/const/automation.py +++ b/apps/accounts/const/automation.py @@ -48,7 +48,7 @@ class SecretStrategy(models.TextChoices): class SSHKeyStrategy(models.TextChoices): add = 'add', _('Append SSH KEY') set = 'set', _('Empty and append SSH KEY') - set_jms = 'set_jms', _('Replace (The key generated by JumpServer) ') + set_jms = 'set_jms', _('Replace (Replace only keys pushed by JumpServer) ') class TriggerChoice(models.TextChoices, TreeChoices): diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index b55f21643..7d2f91c61 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:a4ef9ccfeccf8f45c8753bc901ff6efe970486565c3bcf2d46042657ffa49f42 -size 139618 +oid sha256:24858bf247f7af58abda5adb5be733b7b995df2e26de7c91caf43f7aa0dd3be0 +size 139654 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 1aee33de2..c113ce8ee 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-04-20 17:31+0800\n" +"POT-Creation-Date: 2023-04-21 14:05+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -128,8 +128,8 @@ msgid "Empty and append SSH KEY" msgstr "すべてクリアして追加" #: accounts/const/automation.py:51 ops/const.py:15 -msgid "Replace (The key generated by JumpServer) " -msgstr "置換(JumpServerによって生成された鍵)" +msgid "Replace (Replace only keys pushed by JumpServer) " +msgstr "置換(JumpServer によってプッシュされたキーのみを置換)" #: accounts/const/automation.py:56 msgid "On asset create" @@ -221,7 +221,7 @@ msgstr "ソース ID" #: accounts/serializers/automations/change_secret.py:113 #: accounts/serializers/automations/change_secret.py:133 #: acls/models/base.py:102 acls/serializers/base.py:57 -#: assets/serializers/asset/common.py:124 assets/serializers/gateway.py:28 +#: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 #: audits/models.py:49 ops/models/base.py:18 #: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 #: terminal/backends/command/models.py:21 terminal/models/session/session.py:34 @@ -262,7 +262,7 @@ msgid "Can change asset account template secret" msgstr "アセット アカウント テンプレートのパスワードを変更できます" #: accounts/models/automations/backup_account.py:27 -#: accounts/models/automations/change_secret.py:65 +#: accounts/models/automations/change_secret.py:64 #: accounts/serializers/account/backup.py:34 #: accounts/serializers/automations/change_secret.py:57 msgid "Recipient" @@ -363,7 +363,7 @@ msgid "Secret type" msgstr "鍵の種類" #: accounts/models/automations/change_secret.py:20 -#: accounts/models/automations/change_secret.py:90 accounts/models/base.py:38 +#: accounts/models/automations/change_secret.py:89 accounts/models/base.py:38 #: accounts/serializers/account/base.py:19 #: authentication/models/temp_token.py:10 #: authentication/templates/authentication/_access_key_modal.html:31 @@ -384,32 +384,32 @@ msgstr "パスワードルール" msgid "SSH key change strategy" msgstr "SSHキープッシュ方式" -#: accounts/models/automations/change_secret.py:72 +#: accounts/models/automations/change_secret.py:71 msgid "Change secret automation" msgstr "自動暗号化" -#: accounts/models/automations/change_secret.py:89 +#: accounts/models/automations/change_secret.py:88 msgid "Old secret" msgstr "以前のパスワード" -#: accounts/models/automations/change_secret.py:91 +#: accounts/models/automations/change_secret.py:90 msgid "Date started" msgstr "開始日" -#: accounts/models/automations/change_secret.py:92 +#: accounts/models/automations/change_secret.py:91 #: assets/models/automations/base.py:116 ops/models/base.py:56 #: ops/models/celery.py:64 ops/models/job.py:193 #: terminal/models/applet/host.py:110 msgid "Date finished" msgstr "終了日" -#: accounts/models/automations/change_secret.py:94 +#: accounts/models/automations/change_secret.py:93 #: accounts/serializers/account/account.py:234 assets/const/automation.py:8 #: common/const/choices.py:20 msgid "Error" msgstr "間違い" -#: accounts/models/automations/change_secret.py:98 +#: accounts/models/automations/change_secret.py:97 msgid "Change secret record" msgstr "パスワード レコードの変更" @@ -456,7 +456,7 @@ msgid "Triggers" msgstr "トリガー方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:81 -#: acls/serializers/base.py:81 acls/serializers/login_acl.py:25 +#: acls/serializers/base.py:81 acls/serializers/login_acl.py:26 #: assets/models/cmd_filter.py:81 audits/models.py:65 audits/serializers.py:82 #: authentication/serializers/connect_token_secret.py:108 #: authentication/templates/authentication/_access_key_modal.html:34 @@ -478,8 +478,8 @@ msgstr "アカウントの確認" #: assets/models/cmd_filter.py:21 assets/models/domain.py:18 #: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/platform.py:13 assets/models/platform.py:89 -#: assets/serializers/asset/common.py:144 assets/serializers/platform.py:99 -#: assets/serializers/platform.py:200 +#: assets/serializers/asset/common.py:145 assets/serializers/platform.py:92 +#: assets/serializers/platform.py:193 #: authentication/serializers/connect_token_secret.py:102 ops/mixin.py:21 #: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57 #: ops/models/job.py:92 ops/models/playbook.py:23 ops/serializers/job.py:20 @@ -557,8 +557,8 @@ msgstr "アカウントの存在ポリシー" #: accounts/serializers/account/account.py:179 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:90 -#: assets/serializers/asset/common.py:120 assets/serializers/cagegory.py:8 -#: assets/serializers/platform.py:117 assets/serializers/platform.py:201 +#: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 +#: assets/serializers/platform.py:110 assets/serializers/platform.py:194 #: perms/serializers/user_permission.py:26 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 msgid "Category" @@ -569,8 +569,8 @@ msgstr "カテゴリ" #: acls/serializers/command_acl.py:18 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:91 -#: assets/serializers/asset/common.py:121 assets/serializers/platform.py:101 -#: assets/serializers/platform.py:116 audits/serializers.py:48 +#: assets/serializers/asset/common.py:122 assets/serializers/platform.py:94 +#: assets/serializers/platform.py:109 audits/serializers.py:48 #: authentication/serializers/connect_token_secret.py:115 ops/models/job.py:103 #: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:31 #: terminal/models/component/storage.py:57 @@ -634,7 +634,7 @@ msgstr "ID" #: accounts/serializers/account/account.py:402 acls/models/base.py:98 #: acls/models/login_acl.py:13 acls/serializers/base.py:55 -#: acls/serializers/login_acl.py:21 assets/models/cmd_filter.py:24 +#: acls/serializers/login_acl.py:22 assets/models/cmd_filter.py:24 #: assets/models/label.py:16 audits/models.py:44 audits/models.py:63 #: audits/models.py:141 authentication/models/connection_token.py:30 #: authentication/models/sso_token.py:16 @@ -682,7 +682,7 @@ msgid "Key password" msgstr "キーパスワード" #: accounts/serializers/account/base.py:80 -#: assets/serializers/asset/common.py:304 +#: assets/serializers/asset/common.py:305 msgid "Spec info" msgstr "特別情報" @@ -796,7 +796,7 @@ msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" #: acls/models/base.py:82 acls/serializers/base.py:75 -#: acls/serializers/login_acl.py:23 assets/models/cmd_filter.py:86 +#: acls/serializers/login_acl.py:24 assets/models/cmd_filter.py:86 #: authentication/serializers/connect_token_secret.py:80 msgid "Reviewers" msgstr "レビュー担当者" @@ -852,7 +852,7 @@ msgstr "コマンドフィルタリング" msgid "Command confirm" msgstr "コマンドの確認" -#: acls/models/login_acl.py:16 acls/serializers/login_acl.py:29 +#: acls/models/login_acl.py:16 acls/serializers/login_acl.py:30 msgid "Rule" msgstr "ルール" @@ -872,7 +872,7 @@ msgstr "ログインasset acl" msgid "Login asset confirm" msgstr "ログイン資産の確認" -#: acls/serializers/base.py:10 acls/serializers/login_acl.py:16 +#: acls/serializers/base.py:10 acls/serializers/login_acl.py:17 msgid "With * indicating a match all. " msgstr "* はすべて一致することを示します。" @@ -906,7 +906,7 @@ msgstr "資産(住所)" msgid "Account (username)" msgstr "アカウント (ユーザー名)" -#: acls/serializers/base.py:78 acls/serializers/login_acl.py:27 +#: acls/serializers/base.py:78 acls/serializers/login_acl.py:28 msgid "Reviewers amount" msgstr "承認者数" @@ -964,7 +964,7 @@ msgstr "アプリケーション" msgid "Can match application" msgstr "アプリケーションを一致させることができます" -#: assets/api/asset/asset.py:141 +#: assets/api/asset/asset.py:140 msgid "Cannot create asset directly, you should create a host or other" msgstr "" "資産を直接作成することはできません。ホストまたはその他を作成する必要がありま" @@ -1102,7 +1102,7 @@ msgstr "ファイアウォール" msgid "Other" msgstr "その他" -#: assets/const/types.py:215 +#: assets/const/types.py:218 msgid "All types" msgstr "いろんなタイプ" @@ -1249,7 +1249,7 @@ msgstr "クラウド サービス" msgid "Port" msgstr "ポート" -#: assets/models/asset/common.py:124 assets/serializers/asset/common.py:145 +#: assets/models/asset/common.py:124 assets/serializers/asset/common.py:146 msgid "Address" msgstr "アドレス" @@ -1270,13 +1270,12 @@ msgstr "ドメイン" msgid "Labels" msgstr "ラベル" -#: assets/models/asset/common.py:132 assets/serializers/asset/common.py:305 +#: assets/models/asset/common.py:132 assets/serializers/asset/common.py:306 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "資産ハードウェア情報の収集" #: assets/models/asset/common.py:133 assets/serializers/asset/custom.py:14 -#: assets/serializers/asset/custom.py:22 msgid "Custom info" msgstr "自动化信息" @@ -1338,7 +1337,7 @@ msgid "Submit selector" msgstr "ボタンセレクターを確認する" #: assets/models/automations/base.py:17 assets/models/cmd_filter.py:38 -#: assets/serializers/asset/common.py:303 rbac/tree.py:35 +#: assets/serializers/asset/common.py:304 rbac/tree.py:35 msgid "Accounts" msgstr "アカウント" @@ -1424,7 +1423,7 @@ msgid "Asset group" msgstr "資産グループ" #: assets/models/group.py:34 assets/models/platform.py:17 -#: assets/serializers/platform.py:102 +#: assets/serializers/platform.py:95 #: xpack/plugins/cloud/providers/nutanix.py:30 msgid "Default" msgstr "デフォルト" @@ -1445,9 +1444,9 @@ msgstr "システム" msgid "Value" msgstr "値" -#: assets/models/label.py:40 assets/serializers/asset/common.py:122 +#: assets/models/label.py:40 assets/serializers/asset/common.py:123 #: assets/serializers/cagegory.py:6 assets/serializers/cagegory.py:13 -#: assets/serializers/platform.py:100 +#: assets/serializers/platform.py:93 #: authentication/serializers/connect_token_secret.py:113 #: common/serializers/common.py:85 settings/serializers/sms.py:7 msgid "Label" @@ -1578,23 +1577,23 @@ msgstr "メタ" msgid "Internal" msgstr "ビルトイン" -#: assets/models/platform.py:97 assets/serializers/platform.py:115 +#: assets/models/platform.py:97 assets/serializers/platform.py:108 msgid "Charset" msgstr "シャーセット" -#: assets/models/platform.py:99 assets/serializers/platform.py:142 +#: assets/models/platform.py:99 assets/serializers/platform.py:135 msgid "Domain enabled" msgstr "ドメインを有効にする" -#: assets/models/platform.py:101 assets/serializers/platform.py:141 +#: assets/models/platform.py:101 assets/serializers/platform.py:134 msgid "Su enabled" msgstr "アカウントの切り替えを有効にする" -#: assets/models/platform.py:102 assets/serializers/platform.py:121 +#: assets/models/platform.py:102 assets/serializers/platform.py:114 msgid "Su method" msgstr "アカウントの切り替え方法" -#: assets/models/platform.py:103 assets/serializers/platform.py:124 +#: assets/models/platform.py:103 assets/serializers/platform.py:117 msgid "Custom fields" msgstr "カスタムフィールド" @@ -1611,32 +1610,32 @@ msgstr "" "プラットフォームタイプがスキップされた資産に合致しない、資産内の一括更新プ" "ラットフォーム" -#: assets/serializers/asset/common.py:123 assets/serializers/platform.py:118 +#: assets/serializers/asset/common.py:124 assets/serializers/platform.py:111 #: authentication/serializers/connect_token_secret.py:29 #: authentication/serializers/connect_token_secret.py:64 #: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:99 msgid "Protocols" msgstr "プロトコル" -#: assets/serializers/asset/common.py:125 -#: assets/serializers/asset/common.py:146 +#: assets/serializers/asset/common.py:126 +#: assets/serializers/asset/common.py:147 msgid "Node path" msgstr "ノードパスです" -#: assets/serializers/asset/common.py:143 -#: assets/serializers/asset/common.py:306 +#: assets/serializers/asset/common.py:144 +#: assets/serializers/asset/common.py:307 msgid "Auto info" msgstr "自動情報" -#: assets/serializers/asset/common.py:225 +#: assets/serializers/asset/common.py:226 msgid "Platform not exist" msgstr "プラットフォームが存在しません" -#: assets/serializers/asset/common.py:261 +#: assets/serializers/asset/common.py:262 msgid "port out of range (1-65535)" msgstr "ポート番号が範囲外です (1-65535)" -#: assets/serializers/asset/common.py:268 +#: assets/serializers/asset/common.py:269 msgid "Protocol is required: {}" msgstr "プロトコルが必要です: {}" @@ -1746,27 +1745,27 @@ msgstr "アカウント収集を有効にする" msgid "Gather accounts method" msgstr "アカウントの収集方法" -#: assets/serializers/platform.py:103 +#: assets/serializers/platform.py:96 msgid "Help text" msgstr "ヘルプ" -#: assets/serializers/platform.py:104 +#: assets/serializers/platform.py:97 msgid "Choices" msgstr "" -#: assets/serializers/platform.py:119 +#: assets/serializers/platform.py:112 msgid "Automation" msgstr "オートメーション" -#: assets/serializers/platform.py:143 +#: assets/serializers/platform.py:136 msgid "Default Domain" msgstr "デフォルト ドメイン" -#: assets/serializers/platform.py:152 +#: assets/serializers/platform.py:145 msgid "type is required" msgstr "タイプ このフィールドは必須です." -#: assets/serializers/platform.py:189 +#: assets/serializers/platform.py:182 msgid "Protocols is required" msgstr "同意が必要です" @@ -7474,7 +7473,8 @@ msgstr "" "IP範囲に一致するインスタンスのみが同期されます。
インスタンスに複数のIPア" "ドレスが含まれている場合、一致する最初のIPアドレスが作成されたアセットのIPと" "して使用されます。
デフォルト値の*は、すべてのインスタンスを同期し、IPア" -"ドレスをランダムに一致させることを意味します。
例:192.168.1.0/24,10.1.1.1-10.1.1.20" +"ドレスをランダムに一致させることを意味します。
例:" +"192.168.1.0/24,10.1.1.1-10.1.1.20" #: xpack/plugins/cloud/serializers/task.py:34 msgid "History count" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 20231b06d..521993edd 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:591b458d6f8ea8d125bd584ca57768cd5aa5a7103b42e345eaadac744a73d475 -size 114412 +oid sha256:0788b48bc50cffe3e7ff83803ef0edadfc120c9165bfe6bccd1f896d8bf39397 +size 114419 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index fce65d116..4683b46c6 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-04-20 17:31+0800\n" +"POT-Creation-Date: 2023-04-21 14:05+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -127,8 +127,8 @@ msgid "Empty and append SSH KEY" msgstr "清空所有并添加" #: accounts/const/automation.py:51 ops/const.py:15 -msgid "Replace (The key generated by JumpServer) " -msgstr "替换 (仅替换由 JumpServer 生成的密钥)" +msgid "Replace (Replace only keys pushed by JumpServer) " +msgstr "替换 (只替换由 JumpServer 推送的密钥)" #: accounts/const/automation.py:56 msgid "On asset create" @@ -220,7 +220,7 @@ msgstr "来源 ID" #: accounts/serializers/automations/change_secret.py:113 #: accounts/serializers/automations/change_secret.py:133 #: acls/models/base.py:102 acls/serializers/base.py:57 -#: assets/serializers/asset/common.py:124 assets/serializers/gateway.py:28 +#: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 #: audits/models.py:49 ops/models/base.py:18 #: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 #: terminal/backends/command/models.py:21 terminal/models/session/session.py:34 @@ -261,7 +261,7 @@ msgid "Can change asset account template secret" msgstr "可以更改资产账号模版密码" #: accounts/models/automations/backup_account.py:27 -#: accounts/models/automations/change_secret.py:65 +#: accounts/models/automations/change_secret.py:64 #: accounts/serializers/account/backup.py:34 #: accounts/serializers/automations/change_secret.py:57 msgid "Recipient" @@ -362,7 +362,7 @@ msgid "Secret type" msgstr "密文类型" #: accounts/models/automations/change_secret.py:20 -#: accounts/models/automations/change_secret.py:90 accounts/models/base.py:38 +#: accounts/models/automations/change_secret.py:89 accounts/models/base.py:38 #: accounts/serializers/account/base.py:19 #: authentication/models/temp_token.py:10 #: authentication/templates/authentication/_access_key_modal.html:31 @@ -383,32 +383,32 @@ msgstr "密码规则" msgid "SSH key change strategy" msgstr "SSH 密钥推送方式" -#: accounts/models/automations/change_secret.py:72 +#: accounts/models/automations/change_secret.py:71 msgid "Change secret automation" msgstr "自动化改密" -#: accounts/models/automations/change_secret.py:89 +#: accounts/models/automations/change_secret.py:88 msgid "Old secret" msgstr "原密码" -#: accounts/models/automations/change_secret.py:91 +#: accounts/models/automations/change_secret.py:90 msgid "Date started" msgstr "开始日期" -#: accounts/models/automations/change_secret.py:92 +#: accounts/models/automations/change_secret.py:91 #: assets/models/automations/base.py:116 ops/models/base.py:56 #: ops/models/celery.py:64 ops/models/job.py:193 #: terminal/models/applet/host.py:110 msgid "Date finished" msgstr "结束日期" -#: accounts/models/automations/change_secret.py:94 +#: accounts/models/automations/change_secret.py:93 #: accounts/serializers/account/account.py:234 assets/const/automation.py:8 #: common/const/choices.py:20 msgid "Error" msgstr "错误" -#: accounts/models/automations/change_secret.py:98 +#: accounts/models/automations/change_secret.py:97 msgid "Change secret record" msgstr "改密记录" @@ -455,7 +455,7 @@ msgid "Triggers" msgstr "触发方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:81 -#: acls/serializers/base.py:81 acls/serializers/login_acl.py:25 +#: acls/serializers/base.py:81 acls/serializers/login_acl.py:26 #: assets/models/cmd_filter.py:81 audits/models.py:65 audits/serializers.py:82 #: authentication/serializers/connect_token_secret.py:108 #: authentication/templates/authentication/_access_key_modal.html:34 @@ -477,8 +477,8 @@ msgstr "账号验证" #: assets/models/cmd_filter.py:21 assets/models/domain.py:18 #: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/platform.py:13 assets/models/platform.py:89 -#: assets/serializers/asset/common.py:144 assets/serializers/platform.py:99 -#: assets/serializers/platform.py:200 +#: assets/serializers/asset/common.py:145 assets/serializers/platform.py:92 +#: assets/serializers/platform.py:193 #: authentication/serializers/connect_token_secret.py:102 ops/mixin.py:21 #: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57 #: ops/models/job.py:92 ops/models/playbook.py:23 ops/serializers/job.py:20 @@ -553,8 +553,8 @@ msgstr "账号存在策略" #: accounts/serializers/account/account.py:179 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:90 -#: assets/serializers/asset/common.py:120 assets/serializers/cagegory.py:8 -#: assets/serializers/platform.py:117 assets/serializers/platform.py:201 +#: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 +#: assets/serializers/platform.py:110 assets/serializers/platform.py:194 #: perms/serializers/user_permission.py:26 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 msgid "Category" @@ -565,8 +565,8 @@ msgstr "类别" #: acls/serializers/command_acl.py:18 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:91 -#: assets/serializers/asset/common.py:121 assets/serializers/platform.py:101 -#: assets/serializers/platform.py:116 audits/serializers.py:48 +#: assets/serializers/asset/common.py:122 assets/serializers/platform.py:94 +#: assets/serializers/platform.py:109 audits/serializers.py:48 #: authentication/serializers/connect_token_secret.py:115 ops/models/job.py:103 #: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:31 #: terminal/models/component/storage.py:57 @@ -630,7 +630,7 @@ msgstr "ID" #: accounts/serializers/account/account.py:402 acls/models/base.py:98 #: acls/models/login_acl.py:13 acls/serializers/base.py:55 -#: acls/serializers/login_acl.py:21 assets/models/cmd_filter.py:24 +#: acls/serializers/login_acl.py:22 assets/models/cmd_filter.py:24 #: assets/models/label.py:16 audits/models.py:44 audits/models.py:63 #: audits/models.py:141 authentication/models/connection_token.py:30 #: authentication/models/sso_token.py:16 @@ -678,7 +678,7 @@ msgid "Key password" msgstr "密钥密码" #: accounts/serializers/account/base.py:80 -#: assets/serializers/asset/common.py:304 +#: assets/serializers/asset/common.py:305 msgid "Spec info" msgstr "特殊信息" @@ -792,7 +792,7 @@ msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" #: acls/models/base.py:82 acls/serializers/base.py:75 -#: acls/serializers/login_acl.py:23 assets/models/cmd_filter.py:86 +#: acls/serializers/login_acl.py:24 assets/models/cmd_filter.py:86 #: authentication/serializers/connect_token_secret.py:80 msgid "Reviewers" msgstr "审批人" @@ -848,7 +848,7 @@ msgstr "命令过滤" msgid "Command confirm" msgstr "命令复核" -#: acls/models/login_acl.py:16 acls/serializers/login_acl.py:29 +#: acls/models/login_acl.py:16 acls/serializers/login_acl.py:30 msgid "Rule" msgstr "规则" @@ -868,7 +868,7 @@ msgstr "登录资产访问控制" msgid "Login asset confirm" msgstr "登录资产复核" -#: acls/serializers/base.py:10 acls/serializers/login_acl.py:16 +#: acls/serializers/base.py:10 acls/serializers/login_acl.py:17 msgid "With * indicating a match all. " msgstr "* 表示匹配所有. " @@ -901,7 +901,7 @@ msgstr "资产(地址)" msgid "Account (username)" msgstr "账号(用户名)" -#: acls/serializers/base.py:78 acls/serializers/login_acl.py:27 +#: acls/serializers/base.py:78 acls/serializers/login_acl.py:28 msgid "Reviewers amount" msgstr "审批人数量" @@ -959,7 +959,7 @@ msgstr "应用程序" msgid "Can match application" msgstr "匹配应用" -#: assets/api/asset/asset.py:141 +#: assets/api/asset/asset.py:140 msgid "Cannot create asset directly, you should create a host or other" msgstr "不能直接创建资产, 你应该创建主机或其他资产" @@ -1095,7 +1095,7 @@ msgstr "防火墙" msgid "Other" msgstr "其它" -#: assets/const/types.py:215 +#: assets/const/types.py:218 msgid "All types" msgstr "所有类型" @@ -1242,7 +1242,7 @@ msgstr "云服务" msgid "Port" msgstr "端口" -#: assets/models/asset/common.py:124 assets/serializers/asset/common.py:145 +#: assets/models/asset/common.py:124 assets/serializers/asset/common.py:146 msgid "Address" msgstr "地址" @@ -1263,13 +1263,12 @@ msgstr "网域" msgid "Labels" msgstr "标签管理" -#: assets/models/asset/common.py:132 assets/serializers/asset/common.py:305 +#: assets/models/asset/common.py:132 assets/serializers/asset/common.py:306 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "收集资产硬件信息" #: assets/models/asset/common.py:133 assets/serializers/asset/custom.py:14 -#: assets/serializers/asset/custom.py:22 msgid "Custom info" msgstr "自动化信息" @@ -1331,7 +1330,7 @@ msgid "Submit selector" msgstr "确认按钮选择器" #: assets/models/automations/base.py:17 assets/models/cmd_filter.py:38 -#: assets/serializers/asset/common.py:303 rbac/tree.py:35 +#: assets/serializers/asset/common.py:304 rbac/tree.py:35 msgid "Accounts" msgstr "账号管理" @@ -1417,7 +1416,7 @@ msgid "Asset group" msgstr "资产组" #: assets/models/group.py:34 assets/models/platform.py:17 -#: assets/serializers/platform.py:102 +#: assets/serializers/platform.py:95 #: xpack/plugins/cloud/providers/nutanix.py:30 msgid "Default" msgstr "默认" @@ -1438,9 +1437,9 @@ msgstr "系统" msgid "Value" msgstr "值" -#: assets/models/label.py:40 assets/serializers/asset/common.py:122 +#: assets/models/label.py:40 assets/serializers/asset/common.py:123 #: assets/serializers/cagegory.py:6 assets/serializers/cagegory.py:13 -#: assets/serializers/platform.py:100 +#: assets/serializers/platform.py:93 #: authentication/serializers/connect_token_secret.py:113 #: common/serializers/common.py:85 settings/serializers/sms.py:7 msgid "Label" @@ -1571,23 +1570,23 @@ msgstr "元数据" msgid "Internal" msgstr "内置" -#: assets/models/platform.py:97 assets/serializers/platform.py:115 +#: assets/models/platform.py:97 assets/serializers/platform.py:108 msgid "Charset" msgstr "编码" -#: assets/models/platform.py:99 assets/serializers/platform.py:142 +#: assets/models/platform.py:99 assets/serializers/platform.py:135 msgid "Domain enabled" msgstr "启用网域" -#: assets/models/platform.py:101 assets/serializers/platform.py:141 +#: assets/models/platform.py:101 assets/serializers/platform.py:134 msgid "Su enabled" msgstr "启用账号切换" -#: assets/models/platform.py:102 assets/serializers/platform.py:121 +#: assets/models/platform.py:102 assets/serializers/platform.py:114 msgid "Su method" msgstr "账号切换方式" -#: assets/models/platform.py:103 assets/serializers/platform.py:124 +#: assets/models/platform.py:103 assets/serializers/platform.py:117 msgid "Custom fields" msgstr "自定义属性" @@ -1602,32 +1601,32 @@ msgid "" "type" msgstr "资产中批量更新平台,不符合平台类型跳过的资产" -#: assets/serializers/asset/common.py:123 assets/serializers/platform.py:118 +#: assets/serializers/asset/common.py:124 assets/serializers/platform.py:111 #: authentication/serializers/connect_token_secret.py:29 #: authentication/serializers/connect_token_secret.py:64 #: perms/serializers/user_permission.py:25 xpack/plugins/cloud/models.py:99 msgid "Protocols" msgstr "协议组" -#: assets/serializers/asset/common.py:125 -#: assets/serializers/asset/common.py:146 +#: assets/serializers/asset/common.py:126 +#: assets/serializers/asset/common.py:147 msgid "Node path" msgstr "节点路径" -#: assets/serializers/asset/common.py:143 -#: assets/serializers/asset/common.py:306 +#: assets/serializers/asset/common.py:144 +#: assets/serializers/asset/common.py:307 msgid "Auto info" msgstr "自动化信息" -#: assets/serializers/asset/common.py:225 +#: assets/serializers/asset/common.py:226 msgid "Platform not exist" msgstr "平台不存在" -#: assets/serializers/asset/common.py:261 +#: assets/serializers/asset/common.py:262 msgid "port out of range (1-65535)" msgstr "端口超出范围 (1-65535)" -#: assets/serializers/asset/common.py:268 +#: assets/serializers/asset/common.py:269 msgid "Protocol is required: {}" msgstr "协议是必填的: {}" @@ -1737,27 +1736,27 @@ msgstr "启用账号收集" msgid "Gather accounts method" msgstr "收集账号方式" -#: assets/serializers/platform.py:103 +#: assets/serializers/platform.py:96 msgid "Help text" msgstr "帮助" -#: assets/serializers/platform.py:104 +#: assets/serializers/platform.py:97 msgid "Choices" msgstr "" -#: assets/serializers/platform.py:119 +#: assets/serializers/platform.py:112 msgid "Automation" msgstr "自动化" -#: assets/serializers/platform.py:143 +#: assets/serializers/platform.py:136 msgid "Default Domain" msgstr "默认网域" -#: assets/serializers/platform.py:152 +#: assets/serializers/platform.py:145 msgid "type is required" msgstr "类型 该字段是必填项。" -#: assets/serializers/platform.py:189 +#: assets/serializers/platform.py:182 msgid "Protocols is required" msgstr "协议是必填的" diff --git a/apps/ops/const.py b/apps/ops/const.py index fb394b6bf..5f31bb00d 100644 --- a/apps/ops/const.py +++ b/apps/ops/const.py @@ -12,7 +12,7 @@ class StrategyChoice(models.TextChoices): class SSHKeyStrategy(models.TextChoices): add = 'add', _('Append SSH KEY') set = 'set', _('Empty and append SSH KEY') - set_jms = 'set_jms', _('Replace (The key generated by JumpServer) ') + set_jms = 'set_jms', _('Replace (Replace only keys pushed by JumpServer) ') class PasswordStrategy(models.TextChoices): From f85daa088f869b7d33b7cd1e5dedd855a1205cd5 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 21 Apr 2023 14:53:24 +0800 Subject: [PATCH 05/88] =?UTF-8?q?perf:=20=E5=88=9B=E5=BB=BA=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=20nodes=20=E5=8F=AF=E4=B8=BA=E7=A9=BA=20=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/asset/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 311749d61..0fd14eec9 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -145,6 +145,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali 'name': {'label': _("Name")}, 'address': {'label': _('Address')}, 'nodes_display': {'label': _('Node path')}, + 'nodes': {'allow_empty': True}, } def __init__(self, *args, **kwargs): From a6ab886968c7aa7a45787697bbee659fb2f61571 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 21 Apr 2023 15:12:05 +0800 Subject: [PATCH 06/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/const/custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/const/custom.py b/apps/assets/const/custom.py index 47d4e9229..9046a3069 100644 --- a/apps/assets/const/custom.py +++ b/apps/assets/const/custom.py @@ -8,7 +8,7 @@ class CustomTypes(BaseType): platforms = list(cls.get_custom_platforms()) except Exception: return [] - types = [p.type for p in platforms] + types = set([p.type for p in platforms]) return [(t, t) for t in types] @classmethod From f1ee4542548f4ce16c3282cef91c1cf04d5fefe0 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 21 Apr 2023 15:35:56 +0800 Subject: [PATCH 07/88] perf: user groups filter (#10300) Co-authored-by: feng <1304903146@qq.com> --- apps/users/filters.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/users/filters.py b/apps/users/filters.py index 7672826d4..1bcc1c683 100644 --- a/apps/users/filters.py +++ b/apps/users/filters.py @@ -10,11 +10,12 @@ from users.models.user import User class UserFilter(BaseFilterSet): system_roles = filters.CharFilter(method='filter_system_roles') org_roles = filters.CharFilter(method='filter_org_roles') + groups = filters.CharFilter(field_name="groups__name", lookup_expr='exact') class Meta: model = User fields = ( - 'id', 'username', 'email', 'name', 'source', + 'id', 'username', 'email', 'name', 'groups', 'source', 'org_roles', 'system_roles', 'is_active', ) From a105748a555e6c3b72ebc30e3e4260f6a73290b1 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 21 Apr 2023 16:58:40 +0800 Subject: [PATCH 08/88] =?UTF-8?q?perf:=20=E8=B4=A6=E5=8F=B7=E6=A8=A1?= =?UTF-8?q?=E7=89=88=20protocols=20=E8=BF=87=E6=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/api/account/template.py | 10 ++++++---- apps/assets/const/custom.py | 6 +----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/apps/accounts/api/account/template.py b/apps/accounts/api/account/template.py index 3bbe902ae..11675368f 100644 --- a/apps/accounts/api/account/template.py +++ b/apps/accounts/api/account/template.py @@ -1,13 +1,13 @@ from django_filters import rest_framework as drf_filters -from assets.const import Protocol from accounts import serializers from accounts.models import AccountTemplate -from orgs.mixins.api import OrgBulkModelViewSet -from rbac.permissions import RBACPermission +from assets.const import Protocol +from common.drf.filters import BaseFilterSet from common.permissions import UserConfirmation, ConfirmType from common.views.mixins import RecordViewLogMixin -from common.drf.filters import BaseFilterSet +from orgs.mixins.api import OrgBulkModelViewSet +from rbac.permissions import RBACPermission class AccountTemplateFilterSet(BaseFilterSet): @@ -27,6 +27,8 @@ class AccountTemplateFilterSet(BaseFilterSet): continue _st = protocol_secret_type_map[p].get('secret_types', []) secret_types.update(_st) + if not secret_types: + secret_types = ['password'] queryset = queryset.filter(secret_type__in=secret_types) return queryset diff --git a/apps/assets/const/custom.py b/apps/assets/const/custom.py index 9046a3069..5c72127b3 100644 --- a/apps/assets/const/custom.py +++ b/apps/assets/const/custom.py @@ -48,11 +48,7 @@ class CustomTypes(BaseType): @classmethod def internal_platforms(cls): - return { - # cls.PUBLIC: [], - # cls.PRIVATE: [{'name': 'Vmware-vSphere'}], - # cls.K8S: [{'name': 'Kubernetes'}], - } + return {} @classmethod def get_custom_platforms(cls): From a1f65bccc5170f672c3bfdd3043c7896fa774ade Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 21 Apr 2023 17:31:39 +0800 Subject: [PATCH 09/88] =?UTF-8?q?feat:=20=E5=8F=AA=E6=9C=89=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=AE=A1=E7=90=86=E5=91=98=E6=89=8D=E8=83=BD=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=88=96=E5=88=A0=E9=99=A4=E7=B3=BB=E7=BB=9F=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=91=98=20(#10306)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/users/api/user.py | 3 +++ apps/users/permissions.py | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/users/api/user.py b/apps/users/api/user.py index d1eee8083..6c8b5c8c9 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -12,12 +12,14 @@ from common.api import SuggestionMixin from common.utils import get_logger from orgs.utils import current_org, tmp_to_root_org from rbac.models import Role, RoleBinding +from rbac.permissions import RBACPermission from users.utils import LoginBlockUtil, MFABlockUtils from .mixins import UserQuerysetMixin from .. import serializers from ..filters import UserFilter from ..models import User from ..notifications import ResetMFAMsg +from ..permissions import UserObjectPermission from ..serializers import ( UserSerializer, MiniUserSerializer, InviteSerializer @@ -34,6 +36,7 @@ __all__ = [ class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelViewSet): filterset_class = UserFilter search_fields = ('username', 'email', 'name') + permission_classes = [RBACPermission, UserObjectPermission] serializer_classes = { 'default': UserSerializer, 'suggestion': MiniUserSerializer, diff --git a/apps/users/permissions.py b/apps/users/permissions.py index ee821f8d7..c7099bb3e 100644 --- a/apps/users/permissions.py +++ b/apps/users/permissions.py @@ -1,5 +1,6 @@ from rest_framework import permissions +from rbac.builtin import BuiltinRole from .utils import is_auth_password_time_valid @@ -7,4 +8,20 @@ class IsAuthPasswdTimeValid(permissions.IsAuthenticated): def has_permission(self, request, view): return super().has_permission(request, view) \ - and is_auth_password_time_valid(request.session) + and is_auth_password_time_valid(request.session) + + +class UserObjectPermission(permissions.BasePermission): + + def has_object_permission(self, request, view, obj): + if view.action not in ['update', 'partial_update', 'destroy']: + return True + + user = request.user + if user.is_superuser: + return True + + system_admin_id = BuiltinRole.system_admin.id + return system_admin_id not in [ + str(r.id) for r in obj.system_roles.all() + ] From 149ca1afce08a777b7d866a607afcaba1377af32 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 21 Apr 2023 18:12:39 +0800 Subject: [PATCH 10/88] =?UTF-8?q?perf:=20=E5=BC=80=E6=BA=90=20acl=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/acls/serializers/base.py | 25 +++++++++++++++++++++---- apps/acls/serializers/login_acl.py | 21 +++------------------ 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/apps/acls/serializers/base.py b/apps/acls/serializers/base.py index dbdac67e7..a6e729e4c 100644 --- a/apps/acls/serializers/base.py +++ b/apps/acls/serializers/base.py @@ -3,6 +3,7 @@ from rest_framework import serializers from acls.models.base import ActionChoices from common.serializers.fields import LabeledChoiceField, ObjectRelatedField +from jumpserver.utils import has_valid_xpack_license from orgs.models import Organization from users.models import User @@ -51,7 +52,26 @@ class ACLAccountsSerializer(serializers.Serializer): ) -class BaseUserAssetAccountACLSerializerMixin(serializers.Serializer): +class ActionAclSerializer(serializers.Serializer): + action = LabeledChoiceField( + choices=ActionChoices.choices, default=ActionChoices.reject, label=_("Action") + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_action_choices() + + def set_action_choices(self): + action = self.fields.get("action") + if not action: + return + choices = action.choices + if not has_valid_xpack_license(): + choices.pop(ActionChoices.review, None) + action._choices = choices + + +class BaseUserAssetAccountACLSerializerMixin(ActionAclSerializer, serializers.Serializer): users = ACLUsersSerializer(label=_('User')) assets = ACLAssestsSerializer(label=_('Asset')) accounts = ACLAccountsSerializer(label=_('Account')) @@ -77,9 +97,6 @@ class BaseUserAssetAccountACLSerializerMixin(serializers.Serializer): reviewers_amount = serializers.IntegerField( read_only=True, source="reviewers.count", label=_('Reviewers amount') ) - action = LabeledChoiceField( - choices=ActionChoices.choices, default=ActionChoices.reject, label=_("Action") - ) class Meta: fields_mini = ["id", "name"] diff --git a/apps/acls/serializers/login_acl.py b/apps/acls/serializers/login_acl.py index 0a180d9e5..07e499ed6 100644 --- a/apps/acls/serializers/login_acl.py +++ b/apps/acls/serializers/login_acl.py @@ -2,12 +2,11 @@ from django.utils.translation import ugettext as _ from rest_framework import serializers from common.serializers import BulkModelSerializer, MethodSerializer -from common.serializers.fields import ObjectRelatedField, LabeledChoiceField -from jumpserver.utils import has_valid_xpack_license +from common.serializers.fields import ObjectRelatedField from users.models import User +from .base import ActionAclSerializer from .rules import RuleSerializer from ..models import LoginACL -from ..models.base import ActionChoices __all__ = [ "LoginACLSerializer", @@ -18,12 +17,11 @@ common_help_text = _( ) -class LoginACLSerializer(BulkModelSerializer): +class LoginACLSerializer(ActionAclSerializer, BulkModelSerializer): user = ObjectRelatedField(queryset=User.objects, label=_("User")) reviewers = ObjectRelatedField( queryset=User.objects, label=_("Reviewers"), many=True, required=False ) - action = LabeledChoiceField(choices=ActionChoices.choices, label=_('Action')) reviewers_amount = serializers.IntegerField( read_only=True, source="reviewers.count", label=_("Reviewers amount") ) @@ -45,18 +43,5 @@ class LoginACLSerializer(BulkModelSerializer): "is_active": {"default": True}, } - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.set_action_choices() - - def set_action_choices(self): - action = self.fields.get("action") - if not action: - return - choices = action.choices - if not has_valid_xpack_license(): - choices.pop(LoginACL.ActionChoices.review, None) - action._choices = choices - def get_rules_serializer(self): return RuleSerializer() From 9d2ae7d1ede489a6d702b0ccd2e522623726e9ff Mon Sep 17 00:00:00 2001 From: Bai Date: Sun, 23 Apr 2023 16:43:06 +0800 Subject: [PATCH 11/88] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=20utils/disable?= =?UTF-8?q?=5Fuser=5Fmfa.sh=20otp=5Flevel=20=3D>=20mfa=5Flevel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/disable_user_mfa.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/disable_user_mfa.sh b/utils/disable_user_mfa.sh index ba98a9db8..0663c3643 100644 --- a/utils/disable_user_mfa.sh +++ b/utils/disable_user_mfa.sh @@ -16,7 +16,7 @@ user = User.objects.filter(username="${username}") if not user: print("No user found") sys.exit(1) -user.update(otp_level=0) +user.update(mfa_level=0) print("Disable user ${username} success") EOF } From 917620736b033cd89cfacbc01f5cc6beee777c30 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 25 Apr 2023 10:28:19 +0800 Subject: [PATCH 12/88] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E6=A8=A1?= =?UTF-8?q?=E7=89=88=E8=B4=A6=E5=8F=B7=E5=AF=86=E7=A0=81=20=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E6=9B=B4=E6=96=B0=E5=85=B3=E8=81=94=E7=9A=84=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=20(#10328)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 修改模版账号密码 同步更新关联的账号 * feat: 同步多个账号 --------- Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/filters.py | 3 +- apps/accounts/models/account.py | 44 ++++++++++ apps/accounts/serializers/account/template.py | 85 ++++--------------- 3 files changed, 63 insertions(+), 69 deletions(-) diff --git a/apps/accounts/filters.py b/apps/accounts/filters.py index fa29bcbfc..67e243e1c 100644 --- a/apps/accounts/filters.py +++ b/apps/accounts/filters.py @@ -5,7 +5,6 @@ from django_filters import rest_framework as drf_filters from assets.models import Node from common.drf.filters import BaseFilterSet - from .models import Account, GatheredAccount @@ -46,7 +45,7 @@ class AccountFilterSet(BaseFilterSet): class Meta: model = Account - fields = ['id', 'asset_id'] + fields = ['id', 'asset_id', 'source_id'] class GatheredAccountFilterSet(BaseFilterSet): diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 4094018e1..de89db545 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -1,4 +1,6 @@ from django.db import models +from django.db.models import Count +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords @@ -118,3 +120,45 @@ class AccountTemplate(BaseAccount): def __str__(self): return self.username + + @staticmethod + def bulk_update_accounts(accounts, data): + history_model = Account.history.model + account_ids = accounts.values_list('id', flat=True) + history_accounts = history_model.objects.filter(id__in=account_ids) + account_id_count_map = { + str(i['id']): i['count'] + for i in history_accounts.values('id').order_by('id') + .annotate(count=Count(1)).values('id', 'count') + } + + 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.objects.bulk_update(accounts, ['version', 'secret']) + + @staticmethod + def bulk_create_history_accounts(accounts, user_id): + history_model = Account.history.model + history_account_objs = [] + for account in accounts: + history_account_objs.append( + history_model( + id=account.id, + version=account.version, + secret=account.secret, + secret_type=account.secret_type, + history_user_id=user_id, + history_date=timezone.now() + ) + ) + history_model.objects.bulk_create(history_account_objs) + + def bulk_sync_account_secret(self, accounts, user_id): + """ 批量同步账号密码 """ + if not accounts: + return + self.bulk_update_accounts(accounts, {'secret': self.secret}) + 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 1de4d7c1d..bbf83bd89 100644 --- a/apps/accounts/serializers/account/template.py +++ b/apps/accounts/serializers/account/template.py @@ -1,86 +1,37 @@ -from django.db.transaction import atomic -from django.db.utils import IntegrityError +from rest_framework import serializers from accounts.models import AccountTemplate, Account -from assets.models import Asset from common.serializers import SecretReadableMixin from .base import BaseAccountSerializer class AccountTemplateSerializer(BaseAccountSerializer): + is_sync_account = serializers.BooleanField(default=False, write_only=True) + _is_sync_account = False + class Meta(BaseAccountSerializer.Meta): model = AccountTemplate + fields = BaseAccountSerializer.Meta.fields + ['is_sync_account'] - @staticmethod - def account_save(data, account): - for field, value in data.items(): - setattr(account, field, value) - try: - account.save(update_fields=list(data.keys())) - except IntegrityError: - pass - - # TODO 数据库访问的太多了 后期优化 - @atomic() - def bulk_update_accounts(self, instance, diff): - accounts = Account.objects.filter(source_id=instance.id) - if not accounts: + def sync_accounts_secret(self, instance, diff): + if not self._is_sync_account or 'secret' not in diff: return - diff.pop('secret', None) - name = diff.pop('name', None) - username = diff.pop('username', None) - secret_type = diff.pop('secret_type', None) - update_accounts = [] - for account in accounts: - for field, value in diff.items(): - setattr(account, field, value) - update_accounts.append(account) + accounts = Account.objects.filter(source_id=instance.id) + instance.bulk_sync_account_secret(accounts, self.context['request'].user.id) - if update_accounts: - Account.objects.bulk_update(update_accounts, diff.keys()) - - if name: - for account in accounts: - data = {'name': name} - self.account_save(data, account) - - if secret_type and username: - asset_ids_supports = self.get_asset_ids_supports(accounts, secret_type) - for account in accounts: - asset_id = account.asset_id - if asset_id not in asset_ids_supports: - data = {'username': username} - self.account_save(data, account) - continue - data = {'username': username, 'secret_type': secret_type, 'secret': instance.secret} - self.account_save(data, account) - elif secret_type: - asset_ids_supports = self.get_asset_ids_supports(accounts, secret_type) - for account in accounts: - asset_id = account.asset_id - if asset_id not in asset_ids_supports: - continue - data = {'secret_type': secret_type, 'secret': instance.secret} - self.account_save(data, account) - elif username: - for account in accounts: - data = {'username': username} - self.account_save(data, account) - - @staticmethod - def get_asset_ids_supports(accounts, secret_type): - asset_ids = accounts.values_list('asset_id', flat=True) - secret_type_supports = Asset.get_secret_type_assets(asset_ids, secret_type) - return [asset.id for asset in secret_type_supports] + def validate(self, attrs): + self._is_sync_account = attrs.pop('is_sync_account', None) + attrs = super().validate(attrs) + return attrs def update(self, instance, validated_data): - # diff = { - # k: v for k, v in validated_data.items() - # if getattr(instance, k) != v - # } + diff = { + k: v for k, v in validated_data.items() + if getattr(instance, k) != v + } instance = super().update(instance, validated_data) - # self.bulk_update_accounts(instance, diff) + self.sync_accounts_secret(instance, diff) return instance From 2a196743f5e193a64c4989e613776797b7dc702b Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 25 Apr 2023 11:27:58 +0800 Subject: [PATCH 13/88] =?UTF-8?q?perf:=20=E7=BB=84=E7=BB=87=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=88=B7=E6=96=B0=E7=BC=93=E5=AD=98=20(#10333)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/orgs/signal_handlers/common.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/orgs/signal_handlers/common.py b/apps/orgs/signal_handlers/common.py index 01ce911c4..fe1d673ea 100644 --- a/apps/orgs/signal_handlers/common.py +++ b/apps/orgs/signal_handlers/common.py @@ -59,8 +59,6 @@ def expire_user_orgs(*args): @receiver(post_save, sender=Organization) def on_org_create(sender, instance, created=False, **kwargs): - if not created: - return expire_user_orgs() From f51af9736b7f47813b7a0ddc949ed9d3f2728b17 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 25 Apr 2023 14:25:06 +0800 Subject: [PATCH 14/88] =?UTF-8?q?perf:=20rdp=20=E6=94=AF=E6=8C=81=20consol?= =?UTF-8?q?e=20=E6=A8=A1=E5=BC=8F?= 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, 4 insertions(+) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 5b5dcd700..a7fed5e7a 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -99,6 +99,10 @@ class RDPFileClientProtocolURLMixin: remote_app_options = token.get_remote_app_option() rdp_options.update(remote_app_options) + rdp = token.asset.platform.protocols.filter(name='rdp').first() + if rdp and rdp.setting.get('console'): + rdp_options['administrative session:i:'] = '1' + # 文件名 name = token.asset.name prefix_name = f'{token.user.username}-{name}' From 8fe5ab42e81ddc056099644caec01bbe93473d7c Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:36:01 +0800 Subject: [PATCH 15/88] =?UTF-8?q?perf:=20=E7=94=A8=E6=88=B7=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E5=8F=B0=E8=B5=84=E4=BA=A7=E6=98=BE=E7=A4=BA=E6=9B=B4?= =?UTF-8?q?=E5=A4=9A=E5=AD=97=E6=AE=B5=20(#10338)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/perms/serializers/user_permission.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/perms/serializers/user_permission.py b/apps/perms/serializers/user_permission.py index 4c175cba5..7303eb64f 100644 --- a/apps/perms/serializers/user_permission.py +++ b/apps/perms/serializers/user_permission.py @@ -30,8 +30,9 @@ class AssetPermedSerializer(OrgResourceModelSerializerMixin): class Meta: model = Asset only_fields = [ - "id", "name", "address", 'domain', 'platform', - "comment", "org_id", "is_active", + 'id', 'name', 'address', 'domain', 'platform', + 'comment', 'org_id', 'is_active', 'date_verified', + 'created_by', 'date_created', 'connectivity', 'nodes' ] fields = only_fields + ['protocols', 'category', 'type'] + ['org_name'] read_only_fields = fields From a19586f8b83ddbf04f060f9fe6521159875e6332 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:48:09 +0800 Subject: [PATCH 16/88] perf: perm user asset add labels (#10339) Co-authored-by: feng <1304903146@qq.com> --- apps/assets/serializers/asset/common.py | 2 +- apps/perms/serializers/user_permission.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 0fd14eec9..88a6cfef2 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -23,7 +23,7 @@ __all__ = [ 'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer', 'AssetTaskSerializer', 'AssetsTaskSerializer', 'AssetProtocolsSerializer', 'AssetDetailSerializer', 'DetailMixin', 'AssetAccountSerializer', - 'AccountSecretSerializer', 'AssetProtocolsPermsSerializer' + 'AccountSecretSerializer', 'AssetProtocolsPermsSerializer', 'AssetLabelSerializer' ] diff --git a/apps/perms/serializers/user_permission.py b/apps/perms/serializers/user_permission.py index 7303eb64f..7d054c0d7 100644 --- a/apps/perms/serializers/user_permission.py +++ b/apps/perms/serializers/user_permission.py @@ -8,7 +8,7 @@ from rest_framework import serializers from accounts.models import Account from assets.const import Category, AllTypes from assets.models import Node, Asset, Platform -from assets.serializers.asset.common import AssetProtocolsPermsSerializer +from assets.serializers.asset.common import AssetProtocolsPermsSerializer, AssetLabelSerializer from common.serializers.fields import ObjectRelatedField, LabeledChoiceField from orgs.mixins.serializers import OrgResourceModelSerializerMixin from perms.serializers.permission import ActionChoicesField @@ -25,6 +25,7 @@ class AssetPermedSerializer(OrgResourceModelSerializerMixin): protocols = AssetProtocolsPermsSerializer(many=True, required=False, label=_('Protocols')) category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category')) type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type')) + labels = AssetLabelSerializer(many=True, required=False, label=_('Label')) domain = ObjectRelatedField(required=False, queryset=Node.objects, label=_('Domain')) class Meta: @@ -32,7 +33,7 @@ class AssetPermedSerializer(OrgResourceModelSerializerMixin): only_fields = [ 'id', 'name', 'address', 'domain', 'platform', 'comment', 'org_id', 'is_active', 'date_verified', - 'created_by', 'date_created', 'connectivity', 'nodes' + 'created_by', 'date_created', 'connectivity', 'nodes', 'labels' ] fields = only_fields + ['protocols', 'category', 'type'] + ['org_name'] read_only_fields = fields From 9eec2909ed34a5e8de0376038300180196a1ac0a Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 26 Apr 2023 16:54:14 +0800 Subject: [PATCH 17/88] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9'=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=A4=87=E4=BB=BD=E5=88=97=E8=A1=A8-=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E6=AC=A1=E6=95=B0'=E6=9C=AA=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E4=B8=BA=E8=8B=B1=E6=96=87=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/accounts/serializers/account/backup.py | 2 +- apps/accounts/serializers/automations/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/accounts/serializers/account/backup.py b/apps/accounts/serializers/account/backup.py index af07ca04d..712bdd095 100644 --- a/apps/accounts/serializers/account/backup.py +++ b/apps/accounts/serializers/account/backup.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from accounts.models import AccountBackupAutomation, AccountBackupExecution diff --git a/apps/accounts/serializers/automations/base.py b/apps/accounts/serializers/automations/base.py index cdb08bf36..1468ecf58 100644 --- a/apps/accounts/serializers/automations/base.py +++ b/apps/accounts/serializers/automations/base.py @@ -1,4 +1,4 @@ -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from accounts.models import AutomationExecution From 58d055f114fdc0baaba74f566bcc7cf7a46cc192 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 26 Apr 2023 18:50:30 +0800 Subject: [PATCH 18/88] =?UTF-8?q?perf:=20=E6=94=B9=E5=AF=86=20=E6=8E=A8?= =?UTF-8?q?=E9=80=81=20=E5=8F=AF=E4=BB=A5=E5=AF=B9=E8=87=AA=E5=B7=B1?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=20=E5=90=8C=E6=97=B6=E8=AE=BE=E7=BD=AEsu=5Fe?= =?UTF-8?q?nabled=20=E5=8F=AF=E6=8F=90=E6=9D=83=20(#10349)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- .../automations/change_secret/manager.py | 4 ++-- apps/assets/automations/base/manager.py | 1 + apps/ops/ansible/inventory.py | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/accounts/automations/change_secret/manager.py b/apps/accounts/automations/change_secret/manager.py index 05e2b1349..9d1c2f441 100644 --- a/apps/accounts/automations/change_secret/manager.py +++ b/apps/accounts/automations/change_secret/manager.py @@ -72,14 +72,14 @@ class ChangeSecretManager(AccountBasePlaybookManager): return [] asset = privilege_account.asset - accounts = asset.accounts.exclude(username=privilege_account.username) + accounts = asset.accounts.all() accounts = accounts.filter(id__in=self.account_ids) if self.secret_type: accounts = accounts.filter(secret_type=self.secret_type) if settings.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED: accounts = accounts.filter(privileged=False).exclude( - username__in=['root', 'administrator'] + username__in=['root', 'administrator', privilege_account.username] ) return accounts diff --git a/apps/assets/automations/base/manager.py b/apps/assets/automations/base/manager.py index ae9740347..8d47a6ca4 100644 --- a/apps/assets/automations/base/manager.py +++ b/apps/assets/automations/base/manager.py @@ -166,6 +166,7 @@ class BasePlaybookManager: account_prefer=self.ansible_account_prefer, account_policy=self.ansible_account_policy, host_callback=self.host_callback, + task_type=self.__class__.method_type(), ) inventory.write_to_file(inventory_path) diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index fc124b210..6ecc49698 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -5,12 +5,17 @@ from collections import defaultdict from django.utils.translation import gettext as _ +from accounts.const import AutomationTypes + __all__ = ['JMSInventory'] class JMSInventory: - def __init__(self, assets, account_policy='privileged_first', - account_prefer='root,Administrator', host_callback=None, exclude_localhost=False): + def __init__( + self, assets, account_policy='privileged_first', + account_prefer='root,Administrator', host_callback=None, + exclude_localhost=False, task_type=None + ): """ :param assets: :param account_prefer: account username name if not set use account_policy @@ -22,6 +27,7 @@ class JMSInventory: self.host_callback = host_callback self.exclude_hosts = {} self.exclude_localhost = exclude_localhost + self.task_type = task_type @staticmethod def clean_assets(assets): @@ -92,6 +98,12 @@ class JMSInventory: host['ansible_become_password'] = su_from.secret else: host['ansible_become_password'] = account.secret + 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)) + host['ansible_become'] = True + host['ansible_become_user'] = 'root' + host['ansible_become_password'] = account.secret else: host.update(self.make_account_ansible_vars(account)) From ea1c94c6dbc5f69a0b9b9bd4acb32ec7624bbcd7 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 27 Apr 2023 11:38:33 +0800 Subject: [PATCH 19/88] =?UTF-8?q?perf:=20=E7=94=A8=E6=88=B7=E7=BB=84?= =?UTF-8?q?=E7=BB=87=E6=8C=89=E7=85=A7name=20=E8=BF=9B=E8=A1=8C=E6=8E=92?= =?UTF-8?q?=E5=BA=8F=20(#10354)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/rbac/models/rolebinding.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/rbac/models/rolebinding.py b/apps/rbac/models/rolebinding.py index f8f21d2bf..72b36e5dd 100644 --- a/apps/rbac/models/rolebinding.py +++ b/apps/rbac/models/rolebinding.py @@ -1,9 +1,9 @@ -from django.utils.translation import gettext_lazy as _ -from django.db import models -from django.db.models import Q from django.conf import settings from django.core.exceptions import ValidationError +from django.db import models +from django.db.models import Q from django.db.models.signals import post_save +from django.utils.translation import gettext_lazy as _ from rest_framework.serializers import ValidationError from common.db.models import JMSBaseModel, CASCADE_SIGNAL_SKIP @@ -109,6 +109,13 @@ class RoleBinding(JMSBaseModel): def is_scope_org(self): return self.scope == Scope.org + @staticmethod + def orgs_order_by_name(orgs): + from orgs.models import Organization + default_system_org_ids = [Organization.DEFAULT_ID, Organization.SYSTEM_ID] + default_system_orgs = orgs.filter(id__in=default_system_org_ids) + return default_system_orgs | orgs.exclude(id__in=default_system_org_ids).order_by('name') + @classmethod def get_user_has_the_perm_orgs(cls, perm, user): from orgs.models import Organization @@ -134,6 +141,7 @@ class RoleBinding(JMSBaseModel): org_ids = [b.org.id for b in bindings if b.org] orgs = all_orgs.filter(id__in=org_ids) + orgs = cls.orgs_order_by_name(orgs) workbench_perm = 'rbac.view_workbench' # 全局组织 if orgs and perm != workbench_perm and user.has_perm('orgs.view_rootorg'): @@ -183,7 +191,7 @@ class OrgRoleBinding(RoleBinding): class SystemRoleBindingManager(RoleBindingManager): def get_queryset(self): - queryset = super(RoleBindingManager, self).get_queryset()\ + queryset = super(RoleBindingManager, self).get_queryset() \ .filter(scope=Scope.system) return queryset From 78ddb75b7a20621b6f2d6ad1eaea00d92a85effc Mon Sep 17 00:00:00 2001 From: maninhill <41712985+maninhill@users.noreply.github.com> Date: Thu, 27 Apr 2023 17:38:26 +0800 Subject: [PATCH 20/88] =?UTF-8?q?chore(docs)=EF=BC=9A=E6=9B=B4=E6=96=B0=20?= =?UTF-8?q?README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ca31cce08..377aca5fa 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,9 @@ JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运 ## 社区 -如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose) -或加入到我们的社区当中进行进一步交流沟通。 +如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose)。您也可以到我们的 [社区论坛](https://bbs.fit2cloud.com/c/js/5) 及微信交流群当中进行交流沟通。 -### 微信交流群 +**微信交流群** 微信群二维码 From 62e5389f80f6fd4ec5b6431cfc47c4311b3dd7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=B9=BF?= Date: Thu, 27 Apr 2023 17:47:39 +0800 Subject: [PATCH 21/88] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 377aca5fa..3a4031f96 100644 --- a/README.md +++ b/README.md @@ -72,9 +72,11 @@ JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运 - [东方明珠:JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687) - [江苏农信:JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666) -## 社区 +## 社区交流 -如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose)。您也可以到我们的 [社区论坛](https://bbs.fit2cloud.com/c/js/5) 及微信交流群当中进行交流沟通。 +如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose)。 + +您也可以到我们的 [社区论坛](https://bbs.fit2cloud.com/c/js/5) 及微信交流群当中进行交流沟通。 **微信交流群** @@ -106,7 +108,7 @@ JumpServer是一款安全产品,请参考 [基本安全建议](https://docs.ju - 邮箱:support@fit2cloud.com - 电话:400-052-0755 -## 致谢 +## 致谢开源 - [Apache Guacamole](https://guacamole.apache.org/): Web 页面连接 RDP、SSH、VNC 等协议资产,JumpServer Lion 组件使用到该项目; - [OmniDB](https://omnidb.org/): Web 页面连接使用数据库,JumpServer Web 数据库组件使用到该项目。 From a0151b8d44f975dc145fa134d0d68ab90d813ea8 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 27 Apr 2023 15:35:26 +0800 Subject: [PATCH 22/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=97=A7=20ssh?= =?UTF-8?q?=20=E7=A7=81=E9=92=A5=EF=BC=8C=E8=A7=A3=E6=9E=90=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=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/common/utils/encode.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/common/utils/encode.py b/apps/common/utils/encode.py index 51b97b8b6..767645c88 100644 --- a/apps/common/utils/encode.py +++ b/apps/common/utils/encode.py @@ -175,6 +175,8 @@ def _parse_ssh_private_key(text, password=None): dsa.DSAPrivateKey, ed25519.Ed25519PrivateKey, """ + if not bool(password): + password = None if isinstance(text, str): try: text = text.encode("utf-8") From f8425460425716ffed123de4b757fe0f113932a1 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 28 Apr 2023 17:13:43 +0800 Subject: [PATCH 23/88] =?UTF-8?q?perf:=20=E5=B9=B3=E5=8F=B0=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E8=BF=87=E6=BB=A4=E6=8E=89automation=20(#10367)?= 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/platform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index 3313168cc..f16b62097 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -122,6 +122,7 @@ class PlatformSerializer(WritableNestedModelSerializer): fields_small = fields_mini + [ "category", "type", "charset", ] + fields_unexport = ['automation'] read_only_fields = [ 'internal', 'date_created', 'date_updated', 'created_by', 'updated_by' From 3585ca2d4941325f9701cbe1956b01592b916bc1 Mon Sep 17 00:00:00 2001 From: Bai Date: Thu, 4 May 2023 10:31:19 +0800 Subject: [PATCH 24/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E6=A1=88:=20=E6=B8=85=E9=99=A4=E7=A6=BB=E7=BA=BF=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.po | 2 +- apps/locale/zh/LC_MESSAGES/django.po | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index c113ce8ee..b751ef764 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -6058,7 +6058,7 @@ msgstr "端末の状態を定期的にクリーンアップする" #: terminal/tasks.py:37 msgid "Clean orphan session" -msgstr "孤立したセッションをクリアする" +msgstr "オフライン セッションをクリアする" #: terminal/tasks.py:56 msgid "Upload session replay to external storage" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 4683b46c6..40199acec 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -5979,7 +5979,7 @@ msgstr "周期清理终端状态" #: terminal/tasks.py:37 msgid "Clean orphan session" -msgstr "清除孤儿会话" +msgstr "清除离线会话" #: terminal/tasks.py:56 msgid "Upload session replay to external storage" From 9934456af49f572c167ab60249d326a8e57774e1 Mon Sep 17 00:00:00 2001 From: Bai Date: Thu, 4 May 2023 15:34:55 +0800 Subject: [PATCH 25/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=BF=81?= =?UTF-8?q?=E7=A7=BBredis=E8=B5=84=E4=BA=A7=E8=B4=A6=E5=8F=B7=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=E7=9A=84=E9=97=AE=E9=A2=98(=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=94=A8=E6=88=B7=E5=90=8D=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/migrations/0100_auto_20220711_1413.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/assets/migrations/0100_auto_20220711_1413.py b/apps/assets/migrations/0100_auto_20220711_1413.py index 87eed5fb0..4e57be633 100644 --- a/apps/assets/migrations/0100_auto_20220711_1413.py +++ b/apps/assets/migrations/0100_auto_20220711_1413.py @@ -161,11 +161,12 @@ def migrate_db_accounts(apps, schema_editor): name = f'{username}(token)' else: secret_type = attr - name = username + name = username or f'{username}(password)' auth_infos.append((name, secret_type, secret)) if not auth_infos: - auth_infos.append((username, 'password', '')) + name = username or f'{username}(password)' + auth_infos.append((name, 'password', '')) for name, secret_type, secret in auth_infos: values['name'] = name From 22b56d73b63ccc24ec42ac53d1ab66663ef583b8 Mon Sep 17 00:00:00 2001 From: Bai Date: Thu, 4 May 2023 16:31:55 +0800 Subject: [PATCH 26/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E5=BA=94=E7=94=A8=E6=97=B6(=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E4=B8=8B=E5=8F=AA=E6=9C=89=E6=A0=B9=E8=8A=82=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=90=8E=E7=9A=84=E5=BA=94=E7=94=A8=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E6=B2=A1=E6=9C=89=E8=AE=BE=E7=BD=AE=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0098_auto_20220430_2126.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/assets/migrations/0098_auto_20220430_2126.py b/apps/assets/migrations/0098_auto_20220430_2126.py index 5fd333262..ffd0bf57f 100644 --- a/apps/assets/migrations/0098_auto_20220430_2126.py +++ b/apps/assets/migrations/0098_auto_20220430_2126.py @@ -20,6 +20,7 @@ def get_prop_name_id(apps, app, category): def migrate_database_to_asset(apps, *args): + node_model = apps.get_model('assets', 'Node') app_model = apps.get_model('applications', 'Application') db_model = apps.get_model('assets', 'Database') platform_model = apps.get_model('assets', 'Platform') @@ -84,11 +85,18 @@ def create_app_nodes(apps, org_id): node_keys = node_model.objects.filter(org_id=org_id) \ .filter(key__regex=child_pattern) \ .values_list('key', flat=True) - if not node_keys: - return - node_key_split = [key.split(':') for key in node_keys] - next_value = max([int(k[1]) for k in node_key_split]) + 1 - parent_key = node_key_split[0][0] + if node_keys: + node_key_split = [key.split(':') for key in node_keys] + next_value = max([int(k[1]) for k in node_key_split]) + 1 + parent_key = node_key_split[0][0] + else: + root_node = node_model.objects.filter(org_id=org_id)\ + .filter(parent_key='', key__regex=r'^[0-9]+$').exclude(key__startswith='-').first() + if not root_node: + return + parent_key = root_node.key + next_value = 0 + next_key = '{}:{}'.format(parent_key, next_value) name = 'Apps' parent = node_model.objects.get(key=parent_key) From 127f6730f655e4bc15e0dd7c60911e5867af76e5 Mon Sep 17 00:00:00 2001 From: Bai Date: Thu, 4 May 2023 17:16:48 +0800 Subject: [PATCH 27/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E5=90=8E=E7=9A=84=20Redis=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=B9=B3=E5=8F=B0=E4=BB=8E=20Redis6+=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20Redis6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/migrations/0098_auto_20220430_2126.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/migrations/0098_auto_20220430_2126.py b/apps/assets/migrations/0098_auto_20220430_2126.py index ffd0bf57f..64a85ecb6 100644 --- a/apps/assets/migrations/0098_auto_20220430_2126.py +++ b/apps/assets/migrations/0098_auto_20220430_2126.py @@ -26,7 +26,7 @@ def migrate_database_to_asset(apps, *args): platform_model = apps.get_model('assets', 'Platform') applications = app_model.objects.filter(category='db') - platforms = platform_model.objects.all().filter(internal=True) + platforms = platform_model.objects.all().filter(internal=True).exclude(name='Redis6+') platforms_map = {p.type: p for p in platforms} print() From ee7f1f8f5e3db37813d21c07967c4eda03f584a2 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 27 Apr 2023 19:31:58 +0800 Subject: [PATCH 28/88] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=20mp4=20?= =?UTF-8?q?=E5=BD=95=E5=83=8F=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0=E5=92=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20video=20worker=20=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/api/session/session.py | 2 ++ apps/terminal/const.py | 1 + apps/terminal/models/session/session.py | 4 ++-- apps/terminal/serializers/session.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/terminal/api/session/session.py b/apps/terminal/api/session/session.py index 13cd62b7a..0661536f7 100644 --- a/apps/terminal/api/session/session.py +++ b/apps/terminal/api/session/session.py @@ -186,6 +186,8 @@ class SessionReplayViewSet(AsyncApiMixin, viewsets.ViewSet): tp = 'guacamole' if url.endswith('.cast.gz'): tp = 'asciicast' + if url.endswith('.replay.mp4'): + tp = 'mp4' download_url = reverse('api-terminal:session-replay-download', kwargs={'pk': session.id}) data = { diff --git a/apps/terminal/const.py b/apps/terminal/const.py index fd0421427..cfec290c7 100644 --- a/apps/terminal/const.py +++ b/apps/terminal/const.py @@ -48,6 +48,7 @@ class TerminalType(TextChoices): magnus = 'magnus', 'Magnus' razor = 'razor', 'Razor' tinker = 'tinker', 'Tinker' + video_worker = 'video_worker', 'Video Worker' @classmethod def types(cls): diff --git a/apps/terminal/models/session/session.py b/apps/terminal/models/session/session.py index 2e0f3a5c9..5ddd6e647 100644 --- a/apps/terminal/models/session/session.py +++ b/apps/terminal/models/session/session.py @@ -48,8 +48,8 @@ class Session(OrgModelMixin): upload_to = 'replay' ACTIVE_CACHE_KEY_PREFIX = 'SESSION_ACTIVE_{}' - SUFFIX_MAP = {1: '.gz', 2: '.replay.gz', 3: '.cast.gz'} - DEFAULT_SUFFIXES = ['.replay.gz', '.cast.gz', '.gz'] + SUFFIX_MAP = {1: '.gz', 2: '.replay.gz', 3: '.cast.gz', 4: '.replay.mp4'} + DEFAULT_SUFFIXES = ['.replay.gz', '.cast.gz', '.gz', '.replay.mp4'] # Todo: 将来干掉 local_path, 使用 default storage 实现 def get_all_possible_local_path(self): diff --git a/apps/terminal/serializers/session.py b/apps/terminal/serializers/session.py index 572c1ecc3..67eea06be 100644 --- a/apps/terminal/serializers/session.py +++ b/apps/terminal/serializers/session.py @@ -61,7 +61,7 @@ class SessionDisplaySerializer(SessionSerializer): class ReplaySerializer(serializers.Serializer): file = serializers.FileField(allow_empty_file=True) - version = serializers.IntegerField(write_only=True, required=False, min_value=2, max_value=3) + version = serializers.IntegerField(write_only=True, required=False, min_value=2, max_value=4) class SessionJoinValidateSerializer(serializers.Serializer): From 7ff22cbc3441099b5b1b81ad9788d9a9e649ecb2 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Mon, 8 May 2023 14:37:43 +0800 Subject: [PATCH 29/88] fix: /prometheus/metrics/ api 500 --- apps/terminal/utils/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/terminal/utils/components.py b/apps/terminal/utils/components.py index a3de26322..100391dbd 100644 --- a/apps/terminal/utils/components.py +++ b/apps/terminal/utils/components.py @@ -140,7 +140,7 @@ class ComponentsPrometheusMetricsUtil(TypedComponentsStatusMetricsUtil): for component in self.components: if not component.is_alive: continue - component_stat = component.latest_stat + component_stat = component.last_stat if not component_stat: continue metric_text = state_metric_text % ( From bda748d547f09697181bbbb90abbfb161b7621c3 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Fri, 28 Apr 2023 14:01:44 +0800 Subject: [PATCH 30/88] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E9=92=89?= =?UTF-8?q?=E9=92=89=E3=80=81=E9=A3=9E=E4=B9=A6=E3=80=81=E4=BC=81=E4=B8=9A?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=89=AB=E7=A0=81=E7=99=BB=E5=BD=95=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=97=B6=E8=87=AA=E5=8A=A8=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/dingtalk.py | 53 +++------ apps/authentication/views/feishu.py | 54 +++------ apps/authentication/views/mixins.py | 104 ++++++++++++++++++ apps/authentication/views/wecom.py | 50 +++------ apps/common/sdk/im/dingtalk/__init__.py | 21 +++- apps/common/sdk/im/feishu/__init__.py | 23 +++- apps/common/sdk/im/wecom/__init__.py | 23 ++-- apps/jumpserver/conf.py | 3 + apps/settings/serializers/auth/dingtalk.py | 3 + apps/settings/serializers/auth/feishu.py | 3 + apps/settings/serializers/auth/wecom.py | 3 + .../migrations/0040_alter_user_source.py | 2 +- apps/users/models/user.py | 14 ++- 13 files changed, 221 insertions(+), 135 deletions(-) diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index e0e76f966..7b421851a 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -23,7 +23,7 @@ from common.utils.random import random_string from users.models import User from users.views import UserVerifyPasswordView -from .mixins import METAMixin +from .mixins import METAMixin, QRLoginCallbackMixin logger = get_logger(__file__) @@ -158,7 +158,7 @@ class DingTalkQRBindCallbackView(DingTalkQRMixin, View): appsecret=settings.DINGTALK_APPSECRET, agentid=settings.DINGTALK_AGENTID ) - userid = dingtalk.get_userid_by_code(code) + userid, __ = dingtalk.get_user_id_by_code(code) if not userid: msg = _('DingTalk query user failed') @@ -214,45 +214,20 @@ class DingTalkQRLoginView(DingTalkQRMixin, METAMixin, View): return HttpResponseRedirect(url) -class DingTalkQRLoginCallbackView(AuthMixin, DingTalkQRMixin, View): +class DingTalkQRLoginCallbackView(QRLoginCallbackMixin, AuthMixin, DingTalkQRMixin, View): permission_classes = (AllowAny,) - def get(self, request: HttpRequest): - code = request.GET.get('code') - redirect_url = request.GET.get('redirect_url') - login_url = reverse('authentication:login') + CLIENT_INFO = ( + DingTalk, {'appid': 'DINGTALK_APPKEY', 'appsecret': 'DINGTALK_APPSECRET', 'agentid': 'DINGTALK_AGENTID'} + ) + USER_TYPE = 'dingtalk' + AUTH_BACKEND = 'AUTH_BACKEND_DINGTALK' + CREATE_USER_IF_NOT_EXIST = 'DINGTALK_CREATE_USER_IF_NOT_EXIST' - if not self.verify_state(): - return self.get_verify_state_failed_response(redirect_url) - - dingtalk = DingTalk( - appid=settings.DINGTALK_APPKEY, - appsecret=settings.DINGTALK_APPSECRET, - agentid=settings.DINGTALK_AGENTID - ) - userid = dingtalk.get_userid_by_code(code) - if not userid: - # 正常流程不会出这个错误,hack 行为 - msg = _('Failed to get user from DingTalk') - response = self.get_failed_response(login_url, title=msg, msg=msg) - return response - - user = get_object_or_none(User, dingtalk_id=userid) - if user is None: - title = _('DingTalk is not bound') - msg = _('Please login with a password and then bind the DingTalk') - response = self.get_failed_response(login_url, title=title, msg=msg) - return response - - try: - self.check_oauth2_auth(user, settings.AUTH_BACKEND_DINGTALK) - except errors.AuthFailedError as e: - self.set_login_failed_mark() - msg = e.msg - response = self.get_failed_response(login_url, title=msg, msg=msg) - return response - - return self.redirect_to_guard_view() + MSG_CLIENT_ERR = _('DingTalk Error') + MSG_USER_NOT_BOUND_ERR = _('DingTalk is not bound') + MSG_USER_NEED_BOUND_WARNING = _('Please login with a password and then bind the DingTalk') + MSG_NOT_FOUND_USER_FROM_CLIENT_ERR = _('Failed to get user from DingTalk') class DingTalkOAuthLoginView(DingTalkOAuthMixin, View): @@ -284,7 +259,7 @@ class DingTalkOAuthLoginCallbackView(AuthMixin, DingTalkOAuthMixin, View): appsecret=settings.DINGTALK_APPSECRET, agentid=settings.DINGTALK_AGENTID ) - userid = dingtalk.get_userid_by_code(code) + userid, __ = dingtalk.get_user_id_by_code(code) if not userid: # 正常流程不会出这个错误,hack 行为 msg = _('Failed to get user from DingTalk') diff --git a/apps/authentication/views/feishu.py b/apps/authentication/views/feishu.py index e9734b170..4ba777097 100644 --- a/apps/authentication/views/feishu.py +++ b/apps/authentication/views/feishu.py @@ -9,7 +9,6 @@ from django.views import View from rest_framework.exceptions import APIException from rest_framework.permissions import AllowAny, IsAuthenticated -from authentication import errors from authentication.const import ConfirmType from authentication.mixins import AuthMixin from authentication.notifications import OAuthBindMessage @@ -18,11 +17,12 @@ from common.permissions import UserConfirmation from common.sdk.im.feishu import URL, FeiShu from common.utils import FlashMessageUtil, get_logger from common.utils.common import get_request_ip -from common.utils.django import get_object_or_none, reverse +from common.utils.django import reverse from common.utils.random import random_string -from users.models import User from users.views import UserVerifyPasswordView +from .mixins import QRLoginCallbackMixin + logger = get_logger(__file__) FEISHU_STATE_SESSION_KEY = '_feishu_state' @@ -123,7 +123,7 @@ class FeiShuQRBindCallbackView(FeiShuQRMixin, View): app_id=settings.FEISHU_APP_ID, app_secret=settings.FEISHU_APP_SECRET ) - user_id = feishu.get_user_id_by_code(code) + user_id, __ = feishu.get_user_id_by_code(code) if not user_id: msg = _('FeiShu query user failed') @@ -176,41 +176,17 @@ class FeiShuQRLoginView(FeiShuQRMixin, View): return HttpResponseRedirect(url) -class FeiShuQRLoginCallbackView(AuthMixin, FeiShuQRMixin, View): +class FeiShuQRLoginCallbackView(QRLoginCallbackMixin, AuthMixin, FeiShuQRMixin, View): permission_classes = (AllowAny,) - def get(self, request: HttpRequest): - code = request.GET.get('code') - redirect_url = request.GET.get('redirect_url') - login_url = reverse('authentication:login') + CLIENT_INFO = ( + FeiShu, {'app_id': 'FEISHU_APP_ID', 'app_secret': 'FEISHU_APP_SECRET'} + ) + USER_TYPE = 'feishu' + AUTH_BACKEND = 'AUTH_BACKEND_FEISHU' + CREATE_USER_IF_NOT_EXIST = 'FEISHU_CREATE_USER_IF_NOT_EXIST' - if not self.verify_state(): - return self.get_verify_state_failed_response(redirect_url) - - feishu = FeiShu( - app_id=settings.FEISHU_APP_ID, - app_secret=settings.FEISHU_APP_SECRET - ) - user_id = feishu.get_user_id_by_code(code) - if not user_id: - # 正常流程不会出这个错误,hack 行为 - msg = _('Failed to get user from FeiShu') - response = self.get_failed_response(login_url, title=msg, msg=msg) - return response - - user = get_object_or_none(User, feishu_id=user_id) - if user is None: - title = _('FeiShu is not bound') - msg = _('Please login with a password and then bind the FeiShu') - response = self.get_failed_response(login_url, title=title, msg=msg) - return response - - try: - self.check_oauth2_auth(user, settings.AUTH_BACKEND_FEISHU) - except errors.AuthFailedError as e: - self.set_login_failed_mark() - msg = e.msg - response = self.get_failed_response(login_url, title=msg, msg=msg) - return response - - return self.redirect_to_guard_view() + MSG_CLIENT_ERR = _('FeiShu Error') + MSG_USER_NOT_BOUND_ERR = _('FeiShu is not bound') + MSG_USER_NEED_BOUND_WARNING = _('Please login with a password and then bind the FeiShu') + MSG_NOT_FOUND_USER_FROM_CLIENT_ERR = _('Failed to get user from FeiShu') diff --git a/apps/authentication/views/mixins.py b/apps/authentication/views/mixins.py index 3571dfac7..aa79eef1c 100644 --- a/apps/authentication/views/mixins.py +++ b/apps/authentication/views/mixins.py @@ -1,5 +1,20 @@ # -*- coding: utf-8 -*- # +from functools import lru_cache + +from rest_framework.request import Request +from django.utils.translation import ugettext_lazy as _ +from django.conf import settings +from django.db.utils import IntegrityError + +from authentication import errors +from users.models import User +from common.utils.django import reverse, get_object_or_none +from common.utils import get_logger + + +logger = get_logger(__file__) + class METAMixin: def get_next_url_from_meta(self): @@ -10,3 +25,92 @@ class METAMixin: if len(next_url_item) > 1: next_url = next_url_item[-1] return next_url + + +class Client: + get_user_id_by_code: callable + get_user_detail: callable + + +class QRLoginCallbackMixin: + verify_state: callable + get_verify_state_failed_response: callable + get_failed_response: callable + check_oauth2_auth: callable + set_login_failed_mark: callable + redirect_to_guard_view: callable + # 属性 + _client: Client + CLIENT_INFO: tuple + USER_TYPE: str + AUTH_BACKEND: str + CREATE_USER_IF_NOT_EXIST: str + # 提示信息 + MSG_CLIENT_ERR: str + MSG_USER_NOT_BOUND_ERR: str + MSG_USER_NEED_BOUND_WARNING: str + MSG_NOT_FOUND_USER_FROM_CLIENT_ERR: str + + @property + @lru_cache(maxsize=1) + def client(self): + client_type, client_init = self.CLIENT_INFO + client_init = {k: getattr(settings, v) for k, v in client_init.items()} + return client_type(**client_init) + + def create_user_if_not_exist(self, user_id, **kwargs): + user = None + if not getattr(settings, self.CREATE_USER_IF_NOT_EXIST): + title = self.MSG_CLIENT_ERR + msg = self.MSG_USER_NEED_BOUND_WARNING + return user, (title, msg) + + user_attr = self.client.get_user_detail(user_id, **kwargs) + try: + user, create = User.objects.get_or_create( + username=user_attr['username'], defaults=user_attr + ) + setattr(user, f'{self.USER_TYPE}_id', user_id) + if create: + setattr(user, 'source', self.USER_TYPE) + user.save() + except IntegrityError as err: + logger.error(f'{self.MSG_CLIENT_ERR}: create user error: {err}') + + if user is None: + title = self.MSG_CLIENT_ERR + msg = _('If you have any question, please contact the administrator') + return user, (title, msg) + + return user, None + + def get(self, request: Request): + code = request.GET.get('code') + redirect_url = request.GET.get('redirect_url') + login_url = reverse('authentication:login') + + if not self.verify_state(): + return self.get_verify_state_failed_response(redirect_url) + + user_id, other_info = self.client.get_user_id_by_code(code) + if not user_id: + # 正常流程不会出这个错误,hack 行为 + err = self.MSG_NOT_FOUND_USER_FROM_CLIENT_ERR + response = self.get_failed_response(login_url, title=err, msg=err) + return response + + user = get_object_or_none(User, **{f'{self.USER_TYPE}_id': user_id}) + if user is None: + user, err = self.create_user_if_not_exist(user_id, other_info=other_info) + if err is not None: + response = self.get_failed_response(login_url, title=err[0], msg=err[1]) + return response + + try: + self.check_oauth2_auth(user, getattr(settings, self.AUTH_BACKEND)) + except errors.AuthFailedError as e: + self.set_login_failed_mark() + msg = e.msg + response = self.get_failed_response(login_url, title=msg, msg=msg) + return response + return self.redirect_to_guard_view() diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index c764c2138..ba128834f 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -22,7 +22,8 @@ from authentication import errors from authentication.mixins import AuthMixin from authentication.const import ConfirmType from authentication.notifications import OAuthBindMessage -from .mixins import METAMixin + +from .mixins import METAMixin, QRLoginCallbackMixin logger = get_logger(__file__) @@ -208,45 +209,20 @@ class WeComQRLoginView(WeComQRMixin, METAMixin, View): return HttpResponseRedirect(url) -class WeComQRLoginCallbackView(AuthMixin, WeComQRMixin, View): +class WeComQRLoginCallbackView(QRLoginCallbackMixin, AuthMixin, WeComQRMixin, View): permission_classes = (AllowAny,) - def get(self, request: HttpRequest): - code = request.GET.get('code') - redirect_url = request.GET.get('redirect_url') - login_url = reverse('authentication:login') + CLIENT_INFO = ( + WeCom, {'corpid': 'WECOM_CORPID', 'corpsecret': 'WECOM_SECRET', 'agentid': 'WECOM_AGENTID'} + ) + USER_TYPE = 'wecom' + AUTH_BACKEND = 'AUTH_BACKEND_WECOM' + CREATE_USER_IF_NOT_EXIST = 'WECOM_CREATE_USER_IF_NOT_EXIST' - if not self.verify_state(): - return self.get_verify_state_failed_response(redirect_url) - - wecom = WeCom( - corpid=settings.WECOM_CORPID, - corpsecret=settings.WECOM_SECRET, - agentid=settings.WECOM_AGENTID - ) - wecom_userid, __ = wecom.get_user_id_by_code(code) - if not wecom_userid: - # 正常流程不会出这个错误,hack 行为 - msg = _('Failed to get user from WeCom') - response = self.get_failed_response(login_url, title=msg, msg=msg) - return response - - user = get_object_or_none(User, wecom_id=wecom_userid) - if user is None: - title = _('WeCom is not bound') - msg = _('Please login with a password and then bind the WeCom') - response = self.get_failed_response(login_url, title=title, msg=msg) - return response - - try: - self.check_oauth2_auth(user, settings.AUTH_BACKEND_WECOM) - except errors.AuthFailedError as e: - self.set_login_failed_mark() - msg = e.msg - response = self.get_failed_response(login_url, title=msg, msg=msg) - return response - - return self.redirect_to_guard_view() + MSG_CLIENT_ERR = _('WeCom Error') + MSG_USER_NOT_BOUND_ERR = _('WeCom is not bound') + MSG_USER_NEED_BOUND_WARNING = _('Please login with a password and then bind the WeCom') + MSG_NOT_FOUND_USER_FROM_CLIENT_ERR = _('Failed to get user from WeCom') class WeComOAuthLoginView(WeComOAuthMixin, View): diff --git a/apps/common/sdk/im/dingtalk/__init__.py b/apps/common/sdk/im/dingtalk/__init__.py index d41e73221..4f54e1ea2 100644 --- a/apps/common/sdk/im/dingtalk/__init__.py +++ b/apps/common/sdk/im/dingtalk/__init__.py @@ -5,6 +5,7 @@ import base64 from common.utils import get_logger from common.sdk.im.utils import digest, as_request from common.sdk.im.mixin import BaseRequest +from users.utils import construct_user_email logger = get_logger(__file__) @@ -35,6 +36,7 @@ class URL: SEND_MESSAGE = 'https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2' GET_SEND_MSG_PROGRESS = 'https://oapi.dingtalk.com/topapi/message/corpconversation/getsendprogress' GET_USERID_BY_UNIONID = 'https://oapi.dingtalk.com/topapi/user/getbyunionid' + GET_USER_INFO_BY_USER_ID = 'https://oapi.dingtalk.com/topapi/v2/user/get' class DingTalkRequests(BaseRequest): @@ -129,11 +131,11 @@ class DingTalk: data = self._request.post(URL.GET_USER_INFO_BY_CODE, json=body, with_sign=True) return data['user_info'] - def get_userid_by_code(self, code): + def get_user_id_by_code(self, code): user_info = self.get_userinfo_bycode(code) unionid = user_info['unionid'] userid = self.get_userid_by_unionid(unionid) - return userid + return userid, None def get_userid_by_unionid(self, unionid): body = { @@ -195,3 +197,18 @@ class DingTalk: data = self._request.post(URL.GET_SEND_MSG_PROGRESS, json=body, with_token=True) return data + + def get_user_detail(self, user_id, **kwargs): + # https://open.dingtalk.com/document/orgapp/query-user-details + body = {'userid': user_id} + data = self._request.post( + URL.GET_USER_INFO_BY_USER_ID, json=body, with_token=True + ) + data = data['result'] + username = user_id + name = data.get('name', username) + email = data.get('email') or data.get('org_email') + email = construct_user_email(username, email) + return { + 'username': username, 'name': name, 'email': email + } diff --git a/apps/common/sdk/im/feishu/__init__.py b/apps/common/sdk/im/feishu/__init__.py index cb01b66da..ef3a419b6 100644 --- a/apps/common/sdk/im/feishu/__init__.py +++ b/apps/common/sdk/im/feishu/__init__.py @@ -1,9 +1,9 @@ import json -from django.utils.translation import ugettext_lazy as _ from rest_framework.exceptions import APIException from django.conf import settings +from users.utils import construct_user_email from common.utils.common import get_logger from common.sdk.im.utils import digest from common.sdk.im.mixin import RequestMixin, BaseRequest @@ -30,13 +30,16 @@ class URL: return f'{self.host}/open-apis/auth/v3/tenant_access_token/internal/' @property - def get_user_info_by_code(self): + def get_userinfo_by_code(self): return f'{self.host}/open-apis/authen/v1/access_token' @property def send_message(self): return f'{self.host}/open-apis/im/v1/messages' + def get_user_detail(self, user_id): + return f'{self.host}/open-apis/contact/v3/users/{user_id}' + class ErrorCode: INVALID_APP_ACCESS_TOKEN = 99991664 @@ -103,10 +106,10 @@ class FeiShu(RequestMixin): 'code': code } - data = self._requests.post(URL().get_user_info_by_code, json=body, check_errcode_is_0=False) + data = self._requests.post(URL().get_userinfo_by_code, json=body, check_errcode_is_0=False) self._requests.check_errcode_is_0(data) - return data['data']['user_id'] + return data['data']['user_id'], data['data'] def send_text(self, user_ids, msg): params = { @@ -130,3 +133,15 @@ class FeiShu(RequestMixin): logger.exception(e) invalid_users.append(user_id) return invalid_users + + @staticmethod + def get_user_detail(user_id, **kwargs): + # get_user_id_by_code 已经返回个人信息,这里直接解析 + data = kwargs['other_info'] + username = user_id + name = data.get('name', username) + email = data.get('email') or data.get('enterprise_email') + email = construct_user_email(username, email) + return { + 'username': username, 'name': name, 'email': email + } diff --git a/apps/common/sdk/im/wecom/__init__.py b/apps/common/sdk/im/wecom/__init__.py index bc925508e..5d2d6a4c1 100644 --- a/apps/common/sdk/im/wecom/__init__.py +++ b/apps/common/sdk/im/wecom/__init__.py @@ -3,8 +3,9 @@ from typing import Iterable, AnyStr from django.utils.translation import ugettext_lazy as _ from rest_framework.exceptions import APIException +from users.utils import construct_user_email from common.utils.common import get_logger -from common.sdk.im.utils import digest, DictWrapper, update_values, set_default +from common.sdk.im.utils import digest, update_values from common.sdk.im.mixin import RequestMixin, BaseRequest logger = get_logger(__name__) @@ -151,10 +152,7 @@ class WeCom(RequestMixin): def get_user_id_by_code(self, code): # # https://open.work.weixin.qq.com/api/doc/90000/90135/91437 - - params = { - 'code': code, - } + params = {'code': code} data = self._requests.get(URL.GET_USER_ID_BY_CODE, params=params, check_errcode_is_0=False) errcode = data['errcode'] @@ -175,12 +173,15 @@ class WeCom(RequestMixin): logger.error(f'WeCom response 200 but get field from json error: fields=UserId|OpenId') raise WeComError - def get_user_detail(self, id): + def get_user_detail(self, user_id, **kwargs): # https://open.work.weixin.qq.com/api/doc/90000/90135/90196 - - params = { - 'userid': id, + params = {'userid': user_id} + data = self._requests.get(URL.GET_USER_DETAIL, params) + username = data.get('userid') + name = data.get('name', username) + email = data.get('email') or data.get('biz_mail') + email = construct_user_email(username, email) + return { + 'username': username, 'name': name, 'email': email } - data = self._requests.get(URL.GET_USER_DETAIL, params) - return data diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 343224675..ba2593efd 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -365,18 +365,21 @@ class Config(dict): 'WECOM_CORPID': '', 'WECOM_AGENTID': '', 'WECOM_SECRET': '', + 'WECOM_CREATE_USER_IF_NOT_EXIST': False, # 钉钉 'AUTH_DINGTALK': False, 'DINGTALK_AGENTID': '', 'DINGTALK_APPKEY': '', 'DINGTALK_APPSECRET': '', + 'DINGTALK_CREATE_USER_IF_NOT_EXIST': False, # 飞书 'AUTH_FEISHU': False, 'FEISHU_APP_ID': '', 'FEISHU_APP_SECRET': '', 'FEISHU_VERSION': 'feishu', + 'FEISHU_CREATE_USER_IF_NOT_EXIST': False, 'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS / SAML2 'LOGIN_REDIRECT_MSG_ENABLED': True, diff --git a/apps/settings/serializers/auth/dingtalk.py b/apps/settings/serializers/auth/dingtalk.py index 4ec7814a1..f74d267b8 100644 --- a/apps/settings/serializers/auth/dingtalk.py +++ b/apps/settings/serializers/auth/dingtalk.py @@ -13,3 +13,6 @@ class DingTalkSettingSerializer(serializers.Serializer): DINGTALK_APPKEY = serializers.CharField(max_length=256, required=True, label='AppKey') DINGTALK_APPSECRET = EncryptedField(max_length=256, required=False, label='AppSecret') AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('Enable DingTalk Auth')) + DINGTALK_CREATE_USER_IF_NOT_EXIST = serializers.BooleanField( + default=False, label=_('Create user if not') + ) diff --git a/apps/settings/serializers/auth/feishu.py b/apps/settings/serializers/auth/feishu.py index a06d41b23..0e12f04ab 100644 --- a/apps/settings/serializers/auth/feishu.py +++ b/apps/settings/serializers/auth/feishu.py @@ -19,3 +19,6 @@ class FeiShuSettingSerializer(serializers.Serializer): FEISHU_VERSION = serializers.ChoiceField( choices=VERSION_CHOICES, default='feishu', label=_('Version') ) + FEISHU_CREATE_USER_IF_NOT_EXIST = serializers.BooleanField( + default=False, label=_('Create user if not') + ) diff --git a/apps/settings/serializers/auth/wecom.py b/apps/settings/serializers/auth/wecom.py index 462b17db6..3e57683a6 100644 --- a/apps/settings/serializers/auth/wecom.py +++ b/apps/settings/serializers/auth/wecom.py @@ -13,3 +13,6 @@ class WeComSettingSerializer(serializers.Serializer): WECOM_AGENTID = serializers.CharField(max_length=256, required=True, label='agentid') WECOM_SECRET = EncryptedField(max_length=256, required=False, label='secret') AUTH_WECOM = serializers.BooleanField(default=False, label=_('Enable WeCom Auth')) + WECOM_CREATE_USER_IF_NOT_EXIST = serializers.BooleanField( + default=False, label=_('Create user if not') + ) diff --git a/apps/users/migrations/0040_alter_user_source.py b/apps/users/migrations/0040_alter_user_source.py index 56ad9befc..cb90c4971 100644 --- a/apps/users/migrations/0040_alter_user_source.py +++ b/apps/users/migrations/0040_alter_user_source.py @@ -13,6 +13,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='user', name='source', - field=models.CharField(choices=[('local', 'Local'), ('ldap', 'LDAP/AD'), ('openid', 'OpenID'), ('radius', 'Radius'), ('cas', 'CAS'), ('saml2', 'SAML2'), ('oauth2', 'OAuth2'), ('custom', 'Custom')], default='local', max_length=30, verbose_name='Source'), + field=models.CharField(choices=[('local', 'Local'), ('ldap', 'LDAP/AD'), ('openid', 'OpenID'), ('radius', 'Radius'), ('cas', 'CAS'), ('saml2', 'SAML2'), ('oauth2', 'OAuth2'), ('wecom', 'WeCom'), ('dingtalk', 'DingTalk'), ('feishu', 'FeiShu'), ('custom', 'Custom')], default='local', max_length=30, verbose_name='Source'), ), ] diff --git a/apps/users/models/user.py b/apps/users/models/user.py index b487365e5..efe6ca570 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -677,14 +677,15 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser): cas = 'cas', 'CAS' saml2 = 'saml2', 'SAML2' oauth2 = 'oauth2', 'OAuth2' + wecom = 'wecom', _('WeCom') + dingtalk = 'dingtalk', _('DingTalk') + feishu = 'feishu', _('FeiShu') custom = 'custom', 'Custom' SOURCE_BACKEND_MAPPING = { Source.local: [ settings.AUTH_BACKEND_MODEL, settings.AUTH_BACKEND_PUBKEY, - settings.AUTH_BACKEND_WECOM, - settings.AUTH_BACKEND_DINGTALK, ], Source.ldap: [ settings.AUTH_BACKEND_LDAP @@ -705,6 +706,15 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser): Source.oauth2: [ settings.AUTH_BACKEND_OAUTH2 ], + Source.wecom: [ + settings.AUTH_BACKEND_WECOM + ], + Source.feishu: [ + settings.AUTH_BACKEND_FEISHU + ], + Source.dingtalk: [ + settings.AUTH_BACKEND_DINGTALK + ], Source.custom: [ settings.AUTH_BACKEND_CUSTOM ] From 7a97496f706147b70ee0596f6fe12613460d5a7a Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Fri, 28 Apr 2023 14:04:51 +0800 Subject: [PATCH 31/88] =?UTF-8?q?perf:=20=E5=8F=98=E9=87=8F=E5=90=8D?= =?UTF-8?q?=E8=BF=98=E5=8E=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/sdk/im/feishu/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/common/sdk/im/feishu/__init__.py b/apps/common/sdk/im/feishu/__init__.py index ef3a419b6..087a2fb88 100644 --- a/apps/common/sdk/im/feishu/__init__.py +++ b/apps/common/sdk/im/feishu/__init__.py @@ -30,7 +30,7 @@ class URL: return f'{self.host}/open-apis/auth/v3/tenant_access_token/internal/' @property - def get_userinfo_by_code(self): + def get_user_info_by_code(self): return f'{self.host}/open-apis/authen/v1/access_token' @property @@ -106,7 +106,7 @@ class FeiShu(RequestMixin): 'code': code } - data = self._requests.post(URL().get_userinfo_by_code, json=body, check_errcode_is_0=False) + data = self._requests.post(URL().get_user_info_by_code, json=body, check_errcode_is_0=False) self._requests.check_errcode_is_0(data) return data['data']['user_id'], data['data'] From 3367f65b02ec467554a7805e1a135c3c220cfcbe Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Fri, 28 Apr 2023 16:24:33 +0800 Subject: [PATCH 32/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E6=8A=BD=E7=A6=BBcallback=5Fbase=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/base.py | 105 +++++++++++++++++++++++++ apps/authentication/views/dingtalk.py | 48 ++++-------- apps/authentication/views/feishu.py | 52 ++++-------- apps/authentication/views/mixins.py | 109 +++----------------------- apps/authentication/views/wecom.py | 48 ++++-------- 5 files changed, 161 insertions(+), 201 deletions(-) create mode 100644 apps/authentication/views/base.py diff --git a/apps/authentication/views/base.py b/apps/authentication/views/base.py new file mode 100644 index 000000000..28b4827a1 --- /dev/null +++ b/apps/authentication/views/base.py @@ -0,0 +1,105 @@ +from functools import lru_cache + +from rest_framework.request import Request +from django.utils.translation import ugettext_lazy as _ +from django.conf import settings +from django.db.utils import IntegrityError +from django.views import View + +from authentication import errors +from authentication.mixins import AuthMixin +from users.models import User +from common.utils.django import reverse, get_object_or_none +from common.utils import get_logger + +from .mixins import FlashMessageMixin + + +logger = get_logger(__file__) + + +class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): + + def __init__(self): + super().__init__() + self.client_type = None + self.client_auth_params = {} + self.user_type = None + self.auth_backend = None + self.create_user_if_not_exist_setting = '' + # 提示信息 + self.msg_client_err = _('Error') + self.msg_user_not_bound_err = _('Error') + self.msg_user_need_bound_warning = _('Error') + self.msg_not_found_user_from_client_err = _('Error') + + def verify_state(self): + raise NotImplementedError + + def get_verify_state_failed_response(self, redirect_uri): + raise NotImplementedError + + @property + @lru_cache(maxsize=1) + def client(self): + if self.client_type is None or not self.client_auth_params: + raise NotImplementedError + client_init = {k: getattr(settings, v) for k, v in self.client_auth_params.items()} + return self.client_type(**client_init) + + def create_user_if_not_exist(self, user_id, **kwargs): + user = None + if not getattr(settings, self.create_user_if_not_exist_setting): + title = self.msg_client_err + msg = self.msg_user_need_bound_warning + return user, (title, msg) + + user_attr = self.client.get_user_detail(user_id, **kwargs) + try: + user, create = User.objects.get_or_create( + username=user_attr['username'], defaults=user_attr + ) + setattr(user, f'{self.user_type}_id', user_id) + if create: + setattr(user, 'source', self.user_type) + user.save() + except IntegrityError as err: + logger.error(f'{self.msg_client_err}: create user error: {err}') + + if user is None: + title = self.msg_client_err + msg = _('If you have any question, please contact the administrator') + return user, (title, msg) + + return user, None + + def get(self, request: Request): + code = request.GET.get('code') + redirect_url = request.GET.get('redirect_url') + login_url = reverse('authentication:login') + + if not self.verify_state(): + return self.get_verify_state_failed_response(redirect_url) + + user_id, other_info = self.client.get_user_id_by_code(code) + if not user_id: + # 正常流程不会出这个错误,hack 行为 + err = self.msg_not_found_user_from_client_err + response = self.get_failed_response(login_url, title=err, msg=err) + return response + + user = get_object_or_none(User, **{f'{self.user_type}_id': user_id}) + if user is None: + user, err = self.create_user_if_not_exist(user_id, other_info=other_info) + if err is not None: + response = self.get_failed_response(login_url, title=err[0], msg=err[1]) + return response + + try: + self.check_oauth2_auth(user, getattr(settings, self.auth_backend)) + except errors.AuthFailedError as e: + self.set_login_failed_mark() + msg = e.msg + response = self.get_failed_response(login_url, title=msg, msg=msg) + return response + return self.redirect_to_guard_view() diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index 7b421851a..50f0ddb35 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -23,14 +23,15 @@ from common.utils.random import random_string from users.models import User from users.views import UserVerifyPasswordView -from .mixins import METAMixin, QRLoginCallbackMixin +from .base import BaseLoginCallbackView +from .mixins import METAMixin, FlashMessageMixin logger = get_logger(__file__) DINGTALK_STATE_SESSION_KEY = '_dingtalk_state' -class DingTalkBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, View): +class DingTalkBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashMessageMixin, View): def dispatch(self, request, *args, **kwargs): try: return super().dispatch(request, *args, **kwargs) @@ -56,26 +57,6 @@ class DingTalkBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, Vie msg = _("The system configuration is incorrect. Please contact your administrator") return self.get_failed_response(redirect_uri, msg, msg) - @staticmethod - def get_success_response(redirect_url, title, msg): - message_data = { - 'title': title, - 'message': msg, - 'interval': 5, - 'redirect_url': redirect_url, - } - return FlashMessageUtil.gen_and_redirect_to(message_data) - - @staticmethod - def get_failed_response(redirect_url, title, msg): - message_data = { - 'title': title, - 'error': msg, - 'interval': 5, - 'redirect_url': redirect_url, - } - return FlashMessageUtil.gen_and_redirect_to(message_data) - def get_already_bound_response(self, redirect_url): msg = _('DingTalk is already bound') response = self.get_failed_response(redirect_url, msg, msg) @@ -214,20 +195,21 @@ class DingTalkQRLoginView(DingTalkQRMixin, METAMixin, View): return HttpResponseRedirect(url) -class DingTalkQRLoginCallbackView(QRLoginCallbackMixin, AuthMixin, DingTalkQRMixin, View): +class DingTalkQRLoginCallbackView(DingTalkQRMixin, BaseLoginCallbackView): permission_classes = (AllowAny,) - CLIENT_INFO = ( - DingTalk, {'appid': 'DINGTALK_APPKEY', 'appsecret': 'DINGTALK_APPSECRET', 'agentid': 'DINGTALK_AGENTID'} - ) - USER_TYPE = 'dingtalk' - AUTH_BACKEND = 'AUTH_BACKEND_DINGTALK' - CREATE_USER_IF_NOT_EXIST = 'DINGTALK_CREATE_USER_IF_NOT_EXIST' + def __init__(self): + super(DingTalkQRLoginCallbackView, self).__init__() + self.client_type = DingTalk + self.client_auth_params = {'appid': 'DINGTALK_APPKEY', 'appsecret': 'DINGTALK_APPSECRET', 'agentid': 'DINGTALK_AGENTID'} + self.user_type = 'dingtalk' + self.auth_backend = 'AUTH_BACKEND_DINGTALK' + self.create_user_if_not_exist_setting = 'DINGTALK_CREATE_USER_IF_NOT_EXIST' - MSG_CLIENT_ERR = _('DingTalk Error') - MSG_USER_NOT_BOUND_ERR = _('DingTalk is not bound') - MSG_USER_NEED_BOUND_WARNING = _('Please login with a password and then bind the DingTalk') - MSG_NOT_FOUND_USER_FROM_CLIENT_ERR = _('Failed to get user from DingTalk') + self.msg_client_err = _('DingTalk Error') + self.msg_user_not_bound_err = _('DingTalk is not bound') + self.msg_user_need_bound_warning = _('Please login with a password and then bind the DingTalk') + self.msg_not_found_user_from_client_err = _('Failed to get user from DingTalk') class DingTalkOAuthLoginView(DingTalkOAuthMixin, View): diff --git a/apps/authentication/views/feishu.py b/apps/authentication/views/feishu.py index 4ba777097..7c94e0269 100644 --- a/apps/authentication/views/feishu.py +++ b/apps/authentication/views/feishu.py @@ -10,25 +10,25 @@ from rest_framework.exceptions import APIException from rest_framework.permissions import AllowAny, IsAuthenticated from authentication.const import ConfirmType -from authentication.mixins import AuthMixin from authentication.notifications import OAuthBindMessage from common.views.mixins import PermissionsMixin, UserConfirmRequiredExceptionMixin from common.permissions import UserConfirmation from common.sdk.im.feishu import URL, FeiShu -from common.utils import FlashMessageUtil, get_logger +from common.utils import get_logger from common.utils.common import get_request_ip from common.utils.django import reverse from common.utils.random import random_string from users.views import UserVerifyPasswordView -from .mixins import QRLoginCallbackMixin +from .base import BaseLoginCallbackView +from .mixins import FlashMessageMixin logger = get_logger(__file__) FEISHU_STATE_SESSION_KEY = '_feishu_state' -class FeiShuQRMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, View): +class FeiShuQRMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashMessageMixin, View): def dispatch(self, request, *args, **kwargs): try: return super().dispatch(request, *args, **kwargs) @@ -63,26 +63,6 @@ class FeiShuQRMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, View): url = URL().authen + '?' + urlencode(params) return url - @staticmethod - def get_success_response(redirect_url, title, msg): - message_data = { - 'title': title, - 'message': msg, - 'interval': 5, - 'redirect_url': redirect_url, - } - return FlashMessageUtil.gen_and_redirect_to(message_data) - - @staticmethod - def get_failed_response(redirect_url, title, msg): - message_data = { - 'title': title, - 'error': msg, - 'interval': 5, - 'redirect_url': redirect_url, - } - return FlashMessageUtil.gen_and_redirect_to(message_data) - def get_already_bound_response(self, redirect_url): msg = _('FeiShu is already bound') response = self.get_failed_response(redirect_url, msg, msg) @@ -93,7 +73,6 @@ class FeiShuQRBindView(FeiShuQRMixin, View): permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.ReLogin)) def get(self, request: HttpRequest): - user = request.user redirect_url = request.GET.get('redirect_url') redirect_uri = reverse('authentication:feishu-qr-bind-callback', external=True) @@ -176,17 +155,18 @@ class FeiShuQRLoginView(FeiShuQRMixin, View): return HttpResponseRedirect(url) -class FeiShuQRLoginCallbackView(QRLoginCallbackMixin, AuthMixin, FeiShuQRMixin, View): +class FeiShuQRLoginCallbackView(FeiShuQRMixin, BaseLoginCallbackView): permission_classes = (AllowAny,) - CLIENT_INFO = ( - FeiShu, {'app_id': 'FEISHU_APP_ID', 'app_secret': 'FEISHU_APP_SECRET'} - ) - USER_TYPE = 'feishu' - AUTH_BACKEND = 'AUTH_BACKEND_FEISHU' - CREATE_USER_IF_NOT_EXIST = 'FEISHU_CREATE_USER_IF_NOT_EXIST' + def __init__(self): + super(FeiShuQRLoginCallbackView, self).__init__() + self.client_type = FeiShu + self.client_auth_params = {'app_id': 'FEISHU_APP_ID', 'app_secret': 'FEISHU_APP_SECRET'} + self.user_type = 'feishu' + self.auth_backend = 'AUTH_BACKEND_FEISHU' + self.create_user_if_not_exist_setting = 'FEISHU_CREATE_USER_IF_NOT_EXIST' - MSG_CLIENT_ERR = _('FeiShu Error') - MSG_USER_NOT_BOUND_ERR = _('FeiShu is not bound') - MSG_USER_NEED_BOUND_WARNING = _('Please login with a password and then bind the FeiShu') - MSG_NOT_FOUND_USER_FROM_CLIENT_ERR = _('Failed to get user from FeiShu') + self.msg_client_err = _('FeiShu Error') + self.msg_user_not_bound_err = _('FeiShu is not bound') + self.msg_user_need_bound_warning = _('Please login with a password and then bind the FeiShu') + self.msg_not_found_user_from_client_err = _('Failed to get user from FeiShu') diff --git a/apps/authentication/views/mixins.py b/apps/authentication/views/mixins.py index aa79eef1c..86d8abcf2 100644 --- a/apps/authentication/views/mixins.py +++ b/apps/authentication/views/mixins.py @@ -1,19 +1,6 @@ # -*- coding: utf-8 -*- # -from functools import lru_cache - -from rest_framework.request import Request -from django.utils.translation import ugettext_lazy as _ -from django.conf import settings -from django.db.utils import IntegrityError - -from authentication import errors -from users.models import User -from common.utils.django import reverse, get_object_or_none -from common.utils import get_logger - - -logger = get_logger(__file__) +from common.utils import FlashMessageUtil class METAMixin: @@ -27,90 +14,14 @@ class METAMixin: return next_url -class Client: - get_user_id_by_code: callable - get_user_detail: callable +class FlashMessageMixin: + @staticmethod + def get_response(redirect_url, title, msg, m_type='message'): + message_data = {'title': title, 'interval': 5, 'redirect_url': redirect_url, m_type: msg} + return FlashMessageUtil.gen_and_redirect_to(message_data) + def get_success_response(self, redirect_url, title, msg): + self.get_response(redirect_url, title, msg) -class QRLoginCallbackMixin: - verify_state: callable - get_verify_state_failed_response: callable - get_failed_response: callable - check_oauth2_auth: callable - set_login_failed_mark: callable - redirect_to_guard_view: callable - # 属性 - _client: Client - CLIENT_INFO: tuple - USER_TYPE: str - AUTH_BACKEND: str - CREATE_USER_IF_NOT_EXIST: str - # 提示信息 - MSG_CLIENT_ERR: str - MSG_USER_NOT_BOUND_ERR: str - MSG_USER_NEED_BOUND_WARNING: str - MSG_NOT_FOUND_USER_FROM_CLIENT_ERR: str - - @property - @lru_cache(maxsize=1) - def client(self): - client_type, client_init = self.CLIENT_INFO - client_init = {k: getattr(settings, v) for k, v in client_init.items()} - return client_type(**client_init) - - def create_user_if_not_exist(self, user_id, **kwargs): - user = None - if not getattr(settings, self.CREATE_USER_IF_NOT_EXIST): - title = self.MSG_CLIENT_ERR - msg = self.MSG_USER_NEED_BOUND_WARNING - return user, (title, msg) - - user_attr = self.client.get_user_detail(user_id, **kwargs) - try: - user, create = User.objects.get_or_create( - username=user_attr['username'], defaults=user_attr - ) - setattr(user, f'{self.USER_TYPE}_id', user_id) - if create: - setattr(user, 'source', self.USER_TYPE) - user.save() - except IntegrityError as err: - logger.error(f'{self.MSG_CLIENT_ERR}: create user error: {err}') - - if user is None: - title = self.MSG_CLIENT_ERR - msg = _('If you have any question, please contact the administrator') - return user, (title, msg) - - return user, None - - def get(self, request: Request): - code = request.GET.get('code') - redirect_url = request.GET.get('redirect_url') - login_url = reverse('authentication:login') - - if not self.verify_state(): - return self.get_verify_state_failed_response(redirect_url) - - user_id, other_info = self.client.get_user_id_by_code(code) - if not user_id: - # 正常流程不会出这个错误,hack 行为 - err = self.MSG_NOT_FOUND_USER_FROM_CLIENT_ERR - response = self.get_failed_response(login_url, title=err, msg=err) - return response - - user = get_object_or_none(User, **{f'{self.USER_TYPE}_id': user_id}) - if user is None: - user, err = self.create_user_if_not_exist(user_id, other_info=other_info) - if err is not None: - response = self.get_failed_response(login_url, title=err[0], msg=err[1]) - return response - - try: - self.check_oauth2_auth(user, getattr(settings, self.AUTH_BACKEND)) - except errors.AuthFailedError as e: - self.set_login_failed_mark() - msg = e.msg - response = self.get_failed_response(login_url, title=msg, msg=msg) - return response - return self.redirect_to_guard_view() + def get_failed_response(self, redirect_url, title, msg): + self.get_response(redirect_url, title, msg, 'error') diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index ba128834f..27ea16bed 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -23,14 +23,15 @@ from authentication.mixins import AuthMixin from authentication.const import ConfirmType from authentication.notifications import OAuthBindMessage -from .mixins import METAMixin, QRLoginCallbackMixin +from .base import BaseLoginCallbackView +from .mixins import METAMixin, FlashMessageMixin logger = get_logger(__file__) WECOM_STATE_SESSION_KEY = '_wecom_state' -class WeComBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, View): +class WeComBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashMessageMixin, View): def dispatch(self, request, *args, **kwargs): try: return super().dispatch(request, *args, **kwargs) @@ -56,26 +57,6 @@ class WeComBaseMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, View): msg = _("The system configuration is incorrect. Please contact your administrator") return self.get_failed_response(redirect_uri, msg, msg) - @staticmethod - def get_success_response(redirect_url, title, msg): - message_data = { - 'title': title, - 'message': msg, - 'interval': 5, - 'redirect_url': redirect_url, - } - return FlashMessageUtil.gen_and_redirect_to(message_data) - - @staticmethod - def get_failed_response(redirect_url, title, msg): - message_data = { - 'title': title, - 'error': msg, - 'interval': 5, - 'redirect_url': redirect_url, - } - return FlashMessageUtil.gen_and_redirect_to(message_data) - def get_already_bound_response(self, redirect_url): msg = _('WeCom is already bound') response = self.get_failed_response(redirect_url, msg, msg) @@ -209,20 +190,21 @@ class WeComQRLoginView(WeComQRMixin, METAMixin, View): return HttpResponseRedirect(url) -class WeComQRLoginCallbackView(QRLoginCallbackMixin, AuthMixin, WeComQRMixin, View): +class WeComQRLoginCallbackView(WeComQRMixin, BaseLoginCallbackView): permission_classes = (AllowAny,) - CLIENT_INFO = ( - WeCom, {'corpid': 'WECOM_CORPID', 'corpsecret': 'WECOM_SECRET', 'agentid': 'WECOM_AGENTID'} - ) - USER_TYPE = 'wecom' - AUTH_BACKEND = 'AUTH_BACKEND_WECOM' - CREATE_USER_IF_NOT_EXIST = 'WECOM_CREATE_USER_IF_NOT_EXIST' + def __init__(self): + super(WeComQRLoginCallbackView, self).__init__() + self.client_type = WeCom + self.client_auth_params = {'corpid': 'WECOM_CORPID', 'corpsecret': 'WECOM_SECRET', 'agentid': 'WECOM_AGENTID'} + self.user_type = 'wecom' + self.auth_backend = 'AUTH_BACKEND_WECOM' + self.create_user_if_not_exist_setting = 'WECOM_CREATE_USER_IF_NOT_EXIST' - MSG_CLIENT_ERR = _('WeCom Error') - MSG_USER_NOT_BOUND_ERR = _('WeCom is not bound') - MSG_USER_NEED_BOUND_WARNING = _('Please login with a password and then bind the WeCom') - MSG_NOT_FOUND_USER_FROM_CLIENT_ERR = _('Failed to get user from WeCom') + self.msg_client_err = _('WeCom Error') + self.msg_user_not_bound_err = _('WeCom is not bound') + self.msg_user_need_bound_warning = _('Please login with a password and then bind the WeCom') + self.msg_not_found_user_from_client_err = _('Failed to get user from WeCom') class WeComOAuthLoginView(WeComOAuthMixin, View): From b376491020b7c6fc42577f5510835d41f7cf6f73 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Fri, 28 Apr 2023 16:26:03 +0800 Subject: [PATCH 33/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96user=5Ftype?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/authentication/views/base.py b/apps/authentication/views/base.py index 28b4827a1..e93951e00 100644 --- a/apps/authentication/views/base.py +++ b/apps/authentication/views/base.py @@ -24,7 +24,7 @@ class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): super().__init__() self.client_type = None self.client_auth_params = {} - self.user_type = None + self.user_type = '' self.auth_backend = None self.create_user_if_not_exist_setting = '' # 提示信息 From 38a9b90a8b17ca511439b85b9cada3009d24c74a Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Fri, 28 Apr 2023 16:32:03 +0800 Subject: [PATCH 34/88] =?UTF-8?q?fix:=20flash=5Fmessage=20=E5=B8=A6?= =?UTF-8?q?=E4=B8=8A=E8=BF=94=E5=9B=9E=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/mixins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/authentication/views/mixins.py b/apps/authentication/views/mixins.py index 86d8abcf2..d56206dcd 100644 --- a/apps/authentication/views/mixins.py +++ b/apps/authentication/views/mixins.py @@ -21,7 +21,7 @@ class FlashMessageMixin: return FlashMessageUtil.gen_and_redirect_to(message_data) def get_success_response(self, redirect_url, title, msg): - self.get_response(redirect_url, title, msg) + return self.get_response(redirect_url, title, msg) def get_failed_response(self, redirect_url, title, msg): - self.get_response(redirect_url, title, msg, 'error') + return self.get_response(redirect_url, title, msg, 'error') From 1fef9a2cf0d364fb5a2c49a81a37b938e5e7e382 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Fri, 28 Apr 2023 16:41:13 +0800 Subject: [PATCH 35/88] =?UTF-8?q?perf:=20=E5=8E=BB=E6=8E=89=E4=B8=8D?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E5=AF=BC=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/dingtalk.py | 2 +- apps/authentication/views/wecom.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index 50f0ddb35..34086e040 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -16,7 +16,7 @@ from authentication.notifications import OAuthBindMessage from common.views.mixins import PermissionsMixin, UserConfirmRequiredExceptionMixin from common.permissions import UserConfirmation from common.sdk.im.dingtalk import URL, DingTalk -from common.utils import FlashMessageUtil, get_logger +from common.utils import get_logger from common.utils.common import get_request_ip from common.utils.django import get_object_or_none, reverse from common.utils.random import random_string diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index 27ea16bed..1b1a8a3d0 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -10,7 +10,7 @@ from rest_framework.exceptions import APIException from users.models import User from users.views import UserVerifyPasswordView -from common.utils import get_logger, FlashMessageUtil +from common.utils import get_logger from common.utils.random import random_string from common.utils.django import reverse, get_object_or_none from common.sdk.im.wecom import URL From 3f1858a1055770da5ec747b54b9094cbbb0d4570 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Mon, 8 May 2023 15:36:33 +0800 Subject: [PATCH 36/88] =?UTF-8?q?=E5=B0=86=E9=85=8D=E7=BD=AE=E6=94=B9?= =?UTF-8?q?=E5=88=B0=E7=B1=BB=E5=B1=9E=E6=80=A7=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/base.py | 28 +++++++++++++-------------- apps/authentication/views/dingtalk.py | 23 +++++++++++----------- apps/authentication/views/feishu.py | 21 ++++++++++---------- apps/authentication/views/wecom.py | 20 +++++++++---------- 4 files changed, 45 insertions(+), 47 deletions(-) diff --git a/apps/authentication/views/base.py b/apps/authentication/views/base.py index e93951e00..825c37619 100644 --- a/apps/authentication/views/base.py +++ b/apps/authentication/views/base.py @@ -2,6 +2,7 @@ from functools import lru_cache from rest_framework.request import Request from django.utils.translation import ugettext_lazy as _ +from django.utils.module_loading import import_string from django.conf import settings from django.db.utils import IntegrityError from django.views import View @@ -20,18 +21,16 @@ logger = get_logger(__file__) class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): - def __init__(self): - super().__init__() - self.client_type = None - self.client_auth_params = {} - self.user_type = '' - self.auth_backend = None - self.create_user_if_not_exist_setting = '' - # 提示信息 - self.msg_client_err = _('Error') - self.msg_user_not_bound_err = _('Error') - self.msg_user_need_bound_warning = _('Error') - self.msg_not_found_user_from_client_err = _('Error') + client_type_path = '' + client_auth_params = {} + user_type = '' + auth_backend = None + create_user_if_not_exist_setting = '' + # 提示信息 + msg_client_err = _('Error') + msg_user_not_bound_err = _('Error') + msg_user_need_bound_warning = _('Error') + msg_not_found_user_from_client_err = _('Error') def verify_state(self): raise NotImplementedError @@ -42,10 +41,11 @@ class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): @property @lru_cache(maxsize=1) def client(self): - if self.client_type is None or not self.client_auth_params: + if not all([self.client_type_path, self.client_auth_params]): raise NotImplementedError client_init = {k: getattr(settings, v) for k, v in self.client_auth_params.items()} - return self.client_type(**client_init) + client_type = import_string(self.client_type_path) + return client_type(**client_init) def create_user_if_not_exist(self, user_id, **kwargs): user = None diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index 34086e040..a49d2a38f 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -198,18 +198,19 @@ class DingTalkQRLoginView(DingTalkQRMixin, METAMixin, View): class DingTalkQRLoginCallbackView(DingTalkQRMixin, BaseLoginCallbackView): permission_classes = (AllowAny,) - def __init__(self): - super(DingTalkQRLoginCallbackView, self).__init__() - self.client_type = DingTalk - self.client_auth_params = {'appid': 'DINGTALK_APPKEY', 'appsecret': 'DINGTALK_APPSECRET', 'agentid': 'DINGTALK_AGENTID'} - self.user_type = 'dingtalk' - self.auth_backend = 'AUTH_BACKEND_DINGTALK' - self.create_user_if_not_exist_setting = 'DINGTALK_CREATE_USER_IF_NOT_EXIST' + client_type_path = 'common.sdk.im.dingtalk.DingTalk' + client_auth_params = { + 'appid': 'DINGTALK_APPKEY', 'appsecret': 'DINGTALK_APPSECRET', + 'agentid': 'DINGTALK_AGENTID' + } + user_type = 'dingtalk' + auth_backend = 'AUTH_BACKEND_DINGTALK' + create_user_if_not_exist_setting = 'DINGTALK_CREATE_USER_IF_NOT_EXIST' - self.msg_client_err = _('DingTalk Error') - self.msg_user_not_bound_err = _('DingTalk is not bound') - self.msg_user_need_bound_warning = _('Please login with a password and then bind the DingTalk') - self.msg_not_found_user_from_client_err = _('Failed to get user from DingTalk') + msg_client_err = _('DingTalk Error') + msg_user_not_bound_err = _('DingTalk is not bound') + msg_user_need_bound_warning = _('Please login with a password and then bind the DingTalk') + msg_not_found_user_from_client_err = _('Failed to get user from DingTalk') class DingTalkOAuthLoginView(DingTalkOAuthMixin, View): diff --git a/apps/authentication/views/feishu.py b/apps/authentication/views/feishu.py index 7c94e0269..b4c16fb53 100644 --- a/apps/authentication/views/feishu.py +++ b/apps/authentication/views/feishu.py @@ -158,15 +158,14 @@ class FeiShuQRLoginView(FeiShuQRMixin, View): class FeiShuQRLoginCallbackView(FeiShuQRMixin, BaseLoginCallbackView): permission_classes = (AllowAny,) - def __init__(self): - super(FeiShuQRLoginCallbackView, self).__init__() - self.client_type = FeiShu - self.client_auth_params = {'app_id': 'FEISHU_APP_ID', 'app_secret': 'FEISHU_APP_SECRET'} - self.user_type = 'feishu' - self.auth_backend = 'AUTH_BACKEND_FEISHU' - self.create_user_if_not_exist_setting = 'FEISHU_CREATE_USER_IF_NOT_EXIST' + client_type_path = 'common.sdk.im.feishu.FeiShu' + client_auth_params = {'app_id': 'FEISHU_APP_ID', 'app_secret': 'FEISHU_APP_SECRET'} + user_type = 'feishu' + auth_backend = 'AUTH_BACKEND_FEISHU' + create_user_if_not_exist_setting = 'FEISHU_CREATE_USER_IF_NOT_EXIST' + + msg_client_err = _('FeiShu Error') + msg_user_not_bound_err = _('FeiShu is not bound') + msg_user_need_bound_warning = _('Please login with a password and then bind the FeiShu') + msg_not_found_user_from_client_err = _('Failed to get user from FeiShu') - self.msg_client_err = _('FeiShu Error') - self.msg_user_not_bound_err = _('FeiShu is not bound') - self.msg_user_need_bound_warning = _('Please login with a password and then bind the FeiShu') - self.msg_not_found_user_from_client_err = _('Failed to get user from FeiShu') diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index 1b1a8a3d0..b2061a4d6 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -193,18 +193,16 @@ class WeComQRLoginView(WeComQRMixin, METAMixin, View): class WeComQRLoginCallbackView(WeComQRMixin, BaseLoginCallbackView): permission_classes = (AllowAny,) - def __init__(self): - super(WeComQRLoginCallbackView, self).__init__() - self.client_type = WeCom - self.client_auth_params = {'corpid': 'WECOM_CORPID', 'corpsecret': 'WECOM_SECRET', 'agentid': 'WECOM_AGENTID'} - self.user_type = 'wecom' - self.auth_backend = 'AUTH_BACKEND_WECOM' - self.create_user_if_not_exist_setting = 'WECOM_CREATE_USER_IF_NOT_EXIST' + client_type_path = 'common.sdk.im.wecom.WeCom' + client_auth_params = {'corpid': 'WECOM_CORPID', 'corpsecret': 'WECOM_SECRET', 'agentid': 'WECOM_AGENTID'} + user_type = 'wecom' + auth_backend = 'AUTH_BACKEND_WECOM' + create_user_if_not_exist_setting = 'WECOM_CREATE_USER_IF_NOT_EXIST' - self.msg_client_err = _('WeCom Error') - self.msg_user_not_bound_err = _('WeCom is not bound') - self.msg_user_need_bound_warning = _('Please login with a password and then bind the WeCom') - self.msg_not_found_user_from_client_err = _('Failed to get user from WeCom') + msg_client_err = _('WeCom Error') + msg_user_not_bound_err = _('WeCom is not bound') + msg_user_need_bound_warning = _('Please login with a password and then bind the WeCom') + msg_not_found_user_from_client_err = _('Failed to get user from WeCom') class WeComOAuthLoginView(WeComOAuthMixin, View): From 185f33c3e01b77af736eeaa8bb8a035abf437be9 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Mon, 8 May 2023 16:55:04 +0800 Subject: [PATCH 37/88] =?UTF-8?q?perf:=20=E4=BC=81=E4=B8=9A=E5=BE=AE?= =?UTF-8?q?=E4=BF=A1=E3=80=81=E9=92=89=E9=92=89=E3=80=81=E9=A3=9E=E4=B9=A6?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E6=B2=A1=E6=9C=89=E7=94=A8=E6=88=B7=E5=8D=B3?= =?UTF-8?q?=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/views/base.py | 7 ------- apps/authentication/views/dingtalk.py | 2 -- apps/authentication/views/feishu.py | 2 -- apps/authentication/views/wecom.py | 2 -- apps/jumpserver/conf.py | 3 --- apps/settings/serializers/auth/dingtalk.py | 3 --- apps/settings/serializers/auth/feishu.py | 3 --- apps/settings/serializers/auth/wecom.py | 3 --- 8 files changed, 25 deletions(-) diff --git a/apps/authentication/views/base.py b/apps/authentication/views/base.py index 825c37619..e98e41e70 100644 --- a/apps/authentication/views/base.py +++ b/apps/authentication/views/base.py @@ -25,11 +25,9 @@ class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): client_auth_params = {} user_type = '' auth_backend = None - create_user_if_not_exist_setting = '' # 提示信息 msg_client_err = _('Error') msg_user_not_bound_err = _('Error') - msg_user_need_bound_warning = _('Error') msg_not_found_user_from_client_err = _('Error') def verify_state(self): @@ -49,11 +47,6 @@ class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): def create_user_if_not_exist(self, user_id, **kwargs): user = None - if not getattr(settings, self.create_user_if_not_exist_setting): - title = self.msg_client_err - msg = self.msg_user_need_bound_warning - return user, (title, msg) - user_attr = self.client.get_user_detail(user_id, **kwargs) try: user, create = User.objects.get_or_create( diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index a49d2a38f..77cec63fd 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -205,11 +205,9 @@ class DingTalkQRLoginCallbackView(DingTalkQRMixin, BaseLoginCallbackView): } user_type = 'dingtalk' auth_backend = 'AUTH_BACKEND_DINGTALK' - create_user_if_not_exist_setting = 'DINGTALK_CREATE_USER_IF_NOT_EXIST' msg_client_err = _('DingTalk Error') msg_user_not_bound_err = _('DingTalk is not bound') - msg_user_need_bound_warning = _('Please login with a password and then bind the DingTalk') msg_not_found_user_from_client_err = _('Failed to get user from DingTalk') diff --git a/apps/authentication/views/feishu.py b/apps/authentication/views/feishu.py index b4c16fb53..62660764a 100644 --- a/apps/authentication/views/feishu.py +++ b/apps/authentication/views/feishu.py @@ -162,10 +162,8 @@ class FeiShuQRLoginCallbackView(FeiShuQRMixin, BaseLoginCallbackView): client_auth_params = {'app_id': 'FEISHU_APP_ID', 'app_secret': 'FEISHU_APP_SECRET'} user_type = 'feishu' auth_backend = 'AUTH_BACKEND_FEISHU' - create_user_if_not_exist_setting = 'FEISHU_CREATE_USER_IF_NOT_EXIST' msg_client_err = _('FeiShu Error') msg_user_not_bound_err = _('FeiShu is not bound') - msg_user_need_bound_warning = _('Please login with a password and then bind the FeiShu') msg_not_found_user_from_client_err = _('Failed to get user from FeiShu') diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index b2061a4d6..e127dbeee 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -197,11 +197,9 @@ class WeComQRLoginCallbackView(WeComQRMixin, BaseLoginCallbackView): client_auth_params = {'corpid': 'WECOM_CORPID', 'corpsecret': 'WECOM_SECRET', 'agentid': 'WECOM_AGENTID'} user_type = 'wecom' auth_backend = 'AUTH_BACKEND_WECOM' - create_user_if_not_exist_setting = 'WECOM_CREATE_USER_IF_NOT_EXIST' msg_client_err = _('WeCom Error') msg_user_not_bound_err = _('WeCom is not bound') - msg_user_need_bound_warning = _('Please login with a password and then bind the WeCom') msg_not_found_user_from_client_err = _('Failed to get user from WeCom') diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index ba2593efd..343224675 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -365,21 +365,18 @@ class Config(dict): 'WECOM_CORPID': '', 'WECOM_AGENTID': '', 'WECOM_SECRET': '', - 'WECOM_CREATE_USER_IF_NOT_EXIST': False, # 钉钉 'AUTH_DINGTALK': False, 'DINGTALK_AGENTID': '', 'DINGTALK_APPKEY': '', 'DINGTALK_APPSECRET': '', - 'DINGTALK_CREATE_USER_IF_NOT_EXIST': False, # 飞书 'AUTH_FEISHU': False, 'FEISHU_APP_ID': '', 'FEISHU_APP_SECRET': '', 'FEISHU_VERSION': 'feishu', - 'FEISHU_CREATE_USER_IF_NOT_EXIST': False, 'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS / SAML2 'LOGIN_REDIRECT_MSG_ENABLED': True, diff --git a/apps/settings/serializers/auth/dingtalk.py b/apps/settings/serializers/auth/dingtalk.py index f74d267b8..4ec7814a1 100644 --- a/apps/settings/serializers/auth/dingtalk.py +++ b/apps/settings/serializers/auth/dingtalk.py @@ -13,6 +13,3 @@ class DingTalkSettingSerializer(serializers.Serializer): DINGTALK_APPKEY = serializers.CharField(max_length=256, required=True, label='AppKey') DINGTALK_APPSECRET = EncryptedField(max_length=256, required=False, label='AppSecret') AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('Enable DingTalk Auth')) - DINGTALK_CREATE_USER_IF_NOT_EXIST = serializers.BooleanField( - default=False, label=_('Create user if not') - ) diff --git a/apps/settings/serializers/auth/feishu.py b/apps/settings/serializers/auth/feishu.py index 0e12f04ab..a06d41b23 100644 --- a/apps/settings/serializers/auth/feishu.py +++ b/apps/settings/serializers/auth/feishu.py @@ -19,6 +19,3 @@ class FeiShuSettingSerializer(serializers.Serializer): FEISHU_VERSION = serializers.ChoiceField( choices=VERSION_CHOICES, default='feishu', label=_('Version') ) - FEISHU_CREATE_USER_IF_NOT_EXIST = serializers.BooleanField( - default=False, label=_('Create user if not') - ) diff --git a/apps/settings/serializers/auth/wecom.py b/apps/settings/serializers/auth/wecom.py index 3e57683a6..462b17db6 100644 --- a/apps/settings/serializers/auth/wecom.py +++ b/apps/settings/serializers/auth/wecom.py @@ -13,6 +13,3 @@ class WeComSettingSerializer(serializers.Serializer): WECOM_AGENTID = serializers.CharField(max_length=256, required=True, label='agentid') WECOM_SECRET = EncryptedField(max_length=256, required=False, label='secret') AUTH_WECOM = serializers.BooleanField(default=False, label=_('Enable WeCom Auth')) - WECOM_CREATE_USER_IF_NOT_EXIST = serializers.BooleanField( - default=False, label=_('Create user if not') - ) From 6b6900cfd4c16d6095db2c744e7b100cbc5a415b Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 8 May 2023 17:02:55 +0800 Subject: [PATCH 38/88] =?UTF-8?q?perf:=20=E5=AF=BC=E5=87=BAaccount=20?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E9=9C=80=E5=AF=B9=E6=96=87=E4=BB=B6=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E5=8A=A0=E5=AF=86=20(#10366)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/common/drf/renders/base.py | 36 +++++++++++++++++++++++++++- apps/locale/ja/LC_MESSAGES/django.po | 6 +++++ apps/locale/zh/LC_MESSAGES/django.po | 7 ++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/apps/common/drf/renders/base.py b/apps/common/drf/renders/base.py index 5b2aa5612..cc92eb8c2 100644 --- a/apps/common/drf/renders/base.py +++ b/apps/common/drf/renders/base.py @@ -1,6 +1,10 @@ import abc +import io +import re from datetime import datetime +import pyzipper +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from rest_framework.renderers import BaseRenderer from rest_framework.utils import encoders, json @@ -180,9 +184,39 @@ class BaseFileRenderer(BaseRenderer): self.write_column_titles(column_titles) self.write_rows(rows) self.after_render() - value = self.get_rendered_value() + value = self.compress_into_zip_file(view, request, response) except Exception as e: logger.debug(e, exc_info=True) value = 'Render error! ({})'.format(self.media_type).encode('utf-8') return value return value + + def compress_into_zip_file(self, view, request, response): + value = self.get_rendered_value() + from accounts.models import Account + if str(view.model) not in (str(Account),) or self.template != 'export': + return value + + filename_pattern = re.compile(r'filename="([^"]+)"') + content_disposition = response['Content-Disposition'] + match = filename_pattern.search(content_disposition) + filename = match.group(1) + response['Content-Disposition'] = content_disposition.replace(self.format, 'zip') + + contents_io = io.BytesIO() + secret_key = request.user.secret_key + if not secret_key: + content = _("{} - The encryption password has not been set - " + "please go to personal information -> file encryption password " + "to set the encryption password").format(request.user.name) + + response['Content-Disposition'] = content_disposition.replace(self.format, 'txt') + contents_io.write(content.encode('utf-8')) + return contents_io.getvalue() + + with pyzipper.AESZipFile( + contents_io, 'w', compression=pyzipper.ZIP_LZMA, encryption=pyzipper.WZ_AES + ) as zf: + zf.setpassword(secret_key.encode('utf8')) + zf.writestr(filename, value) + return contents_io.getvalue() diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index b751ef764..759def5fd 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -547,6 +547,12 @@ msgstr "" "{} -暗号化変更タスクが完了しました: 暗号化パスワードが設定されていません-個人" "情報にアクセスしてください-> ファイル暗号化パスワードを設定してください" +msgid "" +"{} - The encryption password has not been set - please go to personal information -> file encryption " +"password to set the encryption password" +msgstr "" +"{} - 暗号化パスワードが設定されていません-個人情報->ファイル暗号化パスワードに暗号化パスワードを設定してください" + #: accounts/serializers/account/account.py:28 msgid "Push now" msgstr "今すぐプッシュ" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 40199acec..ff2b9c6a2 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -524,6 +524,13 @@ msgstr "" "{} - 账号备份任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设" "置加密密码" +msgid "" +"{} - The encryption password has not been set - please go to personal information -> file " +"encryption password to set the encryption password" +msgstr "" +"{} - 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设" +"置加密密码" + #: accounts/notifications.py:33 msgid "Notification of implementation result of encryption change plan" msgstr "改密计划任务结果通知" From 1933e82587dbba71ffe01bfdeba9183a365feec6 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 9 May 2023 15:29:02 +0800 Subject: [PATCH 39/88] =?UTF-8?q?feat:=20=E8=B4=A6=E5=8F=B7=E6=A8=A1?= =?UTF-8?q?=E7=89=88=E5=88=87=E6=8D=A2=E8=87=B3=20(#10396)?= 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 | 16 +++++++++- .../migrations/0011_auto_20230506_1443.py | 29 +++++++++++++++++++ apps/accounts/models/account.py | 22 +++++++++++++- apps/accounts/serializers/account/account.py | 20 +++++++++++-- apps/accounts/serializers/account/template.py | 9 +++++- .../0117_alter_baseautomation_params.py | 18 ++++++++++++ apps/perms/serializers/permission.py | 1 + 7 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 apps/accounts/migrations/0011_auto_20230506_1443.py create mode 100644 apps/assets/migrations/0117_alter_baseautomation_params.py diff --git a/apps/accounts/api/account/template.py b/apps/accounts/api/account/template.py index 11675368f..0aecb5143 100644 --- a/apps/accounts/api/account/template.py +++ b/apps/accounts/api/account/template.py @@ -1,4 +1,6 @@ from django_filters import rest_framework as drf_filters +from rest_framework.decorators import action +from rest_framework.response import Response from accounts import serializers from accounts.models import AccountTemplate @@ -38,8 +40,20 @@ class AccountTemplateViewSet(OrgBulkModelViewSet): filterset_class = AccountTemplateFilterSet search_fields = ('username', 'name') serializer_classes = { - 'default': serializers.AccountTemplateSerializer + 'default': serializers.AccountTemplateSerializer, } + rbac_perms = { + 'su_from_account_templates': 'accounts.view_accounttemplate', + } + + @action(methods=['get'], detail=False, url_path='su-from-account-templates') + def su_from_account_templates(self, request, *args, **kwargs): + pk = request.query_params.get('template_id') + template = AccountTemplate.objects.filter(pk=pk).first() + templates = AccountTemplate.get_su_from_account_templates(template) + templates = self.filter_queryset(templates) + serializer = self.get_serializer(templates, many=True) + return Response(data=serializer.data) class AccountTemplateSecretsViewSet(RecordViewLogMixin, AccountTemplateViewSet): diff --git a/apps/accounts/migrations/0011_auto_20230506_1443.py b/apps/accounts/migrations/0011_auto_20230506_1443.py new file mode 100644 index 000000000..3460376bd --- /dev/null +++ b/apps/accounts/migrations/0011_auto_20230506_1443.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.17 on 2023-05-06 06:43 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0010_gatheraccountsautomation_is_sync_account'), + ] + + operations = [ + migrations.AddField( + model_name='accounttemplate', + name='su_from', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to', to='accounts.accounttemplate', verbose_name='Su from'), + ), + migrations.AlterField( + model_name='changesecretautomation', + name='ssh_key_change_strategy', + field=models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy'), + ), + migrations.AlterField( + model_name='pushaccountautomation', + name='ssh_key_change_strategy', + field=models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy'), + ), + ] diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index de89db545..30eb853e3 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -1,5 +1,5 @@ from django.db import models -from django.db.models import Count +from django.db.models import Count, Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords @@ -108,6 +108,11 @@ class Account(AbsConnectivity, BaseAccount): class AccountTemplate(BaseAccount): + su_from = models.ForeignKey( + 'self', related_name='su_to', null=True, + on_delete=models.SET_NULL, verbose_name=_("Su from") + ) + class Meta: verbose_name = _('Account template') unique_together = ( @@ -118,6 +123,21 @@ class AccountTemplate(BaseAccount): ('change_accounttemplatesecret', _('Can change asset account template secret')), ] + @classmethod + def get_su_from_account_templates(cls, instance=None): + if not instance: + return cls.objects.all() + return cls.objects.exclude(Q(id=instance.id) | Q(su_from=instance)) + + def get_su_from_account(self, asset): + su_from = self.su_from + if su_from and asset.platform.su_enabled: + account = asset.accounts.filter( + username=su_from.username, + secret_type=su_from.secret_type + ).first() + return account + def __str__(self): return self.username diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index c76cb1788..45f7e26f3 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -91,7 +91,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer): self._template = template # Set initial data from template - ignore_fields = ['id', 'date_created', 'date_updated', 'org_id'] + ignore_fields = ['id', 'date_created', 'date_updated', 'su_from', 'org_id'] field_names = [ field.name for field in template._meta.fields if field.name not in ignore_fields @@ -151,6 +151,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer): template = self._template if template is None: return + validated_data['source'] = Source.TEMPLATE validated_data['source_id'] = str(template.id) @@ -238,6 +239,9 @@ class AssetAccountBulkSerializerResultSerializer(serializers.Serializer): class AssetAccountBulkSerializer( AccountCreateUpdateSerializerMixin, AuthValidateMixin, serializers.ModelSerializer ): + su_from_username = serializers.CharField( + max_length=128, required=False, write_only=True, allow_null=True, label=_("Su from") + ) assets = serializers.PrimaryKeyRelatedField(queryset=Asset.objects, many=True, label=_('Assets')) class Meta: @@ -245,7 +249,7 @@ class AssetAccountBulkSerializer( fields = [ 'name', 'username', 'secret', 'secret_type', 'privileged', 'is_active', 'comment', 'template', - 'on_invalid', 'push_now', 'assets', + 'on_invalid', 'push_now', 'assets', 'su_from_username' ] extra_kwargs = { 'name': {'required': False}, @@ -293,8 +297,20 @@ class AssetAccountBulkSerializer( raise serializers.ValidationError(_('Account already exists')) return instance, True, 'created' + def generate_su_from_data(self, validated_data): + template = self._template + asset = validated_data['asset'] + su_from = validated_data.get('su_from') + su_from_username = validated_data.pop('su_from_username', None) + if template: + su_from = template.get_su_from_account() + elif su_from_username: + su_from = asset.accounts.filter(username=su_from_username).first() + validated_data['su_from'] = su_from + def perform_create(self, vd, handler): lookup = self.get_filter_lookup(vd) + self.generate_su_from_data(vd) try: instance, changed, state = handler(vd, lookup) except IntegrityError: diff --git a/apps/accounts/serializers/account/template.py b/apps/accounts/serializers/account/template.py index bbf83bd89..937c69d36 100644 --- a/apps/accounts/serializers/account/template.py +++ b/apps/accounts/serializers/account/template.py @@ -1,7 +1,9 @@ +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from accounts.models import AccountTemplate, Account from common.serializers import SecretReadableMixin +from common.serializers.fields import ObjectRelatedField from .base import BaseAccountSerializer @@ -9,9 +11,14 @@ class AccountTemplateSerializer(BaseAccountSerializer): is_sync_account = serializers.BooleanField(default=False, write_only=True) _is_sync_account = False + su_from = ObjectRelatedField( + required=False, queryset=AccountTemplate.objects, allow_null=True, + allow_empty=True, label=_('Su from'), attrs=('id', 'name', 'username') + ) + class Meta(BaseAccountSerializer.Meta): model = AccountTemplate - fields = BaseAccountSerializer.Meta.fields + ['is_sync_account'] + fields = BaseAccountSerializer.Meta.fields + ['is_sync_account', 'su_from'] def sync_accounts_secret(self, instance, diff): if not self._is_sync_account or 'secret' not in diff: diff --git a/apps/assets/migrations/0117_alter_baseautomation_params.py b/apps/assets/migrations/0117_alter_baseautomation_params.py new file mode 100644 index 000000000..1fc93bdd3 --- /dev/null +++ b/apps/assets/migrations/0117_alter_baseautomation_params.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.17 on 2023-05-06 06:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0116_auto_20230418_1726'), + ] + + operations = [ + migrations.AlterField( + model_name='baseautomation', + name='params', + field=models.JSONField(default=dict, verbose_name='Parameters'), + ), + ] diff --git a/apps/perms/serializers/permission.py b/apps/perms/serializers/permission.py index 0e5f3b80a..c6f770b8a 100644 --- a/apps/perms/serializers/permission.py +++ b/apps/perms/serializers/permission.py @@ -109,6 +109,7 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer): if condition in username_secret_type_dict: continue account_data = {key: getattr(template, key) for key in account_attribute} + account_data['su_from'] = template.get_su_from_account(asset) account_data['name'] = f"{account_data['name']}-{_('Account template')}" need_create_accounts.append(Account(**{'asset_id': asset.id, **account_data})) return Account.objects.bulk_create(need_create_accounts) From b5599fd3a629426ca7666a8c3bf37bf30d9bb07d Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 9 May 2023 17:18:52 +0800 Subject: [PATCH 40/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20job=20permis?= =?UTF-8?q?sion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/api/job.py | 88 +++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index 280ab5b98..eb04efbfa 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -1,23 +1,25 @@ from django.conf import settings from django.db.models import Count from django.db.transaction import atomic -from rest_framework.views import APIView from django.shortcuts import get_object_or_404 from rest_framework.response import Response +from rest_framework.views import APIView +from common.permissions import IsValidUser from ops.const import Types from ops.models import Job, JobExecution from ops.serializers.job import JobSerializer, JobExecutionSerializer -__all__ = ['JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', - 'JobAssetDetail', 'JobExecutionTaskDetail', 'FrequentUsernames'] +__all__ = [ + 'JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', + 'JobAssetDetail', 'JobExecutionTaskDetail', 'FrequentUsernames' +] from ops.tasks import run_ops_job_execution from ops.variables import JMS_JOB_VARIABLE_HELP from orgs.mixins.api import OrgBulkModelViewSet from orgs.utils import tmp_to_org, get_current_org from accounts.models import Account -from rbac.permissions import RBACPermission def set_task_to_serializer_data(serializer, task): @@ -28,7 +30,6 @@ def set_task_to_serializer_data(serializer, task): class JobViewSet(OrgBulkModelViewSet): serializer_class = JobSerializer - permission_classes = (RBACPermission,) search_fields = ('name', 'comment') model = Job @@ -70,7 +71,6 @@ class JobViewSet(OrgBulkModelViewSet): class JobExecutionViewSet(OrgBulkModelViewSet): serializer_class = JobExecutionSerializer http_method_names = ('get', 'post', 'head', 'options',) - permission_classes = (RBACPermission,) model = JobExecution search_fields = ('material',) @@ -94,50 +94,52 @@ class JobExecutionViewSet(OrgBulkModelViewSet): return queryset +class JobAssetDetail(APIView): + rbac_perms = { + 'get': ['ops.view_jobexecution'], + } + + def get(self, request, **kwargs): + execution_id = request.query_params.get('execution_id', '') + execution = get_object_or_404(JobExecution, id=execution_id) + return Response(data=execution.assent_result_detail) + + +class JobExecutionTaskDetail(APIView): + rbac_perms = { + 'get': ['ops.view_jobexecution'], + } + + def get(self, request, **kwargs): + org = get_current_org() + task_id = str(kwargs.get('task_id')) + + with tmp_to_org(org): + execution = get_object_or_404(JobExecution, task_id=task_id) + + return Response(data={ + 'status': execution.status, + 'is_finished': execution.is_finished, + 'is_success': execution.is_success, + 'time_cost': execution.time_cost, + 'job_id': execution.job.id, + }) + + class JobRunVariableHelpAPIView(APIView): - rbac_perms = () - permission_classes = () + permission_classes = [IsValidUser] def get(self, request, **kwargs): return Response(data=JMS_JOB_VARIABLE_HELP) -class JobAssetDetail(APIView): - rbac_perms = () - permission_classes = () - - def get(self, request, **kwargs): - execution_id = request.query_params.get('execution_id') - if execution_id: - execution = get_object_or_404(JobExecution, id=execution_id) - return Response(data=execution.assent_result_detail) - - -class JobExecutionTaskDetail(APIView): - rbac_perms = () - permission_classes = () - - def get(self, request, **kwargs): - org = get_current_org() - task_id = str(kwargs.get('task_id')) - if task_id: - with tmp_to_org(org): - execution = get_object_or_404(JobExecution, task_id=task_id) - return Response(data={ - 'status': execution.status, - 'is_finished': execution.is_finished, - 'is_success': execution.is_success, - 'time_cost': execution.time_cost, - 'job_id': execution.job.id, - }) - - class FrequentUsernames(APIView): - rbac_perms = () - permission_classes = () + permission_classes = [IsValidUser] def get(self, request, **kwargs): - top_accounts = Account.objects.exclude(username='root').exclude(username__startswith='jms_').values( - 'username').annotate( - total=Count('username')).order_by('total')[:5] + top_accounts = Account.objects.exclude(username='root') \ + .exclude(username__startswith='jms_') \ + .values('username') \ + .annotate(total=Count('username')) \ + .order_by('total')[:5] return Response(data=top_accounts) From 873c019b5874949781f47baa3157dad636874a5c Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 9 May 2023 17:23:21 +0800 Subject: [PATCH 41/88] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20job=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/api/job.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index eb04efbfa..308998f09 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -73,6 +73,7 @@ class JobExecutionViewSet(OrgBulkModelViewSet): http_method_names = ('get', 'post', 'head', 'options',) model = JobExecution search_fields = ('material',) + filterset_fields = ['status', 'job_id'] @atomic def perform_create(self, serializer): @@ -88,9 +89,6 @@ class JobExecutionViewSet(OrgBulkModelViewSet): def get_queryset(self): queryset = super().get_queryset() queryset = queryset.filter(creator=self.request.user) - job_id = self.request.query_params.get('job_id') - if job_id: - queryset = queryset.filter(job_id=job_id) return queryset From f0cc64c74ed479b836bc9eb043868f4786a2223e Mon Sep 17 00:00:00 2001 From: Bai Date: Tue, 9 May 2023 18:36:21 +0800 Subject: [PATCH 42/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E7=94=A8=E6=88=B7=E5=90=8D=E5=AD=97=E6=AE=B5=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E4=BF=A1=E6=81=AF=EF=BC=88null=20=E7=9A=84=E6=83=85?= =?UTF-8?q?=E5=86=B5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/serializers/account/base.py | 1 + apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 369 +++++++++++----------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 368 ++++++++++----------- 5 files changed, 385 insertions(+), 361 deletions(-) diff --git a/apps/accounts/serializers/account/base.py b/apps/accounts/serializers/account/base.py index 4e2b1a1df..b79dd51be 100644 --- a/apps/accounts/serializers/account/base.py +++ b/apps/accounts/serializers/account/base.py @@ -78,4 +78,5 @@ class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer): ] extra_kwargs = { 'spec_info': {'label': _('Spec info')}, + 'username': {'help_text': _("Tip: If no username is required for authentication, fill in `null`")} } diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 7d2f91c61..7d17757e9 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:24858bf247f7af58abda5adb5be733b7b995df2e26de7c91caf43f7aa0dd3be0 -size 139654 +oid sha256:e3d2510369021ec160d292274694c0bfbd50200b5937aee4206c32bbe018cec8 +size 139997 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 759def5fd..c98debbd5 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-04-21 14:05+0800\n" +"POT-Creation-Date: 2023-05-09 18:31+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -87,7 +87,7 @@ msgstr "更新" #: accounts/const/account.py:27 #: accounts/serializers/automations/change_secret.py:156 audits/const.py:53 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:58 terminal/const.py:60 xpack/plugins/cloud/const.py:41 +#: ops/const.py:58 terminal/const.py:61 xpack/plugins/cloud/const.py:41 msgid "Failed" msgstr "失敗しました" @@ -179,14 +179,14 @@ msgstr "作成してプッシュ" msgid "Only create" msgstr "作成のみ" -#: accounts/models/account.py:47 +#: accounts/models/account.py:49 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:199 -#: accounts/serializers/account/account.py:232 +#: accounts/serializers/account/account.py:200 +#: accounts/serializers/account/account.py:233 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 -#: acls/models/base.py:100 acls/serializers/base.py:56 +#: acls/models/base.py:100 acls/serializers/base.py:76 #: assets/models/asset/common.py:92 assets/models/asset/common.py:306 #: assets/models/cmd_filter.py:36 assets/serializers/domain.py:19 #: assets/serializers/label.py:27 audits/models.py:48 @@ -198,29 +198,32 @@ msgstr "作成のみ" msgid "Asset" msgstr "資産" -#: accounts/models/account.py:51 accounts/serializers/account/account.py:204 +#: accounts/models/account.py:53 accounts/models/account.py:113 +#: accounts/serializers/account/account.py:205 +#: accounts/serializers/account/account.py:243 +#: accounts/serializers/account/template.py:16 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" msgstr "から切り替え" -#: accounts/models/account.py:53 settings/serializers/auth/cas.py:20 +#: accounts/models/account.py:55 settings/serializers/auth/cas.py:20 #: settings/serializers/auth/feishu.py:20 terminal/models/applet/applet.py:29 msgid "Version" msgstr "バージョン" -#: accounts/models/account.py:55 accounts/serializers/account/account.py:200 -#: users/models/user.py:768 +#: accounts/models/account.py:57 accounts/serializers/account/account.py:201 +#: users/models/user.py:778 msgid "Source" msgstr "ソース" -#: accounts/models/account.py:56 +#: accounts/models/account.py:58 msgid "Source ID" msgstr "ソース ID" -#: accounts/models/account.py:59 +#: accounts/models/account.py:61 #: accounts/serializers/automations/change_secret.py:113 #: accounts/serializers/automations/change_secret.py:133 -#: acls/models/base.py:102 acls/serializers/base.py:57 +#: acls/models/base.py:102 acls/serializers/base.py:77 #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 #: audits/models.py:49 ops/models/base.py:18 #: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 @@ -229,35 +232,35 @@ msgstr "ソース ID" msgid "Account" msgstr "アカウント" -#: accounts/models/account.py:65 +#: accounts/models/account.py:67 msgid "Can view asset account secret" msgstr "資産アカウントの秘密を表示できます" -#: accounts/models/account.py:66 +#: accounts/models/account.py:68 msgid "Can view asset history account" msgstr "資産履歴アカウントを表示できます" -#: accounts/models/account.py:67 +#: accounts/models/account.py:69 msgid "Can view asset history account secret" msgstr "資産履歴アカウントパスワードを表示できます" -#: accounts/models/account.py:68 +#: accounts/models/account.py:70 msgid "Can verify account" msgstr "アカウントを確認できます" -#: accounts/models/account.py:69 +#: accounts/models/account.py:71 msgid "Can push account" msgstr "アカウントをプッシュできます" -#: accounts/models/account.py:110 +#: accounts/models/account.py:117 msgid "Account template" msgstr "アカウント テンプレート" -#: accounts/models/account.py:115 +#: accounts/models/account.py:122 msgid "Can view asset account template secret" msgstr "アセット アカウント テンプレートのパスワードを表示できます" -#: accounts/models/account.py:116 +#: accounts/models/account.py:123 msgid "Can change asset account template secret" msgstr "アセット アカウント テンプレートのパスワードを変更できます" @@ -354,7 +357,7 @@ msgid "Can add push account execution" msgstr "プッシュ アカウントの作成の実行" #: accounts/models/automations/change_secret.py:18 accounts/models/base.py:36 -#: accounts/serializers/account/account.py:394 +#: accounts/serializers/account/account.py:410 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:46 #: authentication/serializers/connect_token_secret.py:41 @@ -404,8 +407,9 @@ msgid "Date finished" msgstr "終了日" #: accounts/models/automations/change_secret.py:93 -#: accounts/serializers/account/account.py:234 assets/const/automation.py:8 -#: common/const/choices.py:20 +#: accounts/serializers/account/account.py:235 assets/const/automation.py:8 +#: authentication/views/base.py:29 authentication/views/base.py:30 +#: authentication/views/base.py:31 common/const/choices.py:20 msgid "Error" msgstr "間違い" @@ -423,13 +427,13 @@ msgstr "最終ログイン日" #: accounts/models/automations/gather_account.py:17 #: accounts/models/automations/push_account.py:15 accounts/models/base.py:34 -#: acls/serializers/base.py:18 acls/serializers/base.py:49 +#: acls/serializers/base.py:19 acls/serializers/base.py:50 #: assets/models/_user.py:23 audits/models.py:157 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:112 -#: users/models/user.py:715 users/templates/users/_msg_user_created.html:12 +#: users/models/user.py:725 users/templates/users/_msg_user_created.html:12 #: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" msgstr "ユーザー名" @@ -456,8 +460,8 @@ msgid "Triggers" msgstr "トリガー方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:81 -#: acls/serializers/base.py:81 acls/serializers/login_acl.py:26 -#: assets/models/cmd_filter.py:81 audits/models.py:65 audits/serializers.py:82 +#: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 +#: audits/models.py:65 audits/serializers.py:82 #: authentication/serializers/connect_token_secret.py:108 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" @@ -472,14 +476,14 @@ msgid "Verify asset account" msgstr "アカウントの確認" #: accounts/models/base.py:33 acls/models/base.py:75 -#: acls/models/command_acl.py:21 acls/serializers/base.py:34 +#: acls/models/command_acl.py:21 acls/serializers/base.py:35 #: applications/models.py:9 assets/models/_user.py:22 #: assets/models/asset/common.py:90 assets/models/asset/common.py:123 #: assets/models/cmd_filter.py:21 assets/models/domain.py:18 #: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/platform.py:13 assets/models/platform.py:89 #: assets/serializers/asset/common.py:145 assets/serializers/platform.py:92 -#: assets/serializers/platform.py:193 +#: assets/serializers/platform.py:194 #: authentication/serializers/connect_token_secret.py:102 ops/mixin.py:21 #: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57 #: ops/models/job.py:92 ops/models/playbook.py:23 ops/serializers/job.py:20 @@ -489,7 +493,7 @@ msgstr "アカウントの確認" #: terminal/models/component/endpoint.py:90 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:15 #: terminal/models/component/terminal.py:84 users/forms/profile.py:33 -#: users/models/group.py:13 users/models/user.py:717 +#: users/models/group.py:13 users/models/user.py:727 #: xpack/plugins/cloud/models.py:28 msgid "Name" msgstr "名前" @@ -547,12 +551,6 @@ msgstr "" "{} -暗号化変更タスクが完了しました: 暗号化パスワードが設定されていません-個人" "情報にアクセスしてください-> ファイル暗号化パスワードを設定してください" -msgid "" -"{} - The encryption password has not been set - please go to personal information -> file encryption " -"password to set the encryption password" -msgstr "" -"{} - 暗号化パスワードが設定されていません-個人情報->ファイル暗号化パスワードに暗号化パスワードを設定してください" - #: accounts/serializers/account/account.py:28 msgid "Push now" msgstr "今すぐプッシュ" @@ -561,16 +559,16 @@ msgstr "今すぐプッシュ" msgid "Exist policy" msgstr "アカウントの存在ポリシー" -#: accounts/serializers/account/account.py:179 applications/models.py:11 +#: accounts/serializers/account/account.py:180 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:90 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 -#: assets/serializers/platform.py:110 assets/serializers/platform.py:194 +#: assets/serializers/platform.py:110 assets/serializers/platform.py:195 #: perms/serializers/user_permission.py:26 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 msgid "Category" msgstr "カテゴリ" -#: accounts/serializers/account/account.py:180 +#: accounts/serializers/account/account.py:181 #: 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 @@ -589,27 +587,27 @@ msgstr "カテゴリ" msgid "Type" msgstr "タイプ" -#: accounts/serializers/account/account.py:195 +#: accounts/serializers/account/account.py:196 msgid "Asset not found" msgstr "資産が存在しません" -#: accounts/serializers/account/account.py:201 +#: accounts/serializers/account/account.py:202 #: accounts/serializers/account/base.py:64 msgid "Has secret" msgstr "エスクローされたパスワード" -#: accounts/serializers/account/account.py:233 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:234 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:235 +#: accounts/serializers/account/account.py:236 msgid "Changed" msgstr "編集済み" -#: accounts/serializers/account/account.py:241 +#: accounts/serializers/account/account.py:245 #: accounts/serializers/automations/base.py:22 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 ops/models/base.py:17 @@ -618,29 +616,29 @@ msgstr "編集済み" msgid "Assets" msgstr "資産" -#: accounts/serializers/account/account.py:293 +#: accounts/serializers/account/account.py:297 msgid "Account already exists" msgstr "アカウントはすでに存在しています" -#: accounts/serializers/account/account.py:330 +#: accounts/serializers/account/account.py:346 #, python-format msgid "Asset does not support this secret type: %s" msgstr "アセットはアカウント タイプをサポートしていません: %s" -#: accounts/serializers/account/account.py:361 +#: accounts/serializers/account/account.py:377 msgid "Account has exist" msgstr "アカウントはすでに存在しています" -#: accounts/serializers/account/account.py:395 +#: accounts/serializers/account/account.py:411 #: authentication/serializers/connect_token_secret.py:146 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:33 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:402 acls/models/base.py:98 -#: acls/models/login_acl.py:13 acls/serializers/base.py:55 -#: acls/serializers/login_acl.py:22 assets/models/cmd_filter.py:24 +#: accounts/serializers/account/account.py:418 acls/models/base.py:98 +#: acls/models/login_acl.py:13 acls/serializers/base.py:75 +#: acls/serializers/login_acl.py:21 assets/models/cmd_filter.py:24 #: assets/models/label.py:16 audits/models.py:44 audits/models.py:63 #: audits/models.py:141 authentication/models/connection_token.py:30 #: authentication/models/sso_token.py:16 @@ -651,12 +649,12 @@ msgstr "ID" #: terminal/models/session/session.py:30 terminal/models/session/sharing.py:32 #: terminal/notifications.py:96 terminal/notifications.py:144 #: terminal/serializers/command.py:16 tickets/models/comment.py:21 -#: users/const.py:14 users/models/user.py:911 users/models/user.py:942 +#: users/const.py:14 users/models/user.py:921 users/models/user.py:952 #: users/serializers/group.py:18 msgid "User" msgstr "ユーザー" -#: accounts/serializers/account/account.py:403 +#: accounts/serializers/account/account.py:419 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:98 terminal/notifications.py:146 msgid "Date" @@ -688,10 +686,14 @@ msgid "Key password" msgstr "キーパスワード" #: accounts/serializers/account/base.py:80 -#: assets/serializers/asset/common.py:305 +#: assets/serializers/asset/common.py:306 msgid "Spec info" msgstr "特別情報" +#: accounts/serializers/account/base.py:81 +msgid "Tip: If no username is required for authentication, fill in `null`" +msgstr "ヒント: 認証にユーザー名が必要ない場合は、null を入力してください" + #: accounts/serializers/automations/base.py:23 #: assets/models/asset/common.py:129 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/serializers/automations/base.py:21 @@ -729,7 +731,7 @@ msgstr "自動タスク実行履歴" #: accounts/serializers/automations/change_secret.py:155 audits/const.py:52 #: audits/models.py:54 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:39 -#: terminal/const.py:59 terminal/models/session/sharing.py:107 +#: terminal/const.py:60 terminal/models/session/sharing.py:107 #: tickets/views/approve.py:114 msgid "Success" msgstr "成功" @@ -801,8 +803,8 @@ msgstr "優先順位" msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" -#: acls/models/base.py:82 acls/serializers/base.py:75 -#: acls/serializers/login_acl.py:24 assets/models/cmd_filter.py:86 +#: acls/models/base.py:82 acls/serializers/base.py:95 +#: acls/serializers/login_acl.py:23 assets/models/cmd_filter.py:86 #: authentication/serializers/connect_token_secret.py:80 msgid "Reviewers" msgstr "レビュー担当者" @@ -816,7 +818,7 @@ msgid "Active" msgstr "アクティブ" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:71 terminal/const.py:67 +#: ops/serializers/job.py:71 terminal/const.py:68 #: terminal/models/session/session.py:43 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -858,7 +860,7 @@ msgstr "コマンドフィルタリング" msgid "Command confirm" msgstr "コマンドの確認" -#: acls/models/login_acl.py:16 acls/serializers/login_acl.py:30 +#: acls/models/login_acl.py:16 acls/serializers/login_acl.py:28 msgid "Rule" msgstr "ルール" @@ -878,11 +880,11 @@ msgstr "ログインasset acl" msgid "Login asset confirm" msgstr "ログイン資産の確認" -#: acls/serializers/base.py:10 acls/serializers/login_acl.py:17 +#: acls/serializers/base.py:11 acls/serializers/login_acl.py:16 msgid "With * indicating a match all. " msgstr "* はすべて一致することを示します。" -#: acls/serializers/base.py:25 +#: acls/serializers/base.py:26 msgid "" "With * indicating a match all. Such as: 192.168.10.1, 192.168.1.0/24, " "10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 (Domain name " @@ -892,35 +894,35 @@ msgstr "" "10.1.1.1-10.1.1.20、2001:db8:2de::e13、2001:db8:1a:1110:::/64 (ドメイン名サ" "ポート)" -#: acls/serializers/base.py:40 assets/serializers/asset/host.py:19 +#: acls/serializers/base.py:41 assets/serializers/asset/host.py:19 msgid "IP/Host" msgstr "IP/ホスト" -#: acls/serializers/base.py:60 +#: acls/serializers/base.py:80 msgid "User (username)" msgstr "ユーザー (ユーザー名)" -#: acls/serializers/base.py:64 +#: acls/serializers/base.py:84 msgid "Asset (name)" msgstr "資産(名前)" -#: acls/serializers/base.py:68 +#: acls/serializers/base.py:88 msgid "Asset (address)" msgstr "資産(住所)" -#: acls/serializers/base.py:72 +#: acls/serializers/base.py:92 msgid "Account (username)" msgstr "アカウント (ユーザー名)" -#: acls/serializers/base.py:78 acls/serializers/login_acl.py:28 +#: acls/serializers/base.py:98 acls/serializers/login_acl.py:26 msgid "Reviewers amount" msgstr "承認者数" -#: acls/serializers/base.py:109 tickets/serializers/ticket/ticket.py:76 +#: acls/serializers/base.py:126 tickets/serializers/ticket/ticket.py:76 msgid "The organization `{}` does not exist" msgstr "組織 '{}'は存在しません" -#: acls/serializers/base.py:115 +#: acls/serializers/base.py:132 msgid "None of the reviewers belong to Organization `{}`" msgstr "いずれのレビューアも組織 '{}' に属していません" @@ -1150,7 +1152,7 @@ msgstr "SSHパブリックキー" #: terminal/models/applet/host.py:111 terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:100 #: terminal/models/session/session.py:47 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:756 +#: tickets/models/ticket/general.py:297 users/models/user.py:766 #: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:111 msgid "Comment" msgstr "コメント" @@ -1158,18 +1160,18 @@ msgstr "コメント" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:22 #: common/db/models.py:35 ops/models/base.py:54 ops/models/job.py:191 -#: users/models/user.py:943 +#: users/models/user.py:953 msgid "Date created" msgstr "作成された日付" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:36 users/models/user.py:777 +#: common/db/models.py:36 users/models/user.py:787 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:21 -#: common/db/models.py:33 users/models/user.py:763 +#: common/db/models.py:33 users/models/user.py:773 #: users/serializers/group.py:31 msgid "Created by" msgstr "によって作成された" @@ -1268,7 +1270,7 @@ msgstr "プラットフォーム" #: assets/models/asset/common.py:127 assets/models/domain.py:21 #: authentication/serializers/connect_token_secret.py:125 -#: perms/serializers/user_permission.py:28 +#: perms/serializers/user_permission.py:29 msgid "Domain" msgstr "ドメイン" @@ -1276,7 +1278,7 @@ msgstr "ドメイン" msgid "Labels" msgstr "ラベル" -#: assets/models/asset/common.py:132 assets/serializers/asset/common.py:306 +#: assets/models/asset/common.py:132 assets/serializers/asset/common.py:307 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "資産ハードウェア情報の収集" @@ -1343,7 +1345,7 @@ msgid "Submit selector" msgstr "ボタンセレクターを確認する" #: assets/models/automations/base.py:17 assets/models/cmd_filter.py:38 -#: assets/serializers/asset/common.py:304 rbac/tree.py:35 +#: assets/serializers/asset/common.py:305 rbac/tree.py:35 msgid "Accounts" msgstr "アカウント" @@ -1388,7 +1390,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:723 +#: users/models/user.py:733 msgid "User group" msgstr "ユーザーグループ" @@ -1438,7 +1440,7 @@ msgstr "デフォルト" msgid "Default asset group" msgstr "デフォルトアセットグループ" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:928 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:938 msgid "System" msgstr "システム" @@ -1454,7 +1456,8 @@ msgstr "値" #: assets/serializers/cagegory.py:6 assets/serializers/cagegory.py:13 #: assets/serializers/platform.py:93 #: authentication/serializers/connect_token_secret.py:113 -#: common/serializers/common.py:85 settings/serializers/sms.py:7 +#: common/serializers/common.py:85 perms/serializers/user_permission.py:28 +#: settings/serializers/sms.py:7 msgid "Label" msgstr "ラベル" @@ -1587,11 +1590,11 @@ msgstr "ビルトイン" msgid "Charset" msgstr "シャーセット" -#: assets/models/platform.py:99 assets/serializers/platform.py:135 +#: assets/models/platform.py:99 assets/serializers/platform.py:136 msgid "Domain enabled" msgstr "ドメインを有効にする" -#: assets/models/platform.py:101 assets/serializers/platform.py:134 +#: assets/models/platform.py:101 assets/serializers/platform.py:135 msgid "Su enabled" msgstr "アカウントの切り替えを有効にする" @@ -1629,19 +1632,19 @@ msgid "Node path" msgstr "ノードパスです" #: assets/serializers/asset/common.py:144 -#: assets/serializers/asset/common.py:307 +#: assets/serializers/asset/common.py:308 msgid "Auto info" msgstr "自動情報" -#: assets/serializers/asset/common.py:226 +#: assets/serializers/asset/common.py:227 msgid "Platform not exist" msgstr "プラットフォームが存在しません" -#: assets/serializers/asset/common.py:262 +#: assets/serializers/asset/common.py:263 msgid "port out of range (1-65535)" msgstr "ポート番号が範囲外です (1-65535)" -#: assets/serializers/asset/common.py:269 +#: assets/serializers/asset/common.py:270 msgid "Protocol is required: {}" msgstr "プロトコルが必要です: {}" @@ -1763,15 +1766,15 @@ msgstr "" msgid "Automation" msgstr "オートメーション" -#: assets/serializers/platform.py:136 +#: assets/serializers/platform.py:137 msgid "Default Domain" msgstr "デフォルト ドメイン" -#: assets/serializers/platform.py:145 +#: assets/serializers/platform.py:146 msgid "type is required" msgstr "タイプ このフィールドは必須です." -#: assets/serializers/platform.py:182 +#: assets/serializers/platform.py:183 msgid "Protocols is required" msgstr "同意が必要です" @@ -2023,7 +2026,7 @@ msgstr "ユーザーエージェント" #: audits/models.py:169 audits/serializers.py:47 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:740 +#: users/forms/profile.py:65 users/models/user.py:750 #: users/serializers/profile.py:126 msgid "MFA" msgstr "MFA" @@ -2077,22 +2080,24 @@ msgid "Auth Token" msgstr "認証トークン" #: audits/signal_handlers/login_log.py:31 authentication/notifications.py:73 -#: authentication/views/login.py:74 authentication/views/wecom.py:177 +#: authentication/views/login.py:74 authentication/views/wecom.py:159 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:778 +#: users/models/user.py:680 users/models/user.py:788 msgid "WeCom" msgstr "企業微信" -#: audits/signal_handlers/login_log.py:32 authentication/views/feishu.py:144 +#: audits/signal_handlers/login_log.py:32 authentication/views/feishu.py:123 #: authentication/views/login.py:86 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:780 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:682 +#: users/models/user.py:790 msgid "FeiShu" msgstr "本を飛ばす" -#: audits/signal_handlers/login_log.py:33 authentication/views/dingtalk.py:179 +#: audits/signal_handlers/login_log.py:33 authentication/views/dingtalk.py:160 #: authentication/views/login.py:80 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:779 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:681 +#: users/models/user.py:789 msgid "DingTalk" msgstr "DingTalk" @@ -2109,19 +2114,19 @@ msgstr "監査セッション タスク ログのクリーンアップ" msgid "This action require verify your MFA" msgstr "この操作には、MFAを検証する必要があります" -#: authentication/api/connection_token.py:296 +#: authentication/api/connection_token.py:300 msgid "Account not found" msgstr "アカウントが見つかりません" -#: authentication/api/connection_token.py:299 +#: authentication/api/connection_token.py:303 msgid "Permission expired" msgstr "承認の有効期限が切れています" -#: authentication/api/connection_token.py:311 +#: authentication/api/connection_token.py:315 msgid "ACL action is reject" msgstr "ACL アクションは拒否です" -#: authentication/api/connection_token.py:315 +#: authentication/api/connection_token.py:319 msgid "ACL action is review" msgstr "ACL アクションはレビューです" @@ -2129,7 +2134,7 @@ msgstr "ACL アクションはレビューです" msgid "Current user not support mfa type: {}" msgstr "現在のユーザーはmfaタイプをサポートしていません: {}" -#: authentication/api/password.py:31 terminal/api/session/session.py:247 +#: authentication/api/password.py:31 terminal/api/session/session.py:249 #: users/views/profile/reset.py:44 msgid "User does not exist: {}" msgstr "ユーザーが存在しない: {}" @@ -2353,21 +2358,21 @@ msgstr "電話が設定されていない" msgid "SSO auth closed" msgstr "SSO authは閉鎖されました" -#: authentication/errors/mfa.py:18 authentication/views/wecom.py:79 +#: authentication/errors/mfa.py:18 authentication/views/wecom.py:61 msgid "WeCom is already bound" msgstr "企業の微信はすでにバインドされています" -#: authentication/errors/mfa.py:23 authentication/views/wecom.py:236 -#: authentication/views/wecom.py:290 +#: authentication/errors/mfa.py:23 authentication/views/wecom.py:202 +#: authentication/views/wecom.py:244 msgid "WeCom is not bound" msgstr "企業の微信をバインドしていません" -#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:242 -#: authentication/views/dingtalk.py:296 +#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:210 +#: authentication/views/dingtalk.py:252 msgid "DingTalk is not bound" msgstr "DingTalkはバインドされていません" -#: authentication/errors/mfa.py:33 authentication/views/feishu.py:203 +#: authentication/errors/mfa.py:33 authentication/views/feishu.py:167 msgid "FeiShu is not bound" msgstr "本を飛ばすは拘束されていません" @@ -2524,7 +2529,7 @@ msgstr "アセット名" #: authentication/models/connection_token.py:43 #: 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:761 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:771 msgid "Date expired" msgstr "期限切れの日付" @@ -2627,7 +2632,7 @@ msgstr "期限切れです" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/email.py:19 #: settings/serializers/email.py:50 users/forms/profile.py:102 -#: users/forms/profile.py:106 users/models/user.py:719 +#: users/forms/profile.py:106 users/models/user.py:729 #: users/templates/users/forgot_password.html:116 #: users/views/profile/reset.py:73 msgid "Email" @@ -2869,76 +2874,77 @@ msgstr "コピー成功" msgid "LAN" msgstr "ローカルエリアネットワーク" -#: authentication/views/dingtalk.py:41 +#: authentication/views/base.py:64 +#: perms/templates/perms/_msg_permed_items_expire.html:21 +msgid "If you have any question, please contact the administrator" +msgstr "質問があったら、管理者に連絡して下さい" + +#: authentication/views/dingtalk.py:42 msgid "DingTalk Error, Please contact your system administrator" msgstr "DingTalkエラー、システム管理者に連絡してください" -#: authentication/views/dingtalk.py:44 +#: authentication/views/dingtalk.py:45 authentication/views/dingtalk.py:209 msgid "DingTalk Error" msgstr "DingTalkエラー" -#: authentication/views/dingtalk.py:56 authentication/views/feishu.py:51 -#: authentication/views/wecom.py:55 +#: authentication/views/dingtalk.py:57 authentication/views/feishu.py:51 +#: authentication/views/wecom.py:57 msgid "" "The system configuration is incorrect. Please contact your administrator" msgstr "システム設定が正しくありません。管理者に連絡してください" -#: authentication/views/dingtalk.py:80 +#: authentication/views/dingtalk.py:61 msgid "DingTalk is already bound" msgstr "DingTalkはすでにバインドされています" -#: authentication/views/dingtalk.py:148 authentication/views/wecom.py:147 +#: authentication/views/dingtalk.py:129 authentication/views/wecom.py:129 msgid "Invalid user_id" msgstr "無効なuser_id" -#: authentication/views/dingtalk.py:164 +#: authentication/views/dingtalk.py:145 msgid "DingTalk query user failed" msgstr "DingTalkクエリユーザーが失敗しました" -#: authentication/views/dingtalk.py:173 +#: authentication/views/dingtalk.py:154 msgid "The DingTalk is already bound to another user" msgstr "DingTalkはすでに別のユーザーにバインドされています" -#: authentication/views/dingtalk.py:180 +#: authentication/views/dingtalk.py:161 msgid "Binding DingTalk successfully" msgstr "DingTalkのバインドに成功" -#: authentication/views/dingtalk.py:236 authentication/views/dingtalk.py:290 +#: authentication/views/dingtalk.py:211 authentication/views/dingtalk.py:246 msgid "Failed to get user from DingTalk" msgstr "DingTalkからユーザーを取得できませんでした" -#: authentication/views/dingtalk.py:243 authentication/views/dingtalk.py:297 +#: authentication/views/dingtalk.py:253 msgid "Please login with a password and then bind the DingTalk" msgstr "パスワードでログインし、DingTalkをバインドしてください" -#: authentication/views/feishu.py:39 +#: authentication/views/feishu.py:39 authentication/views/feishu.py:166 msgid "FeiShu Error" msgstr "FeiShuエラー" -#: authentication/views/feishu.py:87 +#: authentication/views/feishu.py:67 msgid "FeiShu is already bound" msgstr "FeiShuはすでにバインドされています" -#: authentication/views/feishu.py:129 +#: authentication/views/feishu.py:108 msgid "FeiShu query user failed" msgstr "FeiShuクエリユーザーが失敗しました" -#: authentication/views/feishu.py:138 +#: authentication/views/feishu.py:117 msgid "The FeiShu is already bound to another user" msgstr "FeiShuはすでに別のユーザーにバインドされています" -#: authentication/views/feishu.py:145 +#: authentication/views/feishu.py:124 msgid "Binding FeiShu successfully" msgstr "本を飛ばすのバインドに成功" -#: authentication/views/feishu.py:197 +#: authentication/views/feishu.py:168 msgid "Failed to get user from FeiShu" msgstr "本を飛ばすからユーザーを取得できませんでした" -#: authentication/views/feishu.py:204 -msgid "Please login with a password and then bind the FeiShu" -msgstr "パスワードでログインしてから本を飛ばすをバインドしてください" - #: authentication/views/login.py:182 msgid "Redirecting" msgstr "リダイレクト" @@ -2975,31 +2981,31 @@ msgstr "ログアウト成功" msgid "Logout success, return login page" msgstr "ログアウト成功、ログインページを返す" -#: authentication/views/wecom.py:40 +#: authentication/views/wecom.py:42 msgid "WeCom Error, Please contact your system administrator" msgstr "企業微信エラー、システム管理者に連絡してください" -#: authentication/views/wecom.py:43 +#: authentication/views/wecom.py:45 authentication/views/wecom.py:201 msgid "WeCom Error" msgstr "企業微信エラー" -#: authentication/views/wecom.py:162 +#: authentication/views/wecom.py:144 msgid "WeCom query user failed" msgstr "企業微信ユーザーの問合せに失敗しました" -#: authentication/views/wecom.py:171 +#: authentication/views/wecom.py:153 msgid "The WeCom is already bound to another user" msgstr "この企業の微信はすでに他のユーザーをバインドしている。" -#: authentication/views/wecom.py:178 +#: authentication/views/wecom.py:160 msgid "Binding WeCom successfully" msgstr "企業の微信のバインドに成功" -#: authentication/views/wecom.py:230 authentication/views/wecom.py:284 +#: authentication/views/wecom.py:203 authentication/views/wecom.py:238 msgid "Failed to get user from WeCom" msgstr "企業の微信からユーザーを取得できませんでした" -#: authentication/views/wecom.py:237 authentication/views/wecom.py:291 +#: authentication/views/wecom.py:245 msgid "Please login with a password and then bind the WeCom" msgstr "パスワードでログインしてからWeComをバインドしてください" @@ -3019,7 +3025,7 @@ msgstr "タイミングトリガー" msgid "Ready" msgstr "の準備を" -#: common/const/choices.py:16 terminal/const.py:58 tickets/const.py:29 +#: common/const/choices.py:16 terminal/const.py:59 tickets/const.py:29 #: tickets/const.py:39 msgid "Pending" msgstr "未定" @@ -3082,7 +3088,7 @@ msgstr "は破棄されます" msgid "discard time" msgstr "時間を捨てる" -#: common/db/models.py:34 users/models/user.py:764 +#: common/db/models.py:34 users/models/user.py:774 msgid "Updated by" msgstr "によって更新" @@ -3110,6 +3116,14 @@ msgstr "解析ファイルエラー: {}" msgid "Invalid excel file" msgstr "無効 excel 書類" +#: common/drf/renders/base.py:209 +msgid "" +"{} - The encryption password has not been set - please go to personal " +"information -> file encryption password to set the encryption password" +msgstr "" +"{} - 暗号化パスワードが設定されていません-個人情報->ファイル暗号化パスワード" +"に暗号化パスワードを設定してください" + #: common/exceptions.py:15 #, python-format msgid "%s object does not exist." @@ -3151,7 +3165,7 @@ msgstr "サポートされていません Elasticsearch8" msgid "Network error, please contact system administrator" msgstr "ネットワークエラー、システム管理者に連絡してください" -#: common/sdk/im/wecom/__init__.py:15 +#: common/sdk/im/wecom/__init__.py:16 msgid "WeCom error, please contact system administrator" msgstr "企業微信エラー、システム管理者に連絡してください" @@ -3352,15 +3366,15 @@ msgstr "システムメッセージ" msgid "Publish the station message" msgstr "投稿サイトニュース" -#: ops/ansible/inventory.py:77 +#: ops/ansible/inventory.py:83 msgid "No account available" msgstr "利用可能なアカウントがありません" -#: ops/ansible/inventory.py:236 +#: ops/ansible/inventory.py:248 msgid "Ansible disabled" msgstr "Ansible 無効" -#: ops/ansible/inventory.py:252 +#: ops/ansible/inventory.py:264 msgid "Skip hosts below:" msgstr "次のホストをスキップします: " @@ -3852,10 +3866,6 @@ msgstr "" " 次の %(item_type)s は %(count)s 日以内に期限切れになります\n" " " -#: perms/templates/perms/_msg_permed_items_expire.html:21 -msgid "If you have any question, please contact the administrator" -msgstr "質問があったら、管理者に連絡して下さい" - #: rbac/api/role.py:35 msgid "Internal role, can't be destroy" msgstr "内部の役割は、破壊することはできません" @@ -3934,7 +3944,7 @@ msgid "Scope" msgstr "スコープ" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:727 +#: users/models/user.py:737 msgid "Role" msgstr "ロール" @@ -3950,22 +3960,22 @@ msgstr "組織の役割" msgid "Role binding" msgstr "ロールバインディング" -#: rbac/models/rolebinding.py:145 +#: rbac/models/rolebinding.py:153 msgid "All organizations" msgstr "全ての組織" -#: rbac/models/rolebinding.py:174 +#: rbac/models/rolebinding.py:182 msgid "" "User last role in org, can not be delete, you can remove user from org " "instead" msgstr "" "ユーザーの最後のロールは削除できません。ユーザーを組織から削除できます。" -#: rbac/models/rolebinding.py:181 +#: rbac/models/rolebinding.py:189 msgid "Organization role binding" msgstr "組織の役割バインディング" -#: rbac/models/rolebinding.py:196 +#: rbac/models/rolebinding.py:204 msgid "System role binding" msgstr "システムロールバインディング" @@ -5482,15 +5492,15 @@ msgstr "テスト失敗: アカウントが無効" msgid "Have online sessions" msgstr "オンラインセッションを持つ" -#: terminal/api/session/session.py:239 +#: terminal/api/session/session.py:241 msgid "Session does not exist: {}" msgstr "セッションが存在しません: {}" -#: terminal/api/session/session.py:242 +#: terminal/api/session/session.py:244 msgid "Session is finished or the protocol not supported" msgstr "セッションが終了したか、プロトコルがサポートされていません" -#: terminal/api/session/session.py:255 +#: terminal/api/session/session.py:257 msgid "User does not have permission" msgstr "ユーザーに権限がありません" @@ -5543,7 +5553,7 @@ msgstr "クリティカル" msgid "High" msgstr "高い" -#: terminal/const.py:32 terminal/const.py:65 +#: terminal/const.py:32 terminal/const.py:66 #: users/templates/users/reset_password.html:50 msgid "Normal" msgstr "正常" @@ -5552,19 +5562,19 @@ msgstr "正常" msgid "Offline" msgstr "オフライン" -#: terminal/const.py:61 +#: terminal/const.py:62 msgid "Mismatch" msgstr "一致しない" -#: terminal/const.py:66 +#: terminal/const.py:67 msgid "Tunnel" msgstr "" -#: terminal/const.py:71 +#: terminal/const.py:72 msgid "Read Only" msgstr "読み取り専用" -#: terminal/const.py:72 +#: terminal/const.py:73 msgid "Writable" msgstr "書き込み可能" @@ -6440,7 +6450,7 @@ msgstr "無効な承認アクション" msgid "This user is not authorized to approve this ticket" msgstr "このユーザーはこの作業指示を承認する権限がありません" -#: users/api/user.py:182 +#: users/api/user.py:185 msgid "Could not reset self otp, use profile reset instead" msgstr "自己otpをリセットできませんでした、代わりにプロファイルリセットを使用" @@ -6551,7 +6561,7 @@ msgstr "公開鍵は古いものと同じであってはなりません。" msgid "Not a valid ssh public key" msgstr "有効なssh公開鍵ではありません" -#: users/forms/profile.py:170 users/models/user.py:750 +#: users/forms/profile.py:170 users/models/user.py:760 msgid "Public key" msgstr "公開キー" @@ -6559,68 +6569,68 @@ msgstr "公開キー" msgid "Force enable" msgstr "強制有効" -#: users/models/user.py:729 users/serializers/user.py:171 +#: users/models/user.py:739 users/serializers/user.py:171 msgid "Is service account" msgstr "サービスアカウントです" -#: users/models/user.py:731 +#: users/models/user.py:741 msgid "Avatar" msgstr "アバター" -#: users/models/user.py:734 +#: users/models/user.py:744 msgid "Wechat" msgstr "微信" -#: users/models/user.py:737 users/serializers/user.py:109 +#: users/models/user.py:747 users/serializers/user.py:109 msgid "Phone" msgstr "電話" -#: users/models/user.py:743 +#: users/models/user.py:753 msgid "OTP secret key" msgstr "OTP 秘密" -#: users/models/user.py:747 +#: users/models/user.py:757 msgid "Private key" msgstr "ssh秘密鍵" -#: users/models/user.py:753 +#: users/models/user.py:763 msgid "Secret key" msgstr "秘密キー" -#: users/models/user.py:758 users/serializers/profile.py:149 +#: users/models/user.py:768 users/serializers/profile.py:149 #: users/serializers/user.py:168 msgid "Is first login" msgstr "最初のログインです" -#: users/models/user.py:772 +#: users/models/user.py:782 msgid "Date password last updated" msgstr "最終更新日パスワード" -#: users/models/user.py:775 +#: users/models/user.py:785 msgid "Need update password" msgstr "更新パスワードが必要" -#: users/models/user.py:913 +#: users/models/user.py:923 msgid "Can invite user" msgstr "ユーザーを招待できます" -#: users/models/user.py:914 +#: users/models/user.py:924 msgid "Can remove user" msgstr "ユーザーを削除できます" -#: users/models/user.py:915 +#: users/models/user.py:925 msgid "Can match user" msgstr "ユーザーに一致できます" -#: users/models/user.py:924 +#: users/models/user.py:934 msgid "Administrator" msgstr "管理者" -#: users/models/user.py:927 +#: users/models/user.py:937 msgid "Administrator is the super user of system" msgstr "管理者はシステムのスーパーユーザーです" -#: users/models/user.py:952 +#: users/models/user.py:962 msgid "User password history" msgstr "ユーザーパスワード履歴" @@ -7565,3 +7575,6 @@ msgstr "究極のエディション" #: xpack/plugins/license/models.py:86 msgid "Community edition" msgstr "コミュニティ版" + +#~ msgid "Please login with a password and then bind the FeiShu" +#~ msgstr "パスワードでログインしてから本を飛ばすをバインドしてください" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 521993edd..93db09aec 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:0788b48bc50cffe3e7ff83803ef0edadfc120c9165bfe6bccd1f896d8bf39397 -size 114419 +oid sha256:5dd509799bc1f28429dc4500dd893ab3872879c298e7016ecd219a7b9a4597a0 +size 114702 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index ff2b9c6a2..e5cc2a102 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-04-21 14:05+0800\n" +"POT-Creation-Date: 2023-05-09 18:31+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -86,7 +86,7 @@ msgstr "更新" #: accounts/const/account.py:27 #: accounts/serializers/automations/change_secret.py:156 audits/const.py:53 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:58 terminal/const.py:60 xpack/plugins/cloud/const.py:41 +#: ops/const.py:58 terminal/const.py:61 xpack/plugins/cloud/const.py:41 msgid "Failed" msgstr "失败" @@ -178,14 +178,14 @@ msgstr "创建并推送" msgid "Only create" msgstr "仅创建" -#: accounts/models/account.py:47 +#: accounts/models/account.py:49 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:199 -#: accounts/serializers/account/account.py:232 +#: accounts/serializers/account/account.py:200 +#: accounts/serializers/account/account.py:233 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 -#: acls/models/base.py:100 acls/serializers/base.py:56 +#: acls/models/base.py:100 acls/serializers/base.py:76 #: assets/models/asset/common.py:92 assets/models/asset/common.py:306 #: assets/models/cmd_filter.py:36 assets/serializers/domain.py:19 #: assets/serializers/label.py:27 audits/models.py:48 @@ -197,29 +197,32 @@ msgstr "仅创建" msgid "Asset" msgstr "资产" -#: accounts/models/account.py:51 accounts/serializers/account/account.py:204 +#: accounts/models/account.py:53 accounts/models/account.py:113 +#: accounts/serializers/account/account.py:205 +#: accounts/serializers/account/account.py:243 +#: accounts/serializers/account/template.py:16 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" msgstr "切换自" -#: accounts/models/account.py:53 settings/serializers/auth/cas.py:20 +#: accounts/models/account.py:55 settings/serializers/auth/cas.py:20 #: settings/serializers/auth/feishu.py:20 terminal/models/applet/applet.py:29 msgid "Version" msgstr "版本" -#: accounts/models/account.py:55 accounts/serializers/account/account.py:200 -#: users/models/user.py:768 +#: accounts/models/account.py:57 accounts/serializers/account/account.py:201 +#: users/models/user.py:778 msgid "Source" msgstr "来源" -#: accounts/models/account.py:56 +#: accounts/models/account.py:58 msgid "Source ID" msgstr "来源 ID" -#: accounts/models/account.py:59 +#: accounts/models/account.py:61 #: accounts/serializers/automations/change_secret.py:113 #: accounts/serializers/automations/change_secret.py:133 -#: acls/models/base.py:102 acls/serializers/base.py:57 +#: acls/models/base.py:102 acls/serializers/base.py:77 #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 #: audits/models.py:49 ops/models/base.py:18 #: perms/models/asset_permission.py:70 perms/serializers/permission.py:39 @@ -228,35 +231,35 @@ msgstr "来源 ID" msgid "Account" msgstr "账号" -#: accounts/models/account.py:65 +#: accounts/models/account.py:67 msgid "Can view asset account secret" msgstr "可以查看资产账号密码" -#: accounts/models/account.py:66 +#: accounts/models/account.py:68 msgid "Can view asset history account" msgstr "可以查看资产历史账号" -#: accounts/models/account.py:67 +#: accounts/models/account.py:69 msgid "Can view asset history account secret" msgstr "可以查看资产历史账号密码" -#: accounts/models/account.py:68 +#: accounts/models/account.py:70 msgid "Can verify account" msgstr "可以验证账号" -#: accounts/models/account.py:69 +#: accounts/models/account.py:71 msgid "Can push account" msgstr "可以推送账号" -#: accounts/models/account.py:110 +#: accounts/models/account.py:117 msgid "Account template" msgstr "账号模版" -#: accounts/models/account.py:115 +#: accounts/models/account.py:122 msgid "Can view asset account template secret" msgstr "可以查看资产账号模版密码" -#: accounts/models/account.py:116 +#: accounts/models/account.py:123 msgid "Can change asset account template secret" msgstr "可以更改资产账号模版密码" @@ -353,7 +356,7 @@ msgid "Can add push account execution" msgstr "创建推送账号执行" #: accounts/models/automations/change_secret.py:18 accounts/models/base.py:36 -#: accounts/serializers/account/account.py:394 +#: accounts/serializers/account/account.py:410 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:46 #: authentication/serializers/connect_token_secret.py:41 @@ -403,8 +406,9 @@ msgid "Date finished" msgstr "结束日期" #: accounts/models/automations/change_secret.py:93 -#: accounts/serializers/account/account.py:234 assets/const/automation.py:8 -#: common/const/choices.py:20 +#: accounts/serializers/account/account.py:235 assets/const/automation.py:8 +#: authentication/views/base.py:29 authentication/views/base.py:30 +#: authentication/views/base.py:31 common/const/choices.py:20 msgid "Error" msgstr "错误" @@ -422,13 +426,13 @@ msgstr "最后登录日期" #: accounts/models/automations/gather_account.py:17 #: accounts/models/automations/push_account.py:15 accounts/models/base.py:34 -#: acls/serializers/base.py:18 acls/serializers/base.py:49 +#: acls/serializers/base.py:19 acls/serializers/base.py:50 #: assets/models/_user.py:23 audits/models.py:157 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:112 -#: users/models/user.py:715 users/templates/users/_msg_user_created.html:12 +#: users/models/user.py:725 users/templates/users/_msg_user_created.html:12 #: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" msgstr "用户名" @@ -455,8 +459,8 @@ msgid "Triggers" msgstr "触发方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:81 -#: acls/serializers/base.py:81 acls/serializers/login_acl.py:26 -#: assets/models/cmd_filter.py:81 audits/models.py:65 audits/serializers.py:82 +#: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 +#: audits/models.py:65 audits/serializers.py:82 #: authentication/serializers/connect_token_secret.py:108 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" @@ -471,14 +475,14 @@ msgid "Verify asset account" msgstr "账号验证" #: accounts/models/base.py:33 acls/models/base.py:75 -#: acls/models/command_acl.py:21 acls/serializers/base.py:34 +#: acls/models/command_acl.py:21 acls/serializers/base.py:35 #: applications/models.py:9 assets/models/_user.py:22 #: assets/models/asset/common.py:90 assets/models/asset/common.py:123 #: assets/models/cmd_filter.py:21 assets/models/domain.py:18 #: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/platform.py:13 assets/models/platform.py:89 #: assets/serializers/asset/common.py:145 assets/serializers/platform.py:92 -#: assets/serializers/platform.py:193 +#: assets/serializers/platform.py:194 #: authentication/serializers/connect_token_secret.py:102 ops/mixin.py:21 #: ops/models/adhoc.py:21 ops/models/celery.py:15 ops/models/celery.py:57 #: ops/models/job.py:92 ops/models/playbook.py:23 ops/serializers/job.py:20 @@ -488,7 +492,7 @@ msgstr "账号验证" #: terminal/models/component/endpoint.py:90 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:15 #: terminal/models/component/terminal.py:84 users/forms/profile.py:33 -#: users/models/group.py:13 users/models/user.py:717 +#: users/models/group.py:13 users/models/user.py:727 #: xpack/plugins/cloud/models.py:28 msgid "Name" msgstr "名称" @@ -524,13 +528,6 @@ msgstr "" "{} - 账号备份任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设" "置加密密码" -msgid "" -"{} - The encryption password has not been set - please go to personal information -> file " -"encryption password to set the encryption password" -msgstr "" -"{} - 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设" -"置加密密码" - #: accounts/notifications.py:33 msgid "Notification of implementation result of encryption change plan" msgstr "改密计划任务结果通知" @@ -558,16 +555,16 @@ msgstr "立即推送" msgid "Exist policy" msgstr "账号存在策略" -#: accounts/serializers/account/account.py:179 applications/models.py:11 +#: accounts/serializers/account/account.py:180 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:90 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 -#: assets/serializers/platform.py:110 assets/serializers/platform.py:194 +#: assets/serializers/platform.py:110 assets/serializers/platform.py:195 #: perms/serializers/user_permission.py:26 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 msgid "Category" msgstr "类别" -#: accounts/serializers/account/account.py:180 +#: accounts/serializers/account/account.py:181 #: 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 @@ -586,27 +583,27 @@ msgstr "类别" msgid "Type" msgstr "类型" -#: accounts/serializers/account/account.py:195 +#: accounts/serializers/account/account.py:196 msgid "Asset not found" msgstr "资产不存在" -#: accounts/serializers/account/account.py:201 +#: accounts/serializers/account/account.py:202 #: accounts/serializers/account/base.py:64 msgid "Has secret" msgstr "已托管密码" -#: accounts/serializers/account/account.py:233 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:234 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:235 +#: accounts/serializers/account/account.py:236 msgid "Changed" msgstr "已修改" -#: accounts/serializers/account/account.py:241 +#: accounts/serializers/account/account.py:245 #: accounts/serializers/automations/base.py:22 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 ops/models/base.py:17 @@ -615,29 +612,29 @@ msgstr "已修改" msgid "Assets" msgstr "资产" -#: accounts/serializers/account/account.py:293 +#: accounts/serializers/account/account.py:297 msgid "Account already exists" msgstr "账号已存在" -#: accounts/serializers/account/account.py:330 +#: accounts/serializers/account/account.py:346 #, python-format msgid "Asset does not support this secret type: %s" msgstr "资产不支持账号类型: %s" -#: accounts/serializers/account/account.py:361 +#: accounts/serializers/account/account.py:377 msgid "Account has exist" msgstr "账号已存在" -#: accounts/serializers/account/account.py:395 +#: accounts/serializers/account/account.py:411 #: authentication/serializers/connect_token_secret.py:146 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:33 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:402 acls/models/base.py:98 -#: acls/models/login_acl.py:13 acls/serializers/base.py:55 -#: acls/serializers/login_acl.py:22 assets/models/cmd_filter.py:24 +#: accounts/serializers/account/account.py:418 acls/models/base.py:98 +#: acls/models/login_acl.py:13 acls/serializers/base.py:75 +#: acls/serializers/login_acl.py:21 assets/models/cmd_filter.py:24 #: assets/models/label.py:16 audits/models.py:44 audits/models.py:63 #: audits/models.py:141 authentication/models/connection_token.py:30 #: authentication/models/sso_token.py:16 @@ -648,12 +645,12 @@ msgstr "ID" #: terminal/models/session/session.py:30 terminal/models/session/sharing.py:32 #: terminal/notifications.py:96 terminal/notifications.py:144 #: terminal/serializers/command.py:16 tickets/models/comment.py:21 -#: users/const.py:14 users/models/user.py:911 users/models/user.py:942 +#: users/const.py:14 users/models/user.py:921 users/models/user.py:952 #: users/serializers/group.py:18 msgid "User" msgstr "用户" -#: accounts/serializers/account/account.py:403 +#: accounts/serializers/account/account.py:419 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:98 terminal/notifications.py:146 msgid "Date" @@ -685,10 +682,14 @@ msgid "Key password" msgstr "密钥密码" #: accounts/serializers/account/base.py:80 -#: assets/serializers/asset/common.py:305 +#: assets/serializers/asset/common.py:306 msgid "Spec info" msgstr "特殊信息" +#: accounts/serializers/account/base.py:81 +msgid "Tip: If no username is required for authentication, fill in `null`" +msgstr "提示: 如果认证时不需要用户名,则填写为 null" + #: accounts/serializers/automations/base.py:23 #: assets/models/asset/common.py:129 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/serializers/automations/base.py:21 @@ -726,7 +727,7 @@ msgstr "自动化任务执行历史" #: accounts/serializers/automations/change_secret.py:155 audits/const.py:52 #: audits/models.py:54 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:39 -#: terminal/const.py:59 terminal/models/session/sharing.py:107 +#: terminal/const.py:60 terminal/models/session/sharing.py:107 #: tickets/views/approve.py:114 msgid "Success" msgstr "成功" @@ -798,8 +799,8 @@ msgstr "优先级" msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" -#: acls/models/base.py:82 acls/serializers/base.py:75 -#: acls/serializers/login_acl.py:24 assets/models/cmd_filter.py:86 +#: acls/models/base.py:82 acls/serializers/base.py:95 +#: acls/serializers/login_acl.py:23 assets/models/cmd_filter.py:86 #: authentication/serializers/connect_token_secret.py:80 msgid "Reviewers" msgstr "审批人" @@ -813,7 +814,7 @@ msgid "Active" msgstr "激活中" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:71 terminal/const.py:67 +#: ops/serializers/job.py:71 terminal/const.py:68 #: terminal/models/session/session.py:43 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -855,7 +856,7 @@ msgstr "命令过滤" msgid "Command confirm" msgstr "命令复核" -#: acls/models/login_acl.py:16 acls/serializers/login_acl.py:30 +#: acls/models/login_acl.py:16 acls/serializers/login_acl.py:28 msgid "Rule" msgstr "规则" @@ -875,11 +876,11 @@ msgstr "登录资产访问控制" msgid "Login asset confirm" msgstr "登录资产复核" -#: acls/serializers/base.py:10 acls/serializers/login_acl.py:17 +#: acls/serializers/base.py:11 acls/serializers/login_acl.py:16 msgid "With * indicating a match all. " msgstr "* 表示匹配所有. " -#: acls/serializers/base.py:25 +#: acls/serializers/base.py:26 msgid "" "With * indicating a match all. Such as: 192.168.10.1, 192.168.1.0/24, " "10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 (Domain name " @@ -888,35 +889,35 @@ msgstr "" "* 表示匹配所有。例如: 192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:" "db8:2de::e13, 2001:db8:1a:1110::/64 (支持网域)" -#: acls/serializers/base.py:40 assets/serializers/asset/host.py:19 +#: acls/serializers/base.py:41 assets/serializers/asset/host.py:19 msgid "IP/Host" msgstr "IP/主机" -#: acls/serializers/base.py:60 +#: acls/serializers/base.py:80 msgid "User (username)" msgstr "用户(用户名)" -#: acls/serializers/base.py:64 +#: acls/serializers/base.py:84 msgid "Asset (name)" msgstr "资产(名称)" -#: acls/serializers/base.py:68 +#: acls/serializers/base.py:88 msgid "Asset (address)" msgstr "资产(地址)" -#: acls/serializers/base.py:72 +#: acls/serializers/base.py:92 msgid "Account (username)" msgstr "账号(用户名)" -#: acls/serializers/base.py:78 acls/serializers/login_acl.py:28 +#: acls/serializers/base.py:98 acls/serializers/login_acl.py:26 msgid "Reviewers amount" msgstr "审批人数量" -#: acls/serializers/base.py:109 tickets/serializers/ticket/ticket.py:76 +#: acls/serializers/base.py:126 tickets/serializers/ticket/ticket.py:76 msgid "The organization `{}` does not exist" msgstr "组织 `{}` 不存在" -#: acls/serializers/base.py:115 +#: acls/serializers/base.py:132 msgid "None of the reviewers belong to Organization `{}`" msgstr "所有复核人都不属于组织 `{}`" @@ -1144,7 +1145,7 @@ msgstr "SSH公钥" #: terminal/models/applet/host.py:111 terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:100 #: terminal/models/session/session.py:47 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:756 +#: tickets/models/ticket/general.py:297 users/models/user.py:766 #: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:111 msgid "Comment" msgstr "备注" @@ -1152,18 +1153,18 @@ msgstr "备注" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:22 #: common/db/models.py:35 ops/models/base.py:54 ops/models/job.py:191 -#: users/models/user.py:943 +#: users/models/user.py:953 msgid "Date created" msgstr "创建日期" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:36 users/models/user.py:777 +#: common/db/models.py:36 users/models/user.py:787 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:21 -#: common/db/models.py:33 users/models/user.py:763 +#: common/db/models.py:33 users/models/user.py:773 #: users/serializers/group.py:31 msgid "Created by" msgstr "创建者" @@ -1262,7 +1263,7 @@ msgstr "系统平台" #: assets/models/asset/common.py:127 assets/models/domain.py:21 #: authentication/serializers/connect_token_secret.py:125 -#: perms/serializers/user_permission.py:28 +#: perms/serializers/user_permission.py:29 msgid "Domain" msgstr "网域" @@ -1270,7 +1271,7 @@ msgstr "网域" msgid "Labels" msgstr "标签管理" -#: assets/models/asset/common.py:132 assets/serializers/asset/common.py:306 +#: assets/models/asset/common.py:132 assets/serializers/asset/common.py:307 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "收集资产硬件信息" @@ -1337,7 +1338,7 @@ msgid "Submit selector" msgstr "确认按钮选择器" #: assets/models/automations/base.py:17 assets/models/cmd_filter.py:38 -#: assets/serializers/asset/common.py:304 rbac/tree.py:35 +#: assets/serializers/asset/common.py:305 rbac/tree.py:35 msgid "Accounts" msgstr "账号管理" @@ -1382,7 +1383,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:723 +#: users/models/user.py:733 msgid "User group" msgstr "用户组" @@ -1432,7 +1433,7 @@ msgstr "默认" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:928 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:938 msgid "System" msgstr "系统" @@ -1448,7 +1449,8 @@ msgstr "值" #: assets/serializers/cagegory.py:6 assets/serializers/cagegory.py:13 #: assets/serializers/platform.py:93 #: authentication/serializers/connect_token_secret.py:113 -#: common/serializers/common.py:85 settings/serializers/sms.py:7 +#: common/serializers/common.py:85 perms/serializers/user_permission.py:28 +#: settings/serializers/sms.py:7 msgid "Label" msgstr "标签" @@ -1581,11 +1583,11 @@ msgstr "内置" msgid "Charset" msgstr "编码" -#: assets/models/platform.py:99 assets/serializers/platform.py:135 +#: assets/models/platform.py:99 assets/serializers/platform.py:136 msgid "Domain enabled" msgstr "启用网域" -#: assets/models/platform.py:101 assets/serializers/platform.py:134 +#: assets/models/platform.py:101 assets/serializers/platform.py:135 msgid "Su enabled" msgstr "启用账号切换" @@ -1621,19 +1623,19 @@ msgid "Node path" msgstr "节点路径" #: assets/serializers/asset/common.py:144 -#: assets/serializers/asset/common.py:307 +#: assets/serializers/asset/common.py:308 msgid "Auto info" msgstr "自动化信息" -#: assets/serializers/asset/common.py:226 +#: assets/serializers/asset/common.py:227 msgid "Platform not exist" msgstr "平台不存在" -#: assets/serializers/asset/common.py:262 +#: assets/serializers/asset/common.py:263 msgid "port out of range (1-65535)" msgstr "端口超出范围 (1-65535)" -#: assets/serializers/asset/common.py:269 +#: assets/serializers/asset/common.py:270 msgid "Protocol is required: {}" msgstr "协议是必填的: {}" @@ -1755,15 +1757,15 @@ msgstr "" msgid "Automation" msgstr "自动化" -#: assets/serializers/platform.py:136 +#: assets/serializers/platform.py:137 msgid "Default Domain" msgstr "默认网域" -#: assets/serializers/platform.py:145 +#: assets/serializers/platform.py:146 msgid "type is required" msgstr "类型 该字段是必填项。" -#: assets/serializers/platform.py:182 +#: assets/serializers/platform.py:183 msgid "Protocols is required" msgstr "协议是必填的" @@ -2013,7 +2015,7 @@ msgstr "用户代理" #: audits/models.py:169 audits/serializers.py:47 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:740 +#: users/forms/profile.py:65 users/models/user.py:750 #: users/serializers/profile.py:126 msgid "MFA" msgstr "MFA" @@ -2067,22 +2069,24 @@ msgid "Auth Token" msgstr "认证令牌" #: audits/signal_handlers/login_log.py:31 authentication/notifications.py:73 -#: authentication/views/login.py:74 authentication/views/wecom.py:177 +#: authentication/views/login.py:74 authentication/views/wecom.py:159 #: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:778 +#: users/models/user.py:680 users/models/user.py:788 msgid "WeCom" msgstr "企业微信" -#: audits/signal_handlers/login_log.py:32 authentication/views/feishu.py:144 +#: audits/signal_handlers/login_log.py:32 authentication/views/feishu.py:123 #: authentication/views/login.py:86 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:780 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:682 +#: users/models/user.py:790 msgid "FeiShu" msgstr "飞书" -#: audits/signal_handlers/login_log.py:33 authentication/views/dingtalk.py:179 +#: audits/signal_handlers/login_log.py:33 authentication/views/dingtalk.py:160 #: authentication/views/login.py:80 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:779 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:681 +#: users/models/user.py:789 msgid "DingTalk" msgstr "钉钉" @@ -2099,19 +2103,19 @@ msgstr "清理审计会话任务日志" msgid "This action require verify your MFA" msgstr "该操作需要验证您的 MFA, 请先开启并配置" -#: authentication/api/connection_token.py:296 +#: authentication/api/connection_token.py:300 msgid "Account not found" msgstr "账号未找到" -#: authentication/api/connection_token.py:299 +#: authentication/api/connection_token.py:303 msgid "Permission expired" msgstr "授权已过期" -#: authentication/api/connection_token.py:311 +#: authentication/api/connection_token.py:315 msgid "ACL action is reject" msgstr "ACL 动作是拒绝" -#: authentication/api/connection_token.py:315 +#: authentication/api/connection_token.py:319 msgid "ACL action is review" msgstr "ACL 动作是复核" @@ -2119,7 +2123,7 @@ msgstr "ACL 动作是复核" msgid "Current user not support mfa type: {}" msgstr "当前用户不支持 MFA 类型: {}" -#: authentication/api/password.py:31 terminal/api/session/session.py:247 +#: authentication/api/password.py:31 terminal/api/session/session.py:249 #: users/views/profile/reset.py:44 msgid "User does not exist: {}" msgstr "用户不存在: {}" @@ -2333,21 +2337,21 @@ msgstr "手机号没有设置" msgid "SSO auth closed" msgstr "SSO 认证关闭了" -#: authentication/errors/mfa.py:18 authentication/views/wecom.py:79 +#: authentication/errors/mfa.py:18 authentication/views/wecom.py:61 msgid "WeCom is already bound" msgstr "企业微信已经绑定" -#: authentication/errors/mfa.py:23 authentication/views/wecom.py:236 -#: authentication/views/wecom.py:290 +#: authentication/errors/mfa.py:23 authentication/views/wecom.py:202 +#: authentication/views/wecom.py:244 msgid "WeCom is not bound" msgstr "没有绑定企业微信" -#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:242 -#: authentication/views/dingtalk.py:296 +#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:210 +#: authentication/views/dingtalk.py:252 msgid "DingTalk is not bound" msgstr "钉钉没有绑定" -#: authentication/errors/mfa.py:33 authentication/views/feishu.py:203 +#: authentication/errors/mfa.py:33 authentication/views/feishu.py:167 msgid "FeiShu is not bound" msgstr "没有绑定飞书" @@ -2502,7 +2506,7 @@ msgstr "资产名称" #: authentication/models/connection_token.py:43 #: 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:761 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:771 msgid "Date expired" msgstr "失效日期" @@ -2605,7 +2609,7 @@ msgstr "已过期" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/email.py:19 #: settings/serializers/email.py:50 users/forms/profile.py:102 -#: users/forms/profile.py:106 users/models/user.py:719 +#: users/forms/profile.py:106 users/models/user.py:729 #: users/templates/users/forgot_password.html:116 #: users/views/profile/reset.py:73 msgid "Email" @@ -2839,76 +2843,77 @@ msgstr "复制成功" msgid "LAN" msgstr "局域网" -#: authentication/views/dingtalk.py:41 +#: authentication/views/base.py:64 +#: perms/templates/perms/_msg_permed_items_expire.html:21 +msgid "If you have any question, please contact the administrator" +msgstr "如果有疑问或需求,请联系系统管理员" + +#: authentication/views/dingtalk.py:42 msgid "DingTalk Error, Please contact your system administrator" msgstr "钉钉错误,请联系系统管理员" -#: authentication/views/dingtalk.py:44 +#: authentication/views/dingtalk.py:45 authentication/views/dingtalk.py:209 msgid "DingTalk Error" msgstr "钉钉错误" -#: authentication/views/dingtalk.py:56 authentication/views/feishu.py:51 -#: authentication/views/wecom.py:55 +#: authentication/views/dingtalk.py:57 authentication/views/feishu.py:51 +#: authentication/views/wecom.py:57 msgid "" "The system configuration is incorrect. Please contact your administrator" msgstr "企业配置错误,请联系系统管理员" -#: authentication/views/dingtalk.py:80 +#: authentication/views/dingtalk.py:61 msgid "DingTalk is already bound" msgstr "钉钉已经绑定" -#: authentication/views/dingtalk.py:148 authentication/views/wecom.py:147 +#: authentication/views/dingtalk.py:129 authentication/views/wecom.py:129 msgid "Invalid user_id" msgstr "无效的 user_id" -#: authentication/views/dingtalk.py:164 +#: authentication/views/dingtalk.py:145 msgid "DingTalk query user failed" msgstr "钉钉查询用户失败" -#: authentication/views/dingtalk.py:173 +#: authentication/views/dingtalk.py:154 msgid "The DingTalk is already bound to another user" msgstr "该钉钉已经绑定其他用户" -#: authentication/views/dingtalk.py:180 +#: authentication/views/dingtalk.py:161 msgid "Binding DingTalk successfully" msgstr "绑定 钉钉 成功" -#: authentication/views/dingtalk.py:236 authentication/views/dingtalk.py:290 +#: authentication/views/dingtalk.py:211 authentication/views/dingtalk.py:246 msgid "Failed to get user from DingTalk" msgstr "从钉钉获取用户失败" -#: authentication/views/dingtalk.py:243 authentication/views/dingtalk.py:297 +#: authentication/views/dingtalk.py:253 msgid "Please login with a password and then bind the DingTalk" msgstr "请使用密码登录,然后绑定钉钉" -#: authentication/views/feishu.py:39 +#: authentication/views/feishu.py:39 authentication/views/feishu.py:166 msgid "FeiShu Error" msgstr "飞书错误" -#: authentication/views/feishu.py:87 +#: authentication/views/feishu.py:67 msgid "FeiShu is already bound" msgstr "飞书已经绑定" -#: authentication/views/feishu.py:129 +#: authentication/views/feishu.py:108 msgid "FeiShu query user failed" msgstr "飞书查询用户失败" -#: authentication/views/feishu.py:138 +#: authentication/views/feishu.py:117 msgid "The FeiShu is already bound to another user" msgstr "该飞书已经绑定其他用户" -#: authentication/views/feishu.py:145 +#: authentication/views/feishu.py:124 msgid "Binding FeiShu successfully" msgstr "绑定 飞书 成功" -#: authentication/views/feishu.py:197 +#: authentication/views/feishu.py:168 msgid "Failed to get user from FeiShu" msgstr "从飞书获取用户失败" -#: authentication/views/feishu.py:204 -msgid "Please login with a password and then bind the FeiShu" -msgstr "请使用密码登录,然后绑定飞书" - #: authentication/views/login.py:182 msgid "Redirecting" msgstr "跳转中" @@ -2945,31 +2950,31 @@ msgstr "退出登录成功" msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: authentication/views/wecom.py:40 +#: authentication/views/wecom.py:42 msgid "WeCom Error, Please contact your system administrator" msgstr "企业微信错误,请联系系统管理员" -#: authentication/views/wecom.py:43 +#: authentication/views/wecom.py:45 authentication/views/wecom.py:201 msgid "WeCom Error" msgstr "企业微信错误" -#: authentication/views/wecom.py:162 +#: authentication/views/wecom.py:144 msgid "WeCom query user failed" msgstr "企业微信查询用户失败" -#: authentication/views/wecom.py:171 +#: authentication/views/wecom.py:153 msgid "The WeCom is already bound to another user" msgstr "该企业微信已经绑定其他用户" -#: authentication/views/wecom.py:178 +#: authentication/views/wecom.py:160 msgid "Binding WeCom successfully" msgstr "绑定 企业微信 成功" -#: authentication/views/wecom.py:230 authentication/views/wecom.py:284 +#: authentication/views/wecom.py:203 authentication/views/wecom.py:238 msgid "Failed to get user from WeCom" msgstr "从企业微信获取用户失败" -#: authentication/views/wecom.py:237 authentication/views/wecom.py:291 +#: authentication/views/wecom.py:245 msgid "Please login with a password and then bind the WeCom" msgstr "请使用密码登录,然后绑定企业微信" @@ -2989,7 +2994,7 @@ msgstr "定时触发" msgid "Ready" msgstr "准备" -#: common/const/choices.py:16 terminal/const.py:58 tickets/const.py:29 +#: common/const/choices.py:16 terminal/const.py:59 tickets/const.py:29 #: tickets/const.py:39 msgid "Pending" msgstr "待定的" @@ -3052,7 +3057,7 @@ msgstr "忽略的" msgid "discard time" msgstr "忽略时间" -#: common/db/models.py:34 users/models/user.py:764 +#: common/db/models.py:34 users/models/user.py:774 msgid "Updated by" msgstr "最后更新者" @@ -3080,6 +3085,12 @@ msgstr "解析文件错误: {}" msgid "Invalid excel file" msgstr "无效的 excel 文件" +#: common/drf/renders/base.py:209 +msgid "" +"{} - The encryption password has not been set - please go to personal " +"information -> file encryption password to set the encryption password" +msgstr "{} - 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设置加密密码" + #: common/exceptions.py:15 #, python-format msgid "%s object does not exist." @@ -3121,7 +3132,7 @@ msgstr "不支持 Elasticsearch8" msgid "Network error, please contact system administrator" msgstr "网络错误,请联系系统管理员" -#: common/sdk/im/wecom/__init__.py:15 +#: common/sdk/im/wecom/__init__.py:16 msgid "WeCom error, please contact system administrator" msgstr "企业微信错误,请联系系统管理员" @@ -3317,15 +3328,15 @@ msgstr "系统信息" msgid "Publish the station message" msgstr "发布站内消息" -#: ops/ansible/inventory.py:77 +#: ops/ansible/inventory.py:83 msgid "No account available" msgstr "无可用账号" -#: ops/ansible/inventory.py:236 +#: ops/ansible/inventory.py:248 msgid "Ansible disabled" msgstr "Ansible 已禁用" -#: ops/ansible/inventory.py:252 +#: ops/ansible/inventory.py:264 msgid "Skip hosts below:" msgstr "跳过以下主机: " @@ -3816,10 +3827,6 @@ msgstr "" " 以下 %(item_type)s 即将在 %(count)s 天后过期\n" " " -#: perms/templates/perms/_msg_permed_items_expire.html:21 -msgid "If you have any question, please contact the administrator" -msgstr "如果有疑问或需求,请联系系统管理员" - #: rbac/api/role.py:35 msgid "Internal role, can't be destroy" msgstr "内部角色,不能删除" @@ -3898,7 +3905,7 @@ msgid "Scope" msgstr "范围" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:727 +#: users/models/user.py:737 msgid "Role" msgstr "角色" @@ -3914,21 +3921,21 @@ msgstr "组织角色" msgid "Role binding" msgstr "角色绑定" -#: rbac/models/rolebinding.py:145 +#: rbac/models/rolebinding.py:153 msgid "All organizations" msgstr "所有组织" -#: rbac/models/rolebinding.py:174 +#: rbac/models/rolebinding.py:182 msgid "" "User last role in org, can not be delete, you can remove user from org " "instead" msgstr "用户最后一个角色,不能删除,你可以将用户从组织移除" -#: rbac/models/rolebinding.py:181 +#: rbac/models/rolebinding.py:189 msgid "Organization role binding" msgstr "组织角色绑定" -#: rbac/models/rolebinding.py:196 +#: rbac/models/rolebinding.py:204 msgid "System role binding" msgstr "系统角色绑定" @@ -5410,15 +5417,15 @@ msgstr "测试失败: 账号无效" msgid "Have online sessions" msgstr "有在线会话" -#: terminal/api/session/session.py:239 +#: terminal/api/session/session.py:241 msgid "Session does not exist: {}" msgstr "会话不存在: {}" -#: terminal/api/session/session.py:242 +#: terminal/api/session/session.py:244 msgid "Session is finished or the protocol not supported" msgstr "会话已经完成或协议不支持" -#: terminal/api/session/session.py:255 +#: terminal/api/session/session.py:257 msgid "User does not have permission" msgstr "用户没有权限" @@ -5471,7 +5478,7 @@ msgstr "严重" msgid "High" msgstr "较高" -#: terminal/const.py:32 terminal/const.py:65 +#: terminal/const.py:32 terminal/const.py:66 #: users/templates/users/reset_password.html:50 msgid "Normal" msgstr "正常" @@ -5480,19 +5487,19 @@ msgstr "正常" msgid "Offline" msgstr "离线" -#: terminal/const.py:61 +#: terminal/const.py:62 msgid "Mismatch" msgstr "未匹配" -#: terminal/const.py:66 +#: terminal/const.py:67 msgid "Tunnel" msgstr "隧道" -#: terminal/const.py:71 +#: terminal/const.py:72 msgid "Read Only" msgstr "只读" -#: terminal/const.py:72 +#: terminal/const.py:73 msgid "Writable" msgstr "读写" @@ -6356,7 +6363,7 @@ msgstr "无效的审批动作" msgid "This user is not authorized to approve this ticket" msgstr "此用户无权审批此工单" -#: users/api/user.py:182 +#: users/api/user.py:185 msgid "Could not reset self otp, use profile reset instead" msgstr "不能在该页面重置 MFA 多因子认证, 请去个人信息页面重置" @@ -6467,7 +6474,7 @@ msgstr "不能和原来的密钥相同" msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/profile.py:170 users/models/user.py:750 +#: users/forms/profile.py:170 users/models/user.py:760 msgid "Public key" msgstr "SSH公钥" @@ -6475,68 +6482,68 @@ msgstr "SSH公钥" msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:729 users/serializers/user.py:171 +#: users/models/user.py:739 users/serializers/user.py:171 msgid "Is service account" msgstr "服务账号" -#: users/models/user.py:731 +#: users/models/user.py:741 msgid "Avatar" msgstr "头像" -#: users/models/user.py:734 +#: users/models/user.py:744 msgid "Wechat" msgstr "微信" -#: users/models/user.py:737 users/serializers/user.py:109 +#: users/models/user.py:747 users/serializers/user.py:109 msgid "Phone" msgstr "手机" -#: users/models/user.py:743 +#: users/models/user.py:753 msgid "OTP secret key" msgstr "OTP 密钥" -#: users/models/user.py:747 +#: users/models/user.py:757 msgid "Private key" msgstr "ssh私钥" -#: users/models/user.py:753 +#: users/models/user.py:763 msgid "Secret key" msgstr "Secret key" -#: users/models/user.py:758 users/serializers/profile.py:149 +#: users/models/user.py:768 users/serializers/profile.py:149 #: users/serializers/user.py:168 msgid "Is first login" msgstr "首次登录" -#: users/models/user.py:772 +#: users/models/user.py:782 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:775 +#: users/models/user.py:785 msgid "Need update password" msgstr "需要更新密码" -#: users/models/user.py:913 +#: users/models/user.py:923 msgid "Can invite user" msgstr "可以邀请用户" -#: users/models/user.py:914 +#: users/models/user.py:924 msgid "Can remove user" msgstr "可以移除用户" -#: users/models/user.py:915 +#: users/models/user.py:925 msgid "Can match user" msgstr "可以匹配用户" -#: users/models/user.py:924 +#: users/models/user.py:934 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:927 +#: users/models/user.py:937 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/models/user.py:952 +#: users/models/user.py:962 msgid "User password history" msgstr "用户密码历史" @@ -7466,3 +7473,6 @@ msgstr "旗舰版" #: xpack/plugins/license/models.py:86 msgid "Community edition" msgstr "社区版" + +#~ msgid "Please login with a password and then bind the FeiShu" +#~ msgstr "请使用密码登录,然后绑定飞书" From e6bbaac7de56c2edb930c19eeb5f44175e14722e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 22:15:13 +0000 Subject: [PATCH 43/88] chore(deps): bump django from 3.2.17 to 3.2.19 in /requirements Bumps [django](https://github.com/django/django) from 3.2.17 to 3.2.19. - [Commits](https://github.com/django/django/compare/3.2.17...3.2.19) --- updated-dependencies: - dependency-name: django dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index e01baa999..d68a3134c 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -68,7 +68,7 @@ geoip2==4.5.0 ipip-ipdb==1.6.1 pywinrm==0.4.3 # Django environment -Django==3.2.17 +Django==3.2.19 django-bootstrap3==14.2.0 django-filter==2.4.0 django-formtools==2.2 From 8309f00e5eec3a34a9667131f5a084bf6de9486c Mon Sep 17 00:00:00 2001 From: Bai Date: Tue, 9 May 2023 19:46:34 +0800 Subject: [PATCH 44/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E7=B1=BB=E5=9E=8B=E6=A0=91=E5=BE=AA=E7=8E=AF=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=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/api/tree.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/assets/api/tree.py b/apps/assets/api/tree.py index 9c9ffa8c9..f27afd019 100644 --- a/apps/assets/api/tree.py +++ b/apps/assets/api/tree.py @@ -163,8 +163,10 @@ class CategoryTreeApi(SerializeToTreeNodeMixin, generics.ListAPIView): # 资源数量统计可选项 (asset, account) count_resource = self.request.query_params.get('count_resource', 'asset') - if include_asset and self.request.query_params.get('key'): + if not self.request.query_params.get('key'): + nodes = AllTypes.to_tree_nodes(include_asset, count_resource=count_resource) + elif include_asset: nodes = self.get_assets() else: - nodes = AllTypes.to_tree_nodes(include_asset, count_resource=count_resource) + nodes = [] return Response(data=nodes) From 0e98990e17348d3bc0de1d12e5f2a9cb33b2d695 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 9 May 2023 19:46:34 +0800 Subject: [PATCH 45/88] =?UTF-8?q?perf:=20=E8=BF=9C=E7=A8=8B=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E8=B0=83=E5=BA=A6=E4=BC=98=E5=85=88=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=E7=9A=84=E4=B8=8A=E4=B8=AA=E4=B8=BB=E6=9C=BA=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E4=B8=8A=E4=B8=AA=E8=B4=A6=E5=8F=B7=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=90=8C=E5=90=8D=E8=B4=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authentication/models/connection_token.py | 2 +- .../migrations/0050_auto_20220606_1745.py | 11 ++- .../migrations/0061_applet_can_concurrent.py | 18 +++++ apps/terminal/models/applet/applet.py | 68 ++++++++++++++----- apps/terminal/models/applet/host.py | 35 ++++++++-- apps/terminal/signal_handlers/applet.py | 25 ++++++- apps/terminal/tasks.py | 17 ++++- 7 files changed, 143 insertions(+), 33 deletions(-) create mode 100644 apps/terminal/migrations/0061_applet_can_concurrent.py diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index 63049170b..eef633d95 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -172,7 +172,7 @@ class ConnectionToken(JMSOrgBaseModel): if not applet: return None - host_account = applet.select_host_account() + host_account = applet.select_host_account(self.user) if not host_account: raise JMSException({'error': 'No host account available'}) diff --git a/apps/terminal/migrations/0050_auto_20220606_1745.py b/apps/terminal/migrations/0050_auto_20220606_1745.py index e88d37971..d0eb6ea5d 100644 --- a/apps/terminal/migrations/0050_auto_20220606_1745.py +++ b/apps/terminal/migrations/0050_auto_20220606_1745.py @@ -4,7 +4,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ ('terminal', '0049_endpoint_redis_port'), ] @@ -13,10 +12,10 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='terminal', name='type', - field=models.CharField(choices=[ - ('koko', 'KoKo'), ('guacamole', 'Guacamole'), ('omnidb', 'OmniDB'), - ('xrdp', 'Xrdp'), ('lion', 'Lion'), ('core', 'Core'), ('celery', 'Celery'), - ('magnus', 'Magnus'), ('razor', 'Razor'), ('tinker', 'Tinker'), - ], default='koko', max_length=64, verbose_name='type'), + field=models.CharField( + choices=[('koko', 'KoKo'), ('guacamole', 'Guacamole'), ('omnidb', 'OmniDB'), ('xrdp', 'Xrdp'), + ('lion', 'Lion'), ('core', 'Core'), ('celery', 'Celery'), ('magnus', 'Magnus'), + ('razor', 'Razor'), ('tinker', 'Tinker'), ('video_worker', 'Video Worker')], default='koko', + max_length=64, verbose_name='type'), ), ] diff --git a/apps/terminal/migrations/0061_applet_can_concurrent.py b/apps/terminal/migrations/0061_applet_can_concurrent.py new file mode 100644 index 000000000..4ca762e65 --- /dev/null +++ b/apps/terminal/migrations/0061_applet_can_concurrent.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.17 on 2023-05-09 11:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('terminal', '0060_sessionsharing_action_permission'), + ] + + operations = [ + migrations.AddField( + model_name='applet', + name='can_concurrent', + field=models.BooleanField(default=True, verbose_name='Can concurrent'), + ), + ] diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index c7296eda6..8bf6b3a85 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -32,6 +32,7 @@ class Applet(JMSBaseModel): is_active = models.BooleanField(default=True, verbose_name=_('Is active')) builtin = models.BooleanField(default=False, verbose_name=_('Builtin')) protocols = models.JSONField(default=list, verbose_name=_('Protocol')) + can_concurrent = models.BooleanField(default=True, verbose_name=_('Can concurrent')) tags = models.JSONField(default=list, verbose_name=_('Tags')) comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) hosts = models.ManyToManyField( @@ -134,37 +135,68 @@ class Applet(JMSBaseModel): shutil.copytree(path, pkg_path) return instance, serializer - def select_host_account(self): - # 选择激活的发布机 + def select_host(self, user): hosts = [ host for host in self.hosts.filter(is_active=True) if host.load != 'offline' ] - if not hosts: return None - key_tmpl = 'applet_host_accounts_{}_{}' - host = random.choice(hosts) - using_keys = cache.keys(key_tmpl.format(host.id, '*')) or [] - accounts_username_used = list(cache.get_many(using_keys).values()) - logger.debug('Applet host account using: {}: {}'.format(host.name, accounts_username_used)) - accounts = host.accounts.all() \ - .filter(is_active=True, privileged=False) \ - .exclude(username__in=accounts_username_used) + prefer_key = 'applet_host_prefer_{}'.format(user.id) + prefer_host_id = cache.get(prefer_key, None) + pref_host = [host for host in hosts if host.id == prefer_host_id] + if pref_host: + host = pref_host[0] + else: + host = random.choice(hosts) + cache.set(prefer_key, host.id, timeout=None) + return host - msg = 'Applet host remain accounts: {}: {}'.format(host.name, len(accounts)) + @staticmethod + def random_select_prefer_account(user, host, accounts): + msg = 'Applet host remain public accounts: {}: {}'.format(host.name, len(accounts)) if len(accounts) == 0: logger.error(msg) - else: - logger.debug(msg) - - if not accounts: return None + prefer_host_account_key = 'applet_host_prefer_account_{}_{}'.format(user.id, host.id) + prefer_account_id = cache.get(prefer_host_account_key, None) + prefer_account = accounts.filter(id=prefer_account_id).first() + if prefer_account: + account = prefer_account + else: + account = random.choice(accounts) + cache.set(prefer_host_account_key, account.id, timeout=None) + return account - account = random.choice(accounts) + def select_host_account(self, user): + # 选择激活的发布机 + host = self.select_host(user) + if not host: + return None + can_concurrent = self.can_concurrent and self.type == 'general' + + accounts = host.accounts.all().filter(is_active=True, privileged=False) + private_account = accounts.filter(username='js_{}'.format(user.username)).first() + accounts_using_key_tmpl = 'applet_host_accounts_{}_{}' + + if private_account and can_concurrent: + account = private_account + else: + using_keys = cache.keys(accounts_using_key_tmpl.format(host.id, '*')) or [] + accounts_username_used = list(cache.get_many(using_keys).values()) + logger.debug('Applet host account using: {}: {}'.format(host.name, accounts_username_used)) + + # 优先使用 private account + if private_account and private_account.username not in accounts_username_used: + account = private_account + else: + accounts = accounts.exclude(username__in=accounts_username_used) + account = self.random_select_prefer_account(user, host, accounts) + if not account: + return ttl = 60 * 60 * 24 - lock_key = key_tmpl.format(host.id, account.username) + lock_key = accounts_using_key_tmpl.format(host.id, account.username) cache.set(lock_key, account.username, ttl) return { diff --git a/apps/terminal/models/applet/host.py b/apps/terminal/models/applet/host.py index 47de718df..1ad129baa 100644 --- a/apps/terminal/models/applet/host.py +++ b/apps/terminal/models/applet/host.py @@ -84,9 +84,13 @@ class AppletHost(Host): return random_string(16, special_char=True) def generate_accounts(self): - amount = int(os.getenv('TERMINAL_ACCOUNTS_AMOUNT', 100)) - now_count = self.accounts.filter(privileged=False).count() - need = amount - now_count + self.generate_public_accounts() + self.generate_private_accounts() + + def generate_public_accounts(self): + public_amount = int(os.getenv('TERMINAL_ACCOUNTS_AMOUNT', 100)) + now_count = self.accounts.filter(privileged=False, username__startswith='jms').count() + need = public_amount - now_count accounts = [] account_model = self.accounts.model @@ -99,7 +103,30 @@ class AppletHost(Host): org_id=self.LOCKING_ORG, is_active=False, ) accounts.append(account) - bulk_create_with_history(accounts, account_model, batch_size=20) + bulk_create_with_history(accounts, account_model, batch_size=20, ignore_conflicts=True) + + def generate_private_accounts_by_usernames(self, usernames): + accounts = [] + account_model = self.accounts.model + for username in usernames: + password = self.random_password() + username = 'js_' + username + account = account_model( + username=username, secret=password, name=username, + asset_id=self.id, secret_type='password', version=1, + org_id=self.LOCKING_ORG, is_active=False, + ) + accounts.append(account) + bulk_create_with_history(accounts, account_model, batch_size=20, ignore_conflicts=True) + + def generate_private_accounts(self): + from users.models import User + usernames = User.objects \ + .filter(is_active=True, is_service_account=False) \ + .values_list('username', flat=True) + account_usernames = self.accounts.all().values_list('username', flat=True) + not_exist_users = set(usernames) - set(account_usernames) + self.generate_private_accounts_by_usernames(not_exist_users) class AppletHostDeployment(JMSBaseModel): diff --git a/apps/terminal/signal_handlers/applet.py b/apps/terminal/signal_handlers/applet.py index 18595fc7c..4fe390b8e 100644 --- a/apps/terminal/signal_handlers/applet.py +++ b/apps/terminal/signal_handlers/applet.py @@ -2,11 +2,14 @@ from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.utils.functional import LazyObject +from accounts.models import Account from common.signals import django_ready from common.utils import get_logger from common.utils.connection import RedisPubSub from orgs.utils import tmp_to_builtin_org +from users.models import User from ..models import Applet, AppletHost +from ..tasks import applet_host_generate_accounts from ..utils import DBPortManager db_port_manager: DBPortManager @@ -19,12 +22,30 @@ def on_applet_host_create(sender, instance, created=False, **kwargs): return applets = Applet.objects.all() instance.applets.set(applets) - with tmp_to_builtin_org(system=1): - instance.generate_accounts() + applet_host_generate_accounts.delay(instance.id) applet_host_change_pub_sub.publish(True) +@receiver(post_save, sender=User) +def on_user_create_create_account(sender, instance, created=False, **kwargs): + if not created: + return + + with tmp_to_builtin_org(system=1): + applet_hosts = AppletHost.objects.all() + for host in applet_hosts: + host.generate_private_accounts_by_usernames([instance.username]) + + +@receiver(post_delete, sender=User) +def on_user_delete_remove_account(sender, instance, **kwargs): + with tmp_to_builtin_org(system=1): + applet_hosts = AppletHost.objects.all().values_list('id', flat=True) + accounts = Account.objects.filter(asset_id__in=applet_hosts, username=instance.username) + accounts.delete() + + @receiver(post_delete, sender=AppletHost) def on_applet_host_delete(sender, instance, **kwargs): applet_host_change_pub_sub.publish(True) diff --git a/apps/terminal/tasks.py b/apps/terminal/tasks.py index 28356972f..66e6871d7 100644 --- a/apps/terminal/tasks.py +++ b/apps/terminal/tasks.py @@ -16,7 +16,7 @@ from ops.celery.decorator import ( from orgs.utils import tmp_to_builtin_org from .backends import server_replay_storage from .models import ( - Status, Session, Task, AppletHostDeployment + Status, Session, Task, AppletHostDeployment, AppletHost ) from .utils import find_session_replay_local @@ -82,7 +82,7 @@ def upload_session_replay_to_external_storage(session_id): @shared_task( verbose_name=_('Run applet host deployment'), - activity_callback=lambda self, did, *args, **kwargs: ([did], ) + activity_callback=lambda self, did, *args, **kwargs: ([did],) ) def run_applet_host_deployment(did): with tmp_to_builtin_org(system=1): @@ -98,3 +98,16 @@ def run_applet_host_deployment_install_applet(did, applet_id): with tmp_to_builtin_org(system=1): deployment = AppletHostDeployment.objects.get(id=did) deployment.install_applet(applet_id) + + +@shared_task( + verbose_name=_('Generate applet host accounts'), + activity_callback=lambda self, host_id, *args, **kwargs: ([host_id],) +) +def applet_host_generate_accounts(host_id): + applet_host = AppletHost.objects.filter(id=host_id).first() + if not applet_host: + return + + with tmp_to_builtin_org(system=1): + applet_host.generate_accounts() From 4000986d1d85b53a539c5d3b4f88ee476ba8d2a8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 9 May 2023 19:53:29 +0800 Subject: [PATCH 46/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E8=B4=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 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 8bf6b3a85..be8f1e2cc 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -161,7 +161,9 @@ class Applet(JMSBaseModel): return None prefer_host_account_key = 'applet_host_prefer_account_{}_{}'.format(user.id, host.id) prefer_account_id = cache.get(prefer_host_account_key, None) - prefer_account = accounts.filter(id=prefer_account_id).first() + prefer_account = None + if prefer_account_id: + prefer_account = accounts.filter(id=prefer_account_id).first() if prefer_account: account = prefer_account else: From 240f700b92583c1993648bdcc5e52205d73083f6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 10 May 2023 10:47:50 +0800 Subject: [PATCH 47/88] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/api/applet/host.py | 9 +++++++++ apps/terminal/models/applet/host.py | 1 + 2 files changed, 10 insertions(+) diff --git a/apps/terminal/api/applet/host.py b/apps/terminal/api/applet/host.py index 89bc1f96c..cc40936e5 100644 --- a/apps/terminal/api/applet/host.py +++ b/apps/terminal/api/applet/host.py @@ -19,6 +19,9 @@ class AppletHostViewSet(JMSBulkModelViewSet): serializer_class = AppletHostSerializer queryset = AppletHost.objects.all() search_fields = ['asset_ptr__name', 'asset_ptr__address', ] + rbac_perms = { + 'generate_accounts': 'terminal.change_applethost', + } def dispatch(self, request, *args, **kwargs): with tmp_to_builtin_org(system=1): @@ -37,6 +40,12 @@ class AppletHostViewSet(JMSBulkModelViewSet): instance.check_terminal_binding(request) return Response({'msg': 'ok'}) + @action(methods=['put'], detail=True, url_path='generate-accounts') + def generate_accounts(self, request, *args, **kwargs): + instance = self.get_object() + instance.generate_accounts() + return Response({'msg': 'ok'}) + class AppletHostDeploymentViewSet(viewsets.ModelViewSet): serializer_class = AppletHostDeploymentSerializer diff --git a/apps/terminal/models/applet/host.py b/apps/terminal/models/applet/host.py index 1ad129baa..b0e7c478c 100644 --- a/apps/terminal/models/applet/host.py +++ b/apps/terminal/models/applet/host.py @@ -125,6 +125,7 @@ class AppletHost(Host): .filter(is_active=True, is_service_account=False) \ .values_list('username', flat=True) account_usernames = self.accounts.all().values_list('username', flat=True) + account_usernames = [username[3:] for username in account_usernames if username.startswith('js_')] not_exist_users = set(usernames) - set(account_usernames) self.generate_private_accounts_by_usernames(not_exist_users) From 2aa03d5b79e651e17d6ddab2cc2656b1bf23b66f Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 9 May 2023 13:43:46 +0800 Subject: [PATCH 48/88] =?UTF-8?q?perf:=20connect=20token=20=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E5=A4=8D=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/connection_token.py | 34 ++++++++++------ .../0019_connectiontoken_is_reusable.py | 18 +++++++++ .../authentication/models/connection_token.py | 6 +-- .../serializers/connection_token.py | 39 +++++++++++++++++-- apps/common/utils/http.py | 4 ++ apps/jumpserver/conf.py | 4 +- apps/jumpserver/settings/custom.py | 3 ++ apps/settings/serializers/public.py | 1 + 8 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 apps/authentication/migrations/0019_connectiontoken_is_reusable.py diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index a7fed5e7a..03fe9cc8e 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -12,14 +12,12 @@ from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from rest_framework.request import Request from rest_framework.response import Response -from rest_framework.serializers import ValidationError -from assets.const import CloudTypes from common.api import JMSModelViewSet from common.exceptions import JMSException from common.utils import random_string, get_logger from common.utils.django import get_request_os -from common.utils.http import is_true +from common.utils.http import is_true, is_false from orgs.mixins.api import RootOrgViewMixin from perms.models import ActionChoices from terminal.connect_methods import NativeClient, ConnectMethodUtil @@ -27,7 +25,7 @@ from terminal.models import EndpointRule from ..models import ConnectionToken, date_expired_default from ..serializers import ( ConnectionTokenSerializer, ConnectionTokenSecretSerializer, - SuperConnectionTokenSerializer, ConnectTokenAppletOptionSerializer + SuperConnectionTokenSerializer, ConnectTokenAppletOptionSerializer, ConnectionTokenUpdateSerializer ) __all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet'] @@ -230,10 +228,14 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView search_fields = filterset_fields serializer_classes = { 'default': ConnectionTokenSerializer, + 'update': ConnectionTokenUpdateSerializer, + 'partial_update': ConnectionTokenUpdateSerializer, } + http_method_names = ['get', 'post', 'patch', 'head', 'options', 'trace'] rbac_perms = { 'list': 'authentication.view_connectiontoken', 'retrieve': 'authentication.view_connectiontoken', + 'update': 'authentication.change_connectiontoken', 'create': 'authentication.add_connectiontoken', 'exchange': 'authentication.add_connectiontoken', 'expire': 'authentication.change_connectiontoken', @@ -370,19 +372,27 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet): token_id = request.data.get('id') or '' token = get_object_or_404(ConnectionToken, pk=token_id) - if token.is_expired: - raise ValidationError({'id': 'Token is expired'}) - token.is_valid() serializer = self.get_serializer(instance=token) - expire_now = request.data.get('expire_now', True) - # TODO 暂时特殊处理 k8s 不过期 - if token.asset.type == CloudTypes.K8S: - expire_now = False + expire_now = request.data.get('expire_now', None) + asset_type = token.asset.type + asset_category = token.asset.category + # 设置默认值 + if expire_now is None: + # TODO 暂时特殊处理 k8s 不过期 + if asset_type in ['k8s', 'kubernetes']: + expire_now = False + elif asset_category in ['database', 'db']: + expire_now = False + else: + expire_now = True - if expire_now: + if is_false(expire_now) or token.is_reusable: + logger.debug('Token is reusable or specify, not expire') + else: token.expire() + return Response(serializer.data, status=status.HTTP_200_OK) @action(methods=['POST'], detail=False, url_path='applet-option') diff --git a/apps/authentication/migrations/0019_connectiontoken_is_reusable.py b/apps/authentication/migrations/0019_connectiontoken_is_reusable.py new file mode 100644 index 000000000..92ccc892a --- /dev/null +++ b/apps/authentication/migrations/0019_connectiontoken_is_reusable.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.17 on 2023-05-08 07:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0018_alter_connectiontoken_input_secret'), + ] + + operations = [ + migrations.AddField( + model_name='connectiontoken', + name='is_reusable', + field=models.BooleanField(default=False, verbose_name='Reusable'), + ), + ] diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index eef633d95..112fe9331 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -40,6 +40,7 @@ class ConnectionToken(JMSOrgBaseModel): connect_method = models.CharField(max_length=32, verbose_name=_("Connect method")) user_display = models.CharField(max_length=128, default='', verbose_name=_("User display")) asset_display = models.CharField(max_length=128, default='', verbose_name=_("Asset display")) + is_reusable = models.BooleanField(default=False, verbose_name=_("Reusable")) date_expired = models.DateTimeField(default=date_expired_default, verbose_name=_("Date expired")) from_ticket = models.OneToOneField( 'tickets.ApplyLoginAssetTicket', related_name='connection_token', @@ -74,7 +75,7 @@ class ConnectionToken(JMSOrgBaseModel): def expire(self): self.date_expired = timezone.now() - self.save() + self.save(update_fields=['date_expired']) def renewal(self): """ 续期 Token,将来支持用户自定义创建 token 后,续期策略要修改 """ @@ -108,9 +109,8 @@ class ConnectionToken(JMSOrgBaseModel): error = _('No user or invalid user') raise PermissionDenied(error) if not self.asset or not self.asset.is_active: - is_valid = False error = _('No asset or inactive asset') - return is_valid, error + raise PermissionDenied(error) if not self.account: error = _('No account') raise PermissionDenied(error) diff --git a/apps/authentication/serializers/connection_token.py b/apps/authentication/serializers/connection_token.py index 22545115f..7ce1b4291 100644 --- a/apps/authentication/serializers/connection_token.py +++ b/apps/authentication/serializers/connection_token.py @@ -1,13 +1,17 @@ +from django.conf import settings +from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers -from perms.serializers.permission import ActionChoicesField -from orgs.mixins.serializers import OrgResourceModelSerializerMixin from common.serializers.fields import EncryptedField +from common.utils import lazyproperty +from orgs.mixins.serializers import OrgResourceModelSerializerMixin +from perms.serializers.permission import ActionChoicesField from ..models import ConnectionToken __all__ = [ 'ConnectionTokenSerializer', 'SuperConnectionTokenSerializer', + 'ConnectionTokenUpdateSerializer', ] @@ -25,13 +29,13 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin): fields_small = fields_mini + [ 'user', 'asset', 'account', 'input_username', 'input_secret', 'connect_method', 'protocol', 'actions', - 'is_active', 'from_ticket', 'from_ticket_info', + 'is_active', 'is_reusable', 'from_ticket', 'from_ticket_info', 'date_expired', 'date_created', 'date_updated', 'created_by', 'updated_by', 'org_id', 'org_name', ] read_only_fields = [ # 普通 Token 不支持指定 user - 'user', 'expire_time', 'is_expired', + 'user', 'expire_time', 'is_expired', 'date_expired', 'user_display', 'asset_display', ] fields = fields_small + read_only_fields @@ -57,6 +61,33 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin): return info +class ConnectionTokenUpdateSerializer(ConnectionTokenSerializer): + class Meta(ConnectionTokenSerializer.Meta): + can_update_fields = ['is_reusable'] + read_only_fields = list(set(ConnectionTokenSerializer.Meta.fields) - set(can_update_fields)) + + @lazyproperty + def date_expired_max(self): + delta = self.instance.date_expired - self.instance.date_created + if delta.total_seconds() > 3600 * 24: + return self.instance.date_expired + + seconds = settings.CONNECTION_TOKEN_EXPIRATION_MAX + return timezone.now() + timezone.timedelta(seconds=seconds) + + @staticmethod + def validate_is_reusable(value): + if value and not settings.CONNECTION_TOKEN_REUSABLE: + raise serializers.ValidationError(_('Reusable connection token is not allowed, global setting not enabled')) + return value + + def validate(self, attrs): + reusable = attrs.get('is_reusable', False) + if reusable: + attrs['date_expired'] = self.date_expired_max + return attrs + + class SuperConnectionTokenSerializer(ConnectionTokenSerializer): class Meta(ConnectionTokenSerializer.Meta): read_only_fields = list(set(ConnectionTokenSerializer.Meta.read_only_fields) - {'user'}) diff --git a/apps/common/utils/http.py b/apps/common/utils/http.py index b684f004a..baf741407 100644 --- a/apps/common/utils/http.py +++ b/apps/common/utils/http.py @@ -45,3 +45,7 @@ def get_remote_addr(request): def is_true(value): return value in BooleanField.TRUE_VALUES + + +def is_false(value): + return value in BooleanField.FALSE_VALUES diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 343224675..18872058a 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -229,7 +229,9 @@ class Config(dict): 'SESSION_COOKIE_AGE': 3600 * 24, 'SESSION_EXPIRE_AT_BROWSER_CLOSE': False, 'LOGIN_URL': reverse_lazy('authentication:login'), - 'CONNECTION_TOKEN_EXPIRATION': 5 * 60, + 'CONNECTION_TOKEN_EXPIRATION': 5 * 60, # 默认 + 'CONNECTION_TOKEN_EXPIRATION_MAX': 60 * 60 * 24 * 30, # 最大 + 'CONNECTION_TOKEN_REUSABLE': False, # Custom Config 'AUTH_CUSTOM': False, diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 62ba143ac..7312bd7eb 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -131,6 +131,9 @@ TICKETS_ENABLED = CONFIG.TICKETS_ENABLED REFERER_CHECK_ENABLED = CONFIG.REFERER_CHECK_ENABLED CONNECTION_TOKEN_ENABLED = CONFIG.CONNECTION_TOKEN_ENABLED +CONNECTION_TOKEN_REUSABLE = CONFIG.CONNECTION_TOKEN_REUSABLE +CONNECTION_TOKEN_EXPIRATION_MAX = CONFIG.CONNECTION_TOKEN_EXPIRATION_MAX + FORGOT_PASSWORD_URL = CONFIG.FORGOT_PASSWORD_URL # 自定义默认组织名 diff --git a/apps/settings/serializers/public.py b/apps/settings/serializers/public.py index e9861eb72..c195b5e4e 100644 --- a/apps/settings/serializers/public.py +++ b/apps/settings/serializers/public.py @@ -48,3 +48,4 @@ class PrivateSettingSerializer(PublicSettingSerializer): ANNOUNCEMENT = serializers.DictField() TICKETS_ENABLED = serializers.BooleanField() + CONNECTION_TOKEN_REUSABLE = serializers.BooleanField() From 737032418a0093608df67be6eda9fb9747010b0b Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 9 May 2023 14:21:14 +0800 Subject: [PATCH 49/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=86=99?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/connection_token.py | 3 ++- apps/authentication/serializers/connection_token.py | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 03fe9cc8e..4bac0f17a 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -25,7 +25,8 @@ from terminal.models import EndpointRule from ..models import ConnectionToken, date_expired_default from ..serializers import ( ConnectionTokenSerializer, ConnectionTokenSecretSerializer, - SuperConnectionTokenSerializer, ConnectTokenAppletOptionSerializer, ConnectionTokenUpdateSerializer + SuperConnectionTokenSerializer, ConnectTokenAppletOptionSerializer, + ConnectionTokenUpdateSerializer ) __all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet'] diff --git a/apps/authentication/serializers/connection_token.py b/apps/authentication/serializers/connection_token.py index 7ce1b4291..7d3de1bb7 100644 --- a/apps/authentication/serializers/connection_token.py +++ b/apps/authentication/serializers/connection_token.py @@ -4,7 +4,6 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from common.serializers.fields import EncryptedField -from common.utils import lazyproperty from orgs.mixins.serializers import OrgResourceModelSerializerMixin from perms.serializers.permission import ActionChoicesField from ..models import ConnectionToken @@ -66,8 +65,7 @@ class ConnectionTokenUpdateSerializer(ConnectionTokenSerializer): can_update_fields = ['is_reusable'] read_only_fields = list(set(ConnectionTokenSerializer.Meta.fields) - set(can_update_fields)) - @lazyproperty - def date_expired_max(self): + def _get_date_expired(self): delta = self.instance.date_expired - self.instance.date_created if delta.total_seconds() > 3600 * 24: return self.instance.date_expired @@ -84,7 +82,7 @@ class ConnectionTokenUpdateSerializer(ConnectionTokenSerializer): def validate(self, attrs): reusable = attrs.get('is_reusable', False) if reusable: - attrs['date_expired'] = self.date_expired_max + attrs['date_expired'] = self._get_date_expired() return attrs From 9802aec881ba8952e00faef9d65fa53b70317b6a Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Mon, 8 May 2023 16:59:47 +0800 Subject: [PATCH 50/88] =?UTF-8?q?perf:=20=E8=87=AA=E5=8A=A8=E5=8C=96?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E8=AF=A6=E6=83=85=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/asset/asset.py | 12 ++++++++++-- apps/assets/serializers/automations/base.py | 11 +++++++---- apps/assets/urls/api_urls.py | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index ba048e236..96d55cae9 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -12,10 +12,11 @@ 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, AutomationExecution from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual from common.api import SuggestionMixin from common.drf.filters import BaseFilterSet +from common.permissions import IsValidUser from common.utils import get_logger, is_uuid from orgs.mixins import generics from orgs.mixins.api import OrgBulkModelViewSet @@ -25,7 +26,8 @@ from ...notifications import BulkUpdatePlatformSkipAssetUserMsg logger = get_logger(__file__) __all__ = [ "AssetViewSet", "AssetTaskCreateApi", - "AssetsTaskCreateApi", 'AssetFilterSet' + "AssetsTaskCreateApi", 'AssetFilterSet', + "AutomationExecutionRetrieveApi", ] @@ -258,3 +260,9 @@ class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): has = self.request.user.has_perm(perm_required) if not has: self.permission_denied(request) + + +class AutomationExecutionRetrieveApi(generics.RetrieveAPIView): + permission_classes = (IsValidUser,) + model = AutomationExecution + serializer_class = serializers.AutomationExecutionSerializer diff --git a/apps/assets/serializers/automations/base.py b/apps/assets/serializers/automations/base.py index 804bcabc9..527f71628 100644 --- a/apps/assets/serializers/automations/base.py +++ b/apps/assets/serializers/automations/base.py @@ -1,12 +1,12 @@ from django.utils.translation import ugettext as _ from rest_framework import serializers -from ops.mixin import PeriodTaskSerializerMixin from assets.models import Asset, Node, BaseAutomation, AutomationExecution -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from common.utils import get_logger from common.const.choices import Trigger from common.serializers.fields import ObjectRelatedField, LabeledChoiceField +from common.utils import get_logger +from ops.mixin import PeriodTaskSerializerMixin +from orgs.mixins.serializers import BulkOrgResourceModelSerializer logger = get_logger(__file__) @@ -48,9 +48,12 @@ class AutomationExecutionSerializer(serializers.ModelSerializer): @staticmethod def get_snapshot(obj): + from assets.const import AutomationTypes as AssetTypes + from accounts.const import AutomationTypes as AccountTypes + tp_dict = dict(AssetTypes.choices) | dict(AccountTypes.choices) tp = obj.snapshot['type'] snapshot = { - 'type': tp, + 'type': {'value': tp, 'label': tp_dict.get(tp, tp)}, 'name': obj.snapshot['name'], 'comment': obj.snapshot['comment'], 'accounts': obj.snapshot['accounts'], diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index 6b5f469d0..38924f333 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -47,6 +47,7 @@ urlpatterns = [ path('gateways//test-connective/', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'), path('platform-automation-methods/', api.PlatformAutomationMethodsApi.as_view(), name='platform-automation-methods'), + path('automations-execution//', api.AutomationExecutionRetrieveApi.as_view(), name='automation-execution'), ] urlpatterns += router.urls From 90cc2a2519da1433984602dc270c786e260ef5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chuailei000=E2=80=9D?= <2280131253@qq.com> Date: Tue, 9 May 2023 15:48:12 +0800 Subject: [PATCH 51/88] =?UTF-8?q?perf:=20=E5=B1=95=E7=A4=BA=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E8=AF=A6=E6=83=85=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/templates/ops/celery_task_log.html | 56 +++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/apps/ops/templates/ops/celery_task_log.html b/apps/ops/templates/ops/celery_task_log.html index 2305218a9..8c736a510 100644 --- a/apps/ops/templates/ops/celery_task_log.html +++ b/apps/ops/templates/ops/celery_task_log.html @@ -7,9 +7,11 @@ + +
    +
  • + ID: + +
  • +
  • + {% trans 'Task type' %}: + +
  • +
  • + {% trans 'Trigger type' %}: + +
  • +
  • + {% trans 'Date start' %}: + +
  • +
@@ -85,7 +124,24 @@ term.write("Connect websocket server error") } } + getAutomationExecutionInfo(); }).on('resize', window, function () { window.fit.fit(term); }); + function getAutomationExecutionInfo() { + var url = "{% url 'api-assets:automation-execution' pk=task_id %}"; + + requestApi({ + url: url, + method: "GET", + flash_message: false, + success(data) { + const dateStart = new Date(data.date_start).toLocaleString(); + $('.task-id').html(data.id); + $('.task-type').html(data.snapshot.type.label); + $('.trigger-type').html(data.trigger.label); + $('.date-start').html(dateStart); + } + }) + } From 24fd87f7bcc4ffe496263ebaa7c0cdfff24969e5 Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 10 May 2023 15:34:36 +0800 Subject: [PATCH 52/88] =?UTF-8?q?perf:=20=E5=B9=B3=E5=8F=B0=E5=8D=8F?= =?UTF-8?q?=E8=AE=AEAPI=E8=BF=94=E5=9B=9Epublic=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/platform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index f16b62097..7e5b17a01 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -82,7 +82,7 @@ class PlatformProtocolSerializer(serializers.ModelSerializer): model = PlatformProtocol fields = [ "id", "name", "port", "primary", - "required", "default", + "required", "default", "public", "secret_types", "setting", ] From ebb0e796cecc2cb977fe19ad37a89b42c7484d6a Mon Sep 17 00:00:00 2001 From: Aaron3S Date: Wed, 10 May 2023 15:31:43 +0800 Subject: [PATCH 53/88] =?UTF-8?q?feat:=20=E4=BD=9C=E4=B8=9A=E4=B8=AD?= =?UTF-8?q?=E5=BF=83=E6=A0=B9=E6=8D=AE=E5=BD=93=E5=89=8D=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E7=9A=84=E8=B5=84=E4=BA=A7=E6=8F=90=E7=A4=BA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/api/job.py | 38 ++++++++++++++++++++++++++++++++----- apps/ops/serializers/job.py | 16 ---------------- apps/ops/urls/api_urls.py | 2 +- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index 308998f09..bb427999b 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -5,6 +5,7 @@ from django.shortcuts import get_object_or_404 from rest_framework.response import Response from rest_framework.views import APIView +from assets.models import Asset from common.permissions import IsValidUser from ops.const import Types from ops.models import Job, JobExecution @@ -12,7 +13,7 @@ from ops.serializers.job import JobSerializer, JobExecutionSerializer __all__ = [ 'JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', - 'JobAssetDetail', 'JobExecutionTaskDetail', 'FrequentUsernames' + 'JobAssetDetail', 'JobExecutionTaskDetail', 'UsernameHintsAPI' ] from ops.tasks import run_ops_job_execution @@ -20,6 +21,8 @@ from ops.variables import JMS_JOB_VARIABLE_HELP from orgs.mixins.api import OrgBulkModelViewSet from orgs.utils import tmp_to_org, get_current_org from accounts.models import Account +from perms.models import PermNode +from perms.utils import UserPermAssetUtil def set_task_to_serializer_data(serializer, task): @@ -28,6 +31,20 @@ def set_task_to_serializer_data(serializer, task): setattr(serializer, "_data", data) +def merge_nodes_and_assets(nodes, assets, user): + if nodes: + perm_util = UserPermAssetUtil(user=user) + for node_id in nodes: + if node_id == PermNode.FAVORITE_NODE_KEY: + node_assets = perm_util.get_favorite_assets() + elif node_id == PermNode.UNGROUPED_NODE_KEY: + node_assets = perm_util.get_ungroup_assets() + else: + node, node_assets = perm_util.get_node_all_assets(node_id) + assets.extend(node_assets.exclude(id__in=[asset.id for asset in assets])) + return assets + + class JobViewSet(OrgBulkModelViewSet): serializer_class = JobSerializer search_fields = ('name', 'comment') @@ -50,6 +67,10 @@ class JobViewSet(OrgBulkModelViewSet): def perform_create(self, serializer): run_after_save = serializer.validated_data.pop('run_after_save', False) + node_ids = serializer.validated_data.pop('nodes', []) + assets = serializer.validated_data.__getitem__('assets') + assets = merge_nodes_and_assets(node_ids, assets, self.request.user) + serializer.validated_data.__setitem__('assets', assets) instance = serializer.save() if instance.instant or run_after_save: self.run_job(instance, serializer) @@ -105,7 +126,7 @@ class JobAssetDetail(APIView): class JobExecutionTaskDetail(APIView): rbac_perms = { - 'get': ['ops.view_jobexecution'], + 'GET': ['ops.view_jobexecution'], } def get(self, request, **kwargs): @@ -131,13 +152,20 @@ class JobRunVariableHelpAPIView(APIView): return Response(data=JMS_JOB_VARIABLE_HELP) -class FrequentUsernames(APIView): +class UsernameHintsAPI(APIView): permission_classes = [IsValidUser] - def get(self, request, **kwargs): + def post(self, request, **kwargs): + node_ids = request.data.get('nodes', None) + asset_ids = request.data.get('assets', []) + assets = list(Asset.objects.filter(id__in=asset_ids).all()) + + assets = merge_nodes_and_assets(node_ids, assets, request.user) + top_accounts = Account.objects.exclude(username='root') \ .exclude(username__startswith='jms_') \ + .filter(asset__in=assets) \ .values('username') \ .annotate(total=Count('username')) \ - .order_by('total')[:5] + .order_by('total')[:10] return Response(data=top_accounts) diff --git a/apps/ops/serializers/job.py b/apps/ops/serializers/job.py index 8b157b66f..011bea4e2 100644 --- a/apps/ops/serializers/job.py +++ b/apps/ops/serializers/job.py @@ -33,22 +33,6 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): user = request.user if request else None return user - def create(self, validated_data): - assets = validated_data.__getitem__('assets') - node_ids = validated_data.pop('nodes', None) - if node_ids: - user = self.get_request_user() - perm_util = UserPermAssetUtil(user=user) - for node_id in node_ids: - if node_id == PermNode.FAVORITE_NODE_KEY: - node_assets = perm_util.get_favorite_assets() - elif node_id == PermNode.UNGROUPED_NODE_KEY: - node_assets = perm_util.get_ungroup_assets() - else: - node, node_assets = perm_util.get_node_all_assets(node_id) - assets.extend(node_assets.exclude(id__in=[asset.id for asset in assets])) - return super().create(validated_data) - class Meta: model = Job read_only_fields = [ diff --git a/apps/ops/urls/api_urls.py b/apps/ops/urls/api_urls.py index 051ca7894..905f0ed0a 100644 --- a/apps/ops/urls/api_urls.py +++ b/apps/ops/urls/api_urls.py @@ -27,7 +27,7 @@ urlpatterns = [ path('variables/help/', api.JobRunVariableHelpAPIView.as_view(), name='variable-help'), path('job-execution/asset-detail/', api.JobAssetDetail.as_view(), name='asset-detail'), path('job-execution/task-detail//', api.JobExecutionTaskDetail.as_view(), name='task-detail'), - path('frequent-username/', api.FrequentUsernames.as_view(), name='frequent-usernames'), + path('username-hints/', api.UsernameHintsAPI.as_view(), name='username-hints'), path('ansible/job-execution//log/', api.AnsibleTaskLogApi.as_view(), name='job-execution-log'), path('celery/task//task-execution//log/', api.CeleryTaskExecutionLogApi.as_view(), From 42abad75d9791fd7190d9ea9f1f4c6f14ec4062d Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 10 May 2023 16:46:10 +0800 Subject: [PATCH 54/88] =?UTF-8?q?perf:=20=E5=B9=B3=E5=8F=B0=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E6=94=AF=E6=8C=81=E6=9B=B4=E6=94=B9=20public=20?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/platform.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/apps/assets/models/platform.py b/apps/assets/models/platform.py index 030e54de3..f50177642 100644 --- a/apps/assets/models/platform.py +++ b/apps/assets/models/platform.py @@ -26,14 +26,6 @@ class PlatformProtocol(models.Model): def secret_types(self): return Protocol.settings().get(self.name, {}).get('secret_types', ['password']) - def set_public(self): - private_protocol_set = ('winrm',) - self.public = self.name not in private_protocol_set - - def save(self, **kwargs): - self.set_public() - return super().save(**kwargs) - class PlatformAutomation(models.Model): ansible_enabled = models.BooleanField(default=False, verbose_name=_("Enabled")) From b98aa377b6c4b3970607809d02cbbe1b6cd01791 Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 10 May 2023 16:21:32 +0800 Subject: [PATCH 55/88] =?UTF-8?q?perf:=20=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=20=E9=BB=98=E8=AE=A4=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=20=E6=98=AF=E5=BF=85=E5=A1=AB=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/asset/database.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/assets/serializers/asset/database.py b/apps/assets/serializers/asset/database.py index da6c9574a..386cb87e9 100644 --- a/apps/assets/serializers/asset/database.py +++ b/apps/assets/serializers/asset/database.py @@ -1,5 +1,6 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework.serializers import ValidationError +from rest_framework import serializers from assets.models import Database from assets.serializers.gateway import GatewayWithAccountSecretSerializer @@ -9,6 +10,8 @@ __all__ = ['DatabaseSerializer', 'DatabaseWithGatewaySerializer'] class DatabaseSerializer(AssetSerializer): + db_name = serializers.CharField(max_length=1024, label=_('Default database'), required=True) + class Meta(AssetSerializer.Meta): model = Database extra_fields = [ From f06059837d36d3ba6a7246152cf7007964bcb84e Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 11 May 2023 10:15:40 +0800 Subject: [PATCH 56/88] =?UTF-8?q?perf:=20=E6=8E=88=E6=9D=83=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E6=A0=91=20(#10390)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/assets/api/mixin.py | 8 +- apps/assets/const/types.py | 52 ++++++++++--- .../user_permission/tree/node_with_asset.py | 75 ++++++++++++++++++- apps/perms/urls/user_permission.py | 3 + apps/perms/utils/user_perm.py | 6 +- 5 files changed, 125 insertions(+), 19 deletions(-) diff --git a/apps/assets/api/mixin.py b/apps/assets/api/mixin.py index 13ae0f283..39d51197b 100644 --- a/apps/assets/api/mixin.py +++ b/apps/assets/api/mixin.py @@ -69,7 +69,7 @@ class SerializeToTreeNodeMixin: return 'file' @timeit - def serialize_assets(self, assets, node_key=None): + def serialize_assets(self, assets, node_key=None, pid=None): sftp_enabled_platform = PlatformProtocol.objects \ .filter(name='ssh', setting__sftp_enabled=True) \ .values_list('platform', flat=True) \ @@ -83,8 +83,10 @@ class SerializeToTreeNodeMixin: { 'id': str(asset.id), 'name': asset.name, - 'title': f'{asset.address}\n{asset.comment}', - 'pId': get_pid(asset), + 'title': + f'{asset.address}\n{asset.comment}' + if asset.comment else asset.address, + 'pId': pid or get_pid(asset), 'isParent': False, 'open': False, 'iconSkin': self.get_icon(asset), diff --git a/apps/assets/const/types.py b/apps/assets/const/types.py index 8194c5c14..69fa36bb3 100644 --- a/apps/assets/const/types.py +++ b/apps/assets/const/types.py @@ -193,15 +193,38 @@ class AllTypes(ChoicesMixin): } return node - @classmethod - def to_tree_nodes(cls, include_asset, count_resource='asset'): - from accounts.models import Account - from ..models import Asset, Platform - if count_resource == 'account': - resource_platforms = Account.objects.all().values_list('asset__platform_id', flat=True) - else: - resource_platforms = Asset.objects.all().values_list('platform_id', flat=True) + @classmethod + def asset_to_node(cls, asset, pid): + node = { + 'id': '{}'.format(asset.id), + 'name': asset.name, + 'title': f'{asset.address}\n{asset.comment}', + 'pId': pid, + 'isParent': False, + 'open': False, + 'iconSkin': asset.type, + 'chkDisabled': not asset.is_active, + 'meta': { + 'type': 'platform', + 'data': { + 'platform_type': asset.platform.type, + 'org_name': asset.org_name, + # 'sftp': asset.platform_id in sftp_enabled_platform, + 'name': asset.name, + 'address': asset.address + }, + } + } + return node + + @classmethod + def get_root_nodes(cls): + return dict(id='ROOT', name=_('All types'), title=_('All types'), open=True, isParent=True) + + @classmethod + def get_tree_nodes(cls, resource_platforms, include_asset=False): + from ..models import Platform platform_count = defaultdict(int) for platform_id in resource_platforms: platform_count[platform_id] += 1 @@ -215,8 +238,7 @@ class AllTypes(ChoicesMixin): category_type_mapper[p.category] += platform_count[p.id] tp_platforms[p.category + '_' + p.type].append(p) - root = dict(id='ROOT', name=_('All types'), title=_('All types'), open=True, isParent=True) - nodes = [root] + nodes = [cls.get_root_nodes()] for category, type_cls in cls.category_types(): # Category 格式化 meta = {'type': 'category', 'category': category.value} @@ -244,6 +266,16 @@ class AllTypes(ChoicesMixin): nodes.append(platform_node) return nodes + @classmethod + def to_tree_nodes(cls, include_asset, count_resource='asset'): + from accounts.models import Account + from ..models import Asset + if count_resource == 'account': + resource_platforms = Account.objects.all().values_list('asset__platform_id', flat=True) + else: + resource_platforms = Asset.objects.all().values_list('platform_id', flat=True) + return cls.get_tree_nodes(resource_platforms, include_asset) + @classmethod def get_type_default_platform(cls, category, tp): constraints = cls.get_constraints(category, tp) diff --git a/apps/perms/api/user_permission/tree/node_with_asset.py b/apps/perms/api/user_permission/tree/node_with_asset.py index 021933d40..d1da28fbb 100644 --- a/apps/perms/api/user_permission/tree/node_with_asset.py +++ b/apps/perms/api/user_permission/tree/node_with_asset.py @@ -1,4 +1,6 @@ import abc +import re +from collections import defaultdict from urllib.parse import parse_qsl from django.conf import settings @@ -11,6 +13,7 @@ from rest_framework.response import Response from accounts.const import AliasAccount from assets.api import SerializeToTreeNodeMixin +from assets.const import AllTypes from assets.models import Asset from assets.utils import KubernetesTree from authentication.models import ConnectionToken @@ -26,7 +29,8 @@ from ..mixin import SelfOrPKUserMixin __all__ = [ 'UserGrantedK8sAsTreeApi', 'UserPermedNodesWithAssetsAsTreeApi', - 'UserPermedNodeChildrenWithAssetsAsTreeApi' + 'UserPermedNodeChildrenWithAssetsAsTreeApi', + 'UserPermedNodeChildrenWithAssetsAsCategoryTreeApi', ] @@ -137,6 +141,75 @@ class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi): return self.query_node_key or self.default_unfolded_node_key +class UserPermedNodeChildrenWithAssetsAsCategoryTreeApi( + SelfOrPKUserMixin, SerializeToTreeNodeMixin, ListAPIView +): + @property + def is_sync(self): + sync = self.request.query_params.get('sync', 0) + return int(sync) == 1 + + @property + def tp(self): + return self.request.query_params.get('type') + + def get_assets(self): + query_asset_util = UserPermAssetUtil(self.user) + node = PermNode.objects.filter( + granted_node_rels__user=self.user, parent_key='').first() + if node: + __, assets = query_asset_util.get_node_all_assets(node.id) + else: + assets = Asset.objects.none() + return assets + + def to_tree_nodes(self, assets): + if not assets: + return [] + assets = assets.annotate(tp=F('platform__type')) + asset_type_map = defaultdict(list) + for asset in assets: + asset_type_map[asset.tp].append(asset) + tp = self.tp + if tp: + assets = asset_type_map.get(tp, []) + if not assets: + return [] + pid = f'ROOT_{str(assets[0].category).upper()}_{tp}' + return self.serialize_assets(assets, pid=pid) + + resource_platforms = assets.values_list('platform_id', flat=True) + node_all = AllTypes.get_tree_nodes(resource_platforms) + pattern = re.compile(r'\(0\)?') + nodes = [] + for node in node_all: + meta = node.get('meta', {}) + if pattern.search(node['name']) or meta.get('type') == 'platform': + continue + _type = meta.get('_type') + if _type: + node['type'] = _type + nodes.append(node) + + if not self.is_sync: + return nodes + + asset_nodes = [] + for node in nodes: + node['open'] = True + tp = node.get('meta', {}).get('_type') + if not tp: + continue + assets = asset_type_map.get(tp, []) + asset_nodes += self.serialize_assets(assets, pid=node['id']) + return nodes + asset_nodes + + def list(self, request, *args, **kwargs): + assets = self.get_assets() + nodes = self.to_tree_nodes(assets) + return Response(data=nodes) + + class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView): """ 用户授权的K8s树 """ diff --git a/apps/perms/urls/user_permission.py b/apps/perms/urls/user_permission.py index ba172747c..4f3d4c71d 100644 --- a/apps/perms/urls/user_permission.py +++ b/apps/perms/urls/user_permission.py @@ -37,6 +37,9 @@ user_permission_urlpatterns = [ path('/nodes/children-with-assets/tree/', api.UserPermedNodeChildrenWithAssetsAsTreeApi.as_view(), name='user-node-children-with-assets-as-tree'), + path('/nodes/children-with-assets/category/tree/', + api.UserPermedNodeChildrenWithAssetsAsCategoryTreeApi.as_view(), + name='user-node-children-with-assets-as-category-tree'), # 同步树 path('/nodes/all-with-assets/tree/', api.UserPermedNodesWithAssetsAsTreeApi.as_view(), diff --git a/apps/perms/utils/user_perm.py b/apps/perms/utils/user_perm.py index 12f979a4f..d054e67ce 100644 --- a/apps/perms/utils/user_perm.py +++ b/apps/perms/utils/user_perm.py @@ -1,15 +1,11 @@ -from assets.models import FavoriteAsset, Asset - from django.conf import settings from django.db.models import Q +from assets.models import FavoriteAsset, Asset from common.utils.common import timeit - from perms.models import AssetPermission, PermNode, UserAssetGrantedTreeNodeRelation - from .permission import AssetPermissionUtil - __all__ = ['AssetPermissionPermAssetUtil', 'UserPermAssetUtil', 'UserPermNodeUtil'] From ff4f01fb5681ba83b495976aa00c781aaa1571ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chuailei000=E2=80=9D?= <2280131253@qq.com> Date: Thu, 11 May 2023 17:10:10 +0800 Subject: [PATCH 57/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96task=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E6=95=B0=E6=8D=AE=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/templates/ops/celery_task_log.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/ops/templates/ops/celery_task_log.html b/apps/ops/templates/ops/celery_task_log.html index 8c736a510..b0f16130b 100644 --- a/apps/ops/templates/ops/celery_task_log.html +++ b/apps/ops/templates/ops/celery_task_log.html @@ -7,6 +7,7 @@ + From 7edb024abe5e67e6040a7efcce0ac34529a5974b Mon Sep 17 00:00:00 2001 From: maninhill <41712985+maninhill@users.noreply.github.com> Date: Fri, 12 May 2023 14:35:45 +0800 Subject: [PATCH 58/88] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a4031f96..e7db88061 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,14 @@ -------------------------- -JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。 +JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpServer 堡垒机帮助企业以更安全的方式管控和登录所有类型的资产,包括: + +- **SSH**: Linux / Unix / 网络设备 等 +- **Windows**: Web 方式连接 / 原生 RDP 连接 +- **数据库**: MySQL / Oracle / SQLServer / PostgreSQL 等 +- **Kubernetes**: 支持连接到 K8s 集群中的 Pods +- **Web 站点**: 各类系统的 Web 管理后台 +- **应用**: 通过 Remote App 连接各类应用 ## 产品特色 From 9ecde3024aabfd9ccf1ffa17cde8c4fd20784cb9 Mon Sep 17 00:00:00 2001 From: maninhill <41712985+maninhill@users.noreply.github.com> Date: Fri, 12 May 2023 14:42:07 +0800 Subject: [PATCH 59/88] Update README.md --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e7db88061..71872e4a2 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ -------------------------- -JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpServer 堡垒机帮助企业以更安全的方式管控和登录所有类型的资产,包括: +JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpServer 堡垒机帮助企业以更安全的方式管控和登录各种类型的资产,包括: -- **SSH**: Linux / Unix / 网络设备 等 -- **Windows**: Web 方式连接 / 原生 RDP 连接 -- **数据库**: MySQL / Oracle / SQLServer / PostgreSQL 等 -- **Kubernetes**: 支持连接到 K8s 集群中的 Pods -- **Web 站点**: 各类系统的 Web 管理后台 -- **应用**: 通过 Remote App 连接各类应用 +- **SSH**: Linux / Unix / 网络设备 等; +- **Windows**: Web 方式连接 / 原生 RDP 连接; +- **数据库**: MySQL / Oracle / SQLServer / PostgreSQL 等; +- **Kubernetes**: 支持连接到 K8s 集群中的 Pods; +- **Web 站点**: 各类系统的 Web 管理后台; +- **应用**: 通过 Remote App 连接各类应用。 ## 产品特色 @@ -40,8 +40,6 @@ JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运 - **多云支持**: 一套系统,同时管理不同云上面的资产; - **多租户**: 一套系统,多个子公司或部门同时使用; - **云端存储**: 审计录像云端存储,永不丢失; -- **多应用支持**: 全面支持各类资产,包括服务器、数据库、Windows RemoteApp、Kubernetes 等; -- **安全可靠**: 被广泛使用、验证和信赖,连续 9 年的持续研发投入和产品更新升级。 ## UI 展示 From afb49f40408336c6759ed571857e1b5404ac0c74 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Fri, 12 May 2023 15:37:13 +0800 Subject: [PATCH 60/88] =?UTF-8?q?fix:=20oracle=20Ping=20=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/ansible/inventory.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index 6ecc49698..80922859b 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -5,8 +5,6 @@ from collections import defaultdict from django.utils.translation import gettext as _ -from accounts.const import AutomationTypes - __all__ = ['JMSInventory'] @@ -79,6 +77,7 @@ class JMSInventory: return var def make_account_vars(self, host, asset, account, automation, protocol, platform, gateway): + from accounts.const import AutomationTypes if not account: host['error'] = _("No account available") return host From 6afcf7bf423a05401e876c9f0f7a48b86a041b00 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 12 May 2023 17:22:18 +0800 Subject: [PATCH 61/88] =?UTF-8?q?perf:=20=E7=AC=AC=E4=B8=89=E6=96=B9?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=AE=A4=E8=AF=81=E9=94=99=E8=AF=AF=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=8F=90=E7=A4=BA=EF=BC=88=E5=B0=A4=E5=85=B6=E6=98=AF?= =?UTF-8?q?=E7=AC=AC=E4=B8=89=E6=96=B9=E8=AE=A4=E8=AF=81=E8=B7=B3=E8=BD=AC?= =?UTF-8?q?=E7=9A=84=E6=83=85=E5=86=B5=EF=BC=89=20(#10446)?= 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 | 23 ++++++++++++++--------- apps/authentication/mixins.py | 16 ++++++++++++++++ apps/users/signal_handlers.py | 26 +++++++++++++++++++------- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/apps/authentication/middleware.py b/apps/authentication/middleware.py index d03798f94..43f7bdc14 100644 --- a/apps/authentication/middleware.py +++ b/apps/authentication/middleware.py @@ -1,17 +1,16 @@ import base64 -import time +from django.conf import settings +from django.contrib.auth import logout as auth_logout +from django.http import HttpResponse from django.shortcuts import redirect, reverse, render from django.utils.deprecation import MiddlewareMixin -from django.http import HttpResponse -from django.conf import settings from django.utils.translation import ugettext as _ -from django.contrib.auth import logout as auth_logout from apps.authentication import mixins +from authentication.signals import post_auth_failed from common.utils import gen_key_pair from common.utils import get_request_ip -from .signals import post_auth_failed class MFAMiddleware: @@ -76,12 +75,18 @@ class ThirdPartyLoginMiddleware(mixins.AuthMixin): ip = get_request_ip(request) try: self.request = request + self._check_third_party_login_acl() self._check_login_acl(request.user, ip) except Exception as e: - post_auth_failed.send( - sender=self.__class__, username=request.user.username, - request=self.request, reason=e.msg - ) + if getattr(request, 'user_need_delete', False): + request.user.delete() + else: + error_message = getattr(e, 'msg', None) + error_message = error_message or str(e) + post_auth_failed.send( + sender=self.__class__, username=request.user.username, + request=self.request, reason=error_message + ) auth_logout(request) context = { 'title': _('Authentication failed'), diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 14e5fd35f..e98b44028 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -54,6 +54,7 @@ def authenticate(request=None, **credentials): """ username = credentials.get('username') + temp_user = None for backend, backend_path in _get_backends(return_tuples=True): # 检查用户名是否允许认证 (预先检查,不浪费认证时间) logger.info('Try using auth backend: {}'.format(str(backend))) @@ -77,11 +78,19 @@ def authenticate(request=None, **credentials): # 检查用户是否允许认证 if not backend.user_allow_authenticate(user): + temp_user = user + temp_user.backend = backend_path continue # Annotate the user object with the path of the backend. user.backend = backend_path return user + else: + if temp_user is not None: + source_display = temp_user.source_display + request.error_message = '''The administrator has enabled 'Only allow login from user source'. + The current user source is {}. Please contact the administrator.'''.format(source_display) + return temp_user # The credentials supplied are invalid to all backends, fire signal user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request) @@ -345,6 +354,13 @@ class AuthACLMixin: 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) + if not error_message: + return + raise ValueError(error_message) + def check_user_login_confirm_if_need(self, user): if not self.request.session.get("auth_confirm_required"): return diff --git a/apps/users/signal_handlers.py b/apps/users/signal_handlers.py index 487b3c917..03c86c954 100644 --- a/apps/users/signal_handlers.py +++ b/apps/users/signal_handlers.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # from django.conf import settings -from django.core.exceptions import PermissionDenied from django.db.models.signals import post_save from django.dispatch import receiver +from django.utils.translation import ugettext_lazy as _ from django_auth_ldap.backend import populate_user from django_cas_ng.signals import cas_user_authenticated @@ -12,16 +12,29 @@ from authentication.backends.oidc.signals import openid_create_or_update_user from authentication.backends.saml2.signals import saml2_create_or_update_user from common.decorators import on_transaction_commit from common.utils import get_logger +from jumpserver.utils import get_current_request from .models import User, UserPasswordHistory from .signals import post_user_create logger = get_logger(__file__) -def user_authenticated_handle(user, created, source, attrs=None, **kwargs): +def third_party_login_acl(created): if created and settings.ONLY_ALLOW_EXIST_USER_AUTH: - user.delete() - raise PermissionDenied(f'Not allow non-exist user auth: {user.username}') + request = get_current_request() + request.user_need_delete = True + request.error_message = _( + '''The administrator has enabled "Only allow existing users to log in", + and the current user is not in the user list. Please contact the administrator.''' + ) + return False + return True + + +def user_authenticated_handle(user, created, source, attrs=None, **kwargs): + if not third_party_login_acl(created): + return + if created: user.source = source user.save() @@ -122,9 +135,8 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs): @receiver(openid_create_or_update_user) def on_openid_create_or_update_user(sender, request, user, created, name, username, email, **kwargs): - if created and settings.ONLY_ALLOW_EXIST_USER_AUTH: - user.delete() - raise PermissionDenied(f'Not allow non-exist user auth: {username}') + if not third_party_login_acl(created): + return if created: logger.debug( From 5b4979bdb18e6ba79be0a618ae7c3fbfbf4ba7d4 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 12 May 2023 18:11:10 +0800 Subject: [PATCH 62/88] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E4=BB=85?= =?UTF-8?q?=E5=85=81=E8=AE=B8=E5=B7=B2=E5=AD=98=E5=9C=A8=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=99=BB=E5=BD=95=20=E5=88=A4=E6=96=AD=E7=9A=84=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/signal_handlers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/users/signal_handlers.py b/apps/users/signal_handlers.py index 03c86c954..b566a600a 100644 --- a/apps/users/signal_handlers.py +++ b/apps/users/signal_handlers.py @@ -19,7 +19,7 @@ from .signals import post_user_create logger = get_logger(__file__) -def third_party_login_acl(created): +def check_only_allow_exist_user_auth(created): if created and settings.ONLY_ALLOW_EXIST_USER_AUTH: request = get_current_request() request.user_need_delete = True @@ -32,7 +32,7 @@ def third_party_login_acl(created): def user_authenticated_handle(user, created, source, attrs=None, **kwargs): - if not third_party_login_acl(created): + if not check_only_allow_exist_user_auth(created): return if created: @@ -135,7 +135,7 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs): @receiver(openid_create_or_update_user) def on_openid_create_or_update_user(sender, request, user, created, name, username, email, **kwargs): - if not third_party_login_acl(created): + if not check_only_allow_exist_user_auth(created): return if created: From 98802e21a001c550613a0b27f23482beb1aca33f Mon Sep 17 00:00:00 2001 From: Bai Date: Mon, 15 May 2023 11:23:36 +0800 Subject: [PATCH 63/88] =?UTF-8?q?perf:=20=E5=8D=87=E7=BA=A7=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=20grpcio=3D=3D1.54.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile.loong64 | 2 +- requirements/requirements_xpack.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile.loong64 b/Dockerfile.loong64 index 8fdb38705..2de68d742 100644 --- a/Dockerfile.loong64 +++ b/Dockerfile.loong64 @@ -77,7 +77,7 @@ RUN --mount=type=cache,target=/root/.cache/pip \ && pip install https://download.jumpserver.org/pypi/simple/cryptography/cryptography-38.0.4-cp39-cp39-linux_loongarch64.whl \ && pip install https://download.jumpserver.org/pypi/simple/greenlet/greenlet-1.1.2-cp39-cp39-linux_loongarch64.whl \ && pip install https://download.jumpserver.org/pypi/simple/PyNaCl/PyNaCl-1.5.0-cp39-cp39-linux_loongarch64.whl \ - && pip install https://download.jumpserver.org/pypi/simple/grpcio/grpcio-1.54.0-cp39-cp39-linux_loongarch64.whl \ + && pip install https://download.jumpserver.org/pypi/simple/grpcio/grpcio-1.54.2-cp39-cp39-linux_loongarch64.whl \ && pip install $(grep -E 'jms|jumpserver' requirements/requirements.txt) -i ${PIP_JMS_MIRROR} \ && pip install -r requirements/requirements.txt diff --git a/requirements/requirements_xpack.txt b/requirements/requirements_xpack.txt index 4f0f0ea7d..5ff7986e0 100644 --- a/requirements/requirements_xpack.txt +++ b/requirements/requirements_xpack.txt @@ -5,6 +5,7 @@ azure-identity==1.5.0 azure-mgmt-compute==4.6.2 azure-mgmt-network==2.7.0 google-cloud-compute==0.5.0 +grpcio=1.54.2 alibabacloud_dysmsapi20170525==2.0.2 python-novaclient==11.0.1 python-keystoneclient==4.3.0 From 89b75835a64742b2863e3f42694a5d9628c49dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=B0=8F=E7=99=BD?= <296015668@qq.com> Date: Mon, 15 May 2023 13:19:11 +0800 Subject: [PATCH 64/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/management/commands/services/services/flower.py | 2 +- requirements/requirements_xpack.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/common/management/commands/services/services/flower.py b/apps/common/management/commands/services/services/flower.py index bb1452827..94dd8e2f0 100644 --- a/apps/common/management/commands/services/services/flower.py +++ b/apps/common/management/commands/services/services/flower.py @@ -11,7 +11,7 @@ class FlowerService(BaseService): @property def db_file(self): - return os.path.join(BASE_DIR, 'data', 'flower') + return os.path.join(BASE_DIR, 'data', 'flower.db') @property def cmd(self): diff --git a/requirements/requirements_xpack.txt b/requirements/requirements_xpack.txt index 5ff7986e0..c7d48e644 100644 --- a/requirements/requirements_xpack.txt +++ b/requirements/requirements_xpack.txt @@ -5,7 +5,7 @@ azure-identity==1.5.0 azure-mgmt-compute==4.6.2 azure-mgmt-network==2.7.0 google-cloud-compute==0.5.0 -grpcio=1.54.2 +grpcio==1.54.2 alibabacloud_dysmsapi20170525==2.0.2 python-novaclient==11.0.1 python-keystoneclient==4.3.0 From 341dd6adfb81cc4e72e66dbed35a50d1c5352264 Mon Sep 17 00:00:00 2001 From: Bai Date: Mon, 15 May 2023 15:12:50 +0800 Subject: [PATCH 65/88] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20ansible-core?= =?UTF-8?q?=20=E6=BA=90=20gitee=20->=20github?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index d68a3134c..a8ed07b01 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,6 +1,6 @@ aiofiles==22.1.0 amqp==5.0.9 -git+https://gitee.com/jumpserver/ansible@master#egg=ansible-core +git+https://github.com/jumpserver/ansible@master#egg=ansible-core ansible==7.1.0 ansible-runner==2.2.1 asn1crypto==0.24.0 From 155c241ef740052c5131148b96983d0baf4a1e62 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 15 May 2023 18:47:39 +0800 Subject: [PATCH 66/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E6=A8=A1=E7=89=88=E6=9B=B4=E6=96=B0=E6=97=B6=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AF=86=E7=A0=81=E5=AF=86=E9=92=A5500=20(#1?= =?UTF-8?q?0454)?= 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/template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/accounts/serializers/account/template.py b/apps/accounts/serializers/account/template.py index 937c69d36..d2d992ea6 100644 --- a/apps/accounts/serializers/account/template.py +++ b/apps/accounts/serializers/account/template.py @@ -35,7 +35,7 @@ class AccountTemplateSerializer(BaseAccountSerializer): def update(self, instance, validated_data): diff = { k: v for k, v in validated_data.items() - if getattr(instance, k) != v + if getattr(instance, k, None) != v } instance = super().update(instance, validated_data) self.sync_accounts_secret(instance, diff) From ec10ee3298168e4afe3c396c4de735bb78550c8a Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 15 May 2023 18:58:06 +0800 Subject: [PATCH 67/88] =?UTF-8?q?fix:=20=E6=A8=A1=E7=89=88=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=B7=BB=E5=8A=A0=E6=8F=90=E7=A4=BA=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=EF=BC=88core=20=E4=BB=A3=E7=A0=81bug=EF=BC=89=20(#10455)?= 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index 45f7e26f3..90b1b77c6 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -303,7 +303,7 @@ class AssetAccountBulkSerializer( su_from = validated_data.get('su_from') su_from_username = validated_data.pop('su_from_username', None) if template: - su_from = template.get_su_from_account() + su_from = template.get_su_from_account(asset) elif su_from_username: su_from = asset.accounts.filter(username=su_from_username).first() validated_data['su_from'] = su_from From c7c3f711bfc6bff534235492fea77ffde0ae62cf Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 May 2023 14:48:00 +0800 Subject: [PATCH 68/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E6=9C=BA=E4=B8=8D=E6=98=BE=E7=A4=BAtask=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E9=97=AE=E9=A2=98=20(#10450)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 优化发布机不显示task信息问题 * perf: 添加celery task execution api的task_name字段 --------- Co-authored-by: “huailei000” <2280131253@qq.com> Co-authored-by: feng <1304903146@qq.com> --- apps/assets/api/asset/asset.py | 10 +--------- apps/assets/urls/api_urls.py | 1 - apps/ops/api/celery.py | 8 ++++---- apps/ops/serializers/celery.py | 20 +++++++++++++++++--- apps/ops/templates/ops/celery_task_log.html | 10 +++------- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index 96d55cae9..3f0b21f19 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -12,11 +12,10 @@ 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, AutomationExecution +from assets.models import Asset, Gateway, Platform from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual from common.api import SuggestionMixin from common.drf.filters import BaseFilterSet -from common.permissions import IsValidUser from common.utils import get_logger, is_uuid from orgs.mixins import generics from orgs.mixins.api import OrgBulkModelViewSet @@ -27,7 +26,6 @@ logger = get_logger(__file__) __all__ = [ "AssetViewSet", "AssetTaskCreateApi", "AssetsTaskCreateApi", 'AssetFilterSet', - "AutomationExecutionRetrieveApi", ] @@ -260,9 +258,3 @@ class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): has = self.request.user.has_perm(perm_required) if not has: self.permission_denied(request) - - -class AutomationExecutionRetrieveApi(generics.RetrieveAPIView): - permission_classes = (IsValidUser,) - model = AutomationExecution - serializer_class = serializers.AutomationExecutionSerializer diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index 38924f333..6b5f469d0 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -47,7 +47,6 @@ urlpatterns = [ path('gateways//test-connective/', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'), path('platform-automation-methods/', api.PlatformAutomationMethodsApi.as_view(), name='platform-automation-methods'), - path('automations-execution//', api.AutomationExecutionRetrieveApi.as_view(), name='automation-execution'), ] urlpatterns += router.urls diff --git a/apps/ops/api/celery.py b/apps/ops/api/celery.py index 72d970dee..d4540269c 100644 --- a/apps/ops/api/celery.py +++ b/apps/ops/api/celery.py @@ -4,19 +4,19 @@ import os import re from celery.result import AsyncResult -from rest_framework import generics, viewsets, mixins, status from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext as _ from django_celery_beat.models import PeriodicTask +from rest_framework import generics, viewsets, mixins, status from rest_framework.response import Response +from common.api import LogTailApi, CommonApiMixin from common.exceptions import JMSException from common.permissions import IsValidUser -from common.api import LogTailApi, CommonApiMixin from ops.celery import app -from ..models import CeleryTaskExecution, CeleryTask -from ..celery.utils import get_celery_task_log_path from ..ansible.utils import get_ansible_task_log_path +from ..celery.utils import get_celery_task_log_path +from ..models import CeleryTaskExecution, CeleryTask from ..serializers import CeleryResultSerializer, CeleryPeriodTaskSerializer from ..serializers.celery import CeleryTaskSerializer, CeleryTaskExecutionSerializer diff --git a/apps/ops/serializers/celery.py b/apps/ops/serializers/celery.py index 657600e50..5166aa755 100644 --- a/apps/ops/serializers/celery.py +++ b/apps/ops/serializers/celery.py @@ -5,13 +5,14 @@ from django.utils.translation import gettext_lazy as _ from django_celery_beat.models import PeriodicTask from rest_framework import serializers +from ops.celery import app +from ops.models import CeleryTask, CeleryTaskExecution + __all__ = [ 'CeleryResultSerializer', 'CeleryTaskExecutionSerializer', 'CeleryPeriodTaskSerializer', 'CeleryTaskSerializer' ] -from ops.models import CeleryTask, CeleryTaskExecution - class CeleryResultSerializer(serializers.Serializer): id = serializers.UUIDField() @@ -37,11 +38,24 @@ class CeleryTaskSerializer(serializers.ModelSerializer): class CeleryTaskExecutionSerializer(serializers.ModelSerializer): is_success = serializers.BooleanField(required=False, read_only=True, label=_('Success')) + task_name = serializers.SerializerMethodField() class Meta: model = CeleryTaskExecution fields = [ - "id", "name", "args", "kwargs", "time_cost", "timedelta", + "id", "name", "task_name", "args", "kwargs", "time_cost", "timedelta", "is_success", "is_finished", "date_published", "date_start", "date_finished" ] + + @staticmethod + def get_task_name(obj): + from assets.const import AutomationTypes as AssetTypes + from accounts.const import AutomationTypes as AccountTypes + tp_dict = dict(AssetTypes.choices) | dict(AccountTypes.choices) + tp = obj.kwargs.get('tp') + task = app.tasks.get(obj.name) + task_name = getattr(task, 'verbose_name', obj.name) + if tp: + task_name = f'{task_name}({tp_dict.get(tp, tp)})' + return task_name diff --git a/apps/ops/templates/ops/celery_task_log.html b/apps/ops/templates/ops/celery_task_log.html index b0f16130b..576d63be4 100644 --- a/apps/ops/templates/ops/celery_task_log.html +++ b/apps/ops/templates/ops/celery_task_log.html @@ -71,10 +71,6 @@ {% trans 'Task type' %}: -
  • - {% trans 'Trigger type' %}: - -
  • {% trans 'Date start' %}: @@ -93,6 +89,7 @@ var failOverWsURL = scheme + "://" + document.location.hostname + ':' + failOverPort + url; var term; var ws; + var extraQuery = Object.fromEntries(new URLSearchParams(window.location.search)); $(document).ready(function () { term = new Terminal({ @@ -131,7 +128,7 @@ window.fit.fit(term); }); function getAutomationExecutionInfo() { - var url = "{% url 'api-assets:automation-execution' pk=task_id %}"; + let url = "{% url 'api-ops:task-executions-detail' pk=task_id %}"; requestApi({ url: url, @@ -140,8 +137,7 @@ success(data) { const dateStart = new Date(data.date_start).toLocaleString(); $('.task-id').html(data.id); - $('.task-type').html(data.snapshot.type.label); - $('.trigger-type').html(data.trigger.label); + $('.task-type').html(data.task_name); $('.date-start').html(dateStart); } }) From f7e0f533e0ee11e5dfb91d021175dbd9857df864 Mon Sep 17 00:00:00 2001 From: Bai Date: Tue, 16 May 2023 15:21:15 +0800 Subject: [PATCH 69/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E8=AE=BE=E7=BD=AE=E7=99=BB=E5=BD=95=E9=99=90=E5=88=B6?= =?UTF-8?q?=E5=B8=AE=E5=8A=A9=E6=96=87=E6=A1=88=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.po | 242 ++++++++++++++++---------- apps/locale/zh/LC_MESSAGES/django.po | 238 +++++++++++++++---------- apps/settings/serializers/security.py | 6 +- 3 files changed, 307 insertions(+), 179 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index c98debbd5..d9c9969a3 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-05-09 18:31+0800\n" +"POT-Creation-Date: 2023-05-16 15:15+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -279,7 +279,8 @@ msgstr "アカウントバックアップ計画" #: accounts/models/automations/backup_account.py:83 #: assets/models/automations/base.py:115 audits/models.py:55 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:192 -#: perms/models/asset_permission.py:72 terminal/models/applet/host.py:109 +#: ops/templates/ops/celery_task_log.html:79 +#: perms/models/asset_permission.py:72 terminal/models/applet/host.py:137 #: terminal/models/session/session.py:45 #: tickets/models/ticket/apply_application.py:30 #: tickets/models/ticket/apply_asset.py:19 @@ -312,7 +313,7 @@ msgstr "理由" #: accounts/models/automations/backup_account.py:99 #: accounts/serializers/automations/change_secret.py:111 #: accounts/serializers/automations/change_secret.py:134 -#: ops/serializers/job.py:72 terminal/serializers/session.py:45 +#: ops/serializers/job.py:56 terminal/serializers/session.py:45 msgid "Is success" msgstr "成功は" @@ -402,7 +403,7 @@ msgstr "開始日" #: accounts/models/automations/change_secret.py:91 #: assets/models/automations/base.py:116 ops/models/base.py:56 #: ops/models/celery.py:64 ops/models/job.py:193 -#: terminal/models/applet/host.py:110 +#: terminal/models/applet/host.py:138 msgid "Date finished" msgstr "終了日" @@ -481,7 +482,7 @@ msgstr "アカウントの確認" #: assets/models/asset/common.py:90 assets/models/asset/common.py:123 #: assets/models/cmd_filter.py:21 assets/models/domain.py:18 #: assets/models/group.py:20 assets/models/label.py:18 -#: assets/models/platform.py:13 assets/models/platform.py:89 +#: assets/models/platform.py:13 assets/models/platform.py:81 #: assets/serializers/asset/common.py:145 assets/serializers/platform.py:92 #: assets/serializers/platform.py:194 #: authentication/serializers/connect_token_secret.py:102 ops/mixin.py:21 @@ -560,7 +561,7 @@ msgid "Exist policy" msgstr "アカウントの存在ポリシー" #: accounts/serializers/account/account.py:180 applications/models.py:11 -#: assets/models/label.py:21 assets/models/platform.py:90 +#: assets/models/label.py:21 assets/models/platform.py:82 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 #: assets/serializers/platform.py:110 assets/serializers/platform.py:195 #: perms/serializers/user_permission.py:26 settings/models.py:35 @@ -572,7 +573,7 @@ msgstr "カテゴリ" #: 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 -#: assets/models/cmd_filter.py:74 assets/models/platform.py:91 +#: assets/models/cmd_filter.py:74 assets/models/platform.py:83 #: assets/serializers/asset/common.py:122 assets/serializers/platform.py:94 #: assets/serializers/platform.py:109 audits/serializers.py:48 #: authentication/serializers/connect_token_secret.py:115 ops/models/job.py:103 @@ -810,7 +811,7 @@ msgid "Reviewers" msgstr "レビュー担当者" #: acls/models/base.py:83 authentication/models/access_key.py:17 -#: authentication/models/connection_token.py:49 +#: authentication/models/connection_token.py:50 #: authentication/templates/authentication/_access_key_modal.html:32 #: perms/models/asset_permission.py:76 terminal/models/session/sharing.py:27 #: tickets/const.py:37 @@ -818,7 +819,7 @@ msgid "Active" msgstr "アクティブ" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:71 terminal/const.py:68 +#: ops/serializers/job.py:55 terminal/const.py:68 #: terminal/models/session/session.py:43 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -972,7 +973,7 @@ msgstr "アプリケーション" msgid "Can match application" msgstr "アプリケーションを一致させることができます" -#: assets/api/asset/asset.py:140 +#: assets/api/asset/asset.py:142 msgid "Cannot create asset directly, you should create a host or other" msgstr "" "資産を直接作成することはできません。ホストまたはその他を作成する必要がありま" @@ -1017,7 +1018,7 @@ msgid "Unable to connect to port {port} on {address}" msgstr "{port} のポート {address} に接続できません" #: assets/automations/ping_gateway/manager.py:58 -#: authentication/middleware.py:87 xpack/plugins/cloud/providers/fc.py:48 +#: authentication/middleware.py:92 xpack/plugins/cloud/providers/fc.py:48 msgid "Authentication failed" msgstr "認証に失敗しました" @@ -1110,7 +1111,7 @@ msgstr "ファイアウォール" msgid "Other" msgstr "その他" -#: assets/const/types.py:218 +#: assets/const/types.py:223 msgid "All types" msgstr "いろんなタイプ" @@ -1148,8 +1149,8 @@ msgstr "SSHパブリックキー" #: assets/models/cmd_filter.py:88 assets/models/group.py:23 #: common/db/models.py:37 ops/models/adhoc.py:27 ops/models/job.py:111 #: ops/models/playbook.py:26 rbac/models/role.py:37 settings/models.py:38 -#: terminal/models/applet/applet.py:36 terminal/models/applet/applet.py:184 -#: terminal/models/applet/host.py:111 terminal/models/component/endpoint.py:24 +#: terminal/models/applet/applet.py:37 terminal/models/applet/applet.py:218 +#: terminal/models/applet/host.py:139 terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:100 #: terminal/models/session/session.py:47 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:766 @@ -1261,7 +1262,7 @@ msgstr "ポート" msgid "Address" msgstr "アドレス" -#: assets/models/asset/common.py:125 assets/models/platform.py:120 +#: assets/models/asset/common.py:125 assets/models/platform.py:112 #: authentication/serializers/connect_token_secret.py:107 #: perms/serializers/user_permission.py:24 #: xpack/plugins/cloud/serializers/account_attrs.py:196 @@ -1363,7 +1364,7 @@ msgstr "アセットの自動化タスク" #: assets/models/automations/base.py:113 audits/models.py:177 #: audits/serializers.py:49 ops/models/base.py:49 ops/models/job.py:184 -#: terminal/models/applet/applet.py:183 terminal/models/applet/host.py:108 +#: terminal/models/applet/applet.py:217 terminal/models/applet/host.py:136 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:103 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 @@ -1507,102 +1508,102 @@ msgstr "開ける" msgid "Setting" msgstr "設定" -#: assets/models/platform.py:39 audits/const.py:47 settings/models.py:37 +#: assets/models/platform.py:31 audits/const.py:47 settings/models.py:37 #: terminal/serializers/applet_host.py:29 msgid "Enabled" msgstr "有効化" -#: assets/models/platform.py:40 +#: assets/models/platform.py:32 msgid "Ansible config" msgstr "Ansible 構成" -#: assets/models/platform.py:42 assets/serializers/platform.py:63 +#: assets/models/platform.py:34 assets/serializers/platform.py:63 msgid "Ping enabled" msgstr "アセット ディスカバリを有効にする" -#: assets/models/platform.py:43 assets/serializers/platform.py:64 +#: assets/models/platform.py:35 assets/serializers/platform.py:64 msgid "Ping method" msgstr "資産検出方法" -#: assets/models/platform.py:44 +#: assets/models/platform.py:36 msgid "Ping params" msgstr "資産検出パラメータ" -#: assets/models/platform.py:46 assets/models/platform.py:70 +#: assets/models/platform.py:38 assets/models/platform.py:62 #: assets/serializers/platform.py:65 msgid "Gather facts enabled" msgstr "資産情報の収集を有効にする" -#: assets/models/platform.py:48 assets/models/platform.py:72 +#: assets/models/platform.py:40 assets/models/platform.py:64 #: assets/serializers/platform.py:66 msgid "Gather facts method" msgstr "情報収集の方法" -#: assets/models/platform.py:50 assets/models/platform.py:74 +#: assets/models/platform.py:42 assets/models/platform.py:66 msgid "Gather facts params" msgstr "情報収集パラメータ" -#: assets/models/platform.py:52 assets/serializers/platform.py:69 +#: assets/models/platform.py:44 assets/serializers/platform.py:69 msgid "Change secret enabled" msgstr "パスワードの変更が有効" -#: assets/models/platform.py:54 assets/serializers/platform.py:70 +#: assets/models/platform.py:46 assets/serializers/platform.py:70 msgid "Change secret method" msgstr "パスワード変更モード" -#: assets/models/platform.py:56 +#: assets/models/platform.py:48 msgid "Change secret params" msgstr "パスワード変更パラメータ" -#: assets/models/platform.py:58 assets/serializers/platform.py:71 +#: assets/models/platform.py:50 assets/serializers/platform.py:71 msgid "Push account enabled" msgstr "アカウントのプッシュを有効にする" -#: assets/models/platform.py:60 assets/serializers/platform.py:72 +#: assets/models/platform.py:52 assets/serializers/platform.py:72 msgid "Push account method" msgstr "アカウントプッシュ方式" -#: assets/models/platform.py:62 +#: assets/models/platform.py:54 msgid "Push account params" msgstr "アカウントプッシュパラメータ" -#: assets/models/platform.py:64 assets/serializers/platform.py:67 +#: assets/models/platform.py:56 assets/serializers/platform.py:67 msgid "Verify account enabled" msgstr "アカウントの確認をオンにする" -#: assets/models/platform.py:66 assets/serializers/platform.py:68 +#: assets/models/platform.py:58 assets/serializers/platform.py:68 msgid "Verify account method" msgstr "アカウント認証方法" -#: assets/models/platform.py:68 +#: assets/models/platform.py:60 msgid "Verify account params" msgstr "アカウント認証パラメータ" -#: assets/models/platform.py:92 tickets/models/ticket/general.py:300 +#: assets/models/platform.py:84 tickets/models/ticket/general.py:300 msgid "Meta" msgstr "メタ" -#: assets/models/platform.py:93 +#: assets/models/platform.py:85 msgid "Internal" msgstr "ビルトイン" -#: assets/models/platform.py:97 assets/serializers/platform.py:108 +#: assets/models/platform.py:89 assets/serializers/platform.py:108 msgid "Charset" msgstr "シャーセット" -#: assets/models/platform.py:99 assets/serializers/platform.py:136 +#: assets/models/platform.py:91 assets/serializers/platform.py:136 msgid "Domain enabled" msgstr "ドメインを有効にする" -#: assets/models/platform.py:101 assets/serializers/platform.py:135 +#: assets/models/platform.py:93 assets/serializers/platform.py:135 msgid "Su enabled" msgstr "アカウントの切り替えを有効にする" -#: assets/models/platform.py:102 assets/serializers/platform.py:114 +#: assets/models/platform.py:94 assets/serializers/platform.py:114 msgid "Su method" msgstr "アカウントの切り替え方法" -#: assets/models/platform.py:103 assets/serializers/platform.py:117 +#: assets/models/platform.py:95 assets/serializers/platform.py:117 msgid "Custom fields" msgstr "カスタムフィールド" @@ -1648,7 +1649,13 @@ msgstr "ポート番号が範囲外です (1-65535)" msgid "Protocol is required: {}" msgstr "プロトコルが必要です: {}" -#: assets/serializers/asset/database.py:25 common/serializers/fields.py:103 +#: assets/serializers/asset/database.py:13 +#, fuzzy +#| msgid "Default storage" +msgid "Default database" +msgstr "デフォルトのストレージ" + +#: assets/serializers/asset/database.py:28 common/serializers/fields.py:103 #: tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 @@ -1930,7 +1937,7 @@ msgstr "セッションログ" msgid "Login log" msgstr "ログインログ" -#: audits/const.py:42 terminal/models/applet/host.py:112 +#: audits/const.py:42 terminal/models/applet/host.py:140 #: terminal/models/component/task.py:24 msgid "Task" msgstr "タスク" @@ -2114,19 +2121,19 @@ msgstr "監査セッション タスク ログのクリーンアップ" msgid "This action require verify your MFA" msgstr "この操作には、MFAを検証する必要があります" -#: authentication/api/connection_token.py:300 +#: authentication/api/connection_token.py:303 msgid "Account not found" msgstr "アカウントが見つかりません" -#: authentication/api/connection_token.py:303 +#: authentication/api/connection_token.py:306 msgid "Permission expired" msgstr "承認の有効期限が切れています" -#: authentication/api/connection_token.py:315 +#: authentication/api/connection_token.py:318 msgid "ACL action is reject" msgstr "ACL アクションは拒否です" -#: authentication/api/connection_token.py:319 +#: authentication/api/connection_token.py:322 msgid "ACL action is review" msgstr "ACL アクションはレビューです" @@ -2380,15 +2387,15 @@ msgstr "本を飛ばすは拘束されていません" msgid "Your password is invalid" msgstr "パスワードが無効です" -#: authentication/errors/redirect.py:85 authentication/mixins.py:307 +#: authentication/errors/redirect.py:85 authentication/mixins.py:316 msgid "Your password is too simple, please change it for security" msgstr "パスワードがシンプルすぎるので、セキュリティのために変更してください" -#: authentication/errors/redirect.py:93 authentication/mixins.py:314 +#: authentication/errors/redirect.py:93 authentication/mixins.py:323 msgid "You should to change your password before login" msgstr "ログインする前にパスワードを変更する必要があります" -#: authentication/errors/redirect.py:101 authentication/mixins.py:321 +#: authentication/errors/redirect.py:101 authentication/mixins.py:330 msgid "Your password has expired, please reset before logging in" msgstr "" "パスワードの有効期限が切れました。ログインする前にリセットしてください。" @@ -2487,15 +2494,15 @@ msgstr "電話番号を設定して有効にする" msgid "Clear phone number to disable" msgstr "無効にする電話番号をクリアする" -#: authentication/middleware.py:88 settings/utils/ldap.py:652 +#: authentication/middleware.py:93 settings/utils/ldap.py:652 msgid "Authentication failed (before login check failed): {}" msgstr "認証に失敗しました (ログインチェックが失敗する前): {}" -#: authentication/mixins.py:257 +#: authentication/mixins.py:266 msgid "The MFA type ({}) is not enabled" msgstr "MFAタイプ ({}) が有効になっていない" -#: authentication/mixins.py:297 +#: authentication/mixins.py:306 msgid "Please change your password" msgstr "パスワードを変更してください" @@ -2509,7 +2516,7 @@ msgid "Input username" msgstr "カスタム ユーザー名" #: authentication/models/connection_token.py:38 -#: authentication/serializers/connection_token.py:17 +#: authentication/serializers/connection_token.py:20 msgid "Input secret" msgstr "カスタムパスワード" @@ -2527,34 +2534,40 @@ msgid "Asset display" msgstr "アセット名" #: authentication/models/connection_token.py:43 +#, fuzzy +#| msgid "Disable" +msgid "Reusable" +msgstr "無効化" + +#: authentication/models/connection_token.py:44 #: 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:771 msgid "Date expired" msgstr "期限切れの日付" -#: authentication/models/connection_token.py:47 +#: authentication/models/connection_token.py:48 #: perms/models/asset_permission.py:77 msgid "From ticket" msgstr "チケットから" -#: authentication/models/connection_token.py:53 +#: authentication/models/connection_token.py:54 msgid "Connection token" msgstr "接続トークン" -#: authentication/models/connection_token.py:55 +#: authentication/models/connection_token.py:56 msgid "Can view connection token secret" msgstr "接続トークンの秘密を表示できます" -#: authentication/models/connection_token.py:102 +#: authentication/models/connection_token.py:103 msgid "Connection token inactive" msgstr "接続トークンがアクティブ化されていません" -#: authentication/models/connection_token.py:105 +#: authentication/models/connection_token.py:106 msgid "Connection token expired at: {}" msgstr "接続トークンの有効期限: {}" -#: authentication/models/connection_token.py:108 +#: authentication/models/connection_token.py:109 msgid "No user or invalid user" msgstr "ユーザーなしまたは期限切れのユーザー" @@ -2606,15 +2619,15 @@ msgstr "コンポーネント" msgid "Expired now" msgstr "すぐに期限切れ" -#: authentication/serializers/connection_token.py:15 +#: authentication/serializers/connection_token.py:18 msgid "Expired time" msgstr "期限切れ時間" -#: authentication/serializers/connection_token.py:19 +#: authentication/serializers/connection_token.py:22 msgid "Ticket info" msgstr "作業指示情報" -#: authentication/serializers/connection_token.py:20 +#: authentication/serializers/connection_token.py:23 #: perms/models/asset_permission.py:71 perms/serializers/permission.py:36 #: perms/serializers/permission.py:69 #: tickets/models/ticket/apply_application.py:28 @@ -2622,12 +2635,16 @@ msgstr "作業指示情報" msgid "Actions" msgstr "アクション" -#: authentication/serializers/connection_token.py:41 +#: authentication/serializers/connection_token.py:44 #: perms/serializers/permission.py:38 perms/serializers/permission.py:70 #: users/serializers/user.py:97 users/serializers/user.py:172 msgid "Is expired" msgstr "期限切れです" +#: authentication/serializers/connection_token.py:79 +msgid "Reusable connection token is not allowed, global setting not enabled" +msgstr "" + #: authentication/serializers/password_mfa.py:16 #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/email.py:19 @@ -2722,7 +2739,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:417 +#: jumpserver/conf.py:419 #: 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 @@ -3294,11 +3311,11 @@ msgstr "検索のエクスポート: %s" msgid "User %s view/export secret" msgstr "ユーザー %s がパスワードを閲覧/導き出しました" -#: jumpserver/conf.py:416 +#: jumpserver/conf.py:418 msgid "Create account successfully" msgstr "アカウントを正常に作成" -#: jumpserver/conf.py:418 +#: jumpserver/conf.py:420 msgid "Your account has been created successfully" msgstr "アカウントが正常に作成されました" @@ -3366,15 +3383,15 @@ msgstr "システムメッセージ" msgid "Publish the station message" msgstr "投稿サイトニュース" -#: ops/ansible/inventory.py:83 +#: ops/ansible/inventory.py:82 msgid "No account available" msgstr "利用可能なアカウントがありません" -#: ops/ansible/inventory.py:248 +#: ops/ansible/inventory.py:247 msgid "Ansible disabled" msgstr "Ansible 無効" -#: ops/ansible/inventory.py:264 +#: ops/ansible/inventory.py:263 msgid "Skip hosts below:" msgstr "次のホストをスキップします: " @@ -3640,15 +3657,15 @@ msgstr "{max_threshold} を超えるCPUロード: => {value}" msgid "Run after save" msgstr "保存後に実行" -#: ops/serializers/job.py:70 +#: ops/serializers/job.py:54 msgid "Job type" msgstr "タスクの種類" -#: ops/serializers/job.py:73 terminal/serializers/session.py:49 +#: ops/serializers/job.py:57 terminal/serializers/session.py:49 msgid "Is finished" msgstr "終了しました" -#: ops/serializers/job.py:74 +#: ops/serializers/job.py:58 msgid "Time cost" msgstr "時を過ごす" @@ -3680,6 +3697,18 @@ msgstr "例外ジョブのクリーンアップ" msgid "Task log" msgstr "タスクログ" +#: ops/templates/ops/celery_task_log.html:71 +#, fuzzy +#| msgid "Task Center" +msgid "Task type" +msgstr "タスクセンター" + +#: ops/templates/ops/celery_task_log.html:75 +#, fuzzy +#| msgid "Trigger mode" +msgid "Trigger type" +msgstr "トリガーモード" + #: ops/variables.py:24 msgid "The current user`s username of JumpServer" msgstr "JumpServerの現在のユーザーのユーザー名" @@ -4051,8 +4080,8 @@ msgstr "タスクセンター" msgid "My assets" msgstr "私の資産" -#: rbac/tree.py:56 terminal/models/applet/applet.py:43 -#: terminal/models/applet/applet.py:180 terminal/models/applet/host.py:28 +#: rbac/tree.py:56 terminal/models/applet/applet.py:44 +#: terminal/models/applet/applet.py:214 terminal/models/applet/host.py:28 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "リモートアプリケーション" @@ -4923,24 +4952,42 @@ msgid "Only single device login" msgstr "単一デバイスログインのみ" #: settings/serializers/security.py:97 -msgid "Next device login, pre login will be logout" -msgstr "次のデバイスログイン、事前ログインはログアウトになります" +msgid "" +"After the user logs in on the new device, other logged-in devices will " +"automatically log out" +msgstr "" +"ユーザーが新しいデバイスにログインすると、ログインしている他のデバイスは自動" +"的にログアウトします。" #: settings/serializers/security.py:100 msgid "Only exist user login" msgstr "ユーザーログインのみ存在" #: settings/serializers/security.py:101 -msgid "If enable, CAS、OIDC auth will be failed, if user not exist yet" -msgstr "Enableの場合、ユーザーがまだ存在しない場合、CAS、OIDC authは失敗します" +msgid "" +"If enabled, non-existent users will not be allowed to log in; if disabled, " +"users of other authentication methods except local authentication methods " +"are allowed to log in and automatically create users (if the user does not " +"exist)" +msgstr "" +"有効にすると、存在しないユーザーはログインできなくなります。無効にすると、" +"ローカル認証方法を除く他の認証方法のユーザーはログインでき、ユーザーが自動的" +"に作成されます (ユーザーが存在しない場合)。" #: settings/serializers/security.py:104 msgid "Only from source login" msgstr "ソースログインからのみ" #: settings/serializers/security.py:105 -msgid "Only log in from the user source property" -msgstr "ユーザーソースのプロパティからのみログイン" +msgid "" +"If it is enabled, the user will only authenticate to the source when logging " +"in; if it is disabled, the user will authenticate all the enabled " +"authentication methods in a certain order when logging in, and as long as " +"one of the authentication methods is successful, they can log in directly" +msgstr "" +"これが有効な場合、ユーザーはログイン時にソースに対してのみ認証されます。無効" +"な場合、ユーザーはログイン時に、いずれかの認証方法が成功する限り、有効なすべ" +"ての認証方法を特定の順序で認証します。 、直接ログインできます" #: settings/serializers/security.py:109 msgid "MFA verify TTL" @@ -5591,31 +5638,37 @@ msgid "Author" msgstr "著者" #: terminal/models/applet/applet.py:35 +#, fuzzy +#| msgid "Can push account" +msgid "Can concurrent" +msgstr "アカウントをプッシュできます" + +#: terminal/models/applet/applet.py:36 msgid "Tags" msgstr "ラベル" -#: terminal/models/applet/applet.py:39 terminal/serializers/storage.py:157 +#: terminal/models/applet/applet.py:40 terminal/serializers/storage.py:157 msgid "Hosts" msgstr "ホスト" -#: terminal/models/applet/applet.py:84 +#: terminal/models/applet/applet.py:85 msgid "Applet pkg not valid, Missing file {}" msgstr "無効なアプレット パッケージ、ファイル {} がありません" -#: terminal/models/applet/applet.py:103 +#: terminal/models/applet/applet.py:104 msgid "Load platform.yml failed: {}" msgstr "" -#: terminal/models/applet/applet.py:106 +#: terminal/models/applet/applet.py:107 msgid "Only support custom platform" msgstr "" -#: terminal/models/applet/applet.py:111 +#: terminal/models/applet/applet.py:112 msgid "Missing type in platform.yml" msgstr "" -#: terminal/models/applet/applet.py:182 terminal/models/applet/host.py:34 -#: terminal/models/applet/host.py:106 +#: terminal/models/applet/applet.py:216 terminal/models/applet/host.py:34 +#: terminal/models/applet/host.py:134 msgid "Hosting" msgstr "ホスト マシン" @@ -5635,7 +5688,7 @@ msgstr "" msgid "Date synced" msgstr "同期日" -#: terminal/models/applet/host.py:107 +#: terminal/models/applet/host.py:135 msgid "Initial" msgstr "初期化" @@ -6088,6 +6141,12 @@ msgstr "アプリケーション マシンの展開を実行する" msgid "Install applet" msgstr "アプリをインストールする" +#: terminal/tasks.py:104 +#, fuzzy +#| msgid "Gather assets accounts" +msgid "Generate applet host accounts" +msgstr "資産の口座番号を収集する" + #: terminal/templates/terminal/_msg_command_alert.html:10 msgid "view" msgstr "表示" @@ -6733,6 +6792,13 @@ msgstr "セキュリティのために、複数のユーザーのみをリスト msgid "name not unique" msgstr "名前が一意ではない" +#: users/signal_handlers.py:27 +msgid "" +"The administrator has enabled \"Only allow existing users to log in\", \n" +" and the current user is not in the user list. Please contact the " +"administrator." +msgstr "" + #: users/tasks.py:21 msgid "Check password expired" msgstr "パスワードの有効期限が切れていることを確認する" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index e5cc2a102..56388395b 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-05-09 18:31+0800\n" +"POT-Creation-Date: 2023-05-16 15:15+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -278,7 +278,8 @@ msgstr "账号备份计划" #: accounts/models/automations/backup_account.py:83 #: assets/models/automations/base.py:115 audits/models.py:55 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:192 -#: perms/models/asset_permission.py:72 terminal/models/applet/host.py:109 +#: ops/templates/ops/celery_task_log.html:79 +#: perms/models/asset_permission.py:72 terminal/models/applet/host.py:137 #: terminal/models/session/session.py:45 #: tickets/models/ticket/apply_application.py:30 #: tickets/models/ticket/apply_asset.py:19 @@ -311,7 +312,7 @@ msgstr "原因" #: accounts/models/automations/backup_account.py:99 #: accounts/serializers/automations/change_secret.py:111 #: accounts/serializers/automations/change_secret.py:134 -#: ops/serializers/job.py:72 terminal/serializers/session.py:45 +#: ops/serializers/job.py:56 terminal/serializers/session.py:45 msgid "Is success" msgstr "是否成功" @@ -401,7 +402,7 @@ msgstr "开始日期" #: accounts/models/automations/change_secret.py:91 #: assets/models/automations/base.py:116 ops/models/base.py:56 #: ops/models/celery.py:64 ops/models/job.py:193 -#: terminal/models/applet/host.py:110 +#: terminal/models/applet/host.py:138 msgid "Date finished" msgstr "结束日期" @@ -480,7 +481,7 @@ msgstr "账号验证" #: assets/models/asset/common.py:90 assets/models/asset/common.py:123 #: assets/models/cmd_filter.py:21 assets/models/domain.py:18 #: assets/models/group.py:20 assets/models/label.py:18 -#: assets/models/platform.py:13 assets/models/platform.py:89 +#: assets/models/platform.py:13 assets/models/platform.py:81 #: assets/serializers/asset/common.py:145 assets/serializers/platform.py:92 #: assets/serializers/platform.py:194 #: authentication/serializers/connect_token_secret.py:102 ops/mixin.py:21 @@ -556,7 +557,7 @@ msgid "Exist policy" msgstr "账号存在策略" #: accounts/serializers/account/account.py:180 applications/models.py:11 -#: assets/models/label.py:21 assets/models/platform.py:90 +#: assets/models/label.py:21 assets/models/platform.py:82 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 #: assets/serializers/platform.py:110 assets/serializers/platform.py:195 #: perms/serializers/user_permission.py:26 settings/models.py:35 @@ -568,7 +569,7 @@ msgstr "类别" #: 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 -#: assets/models/cmd_filter.py:74 assets/models/platform.py:91 +#: assets/models/cmd_filter.py:74 assets/models/platform.py:83 #: assets/serializers/asset/common.py:122 assets/serializers/platform.py:94 #: assets/serializers/platform.py:109 audits/serializers.py:48 #: authentication/serializers/connect_token_secret.py:115 ops/models/job.py:103 @@ -806,7 +807,7 @@ msgid "Reviewers" msgstr "审批人" #: acls/models/base.py:83 authentication/models/access_key.py:17 -#: authentication/models/connection_token.py:49 +#: authentication/models/connection_token.py:50 #: authentication/templates/authentication/_access_key_modal.html:32 #: perms/models/asset_permission.py:76 terminal/models/session/sharing.py:27 #: tickets/const.py:37 @@ -814,7 +815,7 @@ msgid "Active" msgstr "激活中" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:71 terminal/const.py:68 +#: ops/serializers/job.py:55 terminal/const.py:68 #: terminal/models/session/session.py:43 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -967,7 +968,7 @@ msgstr "应用程序" msgid "Can match application" msgstr "匹配应用" -#: assets/api/asset/asset.py:140 +#: assets/api/asset/asset.py:142 msgid "Cannot create asset directly, you should create a host or other" msgstr "不能直接创建资产, 你应该创建主机或其他资产" @@ -1010,7 +1011,7 @@ msgid "Unable to connect to port {port} on {address}" msgstr "无法连接到 {port} 上的端口 {address}" #: assets/automations/ping_gateway/manager.py:58 -#: authentication/middleware.py:87 xpack/plugins/cloud/providers/fc.py:48 +#: authentication/middleware.py:92 xpack/plugins/cloud/providers/fc.py:48 msgid "Authentication failed" msgstr "认证失败" @@ -1103,7 +1104,7 @@ msgstr "防火墙" msgid "Other" msgstr "其它" -#: assets/const/types.py:218 +#: assets/const/types.py:223 msgid "All types" msgstr "所有类型" @@ -1141,8 +1142,8 @@ msgstr "SSH公钥" #: assets/models/cmd_filter.py:88 assets/models/group.py:23 #: common/db/models.py:37 ops/models/adhoc.py:27 ops/models/job.py:111 #: ops/models/playbook.py:26 rbac/models/role.py:37 settings/models.py:38 -#: terminal/models/applet/applet.py:36 terminal/models/applet/applet.py:184 -#: terminal/models/applet/host.py:111 terminal/models/component/endpoint.py:24 +#: terminal/models/applet/applet.py:37 terminal/models/applet/applet.py:218 +#: terminal/models/applet/host.py:139 terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:100 #: terminal/models/session/session.py:47 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:766 @@ -1254,7 +1255,7 @@ msgstr "端口" msgid "Address" msgstr "地址" -#: assets/models/asset/common.py:125 assets/models/platform.py:120 +#: assets/models/asset/common.py:125 assets/models/platform.py:112 #: authentication/serializers/connect_token_secret.py:107 #: perms/serializers/user_permission.py:24 #: xpack/plugins/cloud/serializers/account_attrs.py:196 @@ -1356,7 +1357,7 @@ msgstr "资产自动化任务" #: assets/models/automations/base.py:113 audits/models.py:177 #: audits/serializers.py:49 ops/models/base.py:49 ops/models/job.py:184 -#: terminal/models/applet/applet.py:183 terminal/models/applet/host.py:108 +#: terminal/models/applet/applet.py:217 terminal/models/applet/host.py:136 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:103 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 @@ -1500,102 +1501,102 @@ msgstr "开放的" msgid "Setting" msgstr "设置" -#: assets/models/platform.py:39 audits/const.py:47 settings/models.py:37 +#: assets/models/platform.py:31 audits/const.py:47 settings/models.py:37 #: terminal/serializers/applet_host.py:29 msgid "Enabled" msgstr "启用" -#: assets/models/platform.py:40 +#: assets/models/platform.py:32 msgid "Ansible config" msgstr "Ansible 配置" -#: assets/models/platform.py:42 assets/serializers/platform.py:63 +#: assets/models/platform.py:34 assets/serializers/platform.py:63 msgid "Ping enabled" msgstr "启用资产探活" -#: assets/models/platform.py:43 assets/serializers/platform.py:64 +#: assets/models/platform.py:35 assets/serializers/platform.py:64 msgid "Ping method" msgstr "资产探活方式" -#: assets/models/platform.py:44 +#: assets/models/platform.py:36 msgid "Ping params" msgstr "资产探活参数" -#: assets/models/platform.py:46 assets/models/platform.py:70 +#: assets/models/platform.py:38 assets/models/platform.py:62 #: assets/serializers/platform.py:65 msgid "Gather facts enabled" msgstr "启用收集资产信息" -#: assets/models/platform.py:48 assets/models/platform.py:72 +#: assets/models/platform.py:40 assets/models/platform.py:64 #: assets/serializers/platform.py:66 msgid "Gather facts method" msgstr "收集信息方式" -#: assets/models/platform.py:50 assets/models/platform.py:74 +#: assets/models/platform.py:42 assets/models/platform.py:66 msgid "Gather facts params" msgstr "收集信息参数" -#: assets/models/platform.py:52 assets/serializers/platform.py:69 +#: assets/models/platform.py:44 assets/serializers/platform.py:69 msgid "Change secret enabled" msgstr "启用改密" -#: assets/models/platform.py:54 assets/serializers/platform.py:70 +#: assets/models/platform.py:46 assets/serializers/platform.py:70 msgid "Change secret method" msgstr "改密方式" -#: assets/models/platform.py:56 +#: assets/models/platform.py:48 msgid "Change secret params" msgstr "改密参数" -#: assets/models/platform.py:58 assets/serializers/platform.py:71 +#: assets/models/platform.py:50 assets/serializers/platform.py:71 msgid "Push account enabled" msgstr "启用账号推送" -#: assets/models/platform.py:60 assets/serializers/platform.py:72 +#: assets/models/platform.py:52 assets/serializers/platform.py:72 msgid "Push account method" msgstr "账号推送方式" -#: assets/models/platform.py:62 +#: assets/models/platform.py:54 msgid "Push account params" msgstr "账号推送参数" -#: assets/models/platform.py:64 assets/serializers/platform.py:67 +#: assets/models/platform.py:56 assets/serializers/platform.py:67 msgid "Verify account enabled" msgstr "开启账号验证" -#: assets/models/platform.py:66 assets/serializers/platform.py:68 +#: assets/models/platform.py:58 assets/serializers/platform.py:68 msgid "Verify account method" msgstr "账号验证方式" -#: assets/models/platform.py:68 +#: assets/models/platform.py:60 msgid "Verify account params" msgstr "账号验证参数" -#: assets/models/platform.py:92 tickets/models/ticket/general.py:300 +#: assets/models/platform.py:84 tickets/models/ticket/general.py:300 msgid "Meta" msgstr "元数据" -#: assets/models/platform.py:93 +#: assets/models/platform.py:85 msgid "Internal" msgstr "内置" -#: assets/models/platform.py:97 assets/serializers/platform.py:108 +#: assets/models/platform.py:89 assets/serializers/platform.py:108 msgid "Charset" msgstr "编码" -#: assets/models/platform.py:99 assets/serializers/platform.py:136 +#: assets/models/platform.py:91 assets/serializers/platform.py:136 msgid "Domain enabled" msgstr "启用网域" -#: assets/models/platform.py:101 assets/serializers/platform.py:135 +#: assets/models/platform.py:93 assets/serializers/platform.py:135 msgid "Su enabled" msgstr "启用账号切换" -#: assets/models/platform.py:102 assets/serializers/platform.py:114 +#: assets/models/platform.py:94 assets/serializers/platform.py:114 msgid "Su method" msgstr "账号切换方式" -#: assets/models/platform.py:103 assets/serializers/platform.py:117 +#: assets/models/platform.py:95 assets/serializers/platform.py:117 msgid "Custom fields" msgstr "自定义属性" @@ -1639,7 +1640,13 @@ msgstr "端口超出范围 (1-65535)" msgid "Protocol is required: {}" msgstr "协议是必填的: {}" -#: assets/serializers/asset/database.py:25 common/serializers/fields.py:103 +#: assets/serializers/asset/database.py:13 +#, fuzzy +#| msgid "Default storage" +msgid "Default database" +msgstr "默认存储" + +#: assets/serializers/asset/database.py:28 common/serializers/fields.py:103 #: tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 @@ -1919,7 +1926,7 @@ msgstr "会话日志" msgid "Login log" msgstr "登录日志" -#: audits/const.py:42 terminal/models/applet/host.py:112 +#: audits/const.py:42 terminal/models/applet/host.py:140 #: terminal/models/component/task.py:24 msgid "Task" msgstr "任务" @@ -2103,19 +2110,19 @@ msgstr "清理审计会话任务日志" msgid "This action require verify your MFA" msgstr "该操作需要验证您的 MFA, 请先开启并配置" -#: authentication/api/connection_token.py:300 +#: authentication/api/connection_token.py:303 msgid "Account not found" msgstr "账号未找到" -#: authentication/api/connection_token.py:303 +#: authentication/api/connection_token.py:306 msgid "Permission expired" msgstr "授权已过期" -#: authentication/api/connection_token.py:315 +#: authentication/api/connection_token.py:318 msgid "ACL action is reject" msgstr "ACL 动作是拒绝" -#: authentication/api/connection_token.py:319 +#: authentication/api/connection_token.py:322 msgid "ACL action is review" msgstr "ACL 动作是复核" @@ -2359,15 +2366,15 @@ msgstr "没有绑定飞书" msgid "Your password is invalid" msgstr "您的密码无效" -#: authentication/errors/redirect.py:85 authentication/mixins.py:307 +#: authentication/errors/redirect.py:85 authentication/mixins.py:316 msgid "Your password is too simple, please change it for security" msgstr "你的密码过于简单,为了安全,请修改" -#: authentication/errors/redirect.py:93 authentication/mixins.py:314 +#: authentication/errors/redirect.py:93 authentication/mixins.py:323 msgid "You should to change your password before login" msgstr "登录完成前,请先修改密码" -#: authentication/errors/redirect.py:101 authentication/mixins.py:321 +#: authentication/errors/redirect.py:101 authentication/mixins.py:330 msgid "Your password has expired, please reset before logging in" msgstr "您的密码已过期,先修改再登录" @@ -2464,15 +2471,15 @@ msgstr "设置手机号码启用" msgid "Clear phone number to disable" msgstr "清空手机号码禁用" -#: authentication/middleware.py:88 settings/utils/ldap.py:652 +#: authentication/middleware.py:93 settings/utils/ldap.py:652 msgid "Authentication failed (before login check failed): {}" msgstr "认证失败(登录前检查失败): {}" -#: authentication/mixins.py:257 +#: authentication/mixins.py:266 msgid "The MFA type ({}) is not enabled" msgstr "该 MFA ({}) 方式没有启用" -#: authentication/mixins.py:297 +#: authentication/mixins.py:306 msgid "Please change your password" msgstr "请修改密码" @@ -2486,7 +2493,7 @@ msgid "Input username" msgstr "自定义用户名" #: authentication/models/connection_token.py:38 -#: authentication/serializers/connection_token.py:17 +#: authentication/serializers/connection_token.py:20 msgid "Input secret" msgstr "自定义密码" @@ -2504,34 +2511,40 @@ msgid "Asset display" msgstr "资产名称" #: authentication/models/connection_token.py:43 +#, fuzzy +#| msgid "Disable" +msgid "Reusable" +msgstr "禁用" + +#: authentication/models/connection_token.py:44 #: 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:771 msgid "Date expired" msgstr "失效日期" -#: authentication/models/connection_token.py:47 +#: authentication/models/connection_token.py:48 #: perms/models/asset_permission.py:77 msgid "From ticket" msgstr "来自工单" -#: authentication/models/connection_token.py:53 +#: authentication/models/connection_token.py:54 msgid "Connection token" msgstr "连接令牌" -#: authentication/models/connection_token.py:55 +#: authentication/models/connection_token.py:56 msgid "Can view connection token secret" msgstr "可以查看连接令牌密文" -#: authentication/models/connection_token.py:102 +#: authentication/models/connection_token.py:103 msgid "Connection token inactive" msgstr "连接令牌未激活" -#: authentication/models/connection_token.py:105 +#: authentication/models/connection_token.py:106 msgid "Connection token expired at: {}" msgstr "连接令牌过期: {}" -#: authentication/models/connection_token.py:108 +#: authentication/models/connection_token.py:109 msgid "No user or invalid user" msgstr "没有用户或用户失效" @@ -2583,15 +2596,15 @@ msgstr "组件" msgid "Expired now" msgstr "立刻过期" -#: authentication/serializers/connection_token.py:15 +#: authentication/serializers/connection_token.py:18 msgid "Expired time" msgstr "过期时间" -#: authentication/serializers/connection_token.py:19 +#: authentication/serializers/connection_token.py:22 msgid "Ticket info" msgstr "工单信息" -#: authentication/serializers/connection_token.py:20 +#: authentication/serializers/connection_token.py:23 #: perms/models/asset_permission.py:71 perms/serializers/permission.py:36 #: perms/serializers/permission.py:69 #: tickets/models/ticket/apply_application.py:28 @@ -2599,12 +2612,16 @@ msgstr "工单信息" msgid "Actions" msgstr "动作" -#: authentication/serializers/connection_token.py:41 +#: authentication/serializers/connection_token.py:44 #: perms/serializers/permission.py:38 perms/serializers/permission.py:70 #: users/serializers/user.py:97 users/serializers/user.py:172 msgid "Is expired" msgstr "已过期" +#: authentication/serializers/connection_token.py:79 +msgid "Reusable connection token is not allowed, global setting not enabled" +msgstr "" + #: authentication/serializers/password_mfa.py:16 #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/email.py:19 @@ -2699,7 +2716,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:417 +#: jumpserver/conf.py:419 #: 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 @@ -3261,11 +3278,11 @@ msgstr "导出搜素: %s" msgid "User %s view/export secret" msgstr "用户 %s 查看/导出 了密码" -#: jumpserver/conf.py:416 +#: jumpserver/conf.py:418 msgid "Create account successfully" msgstr "创建账号成功" -#: jumpserver/conf.py:418 +#: jumpserver/conf.py:420 msgid "Your account has been created successfully" msgstr "你的账号已创建成功" @@ -3328,15 +3345,15 @@ msgstr "系统信息" msgid "Publish the station message" msgstr "发布站内消息" -#: ops/ansible/inventory.py:83 +#: ops/ansible/inventory.py:82 msgid "No account available" msgstr "无可用账号" -#: ops/ansible/inventory.py:248 +#: ops/ansible/inventory.py:247 msgid "Ansible disabled" msgstr "Ansible 已禁用" -#: ops/ansible/inventory.py:264 +#: ops/ansible/inventory.py:263 msgid "Skip hosts below:" msgstr "跳过以下主机: " @@ -3602,15 +3619,15 @@ msgstr "CPU 使用率超过 {max_threshold}: => {value}" msgid "Run after save" msgstr "保存后执行" -#: ops/serializers/job.py:70 +#: ops/serializers/job.py:54 msgid "Job type" msgstr "任务类型" -#: ops/serializers/job.py:73 terminal/serializers/session.py:49 +#: ops/serializers/job.py:57 terminal/serializers/session.py:49 msgid "Is finished" msgstr "是否完成" -#: ops/serializers/job.py:74 +#: ops/serializers/job.py:58 msgid "Time cost" msgstr "花费时间" @@ -3642,6 +3659,18 @@ msgstr "清理异常作业" msgid "Task log" msgstr "任务列表" +#: ops/templates/ops/celery_task_log.html:71 +#, fuzzy +#| msgid "Task Center" +msgid "Task type" +msgstr "任务中心" + +#: ops/templates/ops/celery_task_log.html:75 +#, fuzzy +#| msgid "Trigger mode" +msgid "Trigger type" +msgstr "触发模式" + #: ops/variables.py:24 msgid "The current user`s username of JumpServer" msgstr "JumpServer 当前用户的用户名" @@ -4011,8 +4040,8 @@ msgstr "任务中心" msgid "My assets" msgstr "我的资产" -#: rbac/tree.py:56 terminal/models/applet/applet.py:43 -#: terminal/models/applet/applet.py:180 terminal/models/applet/host.py:28 +#: rbac/tree.py:56 terminal/models/applet/applet.py:44 +#: terminal/models/applet/applet.py:214 terminal/models/applet/host.py:28 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "远程应用" @@ -4868,24 +4897,38 @@ msgid "Only single device login" msgstr "仅一台设备登录" #: settings/serializers/security.py:97 -msgid "Next device login, pre login will be logout" -msgstr "下个设备登录,上次登录会被顶掉" +msgid "" +"After the user logs in on the new device, other logged-in devices will " +"automatically log out" +msgstr "用户在新设备登录后,其他已登录的设备会自动退出" #: settings/serializers/security.py:100 msgid "Only exist user login" msgstr "仅已存在用户登录" #: settings/serializers/security.py:101 -msgid "If enable, CAS、OIDC auth will be failed, if user not exist yet" -msgstr "开启后,如果系统中不存在该用户,CAS、OIDC 登录将会失败" +msgid "" +"If enabled, non-existent users will not be allowed to log in; if disabled, " +"users of other authentication methods except local authentication methods " +"are allowed to log in and automatically create users (if the user does not " +"exist)" +msgstr "" +"如果开启,不存在的用户将不被允许登录;如果关闭,除本地认证方式外,其他认证方" +"式的用户都允许登录并自动创建用户(如果用户不存在)" #: settings/serializers/security.py:104 msgid "Only from source login" msgstr "仅从用户来源登录" #: settings/serializers/security.py:105 -msgid "Only log in from the user source property" -msgstr "开启后,如果用户来源为本地,CAS、OIDC 登录将会失败" +msgid "" +"If it is enabled, the user will only authenticate to the source when logging " +"in; if it is disabled, the user will authenticate all the enabled " +"authentication methods in a certain order when logging in, and as long as " +"one of the authentication methods is successful, they can log in directly" +msgstr "" +"如果开启,用户登录时仅会向来源端进行认证;如果关闭,用户登录时会按照一定的顺" +"序对所有已开启的认证方式进行顺序认证,只要有一个认证成功就可以直接登录" #: settings/serializers/security.py:109 msgid "MFA verify TTL" @@ -5516,31 +5559,37 @@ msgid "Author" msgstr "作者" #: terminal/models/applet/applet.py:35 +#, fuzzy +#| msgid "Can push account" +msgid "Can concurrent" +msgstr "可以推送账号" + +#: terminal/models/applet/applet.py:36 msgid "Tags" msgstr "标签" -#: terminal/models/applet/applet.py:39 terminal/serializers/storage.py:157 +#: terminal/models/applet/applet.py:40 terminal/serializers/storage.py:157 msgid "Hosts" msgstr "主机" -#: terminal/models/applet/applet.py:84 +#: terminal/models/applet/applet.py:85 msgid "Applet pkg not valid, Missing file {}" msgstr "Applet pkg 无效,缺少文件 {}" -#: terminal/models/applet/applet.py:103 +#: terminal/models/applet/applet.py:104 msgid "Load platform.yml failed: {}" msgstr "" -#: terminal/models/applet/applet.py:106 +#: terminal/models/applet/applet.py:107 msgid "Only support custom platform" msgstr "" -#: terminal/models/applet/applet.py:111 +#: terminal/models/applet/applet.py:112 msgid "Missing type in platform.yml" msgstr "" -#: terminal/models/applet/applet.py:182 terminal/models/applet/host.py:34 -#: terminal/models/applet/host.py:106 +#: terminal/models/applet/applet.py:216 terminal/models/applet/host.py:34 +#: terminal/models/applet/host.py:134 msgid "Hosting" msgstr "宿主机" @@ -5560,7 +5609,7 @@ msgstr "初始化日期" msgid "Date synced" msgstr "同步日期" -#: terminal/models/applet/host.py:107 +#: terminal/models/applet/host.py:135 msgid "Initial" msgstr "初始化" @@ -6007,6 +6056,12 @@ msgstr "运行应用机部署" msgid "Install applet" msgstr "安装应用" +#: terminal/tasks.py:104 +#, fuzzy +#| msgid "Gather assets accounts" +msgid "Generate applet host accounts" +msgstr "收集资产上的账号" + #: terminal/templates/terminal/_msg_command_alert.html:10 msgid "view" msgstr "查看" @@ -6646,6 +6701,13 @@ msgstr "为了安全,仅列出几个用户" msgid "name not unique" msgstr "名称重复" +#: users/signal_handlers.py:27 +msgid "" +"The administrator has enabled \"Only allow existing users to log in\", \n" +" and the current user is not in the user list. Please contact the " +"administrator." +msgstr "" + #: users/tasks.py:21 msgid "Check password expired" msgstr "校验密码已过期" diff --git a/apps/settings/serializers/security.py b/apps/settings/serializers/security.py index 1dc41b037..891149add 100644 --- a/apps/settings/serializers/security.py +++ b/apps/settings/serializers/security.py @@ -94,15 +94,15 @@ class SecurityAuthSerializer(serializers.Serializer): ) USER_LOGIN_SINGLE_MACHINE_ENABLED = serializers.BooleanField( required=False, default=False, label=_("Only single device login"), - help_text=_("Next device login, pre login will be logout") + help_text=_("After the user logs in on the new device, other logged-in devices will automatically log out") ) ONLY_ALLOW_EXIST_USER_AUTH = serializers.BooleanField( required=False, default=False, label=_("Only exist user login"), - help_text=_("If enable, CAS、OIDC auth will be failed, if user not exist yet") + help_text=_("If enabled, non-existent users will not be allowed to log in; if disabled, users of other authentication methods except local authentication methods are allowed to log in and automatically create users (if the user does not exist)") ) ONLY_ALLOW_AUTH_FROM_SOURCE = serializers.BooleanField( required=False, default=False, label=_("Only from source login"), - help_text=_("Only log in from the user source property") + help_text=_("If it is enabled, the user will only authenticate to the source when logging in; if it is disabled, the user will authenticate all the enabled authentication methods in a certain order when logging in, and as long as one of the authentication methods is successful, they can log in directly") ) SECURITY_MFA_VERIFY_TTL = serializers.IntegerField( min_value=5, max_value=60 * 60 * 10, From 9cc9600a4c92bc220f23b134d6ee50a2bf2e7e6b Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 May 2023 15:44:02 +0800 Subject: [PATCH 70/88] =?UTF-8?q?fix:=20=E6=89=B9=E9=87=8F=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=B4=A6=E5=8F=B7su=5Ffrom=20=E9=94=99=E4=B9=B1=20(#1?= =?UTF-8?q?0463)?= 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 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index 90b1b77c6..70d7fce4d 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -1,4 +1,5 @@ import uuid +from copy import deepcopy from django.db import IntegrityError from django.db.models import Q @@ -310,6 +311,7 @@ class AssetAccountBulkSerializer( def perform_create(self, vd, handler): lookup = self.get_filter_lookup(vd) + vd = deepcopy(vd) self.generate_su_from_data(vd) try: instance, changed, state = handler(vd, lookup) From 449e7ce45478bb1e3b1b1d040f75542ab0213051 Mon Sep 17 00:00:00 2001 From: Bai Date: Tue, 16 May 2023 16:30:41 +0800 Subject: [PATCH 71/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E7=BB=84=E7=BB=87=E6=97=B6=E7=BB=84=E7=BB=87=E6=A0=B9?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=9C=AA=E8=A2=AB=E5=88=A0=E9=99=A4=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/orgs/signal_handlers/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/orgs/signal_handlers/common.py b/apps/orgs/signal_handlers/common.py index fe1d673ea..998ee216c 100644 --- a/apps/orgs/signal_handlers/common.py +++ b/apps/orgs/signal_handlers/common.py @@ -78,7 +78,7 @@ def on_org_create_or_update(sender, instance, **kwargs): @receiver(pre_delete, sender=Organization) -def on_org_delete(sender, instance, **kwargs): +def delete_org_root_node_on_org_delete(sender, instance, **kwargs): expire_orgs_mapping_for_memory(instance.id) # 删除该组织下所有 节点 @@ -89,7 +89,7 @@ def on_org_delete(sender, instance, **kwargs): @receiver(post_delete, sender=Organization) -def on_org_delete(sender, instance, **kwargs): +def expire_user_orgs_on_org_delete(sender, instance, **kwargs): expire_user_orgs() From 4ce2d991dd85f86dc8c43764b0eb6cd7d4f05e98 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 May 2023 18:15:42 +0800 Subject: [PATCH 72/88] =?UTF-8?q?perf:=20=E6=94=B6=E9=9B=86mysql=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7username=20=E4=BC=98=E5=8C=96=20(#10470)?= 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/gather_accounts/filter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/accounts/automations/gather_accounts/filter.py b/apps/accounts/automations/gather_accounts/filter.py index af7cdefa4..c6db6dbd4 100644 --- a/apps/accounts/automations/gather_accounts/filter.py +++ b/apps/accounts/automations/gather_accounts/filter.py @@ -13,8 +13,8 @@ class GatherAccountsFilter: def mysql_filter(info): result = {} for _, user_dict in info.items(): - for username, data in user_dict.items(): - if data.get('account_locked') == 'N': + for username, _ in user_dict.items(): + if len(username.split('.')) == 1: result[username] = {} return result From cfc91047fd110e94c168157843ea7ca2a176422c Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Tue, 16 May 2023 17:20:15 +0800 Subject: [PATCH 73/88] =?UTF-8?q?perf:=20=E8=87=AA=E5=8A=A8=E5=8C=96?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E6=89=A7=E8=A1=8C=E9=94=99=E8=AF=AF=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=9C=A8DEBUG=5FDEV=E4=B8=8B=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/automations/base/manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/assets/automations/base/manager.py b/apps/assets/automations/base/manager.py index 8d47a6ca4..aa31c886f 100644 --- a/apps/assets/automations/base/manager.py +++ b/apps/assets/automations/base/manager.py @@ -224,7 +224,8 @@ class BasePlaybookManager: pass def on_host_error(self, host, error, result): - print('host error: {} -> {}'.format(host, error)) + if settings.DEBUG_DEV: + print('host error: {} -> {}'.format(host, error)) def on_runner_success(self, runner, cb): summary = cb.summary From a7fed21819796259c71325aee74d397571d6f714 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 May 2023 18:35:48 +0800 Subject: [PATCH 74/88] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91=20(#10472)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/authentication/mixins.py | 4 +- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 81 +++++++++++---------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 78 ++++++++++---------- apps/ops/templates/ops/celery_task_log.html | 2 +- 6 files changed, 92 insertions(+), 81 deletions(-) diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index e98b44028..3bd872dbc 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -88,8 +88,8 @@ def authenticate(request=None, **credentials): else: if temp_user is not None: source_display = temp_user.source_display - request.error_message = '''The administrator has enabled 'Only allow login from user source'. - The current user source is {}. Please contact the administrator.'''.format(source_display) + request.error_message = _('''The administrator has enabled 'Only allow login from user source'. + The current user source is {}. Please contact the administrator.''').format(source_display) return temp_user # The credentials supplied are invalid to all backends, fire signal diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 7d17757e9..04807dc75 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:e3d2510369021ec160d292274694c0bfbd50200b5937aee4206c32bbe018cec8 -size 139997 +oid sha256:523a93e9703e62c39440d2e172c96fea7d8d04965cab43095fc8a378d157bf59 +size 141798 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index d9c9969a3..722d482d8 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-05-16 15:15+0800\n" +"POT-Creation-Date: 2023-05-16 18:32+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -70,7 +70,7 @@ msgstr "ローカル" msgid "Collected" msgstr "集めました" -#: accounts/const/account.py:21 accounts/serializers/account/account.py:25 +#: accounts/const/account.py:21 accounts/serializers/account/account.py:26 #: settings/serializers/auth/sms.py:75 msgid "Template" msgstr "テンプレート" @@ -181,8 +181,8 @@ msgstr "作成のみ" #: accounts/models/account.py:49 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:200 -#: accounts/serializers/account/account.py:233 +#: accounts/serializers/account/account.py:201 +#: accounts/serializers/account/account.py:234 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 @@ -199,8 +199,8 @@ msgid "Asset" msgstr "資産" #: accounts/models/account.py:53 accounts/models/account.py:113 -#: accounts/serializers/account/account.py:205 -#: accounts/serializers/account/account.py:243 +#: accounts/serializers/account/account.py:206 +#: accounts/serializers/account/account.py:244 #: accounts/serializers/account/template.py:16 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" @@ -211,7 +211,7 @@ msgstr "から切り替え" msgid "Version" msgstr "バージョン" -#: accounts/models/account.py:57 accounts/serializers/account/account.py:201 +#: accounts/models/account.py:57 accounts/serializers/account/account.py:202 #: users/models/user.py:778 msgid "Source" msgstr "ソース" @@ -279,7 +279,7 @@ msgstr "アカウントバックアップ計画" #: accounts/models/automations/backup_account.py:83 #: assets/models/automations/base.py:115 audits/models.py:55 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:192 -#: ops/templates/ops/celery_task_log.html:79 +#: ops/templates/ops/celery_task_log.html:75 #: perms/models/asset_permission.py:72 terminal/models/applet/host.py:137 #: terminal/models/session/session.py:45 #: tickets/models/ticket/apply_application.py:30 @@ -358,7 +358,7 @@ msgid "Can add push account execution" msgstr "プッシュ アカウントの作成の実行" #: accounts/models/automations/change_secret.py:18 accounts/models/base.py:36 -#: accounts/serializers/account/account.py:410 +#: accounts/serializers/account/account.py:412 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:46 #: authentication/serializers/connect_token_secret.py:41 @@ -408,7 +408,7 @@ msgid "Date finished" msgstr "終了日" #: accounts/models/automations/change_secret.py:93 -#: accounts/serializers/account/account.py:235 assets/const/automation.py:8 +#: accounts/serializers/account/account.py:236 assets/const/automation.py:8 #: authentication/views/base.py:29 authentication/views/base.py:30 #: authentication/views/base.py:31 common/const/choices.py:20 msgid "Error" @@ -552,15 +552,15 @@ msgstr "" "{} -暗号化変更タスクが完了しました: 暗号化パスワードが設定されていません-個人" "情報にアクセスしてください-> ファイル暗号化パスワードを設定してください" -#: accounts/serializers/account/account.py:28 +#: accounts/serializers/account/account.py:29 msgid "Push now" msgstr "今すぐプッシュ" -#: accounts/serializers/account/account.py:35 +#: accounts/serializers/account/account.py:36 msgid "Exist policy" msgstr "アカウントの存在ポリシー" -#: accounts/serializers/account/account.py:180 applications/models.py:11 +#: accounts/serializers/account/account.py:181 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:82 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 #: assets/serializers/platform.py:110 assets/serializers/platform.py:195 @@ -569,7 +569,7 @@ msgstr "アカウントの存在ポリシー" msgid "Category" msgstr "カテゴリ" -#: accounts/serializers/account/account.py:181 +#: accounts/serializers/account/account.py:182 #: 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 @@ -588,27 +588,27 @@ msgstr "カテゴリ" msgid "Type" msgstr "タイプ" -#: accounts/serializers/account/account.py:196 +#: accounts/serializers/account/account.py:197 msgid "Asset not found" msgstr "資産が存在しません" -#: accounts/serializers/account/account.py:202 +#: accounts/serializers/account/account.py:203 #: accounts/serializers/account/base.py:64 msgid "Has secret" msgstr "エスクローされたパスワード" -#: accounts/serializers/account/account.py:234 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:235 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:236 +#: accounts/serializers/account/account.py:237 msgid "Changed" msgstr "編集済み" -#: accounts/serializers/account/account.py:245 +#: accounts/serializers/account/account.py:246 #: accounts/serializers/automations/base.py:22 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 ops/models/base.py:17 @@ -617,27 +617,27 @@ msgstr "編集済み" msgid "Assets" msgstr "資産" -#: accounts/serializers/account/account.py:297 +#: accounts/serializers/account/account.py:298 msgid "Account already exists" msgstr "アカウントはすでに存在しています" -#: accounts/serializers/account/account.py:346 +#: accounts/serializers/account/account.py:348 #, python-format msgid "Asset does not support this secret type: %s" msgstr "アセットはアカウント タイプをサポートしていません: %s" -#: accounts/serializers/account/account.py:377 +#: accounts/serializers/account/account.py:379 msgid "Account has exist" msgstr "アカウントはすでに存在しています" -#: accounts/serializers/account/account.py:411 +#: accounts/serializers/account/account.py:413 #: authentication/serializers/connect_token_secret.py:146 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:33 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:418 acls/models/base.py:98 +#: accounts/serializers/account/account.py:420 acls/models/base.py:98 #: acls/models/login_acl.py:13 acls/serializers/base.py:75 #: acls/serializers/login_acl.py:21 assets/models/cmd_filter.py:24 #: assets/models/label.py:16 audits/models.py:44 audits/models.py:63 @@ -655,7 +655,7 @@ msgstr "ID" msgid "User" msgstr "ユーザー" -#: accounts/serializers/account/account.py:419 +#: accounts/serializers/account/account.py:421 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:98 terminal/notifications.py:146 msgid "Date" @@ -731,7 +731,7 @@ msgstr "自動タスク実行履歴" #: accounts/serializers/automations/change_secret.py:155 audits/const.py:52 #: audits/models.py:54 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:39 +#: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:40 #: terminal/const.py:60 terminal/models/session/sharing.py:107 #: tickets/views/approve.py:114 msgid "Success" @@ -973,7 +973,7 @@ msgstr "アプリケーション" msgid "Can match application" msgstr "アプリケーションを一致させることができます" -#: assets/api/asset/asset.py:142 +#: assets/api/asset/asset.py:140 msgid "Cannot create asset directly, you should create a host or other" msgstr "" "資産を直接作成することはできません。ホストまたはその他を作成する必要がありま" @@ -2498,6 +2498,14 @@ msgstr "無効にする電話番号をクリアする" msgid "Authentication failed (before login check failed): {}" msgstr "認証に失敗しました (ログインチェックが失敗する前): {}" +#: authentication/mixins.py:91 +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:266 msgid "The MFA type ({}) is not enabled" msgstr "MFAタイプ ({}) が有効になっていない" @@ -3698,16 +3706,8 @@ msgid "Task log" msgstr "タスクログ" #: ops/templates/ops/celery_task_log.html:71 -#, fuzzy -#| msgid "Task Center" -msgid "Task type" -msgstr "タスクセンター" - -#: ops/templates/ops/celery_task_log.html:75 -#, fuzzy -#| msgid "Trigger mode" -msgid "Trigger type" -msgstr "トリガーモード" +msgid "Task name" +msgstr "タスク名" #: ops/variables.py:24 msgid "The current user`s username of JumpServer" @@ -6798,6 +6798,8 @@ msgid "" " and the current user is not in the user list. Please contact the " "administrator." msgstr "" +"管理者は「既存のユーザーのみログインを許可」をオンにしており、現在のユーザー" +"はユーザーリストにありません。管理者に連絡してください。" #: users/tasks.py:21 msgid "Check password expired" @@ -7642,5 +7644,10 @@ msgstr "究極のエディション" msgid "Community edition" msgstr "コミュニティ版" +#, fuzzy +#~| msgid "Trigger mode" +#~ msgid "Trigger type" +#~ msgstr "トリガーモード" + #~ msgid "Please login with a password and then bind the FeiShu" #~ msgstr "パスワードでログインしてから本を飛ばすをバインドしてください" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 93db09aec..ac7231fc1 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:5dd509799bc1f28429dc4500dd893ab3872879c298e7016ecd219a7b9a4597a0 -size 114702 +oid sha256:bd60ca8b6c43b9b5940b14a8ca8073ae26062a5402f663ac39043cbc669199bd +size 116040 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 56388395b..91582121a 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-05-16 15:15+0800\n" +"POT-Creation-Date: 2023-05-16 18:32+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -69,7 +69,7 @@ msgstr "数据库" msgid "Collected" msgstr "收集" -#: accounts/const/account.py:21 accounts/serializers/account/account.py:25 +#: accounts/const/account.py:21 accounts/serializers/account/account.py:26 #: settings/serializers/auth/sms.py:75 msgid "Template" msgstr "模板" @@ -180,8 +180,8 @@ msgstr "仅创建" #: accounts/models/account.py:49 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:200 -#: accounts/serializers/account/account.py:233 +#: accounts/serializers/account/account.py:201 +#: accounts/serializers/account/account.py:234 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:112 #: accounts/serializers/automations/change_secret.py:132 @@ -198,8 +198,8 @@ msgid "Asset" msgstr "资产" #: accounts/models/account.py:53 accounts/models/account.py:113 -#: accounts/serializers/account/account.py:205 -#: accounts/serializers/account/account.py:243 +#: accounts/serializers/account/account.py:206 +#: accounts/serializers/account/account.py:244 #: accounts/serializers/account/template.py:16 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" @@ -210,7 +210,7 @@ msgstr "切换自" msgid "Version" msgstr "版本" -#: accounts/models/account.py:57 accounts/serializers/account/account.py:201 +#: accounts/models/account.py:57 accounts/serializers/account/account.py:202 #: users/models/user.py:778 msgid "Source" msgstr "来源" @@ -278,7 +278,7 @@ msgstr "账号备份计划" #: accounts/models/automations/backup_account.py:83 #: assets/models/automations/base.py:115 audits/models.py:55 #: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:192 -#: ops/templates/ops/celery_task_log.html:79 +#: ops/templates/ops/celery_task_log.html:75 #: perms/models/asset_permission.py:72 terminal/models/applet/host.py:137 #: terminal/models/session/session.py:45 #: tickets/models/ticket/apply_application.py:30 @@ -357,7 +357,7 @@ msgid "Can add push account execution" msgstr "创建推送账号执行" #: accounts/models/automations/change_secret.py:18 accounts/models/base.py:36 -#: accounts/serializers/account/account.py:410 +#: accounts/serializers/account/account.py:412 #: accounts/serializers/account/base.py:16 #: accounts/serializers/automations/change_secret.py:46 #: authentication/serializers/connect_token_secret.py:41 @@ -407,7 +407,7 @@ msgid "Date finished" msgstr "结束日期" #: accounts/models/automations/change_secret.py:93 -#: accounts/serializers/account/account.py:235 assets/const/automation.py:8 +#: accounts/serializers/account/account.py:236 assets/const/automation.py:8 #: authentication/views/base.py:29 authentication/views/base.py:30 #: authentication/views/base.py:31 common/const/choices.py:20 msgid "Error" @@ -548,15 +548,15 @@ msgstr "" "{} - 改密任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设置加" "密密码" -#: accounts/serializers/account/account.py:28 +#: accounts/serializers/account/account.py:29 msgid "Push now" msgstr "立即推送" -#: accounts/serializers/account/account.py:35 +#: accounts/serializers/account/account.py:36 msgid "Exist policy" msgstr "账号存在策略" -#: accounts/serializers/account/account.py:180 applications/models.py:11 +#: accounts/serializers/account/account.py:181 applications/models.py:11 #: assets/models/label.py:21 assets/models/platform.py:82 #: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:8 #: assets/serializers/platform.py:110 assets/serializers/platform.py:195 @@ -565,7 +565,7 @@ msgstr "账号存在策略" msgid "Category" msgstr "类别" -#: accounts/serializers/account/account.py:181 +#: accounts/serializers/account/account.py:182 #: 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 @@ -584,27 +584,27 @@ msgstr "类别" msgid "Type" msgstr "类型" -#: accounts/serializers/account/account.py:196 +#: accounts/serializers/account/account.py:197 msgid "Asset not found" msgstr "资产不存在" -#: accounts/serializers/account/account.py:202 +#: accounts/serializers/account/account.py:203 #: accounts/serializers/account/base.py:64 msgid "Has secret" msgstr "已托管密码" -#: accounts/serializers/account/account.py:234 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:235 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:236 +#: accounts/serializers/account/account.py:237 msgid "Changed" msgstr "已修改" -#: accounts/serializers/account/account.py:245 +#: accounts/serializers/account/account.py:246 #: accounts/serializers/automations/base.py:22 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 ops/models/base.py:17 @@ -613,27 +613,27 @@ msgstr "已修改" msgid "Assets" msgstr "资产" -#: accounts/serializers/account/account.py:297 +#: accounts/serializers/account/account.py:298 msgid "Account already exists" msgstr "账号已存在" -#: accounts/serializers/account/account.py:346 +#: accounts/serializers/account/account.py:348 #, python-format msgid "Asset does not support this secret type: %s" msgstr "资产不支持账号类型: %s" -#: accounts/serializers/account/account.py:377 +#: accounts/serializers/account/account.py:379 msgid "Account has exist" msgstr "账号已存在" -#: accounts/serializers/account/account.py:411 +#: accounts/serializers/account/account.py:413 #: authentication/serializers/connect_token_secret.py:146 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:33 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:418 acls/models/base.py:98 +#: accounts/serializers/account/account.py:420 acls/models/base.py:98 #: acls/models/login_acl.py:13 acls/serializers/base.py:75 #: acls/serializers/login_acl.py:21 assets/models/cmd_filter.py:24 #: assets/models/label.py:16 audits/models.py:44 audits/models.py:63 @@ -651,7 +651,7 @@ msgstr "ID" msgid "User" msgstr "用户" -#: accounts/serializers/account/account.py:419 +#: accounts/serializers/account/account.py:421 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:98 terminal/notifications.py:146 msgid "Date" @@ -727,7 +727,7 @@ msgstr "自动化任务执行历史" #: accounts/serializers/automations/change_secret.py:155 audits/const.py:52 #: audits/models.py:54 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:39 +#: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:40 #: terminal/const.py:60 terminal/models/session/sharing.py:107 #: tickets/views/approve.py:114 msgid "Success" @@ -968,7 +968,7 @@ msgstr "应用程序" msgid "Can match application" msgstr "匹配应用" -#: assets/api/asset/asset.py:142 +#: assets/api/asset/asset.py:140 msgid "Cannot create asset directly, you should create a host or other" msgstr "不能直接创建资产, 你应该创建主机或其他资产" @@ -2475,6 +2475,12 @@ msgstr "清空手机号码禁用" msgid "Authentication failed (before login check failed): {}" msgstr "认证失败(登录前检查失败): {}" +#: authentication/mixins.py:91 +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:266 msgid "The MFA type ({}) is not enabled" msgstr "该 MFA ({}) 方式没有启用" @@ -3660,16 +3666,8 @@ msgid "Task log" msgstr "任务列表" #: ops/templates/ops/celery_task_log.html:71 -#, fuzzy -#| msgid "Task Center" -msgid "Task type" -msgstr "任务中心" - -#: ops/templates/ops/celery_task_log.html:75 -#, fuzzy -#| msgid "Trigger mode" -msgid "Trigger type" -msgstr "触发模式" +msgid "Task name" +msgstr "任务名称" #: ops/variables.py:24 msgid "The current user`s username of JumpServer" @@ -6707,6 +6705,7 @@ msgid "" " and the current user is not in the user list. Please contact the " "administrator." msgstr "" +"管理员已开启'仅允许已存在用户登录',当前用户不在用户列表中,请联系管理员。" #: users/tasks.py:21 msgid "Check password expired" @@ -7536,5 +7535,10 @@ msgstr "旗舰版" msgid "Community edition" msgstr "社区版" +#, fuzzy +#~| msgid "Trigger mode" +#~ msgid "Trigger type" +#~ msgstr "触发模式" + #~ msgid "Please login with a password and then bind the FeiShu" #~ msgstr "请使用密码登录,然后绑定飞书" diff --git a/apps/ops/templates/ops/celery_task_log.html b/apps/ops/templates/ops/celery_task_log.html index 576d63be4..3dfc23886 100644 --- a/apps/ops/templates/ops/celery_task_log.html +++ b/apps/ops/templates/ops/celery_task_log.html @@ -68,7 +68,7 @@
  • - {% trans 'Task type' %}: + {% trans 'Task name' %}:
  • From 9058a79c5c4f532c21b53afdfdd637aea918be34 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 17 May 2023 11:11:39 +0800 Subject: [PATCH 75/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E7=94=A8=E6=88=B7=E7=99=BB=E5=BD=95=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E9=99=90=E5=88=B6=E6=8F=90=E7=A4=BA=E9=94=99=E8=AF=AF=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#10475)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/users/signal_handlers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/users/signal_handlers.py b/apps/users/signal_handlers.py index b566a600a..7b2d3012f 100644 --- a/apps/users/signal_handlers.py +++ b/apps/users/signal_handlers.py @@ -32,13 +32,13 @@ def check_only_allow_exist_user_auth(created): def user_authenticated_handle(user, created, source, attrs=None, **kwargs): - if not check_only_allow_exist_user_auth(created): - return - if created: user.source = source user.save() + if not check_only_allow_exist_user_auth(created): + return + if not attrs: return From 04e95d378c766ed394bd94c08571d56a05a043ac Mon Sep 17 00:00:00 2001 From: Bai Date: Wed, 17 May 2023 14:02:35 +0800 Subject: [PATCH 76/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=88=97=E8=A1=A8-=E6=B7=BB=E5=8A=A0=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=88=87=E6=8D=A2=E8=87=AA=E7=94=A8=E6=88=B7=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=BF=85=E5=A1=AB=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/accounts/serializers/account/account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index 70d7fce4d..eaf2c8485 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -241,7 +241,8 @@ class AssetAccountBulkSerializer( AccountCreateUpdateSerializerMixin, AuthValidateMixin, serializers.ModelSerializer ): su_from_username = serializers.CharField( - max_length=128, required=False, write_only=True, allow_null=True, label=_("Su from") + max_length=128, required=False, write_only=True, allow_null=True, label=_("Su from"), + allow_blank=True, ) assets = serializers.PrimaryKeyRelatedField(queryset=Asset.objects, many=True, label=_('Assets')) From 1fef273669b87167a668c488413c0db9c5744654 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 17 May 2023 17:29:34 +0800 Subject: [PATCH 77/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E6=97=A5=E6=9C=9F=E4=B8=8D=E8=83=BD=E4=B8=BAnull=20se?= =?UTF-8?q?ttings=20=E6=96=87=E4=BB=B6=E6=B2=A1=E6=9C=89=E6=9D=83=E9=99=90?= =?UTF-8?q?bug=20(#10479)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/jumpserver/rewriting/storage/permissions.py | 1 + apps/tickets/serializers/ticket/apply_asset.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/apps/jumpserver/rewriting/storage/permissions.py b/apps/jumpserver/rewriting/storage/permissions.py index 492a6d06f..51b1c9c40 100644 --- a/apps/jumpserver/rewriting/storage/permissions.py +++ b/apps/jumpserver/rewriting/storage/permissions.py @@ -2,6 +2,7 @@ path_perms_map = { 'xpack': '*', + 'settings': '*', 'replay': 'default', 'applets': 'terminal.view_applet', 'playbooks': 'ops.view_playbook' diff --git a/apps/tickets/serializers/ticket/apply_asset.py b/apps/tickets/serializers/ticket/apply_asset.py index ac59ad13b..98587f266 100644 --- a/apps/tickets/serializers/ticket/apply_asset.py +++ b/apps/tickets/serializers/ticket/apply_asset.py @@ -40,6 +40,8 @@ class ApplyAssetSerializer(BaseApplyAssetSerializer, TicketApplySerializer): ticket_extra_kwargs = TicketApplySerializer.Meta.extra_kwargs extra_kwargs = { 'apply_accounts': {'required': False}, + 'apply_date_start': {'allow_null': False}, + 'apply_date_expired': {'allow_null': False}, } extra_kwargs.update(ticket_extra_kwargs) From 3fa80351e0d2474f219403dd70c88273c411d6e0 Mon Sep 17 00:00:00 2001 From: Aaron3S Date: Wed, 17 May 2023 15:07:34 +0800 Subject: [PATCH 78/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E4=BD=9C?= =?UTF-8?q?=E4=B8=9A=E4=B8=AD=E5=BF=83=E6=8F=90=E7=A4=BA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=90=8D=E6=8E=92=E5=BA=8F=E6=AF=8F=E6=AC=A1=E4=B8=8D=E4=B8=80?= =?UTF-8?q?=E6=A0=B7=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/ops/api/job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index bb427999b..ca76263a4 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -167,5 +167,5 @@ class UsernameHintsAPI(APIView): .filter(asset__in=assets) \ .values('username') \ .annotate(total=Count('username')) \ - .order_by('total')[:10] + .order_by('total', 'username')[:10] return Response(data=top_accounts) From 36aa0d301bbe5d7373c85d9b7b27001cbd24f2e0 Mon Sep 17 00:00:00 2001 From: Aaron3S Date: Wed, 17 May 2023 16:16:34 +0800 Subject: [PATCH 79/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20ops=20?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/api/job.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index ca76263a4..5d7012a8f 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -158,14 +158,17 @@ class UsernameHintsAPI(APIView): def post(self, request, **kwargs): node_ids = request.data.get('nodes', None) asset_ids = request.data.get('assets', []) + query = request.data.get('query', None) + assets = list(Asset.objects.filter(id__in=asset_ids).all()) assets = merge_nodes_and_assets(node_ids, assets, request.user) - top_accounts = Account.objects.exclude(username='root') \ + top_accounts = Account.objects \ .exclude(username__startswith='jms_') \ + .filter(username__icontains=query) \ .filter(asset__in=assets) \ .values('username') \ .annotate(total=Count('username')) \ - .order_by('total', 'username')[:10] + .order_by('total', '-username')[:10] return Response(data=top_accounts) From b0b14fe2e1217c2597dd879dbdbf441af7fc30c9 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 17 May 2023 19:05:28 +0800 Subject: [PATCH 80/88] =?UTF-8?q?fix:=20openid=20=E4=B8=89=E6=96=B9?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=99=90=E5=88=B6bug=20(#10480)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/users/signal_handlers.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/users/signal_handlers.py b/apps/users/signal_handlers.py index 7b2d3012f..2b2054ea3 100644 --- a/apps/users/signal_handlers.py +++ b/apps/users/signal_handlers.py @@ -135,9 +135,6 @@ def on_ldap_create_user(sender, user, ldap_user, **kwargs): @receiver(openid_create_or_update_user) def on_openid_create_or_update_user(sender, request, user, created, name, username, email, **kwargs): - if not check_only_allow_exist_user_auth(created): - return - if created: logger.debug( "Receive OpenID user created signal: {}, " @@ -145,7 +142,11 @@ def on_openid_create_or_update_user(sender, request, user, created, name, userna ) user.source = User.Source.openid.value user.save() - elif not created and settings.AUTH_OPENID_ALWAYS_UPDATE_USER: + + if not check_only_allow_exist_user_auth(created): + return + + if not created and settings.AUTH_OPENID_ALWAYS_UPDATE_USER: logger.debug( "Receive OpenID user updated signal: {}, " "Update user info: {}" From 6c19fd41922930662bfe45dfeb139d64eda725ad Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 18 May 2023 15:42:30 +0800 Subject: [PATCH 81/88] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20luna=20?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=A0=91=E6=95=B0=E9=87=8F=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E4=B8=8D=E5=87=86=E7=A1=AEbug=20(#10492)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/perms/api/user_permission/tree/node_with_asset.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/perms/api/user_permission/tree/node_with_asset.py b/apps/perms/api/user_permission/tree/node_with_asset.py index d1da28fbb..5469c6ed3 100644 --- a/apps/perms/api/user_permission/tree/node_with_asset.py +++ b/apps/perms/api/user_permission/tree/node_with_asset.py @@ -177,8 +177,7 @@ class UserPermedNodeChildrenWithAssetsAsCategoryTreeApi( return [] pid = f'ROOT_{str(assets[0].category).upper()}_{tp}' return self.serialize_assets(assets, pid=pid) - - resource_platforms = assets.values_list('platform_id', flat=True) + resource_platforms = assets.order_by('id').values_list('platform_id', flat=True) node_all = AllTypes.get_tree_nodes(resource_platforms) pattern = re.compile(r'\(0\)?') nodes = [] From 44967b1af17f86816c4f0b1dc09c24575c1c2678 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 18 May 2023 15:43:23 +0800 Subject: [PATCH 82/88] =?UTF-8?q?fix:=20=E5=B9=B3=E5=8F=B0=E5=B1=80?= =?UTF-8?q?=E9=83=A8=E6=9B=B4=E6=96=B0=E4=BC=9A=E8=87=AA=E5=8A=A8=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E5=85=B6=E4=BB=96=E5=B1=9E=E6=80=A7=20(#10484)?= 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/platform.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index 7e5b17a01..80421d41b 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -157,20 +157,6 @@ class PlatformSerializer(WritableNestedModelSerializer): constraints = AllTypes.get_constraints(category, tp) return constraints - def validate(self, attrs): - domain_enabled = attrs.get('domain_enabled', False) and self.constraints.get('domain_enabled', False) - su_enabled = attrs.get('su_enabled', False) and self.constraints.get('su_enabled', False) - automation = attrs.get('automation', {}) - automation['ansible_enabled'] = automation.get('ansible_enabled', False) \ - and self.constraints['automation'].get('ansible_enabled', False) - attrs.update({ - 'domain_enabled': domain_enabled, - 'su_enabled': su_enabled, - 'automation': automation, - }) - self.initial_data['automation'] = automation - return attrs - @classmethod def setup_eager_loading(cls, queryset): queryset = queryset.prefetch_related( @@ -188,6 +174,18 @@ class PlatformSerializer(WritableNestedModelSerializer): self.initial_data['protocols'] = protocols return protocols + def validate_su_enabled(self, su_enabled): + return su_enabled and self.constraints.get('su_enabled', False) + + def validate_domain_enabled(self, domain_enabled): + return domain_enabled and self.constraints.get('domain_enabled', False) + + def validate_automation(self, automation): + automation = automation or {} + automation = automation.get('ansible_enabled', False) \ + and self.constraints['automation'].get('ansible_enabled', False) + return automation + class PlatformOpsMethodSerializer(serializers.Serializer): id = serializers.CharField(read_only=True) From b368b6aef403862e6abf5ccf79c3ed8f5212cc9a 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, 18 May 2023 14:06:00 +0800 Subject: [PATCH 83/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E6=9C=BA=E9=83=A8=E7=BD=B2=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../automations/deploy_applet_host/playbook.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/terminal/automations/deploy_applet_host/playbook.yml b/apps/terminal/automations/deploy_applet_host/playbook.yml index b68f6e06c..730f8e54f 100644 --- a/apps/terminal/automations/deploy_applet_host/playbook.yml +++ b/apps/terminal/automations/deploy_applet_host/playbook.yml @@ -25,6 +25,16 @@ register: rds_install - name: Stop Tinker before install (jumpserver) + ansible.windows.win_powershell: + script: | + if (Get-Process -Name 'tinker' -ErrorAction SilentlyContinue) { + TASKKILL /F /IM tinker.exe /T + } + else { + $Ansible.Changed = $false + } + + - name: Stop Tinkerd before install (jumpserver) ansible.windows.win_powershell: script: | if (Get-Service -Name 'JumpServer Tinker' -ErrorAction SilentlyContinue) { From 0596b74fa1b2a1d7355cf262becbba5d613dbb8a Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 18 May 2023 18:00:58 +0800 Subject: [PATCH 84/88] =?UTF-8?q?fix:=20=E8=B4=A6=E5=8F=B7=E5=88=9B?= =?UTF-8?q?=E5=BB=BAssh=20key=20=E6=A0=A1=E9=AA=8C=20(#10494)?= 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 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index eaf2c8485..4ff294ee1 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -36,6 +36,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer): write_only=True, label=_('Exist policy') ) _template = None + clean_auth_fields: callable class Meta: fields = ['template', 'push_now', 'params', 'on_invalid'] @@ -159,6 +160,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer): def create(self, validated_data): push_now = validated_data.pop('push_now', None) params = validated_data.pop('params', None) + self.clean_auth_fields(validated_data) self.generate_source_data(validated_data) instance, stat = self.do_create(validated_data) self.push_account_if_need(instance, push_now, params, stat) @@ -249,7 +251,7 @@ class AssetAccountBulkSerializer( class Meta: model = Account fields = [ - 'name', 'username', 'secret', 'secret_type', + 'name', 'username', 'secret', 'secret_type', 'passphrase', 'privileged', 'is_active', 'comment', 'template', 'on_invalid', 'push_now', 'assets', 'su_from_username' ] @@ -354,6 +356,7 @@ class AssetAccountBulkSerializer( vd = vd.copy() vd['asset'] = asset try: + self.clean_auth_fields(vd) instance, changed, state = self.perform_create(vd, create_handler) _results[asset] = { 'changed': changed, 'instance': instance.id, 'state': state From 2981bfffb16bbf4b815717daaae2ee012f7582c1 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 18 May 2023 18:33:22 +0800 Subject: [PATCH 85/88] =?UTF-8?q?fix:=20=E7=BB=99=20view=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20export=20=E5=B1=9E=E6=80=A7=20(#10495)?= 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/account.py | 1 + apps/common/drf/renders/base.py | 11 ++++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/accounts/api/account/account.py b/apps/accounts/api/account/account.py index b25dafb9f..6fb699721 100644 --- a/apps/accounts/api/account/account.py +++ b/apps/accounts/api/account/account.py @@ -32,6 +32,7 @@ class AccountViewSet(OrgBulkModelViewSet): 'su_from_accounts': 'accounts.view_account', 'clear_secret': 'accounts.change_account', } + export_as_zip = True @action(methods=['get'], detail=False, url_path='su-from-accounts') def su_from_accounts(self, request, *args, **kwargs): diff --git a/apps/common/drf/renders/base.py b/apps/common/drf/renders/base.py index cc92eb8c2..a9b3b5c8a 100644 --- a/apps/common/drf/renders/base.py +++ b/apps/common/drf/renders/base.py @@ -184,19 +184,16 @@ class BaseFileRenderer(BaseRenderer): self.write_column_titles(column_titles) self.write_rows(rows) self.after_render() - value = self.compress_into_zip_file(view, request, response) + value = self.get_rendered_value() + if getattr(view, 'export_as_zip', False) and self.template == 'export': + value = self.compress_into_zip_file(value, request, response) except Exception as e: logger.debug(e, exc_info=True) value = 'Render error! ({})'.format(self.media_type).encode('utf-8') return value return value - def compress_into_zip_file(self, view, request, response): - value = self.get_rendered_value() - from accounts.models import Account - if str(view.model) not in (str(Account),) or self.template != 'export': - return value - + def compress_into_zip_file(self, value, request, response): filename_pattern = re.compile(r'filename="([^"]+)"') content_disposition = response['Content-Disposition'] match = filename_pattern.search(content_disposition) From 59d964d57aa2a247085af75094af2e533bbb5690 Mon Sep 17 00:00:00 2001 From: Bai Date: Thu, 18 May 2023 19:02:26 +0800 Subject: [PATCH 86/88] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=BB=84?= =?UTF-8?q?=E7=BB=87=E7=AE=A1=E7=90=86=E5=91=98=E4=B8=8D=E8=83=BD=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=B3=BB=E7=BB=9F=E7=AE=A1=E7=90=86=E5=91=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/permissions.py | 10 +++------- apps/users/serializers/user.py | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/users/permissions.py b/apps/users/permissions.py index c7099bb3e..33081c5b3 100644 --- a/apps/users/permissions.py +++ b/apps/users/permissions.py @@ -17,11 +17,7 @@ class UserObjectPermission(permissions.BasePermission): if view.action not in ['update', 'partial_update', 'destroy']: return True - user = request.user - if user.is_superuser: - return True + if not request.user.is_superuser and obj.is_superuser: + return False - system_admin_id = BuiltinRole.system_admin.id - return system_admin_id not in [ - str(r.id) for r in obj.system_roles.all() - ] + return True diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index aa997d27f..eb8d35cd9 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -132,6 +132,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer "last_login", "date_updated" # 日期字段 ] fields_bool = [ + "is_superuser", "is_service_account", "is_valid", "is_expired", "is_active", # 布尔字段 "is_otp_secret_key_bound", "can_public_key_auth", From a17fa5a518691246ca98598f1f53e373a7654643 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 18 May 2023 17:51:50 +0800 Subject: [PATCH 87/88] =?UTF-8?q?perf:=20remoteapp=20rdp=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E7=A6=81=E7=94=A8=E5=A4=8D=E7=94=A8=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/models/connection_token.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index 112fe9331..3604d49e5 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -160,6 +160,7 @@ class ConnectionToken(JMSOrgBaseModel): 'remoteapplicationname:s': app, 'alternate shell:s': app, 'remoteapplicationcmdline:s': cmdline_b64, + 'disableconnectionsharing:i': '1', } return options From 31600ba66c8338db529cb2908ba61652c2480cff Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 18 May 2023 19:06:51 +0800 Subject: [PATCH 88/88] =?UTF-8?q?perf:=20rdp=20=E8=AE=BE=E7=BD=AE=E5=88=86?= =?UTF-8?q?=E8=BE=A8=E7=8E=87=E4=B8=8D=E7=94=9F=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/authentication/api/connection_token.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 4bac0f17a..93e26cc6a 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -87,7 +87,8 @@ class RDPFileClientProtocolURLMixin: if width and height: rdp_options['desktopwidth:i'] = width rdp_options['desktopheight:i'] = height - rdp_options['winposstr:s:'] = f'0,1,0,0,{width},{height}' + rdp_options['winposstr:s'] = f'0,1,0,0,{width},{height}' + rdp_options['dynamic resolution:i'] = '0' # 设置其他选项 rdp_options['session bpp:i'] = os.getenv('JUMPSERVER_COLOR_DEPTH', '32')