From 351d3b297dbf9abf1a3513967ab31d193a4feae4 Mon Sep 17 00:00:00 2001 From: "Jiangjie.Bai" Date: Wed, 28 Sep 2022 18:40:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E8=B5=84=E4=BA=A7=E8=B4=A6=E5=8F=B7API?= =?UTF-8?q?=E5=8F=8AModel=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/account.py | 6 ++ apps/assets/models/asset/common.py | 3 +- apps/perms/api/user_permission/common.py | 22 ++-- apps/perms/models/asset_permission.py | 122 ++++++++++++---------- apps/perms/serializers/user_permission.py | 3 +- apps/perms/urls/asset_permission.py | 15 ++- 6 files changed, 98 insertions(+), 73 deletions(-) diff --git a/apps/assets/models/account.py b/apps/assets/models/account.py index be568d9dd..4394f6583 100644 --- a/apps/assets/models/account.py +++ b/apps/assets/models/account.py @@ -1,4 +1,5 @@ from django.db import models +from django.db.models import Q from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords @@ -54,6 +55,11 @@ class Account(BaseAccount): """ @USER 动态用户的账号(self) """ 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 Meta: diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index 163d1a873..ef64e5fb6 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -183,7 +183,8 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel): return self.accounts.all() if AssetPermission.SpecialAccount.ALL in account_names: 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) return accounts diff --git a/apps/perms/api/user_permission/common.py b/apps/perms/api/user_permission/common.py index 962d079bb..52d3a4f31 100644 --- a/apps/perms/api/user_permission/common.py +++ b/apps/perms/api/user_permission/common.py @@ -2,8 +2,8 @@ # import uuid import time +from collections import defaultdict -from django.db.models import Q from django.shortcuts import get_object_or_404 from django.utils.decorators import method_decorator 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 import serializers -from perms.models import AssetPermission +from perms.models import AssetPermission, Action logger = get_logger(__name__) @@ -162,15 +162,19 @@ class UserGrantedAssetAccounts(ListAPIView): return asset def get_queryset(self): - # 获取用户-资产的授权规则 - assetperms = AssetPermission.filter(self.user, self.asset) - account_names = AssetPermission.get_account_names(assetperms) - accounts = list(self.asset.filter_accounts(account_names)) + accounts = AssetPermission.get_user_perm_asset_accounts( + self.user, self.asset, with_actions=True + ) + return accounts # @INPUT @USER - inner_accounts = [Account.get_input_account(), Account.get_user_account(self.user.username)] - all_accounts = accounts + inner_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 - return all_accounts + # return accounts class MyGrantedAssetAccounts(UserGrantedAssetAccounts): diff --git a/apps/perms/models/asset_permission.py b/apps/perms/models/asset_permission.py index fe9d1b1b7..76da872f8 100644 --- a/apps/perms/models/asset_permission.py +++ b/apps/perms/models/asset_permission.py @@ -5,6 +5,7 @@ from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from django.db import models from django.db.models import F, Q, TextChoices +from collections import defaultdict from assets.models import Asset, Node, FamilyMixin, Account 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)) 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): def valid(self): @@ -212,94 +218,96 @@ class AssetPermission(OrgModelMixin): names = [node.full_value for node in self.nodes.all()] return names + # Related accounts def get_asset_accounts(self): asset_ids = self.get_all_assets(flat=True) - queries = Q(asset_id__in=asset_ids) \ - & (Q(username__in=self.accounts) | Q(name__in=self.accounts)) - accounts = Account.objects.filter(queries) + accounts = Account.filter(asset_ids, self.accounts) return accounts @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() for perm in perms: - perm: cls if not isinstance(perm.accounts, list): continue account_names.update(perm.accounts) return account_names @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: user_assetperm_ids = cls.filter_by_user(user, flat=True) - assetperm_ids.append(user_assetperm_ids) + perm_ids.append(user_assetperm_ids) if asset: asset_assetperm_ids = cls.filter_by_asset(asset, flat=True) - assetperm_ids.append(asset_assetperm_ids) - if account: - account_assetperm_ids = cls.filter_by_account(account, flat=True) - assetperm_ids.append(account_assetperm_ids) + perm_ids.append(asset_assetperm_ids) # & 是同时满足,比如有用户,但是用户的规则是空,那么返回也应该是空 - assetperm_ids = list(reduce(lambda x, y: set(x) & set(y), assetperm_ids)) - assetperms = cls.objects.filter(id__in=assetperm_ids).valid().order_by('-date_expired') - return assetperms + perm_ids = list(reduce(lambda x, y: set(x) & set(y), perm_ids)) + perms = cls.objects.filter(id__in=perm_ids) + if account_names: + perms = perms.filter_by_accounts(account_names) + return perms.valid().order_by('-date_expired') @classmethod def filter_by_user(cls, user, with_group=True, flat=False): - assetperm_ids = set() - user_assetperm_ids = AssetPermission.users.through.objects \ - .filter(user_id=user.id) \ - .values_list('assetpermission_id', flat=True) \ - .distinct() - assetperm_ids.update(user_assetperm_ids) - + perm_ids = set() + user_perm_ids = AssetPermission.users.through.objects.filter( + user_id=user.id + ).values_list('assetpermission_id', flat=True).distinct() + perm_ids.update(user_perm_ids) if with_group: usergroup_ids = user.get_groups(flat=True) - usergroups_assetperm_id = AssetPermission.user_groups.through.objects \ - .filter(usergroup_id__in=usergroup_ids) \ - .values_list('assetpermission_id', flat=True) \ - .distinct() - assetperm_ids.update(usergroups_assetperm_id) - + usergroups_perm_id = AssetPermission.user_groups.through.objects.filter( + usergroup_id__in=usergroup_ids + ).values_list('assetpermission_id', flat=True).distinct() + perm_ids.update(usergroups_perm_id) if flat: - return assetperm_ids - else: - assetperms = cls.objects.filter(id__in=assetperm_ids).valid() - return assetperms + return perm_ids + perms = cls.objects.filter(id__in=perm_ids).valid() + return perms @classmethod def filter_by_asset(cls, asset, with_node=True, flat=False): - assetperm_ids = set() - asset_assetperm_ids = AssetPermission.assets.through.objects \ - .filter(asset_id=asset.id) \ - .values_list('assetpermission_id', flat=True) - assetperm_ids.update(asset_assetperm_ids) - + perm_ids = set() + asset_perm_ids = AssetPermission.assets.through.objects.filter( + asset_id=asset.id + ).values_list('assetpermission_id', flat=True).distinct() + perm_ids.update(asset_perm_ids) if with_node: node_ids = asset.get_all_nodes(flat=True) - node_assetperm_ids = AssetPermission.nodes.through.objects \ - .filter(node_id__in=node_ids) \ - .values_list('assetpermission_id', flat=True) - assetperm_ids.update(node_assetperm_ids) - + node_perm_ids = AssetPermission.nodes.through.objects.filter( + node_id__in=node_ids + ).values_list('assetpermission_id', flat=True).distinct() + perm_ids.update(node_perm_ids) if flat: - return assetperm_ids - else: - assetperms = cls.objects.filter(id__in=assetperm_ids).valid() - 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 + return perm_ids + perms = cls.objects.filter(id__in=perm_ids).valid() + return perms class UserAssetGrantedTreeNodeRelation(OrgModelMixin, FamilyMixin, BaseCreateUpdateModel): diff --git a/apps/perms/serializers/user_permission.py b/apps/perms/serializers/user_permission.py index 40754b637..9b7ac091c 100644 --- a/apps/perms/serializers/user_permission.py +++ b/apps/perms/serializers/user_permission.py @@ -49,8 +49,9 @@ class AccountsGrantedSerializer(serializers.ModelSerializer): # Todo: 添加前端登录逻辑中需要的一些字段,比如:是否需要手动输入密码 # need_manual = serializers.BooleanField(label=_('Need manual input')) + actions = ActionsField(read_only=True) class Meta: model = Account - fields = ['id', 'name', 'username'] + fields = ['id', 'name', 'username', 'actions'] read_only_fields = fields diff --git a/apps/perms/urls/asset_permission.py b/apps/perms/urls/asset_permission.py index 28af1a94d..5606ee639 100644 --- a/apps/perms/urls/asset_permission.py +++ b/apps/perms/urls/asset_permission.py @@ -58,14 +58,15 @@ user_permission_urlpatterns = [ # 收藏的资产 path('/nodes/favorite/assets/', api.UserFavoriteGrantedAssetsApi.as_view(), name='user-ungrouped-assets'), path('nodes/favorite/assets/', api.MyFavoriteGrantedAssetsApi.as_view(), name='my-ungrouped-assets'), + # v3 中上面的 API 基本不用动 - # Todo: 删除 + # Todo: v3 删除 # Asset System users path('/assets//system-users/', api.UserGrantedAssetSystemUsersForAdminApi.as_view(), name='user-asset-system-users'), path('assets//system-users/', api.MyGrantedAssetSystemUsersApi.as_view(), name='my-asset-system-users'), - # Todo: 增加 - # 获取所有和资产相关联的账号列表 + # Todo: v3 增加 + # 获取所有和资产-用户关联的账号列表 path('/assets//accounts/', api.UserGrantedAssetAccounts.as_view(), name='user-asset-accounts'), path('assets//accounts/', api.MyGrantedAssetAccounts.as_view(), name='my-asset-accounts') ] @@ -77,17 +78,21 @@ user_group_permission_urlpatterns = [ path('/nodes/children/', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes-children'), path('/nodes/children/tree/', api.UserGroupGrantedNodeChildrenAsTreeApi.as_view(), name='user-group-nodes-children-as-tree'), path('/nodes//assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'), + + # Todo: v3 删除 path('/assets//system-users/', api.UserGroupGrantedAssetSystemUsersApi.as_view(), name='user-group-asset-system-users'), + # Todo: v3 增加 + # 获取所有和资产-用户组关联的账号列表 + path('/assets//accounts/', api.UserGrantedAssetAccounts.as_view(), name='user-asset-accounts'), ] permission_urlpatterns = [ - # Todo: 获取规则中授权的所有账号列表 - # # 授权规则中授权的资产 path('/assets/all/', api.AssetPermissionAllAssetListApi.as_view(), name='asset-permission-all-assets'), path('/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/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'),