From 993ee8c900a45b433e20dee8d3751f42992bf7f8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 21 May 2020 13:22:09 -0400 Subject: [PATCH] Transition ObjectListView to use ObjectPermissionRequiredMixin --- netbox/circuits/views.py | 9 ++-- netbox/dcim/views.py | 79 ++++++++++++---------------------- netbox/extras/views.py | 9 ++-- netbox/ipam/views.py | 27 ++++-------- netbox/secrets/views.py | 6 +-- netbox/tenancy/views.py | 6 +-- netbox/utilities/views.py | 33 ++++++++------ netbox/virtualization/views.py | 12 ++---- 8 files changed, 69 insertions(+), 112 deletions(-) diff --git a/netbox/circuits/views.py b/netbox/circuits/views.py index c3b09f596..e3f347398 100644 --- a/netbox/circuits/views.py +++ b/netbox/circuits/views.py @@ -23,8 +23,7 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider # Providers # -class ProviderListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'circuits.view_provider' +class ProviderListView(ObjectListView): queryset = Provider.objects.annotate(count_circuits=Count('circuits')) filterset = filters.ProviderFilterSet filterset_form = forms.ProviderFilterForm @@ -107,8 +106,7 @@ class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Circuit Types # -class CircuitTypeListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'circuits.view_circuittype' +class CircuitTypeListView(ObjectListView): queryset = CircuitType.objects.annotate(circuit_count=Count('circuits')) table = tables.CircuitTypeTable @@ -143,8 +141,7 @@ class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Circuits # -class CircuitListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'circuits.view_circuit' +class CircuitListView(ObjectListView): _terminations = CircuitTermination.objects.filter(circuit=OuterRef('pk')) queryset = Circuit.objects.prefetch_related( 'provider', 'type', 'tenant', 'terminations__site' diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index 2bcf876c6..9faad490e 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -141,8 +141,7 @@ class BulkDisconnectView(GetReturnURLMixin, View): # Regions # -class RegionListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_region' +class RegionListView(ObjectListView): queryset = Region.objects.add_related_count( Region.objects.all(), Site, @@ -186,8 +185,7 @@ class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Sites # -class SiteListView(ObjectPermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_site' +class SiteListView(ObjectListView): queryset = Site.objects.prefetch_related('region', 'tenant') filterset = filters.SiteFilterSet filterset_form = forms.SiteFilterForm @@ -267,8 +265,7 @@ class SiteBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Rack groups # -class RackGroupListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_rackgroup' +class RackGroupListView(ObjectListView): queryset = RackGroup.objects.add_related_count( RackGroup.objects.all(), Rack, @@ -312,8 +309,7 @@ class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Rack roles # -class RackRoleListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_rackrole' +class RackRoleListView(ObjectListView): queryset = RackRole.objects.annotate(rack_count=Count('racks')) table = tables.RackRoleTable @@ -348,8 +344,7 @@ class RackRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Racks # -class RackListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_rack' +class RackListView(ObjectListView): queryset = Rack.objects.prefetch_related( 'site', 'group', 'tenant', 'role', 'devices__device_type' ).annotate( @@ -476,8 +471,7 @@ class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Rack reservations # -class RackReservationListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_rackreservation' +class RackReservationListView(ObjectListView): queryset = RackReservation.objects.prefetch_related('rack__site') filterset = filters.RackReservationFilterSet filterset_form = forms.RackReservationFilterForm @@ -561,8 +555,7 @@ class RackReservationBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Manufacturers # -class ManufacturerListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_manufacturer' +class ManufacturerListView(ObjectListView): queryset = Manufacturer.objects.annotate( devicetype_count=Count('device_types', distinct=True), inventoryitem_count=Count('inventory_items', distinct=True), @@ -601,8 +594,7 @@ class ManufacturerBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Device types # -class DeviceTypeListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_devicetype' +class DeviceTypeListView(ObjectListView): queryset = DeviceType.objects.prefetch_related('manufacturer').annotate(instance_count=Count('instances')) filterset = filters.DeviceTypeFilterSet filterset_form = forms.DeviceTypeFilterForm @@ -1026,8 +1018,7 @@ class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Device roles # -class DeviceRoleListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_devicerole' +class DeviceRoleListView(ObjectListView): queryset = DeviceRole.objects.all() table = tables.DeviceRoleTable @@ -1062,8 +1053,7 @@ class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Platforms # -class PlatformListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_platform' +class PlatformListView(ObjectListView): queryset = Platform.objects.all() table = tables.PlatformTable @@ -1098,8 +1088,7 @@ class PlatformBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Devices # -class DeviceListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_device' +class DeviceListView(ObjectListView): queryset = Device.objects.prefetch_related( 'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6' ) @@ -1323,8 +1312,7 @@ class DeviceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Console ports # -class ConsolePortListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_consoleport' +class ConsolePortListView(ObjectListView): queryset = ConsolePort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') filterset = filters.ConsolePortFilterSet filterset_form = forms.ConsolePortFilterForm @@ -1379,8 +1367,7 @@ class ConsolePortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Console server ports # -class ConsoleServerPortListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_consoleserverport' +class ConsoleServerPortListView(ObjectListView): queryset = ConsoleServerPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') filterset = filters.ConsoleServerPortFilterSet filterset_form = forms.ConsoleServerPortFilterForm @@ -1447,8 +1434,7 @@ class ConsoleServerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Power ports # -class PowerPortListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_powerport' +class PowerPortListView(ObjectListView): queryset = PowerPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') filterset = filters.PowerPortFilterSet filterset_form = forms.PowerPortFilterForm @@ -1503,8 +1489,7 @@ class PowerPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Power outlets # -class PowerOutletListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_poweroutlet' +class PowerOutletListView(ObjectListView): queryset = PowerOutlet.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') filterset = filters.PowerOutletFilterSet filterset_form = forms.PowerOutletFilterForm @@ -1571,8 +1556,7 @@ class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Interfaces # -class InterfaceListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_interface' +class InterfaceListView(ObjectListView): queryset = Interface.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') filterset = filters.InterfaceFilterSet filterset_form = forms.InterfaceFilterForm @@ -1676,8 +1660,7 @@ class InterfaceBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Front ports # -class FrontPortListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_frontport' +class FrontPortListView(ObjectListView): queryset = FrontPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') filterset = filters.FrontPortFilterSet filterset_form = forms.FrontPortFilterForm @@ -1744,8 +1727,7 @@ class FrontPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Rear ports # -class RearPortListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_rearport' +class RearPortListView(ObjectListView): queryset = RearPort.objects.prefetch_related('device', 'device__tenant', 'device__site', 'cable') filterset = filters.RearPortFilterSet filterset_form = forms.RearPortFilterForm @@ -1812,8 +1794,7 @@ class RearPortBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Device bays # -class DeviceBayListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_devicebay' +class DeviceBayListView(ObjectListView): queryset = DeviceBay.objects.prefetch_related( 'device', 'device__site', 'installed_device', 'installed_device__site' ) @@ -2045,8 +2026,7 @@ class DeviceBulkAddDeviceBayView(PermissionRequiredMixin, BulkComponentCreateVie # Cables # -class CableListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_cable' +class CableListView(ObjectListView): queryset = Cable.objects.prefetch_related( 'termination_a', 'termination_b' ) @@ -2215,7 +2195,7 @@ class CableBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Connections # -class ConsoleConnectionsListView(PermissionRequiredMixin, ObjectListView): +class ConsoleConnectionsListView(ObjectListView): permission_required = ('dcim.view_consoleport', 'dcim.view_consoleserverport') queryset = ConsolePort.objects.prefetch_related( 'device', 'connected_endpoint__device' @@ -2247,7 +2227,7 @@ class ConsoleConnectionsListView(PermissionRequiredMixin, ObjectListView): return '\n'.join(csv_data) -class PowerConnectionsListView(PermissionRequiredMixin, ObjectListView): +class PowerConnectionsListView(ObjectListView): permission_required = ('dcim.view_powerport', 'dcim.view_poweroutlet') queryset = PowerPort.objects.prefetch_related( 'device', '_connected_poweroutlet__device' @@ -2279,8 +2259,7 @@ class PowerConnectionsListView(PermissionRequiredMixin, ObjectListView): return '\n'.join(csv_data) -class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_interface' +class InterfaceConnectionsListView(ObjectListView): queryset = Interface.objects.prefetch_related( 'device', 'cable', '_connected_interface__device' ).filter( @@ -2319,8 +2298,7 @@ class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView): # Inventory items # -class InventoryItemListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_inventoryitem' +class InventoryItemListView(ObjectListView): queryset = InventoryItem.objects.prefetch_related('device', 'manufacturer') filterset = filters.InventoryItemFilterSet filterset_form = forms.InventoryItemFilterForm @@ -2376,8 +2354,7 @@ class InventoryItemBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Virtual chassis # -class VirtualChassisListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_virtualchassis' +class VirtualChassisListView(ObjectListView): queryset = VirtualChassis.objects.prefetch_related('master').annotate(member_count=Count('members')) table = tables.VirtualChassisTable filterset = filters.VirtualChassisFilterSet @@ -2644,8 +2621,7 @@ class VirtualChassisBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Power panels # -class PowerPanelListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_powerpanel' +class PowerPanelListView(ObjectListView): queryset = PowerPanel.objects.prefetch_related( 'site', 'rack_group' ).annotate( @@ -2724,8 +2700,7 @@ class PowerPanelBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Power feeds # -class PowerFeedListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'dcim.view_powerfeed' +class PowerFeedListView(ObjectListView): queryset = PowerFeed.objects.prefetch_related( 'power_panel', 'rack' ) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index e466414b6..c1bee4dd7 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -25,8 +25,7 @@ from .tables import ConfigContextTable, ObjectChangeTable, TagTable, TaggedItemT # Tags # -class TagListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'extras.view_tag' +class TagListView(ObjectListView): queryset = Tag.objects.annotate( items=Count('extras_taggeditem_items', distinct=True) ).order_by( @@ -106,8 +105,7 @@ class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Config contexts # -class ConfigContextListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'extras.view_configcontext' +class ConfigContextListView(ObjectListView): queryset = ConfigContext.objects.all() filterset = filters.ConfigContextFilterSet filterset_form = forms.ConfigContextFilterForm @@ -200,8 +198,7 @@ class ObjectConfigContextView(View): # Change logging # -class ObjectChangeListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'extras.view_objectchange' +class ObjectChangeListView(ObjectListView): queryset = ObjectChange.objects.prefetch_related('user', 'changed_object_type') filterset = filters.ObjectChangeFilterSet filterset_form = forms.ObjectChangeFilterForm diff --git a/netbox/ipam/views.py b/netbox/ipam/views.py index bb0844d4d..09c3f7892 100644 --- a/netbox/ipam/views.py +++ b/netbox/ipam/views.py @@ -113,8 +113,7 @@ def add_available_vlans(vlan_group, vlans): # VRFs # -class VRFListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_vrf' +class VRFListView(ObjectListView): queryset = VRF.objects.prefetch_related('tenant') filterset = filters.VRFFilterSet filterset_form = forms.VRFFilterForm @@ -182,8 +181,7 @@ class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # RIRs # -class RIRListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_rir' +class RIRListView(ObjectListView): queryset = RIR.objects.annotate(aggregate_count=Count('aggregates')) filterset = filters.RIRFilterSet filterset_form = forms.RIRFilterForm @@ -290,8 +288,7 @@ class RIRBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Aggregates # -class AggregateListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_aggregate' +class AggregateListView(ObjectListView): queryset = Aggregate.objects.prefetch_related('rir').annotate( child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ()) ) @@ -409,8 +406,7 @@ class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Prefix/VLAN roles # -class RoleListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_role' +class RoleListView(ObjectListView): queryset = Role.objects.all() table = tables.RoleTable @@ -445,8 +441,7 @@ class RoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Prefixes # -class PrefixListView(ObjectPermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_prefix' +class PrefixListView(ObjectListView): queryset = Prefix.objects.prefetch_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role') filterset = filters.PrefixFilterSet filterset_form = forms.PrefixFilterForm @@ -638,8 +633,7 @@ class PrefixBulkDeleteView(ObjectPermissionRequiredMixin, BulkDeleteView): # IP addresses # -class IPAddressListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_ipaddress' +class IPAddressListView(ObjectListView): queryset = IPAddress.objects.prefetch_related( 'vrf__tenant', 'tenant', 'nat_inside', 'interface__device', 'interface__virtual_machine' ) @@ -813,8 +807,7 @@ class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # VLAN groups # -class VLANGroupListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_vlangroup' +class VLANGroupListView(ObjectListView): queryset = VLANGroup.objects.prefetch_related('site').annotate(vlan_count=Count('vlans')) filterset = filters.VLANGroupFilterSet filterset_form = forms.VLANGroupFilterForm @@ -889,8 +882,7 @@ class VLANGroupVLANsView(PermissionRequiredMixin, View): # VLANs # -class VLANListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_vlan' +class VLANListView(ObjectListView): queryset = VLAN.objects.prefetch_related('site', 'group', 'tenant', 'role').prefetch_related('prefixes') filterset = filters.VLANFilterSet filterset_form = forms.VLANFilterForm @@ -985,8 +977,7 @@ class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Services # -class ServiceListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'ipam.view_service' +class ServiceListView(ObjectListView): queryset = Service.objects.prefetch_related('device', 'virtual_machine') filterset = filters.ServiceFilterSet filterset_form = forms.ServiceFilterForm diff --git a/netbox/secrets/views.py b/netbox/secrets/views.py index 8ce9addb4..eda845375 100644 --- a/netbox/secrets/views.py +++ b/netbox/secrets/views.py @@ -30,8 +30,7 @@ def get_session_key(request): # Secret roles # -class SecretRoleListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'secrets.view_secretrole' +class SecretRoleListView(ObjectListView): queryset = SecretRole.objects.annotate(secret_count=Count('secrets')) table = tables.SecretRoleTable @@ -66,8 +65,7 @@ class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Secrets # -class SecretListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'secrets.view_secret' +class SecretListView(ObjectListView): queryset = Secret.objects.prefetch_related('role', 'device') filterset = filters.SecretFilterSet filterset_form = forms.SecretFilterForm diff --git a/netbox/tenancy/views.py b/netbox/tenancy/views.py index 745362271..b4e37d153 100644 --- a/netbox/tenancy/views.py +++ b/netbox/tenancy/views.py @@ -18,8 +18,7 @@ from .models import Tenant, TenantGroup # Tenant groups # -class TenantGroupListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'tenancy.view_tenantgroup' +class TenantGroupListView(ObjectListView): queryset = TenantGroup.objects.add_related_count( TenantGroup.objects.all(), Tenant, @@ -60,8 +59,7 @@ class TenantGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Tenants # -class TenantListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'tenancy.view_tenant' +class TenantListView(ObjectListView): queryset = Tenant.objects.prefetch_related('group') filterset = filters.TenantFilterSet filterset_form = forms.TenantFilterForm diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index 6097fa5b2..8b4efeb5a 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -27,6 +27,7 @@ from extras.querysets import CustomFieldQueryset from users.models import ObjectPermission from utilities.exceptions import AbortTransaction from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm +from utilities.permissions import get_permission_for_model from utilities.utils import csv_format, prepare_cloned_fields from .error_handlers import handle_protectederror from .forms import ConfirmationForm, ImportForm @@ -45,11 +46,15 @@ class ObjectPermissionRequiredMixin(AccessMixin): """ permission_required = None + def get_required_permission(self): + return self.permission_required + 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(self.permission_required): + if not user.has_perm(permission_required): return False # Superusers implicitly have all permissions @@ -58,23 +63,18 @@ class ObjectPermissionRequiredMixin(AccessMixin): # Determine whether the permission is model-level or object-level. Model-level permissions grant the # specified action to *all* objects, so no further action is needed. - if self.permission_required in {*user._user_perm_cache, *user._group_perm_cache}: + if permission_required in {*user._user_perm_cache, *user._group_perm_cache}: return True # If the permission is granted only at the object level, filter the view's queryset to return only objects # on which the user is permitted to perform the specified action. - attrs = ObjectPermission.objects.get_attr_constraints(user, self.permission_required) + attrs = ObjectPermission.objects.get_attr_constraints(user, permission_required) if attrs: # Update the view's QuerySet to filter only the permitted objects self.queryset = self.queryset.filter(attrs) return True def dispatch(self, request, *args, **kwargs): - if self.permission_required is None: - raise ImproperlyConfigured( - '{0} is missing the permission_required attribute. Define {0}.permission_required, or override ' - '{0}.get_permission_required().'.format(self.__class__.__name__) - ) if not hasattr(self, 'queryset'): raise ImproperlyConfigured( @@ -118,15 +118,15 @@ class GetReturnURLMixin(object): # Generic views # -class ObjectListView(View): +class ObjectListView(ObjectPermissionRequiredMixin, View): """ List a series of objects. - queryset: The queryset of objects to display - filter: A django-filter FilterSet that is applied to the queryset - filter_form: The form used to render filter options - table: The django-tables2 Table used to render the objects list - template_name: The name of the template + :param queryset: The queryset of objects to display + :param filter: A django-filter FilterSet that is applied to the queryset + :param filter_form: The form used to render filter options + :param table: The django-tables2 Table used to render the objects list + :param template_name: The name of the template """ queryset = None filterset = None @@ -135,6 +135,11 @@ class ObjectListView(View): template_name = 'utilities/obj_list.html' 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): """ Export the queryset of objects as concatenated YAML documents. diff --git a/netbox/virtualization/views.py b/netbox/virtualization/views.py index c6f107be7..85dbf4774 100644 --- a/netbox/virtualization/views.py +++ b/netbox/virtualization/views.py @@ -22,8 +22,7 @@ from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine # Cluster types # -class ClusterTypeListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'virtualization.view_clustertype' +class ClusterTypeListView(ObjectListView): queryset = ClusterType.objects.annotate(cluster_count=Count('clusters')) table = tables.ClusterTypeTable @@ -58,8 +57,7 @@ class ClusterTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Cluster groups # -class ClusterGroupListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'virtualization.view_clustergroup' +class ClusterGroupListView(ObjectListView): queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters')) table = tables.ClusterGroupTable @@ -94,8 +92,7 @@ class ClusterGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Clusters # -class ClusterListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'virtualization.view_cluster' +class ClusterListView(ObjectListView): queryset = Cluster.objects.prefetch_related('type', 'group', 'site', 'tenant') table = tables.ClusterTable filterset = filters.ClusterFilterSet @@ -251,8 +248,7 @@ class ClusterRemoveDevicesView(PermissionRequiredMixin, View): # Virtual machines # -class VirtualMachineListView(PermissionRequiredMixin, ObjectListView): - permission_required = 'virtualization.view_virtualmachine' +class VirtualMachineListView(ObjectListView): queryset = VirtualMachine.objects.prefetch_related('cluster', 'tenant', 'role', 'primary_ip4', 'primary_ip6') filterset = filters.VirtualMachineFilterSet filterset_form = forms.VirtualMachineFilterForm