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)