mirror of https://github.com/jumpserver/jumpserver
				
				
				
			fix: 修复用户授权资产账号API及Model处理逻辑
							parent
							
								
									e3b138be3a
								
							
						
					
					
						commit
						351d3b297d
					
				| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,14 +58,15 @@ user_permission_urlpatterns = [
 | 
			
		|||
    # 收藏的资产
 | 
			
		||||
    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'),
 | 
			
		||||
    # v3 中上面的 API 基本不用动
 | 
			
		||||
 | 
			
		||||
    # Todo: 删除
 | 
			
		||||
    # Todo: v3 删除
 | 
			
		||||
    # 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'),
 | 
			
		||||
 | 
			
		||||
    # Todo: 增加
 | 
			
		||||
    # 获取所有和资产相关联的账号列表
 | 
			
		||||
    # Todo: v3 增加
 | 
			
		||||
    # 获取所有和资产-用户关联的账号列表
 | 
			
		||||
    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')
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			@ -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/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'),
 | 
			
		||||
 | 
			
		||||
    # Todo: v3 删除
 | 
			
		||||
    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 = [
 | 
			
		||||
    # Todo: 获取规则中授权的所有账号列表
 | 
			
		||||
    #
 | 
			
		||||
    # 授权规则中授权的资产
 | 
			
		||||
    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'),
 | 
			
		||||
 | 
			
		||||
    # 验证用户是否有某个资产和系统用户的权限
 | 
			
		||||
    # 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'),
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue