diff --git a/apps/assets/api/__init__.py b/apps/assets/api/__init__.py index b0efb6af8..a4404e290 100644 --- a/apps/assets/api/__init__.py +++ b/apps/assets/api/__init__.py @@ -6,3 +6,4 @@ from .node import * from .domain import * from .cmd_filter import * from .asset_user import * +from .gathered_user import * diff --git a/apps/assets/api/admin_user.py b/apps/assets/api/admin_user.py index 9db193643..fd10e6129 100644 --- a/apps/assets/api/admin_user.py +++ b/apps/assets/api/admin_user.py @@ -19,7 +19,7 @@ from rest_framework import generics from rest_framework.response import Response from orgs.mixins.api import OrgBulkModelViewSet -from common.mixins import IDInCacheFilterMixin +from common.mixins import CommonApiMixin from common.utils import get_logger from ..hands import IsOrgAdmin from ..models import AdminUser, Asset diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py index a6280ff56..86e3d8fd2 100644 --- a/apps/assets/api/asset.py +++ b/apps/assets/api/asset.py @@ -5,9 +5,7 @@ import random from rest_framework import generics from rest_framework.response import Response -from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 -from django.db.models import Q from common.utils import get_logger, get_object_or_none from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser @@ -16,7 +14,7 @@ from ..models import Asset, AdminUser, Node from .. import serializers from ..tasks import update_asset_hardware_info_manual, \ test_asset_connectivity_manual -from ..utils import LabelFilter +from ..filters import AssetByNodeFilterBackend, LabelFilterBackend logger = get_logger(__file__) @@ -27,7 +25,7 @@ __all__ = [ ] -class AssetViewSet(LabelFilter, OrgBulkModelViewSet): +class AssetViewSet(OrgBulkModelViewSet): """ API endpoint that allows Asset to be viewed or edited. """ @@ -37,7 +35,7 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet): queryset = Asset.objects.all() serializer_class = serializers.AssetSerializer permission_classes = (IsOrgAdminOrAppUser,) - success_message = _("%(hostname)s was %(action)s successfully") + extra_filter_backends = [AssetByNodeFilterBackend, LabelFilterBackend] def set_assets_node(self, assets): if not isinstance(assets, list): @@ -54,30 +52,6 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet): assets = serializer.save() self.set_assets_node(assets) - def filter_node(self, queryset): - node_id = self.request.query_params.get("node_id") - if not node_id: - return queryset - - node = get_object_or_404(Node, id=node_id) - show_current_asset = self.request.query_params.get("show_current_asset") in ('1', 'true') - - # 当前节点是顶层节点, 并且仅显示直接资产 - if node.is_org_root() and show_current_asset: - queryset = queryset.filter( - Q(nodes=node_id) | Q(nodes__isnull=True) - ).distinct() - # 当前节点是顶层节点,显示所有资产 - elif node.is_org_root() and not show_current_asset: - return queryset - # 当前节点不是鼎城节点,只显示直接资产 - elif not node.is_org_root() and show_current_asset: - queryset = queryset.filter(nodes=node) - else: - children = node.get_all_children(with_self=True) - queryset = queryset.filter(nodes__in=children).distinct() - return queryset - def filter_admin_user_id(self, queryset): admin_user_id = self.request.query_params.get('admin_user_id') if not admin_user_id: @@ -88,7 +62,6 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet): def filter_queryset(self, queryset): queryset = super().filter_queryset(queryset) - queryset = self.filter_node(queryset) queryset = self.filter_admin_user_id(queryset) return queryset diff --git a/apps/assets/api/asset_user.py b/apps/assets/api/asset_user.py index ec71e87f2..7ec1485b7 100644 --- a/apps/assets/api/asset_user.py +++ b/apps/assets/api/asset_user.py @@ -10,7 +10,7 @@ from django.http import Http404 from common.permissions import IsOrgAdminOrAppUser, NeedMFAVerify from common.utils import get_object_or_none, get_logger -from common.mixins import IDInCacheFilterMixin +from common.mixins import CommonApiMixin from ..backends import AssetUserManager from ..models import Asset, Node, SystemUser, AdminUser from .. import serializers @@ -52,7 +52,7 @@ class AssetUserSearchBackend(filters.BaseFilterBackend): return _queryset -class AssetUserViewSet(IDInCacheFilterMixin, BulkModelViewSet): +class AssetUserViewSet(CommonApiMixin, BulkModelViewSet): serializer_class = serializers.AssetUserSerializer permission_classes = [IsOrgAdminOrAppUser] http_method_names = ['get', 'post'] diff --git a/apps/assets/api/gathered_user.py b/apps/assets/api/gathered_user.py new file mode 100644 index 000000000..2f844d9e0 --- /dev/null +++ b/apps/assets/api/gathered_user.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# + +from orgs.mixins.api import OrgModelViewSet +from assets.models import GatheredUser +from common.permissions import IsOrgAdmin + +from ..serializers import GatheredUserSerializer + + +__all__ = ['GatheredUserViewSet'] + + +class GatheredUserViewSet(OrgModelViewSet): + queryset = GatheredUser.objects.all() + serializer_class = GatheredUserSerializer + permission_classes = [IsOrgAdmin] + + filter_fields = ['asset', 'username', 'present'] + search_fields = ['username', 'asset__ip', 'asset__hostname'] + + diff --git a/apps/assets/filters.py b/apps/assets/filters.py new file mode 100644 index 000000000..3636f76e2 --- /dev/null +++ b/apps/assets/filters.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# + +import coreapi +from rest_framework import filters +from django.db.models import Q + +from common.utils import dict_get_any, is_uuid, get_object_or_none +from .models import Node, Label + + +class AssetByNodeFilterBackend(filters.BaseFilterBackend): + fields = ['node', 'all'] + + # def filter_node(self, queryset): + # node_id = self.request.query_params.get("node_id") + # if not node_id: + # return queryset + # + # node = get_object_or_404(Node, id=node_id) + # show_current_asset = self.request.query_params.get("show_current_asset") in ('1', 'true') + # + # # 当前节点是顶层节点, 并且仅显示直接资产 + # if node.is_org_root() and show_current_asset: + # queryset = queryset.filter( + # Q(nodes=node_id) | Q(nodes__isnull=True) + # ).distinct() + # # 当前节点是顶层节点,显示所有资产 + # elif node.is_org_root() and not show_current_asset: + # return queryset + # # 当前节点不是鼎城节点,只显示直接资产 + # elif not node.is_org_root() and show_current_asset: + # queryset = queryset.filter(nodes=node) + # else: + # children = node.get_all_children(with_self=True) + # queryset = queryset.filter(nodes__in=children).distinct() + # return queryset + + def get_schema_fields(self, view): + return [ + coreapi.Field( + name=field, location='query', required=False, + type='string', example='', description='' + ) + for field in self.fields + ] + + def filter_queryset(self, request, queryset, view): + node_id = dict_get_any(request.query_params, ['node', 'node_id']) + if not node_id: + return queryset + query_all_arg = request.query_params.get('all') + show_current_asset_arg = request.query_params.get('show_current_asset') + + query_all = query_all_arg == '1' + if show_current_asset_arg is not None: + query_all = show_current_asset_arg != '1' + + if is_uuid(node_id): + node = get_object_or_none(Node, id=node_id) + else: + node = get_object_or_none(Node, key=node_id) + + if not node: + return queryset.none() + + if query_all: + pattern = node.get_all_children_pattern(with_self=True) + else: + pattern = node.get_children_key_pattern(with_self=True) + return queryset.filter(nodes__key__regex=pattern) + + +class LabelFilterBackend(filters.BaseFilterBackend): + sep = '#' + query_arg = 'label' + + def get_schema_fields(self, view): + example = self.sep.join(['os', 'linux']) + return [ + coreapi.Field( + name=self.query_arg, location='query', required=False, + type='string', example=example, description='' + ) + ] + + def get_query_labels(self, request): + labels_query = request.query_params.getlist(self.query_arg) + if not labels_query: + return None + + q = None + for kv in labels_query: + if self.sep not in kv: + continue + key, value = kv.strip().split(self.sep)[:2] + if not all([key, value]): + continue + if q: + q |= Q(name=key, value=value) + else: + q = Q(name=key, value=value) + if not q: + return [] + labels = Label.objects.filter(q, is_active=True)\ + .values_list('id', flat=True) + return labels + + def filter_queryset(self, request, queryset, view): + labels = self.get_query_labels(request) + if labels is None: + return queryset + if len(labels) == 0: + return queryset.none() + for label in labels: + queryset = queryset.filter(labels=label) + return queryset + + diff --git a/apps/assets/migrations/0039_authbook_is_active.py b/apps/assets/migrations/0039_authbook_is_active.py new file mode 100644 index 000000000..3f600cfac --- /dev/null +++ b/apps/assets/migrations/0039_authbook_is_active.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.7 on 2019-09-17 12:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0038_auto_20190911_1634'), + ] + + operations = [ + migrations.AddField( + model_name='authbook', + name='is_active', + field=models.BooleanField(default=True, verbose_name='Is active'), + ), + ] diff --git a/apps/assets/migrations/0040_auto_20190917_2056.py b/apps/assets/migrations/0040_auto_20190917_2056.py new file mode 100644 index 000000000..2957a1f31 --- /dev/null +++ b/apps/assets/migrations/0040_auto_20190917_2056.py @@ -0,0 +1,36 @@ +# Generated by Django 2.1.7 on 2019-09-17 12:56 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0039_authbook_is_active'), + ] + + operations = [ + migrations.AlterField( + model_name='adminuser', + name='username', + field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'), + ), + migrations.AlterField( + model_name='authbook', + name='username', + field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'), + ), + migrations.AlterField( + model_name='gateway', + name='username', + field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'), + ), + migrations.AlterField( + model_name='systemuser', + name='username', + field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'), + ), + ] diff --git a/apps/assets/migrations/0041_gathereduser.py b/apps/assets/migrations/0041_gathereduser.py new file mode 100644 index 000000000..9accee746 --- /dev/null +++ b/apps/assets/migrations/0041_gathereduser.py @@ -0,0 +1,28 @@ +# Generated by Django 2.1.7 on 2019-09-18 04:10 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0040_auto_20190917_2056'), + ] + + operations = [ + migrations.CreateModel( + name='GatheredUser', + fields=[ + ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('username', models.CharField(blank=True, db_index=True, max_length=32, verbose_name='Username')), + ('present', models.BooleanField(default=True)), + ('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset')), + ], + options={'ordering': ['asset'], 'verbose_name': 'GatherUser'}, + ), + ] diff --git a/apps/assets/models/__init__.py b/apps/assets/models/__init__.py index 4b97a5929..c69f19bf6 100644 --- a/apps/assets/models/__init__.py +++ b/apps/assets/models/__init__.py @@ -9,3 +9,4 @@ from .cmd_filter import * from .authbook import * from .utils import * from .authbook import * +from .gathered_user import * diff --git a/apps/assets/models/authbook.py b/apps/assets/models/authbook.py index 01c8d4630..991729250 100644 --- a/apps/assets/models/authbook.py +++ b/apps/assets/models/authbook.py @@ -13,7 +13,7 @@ __all__ = ['AuthBook'] class AuthBookQuerySet(models.QuerySet): def latest_version(self): - return self.filter(is_latest=True) + return self.filter(is_latest=True).filter(is_active=True) class AuthBookManager(OrgManager): @@ -24,6 +24,7 @@ class AuthBook(AssetUser): asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset')) is_latest = models.BooleanField(default=False, verbose_name=_('Latest version')) version = models.IntegerField(default=1, verbose_name=_('Version')) + is_active = models.BooleanField(default=True, verbose_name=_("Is active")) objects = AuthBookManager.from_queryset(AuthBookQuerySet)() backend = "db" @@ -34,25 +35,25 @@ class AuthBook(AssetUser): class Meta: verbose_name = _('AuthBook') - def _set_latest(self): - self._remove_pre_obj_latest() + def set_to_latest(self): + self.remove_pre_latest() self.is_latest = True self.save() - def _get_pre_obj(self): + def get_pre_latest(self): pre_obj = self.__class__.objects.filter( username=self.username, asset=self.asset ).latest_version().first() return pre_obj - def _remove_pre_obj_latest(self): - pre_obj = self._get_pre_obj() + def remove_pre_latest(self): + pre_obj = self.get_pre_latest() if pre_obj: pre_obj.is_latest = False pre_obj.save() - def _set_version(self): - pre_obj = self._get_pre_obj() + def set_version(self): + pre_obj = self.get_pre_latest() if pre_obj: self.version = pre_obj.version + 1 else: @@ -60,8 +61,8 @@ class AuthBook(AssetUser): self.save() def set_version_and_latest(self): - self._set_version() - self._set_latest() + self.set_version() + self.set_to_latest() def get_related_assets(self): return [self.asset] diff --git a/apps/assets/models/base.py b/apps/assets/models/base.py index 759285b80..2e591c940 100644 --- a/apps/assets/models/base.py +++ b/apps/assets/models/base.py @@ -26,7 +26,7 @@ logger = get_logger(__file__) class AssetUser(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, verbose_name=_('Name')) - username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric]) + username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric], db_index=True) password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key')) public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key')) diff --git a/apps/assets/models/gathered_user.py b/apps/assets/models/gathered_user.py new file mode 100644 index 000000000..305bced37 --- /dev/null +++ b/apps/assets/models/gathered_user.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# +import uuid +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from orgs.mixins.models import OrgModelMixin + +__all__ = ['GatheredUser'] + + +class GatheredUser(OrgModelMixin): + id = models.UUIDField(default=uuid.uuid4, primary_key=True) + asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE) + username = models.CharField(max_length=32, blank=True, db_index=True, + verbose_name=_('Username')) + present = models.BooleanField(default=True) + date_created = models.DateTimeField(auto_now_add=True, + verbose_name=_("Date created")) + date_updated = models.DateTimeField(auto_now=True, + verbose_name=_("Date updated")) + + @property + def hostname(self): + return self.asset.hostname + + @property + def ip(self): + return self.asset.ip + + class Meta: + verbose_name = _('GatherUser') + ordering = ['asset'] + + def __str__(self): + return '{}: {}'.format(self.asset.hostname, self.username) + + + diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 243c352a7..c294ff2ed 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -116,16 +116,24 @@ class FamilyMixin: def all_children(self): return self.get_all_children(with_self=False) - def get_children(self, with_self=False): + def get_children_key_pattern(self, with_self=False): pattern = r'^{0}:[0-9]+$'.format(self.key) if with_self: pattern += r'|^{0}$'.format(self.key) + return pattern + + def get_children(self, with_self=False): + pattern = self.get_children_key_pattern(with_self=with_self) return Node.objects.filter(key__regex=pattern) - def get_all_children(self, with_self=False): + def get_all_children_pattern(self, with_self=False): pattern = r'^{0}:'.format(self.key) if with_self: pattern += r'|^{0}$'.format(self.key) + return pattern + + def get_all_children(self, with_self=False): + pattern = self.get_all_children_pattern(with_self=with_self) children = Node.objects.filter(key__regex=pattern) return children diff --git a/apps/assets/serializers/__init__.py b/apps/assets/serializers/__init__.py index f9866688d..9c86ef407 100644 --- a/apps/assets/serializers/__init__.py +++ b/apps/assets/serializers/__init__.py @@ -9,3 +9,4 @@ from .node import * from .domain import * from .cmd_filter import * from .asset_user import * +from .gathered_user import * diff --git a/apps/assets/serializers/asset_user.py b/apps/assets/serializers/asset_user.py index 0e342e8b2..a93d2b2c3 100644 --- a/apps/assets/serializers/asset_user.py +++ b/apps/assets/serializers/asset_user.py @@ -53,6 +53,7 @@ class AssetUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): if not validated_data.get("name") and validated_data.get("username"): validated_data["name"] = validated_data["username"] instance = AssetUserManager.create(**validated_data) + instance.set_version_and_latest() return instance diff --git a/apps/assets/serializers/gathered_user.py b/apps/assets/serializers/gathered_user.py new file mode 100644 index 000000000..517b6a596 --- /dev/null +++ b/apps/assets/serializers/gathered_user.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# + +from ..models import GatheredUser + +from orgs.mixins.serializers import OrgResourceModelSerializerMixin + + +class GatheredUserSerializer(OrgResourceModelSerializerMixin): + class Meta: + model = GatheredUser + fields = [ + 'id', 'asset', 'hostname', 'ip', 'username', + 'present', 'date_created', 'date_updated' + ] + read_only_fields = fields diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index f4ff2ce04..2c980d22c 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -9,7 +9,7 @@ from django.dispatch import receiver from common.utils import get_logger from common.decorator import on_transaction_commit -from .models import Asset, SystemUser, Node, AuthBook +from .models import Asset, SystemUser, Node from .tasks import ( update_assets_hardware_info_util, test_asset_connectivity_util, @@ -190,10 +190,3 @@ def on_asset_nodes_remove(sender, instance=None, action='', model=None, def on_node_update_or_created(sender, **kwargs): # 刷新节点 Node.refresh_nodes() - - -@receiver(post_save, sender=AuthBook) -def on_auth_book_created(sender, instance=None, created=False, **kwargs): - if created: - logger.debug('Receive create auth book object signal.') - instance.set_version_and_latest() diff --git a/apps/assets/tasks.py b/apps/assets/tasks.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/assets/tasks/gather_asset_hardware_info.py b/apps/assets/tasks/gather_asset_hardware_info.py index 31262f699..2c0d75e99 100644 --- a/apps/assets/tasks/gather_asset_hardware_info.py +++ b/apps/assets/tasks/gather_asset_hardware_info.py @@ -103,7 +103,7 @@ def update_assets_hardware_info_util(assets, task_name=None): ) result = task.run() set_assets_hardware_info(assets, result) - return result + return True @shared_task(queue="ansible") diff --git a/apps/assets/tasks/gather_asset_users.py b/apps/assets/tasks/gather_asset_users.py index a48049d5a..2662e5c76 100644 --- a/apps/assets/tasks/gather_asset_users.py +++ b/apps/assets/tasks/gather_asset_users.py @@ -1,17 +1,102 @@ # ~*~ coding: utf-8 ~*~ +import re from collections import defaultdict from celery import shared_task + from django.utils.translation import ugettext as _ +from orgs.utils import tmp_to_org +from common.utils import get_logger +from ..models import GatheredUser, Node +from .utils import clean_hosts from . import const +__all__ = ['gather_asset_users', 'gather_nodes_asset_users'] +logger = get_logger(__name__) +space = re.compile('\s+') +ignore_login_shell = re.compile(r'nologin$|sync$|shutdown$|halt$') + + +def parse_linux_result_to_users(result): + task_result = {} + for task_name, raw in result.items(): + res = raw.get('ansible_facts', {}).get('getent_passwd') + if res: + task_result = res + break + if not task_result or not isinstance(task_result, dict): + return [] + users = [] + for username, attr in task_result.items(): + if ignore_login_shell.search(attr[-1]): + continue + users.append(username) + return users + + +def parse_windows_result_to_users(result): + task_result = [] + for task_name, raw in result.items(): + res = raw.get('stdout_lines', {}) + if res: + task_result = res + break + if not task_result: + return [] + + users = [] + + for i in range(4): + task_result.pop(0) + for i in range(2): + task_result.pop() + + for line in task_result: + user = space.split(line) + if user[0]: + users.append(user[0]) + return users + + +def add_asset_users(assets, results): + assets_map = {a.hostname: a for a in assets} + parser_map = { + 'linux': parse_linux_result_to_users, + 'windows': parse_windows_result_to_users + } + + assets_users_map = {} + + for platform, platform_results in results.items(): + for hostname, res in platform_results.items(): + parse = parser_map.get(platform) + users = parse(res) + logger.debug('Gathered host users: {} {}'.format(hostname, users)) + asset = assets_map.get(hostname) + if not asset: + continue + assets_users_map[asset] = users + + for asset, users in assets_users_map.items(): + with tmp_to_org(asset.org_id): + GatheredUser.objects.filter(asset=asset, present=True)\ + .update(present=False) + for username in users: + defaults = {'asset': asset, 'username': username, 'present': True} + GatheredUser.objects.update_or_create( + defaults=defaults, asset=asset, username=username, + ) + @shared_task(queue="ansible") -def gather_asset_all_users(assets, task_name=None): +def gather_asset_users(assets, task_name=None): from ops.utils import update_or_create_ansible_task if task_name is None: task_name = _("Gather assets users") + assets = clean_hosts(assets) + if not assets: + return hosts_category = { 'linux': { 'hosts': [], @@ -38,5 +123,12 @@ def gather_asset_all_users(assets, task_name=None): ) raw, summary = task.run() results[k].update(raw['ok']) - return results + add_asset_users(assets, results) + +@shared_task(queue="ansible") +def gather_nodes_asset_users(nodes_key): + assets = Node.get_nodes_all_assets(nodes_key) + assets_groups_by_100 = [assets[i:i+100] for i in range(0, len(assets), 100)] + for _assets in assets_groups_by_100: + gather_asset_users(_assets) diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index fcd0f79ff..3c8f93822 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -85,7 +85,7 @@ @@ -171,9 +171,13 @@ function initTable() { ], ajax_url: '{% url "api-assets:asset-list" %}', columns: [ - {data: "id"}, {data: "hostname" }, {data: "ip" }, + {data: "id"}, {data: "hostname"}, {data: "ip"}, {data: "cpu_cores", orderable: false}, - {data: "connectivity", orderable: false}, {data: "id", orderable: false } + { + data: "connectivity", + orderable: false, + width: '60px' + }, {data: "id", orderable: false} ], op_html: $('#actions').html() }; @@ -271,7 +275,7 @@ $(document).ready(function(){ setAssetModalOptions(modalOption); }) .on('click', '.labels li', function () { - var val = $(this).text(); + var val = 'label:' + $(this).text(); $("#asset_list_table_filter input").val(val); asset_table.search(val).draw(); }) diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index 47ff4dc14..46ea16483 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -21,6 +21,7 @@ router.register(r'gateways', api.GatewayViewSet, 'gateway') router.register(r'cmd-filters', api.CommandFilterViewSet, 'cmd-filter') router.register(r'asset-users', api.AssetUserViewSet, 'asset-user') router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info') +router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user') cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter') cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule') diff --git a/apps/assets/utils.py b/apps/assets/utils.py index 01dae25be..0e4583dc8 100644 --- a/apps/assets/utils.py +++ b/apps/assets/utils.py @@ -24,37 +24,6 @@ def get_system_user_by_id(id): return system_user -class LabelFilterMixin: - def get_filter_labels_ids(self): - query_params = self.request.query_params - query_keys = query_params.keys() - all_label_keys = Label.objects.values_list('name', flat=True) - valid_keys = set(all_label_keys) & set(query_keys) - - if not valid_keys: - return [] - - labels_query = [ - {"name": key, "value": query_params[key]} - for key in valid_keys - ] - args = [Q(**kwargs) for kwargs in labels_query] - args = reduce(lambda x, y: x | y, args) - labels_id = Label.objects.filter(args).values_list('id', flat=True) - return labels_id - - -class LabelFilter(LabelFilterMixin): - def filter_queryset(self, queryset): - queryset = super().filter_queryset(queryset) - labels_ids = self.get_filter_labels_ids() - if not labels_ids: - return queryset - for labels_id in labels_ids: - queryset = queryset.filter(labels=labels_id) - return queryset - - class TreeService(Tree): tag_sep = ' / ' cache_key = '_NODE_FULL_TREE' diff --git a/apps/common/filters.py b/apps/common/filters.py index 701f9c730..0dc275ce0 100644 --- a/apps/common/filters.py +++ b/apps/common/filters.py @@ -1,11 +1,15 @@ # -*- coding: utf-8 -*- # +import coreapi from rest_framework import filters from rest_framework.fields import DateTimeField from rest_framework.serializers import ValidationError +from django.core.cache import cache import logging -__all__ = ["DatetimeRangeFilter"] +from . import const + +__all__ = ["DatetimeRangeFilter", "IDSpmFilter", "CustomFilter"] class DatetimeRangeFilter(filters.BaseFilterBackend): @@ -40,3 +44,50 @@ class DatetimeRangeFilter(filters.BaseFilterBackend): if kwargs: queryset = queryset.filter(**kwargs) return queryset + + +class IDSpmFilter(filters.BaseFilterBackend): + def get_schema_fields(self, view): + return [ + coreapi.Field( + name='spm', location='query', required=False, + type='string', example='', + description='Pre post objects id get spm id, then using filter' + ) + ] + + def filter_queryset(self, request, queryset, view): + spm = request.query_params.get('spm') + if not spm: + return queryset + cache_key = const.KEY_CACHE_RESOURCES_ID.format(spm) + resources_id = cache.get(cache_key) + if not resources_id or not isinstance(resources_id, list): + queryset = queryset.none() + return queryset + queryset = queryset.filter(id__in=resources_id) + return queryset + + +class CustomFilter(filters.BaseFilterBackend): + custom_filter_fields = [] # ["node", "asset"] + + def get_schema_fields(self, view): + fields = [] + defaults = dict( + location='query', required=False, + type='string', example='', + description='' + ) + for field in self.custom_filter_fields: + if isinstance(field, str): + defaults['name'] = field + elif isinstance(field, dict): + defaults.update(field) + else: + continue + fields.append(coreapi.Field(**defaults)) + return fields + + def filter_queryset(self, request, queryset, view): + return queryset diff --git a/apps/common/mixins/api.py b/apps/common/mixins/api.py index 21f0394ce..6b1e8a893 100644 --- a/apps/common/mixins/api.py +++ b/apps/common/mixins/api.py @@ -1,15 +1,13 @@ # -*- coding: utf-8 -*- # from django.http import JsonResponse -from django.core.cache import cache -from django.utils.translation import ugettext_lazy as _ -from django.contrib import messages +from rest_framework.settings import api_settings -from ..const import KEY_CACHE_RESOURCES_ID +from ..filters import IDSpmFilter, CustomFilter __all__ = [ - "JSONResponseMixin", "IDInCacheFilterMixin", "IDExportFilterMixin", - "IDInFilterMixin", "ApiMessageMixin" + "JSONResponseMixin", "CommonApiMixin", + "IDSpmFilterMixin", "CommonApiMixin", ] @@ -20,69 +18,31 @@ class JSONResponseMixin(object): return JsonResponse(context) -class IDInFilterMixin(object): +class IDSpmFilterMixin: + def get_filter_backends(self): + backends = super().get_filter_backends() + backends.append(IDSpmFilter) + return backends + + +class ExtraFilterFieldsMixin: + default_added_filters = [CustomFilter, IDSpmFilter] + filter_backends = api_settings.DEFAULT_FILTER_BACKENDS + extra_filter_fields = [] + extra_filter_backends = [] + + def get_filter_backends(self): + if self.filter_backends != self.__class__.filter_backends: + return self.filter_backends + return list(self.filter_backends) + \ + self.default_added_filters + \ + list(self.extra_filter_backends) + def filter_queryset(self, queryset): - queryset = super(IDInFilterMixin, self).filter_queryset(queryset) - id_list = self.request.query_params.get('id__in') - if id_list: - import json - try: - ids = json.loads(id_list) - except Exception as e: - return queryset - if isinstance(ids, list): - queryset = queryset.filter(id__in=ids) + for backend in self.get_filter_backends(): + queryset = backend().filter_queryset(self.request, queryset, self) return queryset -class IDInCacheFilterMixin(object): - - def filter_queryset(self, queryset): - queryset = super().filter_queryset(queryset) - spm = self.request.query_params.get('spm') - if not spm: - return queryset - cache_key = KEY_CACHE_RESOURCES_ID.format(spm) - resources_id = cache.get(cache_key) - if not resources_id or not isinstance(resources_id, list): - queryset = queryset.none() - return queryset - queryset = queryset.filter(id__in=resources_id) - return queryset - - -class IDExportFilterMixin(object): - def filter_queryset(self, queryset): - # 下载导入模版 - if self.request.query_params.get('template') == 'import': - return [] - else: - return super(IDExportFilterMixin, self).filter_queryset(queryset) - - -class ApiMessageMixin: - success_message = _("%(name)s was %(action)s successfully") - _action_map = {"create": _("create"), "update": _("update")} - - def get_success_message(self, cleaned_data): - if not isinstance(cleaned_data, dict): - return '' - data = {k: v for k, v in cleaned_data.items()} - action = getattr(self, "action", "create") - data["action"] = self._action_map.get(action) - try: - message = self.success_message % data - except: - message = '' - return message - - def dispatch(self, request, *args, **kwargs): - resp = super().dispatch(request, *args, **kwargs) - if request.method.lower() in ("get", "delete", "patch"): - return resp - if resp.status_code >= 400: - return resp - message = self.get_success_message(resp.data) - if message: - messages.success(request, message) - return resp +class CommonApiMixin(ExtraFilterFieldsMixin): + pass diff --git a/apps/common/utils/common.py b/apps/common/utils/common.py index a92af5f06..c73e9611d 100644 --- a/apps/common/utils/common.py +++ b/apps/common/utils/common.py @@ -8,7 +8,6 @@ import datetime import uuid from functools import wraps import time -import copy import ipaddress @@ -199,3 +198,18 @@ def timeit(func): logger.debug(msg) return result return wrapper + + +def group_obj_by_count(objs, count=50): + objs_grouped = [ + objs[i:i + count] for i in range(0, len(objs), count) + ] + return objs_grouped + + +def dict_get_any(d, keys): + for key in keys: + value = d.get(key) + if value: + return value + return None diff --git a/apps/jumpserver/swagger.py b/apps/jumpserver/swagger.py index eb8d89bf7..b68733b63 100644 --- a/apps/jumpserver/swagger.py +++ b/apps/jumpserver/swagger.py @@ -33,6 +33,21 @@ class CustomSwaggerAutoSchema(SwaggerAutoSchema): operation.summary = operation.operation_id return operation + def get_filter_parameters(self): + if not self.should_filter(): + return [] + + fields = [] + if hasattr(self.view, 'get_filter_backends'): + backends = self.view.get_filter_backends() + elif hasattr(self.view, 'filter_backends'): + backends = self.view.filter_backends + else: + backends = [] + for filter_backend in backends: + fields += self.probe_inspectors(self.filter_inspectors, 'get_filter_parameters', filter_backend()) or [] + return fields + def get_swagger_view(version='v1'): from .urls import api_v1, api_v2 diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 7e3bfc87f..428c67c3e 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 7962b471b..1586e0e4e 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Jumpserver 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-12 18:01+0800\n" +"POT-Creation-Date: 2019-09-18 12:53+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -71,13 +71,13 @@ msgstr "目标地址" msgid "Operating parameter" msgstr "运行参数" -#: applications/forms/remote_app.py:104 applications/models/remote_app.py:23 +#: applications/forms/remote_app.py:100 applications/models/remote_app.py:23 #: applications/templates/applications/remote_app_detail.html:57 #: applications/templates/applications/remote_app_list.html:22 #: applications/templates/applications/user_remote_app_list.html:18 #: assets/forms/domain.py:15 assets/forms/label.py:13 #: assets/models/asset.py:295 assets/models/authbook.py:24 -#: assets/serializers/admin_user.py:32 assets/serializers/asset_user.py:81 +#: assets/serializers/admin_user.py:32 assets/serializers/asset_user.py:82 #: assets/serializers/system_user.py:31 #: assets/templates/assets/admin_user_list.html:46 #: assets/templates/assets/domain_detail.html:60 @@ -96,40 +96,20 @@ msgstr "运行参数" #: terminal/templates/terminal/session_list.html:28 #: terminal/templates/terminal/session_list.html:72 #: xpack/plugins/change_auth_plan/forms.py:121 -#: xpack/plugins/change_auth_plan/models.py:413 +#: xpack/plugins/change_auth_plan/models.py:412 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14 #: xpack/plugins/cloud/models.py:310 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63 +#: xpack/plugins/gathered_user/forms.py:13 +#: xpack/plugins/gathered_user/forms.py:15 #: xpack/plugins/orgs/templates/orgs/org_list.html:16 #: xpack/plugins/vault/forms.py:13 xpack/plugins/vault/forms.py:15 msgid "Asset" msgstr "资产" -#: applications/forms/remote_app.py:107 applications/models/remote_app.py:27 -#: applications/templates/applications/remote_app_detail.html:61 -#: applications/templates/applications/remote_app_list.html:23 -#: applications/templates/applications/user_remote_app_list.html:19 -#: assets/models/user.py:168 assets/templates/assets/user_asset_list.html:52 -#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:52 -#: audits/templates/audits/ftp_log_list.html:75 -#: perms/forms/asset_permission.py:85 perms/models/asset_permission.py:80 -#: perms/templates/perms/asset_permission_detail.html:140 -#: perms/templates/perms/asset_permission_list.html:54 -#: perms/templates/perms/asset_permission_list.html:75 -#: perms/templates/perms/asset_permission_list.html:127 templates/_nav.html:25 -#: terminal/backends/command/models.py:14 terminal/models.py:156 -#: terminal/templates/terminal/command_list.html:31 -#: terminal/templates/terminal/command_list.html:67 -#: terminal/templates/terminal/session_list.html:29 -#: terminal/templates/terminal/session_list.html:73 -#: users/templates/users/_granted_assets.html:27 -#: xpack/plugins/orgs/templates/orgs/org_list.html:19 -msgid "System user" -msgstr "系统用户" - #: applications/models/remote_app.py:21 #: applications/templates/applications/remote_app_detail.html:53 #: applications/templates/applications/remote_app_list.html:20 @@ -166,7 +146,7 @@ msgstr "系统用户" #: settings/templates/settings/terminal_setting.html:105 terminal/models.py:22 #: terminal/models.py:258 terminal/templates/terminal/terminal_detail.html:43 #: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14 -#: users/models/user.py:330 users/templates/users/_select_user_modal.html:13 +#: users/models/user.py:373 users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_detail.html:63 #: users/templates/users/user_group_detail.html:55 #: users/templates/users/user_group_list.html:35 @@ -174,7 +154,7 @@ msgstr "系统用户" #: users/templates/users/user_profile.html:51 #: users/templates/users/user_pubkey_update.html:57 #: xpack/plugins/change_auth_plan/forms.py:104 -#: xpack/plugins/change_auth_plan/models.py:61 +#: xpack/plugins/change_auth_plan/models.py:60 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12 #: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144 @@ -187,24 +167,24 @@ msgstr "系统用户" msgid "Name" msgstr "名称" -#: applications/models/remote_app.py:32 -#: applications/templates/applications/remote_app_detail.html:65 +#: applications/models/remote_app.py:28 +#: applications/templates/applications/remote_app_detail.html:61 #: applications/templates/applications/remote_app_list.html:21 #: applications/templates/applications/user_remote_app_list.html:17 msgid "App type" msgstr "应用类型" -#: applications/models/remote_app.py:36 -#: applications/templates/applications/remote_app_detail.html:69 +#: applications/models/remote_app.py:32 +#: applications/templates/applications/remote_app_detail.html:65 msgid "App path" msgstr "应用路径" -#: applications/models/remote_app.py:40 +#: applications/models/remote_app.py:36 msgid "Parameters" msgstr "参数" -#: applications/models/remote_app.py:43 -#: applications/templates/applications/remote_app_detail.html:77 +#: applications/models/remote_app.py:39 +#: applications/templates/applications/remote_app_detail.html:73 #: assets/models/asset.py:174 assets/models/base.py:36 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:25 #: assets/models/cmd_filter.py:58 assets/models/group.py:21 @@ -213,13 +193,13 @@ msgstr "参数" #: assets/templates/assets/cmd_filter_detail.html:77 #: assets/templates/assets/domain_detail.html:72 #: assets/templates/assets/system_user_detail.html:100 -#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:14 +#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:15 #: perms/models/base.py:54 #: perms/templates/perms/asset_permission_detail.html:98 #: perms/templates/perms/remote_app_permission_detail.html:90 -#: users/models/user.py:371 users/serializers/v1.py:119 +#: users/models/user.py:414 users/serializers/v1.py:141 #: users/templates/users/user_detail.html:111 -#: xpack/plugins/change_auth_plan/models.py:106 +#: xpack/plugins/change_auth_plan/models.py:105 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113 #: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179 msgid "Created by" @@ -227,17 +207,17 @@ msgstr "创建者" # msgid "Created by" # msgstr "创建者" -#: applications/models/remote_app.py:46 -#: applications/templates/applications/remote_app_detail.html:73 +#: applications/models/remote_app.py:42 +#: applications/templates/applications/remote_app_detail.html:69 #: assets/models/asset.py:175 assets/models/base.py:34 #: assets/models/cluster.py:26 assets/models/domain.py:23 -#: assets/models/group.py:22 assets/models/label.py:25 -#: assets/templates/assets/admin_user_detail.html:64 +#: assets/models/gathered_user.py:19 assets/models/group.py:22 +#: assets/models/label.py:25 assets/templates/assets/admin_user_detail.html:64 #: assets/templates/assets/cmd_filter_detail.html:69 #: assets/templates/assets/domain_detail.html:68 #: assets/templates/assets/system_user_detail.html:96 #: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:64 -#: orgs/models.py:15 perms/models/base.py:55 +#: orgs/models.py:16 perms/models/base.py:55 #: perms/templates/perms/asset_permission_detail.html:94 #: perms/templates/perms/remote_app_permission_detail.html:86 #: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17 @@ -252,10 +232,10 @@ msgstr "创建日期" # msgid "Date created" # msgstr "创建日期" -#: applications/models/remote_app.py:49 -#: applications/templates/applications/remote_app_detail.html:81 -#: applications/templates/applications/remote_app_list.html:24 -#: applications/templates/applications/user_remote_app_list.html:20 +#: applications/models/remote_app.py:45 +#: applications/templates/applications/remote_app_detail.html:77 +#: applications/templates/applications/remote_app_list.html:23 +#: applications/templates/applications/user_remote_app_list.html:19 #: assets/models/asset.py:176 assets/models/base.py:33 #: assets/models/cluster.py:29 assets/models/cmd_filter.py:22 #: assets/models/cmd_filter.py:55 assets/models/domain.py:21 @@ -271,16 +251,16 @@ msgstr "创建日期" #: assets/templates/assets/domain_list.html:28 #: assets/templates/assets/system_user_detail.html:104 #: assets/templates/assets/system_user_list.html:59 ops/models/adhoc.py:43 -#: orgs/models.py:16 perms/models/base.py:56 +#: orgs/models.py:17 perms/models/base.py:56 #: perms/templates/perms/asset_permission_detail.html:102 #: perms/templates/perms/remote_app_permission_detail.html:94 #: settings/models.py:34 terminal/models.py:32 #: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15 -#: users/models/user.py:363 users/templates/users/user_detail.html:129 +#: users/models/user.py:406 users/templates/users/user_detail.html:129 #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:37 #: users/templates/users/user_profile.html:138 -#: xpack/plugins/change_auth_plan/models.py:102 +#: xpack/plugins/change_auth_plan/models.py:101 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19 #: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173 @@ -293,18 +273,18 @@ msgstr "创建日期" msgid "Comment" msgstr "备注" -#: applications/models/remote_app.py:53 perms/forms/remote_app_permission.py:37 +#: applications/models/remote_app.py:49 perms/forms/remote_app_permission.py:37 #: perms/models/remote_app_permission.py:15 #: perms/templates/perms/remote_app_permission_create_update.html:48 #: perms/templates/perms/remote_app_permission_detail.html:27 #: perms/templates/perms/remote_app_permission_list.html:17 #: perms/templates/perms/remote_app_permission_remote_app.html:26 #: perms/templates/perms/remote_app_permission_user.html:26 -#: templates/_nav.html:36 templates/_nav.html:48 templates/_nav_user.html:16 +#: templates/_nav.html:60 templates/_nav.html:76 templates/_nav_user.html:16 msgid "RemoteApp" msgstr "远程应用" -#: applications/templates/applications/remote_app_create_update.html:56 +#: applications/templates/applications/remote_app_create_update.html:55 #: assets/templates/assets/_system_user.html:75 #: assets/templates/assets/admin_user_create_update.html:45 #: assets/templates/assets/asset_bulk_update.html:23 @@ -315,7 +295,7 @@ msgstr "远程应用" #: assets/templates/assets/gateway_create_update.html:58 #: assets/templates/assets/label_create_update.html:18 #: perms/templates/perms/asset_permission_create_update.html:83 -#: perms/templates/perms/remote_app_permission_create_update.html:83 +#: perms/templates/perms/remote_app_permission_create_update.html:84 #: settings/templates/settings/basic_setting.html:64 #: settings/templates/settings/command_storage_create.html:79 #: settings/templates/settings/email_content_setting.html:54 @@ -336,12 +316,13 @@ msgstr "远程应用" #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:71 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:33 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:53 +#: xpack/plugins/gathered_user/templates/gathered_user/vault_create.html:45 #: xpack/plugins/interface/templates/interface/interface.html:72 #: xpack/plugins/vault/templates/vault/vault_create.html:45 msgid "Reset" msgstr "重置" -#: applications/templates/applications/remote_app_create_update.html:58 +#: applications/templates/applications/remote_app_create_update.html:57 #: assets/templates/assets/_system_user.html:76 #: assets/templates/assets/admin_user_create_update.html:46 #: assets/templates/assets/asset_bulk_update.html:24 @@ -354,7 +335,7 @@ msgstr "重置" #: assets/templates/assets/label_create_update.html:19 #: audits/templates/audits/login_log_list.html:95 #: perms/templates/perms/asset_permission_create_update.html:84 -#: perms/templates/perms/remote_app_permission_create_update.html:84 +#: perms/templates/perms/remote_app_permission_create_update.html:85 #: settings/templates/settings/basic_setting.html:65 #: settings/templates/settings/command_storage_create.html:80 #: settings/templates/settings/email_content_setting.html:55 @@ -374,6 +355,7 @@ msgstr "重置" #: users/templates/users/user_profile_update.html:68 #: users/templates/users/user_pubkey_update.html:81 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:72 +#: xpack/plugins/gathered_user/templates/gathered_user/vault_create.html:46 #: xpack/plugins/interface/templates/interface/interface.html:74 #: xpack/plugins/vault/templates/vault/vault_create.html:46 msgid "Submit" @@ -406,7 +388,7 @@ msgid "Detail" msgstr "详情" #: applications/templates/applications/remote_app_detail.html:21 -#: applications/templates/applications/remote_app_list.html:56 +#: applications/templates/applications/remote_app_list.html:54 #: assets/templates/assets/_asset_user_list.html:69 #: assets/templates/assets/admin_user_detail.html:24 #: assets/templates/assets/admin_user_list.html:26 @@ -453,7 +435,7 @@ msgid "Update" msgstr "更新" #: applications/templates/applications/remote_app_detail.html:25 -#: applications/templates/applications/remote_app_list.html:57 +#: applications/templates/applications/remote_app_list.html:55 #: assets/templates/assets/admin_user_detail.html:28 #: assets/templates/assets/admin_user_list.html:112 #: assets/templates/assets/asset_detail.html:31 @@ -511,8 +493,8 @@ msgstr "下载应用加载器" msgid "Create RemoteApp" msgstr "创建远程应用" -#: applications/templates/applications/remote_app_list.html:25 -#: applications/templates/applications/user_remote_app_list.html:21 +#: applications/templates/applications/remote_app_list.html:24 +#: applications/templates/applications/user_remote_app_list.html:20 #: assets/models/cmd_filter.py:54 #: assets/templates/assets/_asset_user_list.html:20 #: assets/templates/assets/admin_user_list.html:51 @@ -550,7 +532,7 @@ msgstr "创建远程应用" msgid "Action" msgstr "动作" -#: applications/templates/applications/user_remote_app_list.html:57 +#: applications/templates/applications/user_remote_app_list.html:52 #: assets/templates/assets/user_asset_list.html:32 #: perms/models/asset_permission.py:30 msgid "Connect" @@ -558,7 +540,7 @@ msgstr "连接" #: applications/views/remote_app.py:31 applications/views/remote_app.py:47 #: applications/views/remote_app.py:70 applications/views/remote_app.py:89 -#: templates/_nav.html:33 +#: templates/_nav.html:57 msgid "Applications" msgstr "应用管理" @@ -595,20 +577,6 @@ msgstr "更新节点资产硬件信息: {}" msgid "Test if the assets under the node are connectable: {}" msgstr "测试节点下资产是否可连接: {}" -#: assets/const.py:77 assets/models/utils.py:43 -msgid "Unreachable" -msgstr "不可达" - -#: assets/const.py:78 assets/models/utils.py:44 -#: assets/templates/assets/asset_list.html:99 -msgid "Reachable" -msgstr "可连接" - -#: assets/const.py:79 assets/models/utils.py:45 authentication/utils.py:13 -#: xpack/plugins/license/models.py:78 -msgid "Unknown" -msgstr "未知" - #: assets/forms/asset.py:24 assets/models/asset.py:140 #: assets/models/domain.py:50 #: assets/templates/assets/domain_gateway_list.html:69 @@ -621,14 +589,14 @@ msgstr "端口" #: assets/templates/assets/asset_detail.html:198 #: assets/templates/assets/system_user_assets.html:83 #: perms/models/asset_permission.py:79 -#: xpack/plugins/change_auth_plan/models.py:72 +#: xpack/plugins/change_auth_plan/models.py:71 msgid "Nodes" msgstr "节点" #: assets/forms/asset.py:58 assets/forms/asset.py:104 #: assets/models/asset.py:149 assets/models/cluster.py:19 #: assets/models/user.py:68 assets/templates/assets/asset_detail.html:76 -#: templates/_nav.html:24 xpack/plugins/cloud/models.py:161 +#: templates/_nav.html:44 xpack/plugins/cloud/models.py:161 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:68 #: xpack/plugins/orgs/templates/orgs/org_list.html:18 msgid "Admin user" @@ -704,7 +672,7 @@ msgid "SSH gateway support proxy SSH,RDP,VNC" msgstr "SSH网关,支持代理SSH,RDP和VNC" #: assets/forms/domain.py:74 assets/forms/user.py:75 assets/forms/user.py:95 -#: assets/models/base.py:29 +#: assets/models/base.py:29 assets/models/gathered_user.py:16 #: assets/templates/assets/_asset_user_auth_update_modal.html:15 #: assets/templates/assets/_asset_user_auth_view_modal.html:21 #: assets/templates/assets/_asset_user_list.html:16 @@ -720,17 +688,18 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" #: perms/templates/perms/asset_permission_user.html:55 #: perms/templates/perms/remote_app_permission_user.html:54 #: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:14 -#: users/models/user.py:328 users/templates/users/_select_user_modal.html:14 +#: users/models/user.py:371 users/templates/users/_select_user_modal.html:14 #: users/templates/users/user_detail.html:67 #: users/templates/users/user_list.html:36 #: users/templates/users/user_profile.html:47 #: xpack/plugins/change_auth_plan/forms.py:106 -#: xpack/plugins/change_auth_plan/models.py:63 -#: xpack/plugins/change_auth_plan/models.py:409 +#: xpack/plugins/change_auth_plan/models.py:62 +#: xpack/plugins/change_auth_plan/models.py:408 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:13 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:74 msgid "Username" msgstr "用户名" @@ -739,7 +708,7 @@ msgid "Password or private key passphrase" msgstr "密码或密钥密码" #: assets/forms/user.py:26 assets/models/base.py:30 -#: assets/serializers/asset_user.py:62 +#: assets/serializers/asset_user.py:63 #: assets/templates/assets/_asset_user_auth_update_modal.html:21 #: assets/templates/assets/_asset_user_auth_view_modal.html:27 #: authentication/forms.py:15 @@ -752,14 +721,14 @@ msgstr "密码或密钥密码" #: users/templates/users/user_profile_update.html:41 #: users/templates/users/user_pubkey_update.html:41 #: users/templates/users/user_update.html:20 -#: xpack/plugins/change_auth_plan/models.py:93 -#: xpack/plugins/change_auth_plan/models.py:264 +#: xpack/plugins/change_auth_plan/models.py:92 +#: xpack/plugins/change_auth_plan/models.py:263 msgid "Password" msgstr "密码" -#: assets/forms/user.py:29 assets/serializers/asset_user.py:70 +#: assets/forms/user.py:29 assets/serializers/asset_user.py:71 #: assets/templates/assets/_asset_user_auth_update_modal.html:27 -#: users/models/user.py:357 +#: users/models/user.py:400 msgid "Private key" msgstr "ssh私钥" @@ -811,6 +780,7 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" #: perms/templates/perms/asset_permission_asset.html:58 settings/forms.py:140 #: users/templates/users/_granted_assets.html:26 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:51 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:73 msgid "IP" msgstr "IP" @@ -826,6 +796,7 @@ msgstr "IP" #: perms/templates/perms/asset_permission_list.html:73 settings/forms.py:139 #: users/templates/users/_granted_assets.html:25 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:50 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:72 msgid "Hostname" msgstr "主机名" @@ -841,7 +812,7 @@ msgstr "协议" #: assets/models/asset.py:142 assets/serializers/asset.py:63 #: assets/templates/assets/asset_create.html:24 #: assets/templates/assets/user_asset_list.html:50 -#: perms/serializers/user_permission.py:38 +#: perms/serializers/user_permission.py:48 msgid "Protocols" msgstr "协议组" @@ -850,9 +821,9 @@ msgstr "协议组" msgid "Platform" msgstr "系统平台" -#: assets/models/asset.py:146 assets/models/cmd_filter.py:21 -#: assets/models/domain.py:54 assets/models/label.py:22 -#: assets/templates/assets/asset_detail.html:112 +#: assets/models/asset.py:146 assets/models/authbook.py:27 +#: assets/models/cmd_filter.py:21 assets/models/domain.py:54 +#: assets/models/label.py:22 assets/templates/assets/asset_detail.html:112 msgid "Is active" msgstr "激活" @@ -922,7 +893,7 @@ msgid "Hostname raw" msgstr "主机名原始" #: assets/models/asset.py:173 assets/templates/assets/asset_create.html:46 -#: assets/templates/assets/asset_detail.html:224 templates/_nav.html:26 +#: assets/templates/assets/asset_detail.html:224 templates/_nav.html:46 msgid "Labels" msgstr "标签管理" @@ -938,22 +909,24 @@ msgstr "最新版本" msgid "Version" msgstr "版本" -#: assets/models/authbook.py:35 +#: assets/models/authbook.py:36 msgid "AuthBook" msgstr "" -#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:97 -#: xpack/plugins/change_auth_plan/models.py:271 +#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:96 +#: xpack/plugins/change_auth_plan/models.py:270 msgid "SSH private key" msgstr "ssh密钥" -#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:100 -#: xpack/plugins/change_auth_plan/models.py:267 +#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:99 +#: xpack/plugins/change_auth_plan/models.py:266 msgid "SSH public key" msgstr "ssh公钥" -#: assets/models/base.py:35 assets/templates/assets/cmd_filter_detail.html:73 +#: assets/models/base.py:35 assets/models/gathered_user.py:21 +#: assets/templates/assets/cmd_filter_detail.html:73 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:76 msgid "Date updated" msgstr "更新日期" @@ -965,7 +938,7 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 users/models/user.py:349 +#: assets/models/cluster.py:22 users/models/user.py:392 #: users/templates/users/user_detail.html:76 msgid "Phone" msgstr "手机" @@ -991,7 +964,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:14 -#: users/models/user.py:469 +#: users/models/user.py:512 msgid "System" msgstr "系统" @@ -1081,6 +1054,10 @@ msgstr "命令过滤规则" msgid "Gateway" msgstr "网关" +#: assets/models/gathered_user.py:32 +msgid "GatherUser" +msgstr "收集用户" + #: assets/models/group.py:30 msgid "Asset group" msgstr "资产组" @@ -1110,9 +1087,9 @@ msgstr "默认资产组" #: terminal/templates/terminal/command_list.html:65 #: terminal/templates/terminal/session_list.html:27 #: terminal/templates/terminal/session_list.html:71 users/forms.py:316 -#: users/models/user.py:127 users/models/user.py:457 -#: users/serializers/v1.py:108 users/templates/users/user_group_detail.html:78 -#: users/templates/users/user_group_list.html:36 users/views/user.py:243 +#: users/models/user.py:127 users/models/user.py:143 users/models/user.py:500 +#: users/serializers/v1.py:130 users/templates/users/user_group_detail.html:78 +#: users/templates/users/user_group_list.html:36 users/views/user.py:250 #: xpack/plugins/orgs/forms.py:26 #: xpack/plugins/orgs/templates/orgs/org_detail.html:113 #: xpack/plugins/orgs/templates/orgs/org_list.html:14 @@ -1132,11 +1109,11 @@ msgstr "分类" msgid "New node" msgstr "新节点" -#: assets/models/node.py:308 perms/api/mixin.py:146 +#: assets/models/node.py:308 msgid "ungrouped" msgstr "未分组" -#: assets/models/node.py:310 perms/api/mixin.py:151 +#: assets/models/node.py:310 msgid "empty" msgstr "空" @@ -1171,7 +1148,7 @@ msgstr "手动登录" #: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73 #: assets/views/system_user.py:29 assets/views/system_user.py:46 #: assets/views/system_user.py:63 assets/views/system_user.py:79 -#: templates/_nav.html:19 xpack/plugins/change_auth_plan/models.py:68 +#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:67 msgid "Assets" msgstr "资产管理" @@ -1194,11 +1171,45 @@ msgstr "Shell" msgid "Login mode" msgstr "登录模式" +#: assets/models/user.py:168 assets/templates/assets/user_asset_list.html:52 +#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:52 +#: audits/templates/audits/ftp_log_list.html:75 +#: perms/forms/asset_permission.py:85 perms/forms/remote_app_permission.py:40 +#: perms/models/asset_permission.py:80 perms/models/remote_app_permission.py:16 +#: perms/templates/perms/asset_permission_detail.html:140 +#: perms/templates/perms/asset_permission_list.html:54 +#: perms/templates/perms/asset_permission_list.html:75 +#: perms/templates/perms/asset_permission_list.html:127 +#: perms/templates/perms/remote_app_permission_detail.html:131 +#: templates/_nav.html:45 terminal/backends/command/models.py:14 +#: terminal/models.py:156 terminal/templates/terminal/command_list.html:31 +#: terminal/templates/terminal/command_list.html:67 +#: terminal/templates/terminal/session_list.html:29 +#: terminal/templates/terminal/session_list.html:73 +#: users/templates/users/_granted_assets.html:27 +#: xpack/plugins/orgs/templates/orgs/org_list.html:19 +msgid "System user" +msgstr "系统用户" + #: assets/models/utils.py:35 #, python-format msgid "%(value)s is not an even number" msgstr "%(value)s is not an even number" +#: assets/models/utils.py:43 assets/tasks/const.py:81 +msgid "Unreachable" +msgstr "不可达" + +#: assets/models/utils.py:44 assets/tasks/const.py:82 +#: assets/templates/assets/asset_list.html:99 +msgid "Reachable" +msgstr "可连接" + +#: assets/models/utils.py:45 assets/tasks/const.py:83 +#: authentication/utils.py:13 xpack/plugins/license/models.py:78 +msgid "Unknown" +msgstr "未知" + #: assets/serializers/asset.py:21 msgid "Protocol format should {}/{}" msgstr "协议格式 {}/{}" @@ -1216,7 +1227,7 @@ msgstr "连接" msgid "Hardware info" msgstr "硬件信息" -#: assets/serializers/asset.py:91 orgs/mixins/serializers.py:26 +#: assets/serializers/asset.py:91 orgs/mixins/serializers.py:27 msgid "Org name" msgstr "组织名称" @@ -1224,8 +1235,8 @@ msgstr "组织名称" msgid "Backend" msgstr "后端" -#: assets/serializers/asset_user.py:66 users/forms.py:263 -#: users/models/user.py:360 users/templates/users/first_login.html:42 +#: assets/serializers/asset_user.py:67 users/forms.py:263 +#: users/models/user.py:403 users/templates/users/first_login.html:42 #: users/templates/users/user_password_update.html:49 #: users/templates/users/user_profile.html:69 #: users/templates/users/user_profile_update.html:46 @@ -1262,88 +1273,93 @@ msgstr "自动登录模式,必须填写用户名" msgid "Password or private key required" msgstr "密码或密钥密码需要一个" -#: assets/tasks.py:33 -msgid "Asset has been disabled, skipped: {}" -msgstr "资产或许不支持ansible, 跳过: {}" - -#: assets/tasks.py:37 -msgid "Asset may not be support ansible, skipped: {}" -msgstr "资产或许不支持ansible, 跳过: {}" - -#: assets/tasks.py:50 -msgid "No assets matched, stop task" -msgstr "没有匹配到资产,结束任务" - -#: assets/tasks.py:60 -msgid "No assets matched related system user protocol, stop task" -msgstr "没有匹配到与系统用户协议相关的资产,结束任务" - -#: assets/tasks.py:86 -msgid "Get asset info failed: {}" -msgstr "获取资产信息失败:{}" - -#: assets/tasks.py:136 -msgid "Update some assets hardware info" -msgstr "更新资产硬件信息" - -#: assets/tasks.py:153 -msgid "Update asset hardware info: {}" -msgstr "更新资产硬件信息: {}" - -#: assets/tasks.py:178 -msgid "Test assets connectivity" -msgstr "测试资产可连接性" - -#: assets/tasks.py:232 -msgid "Test assets connectivity: {}" -msgstr "测试资产可连接性: {}" - -#: assets/tasks.py:274 +#: assets/tasks/admin_user_connectivity.py:56 msgid "Test admin user connectivity period: {}" msgstr "定期测试管理账号可连接性: {}" -#: assets/tasks.py:281 +#: assets/tasks/admin_user_connectivity.py:63 msgid "Test admin user connectivity: {}" msgstr "测试管理行号可连接性: {}" -#: assets/tasks.py:349 -msgid "Test system user connectivity: {}" -msgstr "测试系统用户可连接性: {}" +#: assets/tasks/asset_connectivity.py:21 +msgid "Test assets connectivity" +msgstr "测试资产可连接性" -#: assets/tasks.py:356 -msgid "Test system user connectivity: {} => {}" -msgstr "测试系统用户可连接性: {} => {}" +#: assets/tasks/asset_connectivity.py:75 +msgid "Test assets connectivity: {}" +msgstr "测试资产可连接性: {}" -#: assets/tasks.py:369 -msgid "Test system user connectivity period: {}" -msgstr "定期测试系统用户可连接性: {}" - -#: assets/tasks.py:487 assets/tasks.py:573 -#: xpack/plugins/change_auth_plan/models.py:522 +#: assets/tasks/asset_user_connectivity.py:27 +#: assets/tasks/push_system_user.py:130 +#: xpack/plugins/change_auth_plan/models.py:521 msgid "The asset {} system platform {} does not support run Ansible tasks" msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务" -#: assets/tasks.py:499 +#: assets/tasks/asset_user_connectivity.py:74 +msgid "Test asset user connectivity: {}" +msgstr "测试资产用户可连接性: {}" + +#: assets/tasks/gather_asset_hardware_info.py:44 +msgid "Get asset info failed: {}" +msgstr "获取资产信息失败:{}" + +#: assets/tasks/gather_asset_hardware_info.py:94 +msgid "Update some assets hardware info" +msgstr "更新资产硬件信息" + +#: assets/tasks/gather_asset_hardware_info.py:111 +msgid "Update asset hardware info: {}" +msgstr "更新资产硬件信息: {}" + +#: assets/tasks/gather_asset_users.py:95 +msgid "Gather assets users" +msgstr "收集资产上的用户" + +#: assets/tasks/push_system_user.py:142 msgid "" "Push system user task skip, auto push not enable or protocol is not ssh or " "rdp: {}" msgstr "推送系统用户任务跳过,自动推送没有打开,或协议不是ssh或rdp: {}" -#: assets/tasks.py:506 +#: assets/tasks/push_system_user.py:149 msgid "For security, do not push user {}" msgstr "为了安全,禁止推送用户 {}" -#: assets/tasks.py:534 assets/tasks.py:548 +#: assets/tasks/push_system_user.py:177 assets/tasks/push_system_user.py:191 msgid "Push system users to assets: {}" msgstr "推送系统用户到入资产: {}" -#: assets/tasks.py:540 +#: assets/tasks/push_system_user.py:183 msgid "Push system users to asset: {} => {}" msgstr "推送系统用户到入资产: {} => {}" -#: assets/tasks.py:620 -msgid "Test asset user connectivity: {}" -msgstr "测试资产用户可连接性: {}" +#: assets/tasks/system_user_connectivity.py:79 +msgid "Test system user connectivity: {}" +msgstr "测试系统用户可连接性: {}" + +#: assets/tasks/system_user_connectivity.py:86 +msgid "Test system user connectivity: {} => {}" +msgstr "测试系统用户可连接性: {} => {}" + +#: assets/tasks/system_user_connectivity.py:99 +msgid "Test system user connectivity period: {}" +msgstr "定期测试系统用户可连接性: {}" + +#: assets/tasks/utils.py:16 +msgid "Asset has been disabled, skipped: {}" +msgstr "资产或许不支持ansible, 跳过: {}" + +#: assets/tasks/utils.py:20 +msgid "Asset may not be support ansible, skipped: {}" +msgstr "资产或许不支持ansible, 跳过: {}" + +#: assets/tasks/utils.py:33 +msgid "No assets matched, stop task" +msgstr "没有匹配到资产,结束任务" + +#: assets/tasks/utils.py:43 +msgid "No assets matched related system user protocol, stop task" +msgstr "没有匹配到与系统用户协议相关的资产,结束任务" #: assets/templates/assets/_admin_user_import_modal.html:4 msgid "Import admin user" @@ -1385,12 +1401,13 @@ msgid "Import assets" msgstr "导入资产" #: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:38 -#: templates/_nav.html:22 xpack/plugins/change_auth_plan/views.py:115 +#: templates/_nav.html:42 xpack/plugins/change_auth_plan/views.py:115 msgid "Asset list" msgstr "资产列表" #: assets/templates/assets/_asset_list_modal.html:33 #: assets/templates/assets/_node_tree.html:40 +#: ops/templates/ops/command_execution_create.html:49 #: users/templates/users/_granted_assets.html:7 #: users/templates/users/_granted_assets.html:83 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:66 @@ -1412,8 +1429,8 @@ msgstr "请输入密码" #: assets/templates/assets/_asset_user_auth_update_modal.html:68 #: assets/templates/assets/asset_detail.html:306 -#: users/templates/users/user_detail.html:311 -#: users/templates/users/user_detail.html:338 +#: users/templates/users/user_detail.html:313 +#: users/templates/users/user_detail.html:340 #: xpack/plugins/interface/views.py:35 msgid "Update successfully!" msgstr "更新成功" @@ -1537,7 +1554,7 @@ msgstr "自动生成密钥" #: assets/templates/assets/asset_create.html:74 #: assets/templates/assets/gateway_create_update.html:53 #: perms/templates/perms/asset_permission_create_update.html:53 -#: perms/templates/perms/remote_app_permission_create_update.html:52 +#: perms/templates/perms/remote_app_permission_create_update.html:53 #: terminal/templates/terminal/terminal_update.html:40 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:67 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:48 @@ -1605,10 +1622,10 @@ msgstr "选择节点" #: authentication/templates/authentication/_mfa_confirm_modal.html:20 #: settings/templates/settings/terminal_setting.html:168 #: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:108 -#: users/templates/users/user_detail.html:392 -#: users/templates/users/user_detail.html:418 -#: users/templates/users/user_detail.html:441 -#: users/templates/users/user_detail.html:486 +#: users/templates/users/user_detail.html:394 +#: users/templates/users/user_detail.html:420 +#: users/templates/users/user_detail.html:443 +#: users/templates/users/user_detail.html:488 #: users/templates/users/user_group_create_update.html:32 #: users/templates/users/user_group_list.html:120 #: users/templates/users/user_list.html:256 @@ -1638,6 +1655,7 @@ msgstr "Jumpserver 使用该用户来 `推送系统用户`、`获取资产硬件 #: audits/templates/audits/login_log_list.html:91 #: users/templates/users/user_group_list.html:10 #: users/templates/users/user_list.html:10 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:59 #: xpack/plugins/vault/templates/vault/vault.html:55 msgid "Export" msgstr "导出" @@ -1721,7 +1739,7 @@ msgstr "创建日期" #: perms/models/base.py:51 #: perms/templates/perms/asset_permission_create_update.html:55 #: perms/templates/perms/asset_permission_detail.html:120 -#: perms/templates/perms/remote_app_permission_create_update.html:54 +#: perms/templates/perms/remote_app_permission_create_update.html:55 #: perms/templates/perms/remote_app_permission_detail.html:112 #: terminal/templates/terminal/terminal_list.html:34 #: users/templates/users/_select_user_modal.html:18 @@ -1805,9 +1823,9 @@ msgstr "显示所有子节点资产" #: assets/templates/assets/asset_list.html:412 #: assets/templates/assets/system_user_list.html:133 -#: users/templates/users/user_detail.html:386 -#: users/templates/users/user_detail.html:412 -#: users/templates/users/user_detail.html:480 +#: users/templates/users/user_detail.html:388 +#: users/templates/users/user_detail.html:414 +#: users/templates/users/user_detail.html:482 #: users/templates/users/user_group_list.html:114 #: users/templates/users/user_list.html:250 #: xpack/plugins/interface/templates/interface/interface.html:97 @@ -1821,9 +1839,9 @@ msgstr "删除选择资产" #: assets/templates/assets/asset_list.html:416 #: assets/templates/assets/system_user_list.html:137 #: settings/templates/settings/terminal_setting.html:166 -#: users/templates/users/user_detail.html:390 -#: users/templates/users/user_detail.html:416 -#: users/templates/users/user_detail.html:484 +#: users/templates/users/user_detail.html:392 +#: users/templates/users/user_detail.html:418 +#: users/templates/users/user_detail.html:486 #: users/templates/users/user_group_create_update.html:31 #: users/templates/users/user_group_list.html:118 #: users/templates/users/user_list.html:254 @@ -2082,7 +2100,7 @@ msgstr "创建命令过滤器规则" msgid "Update command filter rule" msgstr "更新命令过滤器规则" -#: assets/views/domain.py:31 templates/_nav.html:23 +#: assets/views/domain.py:31 templates/_nav.html:43 msgid "Domain list" msgstr "网域列表" @@ -2153,7 +2171,7 @@ msgstr "文件名" #: audits/templates/audits/ftp_log_list.html:79 #: ops/templates/ops/command_execution_list.html:68 #: ops/templates/ops/task_list.html:31 -#: users/templates/users/user_detail.html:462 +#: users/templates/users/user_detail.html:464 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:14 #: xpack/plugins/cloud/api.py:61 msgid "Success" @@ -2161,6 +2179,7 @@ msgstr "成功" #: audits/models.py:32 #: authentication/templates/authentication/_access_key_modal.html:38 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:48 #: xpack/plugins/vault/templates/vault/vault.html:46 msgid "Create" msgstr "创建" @@ -2214,13 +2233,13 @@ msgstr "Agent" #: audits/models.py:85 audits/templates/audits/login_log_list.html:62 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms.py:175 users/models/user.py:352 +#: users/forms.py:175 users/models/user.py:395 #: users/templates/users/first_login.html:45 msgid "MFA" msgstr "MFA" #: audits/models.py:86 audits/templates/audits/login_log_list.html:63 -#: xpack/plugins/change_auth_plan/models.py:417 +#: xpack/plugins/change_auth_plan/models.py:416 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15 #: xpack/plugins/cloud/models.py:281 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69 @@ -2246,8 +2265,8 @@ msgstr "登录日期" #: perms/templates/perms/asset_permission_detail.html:86 #: perms/templates/perms/remote_app_permission_detail.html:78 #: terminal/models.py:165 terminal/templates/terminal/session_list.html:34 -#: xpack/plugins/change_auth_plan/models.py:250 -#: xpack/plugins/change_auth_plan/models.py:420 +#: xpack/plugins/change_auth_plan/models.py:249 +#: xpack/plugins/change_auth_plan/models.py:419 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17 msgid "Date start" @@ -2297,47 +2316,46 @@ msgstr "城市" msgid "Date" msgstr "日期" -#: audits/views.py:85 audits/views.py:129 audits/views.py:166 -#: audits/views.py:211 audits/views.py:243 templates/_nav.html:87 -#: templates/_nav_audits.html:22 +#: audits/views.py:86 audits/views.py:130 audits/views.py:167 +#: audits/views.py:212 audits/views.py:244 templates/_nav.html:126 msgid "Audits" msgstr "日志审计" -#: audits/views.py:86 templates/_nav.html:91 templates/_nav_audits.html:26 +#: audits/views.py:87 templates/_nav.html:130 msgid "FTP log" msgstr "FTP日志" -#: audits/views.py:130 templates/_nav.html:92 templates/_nav_audits.html:27 +#: audits/views.py:131 templates/_nav.html:131 msgid "Operate log" msgstr "操作日志" -#: audits/views.py:167 templates/_nav.html:93 templates/_nav_audits.html:28 +#: audits/views.py:168 templates/_nav.html:132 msgid "Password change log" msgstr "改密日志" -#: audits/views.py:212 templates/_nav.html:90 templates/_nav_audits.html:25 +#: audits/views.py:213 templates/_nav.html:129 msgid "Login log" msgstr "登录日志" -#: audits/views.py:244 +#: audits/views.py:245 msgid "Command execution log" msgstr "命令执行" -#: authentication/api/auth.py:51 authentication/api/token.py:45 +#: authentication/api/auth.py:61 authentication/api/token.py:45 #: authentication/templates/authentication/login.html:52 #: authentication/templates/authentication/new_login.html:77 msgid "Log in frequently and try again later" msgstr "登录频繁, 稍后重试" -#: authentication/api/auth.py:76 +#: authentication/api/auth.py:86 msgid "Please carry seed value and conduct MFA secondary certification" msgstr "请携带seed值, 进行MFA二次认证" -#: authentication/api/auth.py:156 +#: authentication/api/auth.py:176 msgid "Please verify the user name and password first" msgstr "请先进行用户名和密码验证" -#: authentication/api/auth.py:161 +#: authentication/api/auth.py:181 msgid "MFA certification failed" msgstr "MFA认证失败" @@ -2467,14 +2485,14 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:287 users/templates/users/user_profile.html:94 +#: users/models/user.py:330 users/templates/users/user_profile.html:94 #: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:166 msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:288 users/templates/users/user_profile.html:92 +#: users/models/user.py:331 users/templates/users/user_profile.html:92 #: users/templates/users/user_profile.html:170 msgid "Enable" msgstr "启用" @@ -2611,8 +2629,8 @@ msgstr "欢迎回来,请输入用户名和密码登录" msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: authentication/views/login.py:174 users/views/user.py:386 -#: users/views/user.py:411 +#: authentication/views/login.py:174 users/views/user.py:393 +#: users/views/user.py:418 msgid "MFA code invalid, or ntp sync server time" msgstr "MFA验证码不正确,或者服务器端时间不对" @@ -2670,16 +2688,16 @@ msgstr "" msgid "Encrypt field using Secret Key" msgstr "" -#: common/mixins/api.py:63 +#: common/mixins/api.py:64 #, python-format msgid "%(name)s was %(action)s successfully" msgstr "%(name)s %(action)s成功" -#: common/mixins/api.py:64 +#: common/mixins/api.py:65 msgid "create" msgstr "创建" -#: common/mixins/api.py:64 +#: common/mixins/api.py:65 msgid "update" msgstr "更新" @@ -2699,11 +2717,11 @@ msgstr "不能包含特殊字符" msgid "This field must be unique." msgstr "字段必须唯一" -#: jumpserver/views.py:188 templates/_nav.html:4 templates/_nav_audits.html:4 +#: jumpserver/views.py:184 templates/_nav.html:7 msgid "Dashboard" msgstr "仪表盘" -#: jumpserver/views.py:197 +#: jumpserver/views.py:193 msgid "" "
Luna is a separately deployed program, you need to deploy Luna, coco, " "configure nginx for url distribution,
If you see this page, " @@ -2800,8 +2818,8 @@ msgstr "完成时间" #: ops/models/adhoc.py:327 ops/templates/ops/adhoc_history.html:57 #: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:33 -#: xpack/plugins/change_auth_plan/models.py:253 -#: xpack/plugins/change_auth_plan/models.py:423 +#: xpack/plugins/change_auth_plan/models.py:252 +#: xpack/plugins/change_auth_plan/models.py:422 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16 msgid "Time" @@ -2950,39 +2968,39 @@ msgstr "成功资产" msgid "Task log" msgstr "任务列表" -#: ops/templates/ops/command_execution_create.html:71 +#: ops/templates/ops/command_execution_create.html:90 #: terminal/templates/terminal/session_detail.html:91 #: terminal/templates/terminal/session_detail.html:100 msgid "Go" msgstr "" -#: ops/templates/ops/command_execution_create.html:197 +#: ops/templates/ops/command_execution_create.html:215 msgid "Selected assets" msgstr "已选择资产" -#: ops/templates/ops/command_execution_create.html:200 +#: ops/templates/ops/command_execution_create.html:218 msgid "In total" msgstr "总共" -#: ops/templates/ops/command_execution_create.html:236 +#: ops/templates/ops/command_execution_create.html:254 msgid "" "Select the left asset, select the running system user, execute command in " "batch" msgstr "选择左侧资产, 选择运行的系统用户,批量执行命令" -#: ops/templates/ops/command_execution_create.html:257 +#: ops/templates/ops/command_execution_create.html:275 msgid "Unselected assets" msgstr "没有选中资产" -#: ops/templates/ops/command_execution_create.html:261 +#: ops/templates/ops/command_execution_create.html:279 msgid "No input command" msgstr "没有输入命令" -#: ops/templates/ops/command_execution_create.html:265 +#: ops/templates/ops/command_execution_create.html:283 msgid "No system user was selected" msgstr "没有选择系统用户" -#: ops/templates/ops/command_execution_create.html:310 +#: ops/templates/ops/command_execution_create.html:328 msgid "Pending" msgstr "等待" @@ -3044,11 +3062,11 @@ msgstr "更新任务内容: {}" #: ops/views/adhoc.py:45 ops/views/adhoc.py:71 ops/views/adhoc.py:85 #: ops/views/adhoc.py:99 ops/views/adhoc.py:113 ops/views/adhoc.py:127 -#: ops/views/adhoc.py:141 ops/views/command.py:47 ops/views/command.py:77 +#: ops/views/adhoc.py:141 ops/views/command.py:48 ops/views/command.py:79 msgid "Ops" msgstr "作业中心" -#: ops/views/adhoc.py:46 templates/_nav.html:81 +#: ops/views/adhoc.py:46 templates/_nav.html:115 msgid "Task list" msgstr "任务列表" @@ -3056,15 +3074,15 @@ msgstr "任务列表" msgid "Task run history" msgstr "执行历史" -#: ops/views/command.py:48 +#: ops/views/command.py:49 msgid "Command execution list" msgstr "命令执行列表" -#: ops/views/command.py:78 templates/_nav_user.html:26 +#: ops/views/command.py:80 templates/_nav_user.html:26 msgid "Command execution" msgstr "命令执行" -#: orgs/mixins/models.py:61 orgs/mixins/serializers.py:25 orgs/models.py:29 +#: orgs/mixins/models.py:61 orgs/mixins/serializers.py:26 orgs/models.py:30 msgid "Organization" msgstr "组织" @@ -3081,9 +3099,9 @@ msgstr "空" #: perms/templates/perms/asset_permission_list.html:71 #: perms/templates/perms/asset_permission_list.html:118 #: perms/templates/perms/remote_app_permission_list.html:16 -#: templates/_nav.html:14 users/forms.py:286 users/models/group.py:26 -#: users/models/user.py:336 users/templates/users/_select_user_modal.html:16 -#: users/templates/users/user_detail.html:217 +#: templates/_nav.html:21 users/forms.py:286 users/models/group.py:26 +#: users/models/user.py:379 users/templates/users/_select_user_modal.html:16 +#: users/templates/users/user_detail.html:218 #: users/templates/users/user_list.html:38 #: xpack/plugins/orgs/templates/orgs/org_list.html:15 msgid "User group" @@ -3095,7 +3113,7 @@ msgid "" "downloading files" msgstr "提示:RDP 协议不支持单独控制上传或下载文件" -#: perms/forms/asset_permission.py:102 perms/forms/remote_app_permission.py:47 +#: perms/forms/asset_permission.py:102 perms/forms/remote_app_permission.py:50 msgid "User or group at least one required" msgstr "用户和用户组至少选一个" @@ -3123,19 +3141,19 @@ msgstr "上传下载" msgid "Actions" msgstr "动作" -#: perms/models/asset_permission.py:85 templates/_nav.html:44 +#: perms/models/asset_permission.py:85 templates/_nav.html:72 msgid "Asset permission" msgstr "资产授权" #: perms/models/base.py:53 #: perms/templates/perms/asset_permission_detail.html:90 #: perms/templates/perms/remote_app_permission_detail.html:82 -#: users/models/user.py:368 users/templates/users/user_detail.html:107 +#: users/models/user.py:411 users/templates/users/user_detail.html:107 #: users/templates/users/user_profile.html:120 msgid "Date expired" msgstr "失效日期" -#: perms/models/remote_app_permission.py:19 +#: perms/models/remote_app_permission.py:20 msgid "RemoteApp permission" msgstr "远程应用授权" @@ -3165,6 +3183,7 @@ msgstr "添加资产" #: perms/templates/perms/asset_permission_detail.html:157 #: perms/templates/perms/asset_permission_user.html:97 #: perms/templates/perms/asset_permission_user.html:125 +#: perms/templates/perms/remote_app_permission_detail.html:148 #: perms/templates/perms/remote_app_permission_remote_app.html:96 #: perms/templates/perms/remote_app_permission_user.html:96 #: perms/templates/perms/remote_app_permission_user.html:124 @@ -3182,13 +3201,13 @@ msgid "Add node to this permission" msgstr "添加节点" #: perms/templates/perms/asset_permission_asset.html:109 -#: users/templates/users/user_detail.html:234 +#: users/templates/users/user_detail.html:235 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:115 msgid "Join" msgstr "加入" #: perms/templates/perms/asset_permission_create_update.html:61 -#: perms/templates/perms/remote_app_permission_create_update.html:60 +#: perms/templates/perms/remote_app_permission_create_update.html:61 msgid "Validity period" msgstr "有效期" @@ -3217,6 +3236,7 @@ msgid "System user count" msgstr "系统用户数量" #: perms/templates/perms/asset_permission_detail.html:148 +#: perms/templates/perms/remote_app_permission_detail.html:139 msgid "Select system users" msgstr "选择系统用户" @@ -3286,13 +3306,13 @@ msgstr "添加用户组" #: perms/views/asset_permission.py:33 perms/views/asset_permission.py:65 #: perms/views/asset_permission.py:82 perms/views/asset_permission.py:99 -#: perms/views/asset_permission.py:136 perms/views/asset_permission.py:171 +#: perms/views/asset_permission.py:139 perms/views/asset_permission.py:170 #: perms/views/remote_app_permission.py:33 #: perms/views/remote_app_permission.py:49 #: perms/views/remote_app_permission.py:66 #: perms/views/remote_app_permission.py:81 -#: perms/views/remote_app_permission.py:108 -#: perms/views/remote_app_permission.py:145 templates/_nav.html:41 +#: perms/views/remote_app_permission.py:115 +#: perms/views/remote_app_permission.py:148 templates/_nav.html:69 #: xpack/plugins/orgs/templates/orgs/org_list.html:21 msgid "Perms" msgstr "权限管理" @@ -3313,11 +3333,11 @@ msgstr "更新资产授权" msgid "Asset permission detail" msgstr "资产授权详情" -#: perms/views/asset_permission.py:137 +#: perms/views/asset_permission.py:140 msgid "Asset permission user list" msgstr "资产授权用户列表" -#: perms/views/asset_permission.py:172 +#: perms/views/asset_permission.py:171 msgid "Asset permission asset list" msgstr "资产授权资产列表" @@ -3337,11 +3357,11 @@ msgstr "更新远程应用授权规则" msgid "RemoteApp permission detail" msgstr "远程应用授权详情" -#: perms/views/remote_app_permission.py:109 +#: perms/views/remote_app_permission.py:116 msgid "RemoteApp permission user list" msgstr "远程应用授权用户列表" -#: perms/views/remote_app_permission.py:146 +#: perms/views/remote_app_permission.py:149 msgid "RemoteApp permission RemoteApp list" msgstr "远程应用授权远程应用列表" @@ -3684,7 +3704,7 @@ msgid "Please submit the LDAP configuration before import" msgstr "请先提交LDAP配置再进行导入" #: settings/templates/settings/_ldap_list_users_modal.html:32 -#: users/models/user.py:332 users/templates/users/user_detail.html:71 +#: users/models/user.py:375 users/templates/users/user_detail.html:71 #: users/templates/users/user_profile.html:59 msgid "Email" msgstr "邮件" @@ -3888,7 +3908,7 @@ msgstr "用户来源不是LDAP" #: settings/views.py:19 settings/views.py:46 settings/views.py:73 #: settings/views.py:103 settings/views.py:131 settings/views.py:144 -#: settings/views.py:158 settings/views.py:185 templates/_nav.html:122 +#: settings/views.py:158 settings/views.py:185 templates/_nav.html:167 msgid "Settings" msgstr "系统设置" @@ -3917,14 +3937,15 @@ msgstr "文档" msgid "Commercial support" msgstr "商业支持" -#: templates/_header_bar.html:70 templates/_nav_user.html:32 users/forms.py:154 +#: templates/_header_bar.html:70 templates/_nav.html:30 +#: templates/_nav_user.html:32 users/forms.py:154 #: users/templates/users/_user.html:43 #: users/templates/users/first_login.html:39 #: users/templates/users/user_password_update.html:40 #: users/templates/users/user_profile.html:17 #: users/templates/users/user_profile_update.html:37 #: users/templates/users/user_profile_update.html:61 -#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:224 +#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:231 msgid "Profile" msgstr "个人信息" @@ -4029,72 +4050,70 @@ msgstr "" "\"%(user_pubkey_update)s\"> 链接 更新\n" " " -#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:45 -#: users/views/group.py:63 users/views/group.py:81 users/views/group.py:98 -#: users/views/login.py:154 users/views/user.py:60 users/views/user.py:77 -#: users/views/user.py:121 users/views/user.py:188 users/views/user.py:210 -#: users/views/user.py:263 users/views/user.py:298 +#: templates/_nav.html:17 users/views/group.py:28 users/views/group.py:45 +#: users/views/group.py:63 users/views/group.py:82 users/views/group.py:99 +#: users/views/login.py:154 users/views/user.py:61 users/views/user.py:78 +#: users/views/user.py:122 users/views/user.py:189 users/views/user.py:217 +#: users/views/user.py:270 users/views/user.py:305 msgid "Users" msgstr "用户管理" -#: templates/_nav.html:13 users/views/user.py:61 +#: templates/_nav.html:20 users/views/user.py:62 msgid "User list" msgstr "用户列表" -#: templates/_nav.html:27 +#: templates/_nav.html:47 msgid "Command filters" msgstr "命令过滤" -#: templates/_nav.html:55 templates/_nav_audits.html:11 -#: terminal/views/command.py:21 terminal/views/session.py:43 -#: terminal/views/session.py:54 terminal/views/session.py:78 -#: terminal/views/terminal.py:32 terminal/views/terminal.py:48 -#: terminal/views/terminal.py:61 +#: templates/_nav.html:88 terminal/views/command.py:21 +#: terminal/views/session.py:43 terminal/views/session.py:54 +#: terminal/views/session.py:78 terminal/views/terminal.py:32 +#: terminal/views/terminal.py:48 terminal/views/terminal.py:61 msgid "Sessions" msgstr "会话管理" -#: templates/_nav.html:58 templates/_nav_audits.html:14 +#: templates/_nav.html:91 msgid "Session online" msgstr "在线会话" -#: templates/_nav.html:59 templates/_nav_audits.html:15 -#: terminal/views/session.py:55 +#: templates/_nav.html:92 terminal/views/session.py:55 msgid "Session offline" msgstr "历史会话" -#: templates/_nav.html:60 templates/_nav_audits.html:16 +#: templates/_nav.html:93 msgid "Commands" msgstr "命令记录" -#: templates/_nav.html:63 templates/_nav_user.html:37 +#: templates/_nav.html:96 templates/_nav_user.html:37 msgid "Web terminal" msgstr "Web终端" -#: templates/_nav.html:68 templates/_nav_user.html:42 +#: templates/_nav.html:97 templates/_nav_user.html:42 msgid "File manager" msgstr "文件管理" -#: templates/_nav.html:72 +#: templates/_nav.html:101 msgid "Terminal" msgstr "终端管理" -#: templates/_nav.html:78 +#: templates/_nav.html:112 msgid "Job Center" msgstr "作业中心" -#: templates/_nav.html:82 templates/_nav.html:94 templates/_nav_audits.html:29 +#: templates/_nav.html:116 templates/_nav.html:133 msgid "Batch command" msgstr "批量命令" -#: templates/_nav.html:100 +#: templates/_nav.html:143 msgid "XPack" msgstr "" -#: templates/_nav.html:108 xpack/plugins/cloud/views.py:28 +#: templates/_nav.html:151 xpack/plugins/cloud/views.py:28 msgid "Account list" msgstr "账户列表" -#: templates/_nav.html:109 +#: templates/_nav.html:152 msgid "Sync instance" msgstr "同步实例" @@ -4476,11 +4495,11 @@ msgid "" "You should use your ssh client tools connect terminal: {}

{}" msgstr "你可以使用ssh客户端工具连接终端" -#: users/api/user.py:187 +#: users/api/user.py:173 msgid "Could not reset self otp, use profile reset instead" msgstr "不能再该页面重置MFA, 请去个人信息页面重置" -#: users/forms.py:33 users/models/user.py:340 +#: users/forms.py:33 users/models/user.py:383 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:87 #: users/templates/users/user_list.html:37 @@ -4501,7 +4520,7 @@ msgstr "" msgid "Paste user id_rsa.pub here." msgstr "复制用户公钥到这里" -#: users/forms.py:52 users/templates/users/user_detail.html:225 +#: users/forms.py:52 users/templates/users/user_detail.html:226 msgid "Join user groups" msgstr "添加到用户组" @@ -4509,11 +4528,11 @@ msgstr "添加到用户组" msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms.py:91 users/forms.py:252 users/serializers/v1.py:94 +#: users/forms.py:91 users/forms.py:252 users/serializers/v1.py:116 msgid "Not a valid ssh public key" msgstr "ssh密钥不合法" -#: users/forms.py:104 users/views/login.py:114 users/views/user.py:280 +#: users/forms.py:104 users/views/login.py:114 users/views/user.py:287 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" @@ -4525,7 +4544,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户" msgid "Set password" msgstr "设置密码" -#: users/forms.py:133 xpack/plugins/change_auth_plan/models.py:86 +#: users/forms.py:133 xpack/plugins/change_auth_plan/models.py:85 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57 @@ -4599,7 +4618,7 @@ msgstr "选择用户" msgid "User auth from {}, go there change password" msgstr "用户认证源来自 {}, 请去相应系统修改密码" -#: users/models/user.py:126 users/models/user.py:465 +#: users/models/user.py:126 users/models/user.py:508 msgid "Administrator" msgstr "管理员" @@ -4611,68 +4630,80 @@ msgstr "应用程序" msgid "Auditor" msgstr "审计员" -#: users/models/user.py:289 users/templates/users/user_profile.html:90 +#: users/models/user.py:139 +msgid "Org admin" +msgstr "组织管理员" + +#: users/models/user.py:141 +msgid "Org auditor" +msgstr "组织审计员" + +#: users/models/user.py:332 users/templates/users/user_profile.html:90 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:343 +#: users/models/user.py:386 msgid "Avatar" msgstr "头像" -#: users/models/user.py:346 users/templates/users/user_detail.html:82 +#: users/models/user.py:389 users/templates/users/user_detail.html:82 msgid "Wechat" msgstr "微信" -#: users/models/user.py:375 users/templates/users/user_detail.html:103 +#: users/models/user.py:418 users/templates/users/user_detail.html:103 #: users/templates/users/user_list.html:39 #: users/templates/users/user_profile.html:102 msgid "Source" msgstr "用户来源" -#: users/models/user.py:379 +#: users/models/user.py:422 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:468 +#: users/models/user.py:511 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/serializers/v1.py:39 +#: users/serializers/v1.py:45 msgid "Groups name" msgstr "用户组名" -#: users/serializers/v1.py:40 +#: users/serializers/v1.py:46 msgid "Source name" msgstr "用户来源名" -#: users/serializers/v1.py:41 +#: users/serializers/v1.py:47 msgid "Is first login" msgstr "首次登录" -#: users/serializers/v1.py:42 +#: users/serializers/v1.py:48 msgid "Role name" msgstr "角色名" -#: users/serializers/v1.py:43 +#: users/serializers/v1.py:49 msgid "Is valid" msgstr "账户是否有效" -#: users/serializers/v1.py:44 +#: users/serializers/v1.py:50 msgid "Is expired" msgstr " 是否过期" -#: users/serializers/v1.py:45 +#: users/serializers/v1.py:51 msgid "Avatar url" msgstr "头像路径" -#: users/serializers/v1.py:54 +#: users/serializers/v1.py:72 msgid "Role limit to {}" msgstr "角色只能为 {}" -#: users/serializers/v1.py:66 +#: users/serializers/v1.py:84 msgid "Password does not match security rules" msgstr "密码不满足安全规则" +#: users/serializers/v1.py:147 +msgid "Auditors cannot be join in the group" +msgstr "" + #: users/serializers_v2/user.py:36 msgid "name not unique" msgstr "名称重复" @@ -4723,7 +4754,7 @@ msgid "Import users" msgstr "导入用户" #: users/templates/users/_user_update_modal.html:4 -#: users/templates/users/user_update.html:4 users/views/user.py:122 +#: users/templates/users/user_update.html:4 users/views/user.py:123 msgid "Update user" msgstr "更新用户" @@ -4801,7 +4832,7 @@ msgid "Always young, always with tears in my eyes. Stay foolish Stay hungry" msgstr "永远年轻,永远热泪盈眶 stay foolish stay hungry" #: users/templates/users/reset_password.html:46 -#: users/templates/users/user_detail.html:377 users/utils.py:84 +#: users/templates/users/user_detail.html:379 users/utils.py:84 msgid "Reset password" msgstr "重置密码" @@ -4866,12 +4897,12 @@ msgid "Very strong" msgstr "很强" #: users/templates/users/user_create.html:4 -#: users/templates/users/user_list.html:28 users/views/user.py:78 +#: users/templates/users/user_list.html:28 users/views/user.py:79 msgid "Create user" msgstr "创建用户" #: users/templates/users/user_detail.html:19 -#: users/templates/users/user_granted_asset.html:18 users/views/user.py:189 +#: users/templates/users/user_granted_asset.html:18 users/views/user.py:190 msgid "User detail" msgstr "用户详情" @@ -4918,7 +4949,7 @@ msgid "Send reset ssh key mail" msgstr "发送重置密钥邮件" #: users/templates/users/user_detail.html:203 -#: users/templates/users/user_detail.html:465 +#: users/templates/users/user_detail.html:467 msgid "Unblock user" msgstr "解除登录限制" @@ -4926,52 +4957,52 @@ msgstr "解除登录限制" msgid "Unblock" msgstr "解除" -#: users/templates/users/user_detail.html:320 +#: users/templates/users/user_detail.html:322 msgid "Goto profile page enable MFA" msgstr "请去个人信息页面启用自己的MFA" -#: users/templates/users/user_detail.html:376 +#: users/templates/users/user_detail.html:378 msgid "An e-mail has been sent to the user`s mailbox." msgstr "已发送邮件到用户邮箱" -#: users/templates/users/user_detail.html:387 +#: users/templates/users/user_detail.html:389 msgid "This will reset the user password and send a reset mail" msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱" -#: users/templates/users/user_detail.html:402 +#: users/templates/users/user_detail.html:404 msgid "" "The reset-ssh-public-key E-mail has been sent successfully. Please inform " "the user to update his new ssh public key." msgstr "重设密钥邮件将会发送到用户邮箱" -#: users/templates/users/user_detail.html:403 +#: users/templates/users/user_detail.html:405 msgid "Reset SSH public key" msgstr "重置SSH密钥" -#: users/templates/users/user_detail.html:413 +#: users/templates/users/user_detail.html:415 msgid "This will reset the user public key and send a reset mail" msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱" -#: users/templates/users/user_detail.html:431 +#: users/templates/users/user_detail.html:433 msgid "Successfully updated the SSH public key." msgstr "更新ssh密钥成功" -#: users/templates/users/user_detail.html:432 -#: users/templates/users/user_detail.html:436 +#: users/templates/users/user_detail.html:434 +#: users/templates/users/user_detail.html:438 msgid "User SSH public key update" msgstr "ssh密钥" -#: users/templates/users/user_detail.html:481 +#: users/templates/users/user_detail.html:483 msgid "After unlocking the user, the user can log in normally." msgstr "解除用户登录限制后,此用户即可正常登录" -#: users/templates/users/user_detail.html:495 +#: users/templates/users/user_detail.html:497 msgid "Reset user MFA success" msgstr "重置用户MFA成功" #: users/templates/users/user_group_detail.html:22 #: users/templates/users/user_group_granted_asset.html:18 -#: users/views/group.py:82 +#: users/views/group.py:83 msgid "User group detail" msgstr "用户组详情" @@ -5005,24 +5036,24 @@ msgstr "用户组删除失败" msgid "This will delete the selected users !!!" msgstr "删除选中用户 !!!" -#: users/templates/users/user_list.html:267 +#: users/templates/users/user_list.html:262 msgid "User Deleted." msgstr "已被删除" -#: users/templates/users/user_list.html:268 -#: users/templates/users/user_list.html:272 +#: users/templates/users/user_list.html:263 +#: users/templates/users/user_list.html:267 msgid "User Delete" msgstr "删除" -#: users/templates/users/user_list.html:271 +#: users/templates/users/user_list.html:266 msgid "User Deleting failed." msgstr "用户删除失败" -#: users/templates/users/user_list.html:324 +#: users/templates/users/user_list.html:327 msgid "User is expired" msgstr "用户已失效" -#: users/templates/users/user_list.html:327 +#: users/templates/users/user_list.html:330 msgid "User is inactive" msgstr "用户已禁用" @@ -5327,7 +5358,7 @@ msgstr "" msgid "User group list" msgstr "用户组列表" -#: users/views/group.py:99 +#: users/views/group.py:100 msgid "User group granted asset" msgstr "用户组授权资产" @@ -5364,47 +5395,47 @@ msgstr "密码不一致" msgid "First login" msgstr "首次登录" -#: users/views/user.py:140 +#: users/views/user.py:141 msgid "Bulk update user success" msgstr "批量更新用户成功" -#: users/views/user.py:168 +#: users/views/user.py:169 msgid "Bulk update user" msgstr "批量更新用户" -#: users/views/user.py:211 +#: users/views/user.py:218 msgid "User granted assets" msgstr "用户授权资产" -#: users/views/user.py:244 +#: users/views/user.py:251 msgid "Profile setting" msgstr "个人信息设置" -#: users/views/user.py:264 +#: users/views/user.py:271 msgid "Password update" msgstr "密码更新" -#: users/views/user.py:299 +#: users/views/user.py:306 msgid "Public key update" msgstr "密钥更新" -#: users/views/user.py:341 +#: users/views/user.py:348 msgid "Password invalid" msgstr "用户名或密码无效" -#: users/views/user.py:441 +#: users/views/user.py:448 msgid "MFA enable success" msgstr "MFA 绑定成功" -#: users/views/user.py:442 +#: users/views/user.py:449 msgid "MFA enable success, return login page" msgstr "MFA 绑定成功,返回到登录页面" -#: users/views/user.py:444 +#: users/views/user.py:451 msgid "MFA disable success" msgstr "MFA 解绑成功" -#: users/views/user.py:445 +#: users/views/user.py:452 msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" @@ -5413,7 +5444,7 @@ msgid "Password length" msgstr "密码长度" #: xpack/plugins/change_auth_plan/forms.py:51 -#: xpack/plugins/change_auth_plan/models.py:213 +#: xpack/plugins/change_auth_plan/models.py:212 msgid "* For security, do not change {} user's password" msgstr "* 为了安全,禁止更改 {} 用户的密码" @@ -5461,8 +5492,8 @@ msgstr "" "具)
注意: 如果同时设置了定期执行和周期执行,优先使用定期执行" #: xpack/plugins/change_auth_plan/meta.py:9 -#: xpack/plugins/change_auth_plan/models.py:114 -#: xpack/plugins/change_auth_plan/models.py:257 +#: xpack/plugins/change_auth_plan/models.py:113 +#: xpack/plugins/change_auth_plan/models.py:256 #: xpack/plugins/change_auth_plan/views.py:33 #: xpack/plugins/change_auth_plan/views.py:50 #: xpack/plugins/change_auth_plan/views.py:72 @@ -5473,61 +5504,61 @@ msgstr "" msgid "Change auth plan" msgstr "改密计划" -#: xpack/plugins/change_auth_plan/models.py:55 +#: xpack/plugins/change_auth_plan/models.py:54 msgid "Custom password" msgstr "自定义密码" -#: xpack/plugins/change_auth_plan/models.py:56 +#: xpack/plugins/change_auth_plan/models.py:55 msgid "All assets use the same random password" msgstr "所有资产使用相同的随机密码" -#: xpack/plugins/change_auth_plan/models.py:57 +#: xpack/plugins/change_auth_plan/models.py:56 msgid "All assets use different random password" msgstr "所有资产使用不同的随机密码" -#: xpack/plugins/change_auth_plan/models.py:76 -#: xpack/plugins/change_auth_plan/models.py:145 +#: xpack/plugins/change_auth_plan/models.py:75 +#: xpack/plugins/change_auth_plan/models.py:144 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100 #: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91 msgid "Cycle perform" msgstr "周期执行" -#: xpack/plugins/change_auth_plan/models.py:81 -#: xpack/plugins/change_auth_plan/models.py:143 +#: xpack/plugins/change_auth_plan/models.py:80 +#: xpack/plugins/change_auth_plan/models.py:142 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92 #: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83 msgid "Regularly perform" msgstr "定期执行" -#: xpack/plugins/change_auth_plan/models.py:90 +#: xpack/plugins/change_auth_plan/models.py:89 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74 msgid "Password rules" msgstr "密码规则" -#: xpack/plugins/change_auth_plan/models.py:217 +#: xpack/plugins/change_auth_plan/models.py:216 msgid "Assets is empty, please add the asset" msgstr "资产为空,请添加资产" -#: xpack/plugins/change_auth_plan/models.py:261 +#: xpack/plugins/change_auth_plan/models.py:260 msgid "Change auth plan snapshot" msgstr "改密计划快照" -#: xpack/plugins/change_auth_plan/models.py:276 -#: xpack/plugins/change_auth_plan/models.py:427 +#: xpack/plugins/change_auth_plan/models.py:275 +#: xpack/plugins/change_auth_plan/models.py:426 msgid "Change auth plan execution" msgstr "改密计划执行" -#: xpack/plugins/change_auth_plan/models.py:436 +#: xpack/plugins/change_auth_plan/models.py:435 msgid "Change auth plan execution subtask" msgstr "改密计划执行子任务" -#: xpack/plugins/change_auth_plan/models.py:454 +#: xpack/plugins/change_auth_plan/models.py:453 msgid "Authentication failed" msgstr "认证失败" -#: xpack/plugins/change_auth_plan/models.py:456 +#: xpack/plugins/change_auth_plan/models.py:455 msgid "Connection timeout" msgstr "连接超时" @@ -5841,6 +5872,28 @@ msgstr "创建同步实例任务" msgid "Update sync Instance task" msgstr "更新同步实例任务" +#: xpack/plugins/gathered_user/meta.py:11 +#: xpack/plugins/gathered_user/views.py:22 +msgid "Gathered user" +msgstr "收集用户" + +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:75 +msgid "Present" +msgstr "存在" + +#: xpack/plugins/gathered_user/views.py:23 +msgid "Gathered user list" +msgstr "收集用户列表" + +#: xpack/plugins/gathered_user/views.py:37 xpack/plugins/vault/meta.py:11 +#: xpack/plugins/vault/views.py:23 xpack/plugins/vault/views.py:38 +msgid "Vault" +msgstr "密码匣子" + +#: xpack/plugins/gathered_user/views.py:38 xpack/plugins/vault/views.py:39 +msgid "vault create" +msgstr "创建" + #: xpack/plugins/interface/forms.py:17 xpack/plugins/interface/models.py:15 msgid "Title of login page" msgstr "登录页面标题" @@ -6051,11 +6104,6 @@ msgstr "创建组织" msgid "Update org" msgstr "更新组织" -#: xpack/plugins/vault/meta.py:11 xpack/plugins/vault/views.py:23 -#: xpack/plugins/vault/views.py:38 -msgid "Vault" -msgstr "密码匣子" - #: xpack/plugins/vault/templates/vault/_xpack_import_modal.html:4 msgid "Import vault" msgstr "导入密码" @@ -6064,10 +6112,6 @@ msgstr "导入密码" msgid "vault list" msgstr "密码匣子" -#: xpack/plugins/vault/views.py:39 -msgid "vault create" -msgstr "创建" - #~ msgid "* For security, do not change {}'s password" #~ msgstr "* 为了安全,禁止更改 {} 的密码" diff --git a/apps/ops/celery/utils.py b/apps/ops/celery/utils.py index ea975af78..f9a80e794 100644 --- a/apps/ops/celery/utils.py +++ b/apps/ops/celery/utils.py @@ -6,7 +6,9 @@ import os from django.conf import settings from django.utils.timezone import get_current_timezone from django.db.utils import ProgrammingError, OperationalError -from django_celery_beat.models import PeriodicTask, IntervalSchedule, CrontabSchedule +from django_celery_beat.models import ( + PeriodicTask, IntervalSchedule, CrontabSchedule, PeriodicTasks +) def create_or_update_celery_periodic_tasks(tasks): @@ -75,17 +77,20 @@ def create_or_update_celery_periodic_tasks(tasks): task = PeriodicTask.objects.update_or_create( defaults=defaults, name=name, ) + PeriodicTasks.update_changed() return task def disable_celery_periodic_task(task_name): from django_celery_beat.models import PeriodicTask PeriodicTask.objects.filter(name=task_name).update(enabled=False) + PeriodicTasks.update_changed() def delete_celery_periodic_task(task_name): from django_celery_beat.models import PeriodicTask PeriodicTask.objects.filter(name=task_name).delete() + PeriodicTasks.update_changed() def get_celery_task_log_path(task_id): diff --git a/apps/ops/inventory.py b/apps/ops/inventory.py index b6c302e41..694957b14 100644 --- a/apps/ops/inventory.py +++ b/apps/ops/inventory.py @@ -51,7 +51,7 @@ class JMSBaseInventory(BaseInventory): def make_proxy_command(asset): gateway = asset.domain.random_gateway() proxy_command_list = [ - "ssh", "-p", str(gateway.port), + "ssh", "-o", "Port={}".format(gateway.port), "-o", "StrictHostKeyChecking=no", "{}@{}".format(gateway.username, gateway.ip), "-W", "%h:%p", "-q", diff --git a/apps/ops/tasks.py b/apps/ops/tasks.py index d2f55f623..f5e190780 100644 --- a/apps/ops/tasks.py +++ b/apps/ops/tasks.py @@ -2,6 +2,7 @@ import os import subprocess import datetime +import time from django.conf import settings from celery import shared_task, subtask @@ -122,3 +123,22 @@ def hello123(): def hello_callback(result): print(result) print("Hello callback") + + +@shared_task +def add(a, b): + time.sleep(5) + return max(b) + + +@shared_task +def add_m(x): + from celery import chain + a = range(x) + b = [a[i:i + 10] for i in range(0, len(a), 10)] + s = list() + s.append(add.s(b[0], b[1])) + for i in b[1:]: + s.append(add.s(i)) + res = chain(*tuple(s))() + return res diff --git a/apps/orgs/mixins/api.py b/apps/orgs/mixins/api.py index d36360f51..fb72d78e5 100644 --- a/apps/orgs/mixins/api.py +++ b/apps/orgs/mixins/api.py @@ -3,7 +3,7 @@ from django.shortcuts import get_object_or_404 from rest_framework.viewsets import ModelViewSet from rest_framework_bulk import BulkModelViewSet -from common.mixins import IDInCacheFilterMixin +from common.mixins import CommonApiMixin from ..utils import set_to_root_org from ..models import Organization @@ -20,14 +20,16 @@ class RootOrgViewMixin: return super().dispatch(request, *args, **kwargs) -class OrgModelViewSet(IDInCacheFilterMixin, ModelViewSet): +class OrgModelViewSet(CommonApiMixin, ModelViewSet): def get_queryset(self): return super().get_queryset().all() -class OrgBulkModelViewSet(IDInCacheFilterMixin, BulkModelViewSet): +class OrgBulkModelViewSet(CommonApiMixin, BulkModelViewSet): def get_queryset(self): queryset = super().get_queryset().all() + if hasattr(self, 'swagger_fake_view'): + return queryset[:1] if hasattr(self, 'action') and self.action == 'list' and \ hasattr(self, 'serializer_class') and \ hasattr(self.serializer_class, 'setup_eager_loading'): diff --git a/apps/orgs/mixins/serializers.py b/apps/orgs/mixins/serializers.py index 3c039d56f..a34f8d9b1 100644 --- a/apps/orgs/mixins/serializers.py +++ b/apps/orgs/mixins/serializers.py @@ -11,7 +11,8 @@ from ..utils import get_current_org_id_for_serializer __all__ = [ "OrgResourceSerializerMixin", "BulkOrgResourceSerializerMixin", - "BulkOrgResourceModelSerializer", "OrgMembershipSerializerMixin" + "BulkOrgResourceModelSerializer", "OrgMembershipSerializerMixin", + "OrgResourceModelSerializerMixin", ] @@ -42,6 +43,10 @@ class OrgResourceSerializerMixin(serializers.Serializer): return fields +class OrgResourceModelSerializerMixin(OrgResourceSerializerMixin, serializers.ModelSerializer): + pass + + class BulkOrgResourceSerializerMixin(BulkSerializerMixin, OrgResourceSerializerMixin): pass diff --git a/apps/orgs/utils.py b/apps/orgs/utils.py index 90dde34d4..0d040d1d2 100644 --- a/apps/orgs/utils.py +++ b/apps/orgs/utils.py @@ -18,6 +18,8 @@ def get_org_from_request(request): def set_current_org(org): + if isinstance(org, str): + org = Organization.get_instance(org) setattr(thread_local, 'current_org_id', org.id) diff --git a/apps/perms/api/mixin.py b/apps/perms/api/mixin.py index fba8155ae..85db529a0 100644 --- a/apps/perms/api/mixin.py +++ b/apps/perms/api/mixin.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- # -import uuid -from django.db.models import Q from rest_framework.generics import get_object_or_404 -from assets.utils import LabelFilterMixin from common.permissions import IsValidUser, IsOrgAdminOrAppUser from common.utils import get_logger from orgs.utils import set_to_root_org -from ..hands import User, UserGroup, Asset, SystemUser -from .. import serializers +from ..hands import User, UserGroup logger = get_logger(__name__) @@ -54,101 +50,3 @@ class UserGroupPermissionMixin: return user_group -class GrantAssetsMixin(LabelFilterMixin): - serializer_class = serializers.AssetGrantedSerializer - - def get_serializer_queryset(self, queryset): - assets_ids = [] - system_users_ids = set() - for asset in queryset: - assets_ids.append(asset["id"]) - system_users_ids.update(set(asset["system_users"])) - assets = Asset.objects.filter(id__in=assets_ids).only( - *self.serializer_class.Meta.only_fields - ) - assets_map = {asset.id: asset for asset in assets} - system_users = SystemUser.objects.filter(id__in=system_users_ids).only( - *self.serializer_class.system_users_only_fields - ) - system_users_map = {s.id: s for s in system_users} - data = [] - for item in queryset: - i = item["id"] - asset = assets_map.get(i) - if not asset: - continue - - _system_users = item["system_users"] - system_users_granted = [] - for sid, action in _system_users.items(): - system_user = system_users_map.get(sid) - if not system_user: - continue - if not asset.has_protocol(system_user.protocol): - continue - system_user.actions = action - system_users_granted.append(system_user) - asset.system_users_granted = system_users_granted - data.append(asset) - return data - - def get_serializer(self, assets_items=None, many=True): - if assets_items is None: - assets_items = [] - assets_items = self.get_serializer_queryset(assets_items) - return super().get_serializer(assets_items, many=many) - - def filter_queryset_by_id(self, assets_items): - i = self.request.query_params.get("id") - if not i: - return assets_items - try: - pk = uuid.UUID(i) - except ValueError: - return assets_items - assets_map = {asset['id']: asset for asset in assets_items} - if pk in assets_map: - return [assets_map.get(pk)] - else: - return [] - - def search_queryset(self, assets_items): - search = self.request.query_params.get("search") - if not search: - return assets_items - assets_map = {asset['id']: asset for asset in assets_items} - assets_ids = set(assets_map.keys()) - assets_ids_search = Asset.objects.filter(id__in=assets_ids).filter( - Q(hostname__icontains=search) | Q(ip__icontains=search) - ).values_list('id', flat=True) - return [assets_map.get(asset_id) for asset_id in assets_ids_search] - - def filter_queryset_by_label(self, assets_items): - labels_id = self.get_filter_labels_ids() - if not labels_id: - return assets_items - - assets_map = {asset['id']: asset for asset in assets_items} - assets_matched = Asset.objects.filter(id__in=assets_map.keys()) - for label_id in labels_id: - assets_matched = assets_matched.filter(labels=label_id) - assets_ids_matched = assets_matched.values_list('id', flat=True) - return [assets_map.get(asset_id) for asset_id in assets_ids_matched] - - def sort_queryset(self, assets_items): - order_by = self.request.query_params.get('order', 'hostname') - - if order_by not in ['hostname', '-hostname', 'ip', '-ip']: - order_by = 'hostname' - assets_map = {asset['id']: asset for asset in assets_items} - assets_ids_search = Asset.objects.filter(id__in=assets_map.keys())\ - .order_by(order_by)\ - .values_list('id', flat=True) - return [assets_map.get(asset_id) for asset_id in assets_ids_search] - - def filter_queryset(self, assets_items): - assets_items = self.filter_queryset_by_id(assets_items) - assets_items = self.search_queryset(assets_items) - assets_items = self.filter_queryset_by_label(assets_items) - assets_items = self.sort_queryset(assets_items) - return assets_items diff --git a/apps/perms/api/user_permission/mixin.py b/apps/perms/api/user_permission/mixin.py index 3fb1e99ad..a5c6ffca0 100644 --- a/apps/perms/api/user_permission/mixin.py +++ b/apps/perms/api/user_permission/mixin.py @@ -2,12 +2,12 @@ # from ..mixin import UserPermissionMixin from ...utils import AssetPermissionUtilV2, ParserNode -from ...hands import Node +from ...hands import Node, Asset from common.tree import TreeNodeSerializer class UserAssetPermissionMixin(UserPermissionMixin): - util = None + util = AssetPermissionUtilV2(None) tree = None def initial(self, *args, **kwargs): @@ -41,7 +41,9 @@ class UserNodeTreeMixin: queryset = self.parse_nodes_to_queryset(queryset) return queryset - def get_serializer(self, queryset, many=True, **kwargs): + def get_serializer(self, queryset=None, many=True, **kwargs): + if queryset is None: + queryset = Node.objects.none() queryset = self.get_serializer_queryset(queryset) queryset.sort() return super().get_serializer(queryset, many=many, **kwargs) @@ -64,7 +66,9 @@ class UserAssetTreeMixin: _queryset = self.parse_assets_to_queryset(queryset, None) return _queryset - def get_serializer(self, queryset, many=True, **kwargs): + def get_serializer(self, queryset=None, many=True, **kwargs): + if queryset is None: + queryset = Asset.objects.none() queryset = self.get_serializer_queryset(queryset) queryset.sort() return super().get_serializer(queryset, many=many, **kwargs) diff --git a/apps/perms/utils/asset_permission.py b/apps/perms/utils/asset_permission.py index bb0ede53d..ed3616933 100644 --- a/apps/perms/utils/asset_permission.py +++ b/apps/perms/utils/asset_permission.py @@ -126,10 +126,10 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): 'comment', 'is_active', 'os', 'org_id' ) - def __init__(self, obj, cache_policy='0'): + def __init__(self, obj=None, cache_policy='0'): self.object = obj self.cache_policy = cache_policy - self.obj_id = str(obj.id) + self.obj_id = str(obj.id) if obj else None self._permissions = None self._permissions_id = None # 标记_permission的唯一值 self._filter_id = 'None' # 当通过filter更改 permission是标记 @@ -147,6 +147,8 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): def permissions(self): if self._permissions: return self._permissions + if self.object is None: + return AssetPermission.objects.none() object_cls = self.object.__class__.__name__ func = self.get_permissions_map[object_cls] permissions = func(self.object) diff --git a/apps/settings/api.py b/apps/settings/api.py index eed1aa8df..af5742921 100644 --- a/apps/settings/api.py +++ b/apps/settings/api.py @@ -97,6 +97,8 @@ class LDAPUserListApi(generics.ListAPIView): serializer_class = LDAPUserSerializer def get_queryset(self): + if hasattr(self, 'swagger_fake_view'): + return [] util = LDAPUtil() try: users = util.search_user_items() diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 127529a73..03c804da8 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -153,6 +153,7 @@ function activeNav() { } else { $("#" + app).addClass('active'); $('#' + app + ' #' + resource).addClass('active'); + $('#' + app + ' #' + resource.replace('-', '_')).addClass('active'); } } diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 67f6140d8..8b76e9b5d 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -171,7 +171,5 @@ \ No newline at end of file diff --git a/apps/terminal/serializers_v2/terminal.py b/apps/terminal/serializers_v2/terminal.py index c7ebe682c..021519564 100644 --- a/apps/terminal/serializers_v2/terminal.py +++ b/apps/terminal/serializers_v2/terminal.py @@ -58,5 +58,5 @@ class TerminalSerializer(serializers.ModelSerializer): class TerminalRegistrationSerializer(serializers.Serializer): name = serializers.CharField(max_length=128) - comment = serializers.CharField(max_length=128) + comment = serializers.CharField(max_length=128, ) service_account = ServiceAccountSerializer(read_only=True) diff --git a/apps/users/api/user.py b/apps/users/api/user.py index 1864f72f4..b8a215c47 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -14,7 +14,7 @@ from common.permissions import ( IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser, CanUpdateDeleteUser, ) -from common.mixins import IDInCacheFilterMixin +from common.mixins import CommonApiMixin from common.utils import get_logger from orgs.utils import current_org from .. import serializers @@ -30,7 +30,7 @@ __all__ = [ ] -class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet): +class UserViewSet(CommonApiMixin, BulkModelViewSet): filter_fields = ('username', 'email', 'name', 'id') search_fields = filter_fields queryset = User.objects.exclude(role=User.ROLE_APP)