diff --git a/apps/accounts/api/account/account.py b/apps/accounts/api/account/account.py index 631669acc..17938358c 100644 --- a/apps/accounts/api/account/account.py +++ b/apps/accounts/api/account/account.py @@ -45,6 +45,7 @@ class AccountViewSet(OrgBulkModelViewSet): accounts = asset.accounts.all() else: accounts = [] + accounts = self.filter_queryset(accounts) serializer = serializers.AccountSerializer(accounts, many=True) return Response(data=serializer.data) diff --git a/apps/accounts/api/account/task.py b/apps/accounts/api/account/task.py index 050e69052..e5d4b36bb 100644 --- a/apps/accounts/api/account/task.py +++ b/apps/accounts/api/account/task.py @@ -3,6 +3,7 @@ from rest_framework.response import Response from accounts import serializers from accounts.tasks import verify_accounts_connectivity_task, push_accounts_to_assets_task +from assets.exceptions import NotSupportedTemporarilyError __all__ = [ 'AccountsTaskCreateAPI', @@ -28,6 +29,11 @@ class AccountsTaskCreateAPI(CreateAPIView): if data['action'] == 'push': task = push_accounts_to_assets_task.delay(account_ids) else: + account = accounts[0] + asset = account.asset + if not asset.auto_info['ansible_enabled'] or \ + not asset.auto_info['ping_enabled']: + raise NotSupportedTemporarilyError() task = verify_accounts_connectivity_task.delay(account_ids) data = getattr(serializer, '_data', {}) diff --git a/apps/accounts/automations/change_secret/manager.py b/apps/accounts/automations/change_secret/manager.py index c076ab56b..a6c14fa23 100644 --- a/apps/accounts/automations/change_secret/manager.py +++ b/apps/accounts/automations/change_secret/manager.py @@ -86,6 +86,10 @@ class ChangeSecretManager(AccountBasePlaybookManager): accounts = accounts.filter(username__in=self.snapshot_account_usernames) accounts = accounts.filter(secret_type=self.secret_type) + if not accounts: + print('没有发现待改密账号: %s 用户名: %s 类型: %s' % (asset.name, account.username, self.secret_type)) + return [] + method_attr = getattr(automation, self.method_type() + '_method') method_hosts = self.method_hosts_mapper[method_attr] method_hosts = [h for h in method_hosts if h != host['name']] @@ -137,8 +141,10 @@ class ChangeSecretManager(AccountBasePlaybookManager): recorder.status = 'success' recorder.date_finished = timezone.now() recorder.save() - print('recorder.new_secret', recorder.new_secret) account = recorder.account + if not account: + print("Account not found, deleted ?", recorder) + return account.secret = recorder.new_secret account.save(update_fields=['secret']) diff --git a/apps/accounts/automations/push_account/manager.py b/apps/accounts/automations/push_account/manager.py index f2f21c51a..42a53fcb1 100644 --- a/apps/accounts/automations/push_account/manager.py +++ b/apps/accounts/automations/push_account/manager.py @@ -36,7 +36,7 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager): def get_accounts(self, privilege_account, accounts: QuerySet): if not privilege_account: - logger.debug(f'not privilege account') + print(f'not privilege account') return [] snapshot_account_usernames = self.execution.snapshot['accounts'] if '*' in snapshot_account_usernames: diff --git a/apps/accounts/automations/verify_account/database/mongodb/main.yml b/apps/accounts/automations/verify_account/database/mongodb/main.yml index 261fe63ca..483bfc127 100644 --- a/apps/accounts/automations/verify_account/database/mongodb/main.yml +++ b/apps/accounts/automations/verify_account/database/mongodb/main.yml @@ -6,8 +6,8 @@ tasks: - name: Verify account mongodb_ping: - login_user: "{{ jms_account.username }}" - login_password: "{{ jms_account.secret }}" + login_user: "{{ account.username }}" + login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" login_database: "{{ jms_asset.spec_info.db_name }}" diff --git a/apps/accounts/automations/verify_account/database/oracle/main.yml b/apps/accounts/automations/verify_account/database/oracle/main.yml index 12896f09a..3da515e4f 100644 --- a/apps/accounts/automations/verify_account/database/oracle/main.yml +++ b/apps/accounts/automations/verify_account/database/oracle/main.yml @@ -6,9 +6,9 @@ tasks: - name: Verify account oracle_ping: - login_user: "{{ jms_account.username }}" - login_password: "{{ jms_account.secret }}" + login_user: "{{ account.username }}" + login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" login_database: "{{ jms_asset.spec_info.db_name }}" - mode: "{{ jms_account.mode }}" + mode: "{{ account.mode }}" diff --git a/apps/accounts/automations/verify_account/database/sqlserver/main.yml b/apps/accounts/automations/verify_account/database/sqlserver/main.yml index bb079fa59..fa6c78ed7 100644 --- a/apps/accounts/automations/verify_account/database/sqlserver/main.yml +++ b/apps/accounts/automations/verify_account/database/sqlserver/main.yml @@ -6,8 +6,8 @@ tasks: - name: Verify account community.general.mssql_script: - login_user: "{{ jms_account.username }}" - login_password: "{{ jms_account.secret }}" + login_user: "{{ account.username }}" + login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" name: '{{ jms_asset.spec_info.db_name }}' diff --git a/apps/accounts/migrations/0008_alter_account_options.py b/apps/accounts/migrations/0008_alter_account_options.py deleted file mode 100644 index 949840740..000000000 --- a/apps/accounts/migrations/0008_alter_account_options.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.14 on 2023-02-21 05:13 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0007_alter_account_options'), - ] - - operations = [ - migrations.AlterModelOptions( - name='account', - options={'permissions': [('view_accountsecret', 'Can view asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret'), ('verify_account', 'Can verify account'), ('push_account', 'Can push account')], 'verbose_name': 'Account'}, - ), - ] diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 7367c53de..00934b759 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -68,6 +68,9 @@ class Account(AbsConnectivity, BaseAccount): ('push_account', _('Can push account')), ] + def __str__(self): + return '{}'.format(self.username) + @lazyproperty def platform(self): return self.asset.platform @@ -78,9 +81,6 @@ class Account(AbsConnectivity, BaseAccount): return self.username return self.name - def __str__(self): - return '{}'.format(self.username) - @lazyproperty def has_secret(self): return bool(self.secret) @@ -99,14 +99,6 @@ class Account(AbsConnectivity, BaseAccount): """ 排除自己和以自己为 su-from 的账号 """ return self.asset.accounts.exclude(id=self.id).exclude(su_from=self) - def secret_changed(self): - history = self.history.first() - if not history: - return True - if history.secret != self.secret or history.secret_type != self.secret_type: - return True - return False - class AccountTemplate(BaseAccount): class Meta: diff --git a/apps/accounts/models/base.py b/apps/accounts/models/base.py index cd1fef5e8..7233a12a2 100644 --- a/apps/accounts/models/base.py +++ b/apps/accounts/models/base.py @@ -109,7 +109,7 @@ class BaseAccount(JMSOrgBaseModel): @property def private_key_path(self): - if not self.secret_type != SecretType.SSH_KEY \ + if self.secret_type != SecretType.SSH_KEY \ or not self.secret \ or not self.private_key: return None diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index 1f9c143bd..8cf92671e 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -43,7 +43,7 @@ class AccountSerializerCreateValidateMixin: def push_account(instance, push_now): if not push_now: return - push_accounts_to_assets_task.delay([instance.id], [instance.asset_id]) + push_accounts_to_assets_task.delay([instance.id]) def create(self, validated_data): push_now = validated_data.pop('push_now', None) @@ -102,7 +102,7 @@ class AccountSerializer(AccountSerializerCreateMixin, BaseAccountSerializer): class Meta(BaseAccountSerializer.Meta): model = Account fields = BaseAccountSerializer.Meta.fields \ - + ['su_from', 'version', 'asset'] \ + + ['su_from', 'asset'] \ + ['template', 'push_now', 'source'] extra_kwargs = { **BaseAccountSerializer.Meta.extra_kwargs, diff --git a/apps/accounts/serializers/automations/base.py b/apps/accounts/serializers/automations/base.py index b8b5339d7..6b3792559 100644 --- a/apps/accounts/serializers/automations/base.py +++ b/apps/accounts/serializers/automations/base.py @@ -38,6 +38,8 @@ class BaseAutomationSerializer(PeriodTaskSerializerMixin, BulkOrgResourceModelSe } def validate_name(self, name): + if self.instance: + return name if BaseAutomation.objects.filter(name=name, type=self.model_type).exists(): raise serializers.ValidationError(_('Name already exists')) return name diff --git a/apps/accounts/signal_handlers.py b/apps/accounts/signal_handlers.py index df2b0e5b7..bb4eaedb8 100644 --- a/apps/accounts/signal_handlers.py +++ b/apps/accounts/signal_handlers.py @@ -1,17 +1,3 @@ -from django.db.models.signals import pre_save -from django.dispatch import receiver - from common.utils import get_logger -from .models import Account logger = get_logger(__name__) - - -@receiver(pre_save, sender=Account) -def on_account_pre_create(sender, instance, update_fields=(), **kwargs): - # 这是创建时 - if instance.version == 0 or instance.secret_changed(): - instance.version += 1 - - # 即使在 root 组织也不怕 - instance.org_id = instance.asset.org_id diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index 5b6b9084b..5b67e9114 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -8,12 +8,10 @@ from rest_framework.response import Response from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connectivity_task from assets import serializers +from assets.exceptions import NotSupportedTemporarilyError from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend from assets.models import Asset, Gateway -from assets.tasks import ( - test_assets_connectivity_manual, - update_assets_hardware_info_manual -) +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.utils import get_logger, is_uuid @@ -154,6 +152,10 @@ class AssetsTaskMixin: if data["action"] == "refresh": task = update_assets_hardware_info_manual(assets) else: + asset = assets[0] + if not asset.auto_info['ansible_enabled'] or \ + not asset.auto_info['ping_enabled']: + raise NotSupportedTemporarilyError() task = test_assets_connectivity_manual(assets) return task @@ -205,9 +207,9 @@ class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): asset_ids = [asset.id] account_ids = accounts.values_list("id", flat=True) if action == "push_account": - task = push_accounts_to_assets_task.delay(account_ids, asset_ids) + task = push_accounts_to_assets_task.delay(account_ids) elif action == "test_account": - task = verify_accounts_connectivity_task.delay(account_ids, asset_ids) + task = verify_accounts_connectivity_task.delay(account_ids) else: task = None return task diff --git a/apps/assets/automations/base/manager.py b/apps/assets/automations/base/manager.py index 7ef2e528c..6c8dc189a 100644 --- a/apps/assets/automations/base/manager.py +++ b/apps/assets/automations/base/manager.py @@ -67,7 +67,7 @@ class BasePlaybookManager: if not os.path.exists(path): os.makedirs(path, exist_ok=True, mode=0o755) if settings.DEBUG_DEV: - logger.debug('Ansible runtime dir: {}'.format(path)) + print(f'Ansible runtime dir: {path}') return path @staticmethod @@ -156,10 +156,9 @@ class BasePlaybookManager: return sub_playbook_path def get_runners(self): - # TODO 临时打印一下 找一下打印不出日志的原因 - print('ansible runner: 任务开始执行') assets_group_by_platform = self.get_assets_group_by_platform() - print('ansible runner: 获取资产分组', assets_group_by_platform) + if settings.DEBUG_DEV: + print("assets_group_by_platform: {}".format(assets_group_by_platform)) runners = [] for platform, assets in assets_group_by_platform.items(): assets_bulked = [assets[i:i + self.bulk_size] for i in range(0, len(assets), self.bulk_size)] @@ -213,6 +212,7 @@ class BasePlaybookManager: def file_to_json(path): with open(path, 'r') as f: d = json.load(f) + return d @staticmethod diff --git a/apps/assets/exceptions.py b/apps/assets/exceptions.py index 099e68f11..ad22b6339 100644 --- a/apps/assets/exceptions.py +++ b/apps/assets/exceptions.py @@ -1,6 +1,12 @@ +from django.utils.translation import ugettext_lazy as _ from rest_framework import status + from common.exceptions import JMSException class NodeIsBeingUpdatedByOthers(JMSException): status_code = status.HTTP_409_CONFLICT + + +class NotSupportedTemporarilyError(JMSException): + default_detail = _("This function is not supported temporarily") diff --git a/apps/assets/migrations/0109_alter_asset_options.py b/apps/assets/migrations/0109_alter_asset_options.py index 859b1ca0c..4a1c93a15 100644 --- a/apps/assets/migrations/0109_alter_asset_options.py +++ b/apps/assets/migrations/0109_alter_asset_options.py @@ -12,6 +12,6 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='asset', - options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('push_assetaccount', 'Can push account to asset'), ('test_account', 'Can verify account'), ('match_asset', 'Can match asset'), ('change_assettonode', 'Can change asset nodes')], 'verbose_name': 'Asset'}, + options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('match_asset', 'Can match asset'), ('change_assetnodes', 'Can change asset nodes')], 'verbose_name': 'Asset'}, ), ] diff --git a/apps/assets/migrations/0110_alter_asset_options.py b/apps/assets/migrations/0110_alter_asset_options.py deleted file mode 100644 index fa5495e33..000000000 --- a/apps/assets/migrations/0110_alter_asset_options.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.14 on 2023-02-21 05:11 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0109_alter_asset_options'), - ] - - operations = [ - migrations.AlterModelOptions( - name='asset', - options={'ordering': ['name'], 'permissions': [('refresh_assethardwareinfo', 'Can refresh asset hardware info'), ('test_assetconnectivity', 'Can test asset connectivity'), ('match_asset', 'Can match asset'), ('change_assetnodes', 'Can change asset nodes')], 'verbose_name': 'Asset'}, - ), - ] diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 9c31ee695..c5a9ed02d 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -277,6 +277,8 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali @atomic def update(self, instance, validated_data): + if not validated_data.get('accounts'): + validated_data.pop('accounts', None) nodes_display = validated_data.pop('nodes_display', '') instance = super().update(instance, validated_data) self.perform_nodes_display_create(instance, nodes_display) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index f7c555504..b192cd993 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -530,7 +530,7 @@ class Config(dict): 'PERIOD_TASK_ENABLED': True, # 导航栏 帮助 - 'HELP_DOCUMENT_URL': 'http://docs.jumpserver.org', + 'HELP_DOCUMENT_URL': 'https://docs.jumpserver.org/zh/v3/', 'HELP_SUPPORT_URL': 'http://www.jumpserver.org/support/', 'FORGOT_PASSWORD_URL': '', diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index df0751934..781aa1a90 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:331188bb5169bb463da018a635589e12a2136d476db264ac7e5d6e5d63ca474a -size 135916 +oid sha256:af57d16430705feb02ebbb99fc3a2f5fc3bab69209f558aa4d69b1e8055a6f5f +size 136036 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 4e86c7f0c..6e38486bd 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-02-21 18:29+0800\n" +"POT-Creation-Date: 2023-02-21 22:44+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -908,7 +908,7 @@ msgstr "アプリケーション" msgid "Can match application" msgstr "アプリケーションを一致させることができます" -#: assets/api/asset/asset.py:144 +#: assets/api/asset/asset.py:142 msgid "Cannot create asset directly, you should create a host or other" msgstr "" "資産を直接作成することはできません。ホストまたはその他を作成する必要がありま" @@ -1051,6 +1051,10 @@ msgstr "基本" msgid "Script" msgstr "脚本" +#: assets/exceptions.py:12 +msgid "This function is not supported temporarily" +msgstr "この機能は一時的にサポートされていません" + #: assets/models/_user.py:25 msgid "SSH private key" msgstr "SSH秘密鍵" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index cd68677c8..02f116670 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:2cdc2b875c98f41bd698833a989195d8cc4245f39f52b7eab41ad4d95075cb17 -size 111666 +oid sha256:3b6ee4a378810f2515be5020e3fa0b1297e1c207260ca60bb14dc5407ca19c43 +size 111750 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index dc0f1de28..c3f661344 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-02-21 18:29+0800\n" +"POT-Creation-Date: 2023-02-21 22:44+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler <ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n" @@ -902,7 +902,7 @@ msgstr "应用程序" msgid "Can match application" msgstr "匹配应用" -#: assets/api/asset/asset.py:144 +#: assets/api/asset/asset.py:142 msgid "Cannot create asset directly, you should create a host or other" msgstr "不能直接创建资产, 你应该创建主机或其他资产" @@ -1043,6 +1043,10 @@ msgstr "基本" msgid "Script" msgstr "脚本" +#: assets/exceptions.py:12 +msgid "This function is not supported temporarily" +msgstr "暂时不支持此功能" + #: assets/models/_user.py:25 msgid "SSH private key" msgstr "SSH密钥" diff --git a/apps/ops/api/playbook.py b/apps/ops/api/playbook.py index c26643fe5..b3c1fe564 100644 --- a/apps/ops/api/playbook.py +++ b/apps/ops/api/playbook.py @@ -6,6 +6,7 @@ from django.conf import settings from django.shortcuts import get_object_or_404 from rest_framework import status +from common.exceptions import JMSException from orgs.mixins.api import OrgBulkModelViewSet from ..exception import PlaybookNoValidEntry from ..models import Playbook @@ -39,7 +40,11 @@ class PlaybookViewSet(OrgBulkModelViewSet): if 'multipart/form-data' in self.request.headers['Content-Type']: src_path = os.path.join(settings.MEDIA_ROOT, instance.path.name) dest_path = os.path.join(settings.DATA_DIR, "ops", "playbook", instance.id.__str__()) - unzip_playbook(src_path, dest_path) + try: + unzip_playbook(src_path, dest_path) + except RuntimeError as e: + raise JMSException(code='invalid_playbook_file', detail={"msg": "Unzip failed"}) + if 'main.yml' not in os.listdir(dest_path): raise PlaybookNoValidEntry diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index c71fe95c2..ee83c8e52 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -111,7 +111,8 @@ class Applet(JMSBaseModel): return instance def select_host_account(self): - hosts = list(self.hosts.all()) + # 选择激活的发布机 + hosts = list(self.hosts.filter(is_active=True).all()) if not hosts: return None