fix: 修复用户授权资产账号API及Model处理逻辑

pull/8931/head
Jiangjie.Bai 2022-09-28 18:40:33 +08:00
parent e3b138be3a
commit 351d3b297d
6 changed files with 98 additions and 73 deletions

View File

@ -1,4 +1,5 @@
from django.db import models from django.db import models
from django.db.models import Q
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
@ -54,6 +55,11 @@ class Account(BaseAccount):
""" @USER 动态用户的账号(self) """ """ @USER 动态用户的账号(self) """
return cls(name=cls.InnerAccount.USER.value, username=username) return cls(name=cls.InnerAccount.USER.value, username=username)
@classmethod
def filter(cls, asset_ids, account_usernames):
queries = Q(asset_id__in=asset_ids) & Q(username__in=account_usernames)
return cls.objects.filter(queries)
class AccountTemplate(BaseAccount): class AccountTemplate(BaseAccount):
class Meta: class Meta:

View File

@ -183,7 +183,8 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
return self.accounts.all() return self.accounts.all()
if AssetPermission.SpecialAccount.ALL in account_names: if AssetPermission.SpecialAccount.ALL in account_names:
return self.accounts.all() return self.accounts.all()
queries = Q(name__in=account_names) | Q(username__in=account_names) # queries = Q(name__in=account_names) | Q(username__in=account_names)
queries = Q(username__in=account_names)
accounts = self.accounts.filter(queries) accounts = self.accounts.filter(queries)
return accounts return accounts

View File

@ -2,8 +2,8 @@
# #
import uuid import uuid
import time import time
from collections import defaultdict
from django.db.models import Q
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from rest_framework.views import APIView, Response from rest_framework.views import APIView, Response
@ -21,7 +21,7 @@ from common.utils import get_logger, lazyproperty
from perms.hands import User, Asset, Account from perms.hands import User, Asset, Account
from perms import serializers from perms import serializers
from perms.models import AssetPermission from perms.models import AssetPermission, Action
logger = get_logger(__name__) logger = get_logger(__name__)
@ -162,15 +162,19 @@ class UserGrantedAssetAccounts(ListAPIView):
return asset return asset
def get_queryset(self): def get_queryset(self):
# 获取用户-资产的授权规则 accounts = AssetPermission.get_user_perm_asset_accounts(
assetperms = AssetPermission.filter(self.user, self.asset) self.user, self.asset, with_actions=True
account_names = AssetPermission.get_account_names(assetperms) )
accounts = list(self.asset.filter_accounts(account_names)) return accounts
# @INPUT @USER # @INPUT @USER
inner_accounts = [Account.get_input_account(), Account.get_user_account(self.user.username)] # inner_accounts = [
all_accounts = accounts + inner_accounts # Account.get_input_account(), Account.get_user_account(self.user.username)
# ]
# for inner_account in inner_accounts:
# inner_account.actions = Action.ALL
# accounts = accounts + inner_accounts
# 构造默认包含的账号,如: @INPUT @USER # 构造默认包含的账号,如: @INPUT @USER
return all_accounts # return accounts
class MyGrantedAssetAccounts(UserGrantedAssetAccounts): class MyGrantedAssetAccounts(UserGrantedAssetAccounts):

View File

@ -5,6 +5,7 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db import models from django.db import models
from django.db.models import F, Q, TextChoices from django.db.models import F, Q, TextChoices
from collections import defaultdict
from assets.models import Asset, Node, FamilyMixin, Account from assets.models import Asset, Node, FamilyMixin, Account
from orgs.mixins.models import OrgModelMixin from orgs.mixins.models import OrgModelMixin
@ -76,6 +77,11 @@ class AssetPermissionQuerySet(models.QuerySet):
q = (Q(is_active=False) | Q(date_start__gt=now) | Q(date_expired__lt=now)) q = (Q(is_active=False) | Q(date_start__gt=now) | Q(date_expired__lt=now))
return self.filter(q) return self.filter(q)
def filter_by_accounts(self, accounts):
q = Q(accounts__contains=accounts) | \
Q(accounts__contains=AssetPermission.SpecialAccount.ALL.value)
return self.filter(q)
class AssetPermissionManager(OrgManager): class AssetPermissionManager(OrgManager):
def valid(self): def valid(self):
@ -212,94 +218,96 @@ class AssetPermission(OrgModelMixin):
names = [node.full_value for node in self.nodes.all()] names = [node.full_value for node in self.nodes.all()]
return names return names
# Related accounts
def get_asset_accounts(self): def get_asset_accounts(self):
asset_ids = self.get_all_assets(flat=True) asset_ids = self.get_all_assets(flat=True)
queries = Q(asset_id__in=asset_ids) \ accounts = Account.filter(asset_ids, self.accounts)
& (Q(username__in=self.accounts) | Q(name__in=self.accounts))
accounts = Account.objects.filter(queries)
return accounts return accounts
@classmethod @classmethod
def get_account_names(cls, perms): def get_user_perm_asset_accounts(cls, user, asset: Asset, with_actions=False):
perms = cls.filter(user, asset)
all_account_names = cls.retrieve_account_names(perms)
accounts = asset.filter_accounts(all_account_names)
if with_actions:
cls.set_accounts_actions(accounts, perms=perms)
return accounts
@classmethod
def set_accounts_actions(cls, accounts, perms):
# set account actions
account_names = accounts.values_list('username', flat=True)
perms = perms.filter_by_accounts(account_names)
account_names_actions_map = defaultdict(set)
account_names_actions = perms.values_list('accounts', 'actions')
for account_names, actions in account_names_actions:
for account_name in account_names:
account_names_actions_map[account_name] |= actions
for account in accounts:
account.actions = account_names_actions_map.get(account.username)
return accounts
@classmethod
def retrieve_account_names(cls, perms):
account_names = set() account_names = set()
for perm in perms: for perm in perms:
perm: cls
if not isinstance(perm.accounts, list): if not isinstance(perm.accounts, list):
continue continue
account_names.update(perm.accounts) account_names.update(perm.accounts)
return account_names return account_names
@classmethod @classmethod
def filter(cls, user=None, asset=None, account=None): def filter(cls, user=None, asset=None, account_names=None):
""" 获取同时包含 用户-资产-账号 的授权规则 """ """ 获取同时包含 用户-资产-账号 的授权规则 """
assetperm_ids = [] perm_ids = []
if user: if user:
user_assetperm_ids = cls.filter_by_user(user, flat=True) user_assetperm_ids = cls.filter_by_user(user, flat=True)
assetperm_ids.append(user_assetperm_ids) perm_ids.append(user_assetperm_ids)
if asset: if asset:
asset_assetperm_ids = cls.filter_by_asset(asset, flat=True) asset_assetperm_ids = cls.filter_by_asset(asset, flat=True)
assetperm_ids.append(asset_assetperm_ids) perm_ids.append(asset_assetperm_ids)
if account:
account_assetperm_ids = cls.filter_by_account(account, flat=True)
assetperm_ids.append(account_assetperm_ids)
# & 是同时满足,比如有用户,但是用户的规则是空,那么返回也应该是空 # & 是同时满足,比如有用户,但是用户的规则是空,那么返回也应该是空
assetperm_ids = list(reduce(lambda x, y: set(x) & set(y), assetperm_ids)) perm_ids = list(reduce(lambda x, y: set(x) & set(y), perm_ids))
assetperms = cls.objects.filter(id__in=assetperm_ids).valid().order_by('-date_expired') perms = cls.objects.filter(id__in=perm_ids)
return assetperms if account_names:
perms = perms.filter_by_accounts(account_names)
return perms.valid().order_by('-date_expired')
@classmethod @classmethod
def filter_by_user(cls, user, with_group=True, flat=False): def filter_by_user(cls, user, with_group=True, flat=False):
assetperm_ids = set() perm_ids = set()
user_assetperm_ids = AssetPermission.users.through.objects \ user_perm_ids = AssetPermission.users.through.objects.filter(
.filter(user_id=user.id) \ user_id=user.id
.values_list('assetpermission_id', flat=True) \ ).values_list('assetpermission_id', flat=True).distinct()
.distinct() perm_ids.update(user_perm_ids)
assetperm_ids.update(user_assetperm_ids)
if with_group: if with_group:
usergroup_ids = user.get_groups(flat=True) usergroup_ids = user.get_groups(flat=True)
usergroups_assetperm_id = AssetPermission.user_groups.through.objects \ usergroups_perm_id = AssetPermission.user_groups.through.objects.filter(
.filter(usergroup_id__in=usergroup_ids) \ usergroup_id__in=usergroup_ids
.values_list('assetpermission_id', flat=True) \ ).values_list('assetpermission_id', flat=True).distinct()
.distinct() perm_ids.update(usergroups_perm_id)
assetperm_ids.update(usergroups_assetperm_id)
if flat: if flat:
return assetperm_ids return perm_ids
else: perms = cls.objects.filter(id__in=perm_ids).valid()
assetperms = cls.objects.filter(id__in=assetperm_ids).valid() return perms
return assetperms
@classmethod @classmethod
def filter_by_asset(cls, asset, with_node=True, flat=False): def filter_by_asset(cls, asset, with_node=True, flat=False):
assetperm_ids = set() perm_ids = set()
asset_assetperm_ids = AssetPermission.assets.through.objects \ asset_perm_ids = AssetPermission.assets.through.objects.filter(
.filter(asset_id=asset.id) \ asset_id=asset.id
.values_list('assetpermission_id', flat=True) ).values_list('assetpermission_id', flat=True).distinct()
assetperm_ids.update(asset_assetperm_ids) perm_ids.update(asset_perm_ids)
if with_node: if with_node:
node_ids = asset.get_all_nodes(flat=True) node_ids = asset.get_all_nodes(flat=True)
node_assetperm_ids = AssetPermission.nodes.through.objects \ node_perm_ids = AssetPermission.nodes.through.objects.filter(
.filter(node_id__in=node_ids) \ node_id__in=node_ids
.values_list('assetpermission_id', flat=True) ).values_list('assetpermission_id', flat=True).distinct()
assetperm_ids.update(node_assetperm_ids) perm_ids.update(node_perm_ids)
if flat: if flat:
return assetperm_ids return perm_ids
else: perms = cls.objects.filter(id__in=perm_ids).valid()
assetperms = cls.objects.filter(id__in=assetperm_ids).valid() return perms
return assetperms
@classmethod
def filter_by_account(cls, account, flat=False):
queries = Q(accounts__contains=account) | Q(accounts__contains=cls.SpecialAccount.ALL.value)
assetperms = cls.objects.filter(queries).valid()
if flat:
assetperm_ids = assetperms.values_list('id', flat=True)
return assetperm_ids
else:
return assetperms
class UserAssetGrantedTreeNodeRelation(OrgModelMixin, FamilyMixin, BaseCreateUpdateModel): class UserAssetGrantedTreeNodeRelation(OrgModelMixin, FamilyMixin, BaseCreateUpdateModel):

View File

@ -49,8 +49,9 @@ class AccountsGrantedSerializer(serializers.ModelSerializer):
# Todo: 添加前端登录逻辑中需要的一些字段,比如:是否需要手动输入密码 # Todo: 添加前端登录逻辑中需要的一些字段,比如:是否需要手动输入密码
# need_manual = serializers.BooleanField(label=_('Need manual input')) # need_manual = serializers.BooleanField(label=_('Need manual input'))
actions = ActionsField(read_only=True)
class Meta: class Meta:
model = Account model = Account
fields = ['id', 'name', 'username'] fields = ['id', 'name', 'username', 'actions']
read_only_fields = fields read_only_fields = fields

View File

@ -58,14 +58,15 @@ user_permission_urlpatterns = [
# 收藏的资产 # 收藏的资产
path('<uuid:pk>/nodes/favorite/assets/', api.UserFavoriteGrantedAssetsApi.as_view(), name='user-ungrouped-assets'), path('<uuid:pk>/nodes/favorite/assets/', api.UserFavoriteGrantedAssetsApi.as_view(), name='user-ungrouped-assets'),
path('nodes/favorite/assets/', api.MyFavoriteGrantedAssetsApi.as_view(), name='my-ungrouped-assets'), path('nodes/favorite/assets/', api.MyFavoriteGrantedAssetsApi.as_view(), name='my-ungrouped-assets'),
# v3 中上面的 API 基本不用动
# Todo: 删除 # Todo: v3 删除
# Asset System users # Asset System users
path('<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGrantedAssetSystemUsersForAdminApi.as_view(), name='user-asset-system-users'), path('<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGrantedAssetSystemUsersForAdminApi.as_view(), name='user-asset-system-users'),
path('assets/<uuid:asset_id>/system-users/', api.MyGrantedAssetSystemUsersApi.as_view(), name='my-asset-system-users'), path('assets/<uuid:asset_id>/system-users/', api.MyGrantedAssetSystemUsersApi.as_view(), name='my-asset-system-users'),
# Todo: 增加 # Todo: v3 增加
# 获取所有和资产关联的账号列表 # 获取所有和资产-用户关联的账号列表
path('<uuid:pk>/assets/<uuid:asset_id>/accounts/', api.UserGrantedAssetAccounts.as_view(), name='user-asset-accounts'), path('<uuid:pk>/assets/<uuid:asset_id>/accounts/', api.UserGrantedAssetAccounts.as_view(), name='user-asset-accounts'),
path('assets/<uuid:asset_id>/accounts/', api.MyGrantedAssetAccounts.as_view(), name='my-asset-accounts') path('assets/<uuid:asset_id>/accounts/', api.MyGrantedAssetAccounts.as_view(), name='my-asset-accounts')
] ]
@ -77,17 +78,21 @@ user_group_permission_urlpatterns = [
path('<uuid:pk>/nodes/children/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes-children'), path('<uuid:pk>/nodes/children/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes-children'),
path('<uuid:pk>/nodes/children/tree/', api.UserGroupGrantedNodeChildrenAsTreeApi.as_view(), name='user-group-nodes-children-as-tree'), path('<uuid:pk>/nodes/children/tree/', api.UserGroupGrantedNodeChildrenAsTreeApi.as_view(), name='user-group-nodes-children-as-tree'),
path('<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'), path('<uuid:pk>/nodes/<uuid:node_id>/assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'),
# Todo: v3 删除
path('<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGroupGrantedAssetSystemUsersApi.as_view(), name='user-group-asset-system-users'), path('<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGroupGrantedAssetSystemUsersApi.as_view(), name='user-group-asset-system-users'),
# Todo: v3 增加
# 获取所有和资产-用户组关联的账号列表
path('<uuid:pk>/assets/<uuid:asset_id>/accounts/', api.UserGrantedAssetAccounts.as_view(), name='user-asset-accounts'),
] ]
permission_urlpatterns = [ permission_urlpatterns = [
# Todo: 获取规则中授权的所有账号列表
#
# 授权规则中授权的资产 # 授权规则中授权的资产
path('<uuid:pk>/assets/all/', api.AssetPermissionAllAssetListApi.as_view(), name='asset-permission-all-assets'), path('<uuid:pk>/assets/all/', api.AssetPermissionAllAssetListApi.as_view(), name='asset-permission-all-assets'),
path('<uuid:pk>/users/all/', api.AssetPermissionAllUserListApi.as_view(), name='asset-permission-all-users'), path('<uuid:pk>/users/all/', api.AssetPermissionAllUserListApi.as_view(), name='asset-permission-all-users'),
# 验证用户是否有某个资产和系统用户的权限 # 验证用户是否有某个资产和系统用户的权限
# Todo: API 需要修改,验证用户有某个账号的权限
path('user/validate/', api.ValidateUserAssetPermissionApi.as_view(), name='validate-user-asset-permission'), path('user/validate/', api.ValidateUserAssetPermissionApi.as_view(), name='validate-user-asset-permission'),
path('user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'), path('user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'),