mirror of https://github.com/jumpserver/jumpserver
				
				
				
			Merge branch 'v3' of github.com:jumpserver/jumpserver into v3
						commit
						e0e14a2fe1
					
				| 
						 | 
					@ -5,8 +5,10 @@
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
from functools import reduce
 | 
					from functools import reduce
 | 
				
			||||||
 | 
					from collections import Iterable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.db.models import Q
 | 
				
			||||||
from django.utils.translation import ugettext_lazy as _
 | 
					from django.utils.translation import ugettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from common.utils import lazyproperty
 | 
					from common.utils import lazyproperty
 | 
				
			||||||
| 
						 | 
					@ -57,13 +59,17 @@ class NodesRelationMixin:
 | 
				
			||||||
        return nodes
 | 
					        return nodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_all_nodes(self, flat=False):
 | 
					    def get_all_nodes(self, flat=False):
 | 
				
			||||||
        nodes = []
 | 
					        from ..node import Node
 | 
				
			||||||
 | 
					        node_keys = set()
 | 
				
			||||||
        for node in self.get_nodes():
 | 
					        for node in self.get_nodes():
 | 
				
			||||||
            _nodes = node.get_ancestors(with_self=True)
 | 
					            ancestor_keys = node.get_ancestor_keys(with_self=True)
 | 
				
			||||||
            nodes.append(_nodes)
 | 
					            node_keys.update(ancestor_keys)
 | 
				
			||||||
 | 
					        nodes = Node.objects.filter(key__in=node_keys).distinct()
 | 
				
			||||||
        if flat:
 | 
					        if flat:
 | 
				
			||||||
            nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
 | 
					            node_ids = set(nodes.values_list('id', flat=True))
 | 
				
			||||||
        return nodes
 | 
					            return node_ids
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return nodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
 | 
					class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
 | 
				
			||||||
| 
						 | 
					@ -161,6 +167,14 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
 | 
				
			||||||
        tree_node = TreeNode(**data)
 | 
					        tree_node = TreeNode(**data)
 | 
				
			||||||
        return tree_node
 | 
					        return tree_node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def filter_accounts(self, account_names=None):
 | 
				
			||||||
 | 
					        if account_names is None:
 | 
				
			||||||
 | 
					            return self.accounts.all()
 | 
				
			||||||
 | 
					        assert isinstance(account_names, Iterable), '`account_names` must be an iterable object'
 | 
				
			||||||
 | 
					        queries = Q(name__in=account_names) | Q(username__in=account_names)
 | 
				
			||||||
 | 
					        accounts = self.accounts.filter(queries)
 | 
				
			||||||
 | 
					        return accounts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        unique_together = [('org_id', 'name')]
 | 
					        unique_together = [('org_id', 'name')]
 | 
				
			||||||
        verbose_name = _("Asset")
 | 
					        verbose_name = _("Asset")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
| 
						 | 
					@ -20,6 +21,7 @@ from common.utils import get_logger, lazyproperty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from perms.hands import User, Asset
 | 
					from perms.hands import User, Asset
 | 
				
			||||||
from perms import serializers
 | 
					from perms import serializers
 | 
				
			||||||
 | 
					from perms.models import AssetPermission
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logger = get_logger(__name__)
 | 
					logger = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +30,8 @@ __all__ = [
 | 
				
			||||||
    'ValidateUserAssetPermissionApi',
 | 
					    'ValidateUserAssetPermissionApi',
 | 
				
			||||||
    'GetUserAssetPermissionActionsApi',
 | 
					    'GetUserAssetPermissionActionsApi',
 | 
				
			||||||
    'MyGrantedAssetSystemUsersApi',
 | 
					    'MyGrantedAssetSystemUsersApi',
 | 
				
			||||||
 | 
					    'UserGrantedAssetAccounts',
 | 
				
			||||||
 | 
					    'MyGrantedAssetAccounts',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -138,3 +142,37 @@ class MyGrantedAssetSystemUsersApi(UserGrantedAssetSystemUsersForAdminApi):
 | 
				
			||||||
    def user(self):
 | 
					    def user(self):
 | 
				
			||||||
        return self.request.user
 | 
					        return self.request.user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserGrantedAssetAccounts(ListAPIView):
 | 
				
			||||||
 | 
					    serializer_class = serializers.AccountsGrantedSerializer
 | 
				
			||||||
 | 
					    rbac_perms = {
 | 
				
			||||||
 | 
					        'list': 'perms.view_userassets'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @lazyproperty
 | 
				
			||||||
 | 
					    def user(self):
 | 
				
			||||||
 | 
					        user_id = self.kwargs.get('pk')
 | 
				
			||||||
 | 
					        return User.objects.get(id=user_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @lazyproperty
 | 
				
			||||||
 | 
					    def asset(self):
 | 
				
			||||||
 | 
					        asset_id = self.kwargs.get('asset_id')
 | 
				
			||||||
 | 
					        kwargs = {'id': asset_id, 'is_active': True}
 | 
				
			||||||
 | 
					        asset = get_object_or_404(Asset, **kwargs)
 | 
				
			||||||
 | 
					        return asset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_queryset(self):
 | 
				
			||||||
 | 
					        # 获取用户-资产的授权规则
 | 
				
			||||||
 | 
					        assetperms = AssetPermission.filter_permissions(self.user, self.asset)
 | 
				
			||||||
 | 
					        account_names = AssetPermission.get_account_names(assetperms)
 | 
				
			||||||
 | 
					        accounts = self.asset.filter_accounts(account_names)
 | 
				
			||||||
 | 
					        # 构造默认包含的账号,如: @INPUT @USER
 | 
				
			||||||
 | 
					        return accounts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MyGrantedAssetAccounts(UserGrantedAssetAccounts):
 | 
				
			||||||
 | 
					    permission_classes = (IsValidUser,)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @lazyproperty
 | 
				
			||||||
 | 
					    def user(self):
 | 
				
			||||||
 | 
					        return self.request.user
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ from perms.utils.user_permission import UserGrantedNodesQueryUtils
 | 
				
			||||||
logger = get_logger(__name__)
 | 
					logger = get_logger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = [
 | 
					__all__ = [
 | 
				
			||||||
    'UserGrantedNodesForAdminApi',
 | 
					    'UserGrantedNodesApi',
 | 
				
			||||||
    'MyGrantedNodesApi',
 | 
					    'MyGrantedNodesApi',
 | 
				
			||||||
    'MyGrantedNodesAsTreeApi',
 | 
					    'MyGrantedNodesAsTreeApi',
 | 
				
			||||||
    'UserGrantedNodeChildrenForAdminApi',
 | 
					    'UserGrantedNodeChildrenForAdminApi',
 | 
				
			||||||
| 
						 | 
					@ -118,11 +118,11 @@ class MyGrantedNodeChildrenAsTreeApi(AssetRoleUserMixin, UserGrantedNodeChildren
 | 
				
			||||||
        return permissions
 | 
					        return permissions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserGrantedNodesForAdminApi(AssetRoleAdminMixin, UserGrantedNodesMixin, BaseGrantedNodeApi):
 | 
					class UserGrantedNodesApi(AssetRoleAdminMixin, UserGrantedNodesMixin, BaseGrantedNodeApi):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MyGrantedNodesApi(AssetRoleUserMixin, UserGrantedNodesMixin, BaseGrantedNodeApi):
 | 
					class MyGrantedNodesApi(AssetRoleUserMixin, UserGrantedNodesApi):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,17 @@
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					from functools import reduce
 | 
				
			||||||
from django.utils import timezone
 | 
					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 assets.models import Asset, Node, FamilyMixin
 | 
					from assets.models import Asset, Node, FamilyMixin, Account
 | 
				
			||||||
from orgs.mixins.models import OrgModelMixin
 | 
					from orgs.mixins.models import OrgModelMixin
 | 
				
			||||||
from orgs.mixins.models import OrgManager
 | 
					from orgs.mixins.models import OrgManager
 | 
				
			||||||
from common.utils import lazyproperty, date_expired_default
 | 
					from common.utils import lazyproperty, date_expired_default
 | 
				
			||||||
from common.db.models import BaseCreateUpdateModel, BitOperationChoice, UnionQuerySet
 | 
					from common.db.models import BaseCreateUpdateModel, BitOperationChoice, UnionQuerySet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
__all__ = [
 | 
					__all__ = [
 | 
				
			||||||
    'AssetPermission', 'PermNode',
 | 
					    'AssetPermission', 'PermNode',
 | 
				
			||||||
    'UserAssetGrantedTreeNodeRelation',
 | 
					    'UserAssetGrantedTreeNodeRelation',
 | 
				
			||||||
| 
						 | 
					@ -85,20 +85,27 @@ class AssetPermissionManager(OrgManager):
 | 
				
			||||||
class AssetPermission(OrgModelMixin):
 | 
					class AssetPermission(OrgModelMixin):
 | 
				
			||||||
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
 | 
					    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
 | 
				
			||||||
    name = models.CharField(max_length=128, verbose_name=_('Name'))
 | 
					    name = models.CharField(max_length=128, verbose_name=_('Name'))
 | 
				
			||||||
    users = models.ManyToManyField('users.User', blank=True, verbose_name=_("User"), related_name='%(class)ss')
 | 
					    users = models.ManyToManyField('users.User', blank=True, verbose_name=_("User"),
 | 
				
			||||||
    user_groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User group"), related_name='%(class)ss')
 | 
					                                   related_name='%(class)ss')
 | 
				
			||||||
    assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
 | 
					    user_groups = models.ManyToManyField('users.UserGroup', blank=True,
 | 
				
			||||||
    nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes"))
 | 
					                                         verbose_name=_("User group"), related_name='%(class)ss')
 | 
				
			||||||
 | 
					    assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions',
 | 
				
			||||||
 | 
					                                    blank=True, verbose_name=_("Asset"))
 | 
				
			||||||
 | 
					    nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True,
 | 
				
			||||||
 | 
					                                   verbose_name=_("Nodes"))
 | 
				
			||||||
    # 只保存 @ALL (@INPUT @USER 默认包含,将来在全局设置中进行控制)
 | 
					    # 只保存 @ALL (@INPUT @USER 默认包含,将来在全局设置中进行控制)
 | 
				
			||||||
    # 特殊的账号描述
 | 
					    # 特殊的账号描述
 | 
				
			||||||
    # ['@ALL',]
 | 
					    # ['@ALL',]
 | 
				
			||||||
    # 指定账号授权
 | 
					    # 指定账号授权
 | 
				
			||||||
    # ['web', 'root',]
 | 
					    # ['web', 'root',]
 | 
				
			||||||
    accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
 | 
					    accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
 | 
				
			||||||
    actions = models.IntegerField(choices=Action.DB_CHOICES, default=Action.ALL, verbose_name=_("Actions"))
 | 
					    actions = models.IntegerField(choices=Action.DB_CHOICES, default=Action.ALL,
 | 
				
			||||||
 | 
					                                  verbose_name=_("Actions"))
 | 
				
			||||||
    is_active = models.BooleanField(default=True, verbose_name=_('Active'))
 | 
					    is_active = models.BooleanField(default=True, verbose_name=_('Active'))
 | 
				
			||||||
    date_start = models.DateTimeField(default=timezone.now, db_index=True, verbose_name=_("Date start"))
 | 
					    date_start = models.DateTimeField(default=timezone.now, db_index=True,
 | 
				
			||||||
    date_expired = models.DateTimeField(default=date_expired_default, db_index=True, verbose_name=_('Date expired'))
 | 
					                                      verbose_name=_("Date start"))
 | 
				
			||||||
 | 
					    date_expired = models.DateTimeField(default=date_expired_default, db_index=True,
 | 
				
			||||||
 | 
					                                        verbose_name=_('Date expired'))
 | 
				
			||||||
    created_by = models.CharField(max_length=128, blank=True, verbose_name=_('Created by'))
 | 
					    created_by = models.CharField(max_length=128, blank=True, verbose_name=_('Created by'))
 | 
				
			||||||
    date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
 | 
					    date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
 | 
				
			||||||
    from_ticket = models.BooleanField(default=False, verbose_name=_('From ticket'))
 | 
					    from_ticket = models.BooleanField(default=False, verbose_name=_('From ticket'))
 | 
				
			||||||
| 
						 | 
					@ -106,6 +113,11 @@ class AssetPermission(OrgModelMixin):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    objects = AssetPermissionManager.from_queryset(AssetPermissionQuerySet)()
 | 
					    objects = AssetPermissionManager.from_queryset(AssetPermissionQuerySet)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class SpecialAccount(models.TextChoices):
 | 
				
			||||||
 | 
					        ALL = '@ALL', 'All'
 | 
				
			||||||
 | 
					        INPUT = '@INPUT',  'Input'
 | 
				
			||||||
 | 
					        USER = '@USER', 'User'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        unique_together = [('org_id', 'name')]
 | 
					        unique_together = [('org_id', 'name')]
 | 
				
			||||||
        verbose_name = _("Asset permission")
 | 
					        verbose_name = _("Asset permission")
 | 
				
			||||||
| 
						 | 
					@ -174,14 +186,17 @@ class AssetPermission(OrgModelMixin):
 | 
				
			||||||
            models.Prefetch('assets', queryset=Asset.objects.all().only('id')),
 | 
					            models.Prefetch('assets', queryset=Asset.objects.all().only('id')),
 | 
				
			||||||
        ).order_by()
 | 
					        ).order_by()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_all_assets(self):
 | 
					    def get_all_assets(self, flat=False):
 | 
				
			||||||
        from assets.models import Node
 | 
					        from assets.models import Node
 | 
				
			||||||
        nodes_keys = self.nodes.all().values_list('key', flat=True)
 | 
					        nodes_keys = self.nodes.all().values_list('key', flat=True)
 | 
				
			||||||
        asset_ids = set(self.assets.all().values_list('id', flat=True))
 | 
					        asset_ids = set(self.assets.all().values_list('id', flat=True))
 | 
				
			||||||
        nodes_asset_ids = Node.get_nodes_all_asset_ids_by_keys(nodes_keys)
 | 
					        nodes_asset_ids = Node.get_nodes_all_asset_ids_by_keys(nodes_keys)
 | 
				
			||||||
        asset_ids.update(nodes_asset_ids)
 | 
					        asset_ids.update(nodes_asset_ids)
 | 
				
			||||||
        assets = Asset.objects.filter(id__in=asset_ids)
 | 
					        if flat:
 | 
				
			||||||
        return assets
 | 
					            return asset_ids
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            assets = Asset.objects.filter(id__in=asset_ids)
 | 
				
			||||||
 | 
					            return assets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def users_display(self):
 | 
					    def users_display(self):
 | 
				
			||||||
        names = [user.username for user in self.users.all()]
 | 
					        names = [user.username for user in self.users.all()]
 | 
				
			||||||
| 
						 | 
					@ -199,6 +214,94 @@ 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					        return accounts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_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_permissions(cls, user=None, asset=None, account=None):
 | 
				
			||||||
 | 
					        """ 获取同时包含 用户-资产-账号 的授权规则 """
 | 
				
			||||||
 | 
					        assetperm_ids = []
 | 
				
			||||||
 | 
					        if user:
 | 
				
			||||||
 | 
					            user_assetperm_ids = cls.filter_permissions_by_user(user, flat=True)
 | 
				
			||||||
 | 
					            assetperm_ids.append(user_assetperm_ids)
 | 
				
			||||||
 | 
					        if asset:
 | 
				
			||||||
 | 
					            asset_assetperm_ids = cls.filter_permissions_by_asset(asset, flat=True)
 | 
				
			||||||
 | 
					            assetperm_ids.append(asset_assetperm_ids)
 | 
				
			||||||
 | 
					        if account:
 | 
				
			||||||
 | 
					            account_assetperm_ids = cls.filter_permissions_by_account(account, flat=True)
 | 
				
			||||||
 | 
					            assetperm_ids.append(account_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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def filter_permissions_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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if flat:
 | 
				
			||||||
 | 
					            return assetperm_ids
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            assetperms = cls.objects.filter(id__in=assetperm_ids).valid()
 | 
				
			||||||
 | 
					            return assetperms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def filter_permissions_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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if flat:
 | 
				
			||||||
 | 
					            return assetperm_ids
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            assetperms = cls.objects.filter(id__in=assetperm_ids).valid()
 | 
				
			||||||
 | 
					            return assetperms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def filter_permissions_by_account(cls, account, flat=False):
 | 
				
			||||||
 | 
					        assetperms = cls.objects.filter(accounts__contains=account).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):
 | 
				
			||||||
    class NodeFrom(TextChoices):
 | 
					    class NodeFrom(TextChoices):
 | 
				
			||||||
| 
						 | 
					@ -210,7 +313,8 @@ class UserAssetGrantedTreeNodeRelation(OrgModelMixin, FamilyMixin, BaseCreateUpd
 | 
				
			||||||
    node = models.ForeignKey('assets.Node', default=None, on_delete=models.CASCADE,
 | 
					    node = models.ForeignKey('assets.Node', default=None, on_delete=models.CASCADE,
 | 
				
			||||||
                             db_constraint=False, null=False, related_name='granted_node_rels')
 | 
					                             db_constraint=False, null=False, related_name='granted_node_rels')
 | 
				
			||||||
    node_key = models.CharField(max_length=64, verbose_name=_("Key"), db_index=True)
 | 
					    node_key = models.CharField(max_length=64, verbose_name=_("Key"), db_index=True)
 | 
				
			||||||
    node_parent_key = models.CharField(max_length=64, default='', verbose_name=_('Parent key'), db_index=True)
 | 
					    node_parent_key = models.CharField(max_length=64, default='', verbose_name=_('Parent key'),
 | 
				
			||||||
 | 
					                                       db_index=True)
 | 
				
			||||||
    node_from = models.CharField(choices=NodeFrom.choices, max_length=16, db_index=True)
 | 
					    node_from = models.CharField(choices=NodeFrom.choices, max_length=16, db_index=True)
 | 
				
			||||||
    node_assets_amount = models.IntegerField(default=0)
 | 
					    node_assets_amount = models.IntegerField(default=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -297,4 +401,3 @@ class PermedAsset(Asset):
 | 
				
			||||||
            ('view_userassets', _('Can view user assets')),
 | 
					            ('view_userassets', _('Can view user assets')),
 | 
				
			||||||
            ('view_usergroupassets', _('Can view usergroup assets')),
 | 
					            ('view_usergroupassets', _('Can view usergroup assets')),
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,20 +4,19 @@
 | 
				
			||||||
from rest_framework import serializers
 | 
					from rest_framework import serializers
 | 
				
			||||||
from django.utils.translation import ugettext_lazy as _
 | 
					from django.utils.translation import ugettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from assets.models import Node, Asset, Platform
 | 
					from assets.models import Node, Asset, Platform, Account
 | 
				
			||||||
from perms.serializers.permission import ActionsField
 | 
					from perms.serializers.permission import ActionsField
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = [
 | 
					__all__ = [
 | 
				
			||||||
    'NodeGrantedSerializer',
 | 
					    'NodeGrantedSerializer',
 | 
				
			||||||
    'AssetGrantedSerializer',
 | 
					    'AssetGrantedSerializer',
 | 
				
			||||||
    'ActionsSerializer',
 | 
					    'ActionsSerializer',
 | 
				
			||||||
 | 
					    'AccountsGrantedSerializer'
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AssetGrantedSerializer(serializers.ModelSerializer):
 | 
					class AssetGrantedSerializer(serializers.ModelSerializer):
 | 
				
			||||||
    """
 | 
					    """ 被授权资产的数据结构 """
 | 
				
			||||||
    被授权资产的数据结构
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    platform = serializers.SlugRelatedField(
 | 
					    platform = serializers.SlugRelatedField(
 | 
				
			||||||
        slug_field='name', queryset=Platform.objects.all(), label=_("Platform")
 | 
					        slug_field='name', queryset=Platform.objects.all(), label=_("Platform")
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
| 
						 | 
					@ -44,3 +43,14 @@ class NodeGrantedSerializer(serializers.ModelSerializer):
 | 
				
			||||||
class ActionsSerializer(serializers.Serializer):
 | 
					class ActionsSerializer(serializers.Serializer):
 | 
				
			||||||
    actions = ActionsField(read_only=True)
 | 
					    actions = ActionsField(read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AccountsGrantedSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					    """ 授权的账号序列类 """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Todo: 添加前端登录逻辑中需要的一些字段,比如:是否需要手动输入密码
 | 
				
			||||||
 | 
					    # need_manual = serializers.BooleanField(label=_('Need manual input'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Account
 | 
				
			||||||
 | 
					        fields = ['id', 'name', 'username']
 | 
				
			||||||
 | 
					        read_only_fields = fields
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ from rest_framework_bulk.routes import BulkRouter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .. import api
 | 
					from .. import api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# v3 Done
 | 
					 | 
				
			||||||
router = BulkRouter()
 | 
					router = BulkRouter()
 | 
				
			||||||
router.register('asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
 | 
					router.register('asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
 | 
				
			||||||
router.register('asset-permissions-users-relations', api.AssetPermissionUserRelationViewSet, 'asset-permissions-users-relation')
 | 
					router.register('asset-permissions-users-relations', api.AssetPermissionUserRelationViewSet, 'asset-permissions-users-relation')
 | 
				
			||||||
| 
						 | 
					@ -14,42 +13,31 @@ router.register('asset-permissions-assets-relations', api.AssetPermissionAssetRe
 | 
				
			||||||
router.register('asset-permissions-nodes-relations', api.AssetPermissionNodeRelationViewSet, 'asset-permissions-nodes-relation')
 | 
					router.register('asset-permissions-nodes-relations', api.AssetPermissionNodeRelationViewSet, 'asset-permissions-nodes-relation')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
user_permission_urlpatterns = [
 | 
					user_permission_urlpatterns = [
 | 
				
			||||||
    # 统一说明:
 | 
					 | 
				
			||||||
    # `<uuid:pk>`: `User.pk`
 | 
					 | 
				
			||||||
    # 直接授权:在 `AssetPermission` 中关联的对象
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # ---------------------------------------------------------
 | 
					 | 
				
			||||||
    # 以 serializer 格式返回
 | 
					    # 以 serializer 格式返回
 | 
				
			||||||
    path('<uuid:pk>/assets/', api.UserAllGrantedAssetsApi.as_view(), name='user-assets'),
 | 
					    path('<uuid:pk>/assets/', api.UserAllGrantedAssetsApi.as_view(), name='user-assets'),
 | 
				
			||||||
    path('assets/', api.MyAllGrantedAssetsApi.as_view(), name='my-assets'),
 | 
					    path('assets/', api.MyAllGrantedAssetsApi.as_view(), name='my-assets'),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Tree Node 的数据格式返回
 | 
					    # Tree Node 的数据格式返回
 | 
				
			||||||
    path('<uuid:pk>/assets/tree/', api.UserDirectGrantedAssetsAsTreeApi.as_view(), name='user-assets-as-tree'),
 | 
					    path('<uuid:pk>/assets/tree/', api.UserDirectGrantedAssetsAsTreeApi.as_view(), name='user-assets-as-tree'),
 | 
				
			||||||
    path('assets/tree/', api.MyAllAssetsAsTreeApi.as_view(), name='my-assets-as-tree'),
 | 
					    path('assets/tree/', api.MyAllAssetsAsTreeApi.as_view(), name='my-assets-as-tree'),
 | 
				
			||||||
    path('ungroup/assets/tree/', api.MyUngroupAssetsAsTreeApi.as_view(), name='my-ungroup-assets-as-tree'),
 | 
					    path('ungroup/assets/tree/', api.MyUngroupAssetsAsTreeApi.as_view(), name='my-ungroup-assets-as-tree'),
 | 
				
			||||||
    # ^--------------------------------------------------------^
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # 获取用户所有`直接授权的节点`与`直接授权资产`关联的节点
 | 
					    # 获取用户所有`直接授权的节点`与`直接授权资产`关联的节点
 | 
				
			||||||
    # 以 serializer 格式返回
 | 
					    # 以 serializer 格式返回
 | 
				
			||||||
    path('<uuid:pk>/nodes/', api.UserGrantedNodesForAdminApi.as_view(), name='user-nodes'),
 | 
					    path('<uuid:pk>/nodes/', api.UserGrantedNodesApi.as_view(), name='user-nodes'),
 | 
				
			||||||
    path('nodes/', api.MyGrantedNodesApi.as_view(), name='my-nodes'),
 | 
					    path('nodes/', api.MyGrantedNodesApi.as_view(), name='my-nodes'),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 以 Tree Node 的数据格式返回
 | 
					    # 以 Tree Node 的数据格式返回
 | 
				
			||||||
    path('<uuid:pk>/nodes/tree/', api.MyGrantedNodesAsTreeApi.as_view(), name='user-nodes-as-tree'),
 | 
					    path('<uuid:pk>/nodes/tree/', api.MyGrantedNodesAsTreeApi.as_view(), name='user-nodes-as-tree'),
 | 
				
			||||||
    path('nodes/tree/', api.MyGrantedNodesAsTreeApi.as_view(), name='my-nodes-as-tree'),
 | 
					    path('nodes/tree/', api.MyGrantedNodesAsTreeApi.as_view(), name='my-nodes-as-tree'),
 | 
				
			||||||
    # ^--------------------------------------------------------^
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # 一层一层的获取用户授权的节点,
 | 
					    # 一层一层的获取用户授权的节点,
 | 
				
			||||||
    # 以 Serializer 的数据格式返回
 | 
					    # 以 Serializer 的数据格式返回
 | 
				
			||||||
    path('<uuid:pk>/nodes/children/', api.UserGrantedNodeChildrenForAdminApi.as_view(), name='user-nodes-children'),
 | 
					    path('<uuid:pk>/nodes/children/', api.UserGrantedNodeChildrenForAdminApi.as_view(), name='user-nodes-children'),
 | 
				
			||||||
    path('nodes/children/', api.MyGrantedNodeChildrenApi.as_view(), name='my-nodes-children'),
 | 
					    path('nodes/children/', api.MyGrantedNodeChildrenApi.as_view(), name='my-nodes-children'),
 | 
				
			||||||
 | 
					 | 
				
			||||||
    # 以 Tree Node 的数据格式返回
 | 
					    # 以 Tree Node 的数据格式返回
 | 
				
			||||||
    path('<uuid:pk>/nodes/children/tree/', api.UserGrantedNodeChildrenAsTreeForAdminApi.as_view(), name='user-nodes-children-as-tree'),
 | 
					    path('<uuid:pk>/nodes/children/tree/', api.UserGrantedNodeChildrenAsTreeForAdminApi.as_view(), name='user-nodes-children-as-tree'),
 | 
				
			||||||
    # 部分调用位置
 | 
					    # 部分调用位置
 | 
				
			||||||
    # - 普通用户 -> 我的资产 -> 展开节点 时调用
 | 
					    # - 普通用户 -> 我的资产 -> 展开节点 时调用
 | 
				
			||||||
    path('nodes/children/tree/', api.MyGrantedNodeChildrenAsTreeApi.as_view(), name='my-nodes-children-as-tree'),
 | 
					    path('nodes/children/tree/', api.MyGrantedNodeChildrenAsTreeApi.as_view(), name='my-nodes-children-as-tree'),
 | 
				
			||||||
    # ^--------------------------------------------------------^
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # 此接口会返回整棵树
 | 
					    # 此接口会返回整棵树
 | 
				
			||||||
    # 普通用户 -> 命令执行 -> 左侧树
 | 
					    # 普通用户 -> 命令执行 -> 左侧树
 | 
				
			||||||
| 
						 | 
					@ -75,6 +63,11 @@ user_permission_urlpatterns = [
 | 
				
			||||||
    # 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: 增加
 | 
				
			||||||
 | 
					    # 获取所有和资产相关联的账号列表
 | 
				
			||||||
 | 
					    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')
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
user_group_permission_urlpatterns = [
 | 
					user_group_permission_urlpatterns = [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -918,6 +918,21 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_groups(self, flat=False):
 | 
				
			||||||
 | 
					        from users.models import UserGroup
 | 
				
			||||||
 | 
					        usergroup_ids = self.groups.through.objects\
 | 
				
			||||||
 | 
					            .filter(user_id=self.id)\
 | 
				
			||||||
 | 
					            .distinct()\
 | 
				
			||||||
 | 
					            .values_list('usergroup_id', flat=True)
 | 
				
			||||||
 | 
					        usergroups = UserGroup.objects.filter(id__in=usergroup_ids)
 | 
				
			||||||
 | 
					        if flat:
 | 
				
			||||||
 | 
					            usergroup_ids = usergroups.values_list('id', flat=True)
 | 
				
			||||||
 | 
					            return usergroup_ids
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return usergroups
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserPasswordHistory(models.Model):
 | 
					class UserPasswordHistory(models.Model):
 | 
				
			||||||
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
 | 
					    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,38 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# >>> Django 环境配置
 | 
				
			||||||
 | 
					import django
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if os.path.exists('../apps'):
 | 
				
			||||||
 | 
					    sys.path.insert(0, '../apps')
 | 
				
			||||||
 | 
					elif os.path.exists('./apps'):
 | 
				
			||||||
 | 
					    sys.path.insert(0, './apps')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 | 
				
			||||||
 | 
					APPS_DIR = os.path.join(BASE_DIR, 'apps')
 | 
				
			||||||
 | 
					sys.path.insert(0, APPS_DIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					os.environ.setdefault('PYTHONOPTIMIZE', '1')
 | 
				
			||||||
 | 
					if os.getuid() == 0:
 | 
				
			||||||
 | 
					    os.environ.setdefault('C_FORCE_ROOT', '1')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
 | 
				
			||||||
 | 
					django.setup()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# <<<
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Generator(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate(self):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def generate_assets(self):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
		Loading…
	
		Reference in New Issue