diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py deleted file mode 100644 index b3bfd0047..000000000 --- a/apps/assets/api/asset.py +++ /dev/null @@ -1,300 +0,0 @@ -# -*- coding: utf-8 -*- -# -from rest_framework.generics import RetrieveAPIView, ListAPIView -from rest_framework.decorators import action -from django.shortcuts import get_object_or_404 -from django.db.models import Q - -from common.utils import get_logger, get_object_or_none -from common.mixins.api import SuggestionMixin -from users.models import User, UserGroup -from users.serializers import UserSerializer, UserGroupSerializer -from users.filters import UserFilter -from perms.models import AssetPermission -from perms.serializers import AssetPermissionSerializer -from perms.filters import AssetPermissionFilter -from orgs.mixins.api import OrgBulkModelViewSet -from orgs.mixins import generics -from assets.api import FilterAssetByNodeMixin -from ..models import Asset, Node, Platform, Gateway -from .. import serializers -from ..tasks import ( - update_assets_hardware_info_manual, test_assets_connectivity_manual, - test_system_users_connectivity_a_asset, push_system_users_a_asset -) -from ..filters import FilterAssetByNodeFilterBackend, LabelFilterBackend, IpInFilterBackend - -logger = get_logger(__file__) -__all__ = [ - 'AssetViewSet', 'AssetPlatformRetrieveApi', - 'AssetGatewayListApi', 'AssetTaskCreateApi', 'AssetsTaskCreateApi', - 'AssetPermUserListApi', 'AssetPermUserPermissionsListApi', - 'AssetPermUserGroupListApi', 'AssetPermUserGroupPermissionsListApi', -] - - -class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet): - """ - API endpoint that allows Asset to be viewed or edited. - """ - model = Asset - filterset_fields = { - 'hostname': ['exact'], - 'ip': ['exact'], - 'system_users__id': ['exact'], - 'platform__base': ['exact'], - 'is_active': ['exact'], - 'protocols': ['exact', 'icontains'] - } - search_fields = ("hostname", "ip") - ordering_fields = ("hostname", "ip", "port", "cpu_cores") - ordering = ('hostname', ) - serializer_classes = { - 'default': serializers.AssetSerializer, - 'suggestion': serializers.MiniAssetSerializer, - 'platform': serializers.PlatformSerializer - } - rbac_perms = { - 'match': 'assets.match_asset' - } - extra_filter_backends = [FilterAssetByNodeFilterBackend, LabelFilterBackend, IpInFilterBackend] - - def set_assets_node(self, assets): - if not isinstance(assets, list): - assets = [assets] - node_id = self.request.query_params.get('node_id') - if not node_id: - return - node = get_object_or_none(Node, pk=node_id) - if not node: - return - node.assets.add(*assets) - - def perform_create(self, serializer): - assets = serializer.save() - self.set_assets_node(assets) - - @action(methods='GET', detail=True, url_path='platform') - def platform(self, request, *args, **kwargs): - pass - - -class AssetPlatformRetrieveApi(RetrieveAPIView): - queryset = Platform.objects.all() - serializer_class = serializers.PlatformSerializer - rbac_perms = { - 'retrieve': 'assets.view_gateway' - } - - def get_object(self): - asset_pk = self.kwargs.get('pk') - asset = get_object_or_404(Asset, pk=asset_pk) - return asset.platform - - -class AssetsTaskMixin: - - def perform_assets_task(self, serializer): - data = serializer.validated_data - action = data['action'] - assets = data.get('assets', []) - if action == "refresh": - task = update_assets_hardware_info_manual.delay(assets) - else: - # action == 'test': - task = test_assets_connectivity_manual.delay(assets) - return task - - def perform_create(self, serializer): - task = self.perform_assets_task(serializer) - self.set_task_to_serializer_data(serializer, task) - - def set_task_to_serializer_data(self, serializer, task): - data = getattr(serializer, '_data', {}) - data["task"] = task.id - setattr(serializer, '_data', data) - - -class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): - model = Asset - serializer_class = serializers.AssetTaskSerializer - - def create(self, request, *args, **kwargs): - pk = self.kwargs.get('pk') - request.data['asset'] = pk - request.data['assets'] = [pk] - return super().create(request, *args, **kwargs) - - def check_permissions(self, request): - action = request.data.get('action') - action_perm_require = { - 'refresh': 'assets.refresh_assethardwareinfo', - 'push_system_user': 'assets.push_assetsystemuser', - 'test': 'assets.test_assetconnectivity', - 'test_system_user': 'assets.test_assetconnectivity' - } - perm_required = action_perm_require.get(action) - has = self.request.user.has_perm(perm_required) - - if not has: - self.permission_denied(request) - - def perform_asset_task(self, serializer): - data = serializer.validated_data - action = data['action'] - if action not in ['push_system_user', 'test_system_user']: - return - - asset = data['asset'] - system_users = data.get('system_users') - if not system_users: - system_users = asset.get_all_system_users() - if action == 'push_system_user': - task = push_system_users_a_asset.delay(system_users, asset=asset) - elif action == 'test_system_user': - task = test_system_users_connectivity_a_asset.delay(system_users, asset=asset) - else: - task = None - return task - - def perform_create(self, serializer): - task = self.perform_asset_task(serializer) - if not task: - task = self.perform_assets_task(serializer) - self.set_task_to_serializer_data(serializer, task) - - -class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): - model = Asset - serializer_class = serializers.AssetsTaskSerializer - - def check_permissions(self, request): - action = request.data.get('action') - action_perm_require = { - 'refresh': 'assets.refresh_assethardwareinfo', - } - perm_required = action_perm_require.get(action) - has = self.request.user.has_perm(perm_required) - if not has: - self.permission_denied(request) - - -class AssetGatewayListApi(generics.ListAPIView): - serializer_class = serializers.GatewayWithAuthSerializer - rbac_perms = { - 'list': 'assets.view_gateway' - } - - def get_queryset(self): - asset_id = self.kwargs.get('pk') - asset = get_object_or_404(Asset, pk=asset_id) - if not asset.domain: - return Gateway.objects.none() - queryset = asset.domain.gateways.filter(protocol='ssh') - return queryset - - -class BaseAssetPermUserOrUserGroupListApi(ListAPIView): - rbac_perms = { - 'GET': 'perms.view_assetpermission' - } - - def get_object(self): - asset_id = self.kwargs.get('pk') - asset = get_object_or_404(Asset, pk=asset_id) - return asset - - def get_asset_related_perms(self): - asset = self.get_object() - nodes = asset.get_all_nodes(flat=True) - perms = AssetPermission.objects.filter(Q(assets=asset) | Q(nodes__in=nodes)) - return perms - - -class AssetPermUserListApi(BaseAssetPermUserOrUserGroupListApi): - filterset_class = UserFilter - search_fields = ('username', 'email', 'name', 'id', 'source', 'role') - serializer_class = UserSerializer - rbac_perms = { - 'GET': 'perms.view_assetpermission' - } - - def get_queryset(self): - perms = self.get_asset_related_perms() - users = User.objects.filter( - Q(assetpermissions__in=perms) | Q(groups__assetpermissions__in=perms) - ).distinct() - return users - - -class AssetPermUserGroupListApi(BaseAssetPermUserOrUserGroupListApi): - serializer_class = UserGroupSerializer - - def get_queryset(self): - perms = self.get_asset_related_perms() - user_groups = UserGroup.objects.filter(assetpermissions__in=perms).distinct() - return user_groups - - -class BasePermedAssetListApi(generics.ListAPIView): - model = AssetPermission - serializer_class = AssetPermissionSerializer - filterset_class = AssetPermissionFilter - search_fields = ('name',) - rbac_perms = { - 'list': 'perms.view_assetpermission' - } - - def get_object(self): - asset_id = self.kwargs.get('pk') - asset = get_object_or_404(Asset, pk=asset_id) - return asset - - def filter_asset_related(self, queryset): - asset = self.get_object() - nodes = asset.get_all_nodes(flat=True) - perms = queryset.filter(Q(assets=asset) | Q(nodes__in=nodes)) - return perms - - def filter_queryset(self, queryset): - queryset = super().filter_queryset(queryset) - queryset = self.filter_asset_related(queryset) - return queryset - - -class AssetPermUserPermissionsListApi(BasePermedAssetListApi): - def filter_queryset(self, queryset): - queryset = super().filter_queryset(queryset) - queryset = self.filter_user_related(queryset) - queryset = queryset.distinct() - return queryset - - def filter_user_related(self, queryset): - user = self.get_perm_user() - user_groups = user.groups.all() - perms = queryset.filter(Q(users=user) | Q(user_groups__in=user_groups)) - return perms - - def get_perm_user(self): - user_id = self.kwargs.get('perm_user_id') - user = get_object_or_404(User, pk=user_id) - return user - - -class AssetPermUserGroupPermissionsListApi(BasePermedAssetListApi): - def filter_queryset(self, queryset): - queryset = super().filter_queryset(queryset) - queryset = self.filter_user_group_related(queryset) - queryset = queryset.distinct() - return queryset - - def filter_user_group_related(self, queryset): - user_group = self.get_perm_user_group() - perms = queryset.filter(user_groups=user_group) - return perms - - def get_perm_user_group(self): - user_group_id = self.kwargs.get('perm_user_group_id') - user_group = get_object_or_404(UserGroup, pk=user_group_id) - return user_group - diff --git a/apps/assets/api/asset/__init__.py b/apps/assets/api/asset/__init__.py new file mode 100644 index 000000000..85972fb52 --- /dev/null +++ b/apps/assets/api/asset/__init__.py @@ -0,0 +1,2 @@ +from .common import * +from .permission import * diff --git a/apps/assets/api/asset/common.py b/apps/assets/api/asset/common.py new file mode 100644 index 000000000..6b7fa7d2c --- /dev/null +++ b/apps/assets/api/asset/common.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +# +from rest_framework.decorators import action +from rest_framework.response import Response + +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, + test_system_users_connectivity_a_asset, push_system_users_a_asset +) +from assets.filters import FilterAssetByNodeFilterBackend, LabelFilterBackend, IpInFilterBackend + +logger = get_logger(__file__) +__all__ = [ + 'AssetViewSet', 'AssetTaskCreateApi', 'AssetsTaskCreateApi', +] + + +class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet): + """ + API endpoint that allows Asset to be viewed or edited. + """ + model = Asset + filterset_fields = { + 'hostname': ['exact'], + 'ip': ['exact'], + 'system_users__id': ['exact'], + 'platform__base': ['exact'], + 'is_active': ['exact'], + 'protocols': ['exact', 'icontains'] + } + search_fields = ("hostname", "ip") + ordering_fields = ("hostname", "ip", "port", "cpu_cores") + ordering = ('hostname', ) + serializer_classes = { + 'default': serializers.AssetSerializer, + 'suggestion': serializers.MiniAssetSerializer, + 'platform': serializers.PlatformSerializer, + 'gateways': serializers.GatewayWithAuthSerializer + } + rbac_perms = { + 'match': 'assets.match_asset', + 'platform': 'assets.view_platform', + 'gateways': 'assets.view_gateway' + } + extra_filter_backends = [ + FilterAssetByNodeFilterBackend, LabelFilterBackend, + IpInFilterBackend, + ] + + def set_assets_node(self, assets): + if not isinstance(assets, list): + assets = [assets] + node_id = self.request.query_params.get('node_id') + if not node_id: + return + node = get_object_or_none(Node, pk=node_id) + if not node: + return + node.assets.add(*assets) + + def perform_create(self, serializer): + assets = serializer.save() + self.set_assets_node(assets) + + @action(methods=['GET'], detail=True, url_path='platform') + def platform(self, *args, **kwargs): + asset = self.get_object() + serializer = self.get_serializer(asset.platform) + return Response(serializer.data) + + @action(methods=['GET'], detail=True, url_path='gateways') + def gateways(self, *args, **kwargs): + asset = self.get_object() + if not asset.domain: + gateways = Gateway.objects.none() + else: + gateways = asset.domain.gateways.filter(protocol='ssh') + return self.get_paginated_response_from_queryset(gateways) + + +class AssetsTaskMixin: + def perform_assets_task(self, serializer): + data = serializer.validated_data + action = data['action'] + assets = data.get('assets', []) + if action == "refresh": + task = update_assets_hardware_info_manual.delay(assets) + else: + # action == 'test': + task = test_assets_connectivity_manual.delay(assets) + return task + + def perform_create(self, serializer): + task = self.perform_assets_task(serializer) + self.set_task_to_serializer_data(serializer, task) + + def set_task_to_serializer_data(self, serializer, task): + data = getattr(serializer, '_data', {}) + data["task"] = task.id + setattr(serializer, '_data', data) + + +class AssetTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): + model = Asset + serializer_class = serializers.AssetTaskSerializer + + def create(self, request, *args, **kwargs): + pk = self.kwargs.get('pk') + request.data['asset'] = pk + request.data['assets'] = [pk] + return super().create(request, *args, **kwargs) + + def check_permissions(self, request): + action = request.data.get('action') + action_perm_require = { + 'refresh': 'assets.refresh_assethardwareinfo', + 'push_system_user': 'assets.push_assetsystemuser', + 'test': 'assets.test_assetconnectivity', + 'test_system_user': 'assets.test_assetconnectivity' + } + perm_required = action_perm_require.get(action) + has = self.request.user.has_perm(perm_required) + + if not has: + self.permission_denied(request) + + def perform_asset_task(self, serializer): + data = serializer.validated_data + action = data['action'] + if action not in ['push_system_user', 'test_system_user']: + return + + asset = data['asset'] + system_users = data.get('system_users') + if not system_users: + system_users = asset.get_all_system_users() + if action == 'push_system_user': + task = push_system_users_a_asset.delay(system_users, asset=asset) + elif action == 'test_system_user': + task = test_system_users_connectivity_a_asset.delay(system_users, asset=asset) + else: + task = None + return task + + def perform_create(self, serializer): + task = self.perform_asset_task(serializer) + if not task: + task = self.perform_assets_task(serializer) + self.set_task_to_serializer_data(serializer, task) + + +class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView): + model = Asset + serializer_class = serializers.AssetsTaskSerializer + + def check_permissions(self, request): + action = request.data.get('action') + action_perm_require = { + 'refresh': 'assets.refresh_assethardwareinfo', + } + perm_required = action_perm_require.get(action) + has = self.request.user.has_perm(perm_required) + if not has: + self.permission_denied(request) + + diff --git a/apps/assets/api/asset/permission.py b/apps/assets/api/asset/permission.py new file mode 100644 index 000000000..cab75bc17 --- /dev/null +++ b/apps/assets/api/asset/permission.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +# +from rest_framework.generics import ListAPIView +from django.shortcuts import get_object_or_404 +from django.db.models import Q + +from common.utils import get_logger +from users.models import User, UserGroup +from users.serializers import UserSerializer, UserGroupSerializer +from users.filters import UserFilter +from perms.models import AssetPermission +from perms.serializers import AssetPermissionSerializer +from perms.filters import AssetPermissionFilter +from orgs.mixins import generics +from assets.models import Asset + +logger = get_logger(__file__) +__all__ = [ + 'AssetPermUserListApi', 'AssetPermUserPermissionsListApi', + 'AssetPermUserGroupListApi', 'AssetPermUserGroupPermissionsListApi', +] + + +class BaseAssetPermUserOrUserGroupListApi(ListAPIView): + rbac_perms = { + 'GET': 'perms.view_assetpermission' + } + + def get_object(self): + asset_id = self.kwargs.get('pk') + asset = get_object_or_404(Asset, pk=asset_id) + return asset + + def get_asset_related_perms(self): + asset = self.get_object() + nodes = asset.get_all_nodes(flat=True) + perms = AssetPermission.objects.filter(Q(assets=asset) | Q(nodes__in=nodes)) + return perms + + +class AssetPermUserListApi(BaseAssetPermUserOrUserGroupListApi): + filterset_class = UserFilter + search_fields = ('username', 'email', 'name', 'id', 'source', 'role') + serializer_class = UserSerializer + rbac_perms = { + 'GET': 'perms.view_assetpermission' + } + + def get_queryset(self): + perms = self.get_asset_related_perms() + users = User.objects.filter( + Q(assetpermissions__in=perms) | Q(groups__assetpermissions__in=perms) + ).distinct() + return users + + +class AssetPermUserGroupListApi(BaseAssetPermUserOrUserGroupListApi): + serializer_class = UserGroupSerializer + + def get_queryset(self): + perms = self.get_asset_related_perms() + user_groups = UserGroup.objects.filter(assetpermissions__in=perms).distinct() + return user_groups + + +class BaseAssetRelatedPermissionListApi(generics.ListAPIView): + model = AssetPermission + serializer_class = AssetPermissionSerializer + filterset_class = AssetPermissionFilter + search_fields = ('name',) + rbac_perms = { + 'list': 'perms.view_assetpermission' + } + + def get_object(self): + asset_id = self.kwargs.get('pk') + asset = get_object_or_404(Asset, pk=asset_id) + return asset + + def filter_asset_related(self, queryset): + asset = self.get_object() + nodes = asset.get_all_nodes(flat=True) + perms = queryset.filter(Q(assets=asset) | Q(nodes__in=nodes)) + return perms + + def filter_queryset(self, queryset): + queryset = super().filter_queryset(queryset) + queryset = self.filter_asset_related(queryset) + return queryset + + +class AssetPermUserPermissionsListApi(BaseAssetRelatedPermissionListApi): + def filter_queryset(self, queryset): + queryset = super().filter_queryset(queryset) + queryset = self.filter_user_related(queryset) + queryset = queryset.distinct() + return queryset + + def filter_user_related(self, queryset): + user = self.get_perm_user() + user_groups = user.groups.all() + perms = queryset.filter(Q(users=user) | Q(user_groups__in=user_groups)) + return perms + + def get_perm_user(self): + user_id = self.kwargs.get('perm_user_id') + user = get_object_or_404(User, pk=user_id) + return user + + +class AssetPermUserGroupPermissionsListApi(BaseAssetRelatedPermissionListApi): + def filter_queryset(self, queryset): + queryset = super().filter_queryset(queryset) + queryset = self.filter_user_group_related(queryset) + queryset = queryset.distinct() + return queryset + + def filter_user_group_related(self, queryset): + user_group = self.get_perm_user_group() + perms = queryset.filter(user_groups=user_group) + return perms + + def get_perm_user_group(self): + user_group_id = self.kwargs.get('perm_user_group_id') + user_group = get_object_or_404(UserGroup, pk=user_group_id) + return user_group + diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index 59d4ab171..434f09441 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -34,8 +34,7 @@ cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-r urlpatterns = [ - path('assets//gateways/', api.AssetGatewayListApi.as_view(), name='asset-gateway-list'), - path('assets//platform/', api.AssetPlatformRetrieveApi.as_view(), name='asset-platform-detail'), + # path('assets//gateways/', api.AssetGatewayListApi.as_view(), name='asset-gateway-list'), path('assets//tasks/', api.AssetTaskCreateApi.as_view(), name='asset-task-create'), path('assets/tasks/', api.AssetsTaskCreateApi.as_view(), name='assets-task-create'), path('assets//perm-users/', api.AssetPermUserListApi.as_view(), name='asset-perm-user-list'), diff --git a/apps/common/mixins/api/common.py b/apps/common/mixins/api/common.py index 3c59739ad..20e2eabc7 100644 --- a/apps/common/mixins/api/common.py +++ b/apps/common/mixins/api/common.py @@ -83,7 +83,8 @@ class RelationMixin: class CommonApiMixin(SerializerMixin, ExtraFilterFieldsMixin, - QuerySetMixin, RenderToJsonMixin): + QuerySetMixin, RenderToJsonMixin, + PaginatedResponseMixin): pass