diff --git a/apps/assets/api/tree.py b/apps/assets/api/tree.py index 8304f6bc9..87b955edd 100644 --- a/apps/assets/api/tree.py +++ b/apps/assets/api/tree.py @@ -146,7 +146,7 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi): if current_org.is_root(): orgs = Organization.objects.all() node_levels = [1] - with_assets_node_levels = [] + with_assets_node_levels = None expand_level = 0 else: orgs = [current_org] @@ -211,7 +211,7 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi): assets_q_object = Q(name__icontains=search) | Q(address__icontains=search) if current_org.is_root(): orgs = list(Organization.objects.all()) - with_assets_limit = with_assets_limit / len(orgs) + with_assets_limit = max(100, with_assets_limit // max(1, orgs.count())) else: orgs = [current_org] diff --git a/apps/perms/api/user_permission/tree.py b/apps/perms/api/user_permission/tree.py new file mode 100644 index 000000000..c8d923e54 --- /dev/null +++ b/apps/perms/api/user_permission/tree.py @@ -0,0 +1,128 @@ +from django.db.models import Q + +from rest_framework.generics import ListAPIView +from rest_framework.response import Response +from rest_framework.generics import get_object_or_404 + +from common.utils import get_logger, timeit +from orgs.utils import current_org +from assets.api import SerializeToTreeNodeMixin +from assets.models import Node +from perms.tree import UserPermTree, PermTreeNode + +from .mixin import SelfOrPKUserMixin + +logger = get_logger(__name__) + +__all__ = [ + 'UserPermNodeChildrenAsTreeApi' +] + + +class UserPermNodeChildrenAsTreeApi(SelfOrPKUserMixin, SerializeToTreeNodeMixin, ListAPIView): + + @timeit + def list(self, request, *args, **kwargs): + search = request.query_params.get('search') + key = request.query_params.get('key') + if search: + return self.search_user_perm_tree_with_assets(search) + + if key: + return self.expand_tree_node_with_assets(key) + + return self.init_user_perm_tree_with_assets() + + def init_user_perm_tree_with_assets(self): + ''' 初始化用户权限资产树 - 包含资产 + 全局组织: 返回第1级节点,不返回资产,不展开节点 + 实体组织:返回第1级和第2级节点,返回第1级节点的资产,展开第1级节点 + + 返回收藏节点和资产 + 返回未分组节点和资产 (如果需要) + ''' + if current_org.is_root(): + orgs = self.user.orgs.all() + nodes_level = [1] + with_assets_node_levels = None + expand_level = 0 + else: + orgs = [current_org] + nodes_level = [1, 2] + with_assets_node_levels = [1] + expand_level = 1 + + nodes = [] + assets = [] + for org in orgs: + tree = UserPermTree( + user=self.user, org=org, with_assets_node_levels=with_assets_node_levels + ) + _nodes = tree.get_nodes(levels=nodes_level) + nodes.extend(_nodes) + _assets = tree.get_assets() + assets.extend(_assets) + + nodes = self.serialize_nodes( + nodes, with_asset_amount=True, expand_level=expand_level, with_assets=True + ) + assets = self.serialize_assets(assets) + data = [*nodes, *assets] + return Response(data) + + def expand_tree_node_with_assets(self, key): + ''' 展开用户权限资产树节点 - 包含资产 + 全局组织: 返回展开节点的直接孩子节点,返回展开节点的资产,不展开其他节点 + 实体组织: 同上 + ''' + expand_level = 0 + node = get_object_or_404(Node, key=key) + tree = UserPermTree( + user=self.user, org=node.org, with_assets_node_id=node.id + ) + tree_node = tree.get_node(node.key) + if not tree_node: + return Response(data=[]) + + _nodes = tree_node.children + nodes = self.serialize_nodes( + _nodes, with_asset_amount=True, expand_level=expand_level, with_assets=True + ) + _assets = tree.get_assets() + assets = self.serialize_assets(_assets) + data = [*nodes, *assets] + return Response(data=data) + + def search_user_perm_tree_with_assets(self, search): + ''' 初始化用户权限资产搜索树 - 包含资产 + 全局组织: 返回所有节点,返回所有资产,展开所有节点,搜索资产 (最大 1000, n 个组织,每个组织分配1000/n个资产) + 实体组织: 同上,最大资产数 1000 + ''' + expand_level = 10000 + with_assets_all = True + with_assets_limit = 1000 + if current_org.is_root(): + orgs = self.user.orgs.all() + with_assets_limit = max(100, with_assets_limit // max(1, orgs.count())) + else: + orgs = [current_org] + assets_q_object = Q(name__icontains=search) | Q(address__icontains=search) + nodes = [] + assets = [] + for org in orgs: + tree = UserPermTree( + user=self.user, assets_q_object=assets_q_object, org=org, + with_assets_all=with_assets_all, + with_assets_limit=with_assets_limit + ) + _nodes = tree.get_nodes() + nodes.extend(_nodes) + _assets = tree.get_assets() + assets.extend(_assets) + + nodes = self.serialize_nodes( + nodes, with_asset_amount=True, expand_level=expand_level, with_assets=True + ) + assets = self.serialize_assets(assets) + data = [*nodes, *assets] + return Response(data=data) diff --git a/apps/perms/api/user_permission/tree/__init__.py b/apps/perms/api/user_permission/tree_old/__init__.py similarity index 100% rename from apps/perms/api/user_permission/tree/__init__.py rename to apps/perms/api/user_permission/tree_old/__init__.py diff --git a/apps/perms/api/user_permission/tree/asset.py b/apps/perms/api/user_permission/tree_old/asset.py similarity index 100% rename from apps/perms/api/user_permission/tree/asset.py rename to apps/perms/api/user_permission/tree_old/asset.py diff --git a/apps/perms/api/user_permission/tree/node.py b/apps/perms/api/user_permission/tree_old/node.py similarity index 100% rename from apps/perms/api/user_permission/tree/node.py rename to apps/perms/api/user_permission/tree_old/node.py diff --git a/apps/perms/api/user_permission/tree/node_with_asset.py b/apps/perms/api/user_permission/tree_old/node_with_asset.py similarity index 100% rename from apps/perms/api/user_permission/tree/node_with_asset.py rename to apps/perms/api/user_permission/tree_old/node_with_asset.py diff --git a/apps/perms/tree.py b/apps/perms/tree.py index 5354e86c5..49119f82e 100644 --- a/apps/perms/tree.py +++ b/apps/perms/tree.py @@ -1,9 +1,12 @@ +from django.utils.translation import gettext_lazy as _ from collections import defaultdict -from django.db.models import Q, Count +from django.db.models import Q, Count, TextChoices +from django.core.cache import cache from common.utils import get_logger from users.models import User -from assets.tree.asset_tree import AssetTree, AssetTreeNode +from assets.models import FavoriteAsset, Asset +from assets.tree.asset_tree import AssetTree, AssetTreeNode, AssetTreeNodeAsset from perms.utils.utils import UserPermUtil @@ -23,9 +26,9 @@ class PermTreeNode(AssetTreeNode): # Node with only direct permission assets DA = 'da' - def __init__(self, tp, _id, key, value, assets_count=0, assets=None): - super().__init__(_id, key, value, assets_count) + def __init__(self, tp=None, **kwargs): self.type = tp or self.Type.BRIDGE + super().__init__(**kwargs) def as_dict(self, simple=True): data = super().as_dict(simple=simple) @@ -33,27 +36,26 @@ class PermTreeNode(AssetTreeNode): 'type': self.type, }) return data - + class UserPermTree(AssetTree): TreeNode = PermTreeNode - def __init__(self, user=None, assets_q_object=None, category=None, org=None, with_assets=False): - super().__init__( - assets_q_object=assets_q_object, - category=category, - org=org, - with_assets=with_assets, - full_tree=False - ) + def __init__(self, user, **kwargs): + self._user: User = user - self._util = UserPermUtil(user, org=self._org) - + self._util = UserPermUtil(user, org=kwargs.get('org')) + kwargs.update({ + # 用户授权树只返回有资产的节点 + 'full_tree': False, + }) + super().__init__(**kwargs) + def _make_assets_q_object(self): q = super()._make_assets_q_object() - q_perm_assets = Q(id__in=self._util._user_direct_asset_ids) - q_perm_nodes = Q(node_id__in=self._util._user_direct_node_all_children_ids) + q_perm_assets = Q(id__in=list(self._util._user_direct_asset_ids)) + q_perm_nodes = Q(node_id__in=list(self._util._user_direct_node_all_children_ids)) q = q & (q_perm_assets | q_perm_nodes) return q @@ -61,13 +63,13 @@ class UserPermTree(AssetTree): data = super()._get_tree_node_data(node_id) if node_id in self._util._user_direct_node_all_children_ids: tp = PermTreeNode.Type.DN - elif self._nodes_assets_count_mapper.get(node_id, 0) > 0: + elif self._nodes_assets_amount_mapper.get(node_id, 0) > 0: tp = PermTreeNode.Type.DA else: tp = PermTreeNode.Type.BRIDGE data.update({ 'tp': tp }) return data - + def print(self, simple=True, count=10): self._util.print() super().print(simple=simple, count=count) diff --git a/apps/perms/urls/user_permission.py b/apps/perms/urls/user_permission.py index e17ee3218..248ae1c2f 100644 --- a/apps/perms/urls/user_permission.py +++ b/apps/perms/urls/user_permission.py @@ -23,30 +23,34 @@ user_permission_urlpatterns = [ path('/nodes/children/', api.UserPermedNodeChildrenApi.as_view(), name='user-node-children'), + # 授权树只通过这一个 API 获取, 类似资产树 + path('/nodes/children/tree/', api.UserPermNodeChildrenAsTreeApi.as_view(), + name='user-perm-node-children-tree'), + # tree-asset - path('/assets/tree/', api.UserAllPermedAssetsAsTreeApi.as_view(), - name='user-direct-assets-as-tree'), - path('/ungroup/assets/tree/', api.UserUngroupAssetsAsTreeApi.as_view(), - name='user-ungroup-assets-as-tree'), +# path('/assets/tree/', api.UserAllPermedAssetsAsTreeApi.as_view(), +# name='user-direct-assets-as-tree'), +# path('/ungroup/assets/tree/', api.UserUngroupAssetsAsTreeApi.as_view(), +# name='user-ungroup-assets-as-tree'), # tree-node,不包含资产 - path('/nodes/tree/', api.UserAllPermedNodesAsTreeApi.as_view(), - name='user-all-nodes-as-tree'), - path('/nodes/children/tree/', api.UserPermedNodeChildrenAsTreeApi.as_view(), - name='user-node-children-as-tree'), +# path('/nodes/tree/', api.UserAllPermedNodesAsTreeApi.as_view(), +# name='user-all-nodes-as-tree'), +# path('/nodes/children/tree/', api.UserPermedNodeChildrenAsTreeApi.as_view(), +# name='user-node-children-as-tree'), # tree-node-with-asset # 异步树 - path('/nodes/children-with-assets/tree/', - api.UserPermedNodeChildrenWithAssetsAsTreeApi.as_view(), - name='user-node-children-with-assets-as-tree'), - path('/nodes/children-with-assets/category/tree/', - api.UserPermedNodeChildrenWithAssetsAsCategoryTreeApi.as_view(), - name='user-node-children-with-assets-as-category-tree'), +# path('/nodes/children-with-assets/tree/', +# api.UserPermedNodeChildrenWithAssetsAsTreeApi.as_view(), +# name='user-node-children-with-assets-as-tree'), +# path('/nodes/children-with-assets/category/tree/', +# api.UserPermedNodeChildrenWithAssetsAsCategoryTreeApi.as_view(), +# name='user-node-children-with-assets-as-category-tree'), # 同步树 - path('/nodes/all-with-assets/tree/', - api.UserPermedNodesWithAssetsAsTreeApi.as_view(), - name='user-nodes-with-assets-as-tree'), +# path('/nodes/all-with-assets/tree/', +# api.UserPermedNodesWithAssetsAsTreeApi.as_view(), +# name='user-nodes-with-assets-as-tree'), ] user_group_permission_urlpatterns = [ diff --git a/apps/perms/utils/utils.py b/apps/perms/utils/utils.py index 5c7e02c05..60ef890bc 100644 --- a/apps/perms/utils/utils.py +++ b/apps/perms/utils/utils.py @@ -34,6 +34,7 @@ class UserPermUtil(object): self._user_direct_node_all_children_ids = set() self._init() + @timeit def _init(self): self._load_user_permission_ids() self._load_user_group_ids() @@ -45,7 +46,8 @@ class UserPermUtil(object): @timeit def _load_user_permission_ids(self): perm_ids = self.PermUserThrough.objects.filter( - user_id=self._user.id + user_id=self._user.id, + assetpermission__org_id=self._org.id ).distinct('assetpermission_id').values_list('assetpermission_id', flat=True) perm_ids = self._uuids_to_string(perm_ids) self._user_permission_ids.update(perm_ids) @@ -54,7 +56,8 @@ class UserPermUtil(object): @timeit def _load_user_group_ids(self): group_ids = self.UserGroupThrough.objects.filter( - user_id=self._user.id + user_id=self._user.id, + usergroup__org_id=self._org.id ).distinct('usergroup_id').values_list('usergroup_id', flat=True) group_ids = self._uuids_to_string(group_ids) self._user_group_ids.update(group_ids) @@ -62,7 +65,8 @@ class UserPermUtil(object): @timeit def _load_user_group_permission_ids(self): perm_ids = self.PermUserGroupThrough.objects.filter( - usergroup_id__in=self._user_group_ids + usergroup_id__in=self._user_group_ids, + assetpermission__org_id=self._org.id ).distinct('assetpermission_id').values_list('assetpermission_id', flat=True) perm_ids = self._uuids_to_string(perm_ids) self._user_group_permission_ids.update(perm_ids) @@ -89,27 +93,35 @@ class UserPermUtil(object): nid_key_pairs = Node.objects.filter(org_id=self._org.id).values_list('id', 'key') nid_key_mapper = { str(nid): key for nid, key in nid_key_pairs } - dn_keys = [ nid_key_mapper[nid] for nid in self._user_direct_node_ids ] + dn_keys = [ + nid_key_mapper[nid] for nid in self._user_direct_node_ids + ] def has_ancestor_in_direct_nodes(key: str) -> bool: - ancestor_keys = [ ':'.join(key.split(':')[:i]) for i in range(1, key.count(':') + 1) ] + ancestor_keys = [ + ':'.join(key.split(':')[:i]) + for i in range(1, key.count(':') + 1) + ] return bool(set(ancestor_keys) & set(dn_keys)) - dn_children_ids = [ nid for nid, key in nid_key_mapper.items() if has_ancestor_in_direct_nodes(key) ] + dn_children_ids = [ + nid for nid, key in nid_key_mapper.items() + if has_ancestor_in_direct_nodes(key) + ] self._user_direct_node_all_children_ids.update(self._user_direct_node_ids) self._user_direct_node_all_children_ids.update(dn_children_ids) def get_node_assets(self, node: Node): - ''' 获取节点下授权的直接资产, Luna 页面展开时需要 ''' + ''' 获取节点下授权的直接资产 ''' q = Q(node_id=node.id) if str(node.id) not in self._user_direct_node_all_children_ids: q &= Q(id__in=self._user_direct_asset_ids) - assets = Asset.objects.filter(q) + assets = Asset.objects.filter(org_id=self._org.id).filter(q) return assets def get_node_all_assets(self, node: Node): - ''' 获取节点及其子节点下所有授权资产, 测试时需要 ''' + ''' 获取节点及其子节点下所有授权资产 ''' if str(node.id) in self._user_direct_node_all_children_ids: assets = node.get_all_assets() return assets @@ -121,7 +133,7 @@ class UserPermUtil(object): q = Q(node_id__in=dn_all_nodes_ids) q |= Q(node_id__in=other_nodes_ids) & Q(id__in=self._user_direct_asset_ids) - assets = Asset.objects.filter(q) + assets = Asset.objects.filter(org_id=self._org.id).filter(q) return assets def _uuids_to_string(self, uuids):