mirror of https://github.com/jumpserver/jumpserver
Merge branch 'v3' of github.com:jumpserver/jumpserver into v3
commit
a27b43107c
|
@ -30,16 +30,13 @@ __all__ = [
|
|||
|
||||
class AssetFilterSet(BaseFilterSet):
|
||||
type = django_filters.CharFilter(field_name="platform__type", lookup_expr="exact")
|
||||
category = django_filters.CharFilter(
|
||||
field_name="platform__category", lookup_expr="exact"
|
||||
)
|
||||
hostname = django_filters.CharFilter(field_name="name", lookup_expr="exact")
|
||||
category = django_filters.CharFilter(field_name="platform__category", lookup_expr="exact")
|
||||
|
||||
class Meta:
|
||||
model = Asset
|
||||
fields = [
|
||||
"id", "name", "address", "is_active",
|
||||
"type", "category", "hostname"
|
||||
"type", "category"
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ __all__ = [
|
|||
|
||||
|
||||
class UserGroupGrantedAssetsApi(ListAPIView):
|
||||
serializer_class = serializers.AssetGrantedSerializer
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
serializer_class = serializers.AssetPermedSerializer
|
||||
only_fields = serializers.AssetPermedSerializer.Meta.only_fields
|
||||
filterset_fields = ['name', 'address', 'id', 'comment']
|
||||
search_fields = ['name', 'address', 'comment']
|
||||
rbac_perms = {
|
||||
|
@ -60,8 +60,8 @@ class UserGroupGrantedAssetsApi(ListAPIView):
|
|||
|
||||
|
||||
class UserGroupGrantedNodeAssetsApi(ListAPIView):
|
||||
serializer_class = serializers.AssetGrantedSerializer
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
serializer_class = serializers.AssetPermedSerializer
|
||||
only_fields = serializers.AssetPermedSerializer.Meta.only_fields
|
||||
filterset_fields = ['name', 'address', 'id', 'comment']
|
||||
search_fields = ['name', 'address', 'comment']
|
||||
rbac_perms = {
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
#
|
||||
from .nodes import *
|
||||
from .assets import *
|
||||
from .nodes_with_assets import *
|
||||
from .accounts import *
|
||||
from .tree import *
|
||||
|
|
|
@ -25,6 +25,5 @@ class UserPermedAssetAccountsApi(SelfOrPKUserMixin, ListAPIView):
|
|||
return asset
|
||||
|
||||
def get_queryset(self):
|
||||
util = PermAccountUtil()
|
||||
accounts = util.get_permed_accounts_for_user(self.user, self.asset)
|
||||
accounts = PermAccountUtil().get_permed_accounts_for_user(self.user, self.asset)
|
||||
return accounts
|
||||
|
|
|
@ -1,109 +1,76 @@
|
|||
from django.conf import settings
|
||||
import abc
|
||||
from rest_framework.generics import ListAPIView
|
||||
|
||||
from assets.models import Asset, Node
|
||||
from common.utils import get_logger
|
||||
from assets.api.asset.asset import AssetFilterSet
|
||||
from perms import serializers
|
||||
from perms.pagination import AllGrantedAssetPagination
|
||||
from perms.pagination import NodeGrantedAssetPagination
|
||||
from perms.pagination import AllPermedAssetPagination
|
||||
from perms.pagination import NodePermedAssetPagination
|
||||
from perms.utils.user_permission import UserGrantedAssetsQueryUtils
|
||||
from common.utils import get_logger, lazyproperty
|
||||
|
||||
from .mixin import (
|
||||
SelfOrPKUserMixin, RebuildTreeMixin,
|
||||
PermedAssetSerializerMixin, AssetsTreeFormatMixin
|
||||
SelfOrPKUserMixin
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'UserAllPermedAssetsApi',
|
||||
'UserDirectPermedAssetsApi',
|
||||
'UserFavoriteAssetsApi',
|
||||
'UserDirectPermedAssetsAsTreeApi',
|
||||
'UserUngroupAssetsAsTreeApi',
|
||||
'UserAllPermedAssetsApi',
|
||||
'UserPermedNodeAssetsApi',
|
||||
]
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class UserDirectPermedAssetsApi(SelfOrPKUserMixin, PermedAssetSerializerMixin, ListAPIView):
|
||||
""" 直接授权给用户的资产 """
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ListAPIView):
|
||||
ordering = ('name',)
|
||||
ordering_fields = ("name", "address")
|
||||
search_fields = ('name', 'address', 'comment')
|
||||
filterset_class = AssetFilterSet
|
||||
serializer_class = serializers.AssetPermedSerializer
|
||||
only_fields = serializers.AssetPermedSerializer.Meta.only_fields
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, 'swagger_fake_view', False):
|
||||
return Asset.objects.none()
|
||||
|
||||
assets = UserGrantedAssetsQueryUtils(self.user) \
|
||||
.get_direct_granted_assets() \
|
||||
.prefetch_related('platform') \
|
||||
.only(*self.only_fields)
|
||||
return assets
|
||||
|
||||
|
||||
class UserFavoriteAssetsApi(SelfOrPKUserMixin, PermedAssetSerializerMixin, ListAPIView):
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
""" 用户收藏的授权资产 """
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, 'swagger_fake_view', False):
|
||||
return Asset.objects.none()
|
||||
|
||||
user = self.user
|
||||
utils = UserGrantedAssetsQueryUtils(user)
|
||||
assets = utils.get_favorite_assets()
|
||||
assets = self.get_assets()
|
||||
assets = assets.prefetch_related('platform').only(*self.only_fields)
|
||||
return assets
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_assets(self):
|
||||
return Asset.objects.none()
|
||||
|
||||
class UserDirectPermedAssetsAsTreeApi(RebuildTreeMixin, AssetsTreeFormatMixin, UserDirectPermedAssetsApi):
|
||||
""" 用户直接授权的资产作为树 """
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, 'swagger_fake_view', False):
|
||||
return Asset.objects.none()
|
||||
|
||||
assets = UserGrantedAssetsQueryUtils(self.user) \
|
||||
.get_direct_granted_assets() \
|
||||
.prefetch_related('platform') \
|
||||
.only(*self.only_fields)
|
||||
return assets
|
||||
@lazyproperty
|
||||
def query_asset_util(self):
|
||||
return UserGrantedAssetsQueryUtils(self.user)
|
||||
|
||||
|
||||
class UserUngroupAssetsAsTreeApi(UserDirectPermedAssetsAsTreeApi):
|
||||
""" 用户未分组节点下的资产作为树 """
|
||||
class UserAllPermedAssetsApi(BaseUserPermedAssetsApi):
|
||||
pagination_class = AllPermedAssetPagination
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
if not settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
|
||||
queryset = queryset.none()
|
||||
return queryset
|
||||
def get_assets(self):
|
||||
return self.query_asset_util.get_all_granted_assets()
|
||||
|
||||
|
||||
class UserAllPermedAssetsApi(SelfOrPKUserMixin, PermedAssetSerializerMixin, ListAPIView):
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
pagination_class = AllGrantedAssetPagination
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, 'swagger_fake_view', False):
|
||||
return Asset.objects.none()
|
||||
queryset = UserGrantedAssetsQueryUtils(self.user).get_all_granted_assets()
|
||||
only_fields = [i for i in self.only_fields if i not in ['protocols']]
|
||||
queryset = queryset.prefetch_related('platform', 'protocols').only(*only_fields)
|
||||
return queryset
|
||||
class UserDirectPermedAssetsApi(BaseUserPermedAssetsApi):
|
||||
def get_assets(self):
|
||||
return self.query_asset_util.get_direct_granted_assets()
|
||||
|
||||
|
||||
class UserPermedNodeAssetsApi(SelfOrPKUserMixin, PermedAssetSerializerMixin, ListAPIView):
|
||||
only_fields = serializers.AssetGrantedSerializer.Meta.only_fields
|
||||
pagination_class = NodeGrantedAssetPagination
|
||||
kwargs: dict
|
||||
class UserFavoriteAssetsApi(BaseUserPermedAssetsApi):
|
||||
def get_assets(self):
|
||||
return self.query_asset_util.get_favorite_assets()
|
||||
|
||||
|
||||
class UserPermedNodeAssetsApi(BaseUserPermedAssetsApi):
|
||||
pagination_class = NodePermedAssetPagination
|
||||
pagination_node: Node
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, 'swagger_fake_view', False):
|
||||
return Asset.objects.none()
|
||||
def get_assets(self):
|
||||
node_id = self.kwargs.get("node_id")
|
||||
|
||||
node, assets = UserGrantedAssetsQueryUtils(self.user).get_node_all_assets(node_id)
|
||||
assets = assets.prefetch_related('platform').only(*self.only_fields)
|
||||
node, assets = self.query_asset_util.get_node_all_assets(node_id)
|
||||
self.pagination_node = node
|
||||
return assets
|
||||
|
|
|
@ -3,27 +3,13 @@
|
|||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
||||
from assets.api.asset.asset import AssetFilterSet
|
||||
from assets.api.mixin import SerializeToTreeNodeMixin
|
||||
from common.exceptions import JMSObjectDoesNotExist
|
||||
from common.http import is_true
|
||||
from common.utils import is_uuid
|
||||
from perms import serializers
|
||||
from perms.utils.user_permission import UserGrantedTreeRefreshController
|
||||
from rbac.permissions import RBACPermission
|
||||
from users.models import User
|
||||
from rbac.permissions import RBACPermission
|
||||
from common.utils import is_uuid
|
||||
from common.exceptions import JMSObjectDoesNotExist
|
||||
|
||||
|
||||
class RebuildTreeMixin:
|
||||
user: User
|
||||
|
||||
def get(self, request: Request, *args, **kwargs):
|
||||
force = is_true(request.query_params.get('rebuild_tree'))
|
||||
controller = UserGrantedTreeRefreshController(self.user)
|
||||
controller.refresh_if_need(force)
|
||||
return super().get(request, *args, **kwargs)
|
||||
__all__ = ['SelfOrPKUserMixin']
|
||||
|
||||
|
||||
class SelfOrPKUserMixin:
|
||||
|
@ -72,32 +58,3 @@ class SelfOrPKUserMixin:
|
|||
def request_user_is_self(self):
|
||||
return self.kwargs.get('user') in ['my', 'self']
|
||||
|
||||
|
||||
class PermedAssetSerializerMixin:
|
||||
serializer_class = serializers.AssetGrantedSerializer
|
||||
filterset_class = AssetFilterSet
|
||||
search_fields = ['name', 'address', 'comment']
|
||||
ordering_fields = ("name", "address")
|
||||
ordering = ('name',)
|
||||
|
||||
|
||||
class AssetsTreeFormatMixin(SerializeToTreeNodeMixin):
|
||||
"""
|
||||
将 资产 序列化成树的结构返回
|
||||
"""
|
||||
filter_queryset: callable
|
||||
get_queryset: callable
|
||||
|
||||
filterset_fields = ['name', 'address', 'id', 'comment']
|
||||
search_fields = ['name', 'address', 'comment']
|
||||
|
||||
def list(self, request: Request, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
|
||||
if request.query_params.get('search'):
|
||||
# 如果用户搜索的条件不精准,会导致返回大量的无意义数据。
|
||||
# 这里限制一下返回数据的最大条数
|
||||
queryset = queryset[:999]
|
||||
queryset = sorted(queryset, key=lambda asset: asset.name)
|
||||
data = self.serialize_assets(queryset, None)
|
||||
return Response(data=data)
|
||||
|
|
|
@ -1,133 +1,49 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import abc
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.generics import ListAPIView
|
||||
|
||||
from common.utils import get_logger
|
||||
from assets.api.mixin import SerializeToTreeNodeMixin
|
||||
from assets.models import Node
|
||||
from perms import serializers
|
||||
from perms.hands import User
|
||||
from perms.utils.user_permission import UserGrantedNodesQueryUtils
|
||||
from common.utils import get_logger, lazyproperty
|
||||
|
||||
from .mixin import SelfOrPKUserMixin, RebuildTreeMixin
|
||||
from .mixin import SelfOrPKUserMixin
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
__all__ = [
|
||||
'UserPermedNodesApi',
|
||||
'UserPermedNodesAsTreeApi',
|
||||
'UserAllPermedNodesApi',
|
||||
'UserPermedNodeChildrenApi',
|
||||
'UserPermedNodeChildrenAsTreeApi',
|
||||
'BaseGrantedNodeAsTreeApi',
|
||||
'UserGrantedNodesMixin',
|
||||
]
|
||||
|
||||
|
||||
class _GrantedNodeStructApi(ListAPIView, metaclass=abc.ABCMeta):
|
||||
@property
|
||||
def user(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_nodes(self):
|
||||
# 不使用 `get_queryset` 单独定义 `get_nodes` 的原因是
|
||||
# `get_nodes` 返回的不一定是 `queryset`
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class NodeChildrenMixin:
|
||||
def get_children(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_nodes(self):
|
||||
nodes = self.get_children()
|
||||
return nodes
|
||||
|
||||
|
||||
class BaseGrantedNodeApi(_GrantedNodeStructApi, metaclass=abc.ABCMeta):
|
||||
class BaseUserPermedNodesApi(SelfOrPKUserMixin, ListAPIView):
|
||||
serializer_class = serializers.NodeGrantedSerializer
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
nodes = self.get_nodes()
|
||||
serializer = self.get_serializer(nodes, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class BaseNodeChildrenApi(NodeChildrenMixin, BaseGrantedNodeApi, metaclass=abc.ABCMeta):
|
||||
pass
|
||||
|
||||
|
||||
class BaseGrantedNodeAsTreeApi(SerializeToTreeNodeMixin, _GrantedNodeStructApi, metaclass=abc.ABCMeta):
|
||||
def list(self, request: Request, *args, **kwargs):
|
||||
nodes = self.get_nodes()
|
||||
nodes = self.serialize_nodes(nodes, with_asset_amount=True)
|
||||
return Response(data=nodes)
|
||||
|
||||
|
||||
class BaseNodeChildrenAsTreeApi(NodeChildrenMixin, BaseGrantedNodeAsTreeApi, metaclass=abc.ABCMeta):
|
||||
pass
|
||||
|
||||
|
||||
class UserGrantedNodeChildrenMixin:
|
||||
user: User
|
||||
request: Request
|
||||
|
||||
def get_children(self):
|
||||
user = self.user
|
||||
key = self.request.query_params.get('key')
|
||||
nodes = UserGrantedNodesQueryUtils(user).get_node_children(key)
|
||||
return nodes
|
||||
|
||||
|
||||
class UserGrantedNodesMixin:
|
||||
"""
|
||||
查询用户授权的所有节点 直接授权节点 + 授权资产关联的节点
|
||||
"""
|
||||
user: User
|
||||
def get_queryset(self):
|
||||
if getattr(self, 'swagger_fake_view', False):
|
||||
return Node.objects.none()
|
||||
return self.get_nodes()
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_nodes(self):
|
||||
utils = UserGrantedNodesQueryUtils(self.user)
|
||||
nodes = utils.get_whole_tree_nodes()
|
||||
return nodes
|
||||
return []
|
||||
|
||||
@lazyproperty
|
||||
def query_node_util(self):
|
||||
return UserGrantedNodesQueryUtils(self.user)
|
||||
|
||||
|
||||
# API
|
||||
|
||||
|
||||
class UserPermedNodeChildrenApi(
|
||||
SelfOrPKUserMixin,
|
||||
UserGrantedNodeChildrenMixin,
|
||||
BaseNodeChildrenApi
|
||||
):
|
||||
""" 用户授权的节点下的子节点"""
|
||||
pass
|
||||
|
||||
|
||||
class UserPermedNodeChildrenAsTreeApi(
|
||||
SelfOrPKUserMixin,
|
||||
RebuildTreeMixin,
|
||||
UserGrantedNodeChildrenMixin,
|
||||
BaseNodeChildrenAsTreeApi
|
||||
):
|
||||
""" 用户授权的节点下的子节点树"""
|
||||
pass
|
||||
|
||||
|
||||
class UserPermedNodesApi(
|
||||
SelfOrPKUserMixin,
|
||||
UserGrantedNodesMixin,
|
||||
BaseGrantedNodeApi
|
||||
):
|
||||
class UserAllPermedNodesApi(BaseUserPermedNodesApi):
|
||||
""" 用户授权的节点 """
|
||||
pass
|
||||
def get_nodes(self):
|
||||
return self.query_node_util.get_whole_tree_nodes()
|
||||
|
||||
|
||||
class UserPermedNodesAsTreeApi(
|
||||
SelfOrPKUserMixin,
|
||||
RebuildTreeMixin,
|
||||
UserGrantedNodesMixin,
|
||||
BaseGrantedNodeAsTreeApi
|
||||
):
|
||||
""" 用户授权的节点树 """
|
||||
pass
|
||||
class UserPermedNodeChildrenApi(BaseUserPermedNodesApi):
|
||||
""" 用户授权的节点下的子节点 """
|
||||
def get_nodes(self):
|
||||
key = self.request.query_params.get('key')
|
||||
nodes = self.query_node_util.get_node_children(key)
|
||||
return nodes
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.conf import settings
|
||||
from django.db.models import F, Value, CharField
|
||||
from rest_framework.generics import ListAPIView
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from common.utils.common import timeit
|
||||
from common.permissions import IsValidUser
|
||||
|
||||
from assets.models import Asset
|
||||
from assets.api import SerializeToTreeNodeMixin
|
||||
from perms.hands import Node
|
||||
from perms.models import AssetPermission, PermNode
|
||||
from perms.utils.user_permission import (
|
||||
UserGrantedTreeBuildUtils, get_user_all_asset_perm_ids,
|
||||
UserGrantedNodesQueryUtils, UserGrantedAssetsQueryUtils,
|
||||
)
|
||||
|
||||
from .mixin import SelfOrPKUserMixin, RebuildTreeMixin
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class MyGrantedNodesWithAssetsAsTreeApi(SerializeToTreeNodeMixin, ListAPIView):
|
||||
permission_classes = (IsValidUser,)
|
||||
|
||||
@timeit
|
||||
def add_ungrouped_resource(self, data: list, nodes_query_utils, assets_query_utils):
|
||||
if not settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
|
||||
return
|
||||
ungrouped_node = nodes_query_utils.get_ungrouped_node()
|
||||
|
||||
direct_granted_assets = assets_query_utils.get_direct_granted_assets().annotate(
|
||||
parent_key=Value(ungrouped_node.key, output_field=CharField())
|
||||
).prefetch_related('platform')
|
||||
|
||||
data.extend(self.serialize_nodes([ungrouped_node], with_asset_amount=True))
|
||||
data.extend(self.serialize_assets(direct_granted_assets))
|
||||
|
||||
@timeit
|
||||
def add_favorite_resource(self, data: list, nodes_query_utils, assets_query_utils):
|
||||
favorite_node = nodes_query_utils.get_favorite_node()
|
||||
|
||||
favorite_assets = assets_query_utils.get_favorite_assets()
|
||||
favorite_assets = favorite_assets.annotate(
|
||||
parent_key=Value(favorite_node.key, output_field=CharField())
|
||||
).prefetch_related('platform')
|
||||
|
||||
data.extend(self.serialize_nodes([favorite_node], with_asset_amount=True))
|
||||
data.extend(self.serialize_assets(favorite_assets))
|
||||
|
||||
@timeit
|
||||
def add_node_filtered_by_system_user(self, data: list, user, asset_perm_ids):
|
||||
utils = UserGrantedTreeBuildUtils(user, asset_perm_ids)
|
||||
nodes = utils.get_whole_tree_nodes()
|
||||
data.extend(self.serialize_nodes(nodes, with_asset_amount=True))
|
||||
|
||||
def add_assets(self, data: list, assets_query_utils: UserGrantedAssetsQueryUtils):
|
||||
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
|
||||
all_assets = assets_query_utils.get_direct_granted_nodes_assets()
|
||||
else:
|
||||
all_assets = assets_query_utils.get_all_granted_assets()
|
||||
all_assets = all_assets.annotate(parent_key=F('nodes__key')).prefetch_related('platform')
|
||||
data.extend(self.serialize_assets(all_assets))
|
||||
|
||||
def list(self, request: Request, *args, **kwargs):
|
||||
"""
|
||||
此算法依赖 UserGrantedMappingNode
|
||||
获取所有授权的节点和资产
|
||||
|
||||
Node = UserGrantedMappingNode + 授权节点的子节点
|
||||
Asset = 授权节点的资产 + 直接授权的资产
|
||||
"""
|
||||
|
||||
user = request.user
|
||||
data = []
|
||||
asset_perm_ids = get_user_all_asset_perm_ids(user)
|
||||
|
||||
system_user_id = request.query_params.get('system_user')
|
||||
if system_user_id:
|
||||
asset_perm_ids = list(AssetPermission.objects.valid().filter(
|
||||
id__in=asset_perm_ids, system_users__id=system_user_id, actions__gt=0
|
||||
).values_list('id', flat=True).distinct())
|
||||
|
||||
nodes_query_utils = UserGrantedNodesQueryUtils(user, asset_perm_ids)
|
||||
assets_query_utils = UserGrantedAssetsQueryUtils(user, asset_perm_ids)
|
||||
|
||||
self.add_ungrouped_resource(data, nodes_query_utils, assets_query_utils)
|
||||
self.add_favorite_resource(data, nodes_query_utils, assets_query_utils)
|
||||
|
||||
if system_user_id:
|
||||
# 有系统用户筛选的需要重新计算树结构
|
||||
self.add_node_filtered_by_system_user(data, user, asset_perm_ids)
|
||||
else:
|
||||
all_nodes = nodes_query_utils.get_whole_tree_nodes(with_special=False)
|
||||
data.extend(self.serialize_nodes(all_nodes, with_asset_amount=True))
|
||||
|
||||
self.add_assets(data, assets_query_utils)
|
||||
return Response(data=data)
|
||||
|
||||
|
||||
class GrantedNodeChildrenWithAssetsAsTreeApiMixin(SerializeToTreeNodeMixin,
|
||||
ListAPIView):
|
||||
"""
|
||||
带资产的授权树
|
||||
"""
|
||||
user: None
|
||||
|
||||
def ensure_key(self):
|
||||
key = self.request.query_params.get('key', None)
|
||||
id = self.request.query_params.get('id', None)
|
||||
|
||||
if key is not None:
|
||||
return key
|
||||
|
||||
node = get_object_or_none(Node, id=id)
|
||||
if node:
|
||||
return node.key
|
||||
|
||||
def list(self, request: Request, *args, **kwargs):
|
||||
user = self.user
|
||||
key = self.ensure_key()
|
||||
|
||||
nodes_query_utils = UserGrantedNodesQueryUtils(user)
|
||||
assets_query_utils = UserGrantedAssetsQueryUtils(user)
|
||||
|
||||
nodes = PermNode.objects.none()
|
||||
assets = Asset.objects.none()
|
||||
all_tree_nodes = []
|
||||
|
||||
if not key:
|
||||
nodes = nodes_query_utils.get_top_level_nodes()
|
||||
elif key == PermNode.UNGROUPED_NODE_KEY:
|
||||
assets = assets_query_utils.get_ungroup_assets()
|
||||
elif key == PermNode.FAVORITE_NODE_KEY:
|
||||
assets = assets_query_utils.get_favorite_assets()
|
||||
else:
|
||||
nodes = nodes_query_utils.get_node_children(key)
|
||||
assets = assets_query_utils.get_node_assets(key)
|
||||
assets = assets.prefetch_related('platform')
|
||||
|
||||
tree_nodes = self.serialize_nodes(nodes, with_asset_amount=True)
|
||||
tree_assets = self.serialize_assets(assets, key)
|
||||
all_tree_nodes.extend(tree_nodes)
|
||||
all_tree_nodes.extend(tree_assets)
|
||||
return Response(data=all_tree_nodes)
|
||||
|
||||
|
||||
class UserGrantedNodeChildrenWithAssetsAsTreeApi(
|
||||
SelfOrPKUserMixin,
|
||||
RebuildTreeMixin,
|
||||
GrantedNodeChildrenWithAssetsAsTreeApiMixin
|
||||
):
|
||||
""" 用户授权的节点的子节点与资产树 """
|
||||
pass
|
|
@ -0,0 +1,3 @@
|
|||
from .asset import *
|
||||
from .node import *
|
||||
from .node_with_asset import *
|
|
@ -0,0 +1,48 @@
|
|||
from django.conf import settings
|
||||
from rest_framework.response import Response
|
||||
|
||||
from assets.models import Asset
|
||||
from assets.api import SerializeToTreeNodeMixin
|
||||
from common.utils import get_logger
|
||||
|
||||
from ..assets import UserDirectPermedAssetsApi
|
||||
from .mixin import RebuildTreeMixin
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'UserDirectPermedAssetsAsTreeApi',
|
||||
'UserUngroupAssetsAsTreeApi',
|
||||
]
|
||||
|
||||
|
||||
class AssetTreeMixin(RebuildTreeMixin, SerializeToTreeNodeMixin):
|
||||
""" 将资产序列化成树节点的结构返回 """
|
||||
filter_queryset: callable
|
||||
get_queryset: callable
|
||||
|
||||
ordering = ('name',)
|
||||
filterset_fields = ('id', 'name', 'address', 'comment')
|
||||
search_fields = ('name', 'address', 'comment')
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
assets = self.filter_queryset(self.get_queryset())
|
||||
if request.query_params.get('search'):
|
||||
""" 限制返回数量, 搜索的条件不精准时,会返回大量的无意义数据 """
|
||||
assets = assets[:999]
|
||||
data = self.serialize_assets(assets, None)
|
||||
return Response(data=data)
|
||||
|
||||
|
||||
class UserDirectPermedAssetsAsTreeApi(AssetTreeMixin, UserDirectPermedAssetsApi):
|
||||
""" 用户 '直接授权的资产' 作为树 """
|
||||
pass
|
||||
|
||||
|
||||
class UserUngroupAssetsAsTreeApi(UserDirectPermedAssetsAsTreeApi):
|
||||
""" 用户 '未分组节点的资产(直接授权的资产)' 作为树 """
|
||||
def get_assets(self):
|
||||
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
|
||||
return super().get_assets()
|
||||
return Asset.objects.none()
|
|
@ -0,0 +1,17 @@
|
|||
from rest_framework.request import Request
|
||||
|
||||
from users.models import User
|
||||
from perms.utils.user_permission import UserGrantedTreeRefreshController
|
||||
from common.http import is_true
|
||||
|
||||
|
||||
__all__ = ['RebuildTreeMixin']
|
||||
|
||||
|
||||
class RebuildTreeMixin:
|
||||
user: User
|
||||
|
||||
def get(self, request: Request, *args, **kwargs):
|
||||
force = is_true(request.query_params.get('rebuild_tree'))
|
||||
UserGrantedTreeRefreshController(self.user).refresh_if_need(force)
|
||||
return super().get(request, *args, **kwargs)
|
|
@ -0,0 +1,39 @@
|
|||
from rest_framework.response import Response
|
||||
|
||||
from assets.api import SerializeToTreeNodeMixin
|
||||
from common.utils import get_logger
|
||||
|
||||
from .mixin import RebuildTreeMixin
|
||||
from ..nodes import (
|
||||
UserAllPermedNodesApi,
|
||||
UserPermedNodeChildrenApi,
|
||||
)
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
__all__ = [
|
||||
'UserAllPermedNodesAsTreeApi',
|
||||
'UserPermedNodeChildrenAsTreeApi',
|
||||
]
|
||||
|
||||
|
||||
class NodeTreeMixin(RebuildTreeMixin, SerializeToTreeNodeMixin):
|
||||
filter_queryset: callable
|
||||
get_queryset: callable
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
nodes = self.filter_queryset(self.get_queryset())
|
||||
data = self.serialize_nodes(nodes, with_asset_amount=True)
|
||||
return Response(data)
|
||||
|
||||
|
||||
class UserAllPermedNodesAsTreeApi(NodeTreeMixin, UserAllPermedNodesApi):
|
||||
""" 用户 '授权的节点' 作为树 """
|
||||
pass
|
||||
|
||||
|
||||
class UserPermedNodeChildrenAsTreeApi(NodeTreeMixin, UserPermedNodeChildrenApi):
|
||||
""" 用户授权的节点下的子节点树 """
|
||||
pass
|
||||
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
import abc
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import F, Value, CharField
|
||||
from rest_framework.generics import ListAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from assets.models import Asset
|
||||
from assets.api import SerializeToTreeNodeMixin
|
||||
from perms.hands import Node
|
||||
from perms.models import PermNode
|
||||
from perms.utils.user_permission import (
|
||||
UserGrantedNodesQueryUtils, UserGrantedAssetsQueryUtils,
|
||||
)
|
||||
from perms.utils.permission import AssetPermissionUtil
|
||||
from common.utils import get_object_or_none, lazyproperty
|
||||
from common.utils.common import timeit
|
||||
|
||||
|
||||
from ..mixin import SelfOrPKUserMixin
|
||||
from .mixin import RebuildTreeMixin
|
||||
|
||||
__all__ = [
|
||||
'UserPermedNodesWithAssetsAsTreeApi',
|
||||
'UserPermedNodeChildrenWithAssetsAsTreeApi'
|
||||
]
|
||||
|
||||
|
||||
class BaseUserNodeWithAssetAsTreeApi(SelfOrPKUserMixin, RebuildTreeMixin, SerializeToTreeNodeMixin,
|
||||
ListAPIView):
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
nodes, assets = self.get_nodes_assets()
|
||||
tree_nodes = self.serialize_nodes(nodes, with_asset_amount=True)
|
||||
tree_assets = self.serialize_assets(assets, node_key=self.node_key_for_serializer_assets)
|
||||
data = list(tree_nodes) + list(tree_assets)
|
||||
return Response(data=data)
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_nodes_assets(self):
|
||||
return [], []
|
||||
|
||||
@lazyproperty
|
||||
def node_key_for_serializer_assets(self):
|
||||
return None
|
||||
|
||||
|
||||
class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
||||
query_node_util: UserGrantedNodesQueryUtils
|
||||
query_asset_util: UserGrantedAssetsQueryUtils
|
||||
|
||||
def get_nodes_assets(self):
|
||||
perm_ids = AssetPermissionUtil().get_permissions_for_user(self.request.user, flat=True)
|
||||
self.query_node_util = UserGrantedNodesQueryUtils(self.request.user, perm_ids)
|
||||
self.query_asset_util = UserGrantedAssetsQueryUtils(self.request.user, perm_ids)
|
||||
ung_nodes, ung_assets = self._get_nodes_assets_for_ungrouped()
|
||||
fav_nodes, fav_assets = self._get_nodes_assets_for_favorite()
|
||||
all_nodes, all_assets = self._get_nodes_assets_for_all()
|
||||
nodes = list(ung_nodes) + list(fav_nodes) + list(all_nodes)
|
||||
assets = list(ung_assets) + list(fav_assets) + list(all_assets)
|
||||
return nodes, assets
|
||||
|
||||
@timeit
|
||||
def _get_nodes_assets_for_ungrouped(self):
|
||||
if not settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
|
||||
return [], []
|
||||
node = self.query_node_util.get_ungrouped_node()
|
||||
assets = self.query_asset_util.get_ungroup_assets()
|
||||
assets = assets.annotate(parent_key=Value(node.key, output_field=CharField())) \
|
||||
.prefetch_related('platform')
|
||||
return [node], assets
|
||||
|
||||
@timeit
|
||||
def _get_nodes_assets_for_favorite(self):
|
||||
node = self.query_node_util.get_favorite_node()
|
||||
assets = self.query_asset_util.get_favorite_assets()
|
||||
assets = assets.annotate(parent_key=Value(node.key, output_field=CharField())) \
|
||||
.prefetch_related('platform')
|
||||
return [node], assets
|
||||
|
||||
def _get_nodes_assets_for_all(self):
|
||||
nodes = self.query_node_util.get_whole_tree_nodes(with_special=False)
|
||||
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
|
||||
assets = self.query_asset_util.get_direct_granted_nodes_assets()
|
||||
else:
|
||||
assets = self.query_asset_util.get_all_granted_assets()
|
||||
assets = assets.annotate(parent_key=F('nodes__key')).prefetch_related('platform')
|
||||
return nodes, assets
|
||||
|
||||
|
||||
class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
||||
""" 用户授权的节点的子节点与资产树 """
|
||||
|
||||
def get_nodes_assets(self):
|
||||
nodes = PermNode.objects.none()
|
||||
assets = Asset.objects.none()
|
||||
query_node_util = UserGrantedNodesQueryUtils(self.user)
|
||||
query_asset_util = UserGrantedAssetsQueryUtils(self.user)
|
||||
node_key = self.query_node_key
|
||||
if not node_key:
|
||||
nodes = query_node_util.get_top_level_nodes()
|
||||
elif node_key == PermNode.UNGROUPED_NODE_KEY:
|
||||
assets = query_asset_util.get_ungroup_assets()
|
||||
elif node_key == PermNode.FAVORITE_NODE_KEY:
|
||||
assets = query_asset_util.get_favorite_assets()
|
||||
else:
|
||||
nodes = query_node_util.get_node_children(node_key)
|
||||
assets = query_asset_util.get_node_assets(node_key)
|
||||
assets = assets.prefetch_related('platform')
|
||||
return nodes, assets
|
||||
|
||||
@lazyproperty
|
||||
def query_node_key(self):
|
||||
node_key = self.request.query_params.get('key', None)
|
||||
if node_key is not None:
|
||||
return node_key
|
||||
node_id = self.request.query_params.get('id', None)
|
||||
node = get_object_or_none(Node, id=node_id)
|
||||
node_key = getattr(node, 'key', None)
|
||||
return node_key
|
||||
|
||||
@lazyproperty
|
||||
def node_key_for_serializer_assets(self):
|
||||
return self.query_node_key
|
||||
|
|
@ -16,7 +16,7 @@ class GrantedAssetPaginationBase(AssetPaginationBase):
|
|||
self._user = view.user
|
||||
|
||||
|
||||
class NodeGrantedAssetPagination(GrantedAssetPaginationBase):
|
||||
class NodePermedAssetPagination(GrantedAssetPaginationBase):
|
||||
def get_count_from_nodes(self, queryset):
|
||||
node = getattr(self._view, 'pagination_node', None)
|
||||
if node:
|
||||
|
@ -29,7 +29,7 @@ class NodeGrantedAssetPagination(GrantedAssetPaginationBase):
|
|||
return None
|
||||
|
||||
|
||||
class AllGrantedAssetPagination(GrantedAssetPaginationBase):
|
||||
class AllPermedAssetPagination(GrantedAssetPaginationBase):
|
||||
def get_count_from_nodes(self, queryset):
|
||||
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
|
||||
return None
|
||||
|
|
|
@ -11,12 +11,12 @@ from common.drf.fields import ObjectRelatedField, LabeledChoiceField
|
|||
from perms.serializers.permission import ActionChoicesField
|
||||
|
||||
__all__ = [
|
||||
'NodeGrantedSerializer', 'AssetGrantedSerializer',
|
||||
'NodeGrantedSerializer', 'AssetPermedSerializer',
|
||||
'AccountsPermedSerializer'
|
||||
]
|
||||
|
||||
|
||||
class AssetGrantedSerializer(serializers.ModelSerializer):
|
||||
class AssetPermedSerializer(serializers.ModelSerializer):
|
||||
""" 被授权资产的数据结构 """
|
||||
platform = ObjectRelatedField(required=False, queryset=Platform.objects, label=_('Platform'))
|
||||
protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'))
|
||||
|
|
|
@ -4,39 +4,38 @@ from .. import api
|
|||
|
||||
user_permission_urlpatterns = [
|
||||
# <str:user> such as: my | self | user.id
|
||||
|
||||
# assets
|
||||
path('<str:user>/assets/', api.UserAllPermedAssetsApi.as_view(),
|
||||
name='user-assets'),
|
||||
path('<str:user>/assets/tree/', api.UserDirectPermedAssetsAsTreeApi.as_view(),
|
||||
name='user-assets-as-tree'),
|
||||
path('<str:user>/ungroup/assets/tree/', api.UserUngroupAssetsAsTreeApi.as_view(),
|
||||
name='user-ungroup-assets-as-tree'),
|
||||
|
||||
# nodes
|
||||
path('<str:user>/nodes/', api.UserPermedNodesApi.as_view(),
|
||||
name='user-nodes'),
|
||||
path('<str:user>/nodes/tree/', api.UserPermedNodesAsTreeApi.as_view(),
|
||||
name='user-nodes-as-tree'),
|
||||
path('<str:user>/nodes/children/', api.UserPermedNodeChildrenApi.as_view(),
|
||||
name='user-nodes-children'),
|
||||
path('<str:user>/nodes/children/tree/', api.UserPermedNodeChildrenAsTreeApi.as_view(),
|
||||
name='user-nodes-children-as-tree'),
|
||||
|
||||
# node-assets
|
||||
name='user-all-assets'),
|
||||
path('<str:user>/nodes/ungrouped/assets/', api.UserDirectPermedAssetsApi.as_view(),
|
||||
name='user-direct-assets'),
|
||||
path('<str:user>/nodes/favorite/assets/', api.UserFavoriteAssetsApi.as_view(),
|
||||
name='user-favorite-assets'),
|
||||
path('<str:user>/nodes/<uuid:node_id>/assets/', api.UserPermedNodeAssetsApi.as_view(),
|
||||
name='user-node-assets'),
|
||||
path('<str:user>/nodes/ungrouped/assets/', api.UserDirectPermedAssetsApi.as_view(),
|
||||
name='user-ungrouped-assets'),
|
||||
path('<str:user>/nodes/favorite/assets/', api.UserFavoriteAssetsApi.as_view(),
|
||||
name='user-ungrouped-assets'),
|
||||
|
||||
# nodes
|
||||
path('<str:user>/nodes/', api.UserAllPermedNodesApi.as_view(),
|
||||
name='user-all-nodes'),
|
||||
path('<str:user>/nodes/children/', api.UserPermedNodeChildrenApi.as_view(),
|
||||
name='user-node-children'),
|
||||
|
||||
# tree-asset
|
||||
path('<str:user>/assets/tree/', api.UserDirectPermedAssetsAsTreeApi.as_view(),
|
||||
name='user-direct-assets-as-tree'),
|
||||
path('<str:user>/ungroup/assets/tree/', api.UserUngroupAssetsAsTreeApi.as_view(),
|
||||
name='user-ungroup-assets-as-tree'),
|
||||
# tree-node
|
||||
path('<str:user>/nodes/tree/', api.UserAllPermedNodesAsTreeApi.as_view(),
|
||||
name='user-all-nodes-as-tree'),
|
||||
path('<str:user>/nodes/children/tree/', api.UserPermedNodeChildrenAsTreeApi.as_view(),
|
||||
name='user-node-children-as-tree'),
|
||||
# tree-node-with-asset
|
||||
path('<str:user>/nodes/children-with-assets/tree/',
|
||||
api.UserGrantedNodeChildrenWithAssetsAsTreeApi.as_view(),
|
||||
name='user-nodes-children-with-assets-as-tree'),
|
||||
|
||||
path('nodes-with-assets/tree/', api.MyGrantedNodesWithAssetsAsTreeApi.as_view(),
|
||||
name='my-nodes-with-assets-as-tree'),
|
||||
api.UserPermedNodeChildrenWithAssetsAsTreeApi.as_view(),
|
||||
name='user-node-children-with-assets-as-tree'),
|
||||
path('<str:user>/nodes-with-assets/tree/', api.UserPermedNodesWithAssetsAsTreeApi.as_view(),
|
||||
name='user-nodes-with-assets-as-tree'),
|
||||
|
||||
# accounts
|
||||
path('<str:user>/assets/<uuid:asset_id>/accounts/', api.UserPermedAssetAccountsApi.as_view(),
|
||||
|
|
|
@ -4,6 +4,8 @@ from perms.models import AssetPermission
|
|||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
__all__ = ['AssetPermissionUtil']
|
||||
|
||||
|
||||
class AssetPermissionUtil(object):
|
||||
""" 资产授权相关的方法工具 """
|
||||
|
|
Loading…
Reference in New Issue