From 094ef5d230a50ae99f28d3f846a1da038ad0a6bf Mon Sep 17 00:00:00 2001 From: benero Date: Thu, 3 Oct 2024 23:35:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20API=20=E5=B9=B3=E5=8F=B0=E9=89=B4?= =?UTF-8?q?=E6=9D=83=E8=B0=83=E6=95=B4=E4=B8=BA=E6=97=A0=E5=AE=9E=E4=BE=8B?= =?UTF-8?q?=20--story=3D119850967?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- itsm/component/drf/serializers.py | 23 ++++++----- itsm/postman/permissions.py | 68 ++++++++++++++++++++----------- itsm/postman/serializers.py | 6 ++- itsm/postman/views.py | 22 +++++++++- itsm/workflow/permissions.py | 10 ++++- 5 files changed, 89 insertions(+), 40 deletions(-) diff --git a/itsm/component/drf/serializers.py b/itsm/component/drf/serializers.py index 39e4c4bb0..a2ad7dbbb 100644 --- a/itsm/component/drf/serializers.py +++ b/itsm/component/drf/serializers.py @@ -45,7 +45,19 @@ from itsm.component.constants import DEFAULT_PROJECT_PROJECT_KEY -class AuthModelSerializer(serializers.ModelSerializer): +class BaseModelSerializer(serializers.ModelSerializer): + + def to_internal_value(self, data): + data = super().to_internal_value(data) + if self.instance: + # update + if hasattr(self.Meta, "create_only_fields"): + for x in self.Meta.create_only_fields: + data.pop(x, None) + return data + + +class AuthModelSerializer(BaseModelSerializer): """ 权限中心接入每个资源权限内容序列化 """ @@ -107,15 +119,6 @@ def update_auth_actions(self, instance, data): auth_actions=[action for action, result in instance_permissions.items() if result]) return data - def to_internal_value(self, data): - data = super().to_internal_value(data) - if self.instance: - # update - if hasattr(self.Meta, "create_only_fields"): - for x in self.Meta.create_only_fields: - data.pop(x, None) - return data - class DynamicFieldsModelSerializer(AuthModelSerializer): """ diff --git a/itsm/postman/permissions.py b/itsm/postman/permissions.py index c8a4aba0d..7541e4b9d 100644 --- a/itsm/postman/permissions.py +++ b/itsm/postman/permissions.py @@ -25,7 +25,12 @@ from itsm.component.constants import PUBLIC_PROJECT_PROJECT_KEY from itsm.component.drf import permissions as perm -from itsm.postman.models import RemoteSystem +from itsm.component.exceptions import ValidateError +from itsm.postman.models import RemoteSystem, RemoteApi +from django.utils.translation import ugettext as _ + +from itsm.project.models import Project +from itsm.workflow.permissions import WorkflowElementManagePermission class IsObjManager(perm.IsManager): @@ -36,37 +41,52 @@ class IsObjManager(perm.IsManager): pass -class RemoteApiPermit(perm.IamAuthProjectViewPermit): +class RemoteApiPermit(WorkflowElementManagePermission): def has_permission(self, request, view): if view.action == "create": if "remote_system" in request.data: remote_system_id = request.data["remote_system"] project_key = RemoteSystem.objects.get(id=remote_system_id).project_key + # 平台公共API管理 if project_key == PUBLIC_PROJECT_PROJECT_KEY: - apply_actions = ["public_api_view"] - return self.iam_auth(request, apply_actions) - else: - # 项目 - apply_actions = ["system_settings_manage"] - return self.has_project_view_permission( - request, project_key, apply_actions - ) + return self.iam_auth(request, ["public_apis_manage"]) + + # 项目管理 + apply_actions = ["system_settings_manage"] + project = Project.objects.get(pk=project_key) + return self.iam_auth(request, apply_actions, project) + + if view.action == "batch_delete": + api_ids = request.data["id"].split(",") + api_instances = RemoteApi.objects.filter(pk__in=api_ids) + project_keys = set([i.remote_system.project_key for i in api_instances]) + if len(project_keys) != 1: + raise ValidateError(_("API 所属项目异常")) + project_key = project_keys.pop() + + # 平台公共API管理 + if project_key == PUBLIC_PROJECT_PROJECT_KEY: + return self.iam_auth(request, ["public_apis_manage"]) + + # 项目 + project = Project.objects.get(pk=project_key) + return self.iam_auth(request, ["system_settings_manage"], project) + return True - def has_object_permission(self, request, view, obj): - if obj is not None: - # 如果是公共api需要单独鉴权 + def has_object_permission(self, request, view, obj, **kwargs): + if view.action in getattr(view, "permission_free_actions", []): + return True + + if obj: + # 平台公共 API 管理 if obj.remote_system.project_key == PUBLIC_PROJECT_PROJECT_KEY: if view.action == "retrieve": - apply_actions = [] - else: - apply_actions = ["public_api_manage"] - return self.iam_auth(request, apply_actions, obj) - else: - # 项目 - project_key = obj.remote_system.project_key - apply_actions = ["system_settings_manage"] - return self.has_project_view_permission( - request, project_key, apply_actions - ) + return True + return self.iam_auth(request, ["public_apis_manage"]) + + # 项目管理 + project_key = obj.remote_system.project_key + project = Project.objects.get(pk=project_key) + return self.iam_auth(request, ["system_settings_manage"], project) return True diff --git a/itsm/postman/serializers.py b/itsm/postman/serializers.py index cff2448c5..b3a02034f 100644 --- a/itsm/postman/serializers.py +++ b/itsm/postman/serializers.py @@ -39,14 +39,14 @@ LEN_XX_LONG, PUBLIC_PROJECT_PROJECT_KEY, ) -from itsm.component.drf.serializers import DynamicFieldsModelSerializer +from itsm.component.drf.serializers import DynamicFieldsModelSerializer, BaseModelSerializer from itsm.component.exceptions import ParamError from itsm.component.utils.basic import normal_name, dotted_name from itsm.postman.models import RemoteApi, RemoteApiInstance, RemoteSystem from itsm.workflow.models import Field, State -class RemoteSystemSerializer(serializers.ModelSerializer): +class RemoteSystemSerializer(BaseModelSerializer): """API系统序列化""" name = serializers.CharField( @@ -90,6 +90,7 @@ class Meta: "project_key", ) read_only_fields = ("creator", "updated_by") + create_only_fields = ["project_key"] def to_internal_value(self, data): # 新增系统时,system_id为空 @@ -175,6 +176,7 @@ class Meta: "is_builtin", ) read_only_fields = ("creator", "updated_by") + create_only_fields = ["remote_system"] def to_internal_value(self, data): data = super(RemoteApiSerializer, self).to_internal_value(data) diff --git a/itsm/postman/views.py b/itsm/postman/views.py index 2d0affc00..313c1e862 100644 --- a/itsm/postman/views.py +++ b/itsm/postman/views.py @@ -37,6 +37,7 @@ from itsm.component.constants import ResponseCodeStatus, PUBLIC_PROJECT_PROJECT_KEY from itsm.component.dlls.component import ComponentLibrary from itsm.component.drf import viewsets as component_viewsets +from itsm.component.drf.exception import ValidationError from itsm.component.drf.mixins import DynamicListModelMixin from itsm.component.drf.permissions import IamAuthProjectViewPermit from itsm.component.esb.backend_component import bk @@ -52,6 +53,7 @@ RemoteApiSerializer, RemoteSystemSerializer, ) +from itsm.workflow.permissions import WorkflowElementManagePermission class ModelViewSet(component_viewsets.ModelViewSet): @@ -91,8 +93,13 @@ class RemoteSystemViewSet(ModelViewSet): serializer_class = RemoteSystemSerializer queryset = RemoteSystem.objects.all() - permission_classes = (IamAuthProjectViewPermit,) + permission_classes = (WorkflowElementManagePermission,) + # 平台管理 + permission_action_platform = "public_apis_manage" + # 项目管理 permission_action_default = "system_settings_manage" + permission_free_actions = ["list", "all", "get_systems", "get_components"] + pagination_class = None filter_fields = { @@ -180,7 +187,9 @@ class RemoteApiViewSet(DynamicListModelMixin, ModelViewSet): serializer_class = RemoteApiSerializer queryset = RemoteApi.objects.all() + permission_classes = (RemoteApiPermit,) + permission_free_actions = ["retrieve"] filter_fields = { "is_activated": ["exact"], @@ -244,8 +253,17 @@ def batch_delete(self, request, *args, **kwargs): will_deleted = self.queryset.filter(id__in=id_list) real_deleted = list(will_deleted.values_list("id", flat=True)) - will_deleted.delete() + + # 判断输入的接口实例 + if will_deleted.count() != len(real_deleted): + raise ValidationError(_("接口数量异常,请刷新后重试")) + + # 检测实例所属项目是否一致 + project_keys = [i.remote_system.project_key for i in will_deleted] + if len(set(project_keys)) != 1: + raise ValidationError(_("接口数量异常,请刷新后重试")) + will_deleted.delete() return Response(real_deleted) @action(detail=True, methods=["get"]) diff --git a/itsm/workflow/permissions.py b/itsm/workflow/permissions.py index df8e1b908..368646547 100644 --- a/itsm/workflow/permissions.py +++ b/itsm/workflow/permissions.py @@ -28,6 +28,7 @@ from itsm.component.constants import PUBLIC_PROJECT_PROJECT_KEY from itsm.component.drf.permissions import IamAuthPermit +from itsm.project.models import Project from itsm.role.models import UserRole from itsm.service.models import Service from itsm.workflow.models import Workflow @@ -195,7 +196,11 @@ def has_permission(self, request, view): # 项目管理 apply_actions = self.get_view_iam_actions(view) return self.iam_create_auth(request, apply_actions) - + + # 有配置权限则按无实例进行鉴权 + apply_actions = self.get_view_iam_actions(view) + if apply_actions: + return self.iam_auth(request, apply_actions) return True def has_object_permission(self, request, view, obj, **kwargs): @@ -210,7 +215,8 @@ def has_object_permission(self, request, view, obj, **kwargs): # 项目管理 apply_actions = self.get_view_iam_actions(view) - return self.iam_auth(request, apply_actions, obj) + project = Project.objects.get(pk=obj.project_key) + return self.iam_auth(request, apply_actions, project) class TaskSchemaPermit(IamAuthPermit):