diff --git a/apps/assets/api/category.py b/apps/assets/api/category.py index b6ec6790b..b7317e231 100644 --- a/apps/assets/api/category.py +++ b/apps/assets/api/category.py @@ -2,8 +2,8 @@ from rest_framework.decorators import action from rest_framework.mixins import ListModelMixin from rest_framework.response import Response -from assets.const import AllTypes -from assets.serializers import CategorySerializer, TypeSerializer +from assets.const import AllTypes, Protocol +from assets.serializers import CategorySerializer, TypeSerializer, ProtocolSerializer from common.api import JMSGenericViewSet from common.permissions import IsValidUser @@ -13,7 +13,8 @@ __all__ = ['CategoryViewSet'] class CategoryViewSet(ListModelMixin, JMSGenericViewSet): serializer_classes = { 'default': CategorySerializer, - 'types': TypeSerializer + 'types': TypeSerializer, + 'protocols': ProtocolSerializer, } permission_classes = (IsValidUser,) @@ -32,3 +33,8 @@ class CategoryViewSet(ListModelMixin, JMSGenericViewSet): tp = request.query_params.get('type') constraints = AllTypes.get_constraints(category, tp) return Response(constraints) + + @action(methods=['get'], detail=False) + def protocols(self, request, *args, **kwargs): + protocols = list(Protocol.protocols()) + return self.get_paginated_response_from_queryset(protocols) diff --git a/apps/assets/const/protocol.py b/apps/assets/const/protocol.py index 5aea2daec..f61641da9 100644 --- a/apps/assets/const/protocol.py +++ b/apps/assets/const/protocol.py @@ -294,6 +294,10 @@ class Protocol(ChoicesMixin, models.TextChoices): **cls.gpt_protocols(), } + @classmethod + def protocols(cls): + return cls.settings().keys() + @classmethod @cached_method(ttl=600) def xpack_protocols(cls): diff --git a/apps/assets/serializers/cagegory.py b/apps/assets/serializers/cagegory.py index 7f8b61571..dc46f4964 100644 --- a/apps/assets/serializers/cagegory.py +++ b/apps/assets/serializers/cagegory.py @@ -1,5 +1,9 @@ -from rest_framework import serializers from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +__all__ = [ + 'TypeSerializer', 'CategorySerializer', 'ProtocolSerializer' +] class TypeSerializer(serializers.Serializer): @@ -13,3 +17,9 @@ class CategorySerializer(serializers.Serializer): label = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('Label')) value = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('Value')) types = TypeSerializer(many=True, required=False, label=_('Types'), read_only=True) + + +class ProtocolSerializer(serializers.Serializer): + label = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('Label')) + value = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('Value')) + category = serializers.CharField(max_length=64, required=False, allow_blank=True, label=_('Category')) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index e41b47f76..8739e32c1 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -351,8 +351,9 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView self._insert_connect_options(data, user) asset = data.get('asset') account_name = data.get('account') + protocol = data.get('protocol') self.input_username = self.get_input_username(data) - _data = self._validate(user, asset, account_name) + _data = self._validate(user, asset, account_name, protocol) data.update(_data) return serializer @@ -360,12 +361,12 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView user = token.user asset = token.asset account_name = token.account - _data = self._validate(user, asset, account_name) + _data = self._validate(user, asset, account_name, token.protocol) for k, v in _data.items(): setattr(token, k, v) return token - def _validate(self, user, asset, account_name): + def _validate(self, user, asset, account_name, protocol): data = dict() data['org_id'] = asset.org_id data['user'] = user @@ -374,7 +375,7 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView if account_name == AliasAccount.ANON and asset.category not in ['web', 'custom']: raise ValidationError(_('Anonymous account is not supported for this asset')) - account = self._validate_perm(user, asset, account_name) + account = self._validate_perm(user, asset, account_name, protocol) if account.has_secret: data['input_secret'] = '' @@ -387,9 +388,9 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView return data @staticmethod - def _validate_perm(user, asset, account_name): - from perms.utils.account import PermAccountUtil - account = PermAccountUtil().validate_permission(user, asset, account_name) + def _validate_perm(user, asset, account_name, protocol): + from perms.utils.asset_perm import PermAssetDetailUtil + account = PermAssetDetailUtil(user, asset).validate_permission(account_name, protocol) if not account or not account.actions: msg = _('Account not found') raise JMSException(code='perm_account_invalid', detail=msg) diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index 6ea5474c4..48c8e8e16 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -97,10 +97,9 @@ class ConnectionToken(JMSOrgBaseModel): @lazyproperty def permed_account(self): - from perms.utils import PermAccountUtil - permed_account = PermAccountUtil().validate_permission( - self.user, self.asset, self.account - ) + from perms.utils import PermAssetDetailUtil + permed_account = PermAssetDetailUtil(self.user, self.asset) \ + .validate_permission(self.account, self.protocol) return permed_account @lazyproperty @@ -115,6 +114,7 @@ class ConnectionToken(JMSOrgBaseModel): if not self.is_active: error = _('Connection token inactive') raise PermissionDenied(error) + if self.is_expired: error = _('Connection token expired at: {}').format(as_current_tz(self.date_expired)) raise PermissionDenied(error) diff --git a/apps/authentication/permissions.py b/apps/authentication/permissions.py index e49f72796..1bd87b4ea 100644 --- a/apps/authentication/permissions.py +++ b/apps/authentication/permissions.py @@ -55,4 +55,4 @@ class IsValidUserOrConnectionToken(IsValidUser): return False with tmp_to_root_org(): token = get_object_or_none(ConnectionToken, id=token_id) - return token and token.is_valid + return token and token.is_valid() diff --git a/apps/perms/api/user_permission/accounts.py b/apps/perms/api/user_permission/accounts.py index 40789829d..88e0eb27e 100644 --- a/apps/perms/api/user_permission/accounts.py +++ b/apps/perms/api/user_permission/accounts.py @@ -4,9 +4,10 @@ from rest_framework.generics import ListAPIView, get_object_or_404 from common.utils import get_logger, lazyproperty from perms import serializers from perms.hands import Asset -from perms.utils import PermAccountUtil +from perms.utils import PermAssetDetailUtil from .mixin import SelfOrPKUserMixin from ...models import AssetPermission + logger = get_logger(__name__) __all__ = [ @@ -26,5 +27,5 @@ class UserPermedAssetAccountsApi(SelfOrPKUserMixin, ListAPIView): return asset def get_queryset(self): - accounts = PermAccountUtil().get_permed_accounts_for_user(self.user, self.asset) + accounts = PermAssetDetailUtil(self.user, self.asset).get_permed_accounts_for_user() return accounts diff --git a/apps/perms/api/user_permission/assets.py b/apps/perms/api/user_permission/assets.py index 1ec27803f..bc51dfe77 100644 --- a/apps/perms/api/user_permission/assets.py +++ b/apps/perms/api/user_permission/assets.py @@ -1,14 +1,15 @@ import abc -from rest_framework.generics import ListAPIView +from rest_framework.generics import ListAPIView, RetrieveAPIView from assets.api.asset.asset import AssetFilterSet from assets.models import Asset, Node from common.utils import get_logger, lazyproperty, is_uuid +from orgs.utils import tmp_to_root_org from perms import serializers from perms.pagination import AllPermedAssetPagination from perms.pagination import NodePermedAssetPagination -from perms.utils import UserPermAssetUtil +from perms.utils import UserPermAssetUtil, PermAssetDetailUtil from .mixin import ( SelfOrPKUserMixin ) @@ -18,11 +19,25 @@ __all__ = [ 'UserDirectPermedAssetsApi', 'UserFavoriteAssetsApi', 'UserPermedNodeAssetsApi', + 'UserPermedAssetRetrieveApi', ] logger = get_logger(__name__) +class UserPermedAssetRetrieveApi(SelfOrPKUserMixin, RetrieveAPIView): + serializer_class = serializers.AssetPermedDetailSerializer + + def get_object(self): + with tmp_to_root_org(): + asset_id = self.kwargs.get('pk') + util = PermAssetDetailUtil(self.user, asset_id) + asset = util.asset + asset.permed_accounts = util.get_permed_accounts_for_user() + asset.permed_protocols = util.get_permed_protocols_for_user() + return asset + + class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ListAPIView): ordering = ('name',) search_fields = ('name', 'address', 'comment') @@ -30,12 +45,6 @@ class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ListAPIView): filterset_class = AssetFilterSet serializer_class = serializers.AssetPermedSerializer - def get_serializer_class(self): - serializer_class = super().get_serializer_class() - if self.request.query_params.get('id'): - serializer_class = serializers.AssetPermedDetailSerializer - return serializer_class - def get_queryset(self): if getattr(self, 'swagger_fake_view', False): return Asset.objects.none() diff --git a/apps/perms/api/user_permission/tree/node_with_asset.py b/apps/perms/api/user_permission/tree/node_with_asset.py index 1b4ee5d73..0289b9d63 100644 --- a/apps/perms/api/user_permission/tree/node_with_asset.py +++ b/apps/perms/api/user_permission/tree/node_with_asset.py @@ -21,7 +21,7 @@ from common.utils import get_object_or_none, lazyproperty from common.utils.common import timeit from perms.hands import Node from perms.models import PermNode -from perms.utils import PermAccountUtil, UserPermNodeUtil +from perms.utils import PermAssetDetailUtil, UserPermNodeUtil from perms.utils import UserPermAssetUtil from .mixin import RebuildTreeMixin from ..mixin import SelfOrPKUserMixin @@ -225,8 +225,8 @@ class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView): return token def get_account_secret(self, token: ConnectionToken): - util = PermAccountUtil() - accounts = util.get_permed_accounts_for_user(self.user, token.asset) + util = PermAssetDetailUtil(self.user, token.asset) + accounts = util.get_permed_accounts_for_user() account_name = token.account if account_name in [AliasAccount.INPUT, AliasAccount.USER]: diff --git a/apps/perms/migrations/0035_auto_20231125_1025.py b/apps/perms/migrations/0035_auto_20231125_1025.py new file mode 100644 index 000000000..cd3f433ba --- /dev/null +++ b/apps/perms/migrations/0035_auto_20231125_1025.py @@ -0,0 +1,20 @@ +# Generated by Django 4.1.10 on 2023-10-25 02:45 + +from django.db import migrations, models + +import perms.models.asset_permission + + +class Migration(migrations.Migration): + + dependencies = [ + ('perms', '0034_auto_20230525_1734'), + ] + + operations = [ + migrations.AddField( + model_name='assetpermission', + name='protocols', + field=models.JSONField(default=perms.models.asset_permission.default_protocols, verbose_name='Protocols'), + ), + ] diff --git a/apps/perms/models/asset_permission.py b/apps/perms/models/asset_permission.py index 2d5fa6ea5..cee80f562 100644 --- a/apps/perms/models/asset_permission.py +++ b/apps/perms/models/asset_permission.py @@ -52,6 +52,10 @@ class AssetPermissionManager(OrgManager): return self.get_queryset().filter(Q(date_start__lte=now) | Q(date_expired__gte=now)) +def default_protocols(): + return ['all'] + + class AssetPermission(JMSOrgBaseModel): name = models.CharField(max_length=128, verbose_name=_('Name')) users = models.ManyToManyField( @@ -68,6 +72,7 @@ class AssetPermission(JMSOrgBaseModel): ) # 特殊的账号: @ALL, @INPUT @USER 默认包含,将来在全局设置中进行控制. accounts = models.JSONField(default=list, verbose_name=_("Account")) + protocols = models.JSONField(default=default_protocols, verbose_name=_("Protocols")) actions = models.IntegerField(default=ActionChoices.connect, verbose_name=_("Actions")) date_start = models.DateTimeField(default=timezone.now, db_index=True, verbose_name=_("Date start")) date_expired = models.DateTimeField( diff --git a/apps/perms/serializers/permission.py b/apps/perms/serializers/permission.py index 976fc1f31..26afba67c 100644 --- a/apps/perms/serializers/permission.py +++ b/apps/perms/serializers/permission.py @@ -37,6 +37,7 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer): is_valid = serializers.BooleanField(read_only=True, label=_("Is valid")) is_expired = serializers.BooleanField(read_only=True, label=_("Is expired")) accounts = serializers.ListField(label=_("Account"), required=False) + protocols = serializers.ListField(label=_("Protocols"), required=False) template_accounts = AccountTemplate.objects.none() @@ -44,7 +45,7 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer): model = AssetPermission fields_mini = ["id", "name"] fields_generic = [ - "accounts", "actions", "created_by", "date_created", + "accounts", "protocols", "actions", "created_by", "date_created", "date_start", "date_expired", "is_active", "is_expired", "is_valid", "comment", "from_ticket", ] diff --git a/apps/perms/serializers/user_permission.py b/apps/perms/serializers/user_permission.py index ba69cdeaf..96853cff7 100644 --- a/apps/perms/serializers/user_permission.py +++ b/apps/perms/serializers/user_permission.py @@ -8,7 +8,7 @@ from rest_framework import serializers from accounts.models import Account from assets.const import Category, AllTypes from assets.models import Node, Asset, Platform -from assets.serializers.asset.common import AssetProtocolsPermsSerializer, AssetLabelSerializer +from assets.serializers.asset.common import AssetLabelSerializer, AssetProtocolsPermsSerializer from common.serializers.fields import ObjectRelatedField, LabeledChoiceField from orgs.mixins.serializers import OrgResourceModelSerializerMixin from perms.serializers.permission import ActionChoicesField @@ -22,7 +22,6 @@ __all__ = [ class AssetPermedSerializer(OrgResourceModelSerializerMixin): """ 被授权资产的数据结构 """ platform = ObjectRelatedField(required=False, queryset=Platform.objects, label=_('Platform')) - protocols = AssetProtocolsPermsSerializer(many=True, required=False, label=_('Protocols')) category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category')) type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type')) labels = AssetLabelSerializer(many=True, required=False, label=_('Label')) @@ -35,30 +34,25 @@ class AssetPermedSerializer(OrgResourceModelSerializerMixin): 'comment', 'org_id', 'is_active', 'date_verified', 'created_by', 'date_created', 'connectivity', 'nodes', 'labels' ] - fields = only_fields + ['protocols', 'category', 'type'] + ['org_name'] + fields = only_fields + ['category', 'type'] + ['org_name'] read_only_fields = fields @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related('domain', 'nodes', 'labels') \ - .prefetch_related('platform', 'protocols') \ + .prefetch_related('platform') \ .annotate(category=F("platform__category")) \ .annotate(type=F("platform__type")) return queryset -class AssetPermedDetailSerializer(AssetPermedSerializer): - class Meta(AssetPermedSerializer.Meta): - fields = AssetPermedSerializer.Meta.fields + ['spec_info'] - read_only_fields = fields - - class NodePermedSerializer(serializers.ModelSerializer): class Meta: model = Node fields = [ - 'id', 'name', 'key', 'value', 'org_id', "assets_amount" + 'id', 'name', 'key', 'value', + 'org_id', "assets_amount" ] read_only_fields = fields @@ -73,3 +67,12 @@ class AccountsPermedSerializer(serializers.ModelSerializer): 'has_secret', 'secret_type', 'actions' ] read_only_fields = fields + + +class AssetPermedDetailSerializer(AssetPermedSerializer): + permed_protocols = AssetProtocolsPermsSerializer(many=True, required=False, label=_('Protocols')) + permed_accounts = AccountsPermedSerializer(label=_("Accounts"), required=False, many=True) + + class Meta(AssetPermedSerializer.Meta): + fields = AssetPermedSerializer.Meta.fields + ['spec_info', 'permed_protocols', 'permed_accounts'] + read_only_fields = fields diff --git a/apps/perms/urls/user_permission.py b/apps/perms/urls/user_permission.py index 4f3d4c71d..0ba99087e 100644 --- a/apps/perms/urls/user_permission.py +++ b/apps/perms/urls/user_permission.py @@ -5,8 +5,11 @@ from .. import api user_permission_urlpatterns = [ # such as: my | self | user.id # assets + path('/assets//', api.UserPermedAssetRetrieveApi.as_view(), + name='user-permed-asset'), path('/assets/', api.UserAllPermedAssetsApi.as_view(), name='user-all-assets'), + path('/nodes/ungrouped/assets/', api.UserDirectPermedAssetsApi.as_view(), name='user-direct-assets'), path('/nodes/favorite/assets/', api.UserFavoriteAssetsApi.as_view(), @@ -47,9 +50,6 @@ user_permission_urlpatterns = [ path('/nodes/children-with-k8s/tree/', api.UserGrantedK8sAsTreeApi.as_view(), name='user-nodes-children-with-k8s-as-tree'), - # accounts - path('/assets//accounts/', api.UserPermedAssetAccountsApi.as_view(), - name='user-permed-asset-accounts'), ] user_group_permission_urlpatterns = [ diff --git a/apps/perms/utils/__init__.py b/apps/perms/utils/__init__.py index 808bdd1b8..9a9c0ce81 100644 --- a/apps/perms/utils/__init__.py +++ b/apps/perms/utils/__init__.py @@ -1,4 +1,4 @@ +from .asset_perm import * from .permission import * -from .account import * -from .user_perm_tree import * from .user_perm import * +from .user_perm_tree import * diff --git a/apps/perms/utils/account.py b/apps/perms/utils/account.py deleted file mode 100644 index d66338a2c..000000000 --- a/apps/perms/utils/account.py +++ /dev/null @@ -1,84 +0,0 @@ -from collections import defaultdict - -from accounts.const import AliasAccount -from accounts.models import VirtualAccount -from orgs.utils import tmp_to_org -from .permission import AssetPermissionUtil - -__all__ = ['PermAccountUtil'] - - -class PermAccountUtil(AssetPermissionUtil): - """ 资产授权账号相关的工具 """ - - def validate_permission(self, user, asset, account_name): - """ 校验用户有某个资产下某个账号名的权限 - :param user: User - :param asset: Asset - :param account_name: 可能是 @USER @INPUT 字符串 - """ - with tmp_to_org(asset.org): - permed_accounts = self.get_permed_accounts_for_user(user, asset) - accounts_mapper = {account.alias: account for account in permed_accounts} - account = accounts_mapper.get(account_name) - return account - - def get_permed_accounts_for_user(self, user, asset): - """ 获取授权给用户某个资产的账号 """ - perms = self.get_permissions_for_user_asset(user, asset) - permed_accounts = self.get_permed_accounts_from_perms(perms, user, asset) - return permed_accounts - - @staticmethod - def get_permed_accounts_from_perms(perms, user, asset): - # alias: is a collection of account usernames and special accounts [@ALL, @INPUT, @USER, @ANON] - alias_action_bit_mapper = defaultdict(int) - alias_date_expired_mapper = defaultdict(list) - - for perm in perms: - for alias in perm.accounts: - alias_action_bit_mapper[alias] |= perm.actions - alias_date_expired_mapper[alias].append(perm.date_expired) - - asset_accounts = asset.accounts.all().active() - username_accounts_mapper = defaultdict(list) - for account in asset_accounts: - username_accounts_mapper[account.username].append(account) - - cleaned_accounts_action_bit = defaultdict(int) - cleaned_accounts_expired = defaultdict(list) - - # @ALL 账号先处理,后面的每个最多映射一个账号 - all_action_bit = alias_action_bit_mapper.pop(AliasAccount.ALL, None) - if all_action_bit: - for account in asset_accounts: - cleaned_accounts_action_bit[account] |= all_action_bit - cleaned_accounts_expired[account].extend( - alias_date_expired_mapper[AliasAccount.ALL] - ) - - for alias, action_bit in alias_action_bit_mapper.items(): - account = None - _accounts = [] - if alias == AliasAccount.USER and user.username in username_accounts_mapper: - _accounts = username_accounts_mapper[user.username] - elif alias in username_accounts_mapper: - _accounts = username_accounts_mapper[alias] - elif alias in ['@INPUT', '@ANON', '@USER']: - account = VirtualAccount.get_special_account(alias, user, asset, from_permed=True) - elif alias.startswith('@'): - continue - - if account: - _accounts += [account] - - for account in _accounts: - cleaned_accounts_action_bit[account] |= action_bit - cleaned_accounts_expired[account].extend(alias_date_expired_mapper[alias]) - - accounts = [] - for account, action_bit in cleaned_accounts_action_bit.items(): - account.actions = action_bit - account.date_expired = max(cleaned_accounts_expired[account]) - accounts.append(account) - return accounts diff --git a/apps/perms/utils/asset_perm.py b/apps/perms/utils/asset_perm.py new file mode 100644 index 000000000..178a5ab21 --- /dev/null +++ b/apps/perms/utils/asset_perm.py @@ -0,0 +1,139 @@ +from collections import defaultdict + +from accounts.const import AliasAccount +from accounts.models import VirtualAccount +from assets.models import Asset +from common.utils import lazyproperty +from orgs.utils import tmp_to_org, tmp_to_root_org +from .permission import AssetPermissionUtil + +__all__ = ['PermAssetDetailUtil'] + + +class PermAssetDetailUtil: + """ 资产授权账号相关的工具 """ + + def __init__(self, user, asset_or_id): + self.user = user + + if isinstance(asset_or_id, Asset): + self.asset_id = asset_or_id.id + self.asset = asset_or_id + else: + self.asset_id = asset_or_id + + @lazyproperty + def asset(self): + if self.user_asset_perms: + return self._asset + raise Asset.DoesNotExist() + + @lazyproperty + def _asset(self): + from assets.models import Asset + with tmp_to_root_org(): + queryset = Asset.objects.filter(id=self.asset_id) + return queryset.get() + + def validate_permission(self, account_name, protocol): + with tmp_to_org(self.asset.org): + protocols = self.get_permed_protocols_for_user(only_name=True) + if 'all' not in protocols and protocol not in protocols: + return None + permed_accounts = self.get_permed_accounts_for_user() + accounts_mapper = {account.alias: account for account in permed_accounts} + account = accounts_mapper.get(account_name) + return account + + @lazyproperty + def user_asset_perms(self): + perm_util = AssetPermissionUtil() + perms = perm_util.get_permissions_for_user_asset(self.user, self.asset_id) + return perms + + def get_permed_accounts_for_user(self): + """ 获取授权给用户某个资产的账号 """ + perms = self.user_asset_perms + permed_accounts = self.get_permed_accounts_from_perms(perms, self.user, self.asset) + return permed_accounts + + def get_permed_protocols_for_user(self, only_name=False): + """ 获取授权给用户某个资产的账号 """ + perms = self.user_asset_perms + names = set() + for perm in perms: + names |= set(perm.protocols) + if only_name: + return names + protocols = self.asset.protocols.all() + if 'all' not in names: + protocols = protocols.filter(name__in=names) + return protocols + + @staticmethod + def parse_alias_action_date_expire(perms, asset): + alias_action_bit_mapper = defaultdict(int) + alias_date_expired_mapper = defaultdict(list) + + for perm in perms: + for alias in perm.accounts: + alias_action_bit_mapper[alias] |= perm.actions + alias_date_expired_mapper[alias].append(perm.date_expired) + + # @ALL 账号先处理,后面的每个最多映射一个账号 + all_action_bit = alias_action_bit_mapper.pop(AliasAccount.ALL, None) + if not all_action_bit: + return alias_action_bit_mapper, alias_date_expired_mapper + + asset_account_usernames = asset.accounts.all().active().values_list('username', flat=True) + for username in asset_account_usernames: + alias_action_bit_mapper[username] |= all_action_bit + alias_date_expired_mapper[username].extend( + alias_date_expired_mapper[AliasAccount.ALL] + ) + return alias_action_bit_mapper, alias_date_expired_mapper + + @classmethod + def map_alias_to_accounts(cls, alias_action_bit_mapper, alias_date_expired_mapper, asset, user): + username_accounts_mapper = defaultdict(list) + cleaned_accounts_expired = defaultdict(list) + asset_accounts = asset.accounts.all().active() + + # 用户名 -> 账号 + for account in asset_accounts: + username_accounts_mapper[account.username].append(account) + + cleaned_accounts_action_bit = defaultdict(int) + for alias, action_bit in alias_action_bit_mapper.items(): + account = None + _accounts = [] + if alias == AliasAccount.USER and user.username in username_accounts_mapper: + _accounts = username_accounts_mapper[user.username] + elif alias in username_accounts_mapper: + _accounts = username_accounts_mapper[alias] + elif alias in ['@INPUT', '@ANON', '@USER']: + account = VirtualAccount.get_special_account(alias, user, asset, from_permed=True) + elif alias.startswith('@'): + continue + + if account: + _accounts += [account] + + for account in _accounts: + cleaned_accounts_action_bit[account] |= action_bit + cleaned_accounts_expired[account].extend(alias_date_expired_mapper[alias]) + return cleaned_accounts_action_bit, cleaned_accounts_expired + + @classmethod + def get_permed_accounts_from_perms(cls, perms, user, asset): + # alias: is a collection of account usernames and special accounts [@ALL, @INPUT, @USER, @ANON] + alias_action_bit_mapper, alias_date_expired_mapper = cls.parse_alias_action_date_expire(perms, asset) + cleaned_accounts_action_bit, cleaned_accounts_expired = cls.map_alias_to_accounts( + alias_action_bit_mapper, alias_date_expired_mapper, asset, user + ) + accounts = [] + for account, action_bit in cleaned_accounts_action_bit.items(): + account.actions = action_bit + account.date_expired = max(cleaned_accounts_expired[account]) + accounts.append(account) + return accounts diff --git a/apps/perms/utils/user_perm.py b/apps/perms/utils/user_perm.py index 7c78c9149..42d6cc462 100644 --- a/apps/perms/utils/user_perm.py +++ b/apps/perms/utils/user_perm.py @@ -3,7 +3,7 @@ from django.db.models import Q from assets.models import FavoriteAsset, Asset from common.utils.common import timeit -from perms.models import AssetPermission, PermNode, UserAssetGrantedTreeNodeRelation +from perms.models import PermNode, UserAssetGrantedTreeNodeRelation from .permission import AssetPermissionUtil __all__ = ['AssetPermissionPermAssetUtil', 'UserPermAssetUtil', 'UserPermNodeUtil'] @@ -218,4 +218,3 @@ class UserPermNodeUtil: nodes.extend(list(key_node_mapper.values())) return nodes -