You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
jumpserver/apps/perms/utils/user_perm.py

288 lines
11 KiB

import json
import re
from django.conf import settings
from django.core.cache import cache
from django.db.models import Q
from rest_framework.utils.encoders import JSONEncoder
from assets.const import AllTypes
from assets.models import FavoriteAsset, Asset, Node
from common.utils.common import timeit, get_logger
from orgs.utils import current_org
from perms.models import PermNode, UserAssetGrantedTreeNodeRelation, AssetPermission
from .permission import AssetPermissionUtil
__all__ = ['AssetPermissionPermAssetUtil', 'UserPermAssetUtil', 'UserPermNodeUtil']
logger = get_logger(__name__)
class AssetPermissionPermAssetUtil:
def __init__(self, perm_ids):
self.perm_ids = set(perm_ids)
def get_all_assets(self):
node_assets = self.get_perm_nodes_assets()
direct_assets = self.get_direct_assets()
# 比原来的查到所有 asset id 再搜索块很多,因为当资产量大的时候,搜索会很慢
return (node_assets | direct_assets).order_by().distinct()
def get_perm_nodes(self):
""" 获取所有授权节点 """
nodes_ids = AssetPermission.objects \
.filter(id__in=self.perm_ids) \
.values_list('nodes', flat=True)
nodes_ids = set(nodes_ids)
nodes = Node.objects.filter(id__in=nodes_ids).only('id', 'key')
return nodes
@timeit
def get_perm_nodes_assets(self):
""" 获取所有授权节点下的资产 """
nodes = self.get_perm_nodes()
assets = PermNode.get_nodes_all_assets(*nodes, distinct=False)
return assets
@timeit
def get_direct_assets(self):
""" 获取直接授权的资产 """
asset_ids = AssetPermission.assets.through.objects \
.filter(assetpermission_id__in=self.perm_ids) \
.values_list('asset_id', flat=True)
assets = Asset.objects.filter(id__in=asset_ids)
return assets
class UserPermAssetUtil(AssetPermissionPermAssetUtil):
def __init__(self, user):
self.user = user
perm_ids = AssetPermissionUtil().get_permissions_for_user(self.user, flat=True)
super().__init__(perm_ids)
def get_ungroup_assets(self):
return self.get_direct_assets()
@timeit
def get_favorite_assets(self):
assets = Asset.objects.all().valid()
asset_ids = FavoriteAsset.objects.filter(user=self.user).values_list('asset_id', flat=True)
assets = assets.filter(id__in=list(asset_ids))
return assets
def get_type_nodes_tree(self):
assets = self.get_all_assets()
resource_platforms = assets.order_by('id').values_list('platform_id', flat=True)
node_all = AllTypes.get_tree_nodes(resource_platforms, get_root=True)
pattern = re.compile(r'\(0\)?')
nodes = []
for node in node_all:
meta = node.get('meta', {})
if pattern.search(node['name']) or meta.get('type') == 'platform':
continue
_type = meta.get('_type')
if _type:
node['type'] = _type
node['category'] = meta.get('category')
meta.setdefault('data', {})
node['meta'] = meta
nodes.append(node)
return nodes
@classmethod
def get_type_nodes_tree_or_cached(cls, user):
key = f'perms:type-nodes-tree:{user.id}:{current_org.id}'
nodes = cache.get(key)
if nodes is None:
nodes = cls(user).get_type_nodes_tree()
nodes_json = json.dumps(nodes, cls=JSONEncoder)
cache.set(key, nodes_json, 60 * 60 * 24)
else:
nodes = json.loads(nodes)
return nodes
def refresh_type_nodes_tree_cache(self):
logger.debug("Refresh type nodes tree cache")
key = f'perms:type-nodes-tree:{self.user.id}:{current_org.id}'
cache.delete(key)
def refresh_favorite_assets(self):
favor_ids = FavoriteAsset.objects.filter(user=self.user).values_list('asset_id', flat=True)
favor_ids = set(favor_ids)
valid_ids = self.get_all_assets() \
.filter(id__in=favor_ids) \
.values_list('id', flat=True)
valid_ids = set(valid_ids)
invalid_ids = favor_ids - valid_ids
FavoriteAsset.objects.filter(user=self.user, asset_id__in=invalid_ids).delete()
def get_node_assets(self, key):
node = PermNode.objects.get(key=key)
node.compute_node_from_and_assets_amount(self.user)
if node.node_from == node.NodeFrom.granted:
assets = Asset.objects.filter(nodes__id=node.id).order_by()
elif node.node_from == node.NodeFrom.asset:
assets = self._get_indirect_perm_node_assets(node)
else:
assets = Asset.objects.none()
assets = assets.order_by('name')
return assets
def get_node_all_assets(self, node_id):
""" 获取节点下的所有资产 """
node = PermNode.objects.get(id=node_id)
node.compute_node_from_and_assets_amount(self.user)
if node.node_from == node.NodeFrom.granted:
assets = PermNode.get_nodes_all_assets(node)
elif node.node_from in (node.NodeFrom.asset, node.NodeFrom.child):
node.assets_amount = node.granted_assets_amount
assets = self._get_indirect_perm_node_all_assets(node)
else:
node.assets_amount = 0
assets = Asset.objects.none()
return node, assets
def _get_indirect_perm_node_assets(self, node):
""" 获取间接授权节点下的直接资产 """
assets = self.get_direct_assets()
assets = assets.filter(nodes__id=node.id).order_by().distinct()
return assets
@timeit
def _get_indirect_perm_node_all_assets(self, node):
""" 获取间接授权节点下的所有资产
此算法依据 `UserAssetGrantedTreeNodeRelation` 的数据查询
1. 查询该节点下的直接授权节点
2. 查询该节点下授权资产关联的节点
"""
# 查询节点下直接授权的子节点
asset_ids = set()
children_from_granted = UserAssetGrantedTreeNodeRelation.objects \
.filter(user=self.user) \
.filter(node_key__startswith=f'{node.key}:', node_from=node.NodeFrom.granted) \
.only('node_id', 'node_key')
for n in children_from_granted:
n.id = n.node_id
_assets = PermNode.get_nodes_all_assets(*children_from_granted)
_asset_ids = _assets.values_list('id', flat=True)
asset_ids.update(list(_asset_ids))
# 查询节点下资产授权的节点
children_from_assets = UserAssetGrantedTreeNodeRelation.objects \
.filter(user=self.user) \
.filter(node_key__startswith=f'{node.key}:', node_from=node.NodeFrom.asset) \
.values_list('node_id', flat=True)
children_from_assets = set(children_from_assets)
if node.node_from == node.NodeFrom.asset:
children_from_assets.add(node.id)
_asset_ids = Asset.objects \
.filter(nodes__id__in=children_from_assets) \
.filter(granted_by_permissions__id__in=self.perm_ids) \
.distinct() \
.order_by() \
.values_list('id', flat=True)
asset_ids.update(list(_asset_ids))
return Asset.objects.filter(id__in=asset_ids)
class UserPermNodeUtil:
def __init__(self, user):
self.user = user
self.perm_ids = AssetPermissionUtil().get_permissions_for_user(self.user, flat=True)
def get_favorite_node(self):
favor_ids = FavoriteAsset.objects \
.filter(user=self.user) \
.values_list('asset_id') \
.distinct()
assets_amount = Asset.objects.all().valid().filter(id__in=favor_ids).count()
return PermNode.get_favorite_node(assets_amount)
def get_ungrouped_node(self):
assets_amount = UserPermAssetUtil(self.user).get_direct_assets().count()
return PermNode.get_ungrouped_node(assets_amount)
def get_top_level_nodes(self, with_unfolded_node=False):
# 是否有节点展开, 展开的节点
unfolded_node = None
nodes = self.get_special_nodes()
real_nodes = self._get_perm_node_children_from_relation(key='')
nodes.extend(real_nodes)
if len(real_nodes) == 1:
unfolded_node = real_nodes[0]
children = self.get_node_children(unfolded_node.key)
nodes.extend(children)
if with_unfolded_node:
return nodes, unfolded_node
else:
return nodes
def get_special_nodes(self):
nodes = []
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
ung_node = self.get_ungrouped_node()
nodes.append(ung_node)
fav_node = self.get_favorite_node()
nodes.append(fav_node)
return nodes
def get_node_children(self, key):
if not key:
return self.get_top_level_nodes()
if key in [PermNode.FAVORITE_NODE_KEY, PermNode.UNGROUPED_NODE_KEY]:
return PermNode.objects.none()
node = PermNode.objects.get(key=key)
node.compute_node_from_and_assets_amount(self.user)
if node.node_from == node.NodeFrom.granted:
""" 直接授权的节点, 直接从完整资产树获取子节点 """
children = PermNode.objects.filter(parent_key=key)
elif node.node_from in (node.NodeFrom.asset, node.NodeFrom.child):
""" 间接授权的节点, 从 Relation 表中获取子节点 """
children = self._get_perm_node_children_from_relation(key)
else:
children = PermNode.objects.none()
children = sorted(children, key=lambda x: x.value)
return children
def _get_perm_node_children_from_relation(self, key):
""" 获取授权节点的子节点, 从用户授权节点关系表中获取 """
children = PermNode.objects.filter(granted_node_rels__user=self.user, parent_key=key)
children = children.annotate(**PermNode.annotate_granted_node_rel_fields).distinct()
for node in children:
node.assets_amount = node.granted_assets_amount
return children
@timeit
def get_whole_tree_nodes(self, with_special=True):
user_nodes = PermNode.objects.filter(granted_node_rels__user=self.user)
user_nodes = user_nodes.annotate(**PermNode.annotate_granted_node_rel_fields).distinct()
key_node_mapper = {}
q_nodes_descendant = Q()
for node in user_nodes:
node.assets_amount = node.granted_assets_amount
key_node_mapper[node.key] = node
if node.node_from == node.NodeFrom.granted:
""" 直接授权的节点, 增加后代节点的过滤条件 """
q_nodes_descendant |= Q(key__startswith=f'{node.key}:')
if q_nodes_descendant:
descendant_nodes = PermNode.objects.filter(q_nodes_descendant)
for node in descendant_nodes:
key_node_mapper[node.key] = node
nodes = []
if with_special:
special_nodes = self.get_special_nodes()
nodes.extend(special_nodes)
nodes.extend(list(key_node_mapper.values()))
return nodes