diff --git a/netbox/extras/api/mixins.py b/netbox/extras/api/mixins.py index d476287db..72fdef9dd 100644 --- a/netbox/extras/api/mixins.py +++ b/netbox/extras/api/mixins.py @@ -63,12 +63,6 @@ class ConfigTemplateRenderMixin: class RenderConfigMixin(ConfigTemplateRenderMixin): - """ - Override initial() to save a copy of the queryset for "un-restricting" the queryset when rendering. - """ - def initial(self, request, *args, **kwargs): - self.original_queryset = self.queryset - super().initial(request, *args, **kwargs) """ Provides a /render-config/ endpoint for REST API views whose model may have a ConfigTemplate assigned. @@ -79,7 +73,6 @@ class RenderConfigMixin(ConfigTemplateRenderMixin): """ Resolve and render the preferred ConfigTemplate for this Device. """ - self.queryset = self.original_queryset.restrict(request.user, 'view') instance = self.get_object() object_type = instance._meta.model_name configtemplate = instance.get_config_template() diff --git a/netbox/netbox/api/authentication.py b/netbox/netbox/api/authentication.py index e5dc748be..45059b4b7 100644 --- a/netbox/netbox/api/authentication.py +++ b/netbox/netbox/api/authentication.py @@ -10,6 +10,17 @@ from users.models import Token from utilities.request import get_client_ip +HTTP_ACTIONS = { + 'GET': 'view', + 'OPTIONS': None, + 'HEAD': 'view', + 'POST': 'add', + 'PUT': 'change', + 'PATCH': 'change', + 'DELETE': 'delete', +} + + class TokenAuthentication(authentication.TokenAuthentication): """ A custom authentication scheme which enforces Token expiration times and source IP restrictions. @@ -123,6 +134,18 @@ class TokenPermissions(DjangoObjectPermissions): return super().has_object_permission(request, view, obj) + # Helper function to obtain the desired action from the first permission in the permission map + def get_action(self, method): + # Get the first permission + 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]: + return action + return None + class ViewOnlyPermissions(TokenPermissions): """ diff --git a/netbox/netbox/api/viewsets/__init__.py b/netbox/netbox/api/viewsets/__init__.py index d72507e8a..307b5b510 100644 --- a/netbox/netbox/api/viewsets/__init__.py +++ b/netbox/netbox/api/viewsets/__init__.py @@ -19,15 +19,7 @@ __all__ = ( 'NetBoxModelViewSet', ) -HTTP_ACTIONS = { - 'GET': 'view', - 'OPTIONS': None, - 'HEAD': 'view', - 'POST': 'add', - 'PUT': 'change', - 'PATCH': 'change', - 'DELETE': 'delete', -} +from netbox.api.authentication import HTTP_ACTIONS class BaseViewSet(GenericViewSet): @@ -41,8 +33,11 @@ class BaseViewSet(GenericViewSet): # Restrict the view's QuerySet to allow only the permitted objects if request.user.is_authenticated: - if action := HTTP_ACTIONS[request.method]: - self.queryset = self.queryset.restrict(request.user, action) + 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):