diff --git a/netbox/extras/api/mixins.py b/netbox/extras/api/mixins.py index 72fdef9dd..c96e90d69 100644 --- a/netbox/extras/api/mixins.py +++ b/netbox/extras/api/mixins.py @@ -5,7 +5,7 @@ from rest_framework.response import Response from rest_framework.status import HTTP_400_BAD_REQUEST from dcim.models import Device -from netbox.api.authentication import ViewOnlyPermissions +from netbox.api.authentication import RequireViewOnlyPermissions from netbox.api.renderers import TextRenderer from .serializers import ConfigTemplateSerializer @@ -68,7 +68,7 @@ class RenderConfigMixin(ConfigTemplateRenderMixin): Provides a /render-config/ endpoint for REST API views whose model may have a ConfigTemplate assigned. """ @action(detail=True, methods=['post'], url_path='render-config', renderer_classes=[JSONRenderer, TextRenderer], - permission_classes=[ViewOnlyPermissions]) + permission_classes=[RequireViewOnlyPermissions]) def render_config(self, request, pk): """ Resolve and render the preferred ConfigTemplate for this Device. diff --git a/netbox/netbox/api/authentication.py b/netbox/netbox/api/authentication.py index 45059b4b7..6c8cf49b7 100644 --- a/netbox/netbox/api/authentication.py +++ b/netbox/netbox/api/authentication.py @@ -7,6 +7,7 @@ from rest_framework.permissions import BasePermission, DjangoObjectPermissions, from netbox.config import get_config from users.models import Token +from utilities.permissions import resolve_permission from utilities.request import get_client_ip @@ -140,26 +141,18 @@ class TokenPermissions(DjangoObjectPermissions): permission = self.perms_map.get(method)[0] if len(self.perms_map.get(method)) > 0 else None if permission: # Remove app and model label - action = permission.replace('%(app_label)s.', '').replace('_%(model_name)s', '') - return action - elif action := HTTP_ACTIONS[method]: + action = resolve_permission(permission) return action return None -class ViewOnlyPermissions(TokenPermissions): - """ - Override the stock perm_map to require only view permissions - """ - perms_map = { - 'GET': ['%(app_label)s.view_%(model_name)s'], - 'OPTIONS': [], - 'HEAD': ['%(app_label)s.view_%(model_name)s'], - 'POST': ['%(app_label)s.view_%(model_name)s'], - 'PUT': ['%(app_label)s.view_%(model_name)s'], - 'PATCH': ['%(app_label)s.view_%(model_name)s'], - 'DELETE': ['%(app_label)s.view_%(model_name)s'], - } +class RequireViewOnlyPermissions(TokenPermissions): + + # Only return view as the action + def get_action(self, method): + if method != 'OPTIONS': + return 'view' + return None class IsAuthenticatedOrLoginNotRequired(BasePermission): diff --git a/netbox/netbox/api/viewsets/__init__.py b/netbox/netbox/api/viewsets/__init__.py index 307b5b510..b5bdffa47 100644 --- a/netbox/netbox/api/viewsets/__init__.py +++ b/netbox/netbox/api/viewsets/__init__.py @@ -36,8 +36,6 @@ class BaseViewSet(GenericViewSet): for permission in self.get_permissions(): if hasattr(permission, 'get_action') and (action := permission.get_action(request.method)): self.queryset = self.queryset.restrict(request.user, action) - elif action := HTTP_ACTIONS[request.method]: - self.queryset = self.queryset.restrict(request.user, action) def initialize_request(self, request, *args, **kwargs):