Przeglądaj źródła

Fixes #20929: Require render_config permission for UI config rendering (#20975)

* Closes #20929: Require render_config permission for UI config rendering

- Modified `ObjectRenderConfigView.has_permission()` to require both view and render_config permissions
- Added `remove_permissions()` test helper to remove permissions from existing ObjectPermission objects
- Added regression tests for Device and VirtualMachine render-config permission enforcement

The `render_config` permission action was introduced in #16681 for API endpoints. This extends PR_7604_description
to the UI render-config tabs, preventing users from viewing rendered configurations without explicit permission.

* Address PR feedback

* Address PR feedback
Jason Novinger 1 miesiąc temu
rodzic
commit
a364ee832d

+ 23 - 0
netbox/dcim/tests/test_views.py

@@ -11,6 +11,7 @@ from core.models import ObjectType
 from dcim.choices import *
 from dcim.constants import *
 from dcim.models import *
+from extras.models import ConfigTemplate
 from ipam.models import ASN, RIR, VLAN, VRF
 from netbox.choices import CSVDelimiterChoices, ImportFormatChoices, WeightUnitChoices
 from tenancy.models import Tenant
@@ -2339,6 +2340,28 @@ class DeviceTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         url = reverse('dcim:device_inventory', kwargs={'pk': device.pk})
         self.assertHttpStatus(self.client.get(url), 200)
 
+    def test_device_renderconfig(self):
+        configtemplate = ConfigTemplate.objects.create(
+            name='Test Config Template',
+            template_code='Config for device {{ device.name }}'
+        )
+        device = Device.objects.first()
+        device.config_template = configtemplate
+        device.save()
+        url = reverse('dcim:device_render-config', kwargs={'pk': device.pk})
+
+        # User with only view permission should NOT be able to render config
+        self.add_permissions('dcim.view_device')
+        self.assertHttpStatus(self.client.get(url), 403)
+
+        # With render_config permission added should be able to render config
+        self.add_permissions('dcim.render_config_device')
+        self.assertHttpStatus(self.client.get(url), 200)
+
+        # With view permission removed should NOT be able to render config
+        self.remove_permissions('dcim.view_device')
+        self.assertHttpStatus(self.client.get(url), 403)
+
 
 class ModuleTestCase(
     # Module does not support bulk renaming (no name field) or

+ 1 - 0
netbox/dcim/views.py

@@ -2682,6 +2682,7 @@ class DeviceConfigContextView(ObjectConfigContextView):
 class DeviceRenderConfigView(ObjectRenderConfigView):
     queryset = Device.objects.all()
     base_template = 'dcim/device/base.html'
+    additional_permissions = ['dcim.render_config_device']
     tab = ViewTab(
         label=_('Render Config'),
         weight=2100,

+ 10 - 0
netbox/utilities/testing/base.py

@@ -67,6 +67,16 @@ class TestCase(_TestCase):
             obj_perm.users.add(self.user)
             obj_perm.object_types.add(object_type)
 
+    def remove_permissions(self, *names):
+        """
+        Remove a set of permissions from the test user. Accepts permission names in the form <app>.<action>_<model>.
+        """
+        for name in names:
+            object_type, action = resolve_permission_type(name)
+            ObjectPermission.objects.filter(
+                actions__contains=[action], object_types=object_type, users=self.user
+            ).delete()
+
     #
     # Custom assertions
     #

+ 23 - 0
netbox/virtualization/tests/test_views.py

@@ -4,6 +4,7 @@ from django.urls import reverse
 
 from dcim.choices import InterfaceModeChoices
 from dcim.models import DeviceRole, Platform, Site
+from extras.models import ConfigTemplate
 from ipam.models import VLAN, VRF
 from utilities.testing import ViewTestCases, create_tags, create_test_device, create_test_virtualmachine
 from virtualization.choices import *
@@ -326,6 +327,28 @@ class VirtualMachineTestCase(ViewTestCases.PrimaryObjectViewTestCase):
         url = reverse('virtualization:virtualmachine_interfaces', kwargs={'pk': virtualmachine.pk})
         self.assertHttpStatus(self.client.get(url), 200)
 
+    def test_virtualmachine_renderconfig(self):
+        configtemplate = ConfigTemplate.objects.create(
+            name='Test Config Template',
+            template_code='Config for VM {{ virtualmachine.name }}'
+        )
+        vm = VirtualMachine.objects.first()
+        vm.config_template = configtemplate
+        vm.save()
+        url = reverse('virtualization:virtualmachine_render-config', kwargs={'pk': vm.pk})
+
+        # User with only view permission should NOT be able to render config
+        self.add_permissions('virtualization.view_virtualmachine')
+        self.assertHttpStatus(self.client.get(url), 403)
+
+        # With render_config permission added should be able to render config
+        self.add_permissions('virtualization.render_config_virtualmachine')
+        self.assertHttpStatus(self.client.get(url), 200)
+
+        # With view permission removed should NOT be able to render config
+        self.remove_permissions('virtualization.view_virtualmachine')
+        self.assertHttpStatus(self.client.get(url), 403)
+
 
 class VMInterfaceTestCase(ViewTestCases.DeviceComponentViewTestCase):
     model = VMInterface

+ 1 - 0
netbox/virtualization/views.py

@@ -405,6 +405,7 @@ class VirtualMachineConfigContextView(ObjectConfigContextView):
 class VirtualMachineRenderConfigView(ObjectRenderConfigView):
     queryset = VirtualMachine.objects.all()
     base_template = 'virtualization/virtualmachine/base.html'
+    additional_permissions = ['virtualization.render_config_virtualmachine']
     tab = ViewTab(
         label=_('Render Config'),
         weight=2100,