From 3ef4287d57b95462ff63f2cfb0eb8d0fcf4b8c8a Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 22 May 2020 12:41:20 -0400 Subject: [PATCH] Add additional_permissions to ObjectPermissionRequiredMixin --- netbox/dcim/views.py | 12 +++++------- netbox/ipam/views.py | 4 +--- netbox/utilities/views.py | 16 ++++++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 3c0010859..2dfe0f207 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -1082,7 +1082,7 @@ class DeviceInventoryView(ObjectView): class DeviceStatusView(ObjectView): - permission_required = ('dcim.view_device', 'dcim.napalm_read') + additional_permissions = ['dcim.napalm_read'] queryset = Device.objects.all() def get(self, request, pk): @@ -1096,7 +1096,7 @@ class DeviceStatusView(ObjectView): class DeviceLLDPNeighborsView(ObjectView): - permission_required = ('dcim.view_device', 'dcim.napalm_read') + additional_permissions = ['dcim.napalm_read'] queryset = Device.objects.all() def get(self, request, pk): @@ -1114,7 +1114,7 @@ class DeviceLLDPNeighborsView(ObjectView): class DeviceConfigView(ObjectView): - permission_required = ('dcim.view_device', 'dcim.napalm_read') + additional_permissions = ['dcim.napalm_read'] queryset = Device.objects.all() def get(self, request, pk): @@ -1857,11 +1857,11 @@ class CableView(ObjectView): }) -class CableTraceView(ObjectPermissionRequiredMixin, View): +class CableTraceView(ObjectView): """ Trace a cable path beginning from the given termination. """ - permission_required = 'dcim.view_cable' + additional_permissions = ['dcim.view_cable'] def dispatch(self, request, *args, **kwargs): model = kwargs.pop('model') @@ -2006,7 +2006,6 @@ class CableBulkDeleteView(BulkDeleteView): # class ConsoleConnectionsListView(ObjectListView): - permission_required = ('dcim.view_consoleport', 'dcim.view_consoleserverport') queryset = ConsolePort.objects.prefetch_related( 'device', 'connected_endpoint__device' ).filter( @@ -2038,7 +2037,6 @@ class ConsoleConnectionsListView(ObjectListView): class PowerConnectionsListView(ObjectListView): - permission_required = ('dcim.view_powerport', 'dcim.view_poweroutlet') queryset = PowerPort.objects.prefetch_related( 'device', '_connected_poweroutlet__device' ).filter( diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index 14c6a6864..d3b604be6 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -671,7 +671,7 @@ class IPAddressEditView(ObjectEditView): return obj -class IPAddressAssignView(ObjectPermissionRequiredMixin, View): +class IPAddressAssignView(ObjectView): """ Search for IPAddresses to be assigned to an Interface. """ @@ -719,7 +719,6 @@ class IPAddressDeleteView(ObjectDeleteView): class IPAddressBulkCreateView(BulkCreateView): - permission_required = 'ipam.add_ipaddress' form = forms.IPAddressBulkCreateForm model_form = forms.IPAddressBulkAddForm pattern_target = 'address' @@ -761,7 +760,6 @@ class VLANGroupListView(ObjectListView): class VLANGroupEditView(ObjectEditView): - permission_required = 'ipam.add_vlangroup' queryset = VLANGroup.objects.all() model_form = forms.VLANGroupForm default_return_url = 'ipam:vlangroup_list' diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 87f63678a..b586342e1 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -43,18 +43,24 @@ class ObjectPermissionRequiredMixin(AccessMixin): Similar to Django's built-in PermissionRequiredMixin, but extended to check for both model-level and object-level permission assignments. If the user has only object-level permissions assigned, the view's queryset is filtered to return only those objects on which the user is permitted to perform the specified action. + + additional_permissions: An optional iterable of statically declared permissions to evaluate in addition to those + derived from the object type """ - permission_required = None + additional_permissions = list() def get_required_permission(self): - return self.permission_required + """ + Return the specific permission necessary to perform the requested action on an object. + """ + raise NotImplementedError(f"{self.__class__.__name__} must implement get_required_permission()") def has_permission(self): user = self.request.user permission_required = self.get_required_permission() - # First, check that the user is granted the required permission at either the model or object level. - if not user.has_perm(permission_required): + # First, check that the user is granted the required permission(s) at either the model or object level. + if not user.has_perms((permission_required, *self.additional_permissions)): return False # Superusers implicitly have all permissions @@ -148,8 +154,6 @@ class ObjectListView(ObjectPermissionRequiredMixin, View): action_buttons = ('add', 'import', 'export') def get_required_permission(self): - if getattr(self, 'permission_required') is not None: - return self.permission_required return get_permission_for_model(self.queryset.model, 'view') def queryset_to_yaml(self):