diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py index ddb203e0b..bc4f9cd2a 100644 --- a/netbox/users/forms/model_forms.py +++ b/netbox/users/forms/model_forms.py @@ -430,17 +430,20 @@ class ObjectPermissionForm(forms.ModelForm): # Pre-select registered actions selected_registered = [] + consumed_actions = set() for ct in self.instance.object_types.all(): model_key = f'{ct.app_label}.{ct.model}' if model_key in model_actions: for ma in model_actions[model_key]: if ma.name in remaining_actions: selected_registered.append(f'{model_key}.{ma.name}') - remaining_actions.remove(ma.name) + consumed_actions.add(ma.name) self.fields['registered_actions'].initial = selected_registered # Remaining actions go to the additional actions field - self.fields['actions'].initial = remaining_actions + self.initial['actions'] = [ + a for a in remaining_actions if a not in consumed_actions + ] # Populate initial data for a new ObjectPermission elif self.initial: diff --git a/netbox/utilities/tests/test_permissions.py b/netbox/utilities/tests/test_permissions.py index fcfe022b0..c4e123b31 100644 --- a/netbox/utilities/tests/test_permissions.py +++ b/netbox/utilities/tests/test_permissions.py @@ -1,8 +1,12 @@ from django.test import TestCase -from dcim.models import Site +from core.models import ObjectType +from dcim.models import Device, Site from netbox.registry import registry +from users.forms.model_forms import ObjectPermissionForm +from users.models import ObjectPermission from utilities.permissions import ModelAction, register_model_actions +from virtualization.models import VirtualMachine class ModelActionTest(TestCase): @@ -75,3 +79,37 @@ class RegisterModelActionsTest(TestCase): self.assertEqual(len(actions), 2) self.assertEqual(actions[0].name, 'first') self.assertEqual(actions[1].name, 'second') + + +class ObjectPermissionFormTest(TestCase): + + def setUp(self): + self.original_actions = dict(registry['model_actions']) + + def tearDown(self): + registry['model_actions'].clear() + registry['model_actions'].update(self.original_actions) + + def test_shared_action_preselection(self): + register_model_actions(Device, [ModelAction('render_config')]) + register_model_actions(VirtualMachine, [ModelAction('render_config')]) + + device_ct = ObjectType.objects.get_for_model(Device) + vm_ct = ObjectType.objects.get_for_model(VirtualMachine) + + permission = ObjectPermission.objects.create( + name='Test Permission', + actions=['view', 'render_config'], + ) + permission.object_types.set([device_ct, vm_ct]) + + form = ObjectPermissionForm(instance=permission) + + initial = form.fields['registered_actions'].initial + self.assertIn('dcim.device.render_config', initial) + self.assertIn('virtualization.virtualmachine.render_config', initial) + + # Should not leak into the additional actions field + self.assertEqual(form.initial['actions'], []) + + permission.delete()