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 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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'),
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue