diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py index 1227b01c9..ff5047ba3 100644 --- a/apps/assets/api/asset.py +++ b/apps/assets/api/asset.py @@ -11,8 +11,7 @@ from django.db.models import Q from common.mixins import IDInFilterMixin from common.utils import get_logger -from ..hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \ - NodePermissionUtil +from ..hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser from ..models import Asset, SystemUser, AdminUser, Node from .. import serializers from ..tasks import update_asset_hardware_info_manual, \ @@ -22,7 +21,7 @@ from ..utils import LabelFilter logger = get_logger(__file__) __all__ = [ - 'AssetViewSet', 'UserAssetListView', 'AssetListUpdateApi', + 'AssetViewSet', 'AssetListUpdateApi', 'AssetRefreshHardwareApi', 'AssetAdminUserTestApi' ] @@ -71,19 +70,6 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): return queryset -class UserAssetListView(generics.ListAPIView): - queryset = Asset.objects.all() - serializer_class = serializers.AssetSerializer - permission_classes = (IsValidUser,) - - def get_queryset(self): - assets_granted = NodePermissionUtil.get_user_assets(self.request.user).keys() - queryset = self.queryset.filter( - id__in=[asset.id for asset in assets_granted] - ) - return queryset - - class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): """ Asset bulk update api diff --git a/apps/assets/hands.py b/apps/assets/hands.py index ad44052d3..a1a376135 100644 --- a/apps/assets/hands.py +++ b/apps/assets/hands.py @@ -14,4 +14,3 @@ from common.mixins import AdminUserRequiredMixin from common.permissions import IsAppUser, IsSuperUser, IsValidUser, IsSuperUserOrAppUser from users.models import User, UserGroup -from perms.utils import NodePermissionUtil diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 6e6b6c678..a6a1d48d0 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -5,6 +5,7 @@ import uuid import logging import random +from functools import reduce from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -149,6 +150,15 @@ class Asset(models.Model): nodes = self.nodes.all() or [Node.root()] return nodes + def get_all_nodes(self, flat=False): + nodes = [] + for node in self.get_nodes_or_cache(): + _nodes = node.get_ancestor(with_self=True) + _nodes.append(_nodes) + if flat: + nodes = list(reduce(lambda x, y: set(x) | set(y), nodes)) + return nodes + @property def nodes_cache_key(self): key = "NODES_OF_{}".format(str(self.id)) diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index ed712c8f8..88b685d28 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -28,10 +28,9 @@ class Node(models.Model): @property def full_value(self): - ancestor = [a.value for a in self.ancestor] + ancestor = [a.value for a in self.get_ancestor(with_self=True)] if self.is_root(): return self.value - ancestor.append(self.value) return ' / '.join(ancestor) @property @@ -55,32 +54,35 @@ class Node(models.Model): return "{}:{}".format(self.key, mark) def create_child(self, value): - child_key = self.get_next_child_key() - child = self.__class__.objects.create(key=child_key, value=value) - return child + with transaction.atomic(): + child_key = self.get_next_child_key() + child = self.__class__.objects.create(key=child_key, value=value) + return child - def get_children(self): + def get_children(self, with_self=False): + pattern = r'^{0}$|^{}:[0-9]+$' if with_self else r'^{}:[0-9]+$' return self.__class__.objects.filter( - key__regex=r'^{}:[0-9]+$'.format(self.key) + key__regex=pattern.format(self.key) ) - def get_children_with_self(self): + def get_all_children(self, with_self=False): + pattern = r'^{0}$|^{0}:' if with_self else r'^{0}' return self.__class__.objects.filter( - key__regex=r'^{0}$|^{0}:[0-9]+$'.format(self.key) + key__regex=pattern.format(self.key) ) - def get_all_children(self): - return self.__class__.objects.filter( - key__startswith='{}:'.format(self.key) - ) - - def get_all_children_with_self(self): - return self.__class__.objects.filter( - key__regex=r'^{0}$|^{0}:'.format(self.key) + def get_sibling(self, with_self=False): + key = ':'.join(self.key.split(':')[:-1]) + pattern = r'^{}:[0-9]+$'.format(key) + sibling = self.__class__.objects.filter( + key__regex=pattern.format(self.key) ) + if not with_self: + sibling = sibling.exclude(key=self.key) + return sibling def get_family(self): - ancestor = self.ancestor + ancestor = self.get_ancestor() children = self.get_all_children() return [*tuple(ancestor), self, *tuple(children)] @@ -102,7 +104,7 @@ class Node(models.Model): if self.is_root(): assets = Asset.objects.all() else: - nodes = self.get_all_children_with_self() + nodes = self.get_all_children(with_self=True) assets = Asset.objects.filter(nodes__in=nodes).distinct() return assets @@ -127,24 +129,21 @@ class Node(models.Model): def parent(self, parent): self.key = parent.get_next_child_key() - @property - def ancestor(self): + def get_ancestor(self, with_self=False): if self.is_root(): ancestor = self.__class__.objects.filter(key='0') - else: - _key = self.key.split(':') - ancestor_keys = [] - for i in range(len(_key)-1): - _key.pop() - ancestor_keys.append(':'.join(_key)) - ancestor = self.__class__.objects.filter(key__in=ancestor_keys) - ancestor = list(ancestor) - return ancestor + return ancestor - @property - def ancestor_with_self(self): - ancestor = list(self.ancestor) - ancestor.insert(0, self) + _key = self.key.split(':') + if not with_self: + _key.pop() + ancestor_keys = [] + for i in range(len(_key)): + ancestor_keys.append(':'.join(_key)) + _key.pop() + ancestor = self.__class__.objects.filter( + key__in=ancestor_keys + ).order_by('key') return ancestor @classmethod @@ -153,3 +152,12 @@ class Node(models.Model): key='0', defaults={"key": '0', 'value': "ROOT"} ) return obj + + +class Tree: + def __init__(self, root): + self.root = root + self.nodes = [] + + def add_node(self, node): + pass diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index 4429d0f24..ce622d648 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -23,8 +23,6 @@ urlpatterns = [ api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'), url(r'^v1/assets/(?P[0-9a-zA-Z\-]{36})/alive/$', api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'), - url(r'^v1/assets/user-assets/$', - api.UserAssetListView.as_view(), name='user-asset-list'), url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/nodes/$', api.ReplaceNodesAdminUserApi.as_view(), name='replace-nodes-admin-user'), url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/auth/$', @@ -35,17 +33,26 @@ urlpatterns = [ api.SystemUserPushApi.as_view(), name='system-user-push'), url(r'^v1/system-user/(?P[0-9a-zA-Z\-]{36})/connective/$', api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/$', api.NodeChildrenApi.as_view(), name='node-children'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/$', + api.NodeChildrenApi.as_view(), name='node-children'), url(r'^v1/nodes/children/$', api.NodeChildrenApi.as_view(), name='node-children-2'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.NodeAssetsApi.as_view(), name='node-assets'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/replace/$', api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/test-connective/$', api.TestNodeConnectiveApi.as_view(), name='node-test-connective'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/add/$', + api.NodeAddChildrenApi.as_view(), name='node-add-children'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', + api.NodeAssetsApi.as_view(), name='node-assets'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/add/$', + api.NodeAddAssetsApi.as_view(), name='node-add-assets'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/replace/$', + api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/remove/$', + api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', + api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'), + url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/test-connective/$', + api.TestNodeConnectiveApi.as_view(), name='node-test-connective'), - url(r'^v1/gateway/(?P[0-9a-zA-Z\-]{36})/test-connective/$', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'), + url(r'^v1/gateway/(?P[0-9a-zA-Z\-]{36})/test-connective/$', + api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'), ] urlpatterns += router.urls diff --git a/apps/perms/api.py b/apps/perms/api.py index bd2fb1139..b4172d03f 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -41,11 +41,11 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): asset = get_object_or_404(Asset, pk=asset_id) permissions = set(queryset.filter(assets=asset)) for node in asset.nodes.all(): - inherit_nodes.update(set(node.ancestor_with_self)) + inherit_nodes.update(set(node.get_ancestor(with_self=True))) elif node_id: node = get_object_or_404(Node, pk=node_id) permissions = set(queryset.filter(nodes=node)) - inherit_nodes = node.ancestor + inherit_nodes = node.get_ancestor() for n in inherit_nodes: _permissions = queryset.filter(nodes=n) diff --git a/apps/perms/utils.py b/apps/perms/utils.py index 4844cda06..91f530f1d 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -3,12 +3,13 @@ from __future__ import absolute_import, unicode_literals import collections from collections import defaultdict +from django.db.models import Q from django.utils import timezone import copy from common.utils import set_or_append_attr_bulk, get_logger from .models import AssetPermission -from .hands import Node +from .hands import Node, User, UserGroup, Asset, SystemUser logger = get_logger(__file__) @@ -254,106 +255,47 @@ class AssetPermissionUtil: return system_users -# Abandon -class NodePermissionUtil: - """ +class AssetPermissionUtilsV2: + def __init__(self, obj): + self.object = obj + self._permissions = None - """ + @staticmethod + def get_user_permissions(user): + groups = user.groups.all() + return AssetPermission.objects.all().valid().filter( + Q(users=user) | Q(user_groups=groups) + ) @staticmethod def get_user_group_permissions(user_group): - return user_group.nodepermission_set.all() \ - .filter(is_active=True) \ - .filter(date_expired__gt=timezone.now()) + return AssetPermission.objects.all().valid().filter( + user_groups=user_group + ) + + @staticmethod + def get_asset_permissions(asset): + direct_nodes = asset.get_nodes_or_cache() + + return AssetPermission.objects.all().valid().filter( + Q(assets=asset) | Q(nodes=direct_nodes) + ) + + @staticmethod + def get_node_permissions(node): + return AssetPermission.objects.all().valid().filter(nodes=node) @staticmethod def get_system_user_permissions(system_user): - return system_user.nodepermission_set.all() \ - .filter(is_active=True) \ - .filter(date_expired__gt=timezone.now()) + return AssetPermission.objects.valid().all().filter( + system_users=system_user + ) - @classmethod - def get_user_group_nodes(cls, user_group): - """ - 获取用户组授权的node和系统用户 - :param user_group: - :return: {"node": set(systemuser1, systemuser2), ..} - """ - permissions = cls.get_user_group_permissions(user_group) - nodes_directed = collections.defaultdict(set) + @property + def permissions(self): + if self._permissions: + return self._permissions + if isinstance(self.object, User): + pass - for perm in permissions: - nodes_directed[perm.node].add(perm.system_user) - - nodes = copy.deepcopy(nodes_directed) - for node, system_users in nodes_directed.items(): - for child in node.get_all_children_with_self(): - nodes[child].update(system_users) - return nodes - - @classmethod - def get_user_group_nodes_with_assets(cls, user_group): - """ - 获取用户组授权的节点和系统用户,节点下带有资产 - :param user_group: - :return: {"node": {"assets": "", "system_user": ""}, {}} - """ - nodes = cls.get_user_group_nodes(user_group) - nodes_with_assets = dict() - for node, system_users in nodes.items(): - nodes_with_assets[node] = { - 'assets': node.get_valid_assets(), - 'system_users': system_users - } - return nodes_with_assets - - @classmethod - def get_user_group_assets(cls, user_group): - assets = collections.defaultdict(set) - permissions = cls.get_user_group_permissions(user_group) - - for perm in permissions: - for asset in perm.node.get_all_assets(): - assets[asset].add(perm.system_user) - return assets - - @classmethod - def get_user_nodes(cls, user): - nodes = collections.defaultdict(set) - groups = user.groups.all() - for group in groups: - group_nodes = cls.get_user_group_nodes(group) - for node, system_users in group_nodes.items(): - nodes[node].update(system_users) - return nodes - - @classmethod - def get_user_nodes_with_assets(cls, user): - nodes = cls.get_user_nodes(user) - nodes_with_assets = dict() - for node, system_users in nodes.items(): - nodes_with_assets[node] = { - 'assets': node.get_valid_assets(), - 'system_users': system_users - } - return nodes_with_assets - - @classmethod - def get_user_assets(cls, user): - assets = collections.defaultdict(set) - nodes_with_assets = cls.get_user_nodes_with_assets(user) - - for v in nodes_with_assets.values(): - for asset in v['assets']: - assets[asset].update(v['system_users']) - return assets - - @classmethod - def get_system_user_assets(cls, system_user): - assets = set() - permissions = cls.get_system_user_permissions(system_user) - - for perm in permissions: - assets.update(perm.node.get_all_assets()) - return assets