Skip to content

Commit

Permalink
fix(open api): add request_from_gateway_required for all open apis (#648
Browse files Browse the repository at this point in the history
)
  • Loading branch information
wklken authored May 20, 2024
1 parent d419e90 commit 05492e9
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@


class ComponentViewSet(viewsets.GenericViewSet):
request_from_gateway_required = True
serializer_class = serializers.AppPermissionComponentSLZ

@swagger_auto_schema(
Expand All @@ -62,6 +63,7 @@ def list(self, request, system_id: int, *args, **kwargs):


class AppPermissionApplyV1APIView(viewsets.GenericViewSet):
request_from_gateway_required = True
serializer_class = serializers.AppPermissionApplySLZ

@transaction.atomic
Expand Down Expand Up @@ -100,6 +102,7 @@ class AppPermissionRenewAPIView(viewsets.GenericViewSet):
权限续期
"""

request_from_gateway_required = True
serializer_class = serializers.AppPermissionRenewSLZ

def renew(self, request, *args, **kwargs):
Expand All @@ -119,6 +122,8 @@ def renew(self, request, *args, **kwargs):


class AppPermissionViewSet(viewsets.ViewSet):
request_from_gateway_required = True

def list(self, request, *args, **kwargs):
"""已申请权限列表"""
slz = serializers.AppPermissionQuerySLZ(data=request.query_params)
Expand All @@ -137,6 +142,7 @@ def list(self, request, *args, **kwargs):


class AppPermissionApplyRecordViewSet(viewsets.GenericViewSet):
request_from_gateway_required = True
serializer_class = serializers.AppPermissionApplyRecordQuerySLZ

def list(self, request, *args, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

class SystemViewSet(viewsets.GenericViewSet):
gateway_permission_exempt = True
request_from_gateway_required = True

def _filter_active_and_public_systems(self, boards: List[str]):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
)
class GatewayListApi(generics.ListAPIView):
serializer_class = serializers.GatewayListV1OutputSLZ
request_from_gateway_required = True

def get_queryset(self):
return Gateway.objects.all()
Expand Down Expand Up @@ -130,6 +131,7 @@ def _filter_list_queryset(
),
)
class GatewayRetrieveApi(generics.RetrieveAPIView):
request_from_gateway_required = True
serializer_class = serializers.GatewayRetrieveV1OutputSLZ
lookup_field = "id"

Expand Down Expand Up @@ -159,8 +161,8 @@ def get(self, request, gateway_name: str, *args, **kwargs):


class GatewaySyncApi(generics.CreateAPIView):
serializer_class = serializers.GatewaySyncInputSLZ
permission_classes = [GatewayRelatedAppPermission]
serializer_class = serializers.GatewaySyncInputSLZ
allow_gateway_not_exist = True

@swagger_auto_schema(request_body=serializers.GatewaySyncInputSLZ, tags=["OpenAPI.Gateway"])
Expand Down Expand Up @@ -208,8 +210,8 @@ def post(self, request, gateway_name: str, *args, **kwargs):


class GatewayUpdateStatusApi(generics.CreateAPIView):
serializer_class = serializers.GatewayUpdateStatusInputSLZ
permission_classes = [GatewayRelatedAppPermission]
serializer_class = serializers.GatewayUpdateStatusInputSLZ

@swagger_auto_schema(request_body=serializers.GatewayUpdateStatusInputSLZ, tags=["OpenAPI.Gateway"])
def post(self, request, *args, **kwargs):
Expand All @@ -234,8 +236,8 @@ def post(self, request, *args, **kwargs):


class GatewayRelatedAppAddApi(generics.CreateAPIView):
serializer_class = serializers.GatewayRelatedAppsAddInputSLZ
permission_classes = [GatewayRelatedAppPermission]
serializer_class = serializers.GatewayRelatedAppsAddInputSLZ

@swagger_auto_schema(request_body=serializers.GatewayRelatedAppsAddInputSLZ, tags=["OpenAPI.Gateway"])
@transaction.atomic
Expand Down
29 changes: 18 additions & 11 deletions src/dashboard/apigateway/apigateway/apis/open/permission/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def get_permission_model(dimension: str):

class ResourceViewSet(viewsets.ViewSet):
gateway_permission_exempt = True
request_from_gateway_required = True

@swagger_auto_schema(
query_serializer=serializers.AppResourcePermissionInputSLZ,
Expand Down Expand Up @@ -97,8 +98,9 @@ def list(self, request, *args, **kwargs):


class AppGatewayPermissionViewSet(viewsets.GenericViewSet):
serializer_class = serializers.AppGatewayPermissionInputSLZ
gateway_permission_exempt = True
request_from_gateway_required = True
serializer_class = serializers.AppGatewayPermissionInputSLZ

def allow_apply_by_gateway(self, request, *args, **kwargs):
slz = self.get_serializer(data=request.query_params)
Expand All @@ -117,7 +119,7 @@ def allow_apply_by_gateway(self, request, *args, **kwargs):
)


class BaseAppPermissinApplyAPIView(APIView, metaclass=ABCMeta):
class BaseAppPermissionApplyAPIView(APIView, metaclass=ABCMeta):
@abstractmethod
def get_serializer_class(self):
pass
Expand Down Expand Up @@ -161,27 +163,28 @@ def post(self, request, *args, **kwargs):
)


class PaaSAppPermissionApplyAPIView(BaseAppPermissinApplyAPIView):
class PaaSAppPermissionApplyAPIView(BaseAppPermissionApplyAPIView):
"""
PaaS中应用申请访问网关API的权限
PaaS 中应用申请访问网关 API 的权限
- 提供给 paas3 开发者中心的接口
"""

gateway_permission_exempt = True
request_from_gateway_required = True

def get_serializer_class(self):
return serializers.PaaSAppPermissionApplyInputSLZ


class AppPermissionApplyV1APIView(BaseAppPermissinApplyAPIView):
class AppPermissionApplyV1APIView(BaseAppPermissionApplyAPIView):
"""
普通应用直接申请访问网关API的权限
普通应用直接申请访问网关 API 的权限
- 提供给普通应用的接口
- 支持应用,申请网关API的权限
- 支持应用,申请网关 API 的权限
- 暂支持按网关申请,不支持按资源申请
"""

# 使用 GatewayRelatedAppPermission 中设置request.gateway 的功能,而不需要校验权限
# 使用 GatewayRelatedAppPermission 中设置 request.gateway 的功能,而不需要校验权限
permission_classes = [GatewayRelatedAppPermission]
gateway_permission_exempt = True

Expand All @@ -190,7 +193,7 @@ def get_serializer_class(self):


class AppPermissionGrantViewSet(viewsets.ViewSet):
"""网关关联应用,主动为应用授权访问网关API的权限"""
"""网关关联应用,主动为应用授权访问网关 API 的权限"""

permission_classes = [GatewayRelatedAppPermission]

Expand Down Expand Up @@ -219,7 +222,7 @@ def grant(self, request, *args, **kwargs):


class RevokeAppPermissionViewSet(viewsets.ViewSet):
"""网关关联应用,回收应用访问网关API的权限"""
"""网关关联应用,回收应用访问网关 API 的权限"""

permission_classes = [GatewayRelatedAppPermission]

Expand All @@ -244,6 +247,7 @@ class AppPermissionRenewAPIView(APIView):
"""

gateway_permission_exempt = True
request_from_gateway_required = True

def post(self, request, *args, **kwargs):
slz = serializers.AppPermissionRenewInputSLZ(
Expand All @@ -258,7 +262,7 @@ def post(self, request, *args, **kwargs):

for gateway_id, resource_ids in ResourceHandler.group_by_gateway_id(data["resource_ids"]).items():
gateway = Gateway.objects.get(id=gateway_id)
# 如果应用-资源权限不存在,则将按网关的权限同步到应用-资源权限
# 如果应用 - 资源权限不存在,则将按网关的权限同步到应用 - 资源权限
AppResourcePermission.objects.sync_from_gateway_permission(
gateway=gateway,
bk_app_code=data["target_app_code"],
Expand All @@ -277,6 +281,8 @@ def post(self, request, *args, **kwargs):


class AppPermissionViewSet(viewsets.ViewSet):
request_from_gateway_required = True

def list(self, request, *args, **kwargs):
"""已申请权限列表"""
slz = serializers.AppPermissionInputSLZ(data=request.query_params)
Expand All @@ -290,6 +296,7 @@ def list(self, request, *args, **kwargs):


class AppPermissionRecordViewSet(viewsets.GenericViewSet):
request_from_gateway_required = True
serializer_class = serializers.AppPermissionRecordInputSLZ

def list(self, request, *args, **kwargs):
Expand Down
10 changes: 7 additions & 3 deletions src/dashboard/apigateway/apigateway/apis/open/released/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@
),
)
class ReleasedResourceRetrieveApi(generics.RetrieveAPIView):
gateway_permission_exempt = True
request_from_gateway_required = True

serializer_class = serializers.ReleasedResourceOutputSLZ
lookup_field = "id"
gateway_permission_exempt = True

def get_queryset(self):
return ReleasedResource.objects.filter(gateway=self.request.gateway)
Expand Down Expand Up @@ -82,8 +84,9 @@ def retrieve(self, request, *args, **kwargs):
),
)
class ReleasedResourceListApi(generics.ListAPIView):
lookup_field = "id"
gateway_permission_exempt = True
request_from_gateway_required = True
lookup_field = "id"

def get_queryset(self):
return ReleasedResource.objects.filter(gateway=self.request.gateway)
Expand Down Expand Up @@ -117,8 +120,9 @@ def list(self, request, stage_name: Optional[str] = None, *args, **kwargs):
),
)
class ReleasedResourceListByGatewayNameApi(generics.ListAPIView):
lookup_field = "id"
gateway_permission_exempt = True
request_from_gateway_required = True
lookup_field = "id"

permission_classes = [GatewayRelatedAppPermission]

Expand Down
4 changes: 3 additions & 1 deletion src/dashboard/apigateway/apigateway/apis/open/stage/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@


class StageViewSet(viewsets.ModelViewSet):
gateway_permission_exempt = True
request_from_gateway_required = True

serializer_class = serializers.StageV1SLZ
lookup_field = "id"
gateway_permission_exempt = True

def get_queryset(self):
return Stage.objects.filter(gateway=self.request.gateway)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@


class APISDKV1ViewSet(viewsets.ModelViewSet):
gateway_permission_exempt = True
request_from_gateway_required = True
serializer_class = serializers.APISDKV1SLZ
lookup_field = "id"
gateway_permission_exempt = True

def get_queryset(self):
return GatewaySDK.objects.all()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,13 @@ class GatewayPermission(permissions.BasePermission):
message = gettext_lazy("当前用户无访问网关权限")

def has_permission(self, request, view):
# openapi 的请求来源必须是网关,此时经过网关的中间件(所有都开启了应用认证), 请求中会注入 app 对象
if getattr(view, "request_from_gateway_required", False) and not hasattr(request, "app"):
return False

gateway_obj = self.get_gateway_object(view)

# FIXME: 可能的越权,待重构 open api 之后,确认剩下的逻辑哪里有需要这个的
# 路径参数 gateway_id 不存在,不需要校验网关权限
if not gateway_obj:
return True
Expand Down Expand Up @@ -71,9 +76,13 @@ class GatewayRelatedAppPermission(permissions.BasePermission):
message = gettext_lazy("应用无操作网关权限")

def has_permission(self, request, view):
gateway_obj = self.get_gateway_object(view)
# openapi 的请求来源必须是网关,此时经过网关的中间件(所有都开启了应用认证), 请求中会注入 app 对象
if not hasattr(request, "app"):
return False

# 通过 view 属性 allow_gateway_not_exist,控制是否允许网关为 None
gateway_obj = self.get_gateway_object(view)
# NOTE: only for GatewaySyncApi /<slug:gateway_name>/sync/, at that time, the gateway_obj is None
# should be refactored in the future 新版 openapi 不要这么设计了
if not gateway_obj and getattr(view, "allow_gateway_not_exist", False):
return True

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ stage:
grant_permissions:
- bk_app_code: visual-layout
- bk_app_code: bk_lesscode
- bk_app_code: {{ settings.BK_APP_CODE }}

resource_docs:
basedir: "{{ settings.BASE_DIR }}/data/apidocs/"
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# to the current version of the project delivered to anyone in the future.
#
import math
from unittest import mock

import pytest
from ddf import G
Expand Down Expand Up @@ -63,6 +64,7 @@ def test_list(self, mocker, request_factory):
}

request = request_factory.get("/", data=params)
request.app = mock.MagicMock(app_code="test")

view = views.ComponentViewSet.as_view({"get": "list"})
response = view(request, system_id=1)
Expand Down Expand Up @@ -107,6 +109,7 @@ def test_apply(self, settings, mocker, request_factory, unique_id):
}

request = request_factory.post("", data=params)
request.app = mock.MagicMock(app_code="test")

view = views.AppPermissionApplyV1APIView.as_view({"post": "apply"})
response = view(request, system_id=system.id)
Expand Down Expand Up @@ -155,6 +158,7 @@ def test_list(self, mocker, request_factory):
}

request = request_factory.get("/", data=params)
request.app = mock.MagicMock(app_code="test")

view = views.AppPermissionViewSet.as_view({"get": "list"})
response = view(request)
Expand Down Expand Up @@ -207,6 +211,7 @@ def test_list(self, mocker, request_factory, unique_id):
)

request = request_factory.get("/backend/api/v1/", data=params)
request.app = mock.MagicMock(app_code="test")

view = views.AppPermissionApplyRecordViewSet.as_view({"get": "list"})
response = view(request)
Expand Down Expand Up @@ -247,6 +252,7 @@ def test_retrieve(self, mocker, request_factory, unique_id):
)

request = request_factory.get("/", data=params)
request.app = mock.MagicMock(app_code="test")

view = views.AppPermissionApplyRecordViewSet.as_view({"get": "retrieve"})
response = view(request, record.id)
Expand Down
Loading

0 comments on commit 05492e9

Please sign in to comment.