diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index c294ff2ed..259405104 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext from django.core.cache import cache -from common.utils import get_logger +from common.utils import get_logger, timeit from orgs.mixins.models import OrgModelMixin, OrgManager from orgs.utils import set_current_org, get_current_org, tmp_to_org from orgs.models import Organization @@ -298,14 +298,15 @@ class NodeAssetsMixin: return self.get_all_assets().valid() @classmethod + @timeit def get_nodes_all_assets(cls, nodes_keys): from .asset import Asset nodes_keys = cls.clean_children_keys(nodes_keys) - pattern = set() + assets_ids = set() for key in nodes_keys: - pattern.add(r'^{0}$|^{0}:'.format(key)) - pattern = '|'.join(list(pattern)) - return Asset.objects.filter(nodes__key__regex=pattern) + node_assets_ids = cls.tree().all_assets(key) + assets_ids.update(set(node_assets_ids)) + return Asset.objects.filter(id__in=assets_ids) class SomeNodesMixin: diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index 2c980d22c..ea5f8f4ad 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -7,13 +7,14 @@ from django.db.models.signals import ( from django.db.models.aggregates import Count from django.dispatch import receiver -from common.utils import get_logger +from common.utils import get_logger, timeit from common.decorator import on_transaction_commit from .models import Asset, SystemUser, Node from .tasks import ( update_assets_hardware_info_util, test_asset_connectivity_util, - push_system_user_to_assets + push_system_user_to_assets, + add_nodes_assets_to_system_users ) @@ -99,7 +100,7 @@ def on_system_user_nodes_change(sender, instance=None, action=None, model=None, """ if action != "post_add": return - logger.info("System user `{}` nodes update signal recv".format(instance)) + logger.info("System user nodes update signal recv: {}".format(instance)) queryset = model.objects.filter(pk__in=pk_set) if model == Node: @@ -108,9 +109,7 @@ def on_system_user_nodes_change(sender, instance=None, action=None, model=None, else: nodes_keys = [instance.key] system_users = queryset - assets = Node.get_nodes_all_assets(nodes_keys).values_list('id', flat=True) - for system_user in system_users: - system_user.assets.add(*tuple(assets)) + add_nodes_assets_to_system_users.delay(nodes_keys, system_users) @receiver(m2m_changed, sender=Asset.nodes.through) diff --git a/apps/assets/tasks/__init__.py b/apps/assets/tasks/__init__.py index eb8c5a7b9..6f53c9fa2 100644 --- a/apps/assets/tasks/__init__.py +++ b/apps/assets/tasks/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # from .utils import * +from .common import * from .admin_user_connectivity import * from .asset_connectivity import * from .asset_user_connectivity import * diff --git a/apps/assets/tasks/common.py b/apps/assets/tasks/common.py new file mode 100644 index 000000000..2c352b64e --- /dev/null +++ b/apps/assets/tasks/common.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# + +from celery import shared_task + +__all__ = ['add_nodes_assets_to_system_users'] + + +@shared_task +def add_nodes_assets_to_system_users(nodes_keys, system_users): + from ..models import Node + assets = Node.get_nodes_all_assets(nodes_keys).values_list('id', flat=True) + for system_user in system_users: + system_user.assets.add(*tuple(assets)) diff --git a/apps/assets/templates/assets/user_asset_list.html b/apps/assets/templates/assets/user_asset_list.html index cd268a699..a158244a5 100644 --- a/apps/assets/templates/assets/user_asset_list.html +++ b/apps/assets/templates/assets/user_asset_list.html @@ -24,7 +24,7 @@ var treeUrl = "{% url 'api-perms:my-nodes-children-as-tree' %}?&cache_policy=1"; var assetTableUrl = "{% url 'api-perms:my-assets' %}?cache_policy=1"; var selectUrl = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}?cache_policy=1&all=1'; -var systemUsersUrl = "{% url 'api-perms:my-asset-system-users' asset_id=DEFAULT_PK %}"; +var systemUsersUrl = "{% url 'api-perms:my-asset-system-users' asset_id=DEFAULT_PK %}?cache_policy=1"; var showAssetHref = false; // Need input default true var actions = { targets: 4, createdCell: function (td, cellData) { diff --git a/apps/common/utils/common.py b/apps/common/utils/common.py index c73e9611d..667f166b3 100644 --- a/apps/common/utils/common.py +++ b/apps/common/utils/common.py @@ -213,3 +213,16 @@ def dict_get_any(d, keys): if value: return value return None + + +class lazyproperty: + def __init__(self, func): + self.func = func + + def __get__(self, instance, cls): + if instance is None: + return self + else: + value = self.func(instance) + setattr(instance, self.func.__name__, value) + return value \ No newline at end of file diff --git a/apps/perms/api/asset_permission.py b/apps/perms/api/asset_permission.py index 3e7f4a078..c5745b84a 100644 --- a/apps/perms/api/asset_permission.py +++ b/apps/perms/api/asset_permission.py @@ -180,6 +180,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView): users = serializer.validated_data.get('users') if users: perm.users.remove(*tuple(users)) + perm.save() return Response({"msg": "ok"}) else: return Response({"error": serializer.errors}) @@ -197,6 +198,7 @@ class AssetPermissionAddUserApi(RetrieveUpdateAPIView): users = serializer.validated_data.get('users') if users: perm.users.add(*tuple(users)) + perm.save() return Response({"msg": "ok"}) else: return Response({"error": serializer.errors}) @@ -217,6 +219,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView): assets = serializer.validated_data.get('assets') if assets: perm.assets.remove(*tuple(assets)) + perm.save() return Response({"msg": "ok"}) else: return Response({"error": serializer.errors}) @@ -234,6 +237,7 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView): assets = serializer.validated_data.get('assets') if assets: perm.assets.add(*tuple(assets)) + perm.save() return Response({"msg": "ok"}) else: return Response({"error": serializer.errors}) diff --git a/apps/perms/api/user_permission/common.py b/apps/perms/api/user_permission/common.py index 6a2f8adf7..7e4271af8 100644 --- a/apps/perms/api/user_permission/common.py +++ b/apps/perms/api/user_permission/common.py @@ -51,8 +51,7 @@ class GetUserAssetPermissionActionsApi(UserAssetPermissionMixin, asset = get_object_or_404(Asset, id=asset_id) system_user = get_object_or_404(SystemUser, id=system_id) - system_users_actions = self.util.get_asset_system_users_with_actions( - asset) + system_users_actions = self.util.get_asset_system_users_with_actions(asset) actions = system_users_actions.get(system_user) return {"actions": actions} @@ -103,8 +102,7 @@ class UserGrantedAssetSystemUsersApi(UserAssetPermissionMixin, ListAPIView): def get_queryset(self): asset_id = self.kwargs.get('asset_id') asset = get_object_or_404(Asset, id=asset_id) - system_users_with_actions = self.util.get_asset_system_users_with_actions( - asset) + system_users_with_actions = self.util.get_asset_system_users_with_actions(asset) system_users = [] for system_user, actions in system_users_with_actions.items(): system_user.actions = actions diff --git a/apps/perms/api/user_permission/mixin.py b/apps/perms/api/user_permission/mixin.py index 9ca8c5e7d..a25e49251 100644 --- a/apps/perms/api/user_permission/mixin.py +++ b/apps/perms/api/user_permission/mixin.py @@ -1,23 +1,27 @@ # -*- coding: utf-8 -*- # +from common.utils import lazyproperty +from common.tree import TreeNodeSerializer from ..mixin import UserPermissionMixin from ...utils import AssetPermissionUtilV2, ParserNode from ...hands import Node, Asset -from common.tree import TreeNodeSerializer class UserAssetPermissionMixin(UserPermissionMixin): util = None - tree = None - def initial(self, *args, **kwargs): - super().initial(*args, *kwargs) + @lazyproperty + def util(self): cache_policy = self.request.query_params.get('cache_policy', '0') system_user_id = self.request.query_params.get("system_user") - self.util = AssetPermissionUtilV2(self.obj, cache_policy=cache_policy) + util = AssetPermissionUtilV2(self.obj, cache_policy=cache_policy) if system_user_id: - self.util.filter_permissions(system_users=system_user_id) - self.tree = self.util.get_user_tree() + util.filter_permissions(system_users=system_user_id) + return util + + @lazyproperty + def tree(self): + return self.util.get_user_tree() class UserNodeTreeMixin: diff --git a/apps/perms/signals_handler.py b/apps/perms/signals_handler.py index 8066ce5ca..eecd101e8 100644 --- a/apps/perms/signals_handler.py +++ b/apps/perms/signals_handler.py @@ -19,18 +19,6 @@ permission_m2m_senders = ( ) -@on_transaction_commit -def on_permission_m2m_change(sender, action='', **kwargs): - if not action.startswith('post'): - return - logger.debug('Asset permission m2m changed, refresh user tree cache') - AssetPermissionUtilV2.expire_all_user_tree_cache() - - -for sender in permission_m2m_senders: - m2m_changed.connect(on_permission_m2m_change, sender=sender) - - @receiver([post_save, post_delete], sender=AssetPermission) @on_transaction_commit def on_permission_change(sender, action='', **kwargs): diff --git a/apps/perms/utils/asset_permission.py b/apps/perms/utils/asset_permission.py index ed3616933..f472a162a 100644 --- a/apps/perms/utils/asset_permission.py +++ b/apps/perms/utils/asset_permission.py @@ -1,7 +1,7 @@ # coding: utf-8 +import time import pickle -import threading from collections import defaultdict from functools import reduce from hashlib import md5 @@ -12,7 +12,7 @@ from django.db.models import Q from django.conf import settings from orgs.utils import set_to_root_org -from common.utils import get_logger, timeit +from common.utils import get_logger, timeit, lazyproperty from common.tree import TreeNode from assets.utils import TreeService from ..models import AssetPermission @@ -131,18 +131,19 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): self.cache_policy = cache_policy self.obj_id = str(obj.id) if obj else None self._permissions = None - self._permissions_id = None # 标记_permission的唯一值 self._filter_id = 'None' # 当通过filter更改 permission是标记 self.change_org_if_need() self._user_tree = None self._user_tree_filter_id = 'None' - self.full_tree = Node.tree() - self.mutex = threading.Lock() @staticmethod def change_org_if_need(): set_to_root_org() + @lazyproperty + def full_tree(self): + return Node.tree() + @property def permissions(self): if self._permissions: @@ -161,7 +162,7 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): self._permissions = self.permissions.filter(**filters) self._filter_id = md5(filters_json.encode()).hexdigest() - @property + @lazyproperty def user_tree(self): return self.get_user_tree() @@ -305,27 +306,26 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): @timeit def get_user_tree(self): # 使用锁,保证多次获取tree的时候顺序执行,可以使用缓存 - with self.mutex: - user_tree = self.get_user_tree_from_local() - if user_tree: - return user_tree - user_tree = self.get_user_tree_from_cache_if_need() - if user_tree: - self.set_user_tree_to_local(user_tree) - return user_tree - user_tree = TreeService() - full_tree_root = self.full_tree.root_node() - user_tree.create_node( - tag=full_tree_root.tag, - identifier=full_tree_root.identifier - ) - self.add_direct_nodes_to_user_tree(user_tree) - self.add_single_assets_node_to_user_tree(user_tree) - self.parse_user_tree_to_full_tree(user_tree) - self.add_empty_node_if_need(user_tree) - self.set_user_tree_to_cache_if_need(user_tree) + user_tree = self.get_user_tree_from_local() + if user_tree: + return user_tree + user_tree = self.get_user_tree_from_cache_if_need() + if user_tree: self.set_user_tree_to_local(user_tree) return user_tree + user_tree = TreeService() + full_tree_root = self.full_tree.root_node() + user_tree.create_node( + tag=full_tree_root.tag, + identifier=full_tree_root.identifier + ) + self.add_direct_nodes_to_user_tree(user_tree) + self.add_single_assets_node_to_user_tree(user_tree) + self.parse_user_tree_to_full_tree(user_tree) + self.add_empty_node_if_need(user_tree) + self.set_user_tree_to_cache_if_need(user_tree) + self.set_user_tree_to_local(user_tree) + return user_tree # Todo: 是否可以获取多个资产的系统用户 def get_asset_system_users_with_actions(self, asset): diff --git a/apps/users/templates/users/_granted_assets.html b/apps/users/templates/users/_granted_assets.html index 173aec607..eabff1487 100644 --- a/apps/users/templates/users/_granted_assets.html +++ b/apps/users/templates/users/_granted_assets.html @@ -72,16 +72,10 @@ function initTable() { $(td).html(cellData); }}, {targets: 3, createdCell: function (td, cellData) { - function success(systemUsers) { - var users = []; - $.each(systemUsers, function (id, data) { - var name = htmlEscape(data.name); - users.push(name); - }); - $(td).html(users.join(',')) - } - $(td).html("{% trans 'Loading' %}"); - getGrantedAssetSystemUsers(cellData, success) + var innerHtml = ' {% trans "Show" %} ' + .replace('99999999', cellData); + $(td).html(innerHtml); + }}, ], ajax_url: assetTableUrl, @@ -185,5 +179,19 @@ $(document).ready(function () { var val = $(this).text(); $("#user_assets_table_filter input").val(val); assetTable.search(val).draw(); - }) +}).on('click', '.btn-show-system-users', function () { + var $this = $(this); + var assetId = $this.data('aid'); + + function success(systemUsers) { + var users = []; + $.each(systemUsers, function (id, data) { + var name = htmlEscape(data.name); + users.push(name); + }); + $this.parent().html(users.join(',')) + } + + getGrantedAssetSystemUsers(assetId, success) +}) diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index d70418081..184537cd4 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -33,9 +33,9 @@ {% block custom_foot_js %}