diff --git a/apps/assets/api/asset/__init__.py b/apps/assets/api/asset/__init__.py index a2d88408d..f9c9ae5e6 100644 --- a/apps/assets/api/asset/__init__.py +++ b/apps/assets/api/asset/__init__.py @@ -1,4 +1,4 @@ -from .common import * +from .asset import * from .host import * from .database import * from .permission import * diff --git a/apps/assets/api/asset/common.py b/apps/assets/api/asset/asset.py similarity index 89% rename from apps/assets/api/asset/common.py rename to apps/assets/api/asset/asset.py index 756ce09ee..18325cb58 100644 --- a/apps/assets/api/asset/common.py +++ b/apps/assets/api/asset/asset.py @@ -2,18 +2,20 @@ # from rest_framework.decorators import action from rest_framework.response import Response +import django_filters +from common.drf.filters import BaseFilterSet from common.utils import get_logger, get_object_or_none from common.mixins.api import SuggestionMixin from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins import generics -from assets.api import FilterAssetByNodeMixin from assets.models import Asset, Node, Gateway from assets import serializers from assets.tasks import ( update_assets_hardware_info_manual, test_assets_connectivity_manual, ) -from assets.filters import FilterAssetByNodeFilterBackend, LabelFilterBackend, IpInFilterBackend +from assets.filters import NodeFilterBackend, LabelFilterBackend, IpInFilterBackend +from ..mixin import NodeFilterMixin logger = get_logger(__file__) __all__ = [ @@ -21,17 +23,21 @@ __all__ = [ ] -class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet): +class AssetFilterSet(BaseFilterSet): + type = django_filters.CharFilter(field_name='platform__type', lookup_expr='exact') + category = django_filters.CharFilter(field_name='platform__category', lookup_expr='exact') + + class Meta: + model = Asset + fields = ['name', 'ip', 'is_active', 'type', 'category'] + + +class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): """ API endpoint that allows Asset to be viewed or edited. """ model = Asset - filterset_fields = { - 'name': ['exact'], - 'ip': ['exact'], - 'is_active': ['exact'], - 'protocols': ['exact', 'icontains'] - } + filterset_class = AssetFilterSet search_fields = ("name", "ip") ordering_fields = ("name", "ip", "port") ordering = ('name', ) @@ -47,9 +53,9 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet) ('gateways', 'assets.view_gateway') ) extra_filter_backends = [ - FilterAssetByNodeFilterBackend, LabelFilterBackend, IpInFilterBackend, + NodeFilterBackend ] def set_assets_node(self, assets): diff --git a/apps/assets/api/asset/database.py b/apps/assets/api/asset/database.py index 786ff06a3..820812d18 100644 --- a/apps/assets/api/asset/database.py +++ b/apps/assets/api/asset/database.py @@ -1,7 +1,7 @@ from assets.models import Database from assets.serializers import DatabaseSerializer -from .common import AssetViewSet +from .asset import AssetViewSet __all__ = ['DatabaseViewSet'] diff --git a/apps/assets/api/asset/host.py b/apps/assets/api/asset/host.py index a27d731f2..3094e15a8 100644 --- a/apps/assets/api/asset/host.py +++ b/apps/assets/api/asset/host.py @@ -1,7 +1,7 @@ from assets.models import Host from assets.serializers import HostSerializer -from .common import AssetViewSet +from .asset import AssetViewSet __all__ = ['HostViewSet'] diff --git a/apps/assets/api/mixin.py b/apps/assets/api/mixin.py index d814ce11f..d7d33e17b 100644 --- a/apps/assets/api/mixin.py +++ b/apps/assets/api/mixin.py @@ -1,10 +1,10 @@ from typing import List +from rest_framework.request import Request -from common.utils.common import timeit +from common.utils import lazyproperty, timeit from assets.models import Node, Asset from assets.pagination import NodeAssetTreePagination -from common.utils import lazyproperty -from assets.utils import get_node, is_query_node_all_assets +from assets.utils import get_node_from_request, is_query_node_all_assets class SerializeToTreeNodeMixin: @@ -80,8 +80,9 @@ class SerializeToTreeNodeMixin: return data -class FilterAssetByNodeMixin: +class NodeFilterMixin: pagination_class = NodeAssetTreePagination + request: Request @lazyproperty def is_query_node_all_assets(self): @@ -89,4 +90,4 @@ class FilterAssetByNodeMixin: @lazyproperty def node(self): - return get_node(self.request) + return get_node_from_request(self.request) diff --git a/apps/assets/filters.py b/apps/assets/filters.py index 346c507b1..dd8bf80a4 100644 --- a/apps/assets/filters.py +++ b/apps/assets/filters.py @@ -6,7 +6,7 @@ from rest_framework import filters from django.db.models import Q from .models import Label -from assets.utils import is_query_node_all_assets, get_node +from assets.utils import is_query_node_all_assets, get_node_from_request class AssetByNodeFilterBackend(filters.BaseFilterBackend): @@ -31,7 +31,7 @@ class AssetByNodeFilterBackend(filters.BaseFilterBackend): return queryset.filter(nodes__key=node.key).distinct() def filter_queryset(self, request, queryset, view): - node = get_node(request) + node = get_node_from_request(request) if node is None: return queryset @@ -42,9 +42,9 @@ class AssetByNodeFilterBackend(filters.BaseFilterBackend): return self.filter_node_related_direct(queryset, node) -class FilterAssetByNodeFilterBackend(filters.BaseFilterBackend): +class NodeFilterBackend(filters.BaseFilterBackend): """ - 需要与 `assets.api.mixin.FilterAssetByNodeMixin` 配合使用 + 需要与 `assets.api.mixin.NodeFilterMixin` 配合使用 """ fields = ['node', 'all'] @@ -58,10 +58,11 @@ class FilterAssetByNodeFilterBackend(filters.BaseFilterBackend): ] def filter_queryset(self, request, queryset, view): - node = view.node + node = get_node_from_request(request) if node is None: return queryset - query_all = view.is_query_node_all_assets + + query_all = is_query_node_all_assets(request) if query_all: return queryset.filter( Q(nodes__key__istartswith=f'{node.key}:') | @@ -94,6 +95,9 @@ class LabelFilterBackend(filters.BaseFilterBackend): for kv in labels_query: if '#' in kv: self.sep = '#' + break + + for kv in labels_query: if self.sep not in kv: continue key, value = kv.strip().split(self.sep)[:2] diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index dd8d07dd1..9c147e966 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -9,6 +9,7 @@ from functools import reduce from django.db import models from django.utils.translation import ugettext_lazy as _ +from common.utils import lazyproperty from orgs.mixins.models import OrgManager, JMSOrgBaseModel from ..platform import Platform from ..base import AbsConnectivity @@ -110,11 +111,11 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel): names.append(n.name + ':' + n.value) return names - @property + @lazyproperty def type(self): return self.platform.type - @property + @lazyproperty def category(self): return self.platform.category diff --git a/apps/assets/pagination.py b/apps/assets/pagination.py index f913e9eed..8ae42ef16 100644 --- a/apps/assets/pagination.py +++ b/apps/assets/pagination.py @@ -8,6 +8,9 @@ logger = get_logger(__name__) class AssetPaginationBase(LimitOffsetPagination): + _request = None + _view = None + _user = None def init_attrs(self, queryset, request: Request, view=None): self._request = request @@ -28,7 +31,8 @@ class AssetPaginationBase(LimitOffsetPagination): } for k, v in self._request.query_params.items(): if k not in exclude_query_params and v is not None: - logger.warn(f'Not hit node.assets_amount because find a unknow query_param `{k}` -> {self._request.get_full_path()}') + logger.warn(f'Not hit node.assets_amount because find a unknown query_param ' + f'`{k}` -> {self._request.get_full_path()}') return super().get_count(queryset) node_assets_count = self.get_count_from_nodes(queryset) if node_assets_count is None: @@ -49,4 +53,4 @@ class NodeAssetTreePagination(AssetPaginationBase): node = Node.org_root() if node: logger.debug(f'Hit node assets_amount cache: [{node.assets_amount}]') - return node.assets_amount + return node.assets_amount diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 284ec2e0d..778137e23 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -2,6 +2,7 @@ # from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ +from django.db.models import F from common.drf.serializers import JMSWritableNestedModelSerializer from common.drf.fields import ChoiceDisplayField @@ -107,7 +108,9 @@ class AssetSerializer(JMSWritableNestedModelSerializer): @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ - queryset = queryset.prefetch_related('domain', 'platform', 'protocols') + queryset = queryset.prefetch_related('domain', 'platform', 'protocols')\ + .annotate(category=F("platform__category"))\ + .annotate(type=F("platform__type")) queryset = queryset.prefetch_related('nodes', 'labels') return queryset diff --git a/apps/assets/utils.py b/apps/assets/utils.py index c9857f802..478b2187d 100644 --- a/apps/assets/utils.py +++ b/apps/assets/utils.py @@ -53,7 +53,7 @@ def is_query_node_all_assets(request): return is_true(query_all_arg) -def get_node(request): +def get_node_from_request(request): node_id = dict_get_any(request.query_params, ['node', 'node_id']) if not node_id: return None diff --git a/apps/perms/api/__init__.py b/apps/perms/api/__init__.py index e5e3698dd..67986edc6 100644 --- a/apps/perms/api/__init__.py +++ b/apps/perms/api/__init__.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- # -from .asset import * +from .user_permission import * +from .asset_permission import * +from .asset_permission_relation import * +from .user_group_permission import * diff --git a/apps/perms/api/asset/__init__.py b/apps/perms/api/asset/__init__.py deleted file mode 100644 index 49c998e95..000000000 --- a/apps/perms/api/asset/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .user_permission import * -from .asset_permission import * -from .asset_permission_relation import * -from .user_group_permission import * diff --git a/apps/perms/api/asset/user_permission/__init__.py b/apps/perms/api/asset/user_permission/__init__.py deleted file mode 100644 index 590235cc6..000000000 --- a/apps/perms/api/asset/user_permission/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -# -from .common import * -from .user_permission_nodes import * -from .user_permission_assets import * -from .user_permission_nodes_with_assets import * diff --git a/apps/perms/api/asset/user_permission/user_permission_assets/__init__.py b/apps/perms/api/asset/user_permission/user_permission_assets/__init__.py deleted file mode 100644 index 6b274abdd..000000000 --- a/apps/perms/api/asset/user_permission/user_permission_assets/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .views import * diff --git a/apps/perms/api/asset/asset_permission.py b/apps/perms/api/asset_permission.py similarity index 100% rename from apps/perms/api/asset/asset_permission.py rename to apps/perms/api/asset_permission.py diff --git a/apps/perms/api/asset/asset_permission_relation.py b/apps/perms/api/asset_permission_relation.py similarity index 100% rename from apps/perms/api/asset/asset_permission_relation.py rename to apps/perms/api/asset_permission_relation.py diff --git a/apps/perms/api/base.py b/apps/perms/api/base.py deleted file mode 100644 index 0e285796e..000000000 --- a/apps/perms/api/base.py +++ /dev/null @@ -1,95 +0,0 @@ -from django.db.models import Q -from common.utils import get_object_or_none -from orgs.mixins.api import OrgBulkModelViewSet -from assets.models import SystemUser -from users.models import User, UserGroup - - -__all__ = ['BasePermissionViewSet'] - - -class BasePermissionViewSet(OrgBulkModelViewSet): - custom_filter_fields = [ - 'user_id', 'username', 'system_user_id', 'system_user', - 'user_group_id', 'user_group' - ] - - def filter_valid(self, queryset): - valid_query = self.request.query_params.get('is_valid', None) - if valid_query is None: - return queryset - invalid = valid_query in ['0', 'N', 'false', 'False'] - if invalid: - queryset = queryset.invalid() - else: - queryset = queryset.valid() - return queryset - - def is_query_all(self): - query_all = self.request.query_params.get('all', '1') == '1' - return query_all - - def filter_user(self, queryset): - user_id = self.request.query_params.get('user_id') - username = self.request.query_params.get('username') - if user_id: - user = get_object_or_none(User, pk=user_id) - elif username: - user = get_object_or_none(User, username=username) - else: - return queryset - if not user: - return queryset.none() - if not self.is_query_all(): - queryset = queryset.filter(users=user) - return queryset - groups = list(user.groups.all().values_list('id', flat=True)) - queryset = queryset.filter( - Q(users=user) | Q(user_groups__in=groups) - ).distinct() - return queryset - - def filter_keyword(self, queryset): - keyword = self.request.query_params.get('search') - if not keyword: - return queryset - queryset = queryset.filter(name__icontains=keyword) - return queryset - - def filter_system_user(self, queryset): - system_user_id = self.request.query_params.get('system_user_id') - system_user_name = self.request.query_params.get('system_user') - if system_user_id: - system_user = get_object_or_none(SystemUser, pk=system_user_id) - elif system_user_name: - system_user = get_object_or_none(SystemUser, name=system_user_name) - else: - return queryset - if not system_user: - return queryset.none() - queryset = queryset.filter(system_users=system_user) - return queryset - - def filter_user_group(self, queryset): - user_group_id = self.request.query_params.get('user_group_id') - user_group_name = self.request.query_params.get('user_group') - if user_group_id: - group = get_object_or_none(UserGroup, pk=user_group_id) - elif user_group_name: - group = get_object_or_none(UserGroup, name=user_group_name) - else: - return queryset - if not group: - return queryset.none() - queryset = queryset.filter(user_groups=group) - return queryset - - def filter_queryset(self, queryset): - queryset = super().filter_queryset(queryset) - queryset = self.filter_valid(queryset) - queryset = self.filter_user(queryset) - queryset = self.filter_system_user(queryset) - queryset = self.filter_user_group(queryset) - queryset = self.filter_keyword(queryset) - queryset = queryset.distinct() - return queryset diff --git a/apps/perms/api/asset/user_group_permission.py b/apps/perms/api/user_group_permission.py similarity index 99% rename from apps/perms/api/asset/user_group_permission.py rename to apps/perms/api/user_group_permission.py index 5c9e0d47e..ca090f77b 100644 --- a/apps/perms/api/asset/user_group_permission.py +++ b/apps/perms/api/user_group_permission.py @@ -9,7 +9,7 @@ from rest_framework.response import Response from common.utils import lazyproperty from perms.models import AssetPermission from assets.models import Asset, Node -from perms.api.asset import user_permission as uapi +from . import user_permission as uapi from perms import serializers from perms.utils.asset.permission import get_asset_system_user_ids_with_actions_by_group from assets.api.mixin import SerializeToTreeNodeMixin diff --git a/apps/perms/api/user_permission/__init__.py b/apps/perms/api/user_permission/__init__.py new file mode 100644 index 000000000..47f3e84a3 --- /dev/null +++ b/apps/perms/api/user_permission/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# +from .common import * +from .nodes import * +from .assets import * +from .nodes_with_assets import * diff --git a/apps/perms/api/user_permission/assets/__init__.py b/apps/perms/api/user_permission/assets/__init__.py new file mode 100644 index 000000000..0a0e47b0b --- /dev/null +++ b/apps/perms/api/user_permission/assets/__init__.py @@ -0,0 +1 @@ +from .api import * diff --git a/apps/perms/api/asset/user_permission/user_permission_assets/views.py b/apps/perms/api/user_permission/assets/api.py similarity index 100% rename from apps/perms/api/asset/user_permission/user_permission_assets/views.py rename to apps/perms/api/user_permission/assets/api.py diff --git a/apps/perms/api/asset/user_permission/user_permission_assets/mixin.py b/apps/perms/api/user_permission/assets/mixin.py similarity index 100% rename from apps/perms/api/asset/user_permission/user_permission_assets/mixin.py rename to apps/perms/api/user_permission/assets/mixin.py diff --git a/apps/perms/api/asset/user_permission/common.py b/apps/perms/api/user_permission/common.py similarity index 100% rename from apps/perms/api/asset/user_permission/common.py rename to apps/perms/api/user_permission/common.py diff --git a/apps/perms/api/asset/user_permission/mixin.py b/apps/perms/api/user_permission/mixin.py similarity index 100% rename from apps/perms/api/asset/user_permission/mixin.py rename to apps/perms/api/user_permission/mixin.py diff --git a/apps/perms/api/asset/user_permission/user_permission_nodes.py b/apps/perms/api/user_permission/nodes.py similarity index 100% rename from apps/perms/api/asset/user_permission/user_permission_nodes.py rename to apps/perms/api/user_permission/nodes.py diff --git a/apps/perms/api/asset/user_permission/user_permission_nodes_with_assets.py b/apps/perms/api/user_permission/nodes_with_assets.py similarity index 100% rename from apps/perms/api/asset/user_permission/user_permission_nodes_with_assets.py rename to apps/perms/api/user_permission/nodes_with_assets.py diff --git a/apps/perms/pagination.py b/apps/perms/pagination.py index 248958a3e..622306924 100644 --- a/apps/perms/pagination.py +++ b/apps/perms/pagination.py @@ -9,6 +9,8 @@ logger = get_logger(__name__) class GrantedAssetPaginationBase(AssetPaginationBase): + _user: object + def init_attrs(self, queryset, request: Request, view=None): super().init_attrs(queryset, request, view) self._user = view.user @@ -18,10 +20,12 @@ class NodeGrantedAssetPagination(GrantedAssetPaginationBase): def get_count_from_nodes(self, queryset): node = getattr(self._view, 'pagination_node', None) if node: - logger.debug(f'Hit node.assets_amount[{node.assets_amount}] -> {self._request.get_full_path()}') + logger.debug(f'Hit node.assets_amount[{node.assets_amount}] -> ' + f'{self._request.get_full_path()}') return node.assets_amount else: - logger.warn(f'Not hit node.assets_amount[{node}] because {self._view} not has `pagination_node` -> {self._request.get_full_path()}') + logger.warn(f'Not hit node.assets_amount[{node}] because {self._view} ' + f'not has `pagination_node` -> {self._request.get_full_path()}') return None