diff --git a/docs/features/configuration-rendering.md b/docs/features/configuration-rendering.md index db3cc3205..16384b81c 100644 --- a/docs/features/configuration-rendering.md +++ b/docs/features/configuration-rendering.md @@ -92,8 +92,8 @@ http://netbox:8000/api/extras/config-templates/123/render/ \ ``` !!! note "Permissions" - Rendering configuration templates via the REST API requires the `render_config` permission for the relevant object type: + Rendering configuration templates via the REST API requires appropriate permissions for the relevant object type: * To render a device's configuration via `/api/dcim/devices/{id}/render-config/`, assign a permission for "DCIM > Device" with the `render_config` action * To render a virtual machine's configuration via `/api/virtualization/virtual-machines/{id}/render-config/`, assign a permission for "Virtualization > Virtual Machine" with the `render_config` action - * To render a config template directly via `/api/extras/config-templates/{id}/render/`, assign a permission for "Extras > Config Template" with the `render_config` action + * To render a config template directly via `/api/extras/config-templates/{id}/render/`, assign a permission for "Extras > Config Template" with the `render` action diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py index e2e5b9070..8b6cac98d 100644 --- a/netbox/dcim/tests/test_api.py +++ b/netbox/dcim/tests/test_api.py @@ -1306,7 +1306,6 @@ class DeviceTest(APIViewTestCases.APIViewTestCase): } user_permissions = ( 'dcim.view_site', 'dcim.view_rack', 'dcim.view_location', 'dcim.view_devicerole', 'dcim.view_devicetype', - 'extras.view_configtemplate', ) @classmethod @@ -1486,7 +1485,7 @@ class DeviceTest(APIViewTestCases.APIViewTestCase): device.config_template = configtemplate device.save() - self.add_permissions('dcim.render_config_device') + self.add_permissions('dcim.render_config_device', 'dcim.view_device', 'extras.view_configtemplate') url = reverse('dcim-api:device-detail', kwargs={'pk': device.pk}) + 'render-config/' response = self.client.post(url, {}, format='json', **self.header) self.assertHttpStatus(response, status.HTTP_200_OK) diff --git a/netbox/extras/api/mixins.py b/netbox/extras/api/mixins.py index c49bf0252..0716788f7 100644 --- a/netbox/extras/api/mixins.py +++ b/netbox/extras/api/mixins.py @@ -8,7 +8,6 @@ from rest_framework.status import HTTP_400_BAD_REQUEST from netbox.api.authentication import TokenWritePermission from netbox.api.renderers import TextRenderer -from utilities.permissions import get_permission_for_model from .serializers import ConfigTemplateSerializer __all__ = ( @@ -80,14 +79,11 @@ class RenderConfigMixin(ConfigTemplateRenderMixin): """ Resolve and render the preferred ConfigTemplate for this Device. """ - self.queryset = self.queryset.model.objects.all().restrict(request.user, 'render_config') + self.queryset = self.queryset.model.objects.restrict(request.user, 'render_config').restrict( + request.user, 'view' + ) instance = self.get_object() - # Check render_config permission - perm = get_permission_for_model(instance, 'render_config') - if not request.user.has_perm(perm, obj=instance): - raise PermissionDenied(_("This user does not have permission to render configurations for this object.")) - object_type = instance._meta.model_name configtemplate = instance.get_config_template() if not configtemplate: @@ -95,6 +91,10 @@ class RenderConfigMixin(ConfigTemplateRenderMixin): 'error': f'No config template found for this {object_type}.' }, status=HTTP_400_BAD_REQUEST) + # Check view permission for ConfigTemplate + if not request.user.has_perm('extras.view_configtemplate', obj=configtemplate): + raise PermissionDenied(_("This user does not have permission to view this configuration template.")) + # Compile context data context_data = instance.get_config_context() context_data.update(request.data) diff --git a/netbox/extras/api/views.py b/netbox/extras/api/views.py index 9c8b6ca45..defb8c267 100644 --- a/netbox/extras/api/views.py +++ b/netbox/extras/api/views.py @@ -1,6 +1,5 @@ from django.http import Http404 from django.shortcuts import get_object_or_404 -from django.utils.translation import gettext_lazy as _ from django_rq.queues import get_connection from drf_spectacular.utils import extend_schema, extend_schema_view from rest_framework import status @@ -23,7 +22,6 @@ from netbox.api.metadata import ContentTypeMetadata from netbox.api.renderers import TextRenderer from netbox.api.viewsets import BaseViewSet, NetBoxModelViewSet from utilities.exceptions import RQWorkerNotRunningException -from utilities.permissions import get_permission_for_model from utilities.request import copy_safe_request from . import serializers from .mixins import ConfigTemplateRenderMixin @@ -252,14 +250,9 @@ class ConfigTemplateViewSet(SyncedDataMixin, ConfigTemplateRenderMixin, NetBoxMo Render a ConfigTemplate using the context data provided (if any). If the client requests "text/plain" data, return the raw rendered content, rather than serialized JSON. """ - self.queryset = self.queryset.model.objects.all().restrict(request.user, 'render_config') + self.queryset = self.queryset.model.objects.restrict(request.user, 'render').restrict(request.user, 'view') configtemplate = self.get_object() - # Check render_config permission - perm = get_permission_for_model(configtemplate, 'render_config') - if not request.user.has_perm(perm, obj=configtemplate): - raise PermissionDenied(_("This user does not have permission to render configuration templates.")) - context = request.data return self.render_configtemplate(request, configtemplate, context) diff --git a/netbox/extras/tests/test_api.py b/netbox/extras/tests/test_api.py index 70dfa96d9..ea55668ca 100644 --- a/netbox/extras/tests/test_api.py +++ b/netbox/extras/tests/test_api.py @@ -858,7 +858,7 @@ class ConfigTemplateTest(APIViewTestCases.APIViewTestCase): def test_render(self): configtemplate = ConfigTemplate.objects.first() - self.add_permissions('extras.render_config_configtemplate') + self.add_permissions('extras.render_configtemplate', 'extras.view_configtemplate') url = reverse('extras-api:configtemplate-detail', kwargs={'pk': configtemplate.pk}) + 'render/' response = self.client.post(url, {'foo': 'bar'}, format='json', **self.header) self.assertHttpStatus(response, status.HTTP_200_OK) @@ -867,7 +867,7 @@ class ConfigTemplateTest(APIViewTestCases.APIViewTestCase): def test_render_without_permission(self): configtemplate = ConfigTemplate.objects.first() - # No permissions added - user has no render_config permission + # No permissions added - user has no render permission url = reverse('extras-api:configtemplate-detail', kwargs={'pk': configtemplate.pk}) + 'render/' response = self.client.post(url, {'foo': 'bar'}, format='json', **self.header) self.assertHttpStatus(response, status.HTTP_404_NOT_FOUND) diff --git a/netbox/virtualization/tests/test_api.py b/netbox/virtualization/tests/test_api.py index 0e9f45eb8..4c4d9be4d 100644 --- a/netbox/virtualization/tests/test_api.py +++ b/netbox/virtualization/tests/test_api.py @@ -281,7 +281,10 @@ class VirtualMachineTest(APIViewTestCases.APIViewTestCase): vm.config_template = configtemplate vm.save() - self.add_permissions('virtualization.render_config_virtualmachine') + self.add_permissions( + 'virtualization.render_config_virtualmachine', 'virtualization.view_virtualmachine', + 'extras.view_configtemplate' + ) url = reverse('virtualization-api:virtualmachine-detail', kwargs={'pk': vm.pk}) + 'render-config/' response = self.client.post(url, {}, format='json', **self.header) self.assertHttpStatus(response, status.HTTP_200_OK)