Skip to content

Commit

Permalink
refactor: improve the permission_classes decorator by add more commen…
Browse files Browse the repository at this point in the history
…ts and better naming. (#1481)
  • Loading branch information
piglei authored Jul 19, 2024
1 parent 613d469 commit aedf834
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 88 deletions.
24 changes: 22 additions & 2 deletions apiserver/paasng/paasng/accessories/log/views/legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,37 @@
# to the current version of the project delivered to anyone in the future.

"""这里放由于接口已注册到 APIGW, 不能即时下线的接口"""
from typing import List

from typing import List, Union

from rest_framework.response import Response

from paasng.accessories.log.serializers import LogQueryParamsSLZ
from paasng.accessories.log.views.logs import ModuleStdoutLogAPIView, ModuleStructuredLogAPIView
from paasng.infras.accounts.permissions.application import BaseAppPermission
from paasng.infras.accounts.permissions.constants import SiteAction
from paasng.infras.accounts.permissions.global_site import site_perm_required
from paasng.platform.applications.models import Application
from paasng.platform.modules.models import Module
from paasng.utils.datetime import convert_timestamp_to_str


class AllowAnyActionsOnAllApps(BaseAppPermission):
"""This class pass all permission checks.
Why we need this class:
- Legacy system log API reuse the user log API View which use ApplicationCodeInPathMixin
to get the application and module instances.
- ApplicationCodeInPathMixin requires at least one application permission class
or it won't work.
- The system log API doesn't need any permission check
"""

def has_object_permission(self, request, view, obj: Union[Application, Module]):
return True


class V1StdoutLogAPIView(ModuleStdoutLogAPIView):
# 该接口已注册到 APIGW
# 网关名称 search_standard_log_with_post
Expand Down Expand Up @@ -64,7 +84,7 @@ def query_logs_scroll_with_get(self, request, code, module_name, environment=Non


class V1SysStructuredLogAPIView(ModuleStructuredLogAPIView):
permission_classes: List = []
permission_classes: List = [AllowAnyActionsOnAllApps]

# 该接口已注册到 APIGW
# 网关名称 search_structured_log
Expand Down
5 changes: 2 additions & 3 deletions apiserver/paasng/paasng/accessories/publish/entrance/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@
UpdateExposedURLTypeSLZ,
)
from paasng.accessories.publish.market.utils import ModuleEnvAvailableAddressHelper
from paasng.infras.accounts.permissions.application import application_perm_class
from paasng.infras.accounts.permissions.application import app_action_required, application_perm_class
from paasng.infras.iam.permissions.resources.application import AppAction
from paasng.platform.applications.mixins import ApplicationCodeInPathMixin
from paasng.platform.modules.constants import ExposedURLType
from paasng.platform.modules.helpers import get_module_prod_env_root_domains
from paasng.utils.views import permission_classes as perm_classes


class ExposedURLTypeViewset(viewsets.ViewSet, ApplicationCodeInPathMixin):
Expand Down Expand Up @@ -173,7 +172,7 @@ def list_root_domains(self, request, code, module_name):
).data
)

@perm_classes(permission_classes=[application_perm_class(AppAction.BASIC_DEVELOP)], policy="merge")
@app_action_required(AppAction.BASIC_DEVELOP)
def update_preferred_root_domain(self, request, code, module_name):
"""
更新模块的偏好根域
Expand Down
21 changes: 10 additions & 11 deletions apiserver/paasng/paasng/accessories/servicehub/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from paasng.accessories.servicehub.sharing import ServiceSharingManager, SharingReferencesManager
from paasng.accessories.services.models import ServiceCategory
from paasng.core.region.models import get_all_regions
from paasng.infras.accounts.permissions.application import application_perm_class
from paasng.infras.accounts.permissions.application import app_action_required, application_perm_class
from paasng.infras.accounts.permissions.constants import SiteAction
from paasng.infras.accounts.permissions.global_site import site_perm_class
from paasng.infras.iam.permissions.resources.application import AppAction
Expand All @@ -67,7 +67,6 @@
from paasng.platform.templates.models import Template
from paasng.utils.api_docs import openapi_empty_response
from paasng.utils.error_codes import error_codes
from paasng.utils.views import permission_classes as perm_classes

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -115,7 +114,7 @@ def _get_application_by_code(self, application_code):

@transaction.atomic
@swagger_auto_schema(request_body=slzs.CreateAttachmentSLZ, response_serializer=slzs.ServiceAttachmentSLZ)
@perm_classes([application_perm_class(AppAction.BASIC_DEVELOP)], policy="merge")
@app_action_required(AppAction.BASIC_DEVELOP)
def bind(self, request):
"""创建蓝鲸应用与增强服务的绑定关系"""
serializer = slzs.CreateAttachmentSLZ(data=request.data)
Expand Down Expand Up @@ -155,7 +154,7 @@ def bind(self, request):
)
return Response(serializer.data)

@perm_classes([application_perm_class(AppAction.BASIC_DEVELOP)], policy="merge")
@app_action_required(AppAction.BASIC_DEVELOP)
def retrieve(self, request, code, module_name, service_id):
"""查看应用模块与增强服务的绑定关系详情"""
application = self.get_application()
Expand Down Expand Up @@ -183,7 +182,7 @@ def retrieve(self, request, code, module_name, service_id):
serializer = slzs.ServiceInstanceInfoSLZ(results, many=True)
return Response({"count": len(results), "results": serializer.data})

@perm_classes([application_perm_class(AppAction.BASIC_DEVELOP)], policy="merge")
@app_action_required(AppAction.BASIC_DEVELOP)
def retrieve_specs(self, request, code, module_name, service_id):
"""获取应用已绑定的服务规格"""
application = self.get_application()
Expand Down Expand Up @@ -211,7 +210,7 @@ def retrieve_specs(self, request, code, module_name, service_id):
slz = slzs.ServicePlanSpecificationSLZ(results, many=True)
return Response({"results": slz.data})

@perm_classes([application_perm_class(AppAction.MANAGE_ADDONS_SERVICES)], policy="merge")
@app_action_required(AppAction.MANAGE_ADDONS_SERVICES)
def unbind(self, request, code, module_name, service_id):
"""删除一个服务绑定关系"""
application = self._get_application_by_code(code)
Expand All @@ -236,7 +235,7 @@ def unbind(self, request, code, module_name, service_id):
module_attachment.delete()
return Response(status=status.HTTP_200_OK)

@perm_classes([application_perm_class(AppAction.BASIC_DEVELOP)], policy="merge")
@app_action_required(AppAction.BASIC_DEVELOP)
def list_provisioned_env_keys(self, request, code, module_name):
"""获取已经生效的增强服务环境变量 KEY"""
module = self.get_module_via_path()
Expand Down Expand Up @@ -580,7 +579,7 @@ def get_service(service_id, application):
return mixed_service_mgr.get_or_404(service_id, region=application.region)

@swagger_auto_schema(tags=["增强服务"], response_serializer=MinimalModuleSLZ(many=True))
@perm_classes([application_perm_class(AppAction.BASIC_DEVELOP)], policy="merge")
@app_action_required(AppAction.BASIC_DEVELOP)
def list_shareable(self, request, code, module_name, service_id):
"""查看所有可被共享的模块
Expand All @@ -594,7 +593,7 @@ def list_shareable(self, request, code, module_name, service_id):
@swagger_auto_schema(
tags=["增强服务"], request_body=slzs.CreateSharedAttachmentsSLZ, responses={201: openapi_empty_response}
)
@perm_classes([application_perm_class(AppAction.BASIC_DEVELOP)], policy="merge")
@app_action_required(AppAction.BASIC_DEVELOP)
def create_shared(self, request, code, module_name, service_id):
"""创建增强服务共享关系
Expand Down Expand Up @@ -630,7 +629,7 @@ def create_shared(self, request, code, module_name, service_id):
return Response({}, status.HTTP_201_CREATED)

@swagger_auto_schema(tags=["增强服务"], response_serializer=slzs.SharedServiceInfoSLZ)
@perm_classes([application_perm_class(AppAction.BASIC_DEVELOP)], policy="merge")
@app_action_required(AppAction.BASIC_DEVELOP)
def retrieve(self, request, code, module_name, service_id):
"""查看已创建的共享关系
Expand All @@ -644,7 +643,7 @@ def retrieve(self, request, code, module_name, service_id):
return Response(slzs.SharedServiceInfoSLZ(info).data)

@swagger_auto_schema(tags=["增强服务"], responses={204: openapi_empty_response})
@perm_classes([application_perm_class(AppAction.MANAGE_ADDONS_SERVICES)], policy="merge")
@app_action_required(AppAction.MANAGE_ADDONS_SERVICES)
def destroy(self, request, code, module_name, service_id):
"""解除共享关系
Expand Down
26 changes: 13 additions & 13 deletions apiserver/paasng/paasng/bk_plugins/pluginscenter/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
from paasng.bk_plugins.pluginscenter.thirdparty.members import sync_members
from paasng.utils.api_docs import openapi_empty_schema
from paasng.utils.i18n import to_translated_field
from paasng.utils.views import permission_classes as _permission_classes
from paasng.utils.views import action_perms

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -210,7 +210,7 @@ def filter_queryset(self, queryset):

@atomic
@swagger_auto_schema(request_body=serializers.StubCreatePluginSLZ)
@_permission_classes([IsAuthenticated, PluginCenterFeaturePermission])
@action_perms([IsAuthenticated, PluginCenterFeaturePermission])
def create(self, request, pd_id, **kwargs):
pd = get_object_or_404(PluginDefinition, identifier=pd_id)
slz = serializers.make_plugin_slz_class(pd, creation=True)(data=request.data, context={"pd": pd})
Expand Down Expand Up @@ -259,7 +259,7 @@ def retrieve(self, request, pd_id, plugin_id):

@atomic
@swagger_auto_schema(request_body=serializers.StubUpdatePluginSLZ)
@_permission_classes(
@action_perms(
[
IsAuthenticated,
PluginCenterFeaturePermission,
Expand Down Expand Up @@ -297,7 +297,7 @@ def update(self, request, pd_id, plugin_id):

@atomic
@swagger_auto_schema(request_body=serializers.StubUpdatePluginSLZ)
@_permission_classes(
@action_perms(
[
IsAuthenticated,
PluginCenterFeaturePermission,
Expand Down Expand Up @@ -330,7 +330,7 @@ def update_extra_fields(self, request, pd_id, plugin_id):

@atomic
@swagger_auto_schema(request_body=serializers.PluginPublisher)
@_permission_classes(
@action_perms(
[IsAuthenticated, PluginCenterFeaturePermission, plugin_action_permission_class([Actions.EDIT_PLUGIN])]
)
def update_publisher(self, request, pd_id, plugin_id):
Expand Down Expand Up @@ -359,7 +359,7 @@ def update_publisher(self, request, pd_id, plugin_id):

@atomic
@swagger_auto_schema(request_body=serializers.PluginInstanceLogoSLZ)
@_permission_classes(
@action_perms(
[
IsAuthenticated,
PluginCenterFeaturePermission,
Expand Down Expand Up @@ -388,7 +388,7 @@ def update_logo(self, request, pd_id, plugin_id):
return Response(data=self.get_serializer(plugin).data)

@atomic
@_permission_classes([IsAuthenticated, PluginCenterFeaturePermission, IsPluginCreator])
@action_perms([IsAuthenticated, PluginCenterFeaturePermission, IsPluginCreator])
def destroy(self, request, pd_id, plugin_id):
"""仅创建审批失败的插件可删除"""
plugin = self.get_plugin_instance()
Expand All @@ -401,7 +401,7 @@ def destroy(self, request, pd_id, plugin_id):
return Response(status=status.HTTP_204_NO_CONTENT)

@atomic
@_permission_classes(
@action_perms(
[IsAuthenticated, PluginCenterFeaturePermission, plugin_action_permission_class([Actions.DELETE_PLUGIN])]
)
def archive(self, request, pd_id, plugin_id):
Expand All @@ -424,7 +424,7 @@ def archive(self, request, pd_id, plugin_id):
return Response(status=status.HTTP_204_NO_CONTENT)

@atomic
@_permission_classes(
@action_perms(
[IsAuthenticated, PluginCenterFeaturePermission, plugin_action_permission_class([Actions.DELETE_PLUGIN])]
)
def reactivate(self, request, pd_id, plugin_id):
Expand Down Expand Up @@ -512,7 +512,7 @@ def get_queryset(self):


@method_decorator(
_permission_classes(
action_perms(
[IsAuthenticated, PluginCenterFeaturePermission, plugin_action_permission_class([Actions.BASIC_DEVELOPMENT])]
),
name="list",
Expand Down Expand Up @@ -557,7 +557,7 @@ def get_compare_url(self, request, pd_id, plugin_id, from_revision, to_revision)
request_body=serializers.StubCreatePluginReleaseVersionSLZ,
responses={201: serializers.PluginReleaseVersionSLZ},
)
@_permission_classes(
@action_perms(
[
IsAuthenticated,
PluginCenterFeaturePermission,
Expand Down Expand Up @@ -859,7 +859,7 @@ class PluginMembersViewSet(PluginInstanceMixin, GenericViewSet):
]
serializer_class = serializers.PluginMemberSLZ

@_permission_classes(
@action_perms(
[IsAuthenticated, PluginCenterFeaturePermission, plugin_action_permission_class([Actions.BASIC_DEVELOPMENT])]
)
def list(self, request, pd_id, plugin_id):
Expand All @@ -868,7 +868,7 @@ def list(self, request, pd_id, plugin_id):
return Response(data=self.get_serializer(members, many=True).data)

@swagger_auto_schema(request_body=openapi_empty_schema)
@_permission_classes(
@action_perms(
[IsAuthenticated, PluginCenterFeaturePermission, plugin_action_permission_class([Actions.BASIC_DEVELOPMENT])]
)
def leave(self, request, pd_id, plugin_id):
Expand Down
26 changes: 19 additions & 7 deletions apiserver/paasng/paasng/infras/accounts/permissions/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import logging
import time
from typing import Union
from typing import Type, Union

from django.conf import settings
from iam.exceptions import AuthAPIError
Expand All @@ -29,23 +29,35 @@
from paasng.platform.applications.models import Application
from paasng.platform.modules.models import Module
from paasng.utils.basic import get_username_by_bkpaas_user_id
from paasng.utils.views import action_perms

logger = logging.getLogger(__name__)


def application_perm_class(action: AppAction):
class BaseAppPermission(BasePermission):
"""The base class for application permission. It doesn't provide any functionality
yet but it can be useful for other modules to check the permission type.
"""
构建 DRF 可用的应用权限类

注意:该权限类使用装饰器附加到 viewset 方法时,需要使用 paasng.utils.views.permission_classes 并指定 policy='merge'
原因是 self.get_application() 时, check_object_permissions 用的是 self.get_permissions,会使用类的权限类
而 drf 的装饰器只是修改 func.permission_classes,会导致鉴权失效

def app_action_required(action: AppAction, policy: str = "extend"):
"""A decorator to require the user to have the specified application action permission,
must be used on viewset method.
:param action: Application action type.
:param policy: How to combine the permission classes. Default is "extend".
"""
return action_perms(permission_classes=[application_perm_class(action)], policy=policy)


def application_perm_class(action: AppAction) -> Type[BasePermission]:
"""构建 DRF 可用的应用权限类
:param action: Application operation type.
:return: Application permission class.
"""

class AppModulePermission(BasePermission):
class AppModulePermission(BaseAppPermission):
"""The permission class for application and module."""

def has_object_permission(self, request, view, obj: Union[Application, Module]):
Expand Down
9 changes: 4 additions & 5 deletions apiserver/paasng/paasng/misc/monitoring/monitor/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, ViewSet

from paasng.infras.accounts.permissions.application import application_perm_class
from paasng.infras.accounts.permissions.application import app_action_required, application_perm_class
from paasng.infras.bkmonitorv3.client import make_bk_monitor_client
from paasng.infras.bkmonitorv3.exceptions import BkMonitorGatewayServiceError, BkMonitorSpaceDoesNotExist
from paasng.infras.iam.permissions.resources.application import AppAction
Expand All @@ -36,7 +36,6 @@
from paasng.platform.applications.mixins import ApplicationCodeInPathMixin
from paasng.platform.applications.models import UserApplicationFilter
from paasng.utils.error_codes import error_codes
from paasng.utils.views import permission_classes as perm_classes

from .exceptions import BKMonitorNotSupportedError
from .models import AppAlertRule
Expand Down Expand Up @@ -188,7 +187,7 @@ class AlertRulesView(GenericViewSet, ApplicationCodeInPathMixin):
permission_classes = [IsAuthenticated, application_perm_class(AppAction.VIEW_ALERT_RECORDS)]

@swagger_auto_schema(query_serializer=ListAlertRulesSLZ)
@perm_classes([application_perm_class(AppAction.VIEW_BASIC_INFO)], policy="merge")
@app_action_required(AppAction.VIEW_BASIC_INFO)
def list(self, request, code, module_name):
"""查询告警规则列表"""
serializer = ListAlertRulesSLZ(data=self.request.query_params)
Expand All @@ -212,7 +211,7 @@ def list(self, request, code, module_name):
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

@perm_classes([application_perm_class(AppAction.EDIT_ALERT_POLICY)], policy="merge")
@app_action_required(AppAction.EDIT_ALERT_POLICY)
def update(self, request, code, id):
"""更新告警规则"""
filter_kwargs = {"id": id, "application": self.get_application()}
Expand All @@ -235,7 +234,7 @@ def list_supported_alert_rules(self, request):
serializer = SupportedAlertSLZ(supported_alerts, many=True)
return Response(serializer.data)

@perm_classes([application_perm_class(AppAction.EDIT_ALERT_POLICY)], policy="merge")
@app_action_required(AppAction.EDIT_ALERT_POLICY)
def init_alert_rules(self, request, code):
"""初始化告警规则"""
application = self.get_application()
Expand Down
2 changes: 1 addition & 1 deletion apiserver/paasng/paasng/plat_admin/system/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def provision_service(self, request, code, module_name, environment, service_nam
"""分配增强服务实例"""
application = get_object_or_404(Application, code=code)
module = application.get_module(module_name)
engine_app = module.envs.get(environment=environment).wl_app
engine_app = application.get_engine_app(environment, module_name=module_name)

try:
svc = mixed_service_mgr.find_by_name(name=service_name, region=application.region)
Expand Down
Loading

0 comments on commit aedf834

Please sign in to comment.