Files
jumpserver/apps/perms/api/user_permission.py
BaiJiangJie f235e20153 [Feature] 授权规则添加 actions 选项,控制用户对资产的操作行为 (#2610)
* [Feature] 1. perms actions - 添加 Action Model

* [Feature] 2. perms actions - 添加 Action API

* [Feature] 3. perms actions - 授权规则: 添加actions字段

* [Feature] 4. perms actions - 授权规则创建页面: 设置 actions 默认 all

* [Feature] 5. perms actions - 资产授权工具: 动态给system_user设置actions属性; 修改授权相关的API-serializer类: 添加actions字段值

* [Feature] 6. perms actions - 更新API(用户使用系统用户连接资产时权限校验): 添加actions校验

* [Feature] 7. perms actions - 迁移文件中为已经存在的perms添加默认的action

* [Feature] 8. perms actions - 创建授权规则时设置默认action(如果actions字段值为空)

* [Feature] 9. check actions - 修改校验用户资产权限API逻辑(添加actions校验)

* [Feature] 10. check actions - 修改注释

* [Feature] 11. check actions - 添加API: 获取用户指定资产和系统用户被授权的actions

* [Feature] 12. check actions - 添加翻译信息
2019-04-22 11:42:20 +08:00

450 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
#
from hashlib import md5
from django.core.cache import cache
from django.conf import settings
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView, Response
from rest_framework.generics import (
ListAPIView, get_object_or_404,
)
from rest_framework.pagination import LimitOffsetPagination
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
from common.tree import TreeNodeSerializer
from common.utils import get_logger
from orgs.utils import set_to_root_org
from ..utils import (
AssetPermissionUtil, parse_asset_to_tree_node, parse_node_to_tree_node,
check_system_user_action
)
from ..hands import (
AssetGrantedSerializer, User, Asset, Node,
SystemUser, NodeSerializer
)
from .. import serializers
from ..mixins import AssetsFilterMixin
from ..models import Action
logger = get_logger(__name__)
__all__ = [
'UserGrantedAssetsApi', 'UserGrantedNodesApi',
'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi',
'ValidateUserAssetPermissionApi', 'UserGrantedNodeChildrenApi',
'UserGrantedNodesWithAssetsAsTreeApi', 'GetUserAssetPermissionActionsApi',
]
class UserPermissionCacheMixin:
cache_policy = '0'
RESP_CACHE_KEY = '_PERMISSION_RESPONSE_CACHE_{}'
CACHE_TIME = settings.ASSETS_PERM_CACHE_TIME
_object = None
@staticmethod
def change_org_if_need(request, kwargs):
if request.user.is_authenticated and \
request.user.is_superuser or \
request.user.is_app or \
kwargs.get('pk') is None:
set_to_root_org()
def get_object(self):
return None
# 内部使用可控制缓存
def _get_object(self):
if not self._object:
self._object = self.get_object()
return self._object
def get_object_id(self):
obj = self._get_object()
if obj:
return str(obj.id)
return None
def get_request_md5(self):
full_path = self.request.get_full_path()
return md5(full_path.encode()).hexdigest()
def get_meta_cache_id(self):
obj = self._get_object()
util = AssetPermissionUtil(obj, cache_policy=self.cache_policy)
meta_cache_id = util.cache_meta.get('id')
return meta_cache_id
def get_response_cache_id(self):
obj_id = self.get_object_id()
request_md5 = self.get_request_md5()
meta_cache_id = self.get_meta_cache_id()
resp_cache_id = '{}_{}_{}'.format(obj_id, request_md5, meta_cache_id)
return resp_cache_id
def get_response_from_cache(self):
resp_cache_id = self.get_response_cache_id()
# 没有数据缓冲
meta_cache_id = self.get_meta_cache_id()
if not meta_cache_id:
return None
# 从响应缓冲里获取响应
key = self.RESP_CACHE_KEY.format(resp_cache_id)
data = cache.get(key)
if not data:
return None
logger.debug("Get user permission from cache: {}".format(self.get_object()))
response = Response(data)
return response
def expire_response_cache(self):
obj_id = self.get_object_id()
expire_cache_id = '{}_{}'.format(obj_id, '*')
key = self.RESP_CACHE_KEY.format(expire_cache_id)
cache.delete_pattern(key)
def set_response_to_cache(self, response):
resp_cache_id = self.get_response_cache_id()
key = self.RESP_CACHE_KEY.format(resp_cache_id)
cache.set(key, response.data, self.CACHE_TIME)
def get(self, request, *args, **kwargs):
self.change_org_if_need(request, kwargs)
self.cache_policy = request.GET.get('cache_policy', '0')
obj = self._get_object()
if obj is None:
return super().get(request, *args, **kwargs)
if AssetPermissionUtil.is_not_using_cache(self.cache_policy):
return super().get(request, *args, **kwargs)
elif AssetPermissionUtil.is_refresh_cache(self.cache_policy):
self.expire_response_cache()
resp = self.get_response_from_cache()
if not resp:
resp = super().get(request, *args, **kwargs)
self.set_response_to_cache(resp)
return resp
class UserGrantedAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIView):
"""
用户授权的所有资产
"""
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = AssetGrantedSerializer
pagination_class = LimitOffsetPagination
def get_object(self):
user_id = self.kwargs.get('pk', '')
if user_id:
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
return user
def get_queryset(self):
queryset = []
user = self.get_object()
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
assets = util.get_assets()
for k, v in assets.items():
system_users_granted = [s for s in v if s.protocol == k.protocol]
k.system_users_granted = system_users_granted
queryset.append(k)
return queryset
def get_permissions(self):
if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
class UserGrantedNodesApi(UserPermissionCacheMixin, ListAPIView):
"""
查询用户授权的所有节点的API, 如果是超级用户或者是 app切换到root org
"""
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = NodeSerializer
def get_object(self):
user_id = self.kwargs.get('pk', '')
if user_id:
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
return user
def get_queryset(self):
user = self.get_object()
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
nodes = util.get_nodes_with_assets()
return nodes.keys()
def get_permissions(self):
if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
class UserGrantedNodesWithAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIView):
"""
用户授权的节点并带着节点下资产的api
"""
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = serializers.NodeGrantedSerializer
def get_object(self):
user_id = self.kwargs.get('pk', '')
if not user_id:
user = self.request.user
else:
user = get_object_or_404(User, id=user_id)
return user
def get_queryset(self):
queryset = []
user = self.get_object()
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
nodes = util.get_nodes_with_assets()
for node, _assets in nodes.items():
assets = _assets.keys()
for k, v in _assets.items():
system_users_granted = [s for s in v if
s.protocol == k.protocol]
k.system_users_granted = system_users_granted
node.assets_granted = assets
queryset.append(node)
return queryset
def sort_assets(self, queryset):
for node in queryset:
node.assets_granted = super().sort_assets(node.assets_granted)
return queryset
def get_permissions(self):
if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
class UserGrantedNodesWithAssetsAsTreeApi(UserPermissionCacheMixin, ListAPIView):
serializer_class = TreeNodeSerializer
permission_classes = (IsOrgAdminOrAppUser,)
show_assets = True
system_user_id = None
def get_permissions(self):
if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
def get_object(self):
user_id = self.kwargs.get('pk', '')
if not user_id:
user = self.request.user
else:
user = get_object_or_404(User, id=user_id)
return user
def list(self, request, *args, **kwargs):
resp = super().list(request, *args, **kwargs)
return resp
def get_queryset(self):
queryset = []
self.show_assets = self.request.query_params.get('show_assets', '1') == '1'
self.system_user_id = self.request.query_params.get('system_user')
user = self.get_object()
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
if self.system_user_id:
util.filter_permissions(
system_users=self.system_user_id
)
nodes = util.get_nodes_with_assets()
for node, assets in nodes.items():
data = parse_node_to_tree_node(node)
queryset.append(data)
if not self.show_assets:
continue
for asset, system_users in assets.items():
data = parse_asset_to_tree_node(node, asset, system_users)
queryset.append(data)
queryset = sorted(queryset)
return queryset
class UserGrantedNodeAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIView):
"""
查询用户授权的节点下的资产的api, 与上面api不同的是只返回某个节点下的资产
"""
permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = AssetGrantedSerializer
pagination_class = LimitOffsetPagination
def get_object(self):
user_id = self.kwargs.get('pk', '')
if user_id:
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
return user
def get_queryset(self):
user = self.get_object()
node_id = self.kwargs.get('node_id')
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
node = get_object_or_404(Node, id=node_id)
nodes = util.get_nodes_with_assets()
assets = nodes.get(node, [])
for asset, system_users in assets.items():
asset.system_users_granted = system_users
assets = list(assets.keys())
return assets
def get_permissions(self):
if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,)
return super().get_permissions()
class UserGrantedNodeChildrenApi(UserPermissionCacheMixin, ListAPIView):
"""
获取用户自己授权节点下子节点的api
"""
permission_classes = (IsValidUser,)
serializer_class = serializers.AssetPermissionNodeSerializer
def get_object(self):
return self.request.user
def get_children_queryset(self):
user = self.get_object()
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
node_id = self.request.query_params.get('id')
nodes_granted = util.get_nodes_with_assets()
if not nodes_granted:
return []
root_nodes = [node for node in nodes_granted.keys() if node.is_root()]
queryset = []
if node_id and node_id in [str(node.id) for node in nodes_granted]:
node = [node for node in nodes_granted if str(node.id) == node_id][0]
elif len(root_nodes) == 1:
node = root_nodes[0]
node.assets_amount = len(nodes_granted[node])
queryset.append(node)
else:
for node in root_nodes:
node.assets_amount = len(nodes_granted[node])
queryset.append(node)
return queryset
children = []
for child in node.get_children():
if child in nodes_granted:
child.assets_amount = len(nodes_granted[node])
children.append(child)
children = sorted(children, key=lambda x: x.value)
queryset.extend(children)
fake_nodes = []
for asset, system_users in nodes_granted[node].items():
fake_node = asset.as_node()
fake_node.assets_amount = 0
system_users = [s for s in system_users if s.protocol == asset.protocol]
fake_node.asset.system_users_granted = system_users
fake_node.key = node.key + ':0'
fake_nodes.append(fake_node)
fake_nodes = sorted(fake_nodes, key=lambda x: x.value)
queryset.extend(fake_nodes)
return queryset
def get_search_queryset(self, keyword):
user = self.get_object()
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
nodes_granted = util.get_nodes_with_assets()
queryset = []
for node, assets in nodes_granted.items():
matched_assets = []
node_matched = node.value.lower().find(keyword.lower()) >= 0
asset_has_matched = False
for asset, system_users in assets.items():
asset_matched = (asset.hostname.lower().find(keyword.lower()) >= 0) \
or (asset.ip.find(keyword.lower()) >= 0)
if node_matched or asset_matched:
asset_has_matched = True
fake_node = asset.as_node()
fake_node.assets_amount = 0
system_users = [s for s in system_users if
s.protocol == asset.protocol]
fake_node.asset.system_users_granted = system_users
fake_node.key = node.key + ':0'
matched_assets.append(fake_node)
if asset_has_matched:
node.assets_amount = len(matched_assets)
queryset.append(node)
queryset.extend(sorted(matched_assets, key=lambda x: x.value))
return queryset
def get_queryset(self):
keyword = self.request.query_params.get('search')
if keyword:
return self.get_search_queryset(keyword)
else:
return self.get_children_queryset()
class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
permission_classes = (IsOrgAdminOrAppUser,)
def get(self, request, *args, **kwargs):
user_id = request.query_params.get('user_id', '')
asset_id = request.query_params.get('asset_id', '')
system_id = request.query_params.get('system_user_id', '')
action_name = request.query_params.get('action_name', '')
user = get_object_or_404(User, id=user_id)
asset = get_object_or_404(Asset, id=asset_id)
su = get_object_or_404(SystemUser, id=system_id)
action = get_object_or_404(Action, name=action_name)
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
granted_assets = util.get_assets()
granted_system_users = granted_assets.get(asset, [])
if su not in granted_system_users:
return Response({'msg': False}, status=403)
_su = next((s for s in granted_system_users if s.id == su.id), None)
if not check_system_user_action(_su, action):
return Response({'msg': False}, status=403)
return Response({'msg': True}, status=200)
class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, APIView):
permission_classes = (IsOrgAdminOrAppUser,)
def get(self, request, *args, **kwargs):
user_id = request.query_params.get('user_id', '')
asset_id = request.query_params.get('asset_id', '')
system_id = request.query_params.get('system_user_id', '')
user = get_object_or_404(User, id=user_id)
asset = get_object_or_404(Asset, id=asset_id)
su = get_object_or_404(SystemUser, id=system_id)
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
granted_assets = util.get_assets()
granted_system_users = granted_assets.get(asset, [])
_su = next((s for s in granted_system_users if s.id == su.id), None)
if not _su:
return Response({'actions': []}, status=403)
actions = [action.name for action in getattr(_su, 'actions', [])]
return Response({'actions': actions}, status=200)