From b9f82fd0aca178c55c2fff597a6e87366ac7bc0f Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 2 Jul 2019 22:08:50 +0800 Subject: [PATCH] =?UTF-8?q?[Update]=20=E4=BC=98=E5=8C=96=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/assets/_asset_user_list.html | 4 +- .../templates/assets/system_user_list.html | 42 ++-- apps/common/apps.py | 3 +- apps/orgs/models.py | 8 + apps/orgs/signals_handler.py | 5 + apps/perms/api/user_permission.py | 1 + apps/perms/utils/asset_permission.py | 133 ++++++------ apps/settings/signals_handler.py | 2 +- apps/static/js/jumpserver.js | 7 +- apps/terminal/api/session.py | 14 +- apps/terminal/backends/command/multi.py | 6 +- .../templates/terminal/command_list.html | 189 +++++++++++------- apps/terminal/utils.py | 2 +- apps/terminal/views/command.py | 20 -- .../templates/users/user_group_list.html | 42 ++-- 15 files changed, 267 insertions(+), 211 deletions(-) diff --git a/apps/assets/templates/assets/_asset_user_list.html b/apps/assets/templates/assets/_asset_user_list.html index 52cd94b52..fda69e5d8 100644 --- a/apps/assets/templates/assets/_asset_user_list.html +++ b/apps/assets/templates/assets/_asset_user_list.html @@ -60,8 +60,8 @@ function initAssetUserTable() { }, { targets: 6, createdCell: function (td, cellData) { - var date = new Date(cellData); - $(td).html(date.toLocaleString()); + var data = formatDateAsCN(cellData); + $(td).html(data); }, }, { diff --git a/apps/assets/templates/assets/system_user_list.html b/apps/assets/templates/assets/system_user_list.html index 176744212..50d294026 100644 --- a/apps/assets/templates/assets/system_user_list.html +++ b/apps/assets/templates/assets/system_user_list.html @@ -15,27 +15,27 @@ {% block table_search %}
- -
+
+ + +
+ {% endblock %} {% block table_container %} diff --git a/apps/common/apps.py b/apps/common/apps.py index d5a78046e..9d4d80677 100644 --- a/apps/common/apps.py +++ b/apps/common/apps.py @@ -6,11 +6,12 @@ from django.dispatch import receiver from django.db.backends.signals import connection_created -@receiver(connection_created, dispatch_uid="my_unique_identifier") +@receiver(connection_created) def on_db_connection_ready(sender, **kwargs): from .signals import django_ready if 'migrate' not in sys.argv: django_ready.send(CommonConfig) + connection_created.disconnect(on_db_connection_ready) class CommonConfig(AppConfig): diff --git a/apps/orgs/models.py b/apps/orgs/models.py index 2547e373c..7f44c861c 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -21,6 +21,7 @@ class Organization(models.Model): ROOT_NAME = 'ROOT' DEFAULT_ID = 'DEFAULT' DEFAULT_NAME = 'DEFAULT' + _user_admin_orgs = None class Meta: verbose_name = _("Organization") @@ -92,6 +93,8 @@ class Organization(models.Model): @classmethod def get_user_admin_orgs(cls, user): + if cls._user_admin_orgs and user.id in cls._user_admin_orgs: + return cls._user_admin_orgs[user.id] admin_orgs = [] if user.is_anonymous: return admin_orgs @@ -100,6 +103,11 @@ class Organization(models.Model): admin_orgs.append(cls.default()) elif user.is_org_admin: admin_orgs = user.admin_orgs.all() + + if cls._user_admin_orgs is None: + cls._user_admin_orgs = {user.id: admin_orgs} + else: + cls._user_admin_orgs[user.id] = admin_orgs return admin_orgs @classmethod diff --git a/apps/orgs/signals_handler.py b/apps/orgs/signals_handler.py index 42387ccde..9d3c8882f 100644 --- a/apps/orgs/signals_handler.py +++ b/apps/orgs/signals_handler.py @@ -41,3 +41,8 @@ def on_org_user_changed(sender, instance=None, **kwargs): for user_group in user_groups: user_group.users.remove(user) set_current_org(old_org) + + +@receiver(m2m_changed, sender=Organization.admins.through) +def on_org_admin_change(sender, **kwargs): + Organization._user_admin_orgs = None diff --git a/apps/perms/api/user_permission.py b/apps/perms/api/user_permission.py index 666b7f55a..377b39722 100644 --- a/apps/perms/api/user_permission.py +++ b/apps/perms/api/user_permission.py @@ -263,6 +263,7 @@ class UserGrantedNodesWithAssetsAsTreeApi(UserPermissionCacheMixin, ListAPIView) system_users=self.system_user_id ) nodes = util.get_nodes_with_assets() + print(list(nodes.keys())) for node, assets in nodes.items(): data = parse_node_to_tree_node(node) queryset.append(data) diff --git a/apps/perms/utils/asset_permission.py b/apps/perms/utils/asset_permission.py index 4f2b8561f..c26047ff4 100644 --- a/apps/perms/utils/asset_permission.py +++ b/apps/perms/utils/asset_permission.py @@ -180,7 +180,7 @@ class GenerateTree: return dict(nodes) def get_nodes(self): - return self.nodes.keys() + return list(self.nodes.keys()) def get_user_permissions(user, include_group=True): @@ -256,9 +256,13 @@ class AssetPermissionCacheMixin: ) @property - def node_key(self): + def node_asset_key(self): return self.get_cache_key('NODES_WITH_ASSETS') + @property + def node_key(self): + return self.get_cache_key('NODES') + @property def asset_key(self): key = self.get_cache_key('ASSETS') @@ -268,54 +272,47 @@ class AssetPermissionCacheMixin: def system_key(self): return self.get_cache_key('SYSTEM_USER') - def get_assets_from_cache(self): - cached = cache.get(self.asset_key) + def get_resource_from_cache(self, resource): + key_map = { + "assets": self.asset_key, + "nodes": self.node_key, + "nodes_with_assets": self.node_asset_key, + "system_users": self.system_key + } + key = key_map.get(resource) + if not key: + raise ValueError("Not a valid resource: {}".format(resource)) + cached = cache.get(key) if not cached: self.update_cache() - cached = cache.get(self.asset_key) + cached = cache.get(key) return cached - def get_nodes_with_assets_from_cache(self): - cached = cache.get(self.node_key) - if not cached: - self.update_cache() - cached = cache.get(self.node_key) - return cached + def get_resource(self, resource): + if self._is_using_cache(): + return self.get_resource_from_cache(resource) + elif self._is_refresh_cache(): + self.expire_cache() + data = self.get_resource_from_cache(resource) + return data + else: + return self.get_resource_without_cache(resource) + + def get_resource_without_cache(self, resource): + attr = 'get_{}_without_cache'.format(resource) + return getattr(self, attr)() def get_nodes_with_assets(self): - if self._is_using_cache(): - return self.get_nodes_with_assets_from_cache() - elif self._is_refresh_cache(): - self.expire_cache() - return self.get_nodes_with_assets_from_cache() - else: - return self.get_nodes_with_assets_without_cache() - - def get_system_user_from_cache(self): - cached = cache.get(self.system_key) - if not cached: - self.update_cache() - cached = cache.get(self.system_key) - return cached + return self.get_resource("nodes_with_assets") def get_assets(self): - if self._is_using_cache(): - return self.get_assets_from_cache() - elif self._is_refresh_cache(): - self.expire_cache() - return self.get_assets_from_cache() - else: - self.expire_cache() - return self.get_assets_without_cache() + return self.get_resource("assets") + + def get_nodes(self): + return self.get_resource("nodes") def get_system_users(self): - if self._is_using_cache(): - return self.get_system_user_from_cache() - elif self._is_refresh_cache(): - self.expire_cache() - return self.get_system_user_from_cache() - else: - return self.get_system_user_without_cache() + return self.get_resource("system_users") def get_meta_cache_key(self): cache_key = self.CACHE_META_KEY_PREFIX + '{obj_id}_{filter_id}' @@ -332,6 +329,17 @@ class AssetPermissionCacheMixin: # print("Meta id: {}".format(meta["id"])) return meta + def update_cache(self): + assets = self.get_resource_without_cache("assets") + nodes_with_assets = self.get_resource_without_cache("nodes_with_assets") + system_users = self.get_resource_without_cache("system_users") + nodes = self.get_resource_without_cache("nodes") + cache.set(self.asset_key, assets, self.CACHE_TIME) + cache.set(self.node_asset_key, nodes_with_assets, self.CACHE_TIME) + cache.set(self.system_key, system_users, self.CACHE_TIME) + cache.set(self.node_key, nodes, self.CACHE_TIME) + self.set_meta_to_cache() + def set_meta_to_cache(self): key = self.get_meta_cache_key() meta = { @@ -348,15 +356,6 @@ class AssetPermissionCacheMixin: key = cache_key.format(obj_id=self.obj_id) cache.delete_pattern(key) - def update_cache(self): - assets = self.get_assets_without_cache() - nodes = self.get_nodes_with_assets_without_cache() - system_users = self.get_system_user_without_cache() - cache.set(self.asset_key, assets, self.CACHE_TIME) - cache.set(self.node_key, nodes, self.CACHE_TIME) - cache.set(self.system_key, system_users, self.CACHE_TIME) - self.set_meta_to_cache() - def expire_cache(self): """ 因为 获取用户的节点,资产,系统用户等都能会缓存,这里会清理所有与该对象有关的 @@ -378,15 +377,6 @@ class AssetPermissionCacheMixin: key = cls.CACHE_KEY_PREFIX + '*' cache.delete_pattern(key) - def get_assets_without_cache(self): - raise NotImplementedError() - - def get_nodes_with_assets_without_cache(self): - raise NotImplementedError() - - def get_system_user_without_cache(self): - raise NotImplementedError() - class AssetPermissionUtil(AssetPermissionCacheMixin): get_permissions_map = { @@ -396,8 +386,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): "Node": get_node_permissions, "SystemUser": get_system_user_permissions, } - assets_prefetch = ('id', 'hostname', 'ip', "platform", "domain_id", - "comment", "is_active", "os", "org_id") + assets_only = ( + 'id', 'hostname', 'ip', "platform", "domain_id", + 'comment', 'is_active', 'os', 'org_id' + ) def __init__(self, obj, cache_policy='0'): self.object = obj @@ -411,6 +403,8 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): self.change_org_if_need() self.nodes = None self._nodes = None + self._assets_direct = None + self._nodes_direct = None @staticmethod def change_org_if_need(): @@ -438,6 +432,8 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): 返回用户/组授权规则直接关联的节点 :return: {node1: {system_user1: {'actions': set()},}} """ + if self._nodes_direct: + return self._nodes_direct nodes = defaultdict(lambda: defaultdict(int)) for perm in self.permissions: actions = [perm.actions] @@ -446,9 +442,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): for node, system_user, action in itertools.product(_nodes, system_users, actions): nodes[node][system_user] |= action self.tree.add_nodes(nodes) + self._nodes_direct = nodes return nodes - def get_nodes(self): + def get_nodes_without_cache(self): self.get_assets_direct() return self.tree.get_nodes() @@ -458,15 +455,18 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): 返回用户授权规则直接关联的资产 :return: {asset1: {system_user1: 1,}} """ + if self._assets_direct: + return self._assets_direct assets = defaultdict(lambda: defaultdict(int)) for perm in self.permissions: actions = [perm.actions] - _assets = perm.assets.all().prefetch_related(*self.assets_prefetch) + _assets = perm.assets.all().only(*self.assets_only) system_users = perm.system_users.all() iterable = itertools.product(_assets, system_users, actions) for asset, system_user, action in iterable: assets[asset][system_user] |= action self.tree.add_assets(assets) + self._assets_direct = assets return assets #@timeit @@ -476,6 +476,7 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): """ if self._assets: return self._assets + self.get_assets_direct() nodes = self.get_nodes_direct() pattern = set() for node in nodes: @@ -484,7 +485,7 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): if pattern: assets = Asset.objects.filter(nodes__key__regex=pattern)\ .prefetch_related('nodes', "protocols")\ - .only(*self.assets_prefetch)\ + .only(*self.assets_only)\ .distinct() else: assets = [] @@ -501,9 +502,11 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): :return: """ self.get_assets_without_cache() - return self.tree.get_nodes_with_assets() + nodes_assets = self.tree.get_nodes_with_assets() + print(nodes_assets.keys()) + return nodes_assets - def get_system_user_without_cache(self): + def get_system_users_without_cache(self): system_users = set() permissions = self.permissions.prefetch_related('system_users') for perm in permissions: diff --git a/apps/settings/signals_handler.py b/apps/settings/signals_handler.py index 9302b63e2..cb7603ec7 100644 --- a/apps/settings/signals_handler.py +++ b/apps/settings/signals_handler.py @@ -24,6 +24,7 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs): @receiver(django_ready, dispatch_uid="my_unique_identifier") def monkey_patch_settings(sender, **kwargs): + logger.debug("Monkey patch settings") cache_key_prefix = '_SETTING_' custom_need_cache_settings = [ 'AUTHENTICATION_BACKENDS', 'TERMINAL_HOST_KEY', @@ -77,7 +78,6 @@ def monkey_patch_settings(sender, **kwargs): @receiver(django_ready) def auto_generate_terminal_host_key(sender, **kwargs): try: - print("Auto gen host key") if Setting.objects.filter(name='TERMINAL_HOST_KEY').exists(): return private_key, public_key = ssh_key_gen() diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 5f85d5e42..471cb00a4 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -648,8 +648,6 @@ jumpserver.initServerSideDataTable = function (options) { $.each(rows, function (id, row) { table.selected_rows.push(row); if (row.id && $.inArray(row.id, table.selected) === -1){ - console.log(table) - console.log(table.selected); table.selected.push(row.id) } }) @@ -1096,3 +1094,8 @@ function objectAttrsIsBool(obj, attrs) { } }) } + +function formatDateAsCN(d) { + var date = new Date(d); + return date.toISOString().replace("T", " ").replace(/\..*/, ""); +} diff --git a/apps/terminal/api/session.py b/apps/terminal/api/session.py index 93628af22..04f97bfc3 100644 --- a/apps/terminal/api/session.py +++ b/apps/terminal/api/session.py @@ -55,7 +55,7 @@ class SessionViewSet(BulkModelViewSet): return super().perform_create(serializer) -class CommandViewSet(viewsets.ViewSet): +class CommandViewSet(viewsets.ModelViewSet): """接受app发送来的command log, 格式如下 { "user": "admin", @@ -70,10 +70,14 @@ class CommandViewSet(viewsets.ViewSet): """ command_store = get_command_storage() serializer_class = SessionCommandSerializer + pagination_class = LimitOffsetPagination permission_classes = (IsOrgAdminOrAppUser | IsAuditor,) + filter_fields = ("asset", "system_user", "user", "input") def get_queryset(self): - self.command_store.filter(**dict(self.request.query_params)) + multi_command_storage = get_multi_command_storage() + queryset = multi_command_storage.filter() + return queryset def create(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data, many=True) @@ -88,12 +92,6 @@ class CommandViewSet(viewsets.ViewSet): logger.error(msg) return Response({"msg": msg}, status=401) - def list(self, request, *args, **kwargs): - multi_command_storage = get_multi_command_storage() - queryset = multi_command_storage.filter() - serializer = self.serializer_class(queryset, many=True) - return Response(serializer.data) - class SessionReplayViewSet(viewsets.ViewSet): serializer_class = serializers.ReplaySerializer diff --git a/apps/terminal/backends/command/multi.py b/apps/terminal/backends/command/multi.py index 1ac822a81..0a4418c0d 100644 --- a/apps/terminal/backends/command/multi.py +++ b/apps/terminal/backends/command/multi.py @@ -9,8 +9,12 @@ class CommandStore(CommandBase): self.storage_list = storage_list def filter(self, **kwargs): - queryset = [] + if len(self.storage_list) == 1: + storage = list(self.storage_list)[0] + queryset = storage.filter(**kwargs) + return queryset + queryset = [] for storage in self.storage_list: queryset.extend(storage.filter(**kwargs)) return sorted(queryset, key=lambda command: command.timestamp, reverse=True) diff --git a/apps/terminal/templates/terminal/command_list.html b/apps/terminal/templates/terminal/command_list.html index 50daf682d..1ce2d80a5 100644 --- a/apps/terminal/templates/terminal/command_list.html +++ b/apps/terminal/templates/terminal/command_list.html @@ -8,109 +8,65 @@ {% endblock %} -{% block content_left_head %} +{% block table_pagination %} {% endblock %} {% block table_search %} -
-
-
- - - to - -
-
-
- -
-
- -
-
- -
-
- -
-
-
- -
-
-
{% endblock %} {% block table_container %} - +
- + - - {% for command in command_list %} - - - - - - - - - - - {% endfor %}
ID {% trans 'Command' %} {% trans 'User' %} {% trans 'Asset' %} {% trans 'System user'%} {% trans 'Session' %} {% trans 'Datetime' %}
{{ forloop.counter }}{{ command.input }}{{ command.user }}{{ command.asset }}{{ command.system_user }}{% trans "Goto" %}{{ command.timestamp|ts_to_date }}
{{ command.output }}
-
+
- +
-{% endblock %} + + +{% endblock %} +{% block content_bottom_left %}{% endblock %} {% block custom_foot_js %} {% endblock %} diff --git a/apps/terminal/utils.py b/apps/terminal/utils.py index d7dace0a9..38a5d24d2 100644 --- a/apps/terminal/utils.py +++ b/apps/terminal/utils.py @@ -9,7 +9,7 @@ from .const import USERS_CACHE_KEY, ASSETS_CACHE_KEY, SYSTEM_USER_CACHE_KEY def get_session_asset_list(): - return Asset.objects.values_list('hostname', flat=True) + return Asset.objects.values_list() def get_session_user_list(): diff --git a/apps/terminal/views/command.py b/apps/terminal/views/command.py index c1a5eb393..2810db9d8 100644 --- a/apps/terminal/views/command.py +++ b/apps/terminal/views/command.py @@ -23,26 +23,13 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, ListView): template_name = "terminal/command_list.html" context_object_name = 'command_list' paginate_by = settings.DISPLAY_PER_PAGE - command = user = asset = system_user = "" date_from = date_to = None permission_classes = [IsOrgAdmin | IsAuditor] def get_queryset(self): - self.command = self.request.GET.get('command', '') - self.user = self.request.GET.get("user", '') - self.asset = self.request.GET.get('asset', '') - self.system_user = self.request.GET.get('system_user', '') filter_kwargs = dict() filter_kwargs['date_from'] = self.date_from filter_kwargs['date_to'] = self.date_to - if self.user: - filter_kwargs['user'] = self.user - if self.asset: - filter_kwargs['asset'] = self.asset - if self.system_user: - filter_kwargs['system_user'] = self.system_user - if self.command: - filter_kwargs['input'] = self.command queryset = common_storage.filter(**filter_kwargs) return queryset @@ -50,15 +37,8 @@ class CommandListView(DatetimeSearchMixin, PermissionsMixin, ListView): context = { 'app': _('Sessions'), 'action': _('Command list'), - 'user_list': utils.get_session_user_list(), - 'asset_list': utils.get_session_asset_list(), - 'system_user_list': utils.get_session_system_user_list(), - 'command': self.command, 'date_from': self.date_from, 'date_to': self.date_to, - 'user': self.user, - 'asset': self.asset, - 'system_user': self.system_user, } kwargs.update(context) return super().get_context_data(**kwargs) diff --git a/apps/users/templates/users/user_group_list.html b/apps/users/templates/users/user_group_list.html index c2839cad9..48394b30d 100644 --- a/apps/users/templates/users/user_group_list.html +++ b/apps/users/templates/users/user_group_list.html @@ -2,27 +2,27 @@ {% load i18n static %} {% block table_search %}
- -
+ +
{% endblock %} {% block table_container %}
{% trans "Create user group" %}