diff --git a/apps/accounts/api/account/backup.py b/apps/accounts/api/account/backup.py index e8fcd87f6..fa068a0df 100644 --- a/apps/accounts/api/account/backup.py +++ b/apps/accounts/api/account/backup.py @@ -20,7 +20,6 @@ class AccountBackupPlanViewSet(OrgBulkModelViewSet): model = AccountBackupAutomation filter_fields = ('name',) search_fields = filter_fields - ordering_fields = ('name',) ordering = ('name',) serializer_class = serializers.AccountBackupSerializer diff --git a/apps/accounts/api/automations/change_secret.py b/apps/accounts/api/automations/change_secret.py index b6034c79d..2af9afd09 100644 --- a/apps/accounts/api/automations/change_secret.py +++ b/apps/accounts/api/automations/change_secret.py @@ -25,7 +25,6 @@ class ChangeSecretAutomationViewSet(OrgBulkModelViewSet): model = ChangeSecretAutomation filter_fields = ('name', 'secret_type', 'secret_strategy') search_fields = filter_fields - ordering_fields = ('name',) serializer_class = serializers.ChangeSecretAutomationSerializer diff --git a/apps/accounts/api/automations/gather_accounts.py b/apps/accounts/api/automations/gather_accounts.py index 8fbd2dc92..59f55c60e 100644 --- a/apps/accounts/api/automations/gather_accounts.py +++ b/apps/accounts/api/automations/gather_accounts.py @@ -15,7 +15,6 @@ class GatherAccountsAutomationViewSet(OrgBulkModelViewSet): model = GatherAccountsAutomation filter_fields = ('name',) search_fields = filter_fields - ordering_fields = ('name',) serializer_class = serializers.GatherAccountAutomationSerializer diff --git a/apps/accounts/api/automations/push_account.py b/apps/accounts/api/automations/push_account.py index a736daaf6..94cf64d51 100644 --- a/apps/accounts/api/automations/push_account.py +++ b/apps/accounts/api/automations/push_account.py @@ -22,7 +22,6 @@ class PushAccountAutomationViewSet(OrgBulkModelViewSet): model = PushAccountAutomation filter_fields = ('name', 'secret_type', 'secret_strategy') search_fields = filter_fields - ordering_fields = ('name',) serializer_class = serializers.PushAccountAutomationSerializer diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index b032615e6..630a24552 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -95,7 +95,6 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): model = Asset filterset_class = AssetFilterSet search_fields = ("name", "address") - ordering_fields = ("name", "address", "connectivity") ordering = ("name", "connectivity") serializer_classes = ( ("default", serializers.AssetSerializer), diff --git a/apps/assets/api/domain.py b/apps/assets/api/domain.py index 041de210d..f27f09c90 100644 --- a/apps/assets/api/domain.py +++ b/apps/assets/api/domain.py @@ -3,8 +3,9 @@ from django.utils.translation import ugettext as _ from django.views.generic.detail import SingleObjectMixin from rest_framework.serializers import ValidationError from rest_framework.views import APIView, Response -from common.utils import get_logger + from assets.tasks import test_gateways_connectivity_manual +from common.utils import get_logger from orgs.mixins.api import OrgBulkModelViewSet from .asset import HostViewSet from .. import serializers @@ -18,14 +19,12 @@ class DomainViewSet(OrgBulkModelViewSet): model = Domain filterset_fields = ("name",) search_fields = filterset_fields - serializer_class = serializers.DomainSerializer - ordering_fields = ('name',) ordering = ('name',) def get_serializer_class(self): if self.request.query_params.get('gateway'): return serializers.DomainWithGatewaySerializer - return super().get_serializer_class() + return serializers.DomainSerializer class GatewayViewSet(HostViewSet): diff --git a/apps/assets/migrations/0110_auto_20230220_1051.py b/apps/assets/migrations/0110_auto_20230220_1051.py new file mode 100644 index 000000000..a94fd7d03 --- /dev/null +++ b/apps/assets/migrations/0110_auto_20230220_1051.py @@ -0,0 +1,38 @@ +# Generated by Django 3.2.14 on 2023-02-20 02:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0109_alter_asset_options'), + ] + + operations = [ + migrations.AddField( + model_name='platform', + name='created_by', + field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'), + ), + migrations.AddField( + model_name='platform', + name='date_created', + field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created'), + ), + migrations.AddField( + model_name='platform', + name='date_updated', + field=models.DateTimeField(auto_now=True, verbose_name='Date updated'), + ), + migrations.AddField( + model_name='platform', + name='updated_by', + field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'), + ), + migrations.AlterField( + model_name='platform', + name='comment', + field=models.TextField(blank=True, default='', verbose_name='Comment'), + ), + ] diff --git a/apps/assets/models/platform.py b/apps/assets/models/platform.py index 1133969b8..929eff10e 100644 --- a/apps/assets/models/platform.py +++ b/apps/assets/models/platform.py @@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _ from assets.const import AllTypes from assets.const import Protocol from common.db.fields import JsonDictTextField +from common.db.models import JMSBaseModel __all__ = ['Platform', 'PlatformProtocol', 'PlatformAutomation'] @@ -61,7 +62,7 @@ class PlatformAutomation(models.Model): ) -class Platform(models.Model): +class Platform(JMSBaseModel): """ 对资产提供 约束和默认值 对资产进行抽象 @@ -71,12 +72,12 @@ class Platform(models.Model): utf8 = 'utf-8', 'UTF-8' gbk = 'gbk', 'GBK' + id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID') name = models.SlugField(verbose_name=_("Name"), unique=True, allow_unicode=True) category = models.CharField(default='host', max_length=32, verbose_name=_("Category")) type = models.CharField(max_length=32, default='linux', verbose_name=_("Type")) meta = JsonDictTextField(blank=True, null=True, verbose_name=_("Meta")) internal = models.BooleanField(default=False, verbose_name=_("Internal")) - comment = models.TextField(blank=True, null=True, verbose_name=_("Comment")) # 资产有关的 charset = models.CharField( default=CharsetChoices.utf8, choices=CharsetChoices.choices, max_length=8, verbose_name=_("Charset") diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index 0bbb0aa51..893d980a3 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -106,12 +106,13 @@ class PlatformSerializer(WritableNestedModelSerializer): fields_small = fields_mini + [ "category", "type", "charset", ] - fields = fields_small + [ - "protocols", - "domain_enabled", "su_enabled", - "su_method", "automation", - "comment", + fields_other = [ + 'date_created', 'date_updated', 'created_by', 'updated_by', ] + fields = fields_small + [ + "protocols", "domain_enabled", "su_enabled", + "su_method", "automation", "comment", + ] + fields_other extra_kwargs = { "su_enabled": {"label": _('Su enabled')}, "domain_enabled": {"label": _('Domain enabled')}, diff --git a/apps/common/api/filter.py b/apps/common/api/filter.py index 1d1451b66..c0307dd59 100644 --- a/apps/common/api/filter.py +++ b/apps/common/api/filter.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- # +import logging from itertools import chain +from django.db import models from rest_framework.settings import api_settings from common.drf.filters import IDSpmFilter, CustomFilter, IDInFilter - -__all__ = ['ExtraFilterFieldsMixin'] +__all__ = ['ExtraFilterFieldsMixin', 'OrderingFielderFieldsMixin'] class ExtraFilterFieldsMixin: @@ -33,3 +34,55 @@ class ExtraFilterFieldsMixin: for backend in self.get_filter_backends(): queryset = backend().filter_queryset(self.request, queryset, self) return queryset + + +class OrderingFielderFieldsMixin: + """ + 额外的 api ordering + """ + ordering_fields = None + extra_ordering_fields = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.ordering_fields = self._get_ordering_fields() + + def _get_ordering_fields(self): + if isinstance(self.__class__.ordering_fields, (list, tuple)): + return self.__class__.ordering_fields + + try: + valid_fields = self.get_valid_ordering_fields() + except Exception as e: + logging.debug('get_valid_ordering_fields error: %s' % e) + valid_fields = [] + + fields = list(chain( + valid_fields, + self.extra_ordering_fields + )) + return fields + + def get_valid_ordering_fields(self): + if getattr(self, 'model', None): + model = self.model + elif getattr(self, 'queryset', None): + model = self.queryset.model + else: + queryset = self.get_queryset() + model = queryset.model + + if not model: + return [] + + excludes_fields = ( + models.UUIDField, models.Model, models.ForeignKey, + models.FileField, models.JSONField, models.ManyToManyField, + models.DurationField, + ) + valid_fields = [] + for field in model._meta.fields: + if isinstance(field, excludes_fields): + continue + valid_fields.append(field.name) + return valid_fields diff --git a/apps/common/api/mixin.py b/apps/common/api/mixin.py index 0e2ce3958..3c8c2d487 100644 --- a/apps/common/api/mixin.py +++ b/apps/common/api/mixin.py @@ -7,7 +7,7 @@ from django.db.models.signals import m2m_changed from rest_framework.response import Response from .action import RenderToJsonMixin -from .filter import ExtraFilterFieldsMixin +from .filter import ExtraFilterFieldsMixin, OrderingFielderFieldsMixin from .serializer import SerializerMixin __all__ = [ @@ -98,7 +98,6 @@ class QuerySetMixin: return queryset -class CommonApiMixin(SerializerMixin, ExtraFilterFieldsMixin, - QuerySetMixin, RenderToJsonMixin, - PaginatedResponseMixin): +class CommonApiMixin(SerializerMixin, ExtraFilterFieldsMixin, OrderingFielderFieldsMixin, + QuerySetMixin, RenderToJsonMixin, PaginatedResponseMixin): pass diff --git a/apps/common/drf/filters.py b/apps/common/drf/filters.py index 80074edf9..949260a47 100644 --- a/apps/common/drf/filters.py +++ b/apps/common/drf/filters.py @@ -1,18 +1,20 @@ # -*- coding: utf-8 -*- # -from rest_framework import filters -from rest_framework.fields import DateTimeField -from rest_framework.serializers import ValidationError -from rest_framework.compat import coreapi, coreschema +import logging + from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured from django_filters import rest_framework as drf_filters -import logging +from rest_framework import filters +from rest_framework.compat import coreapi, coreschema +from rest_framework.fields import DateTimeField +from rest_framework.serializers import ValidationError from common import const __all__ = [ - "DatetimeRangeFilter", "IDSpmFilter", 'IDInFilter', "CustomFilter", + "DatetimeRangeFilter", "IDSpmFilter", + 'IDInFilter', "CustomFilter", "BaseFilterSet" ] diff --git a/apps/ops/serializers/celery.py b/apps/ops/serializers/celery.py index 12efdbea8..657600e50 100644 --- a/apps/ops/serializers/celery.py +++ b/apps/ops/serializers/celery.py @@ -1,9 +1,9 @@ # ~*~ coding: utf-8 ~*~ from __future__ import unicode_literals -from rest_framework import serializers -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django_celery_beat.models import PeriodicTask +from rest_framework import serializers __all__ = [ 'CeleryResultSerializer', 'CeleryTaskExecutionSerializer', @@ -41,7 +41,7 @@ class CeleryTaskExecutionSerializer(serializers.ModelSerializer): class Meta: model = CeleryTaskExecution fields = [ - "id", "name", "args", "kwargs", "time_cost", "timedelta", "is_success", "is_finished", "date_published", - "date_start", - "date_finished" + "id", "name", "args", "kwargs", "time_cost", "timedelta", + "is_success", "is_finished", "date_published", + "date_start", "date_finished" ] diff --git a/apps/orgs/api.py b/apps/orgs/api.py index 0452f2bff..b49bb08db 100644 --- a/apps/orgs/api.py +++ b/apps/orgs/api.py @@ -1,20 +1,20 @@ # -*- coding: utf-8 -*- # -from django.utils.translation import ugettext as _ from django.conf import settings -from rest_framework_bulk import BulkModelViewSet -from rest_framework.generics import RetrieveAPIView +from django.utils.translation import ugettext as _ from rest_framework.exceptions import PermissionDenied +from rest_framework.generics import RetrieveAPIView -from common.utils import get_logger -from common.permissions import IsValidUser -from users.models import User, UserGroup from assets.models import ( Asset, Domain, Label, Node, ) -from perms.models import AssetPermission +from common.api import JMSBulkModelViewSet +from common.permissions import IsValidUser +from common.utils import get_logger from orgs.utils import current_org, tmp_to_root_org +from perms.models import AssetPermission +from users.models import User, UserGroup from .models import Organization from .serializers import ( OrgSerializer, CurrentOrgSerializer @@ -29,12 +29,11 @@ org_related_models = [ ] -class OrgViewSet(BulkModelViewSet): +class OrgViewSet(JMSBulkModelViewSet): filterset_fields = ('name',) search_fields = ('name', 'comment') queryset = Organization.objects.all() serializer_class = OrgSerializer - ordering_fields = ('name',) ordering = ('name',) def get_serializer_class(self): diff --git a/apps/perms/api/asset_permission.py b/apps/perms/api/asset_permission.py index de15a6c6f..750f41fb4 100644 --- a/apps/perms/api/asset_permission.py +++ b/apps/perms/api/asset_permission.py @@ -16,5 +16,4 @@ class AssetPermissionViewSet(OrgBulkModelViewSet): serializer_class = serializers.AssetPermissionSerializer filterset_class = AssetPermissionFilter search_fields = ('name',) - ordering_fields = ('name',) ordering = ('name',) diff --git a/apps/perms/api/user_permission/assets.py b/apps/perms/api/user_permission/assets.py index e499a3127..98339fde3 100644 --- a/apps/perms/api/user_permission/assets.py +++ b/apps/perms/api/user_permission/assets.py @@ -1,19 +1,18 @@ import abc + from rest_framework.generics import ListAPIView -from assets.models import Asset, Node from assets.api.asset.asset import AssetFilterSet +from assets.models import Asset, Node +from common.utils import get_logger, lazyproperty from perms import serializers from perms.pagination import AllPermedAssetPagination from perms.pagination import NodePermedAssetPagination from perms.utils import UserPermAssetUtil -from common.utils import get_logger, lazyproperty - from .mixin import ( SelfOrPKUserMixin ) - __all__ = [ 'UserAllPermedAssetsApi', 'UserDirectPermedAssetsApi', @@ -26,8 +25,8 @@ logger = get_logger(__name__) class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ListAPIView): ordering = ('name',) - ordering_fields = ("name", "address") search_fields = ('name', 'address', 'comment') + ordering_fields = ("name", "address") filterset_class = AssetFilterSet serializer_class = serializers.AssetPermedSerializer only_fields = serializers.AssetPermedSerializer.Meta.only_fields diff --git a/apps/terminal/api/session/task.py b/apps/terminal/api/session/task.py index 80fee6097..8daeeb4a7 100644 --- a/apps/terminal/api/session/task.py +++ b/apps/terminal/api/session/task.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- # import logging -from rest_framework.views import APIView, Response -from rest_framework_bulk import BulkModelViewSet + from rest_framework import status from rest_framework.permissions import IsAuthenticated +from rest_framework.views import APIView, Response +from common.api import JMSBulkModelViewSet from common.utils import get_object_or_none from orgs.utils import tmp_to_root_org -from terminal.models import Session, Task from terminal import serializers +from terminal.models import Session, Task from terminal.utils import is_session_approver __all__ = ['TaskViewSet', 'KillSessionAPI', 'KillSessionForTicketAPI'] logger = logging.getLogger(__file__) -class TaskViewSet(BulkModelViewSet): +class TaskViewSet(JMSBulkModelViewSet): queryset = Task.objects.all() serializer_class = serializers.TaskSerializer filterset_fields = ('is_finished',) @@ -51,7 +52,7 @@ class KillSessionAPI(APIView): class KillSessionForTicketAPI(APIView): - permission_classes = (IsAuthenticated, ) + permission_classes = (IsAuthenticated,) def post(self, request, *args, **kwargs): session_ids = request.data diff --git a/apps/tickets/api/ticket.py b/apps/tickets/api/ticket.py index 4927527ce..52c388cec 100644 --- a/apps/tickets/api/ticket.py +++ b/apps/tickets/api/ticket.py @@ -35,10 +35,6 @@ class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet): search_fields = [ 'title', 'type', 'status' ] - ordering_fields = ( - 'title', 'status', 'state', 'action_display', - 'date_created', 'serial_num', - ) ordering = ('-date_created',) rbac_perms = { 'open': 'tickets.view_ticket', diff --git a/apps/users/api/group.py b/apps/users/api/group.py index 4c1f06ea7..1107a7eb8 100644 --- a/apps/users/api/group.py +++ b/apps/users/api/group.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- # -from ..serializers import UserGroupSerializer -from ..models import UserGroup from orgs.mixins.api import OrgBulkModelViewSet - +from ..models import UserGroup +from ..serializers import UserGroupSerializer __all__ = ['UserGroupViewSet'] @@ -14,5 +13,4 @@ class UserGroupViewSet(OrgBulkModelViewSet): filterset_fields = ("name",) search_fields = filterset_fields serializer_class = UserGroupSerializer - ordering_fields = ('name', ) - ordering = ('name', ) + ordering = ('name',) diff --git a/apps/users/api/user.py b/apps/users/api/user.py index 4457ab624..2b08418bf 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -39,7 +39,6 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelV 'suggestion': MiniUserSerializer, 'invite': InviteSerializer, } - ordering_fields = ('name',) ordering = ('name',) rbac_perms = { 'match': 'users.match_user',