Introduce ObjectView to enforce object-level permissions for individual object views

This commit is contained in:
Jeremy Stretch 2020-05-21 15:39:07 -04:00
parent 8fd860a413
commit e61fc1f709
9 changed files with 118 additions and 106 deletions

View File

@ -4,6 +4,10 @@ Utility views are reusable views that handle common CRUD tasks, such as listing
## Individual Views ## Individual Views
### ObjectView
Retrieve and display a single object.
### ObjectListView ### ObjectListView
Generates a paginated table of objects from a given queryset, which may optionally be filtered. Generates a paginated table of objects from a given queryset, which may optionally be filtered.

View File

@ -1,18 +1,16 @@
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import transaction from django.db import transaction
from django.db.models import Count, OuterRef from django.db.models import Count, OuterRef
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.views.generic import View
from django_tables2 import RequestConfig from django_tables2 import RequestConfig
from extras.models import Graph from extras.models import Graph
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator from utilities.paginator import EnhancedPaginator
from utilities.views import ( from utilities.views import (
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView,
) )
from . import filters, forms, tables from . import filters, forms, tables
from .choices import CircuitTerminationSideChoices from .choices import CircuitTerminationSideChoices
@ -30,12 +28,12 @@ class ProviderListView(ObjectListView):
table = tables.ProviderTable table = tables.ProviderTable
class ProviderView(PermissionRequiredMixin, View): class ProviderView(ObjectView):
permission_required = 'circuits.view_provider' queryset = Provider.objects.all()
def get(self, request, slug): def get(self, request, slug):
provider = get_object_or_404(Provider, slug=slug) provider = get_object_or_404(self.queryset, slug=slug)
circuits = Circuit.objects.filter( circuits = Circuit.objects.filter(
provider=provider provider=provider
).prefetch_related( ).prefetch_related(
@ -135,12 +133,12 @@ class CircuitListView(ObjectListView):
table = tables.CircuitTable table = tables.CircuitTable
class CircuitView(PermissionRequiredMixin, View): class CircuitView(ObjectView):
permission_required = 'circuits.view_circuit' queryset = Circuit.objects.prefetch_related('provider', 'type', 'tenant__group')
def get(self, request, pk): def get(self, request, pk):
circuit = get_object_or_404(Circuit.objects.prefetch_related('provider', 'type', 'tenant__group'), pk=pk) circuit = get_object_or_404(self.queryset, pk=pk)
termination_a = CircuitTermination.objects.prefetch_related( termination_a = CircuitTermination.objects.prefetch_related(
'site__region', 'connected_endpoint__device' 'site__region', 'connected_endpoint__device'
).filter( ).filter(

View File

@ -26,7 +26,7 @@ from utilities.paginator import EnhancedPaginator
from utilities.utils import csv_format from utilities.utils import csv_format
from utilities.views import ( from utilities.views import (
BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, GetReturnURLMixin, BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, GetReturnURLMixin,
ObjectImportView, ObjectDeleteView, ObjectEditView, ObjectListView, ObjectPermissionRequiredMixin, ObjectView, ObjectImportView, ObjectDeleteView, ObjectEditView, ObjectListView, ObjectPermissionRequiredMixin,
) )
from virtualization.models import VirtualMachine from virtualization.models import VirtualMachine
from . import filters, forms, tables from . import filters, forms, tables
@ -185,8 +185,7 @@ class SiteListView(ObjectListView):
table = tables.SiteTable table = tables.SiteTable
class SiteView(ObjectPermissionRequiredMixin, View): class SiteView(ObjectView):
permission_required = 'dcim.view_site'
queryset = Site.objects.prefetch_related('region', 'tenant__group') queryset = Site.objects.prefetch_related('region', 'tenant__group')
def get(self, request, slug): def get(self, request, slug):
@ -362,12 +361,12 @@ class RackElevationListView(PermissionRequiredMixin, View):
}) })
class RackView(PermissionRequiredMixin, View): class RackView(ObjectView):
permission_required = 'dcim.view_rack' queryset = Rack.objects.prefetch_related('site__region', 'tenant__group', 'group', 'role')
def get(self, request, pk): def get(self, request, pk):
rack = get_object_or_404(Rack.objects.prefetch_related('site__region', 'tenant__group', 'group', 'role'), pk=pk) rack = get_object_or_404(self.queryset, pk=pk)
nonracked_devices = Device.objects.filter( nonracked_devices = Device.objects.filter(
rack=rack, rack=rack,
@ -440,12 +439,12 @@ class RackReservationListView(ObjectListView):
action_buttons = ('export',) action_buttons = ('export',)
class RackReservationView(PermissionRequiredMixin, View): class RackReservationView(ObjectView):
permission_required = 'dcim.view_rackreservation' queryset = RackReservation.objects.prefetch_related('rack')
def get(self, request, pk): def get(self, request, pk):
rackreservation = get_object_or_404(RackReservation.objects.prefetch_related('rack'), pk=pk) rackreservation = get_object_or_404(self.queryset, pk=pk)
return render(request, 'dcim/rackreservation.html', { return render(request, 'dcim/rackreservation.html', {
'rackreservation': rackreservation, 'rackreservation': rackreservation,
@ -546,12 +545,12 @@ class DeviceTypeListView(ObjectListView):
table = tables.DeviceTypeTable table = tables.DeviceTypeTable
class DeviceTypeView(PermissionRequiredMixin, View): class DeviceTypeView(ObjectView):
permission_required = 'dcim.view_devicetype' queryset = DeviceType.objects.prefetch_related('manufacturer')
def get(self, request, pk): def get(self, request, pk):
devicetype = get_object_or_404(DeviceType, pk=pk) devicetype = get_object_or_404(self.queryset, pk=pk)
# Component tables # Component tables
consoleport_table = tables.ConsolePortTemplateTable( consoleport_table = tables.ConsolePortTemplateTable(
@ -990,14 +989,14 @@ class DeviceListView(ObjectListView):
template_name = 'dcim/device_list.html' template_name = 'dcim/device_list.html'
class DeviceView(PermissionRequiredMixin, View): class DeviceView(ObjectView):
permission_required = 'dcim.view_device' queryset = Device.objects.prefetch_related(
'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform'
)
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device.objects.prefetch_related( device = get_object_or_404(self.queryset, pk=pk)
'site__region', 'rack__group', 'tenant__group', 'device_role', 'platform'
), pk=pk)
# VirtualChassis members # VirtualChassis members
if device.virtual_chassis is not None: if device.virtual_chassis is not None:
@ -1068,12 +1067,12 @@ class DeviceView(PermissionRequiredMixin, View):
}) })
class DeviceInventoryView(PermissionRequiredMixin, View): class DeviceInventoryView(ObjectView):
permission_required = 'dcim.view_device' queryset = Device.objects.all()
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk) device = get_object_or_404(self.queryset, pk=pk)
inventory_items = InventoryItem.objects.filter( inventory_items = InventoryItem.objects.filter(
device=device, parent=None device=device, parent=None
).prefetch_related( ).prefetch_related(
@ -1087,12 +1086,13 @@ class DeviceInventoryView(PermissionRequiredMixin, View):
}) })
class DeviceStatusView(PermissionRequiredMixin, View): class DeviceStatusView(ObjectView):
permission_required = ('dcim.view_device', 'dcim.napalm_read') permission_required = ('dcim.view_device', 'dcim.napalm_read')
queryset = Device.objects.all()
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk) device = get_object_or_404(self.queryset, pk=pk)
return render(request, 'dcim/device_status.html', { return render(request, 'dcim/device_status.html', {
'device': device, 'device': device,
@ -1102,10 +1102,11 @@ class DeviceStatusView(PermissionRequiredMixin, View):
class DeviceLLDPNeighborsView(PermissionRequiredMixin, View): class DeviceLLDPNeighborsView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_device', 'dcim.napalm_read') permission_required = ('dcim.view_device', 'dcim.napalm_read')
queryset = Device.objects.all()
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk) device = get_object_or_404(self.queryset, pk=pk)
interfaces = device.vc_interfaces.exclude(type__in=NONCONNECTABLE_IFACE_TYPES).prefetch_related( interfaces = device.vc_interfaces.exclude(type__in=NONCONNECTABLE_IFACE_TYPES).prefetch_related(
'_connected_interface__device' '_connected_interface__device'
) )
@ -1119,10 +1120,11 @@ class DeviceLLDPNeighborsView(PermissionRequiredMixin, View):
class DeviceConfigView(PermissionRequiredMixin, View): class DeviceConfigView(PermissionRequiredMixin, View):
permission_required = ('dcim.view_device', 'dcim.napalm_read') permission_required = ('dcim.view_device', 'dcim.napalm_read')
queryset = Device.objects.all()
def get(self, request, pk): def get(self, request, pk):
device = get_object_or_404(Device, pk=pk) device = get_object_or_404(self.queryset, pk=pk)
return render(request, 'dcim/device_config.html', { return render(request, 'dcim/device_config.html', {
'device': device, 'device': device,
@ -1426,12 +1428,12 @@ class InterfaceListView(ObjectListView):
action_buttons = ('import', 'export') action_buttons = ('import', 'export')
class InterfaceView(PermissionRequiredMixin, View): class InterfaceView(ObjectView):
permission_required = 'dcim.view_interface' queryset = Interface.objects.all()
def get(self, request, pk): def get(self, request, pk):
interface = get_object_or_404(Interface, pk=pk) interface = get_object_or_404(self.queryset, pk=pk)
# Get assigned IP addresses # Get assigned IP addresses
ipaddress_table = InterfaceIPAddressTable( ipaddress_table = InterfaceIPAddressTable(
@ -1878,12 +1880,12 @@ class CableListView(ObjectListView):
action_buttons = ('import', 'export') action_buttons = ('import', 'export')
class CableView(PermissionRequiredMixin, View): class CableView(ObjectView):
permission_required = 'dcim.view_cable' queryset = Cable.objects.all()
def get(self, request, pk): def get(self, request, pk):
cable = get_object_or_404(Cable, pk=pk) cable = get_object_or_404(self.queryset, pk=pk)
return render(request, 'dcim/cable.html', { return render(request, 'dcim/cable.html', {
'cable': cable, 'cable': cable,
@ -2194,11 +2196,11 @@ class VirtualChassisListView(ObjectListView):
action_buttons = ('export',) action_buttons = ('export',)
class VirtualChassisView(PermissionRequiredMixin, View): class VirtualChassisView(ObjectView):
permission_required = 'dcim.view_virtualchassis' queryset = VirtualChassis.objects.prefetch_related('members')
def get(self, request, pk): def get(self, request, pk):
virtualchassis = get_object_or_404(VirtualChassis.objects.prefetch_related('members'), pk=pk) virtualchassis = get_object_or_404(self.queryset, pk=pk)
return render(request, 'dcim/virtualchassis.html', { return render(request, 'dcim/virtualchassis.html', {
'virtualchassis': virtualchassis, 'virtualchassis': virtualchassis,
@ -2461,12 +2463,12 @@ class PowerPanelListView(ObjectListView):
table = tables.PowerPanelTable table = tables.PowerPanelTable
class PowerPanelView(PermissionRequiredMixin, View): class PowerPanelView(ObjectView):
permission_required = 'dcim.view_powerpanel' queryset = PowerPanel.objects.prefetch_related('site', 'rack_group')
def get(self, request, pk): def get(self, request, pk):
powerpanel = get_object_or_404(PowerPanel.objects.prefetch_related('site', 'rack_group'), pk=pk) powerpanel = get_object_or_404(self.queryset, pk=pk)
powerfeed_table = tables.PowerFeedTable( powerfeed_table = tables.PowerFeedTable(
data=PowerFeed.objects.filter(power_panel=powerpanel).prefetch_related('rack'), data=PowerFeed.objects.filter(power_panel=powerpanel).prefetch_related('rack'),
orderable=False orderable=False
@ -2529,12 +2531,12 @@ class PowerFeedListView(ObjectListView):
table = tables.PowerFeedTable table = tables.PowerFeedTable
class PowerFeedView(PermissionRequiredMixin, View): class PowerFeedView(ObjectView):
permission_required = 'dcim.view_powerfeed' queryset = PowerFeed.objects.prefetch_related('power_panel', 'rack')
def get(self, request, pk): def get(self, request, pk):
powerfeed = get_object_or_404(PowerFeed.objects.prefetch_related('power_panel', 'rack'), pk=pk) powerfeed = get_object_or_404(self.queryset, pk=pk)
return render(request, 'dcim/powerfeed.html', { return render(request, 'dcim/powerfeed.html', {
'powerfeed': powerfeed, 'powerfeed': powerfeed,

View File

@ -13,7 +13,7 @@ from django_tables2 import RequestConfig
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.paginator import EnhancedPaginator from utilities.paginator import EnhancedPaginator
from utilities.utils import shallow_compare_dict from utilities.utils import shallow_compare_dict
from utilities.views import BulkDeleteView, BulkEditView, ObjectDeleteView, ObjectEditView, ObjectListView from utilities.views import BulkDeleteView, BulkEditView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView
from . import filters, forms from . import filters, forms
from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult, Tag, TaggedItem from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult, Tag, TaggedItem
from .reports import get_report, get_reports from .reports import get_report, get_reports
@ -37,12 +37,12 @@ class TagListView(ObjectListView):
action_buttons = () action_buttons = ()
class TagView(PermissionRequiredMixin, View): class TagView(ObjectView):
permission_required = 'extras.view_tag' queryset = Tag.objects.all()
def get(self, request, slug): def get(self, request, slug):
tag = get_object_or_404(Tag, slug=slug) tag = get_object_or_404(self.queryset, slug=slug)
tagged_items = TaggedItem.objects.filter( tagged_items = TaggedItem.objects.filter(
tag=tag tag=tag
).prefetch_related( ).prefetch_related(
@ -109,11 +109,11 @@ class ConfigContextListView(ObjectListView):
action_buttons = ('add',) action_buttons = ('add',)
class ConfigContextView(PermissionRequiredMixin, View): class ConfigContextView(ObjectView):
permission_required = 'extras.view_configcontext' queryset = ConfigContext.objects.all()
def get(self, request, pk): def get(self, request, pk):
configcontext = get_object_or_404(ConfigContext, pk=pk) configcontext = get_object_or_404(self.queryset, pk=pk)
# Determine user's preferred output format # Determine user's preferred output format
if request.GET.get('format') in ['json', 'yaml']: if request.GET.get('format') in ['json', 'yaml']:
@ -195,12 +195,12 @@ class ObjectChangeListView(ObjectListView):
action_buttons = ('export',) action_buttons = ('export',)
class ObjectChangeView(PermissionRequiredMixin, View): class ObjectChangeView(ObjectView):
permission_required = 'extras.view_objectchange' queryset = ObjectChange.objects.all()
def get(self, request, pk): def get(self, request, pk):
objectchange = get_object_or_404(ObjectChange, pk=pk) objectchange = get_object_or_404(self.queryset, pk=pk)
related_changes = ObjectChange.objects.filter(request_id=objectchange.request_id).exclude(pk=objectchange.pk) related_changes = ObjectChange.objects.filter(request_id=objectchange.request_id).exclude(pk=objectchange.pk)
related_changes_table = ObjectChangeTable( related_changes_table = ObjectChangeTable(

View File

@ -10,8 +10,8 @@ from django_tables2 import RequestConfig
from dcim.models import Device, Interface from dcim.models import Device, Interface
from utilities.paginator import EnhancedPaginator from utilities.paginator import EnhancedPaginator
from utilities.views import ( from utilities.views import (
BulkCreateView, BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, BulkCreateView, BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView,
ObjectPermissionRequiredMixin, ObjectListView,
) )
from virtualization.models import VirtualMachine from virtualization.models import VirtualMachine
from . import filters, forms, tables from . import filters, forms, tables
@ -120,12 +120,12 @@ class VRFListView(ObjectListView):
table = tables.VRFTable table = tables.VRFTable
class VRFView(PermissionRequiredMixin, View): class VRFView(ObjectView):
permission_required = 'ipam.view_vrf' queryset = VRF.objects.all()
def get(self, request, pk): def get(self, request, pk):
vrf = get_object_or_404(VRF.objects.all(), pk=pk) vrf = get_object_or_404(self.queryset, pk=pk)
prefix_count = Prefix.objects.filter(vrf=vrf).count() prefix_count = Prefix.objects.filter(vrf=vrf).count()
return render(request, 'ipam/vrf.html', { return render(request, 'ipam/vrf.html', {
@ -298,12 +298,12 @@ class AggregateListView(ObjectListView):
} }
class AggregateView(PermissionRequiredMixin, View): class AggregateView(ObjectView):
permission_required = 'ipam.view_aggregate' queryset = Aggregate.objects.all()
def get(self, request, pk): def get(self, request, pk):
aggregate = get_object_or_404(Aggregate, pk=pk) aggregate = get_object_or_404(self.queryset, pk=pk)
# Find all child prefixes contained by this aggregate # Find all child prefixes contained by this aggregate
child_prefixes = Prefix.objects.filter( child_prefixes = Prefix.objects.filter(
@ -422,8 +422,7 @@ class PrefixListView(ObjectListView):
return self.queryset.annotate_depth(limit=limit) return self.queryset.annotate_depth(limit=limit)
class PrefixView(ObjectPermissionRequiredMixin, View): class PrefixView(ObjectView):
permission_required = 'ipam.view_prefix'
queryset = Prefix.objects.prefetch_related('vrf', 'site__region', 'tenant__group', 'vlan__group', 'role') queryset = Prefix.objects.prefetch_related('vrf', 'site__region', 'tenant__group', 'vlan__group', 'role')
def get(self, request, pk): def get(self, request, pk):
@ -465,12 +464,12 @@ class PrefixView(ObjectPermissionRequiredMixin, View):
}) })
class PrefixPrefixesView(PermissionRequiredMixin, View): class PrefixPrefixesView(ObjectView):
permission_required = 'ipam.view_prefix' queryset = Prefix.objects.all()
def get(self, request, pk): def get(self, request, pk):
prefix = get_object_or_404(Prefix.objects.all(), pk=pk) prefix = get_object_or_404(self.queryset, pk=pk)
# Child prefixes table # Child prefixes table
child_prefixes = prefix.get_child_prefixes().prefetch_related( child_prefixes = prefix.get_child_prefixes().prefetch_related(
@ -509,12 +508,12 @@ class PrefixPrefixesView(PermissionRequiredMixin, View):
}) })
class PrefixIPAddressesView(PermissionRequiredMixin, View): class PrefixIPAddressesView(ObjectView):
permission_required = 'ipam.view_prefix' queryset = Prefix.objects.all()
def get(self, request, pk): def get(self, request, pk):
prefix = get_object_or_404(Prefix.objects.all(), pk=pk) prefix = get_object_or_404(self.queryset, pk=pk)
# Find all IPAddresses belonging to this Prefix # Find all IPAddresses belonging to this Prefix
ipaddresses = prefix.get_child_ips().prefetch_related( ipaddresses = prefix.get_child_ips().prefetch_related(
@ -601,12 +600,12 @@ class IPAddressListView(ObjectListView):
table = tables.IPAddressDetailTable table = tables.IPAddressDetailTable
class IPAddressView(PermissionRequiredMixin, View): class IPAddressView(ObjectView):
permission_required = 'ipam.view_ipaddress' queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
def get(self, request, pk): def get(self, request, pk):
ipaddress = get_object_or_404(IPAddress.objects.prefetch_related('vrf__tenant', 'tenant'), pk=pk) ipaddress = get_object_or_404(self.queryset, pk=pk)
# Parent prefixes table # Parent prefixes table
parent_prefixes = Prefix.objects.filter( parent_prefixes = Prefix.objects.filter(
@ -833,14 +832,12 @@ class VLANListView(ObjectListView):
table = tables.VLANDetailTable table = tables.VLANDetailTable
class VLANView(PermissionRequiredMixin, View): class VLANView(ObjectView):
permission_required = 'ipam.view_vlan' queryset = VLAN.objects.prefetch_related('site__region', 'tenant__group', 'role')
def get(self, request, pk): def get(self, request, pk):
vlan = get_object_or_404(VLAN.objects.prefetch_related( vlan = get_object_or_404(self.queryset, pk=pk)
'site__region', 'tenant__group', 'role'
), pk=pk)
prefixes = Prefix.objects.filter(vlan=vlan).prefetch_related('vrf', 'site', 'role') prefixes = Prefix.objects.filter(vlan=vlan).prefetch_related('vrf', 'site', 'role')
prefix_table = tables.PrefixTable(list(prefixes), orderable=False) prefix_table = tables.PrefixTable(list(prefixes), orderable=False)
prefix_table.exclude = ('vlan',) prefix_table.exclude = ('vlan',)
@ -851,12 +848,12 @@ class VLANView(PermissionRequiredMixin, View):
}) })
class VLANMembersView(PermissionRequiredMixin, View): class VLANMembersView(ObjectView):
permission_required = 'ipam.view_vlan' queryset = VLAN.objects.all()
def get(self, request, pk): def get(self, request, pk):
vlan = get_object_or_404(VLAN.objects.all(), pk=pk) vlan = get_object_or_404(self.queryset, pk=pk)
members = vlan.get_members().prefetch_related('device', 'virtual_machine') members = vlan.get_members().prefetch_related('device', 'virtual_machine')
members_table = tables.VLANMemberTable(members) members_table = tables.VLANMemberTable(members)
@ -920,12 +917,12 @@ class ServiceListView(ObjectListView):
action_buttons = ('export',) action_buttons = ('export',)
class ServiceView(PermissionRequiredMixin, View): class ServiceView(ObjectView):
permission_required = 'ipam.view_service' queryset = Service.objects.all()
def get(self, request, pk): def get(self, request, pk):
service = get_object_or_404(Service, pk=pk) service = get_object_or_404(self.queryset, pk=pk)
return render(request, 'ipam/service.html', { return render(request, 'ipam/service.html', {
'service': service, 'service': service,

View File

@ -9,7 +9,8 @@ from django.urls import reverse
from django.views.generic import View from django.views.generic import View
from utilities.views import ( from utilities.views import (
BulkDeleteView, BulkEditView, BulkImportView, GetReturnURLMixin, ObjectDeleteView, ObjectEditView, ObjectListView, BulkDeleteView, BulkEditView, BulkImportView, GetReturnURLMixin, ObjectView, ObjectDeleteView, ObjectEditView,
ObjectListView,
) )
from . import filters, forms, tables from . import filters, forms, tables
from .decorators import userkey_required from .decorators import userkey_required
@ -66,12 +67,12 @@ class SecretListView(ObjectListView):
action_buttons = ('import', 'export') action_buttons = ('import', 'export')
class SecretView(PermissionRequiredMixin, View): class SecretView(ObjectView):
permission_required = 'secrets.view_secret' queryset = Secret.objects.all()
def get(self, request, pk): def get(self, request, pk):
secret = get_object_or_404(Secret, pk=pk) secret = get_object_or_404(self.queryset, pk=pk)
return render(request, 'secrets/secret.html', { return render(request, 'secrets/secret.html', {
'secret': secret, 'secret': secret,

View File

@ -1,13 +1,11 @@
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count from django.db.models import Count
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.views.generic import View
from circuits.models import Circuit from circuits.models import Circuit
from dcim.models import Site, Rack, Device, RackReservation from dcim.models import Site, Rack, Device, RackReservation
from ipam.models import IPAddress, Prefix, VLAN, VRF from ipam.models import IPAddress, Prefix, VLAN, VRF
from utilities.views import ( from utilities.views import (
BulkDeleteView, BulkEditView, BulkImportView, ObjectDeleteView, ObjectEditView, ObjectListView, BulkDeleteView, BulkEditView, BulkImportView, ObjectView, ObjectDeleteView, ObjectEditView, ObjectListView,
) )
from virtualization.models import VirtualMachine, Cluster from virtualization.models import VirtualMachine, Cluster
from . import filters, forms, tables from . import filters, forms, tables
@ -59,12 +57,12 @@ class TenantListView(ObjectListView):
table = tables.TenantTable table = tables.TenantTable
class TenantView(PermissionRequiredMixin, View): class TenantView(ObjectView):
permission_required = 'tenancy.view_tenant' queryset = Tenant.objects.prefetch_related('group')
def get(self, request, slug): def get(self, request, slug):
tenant = get_object_or_404(Tenant, slug=slug) tenant = get_object_or_404(self.queryset, slug=slug)
stats = { stats = {
'site_count': Site.objects.filter(tenant=tenant).count(), 'site_count': Site.objects.filter(tenant=tenant).count(),
'rack_count': Rack.objects.filter(tenant=tenant).count(), 'rack_count': Rack.objects.filter(tenant=tenant).count(),

View File

@ -118,6 +118,18 @@ class GetReturnURLMixin(object):
# Generic views # Generic views
# #
class ObjectView(ObjectPermissionRequiredMixin, View):
"""
Retrieve a single object for display.
:param queryset: The base queryset for retrieving the object.
"""
queryset = None
def get_required_permission(self):
return get_permission_for_model(self.queryset.model, 'view')
class ObjectListView(ObjectPermissionRequiredMixin, View): class ObjectListView(ObjectPermissionRequiredMixin, View):
""" """
List a series of objects. List a series of objects.

View File

@ -11,8 +11,8 @@ from dcim.tables import DeviceTable
from extras.views import ObjectConfigContextView from extras.views import ObjectConfigContextView
from ipam.models import Service from ipam.models import Service
from utilities.views import ( from utilities.views import (
BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, ObjectDeleteView, BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, ObjectView,
ObjectEditView, ObjectListView, ObjectDeleteView, ObjectEditView, ObjectListView,
) )
from . import filters, forms, tables from . import filters, forms, tables
from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
@ -85,12 +85,12 @@ class ClusterListView(ObjectListView):
filterset_form = forms.ClusterFilterForm filterset_form = forms.ClusterFilterForm
class ClusterView(PermissionRequiredMixin, View): class ClusterView(ObjectView):
permission_required = 'virtualization.view_cluster' queryset = Cluster.objects.all()
def get(self, request, pk): def get(self, request, pk):
cluster = get_object_or_404(Cluster, pk=pk) cluster = get_object_or_404(self.queryset, pk=pk)
devices = Device.objects.filter(cluster=cluster).prefetch_related( devices = Device.objects.filter(cluster=cluster).prefetch_related(
'site', 'rack', 'tenant', 'device_type__manufacturer' 'site', 'rack', 'tenant', 'device_type__manufacturer'
) )
@ -233,12 +233,12 @@ class VirtualMachineListView(ObjectListView):
template_name = 'virtualization/virtualmachine_list.html' template_name = 'virtualization/virtualmachine_list.html'
class VirtualMachineView(PermissionRequiredMixin, View): class VirtualMachineView(ObjectView):
permission_required = 'virtualization.view_virtualmachine' queryset = VirtualMachine.objects.prefetch_related('tenant__group')
def get(self, request, pk): def get(self, request, pk):
virtualmachine = get_object_or_404(VirtualMachine.objects.prefetch_related('tenant__group'), pk=pk) virtualmachine = get_object_or_404(self.queryset, pk=pk)
interfaces = Interface.objects.filter(virtual_machine=virtualmachine) interfaces = Interface.objects.filter(virtual_machine=virtualmachine)
services = Service.objects.filter(virtual_machine=virtualmachine) services = Service.objects.filter(virtual_machine=virtualmachine)