Enforce view permissions for UI views

This commit is contained in:
Jeremy Stretch 2019-04-11 17:27:38 -04:00
parent ea6815b9bb
commit e710ccb0e6
16 changed files with 257 additions and 168 deletions

View File

@ -4,13 +4,15 @@ from django.test import Client, TestCase
from django.urls import reverse
from circuits.models import Circuit, CircuitType, Provider
from utilities.testing import create_test_user
class ProviderTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['circuits.view_provider'])
self.client = Client()
self.client.force_login(user)
Provider.objects.bulk_create([
Provider(name='Provider 1', slug='provider-1', asn=65001),
@ -38,8 +40,9 @@ class ProviderTestCase(TestCase):
class CircuitTypeTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['circuits.view_circuittype'])
self.client = Client()
self.client.force_login(user)
CircuitType.objects.bulk_create([
CircuitType(name='Circuit Type 1', slug='circuit-type-1'),
@ -58,8 +61,9 @@ class CircuitTypeTestCase(TestCase):
class CircuitTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['circuits.view_circuit'])
self.client = Client()
self.client.force_login(user)
provider = Provider(name='Provider 1', slug='provider-1', asn=65001)
provider.save()
@ -84,8 +88,8 @@ class CircuitTestCase(TestCase):
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
self.assertEqual(response.status_code, 200)
def test_provider(self):
def test_circuit(self):
provider = Provider.objects.first()
response = self.client.get(provider.get_absolute_url())
circuit = Circuit.objects.first()
response = self.client.get(circuit.get_absolute_url())
self.assertEqual(response.status_code, 200)

View File

@ -20,7 +20,8 @@ from .models import Circuit, CircuitTermination, CircuitType, Provider
# Providers
#
class ProviderListView(ObjectListView):
class ProviderListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_provider'
queryset = Provider.objects.annotate(count_circuits=Count('circuits'))
filter = filters.ProviderFilter
filter_form = forms.ProviderFilterForm
@ -28,7 +29,8 @@ class ProviderListView(ObjectListView):
template_name = 'circuits/provider_list.html'
class ProviderView(View):
class ProviderView(PermissionRequiredMixin, View):
permission_required = 'circuits.view_provider'
def get(self, request, slug):
@ -93,7 +95,8 @@ class ProviderBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Circuit Types
#
class CircuitTypeListView(ObjectListView):
class CircuitTypeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_circuittype'
queryset = CircuitType.objects.annotate(circuit_count=Count('circuits'))
table = tables.CircuitTypeTable
template_name = 'circuits/circuittype_list.html'
@ -128,7 +131,8 @@ class CircuitTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Circuits
#
class CircuitListView(ObjectListView):
class CircuitListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'circuits.view_circuit'
queryset = Circuit.objects.select_related(
'provider', 'type', 'tenant'
).prefetch_related(
@ -140,7 +144,8 @@ class CircuitListView(ObjectListView):
template_name = 'circuits/circuit_list.html'
class CircuitView(View):
class CircuitView(PermissionRequiredMixin, View):
permission_required = 'circuits.view_circuit'
def get(self, request, pk):

View File

@ -1,6 +1,5 @@
import urllib.parse
from django.contrib.auth import get_user_model
from django.test import Client, TestCase
from django.urls import reverse
@ -9,13 +8,15 @@ from dcim.models import (
Cable, Device, DeviceRole, DeviceType, Interface, InventoryItem, Manufacturer, Platform, Rack, RackGroup,
RackReservation, RackRole, Site, Region, VirtualChassis,
)
from utilities.testing import create_test_user
class RegionTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_region'])
self.client = Client()
self.client.force_login(user)
# Create three Regions
for i in range(1, 4):
@ -32,8 +33,9 @@ class RegionTestCase(TestCase):
class SiteTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_site'])
self.client = Client()
self.client.force_login(user)
region = Region(name='Region 1', slug='region-1')
region.save()
@ -64,8 +66,9 @@ class SiteTestCase(TestCase):
class RackGroupTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_rackgroup'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -84,11 +87,12 @@ class RackGroupTestCase(TestCase):
self.assertEqual(response.status_code, 200)
class RackTypeTestCase(TestCase):
class RackRoleTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_rackrole'])
self.client = Client()
self.client.force_login(user)
RackRole.objects.bulk_create([
RackRole(name='Rack Role 1', slug='rack-role-1'),
@ -107,12 +111,9 @@ class RackTypeTestCase(TestCase):
class RackReservationTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_rackreservation'])
self.client = Client()
User = get_user_model()
user = User(username='testuser', email='testuser@example.com')
user.save()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -137,8 +138,9 @@ class RackReservationTestCase(TestCase):
class RackTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_rack'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -169,8 +171,9 @@ class RackTestCase(TestCase):
class ManufacturerTypeTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_manufacturer'])
self.client = Client()
self.client.force_login(user)
Manufacturer.objects.bulk_create([
Manufacturer(name='Manufacturer 1', slug='manufacturer-1'),
@ -189,8 +192,9 @@ class ManufacturerTypeTestCase(TestCase):
class DeviceTypeTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_devicetype'])
self.client = Client()
self.client.force_login(user)
manufacturer = Manufacturer(name='Manufacturer 1', slug='manufacturer-1')
manufacturer.save()
@ -221,8 +225,9 @@ class DeviceTypeTestCase(TestCase):
class DeviceRoleTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_devicerole'])
self.client = Client()
self.client.force_login(user)
DeviceRole.objects.bulk_create([
DeviceRole(name='Device Role 1', slug='device-role-1'),
@ -241,8 +246,9 @@ class DeviceRoleTestCase(TestCase):
class PlatformTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_platform'])
self.client = Client()
self.client.force_login(user)
Platform.objects.bulk_create([
Platform(name='Platform 1', slug='platform-1'),
@ -261,8 +267,9 @@ class PlatformTestCase(TestCase):
class DeviceTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_device'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -303,8 +310,9 @@ class DeviceTestCase(TestCase):
class InventoryItemTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_inventoryitem'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -337,18 +345,13 @@ class InventoryItemTestCase(TestCase):
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)))
self.assertEqual(response.status_code, 200)
def test_inventoryitem(self):
inventoryitem = InventoryItem.objects.first()
response = self.client.get(inventoryitem.get_absolute_url())
self.assertEqual(response.status_code, 200)
class CableTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_cable'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -401,11 +404,12 @@ class CableTestCase(TestCase):
self.assertEqual(response.status_code, 200)
class VirtualMachineTestCase(TestCase):
class VirtualChassisTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['dcim.view_virtualchassis'])
self.client = Client()
self.client.force_login(user)
site = Site.objects.create(name='Site 1', slug='site-1')
manufacturer = Manufacturer.objects.create(name='Manufacturer', slug='manufacturer-1')
@ -450,9 +454,3 @@ class VirtualMachineTestCase(TestCase):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_virtualchassis(self):
virtualchassis = VirtualChassis.objects.first()
response = self.client.get(virtualchassis.get_absolute_url())
self.assertEqual(response.status_code, 200)

View File

@ -138,7 +138,8 @@ class BulkDisconnectView(GetReturnURLMixin, View):
# Regions
#
class RegionListView(ObjectListView):
class RegionListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_region'
queryset = Region.objects.add_related_count(
Region.objects.all(),
Site,
@ -182,7 +183,8 @@ class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Sites
#
class SiteListView(ObjectListView):
class SiteListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_site'
queryset = Site.objects.select_related('region', 'tenant')
filter = filters.SiteFilter
filter_form = forms.SiteFilterForm
@ -190,7 +192,8 @@ class SiteListView(ObjectListView):
template_name = 'dcim/site_list.html'
class SiteView(View):
class SiteView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_site'
def get(self, request, slug):
@ -254,7 +257,8 @@ class SiteBulkEditView(PermissionRequiredMixin, BulkEditView):
# Rack groups
#
class RackGroupListView(ObjectListView):
class RackGroupListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rackgroup'
queryset = RackGroup.objects.select_related('site').annotate(rack_count=Count('racks'))
filter = filters.RackGroupFilter
filter_form = forms.RackGroupFilterForm
@ -292,7 +296,8 @@ class RackGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Rack roles
#
class RackRoleListView(ObjectListView):
class RackRoleListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rackrole'
queryset = RackRole.objects.annotate(rack_count=Count('racks'))
table = tables.RackRoleTable
template_name = 'dcim/rackrole_list.html'
@ -327,7 +332,8 @@ class RackRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Racks
#
class RackListView(ObjectListView):
class RackListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rack'
queryset = Rack.objects.select_related(
'site', 'group', 'tenant', 'role'
).prefetch_related(
@ -341,10 +347,11 @@ class RackListView(ObjectListView):
template_name = 'dcim/rack_list.html'
class RackElevationListView(View):
class RackElevationListView(PermissionRequiredMixin, View):
"""
Display a set of rack elevations side-by-side.
"""
permission_required = 'dcim.view_rack'
def get(self, request):
@ -382,7 +389,8 @@ class RackElevationListView(View):
})
class RackView(View):
class RackView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_rack'
def get(self, request, pk):
@ -454,7 +462,8 @@ class RackBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Rack reservations
#
class RackReservationListView(ObjectListView):
class RackReservationListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_rackreservation'
queryset = RackReservation.objects.all()
filter = filters.RackReservationFilter
filter_form = forms.RackReservationFilterForm
@ -510,7 +519,8 @@ class RackReservationBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Manufacturers
#
class ManufacturerListView(ObjectListView):
class ManufacturerListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_manufacturer'
queryset = Manufacturer.objects.annotate(
devicetype_count=Count('device_types', distinct=True),
platform_count=Count('platforms', distinct=True),
@ -548,7 +558,8 @@ class ManufacturerBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Device types
#
class DeviceTypeListView(ObjectListView):
class DeviceTypeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_devicetype'
queryset = DeviceType.objects.select_related('manufacturer').annotate(instance_count=Count('instances'))
filter = filters.DeviceTypeFilter
filter_form = forms.DeviceTypeFilterForm
@ -556,7 +567,8 @@ class DeviceTypeListView(ObjectListView):
template_name = 'dcim/devicetype_list.html'
class DeviceTypeView(View):
class DeviceTypeView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_devicetype'
def get(self, request, pk):
@ -812,7 +824,8 @@ class DeviceBayTemplateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Device roles
#
class DeviceRoleListView(ObjectListView):
class DeviceRoleListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_devicerole'
queryset = DeviceRole.objects.all()
table = tables.DeviceRoleTable
template_name = 'dcim/devicerole_list.html'
@ -847,7 +860,8 @@ class DeviceRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Platforms
#
class PlatformListView(ObjectListView):
class PlatformListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_platform'
queryset = Platform.objects.all()
table = tables.PlatformTable
template_name = 'dcim/platform_list.html'
@ -882,7 +896,8 @@ class PlatformBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Devices
#
class DeviceListView(ObjectListView):
class DeviceListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_device'
queryset = Device.objects.select_related(
'device_type__manufacturer', 'device_role', 'tenant', 'site', 'rack', 'primary_ip4', 'primary_ip6'
)
@ -892,7 +907,8 @@ class DeviceListView(ObjectListView):
template_name = 'dcim/device_list.html'
class DeviceView(View):
class DeviceView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_device'
def get(self, request, pk):
@ -972,7 +988,8 @@ class DeviceView(View):
})
class DeviceInventoryView(View):
class DeviceInventoryView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_device'
def get(self, request, pk):
@ -993,7 +1010,7 @@ class DeviceInventoryView(View):
class DeviceStatusView(PermissionRequiredMixin, View):
permission_required = 'dcim.napalm_read'
permission_required = ('dcim.view_device', 'dcim.napalm_read')
def get(self, request, pk):
@ -1006,7 +1023,7 @@ class DeviceStatusView(PermissionRequiredMixin, View):
class DeviceLLDPNeighborsView(PermissionRequiredMixin, View):
permission_required = 'dcim.napalm_read'
permission_required = ('dcim.view_device', 'dcim.napalm_read')
def get(self, request, pk):
@ -1023,7 +1040,7 @@ class DeviceLLDPNeighborsView(PermissionRequiredMixin, View):
class DeviceConfigView(PermissionRequiredMixin, View):
permission_required = 'dcim.napalm_read'
permission_required = ('dcim.view_device', 'dcim.napalm_read')
def get(self, request, pk):
@ -1035,7 +1052,8 @@ class DeviceConfigView(PermissionRequiredMixin, View):
})
class DeviceConfigContextView(ObjectConfigContextView):
class DeviceConfigContextView(PermissionRequiredMixin, ObjectConfigContextView):
permission_required = 'dcim.view_device'
object_class = Device
base_template = 'dcim/device.html'
@ -1258,7 +1276,8 @@ class PowerOutletBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Interfaces
#
class InterfaceView(View):
class InterfaceView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_interface'
def get(self, request, pk):
@ -1639,7 +1658,8 @@ class DeviceBulkAddDeviceBayView(PermissionRequiredMixin, BulkComponentCreateVie
# Cables
#
class CableListView(ObjectListView):
class CableListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_cable'
queryset = Cable.objects.prefetch_related(
'termination_a', 'termination_b'
)
@ -1649,7 +1669,8 @@ class CableListView(ObjectListView):
template_name = 'dcim/cable_list.html'
class CableView(View):
class CableView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_cable'
def get(self, request, pk):
@ -1660,10 +1681,11 @@ class CableView(View):
})
class CableTraceView(View):
class CableTraceView(PermissionRequiredMixin, View):
"""
Trace a cable path beginning from the given termination.
"""
permission_required = 'dcim.view_cable'
def get(self, request, model, pk):
@ -1792,7 +1814,8 @@ class CableBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Connections
#
class ConsoleConnectionsListView(ObjectListView):
class ConsoleConnectionsListView(PermissionRequiredMixin, ObjectListView):
permission_required = ('dcim.view_consoleport', 'dcim.view_consoleserverport')
queryset = ConsolePort.objects.select_related(
'device', 'connected_endpoint__device'
).filter(
@ -1822,7 +1845,8 @@ class ConsoleConnectionsListView(ObjectListView):
return csv_data
class PowerConnectionsListView(ObjectListView):
class PowerConnectionsListView(PermissionRequiredMixin, ObjectListView):
permission_required = ('dcim.view_powerport', 'dcim.view_poweroutlet')
queryset = PowerPort.objects.select_related(
'device', '_connected_poweroutlet__device'
).filter(
@ -1852,7 +1876,8 @@ class PowerConnectionsListView(ObjectListView):
return csv_data
class InterfaceConnectionsListView(ObjectListView):
class InterfaceConnectionsListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.interface'
queryset = Interface.objects.select_related(
'device', 'cable', '_connected_interface__device'
).filter(
@ -1894,7 +1919,8 @@ class InterfaceConnectionsListView(ObjectListView):
# Inventory items
#
class InventoryItemListView(ObjectListView):
class InventoryItemListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_inventoryitem'
queryset = InventoryItem.objects.select_related('device', 'manufacturer')
filter = filters.InventoryItemFilter
filter_form = forms.InventoryItemFilterForm
@ -1949,7 +1975,8 @@ class InventoryItemBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Virtual chassis
#
class VirtualChassisListView(ObjectListView):
class VirtualChassisListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_virtualchassis'
queryset = VirtualChassis.objects.select_related('master').annotate(member_count=Count('members'))
table = tables.VirtualChassisTable
filter = filters.VirtualChassisFilter
@ -2184,7 +2211,8 @@ class VirtualChassisRemoveMemberView(PermissionRequiredMixin, GetReturnURLMixin,
# Power panels
#
class PowerPanelListView(ObjectListView):
class PowerPanelListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_powerpanel'
queryset = PowerPanel.objects.select_related(
'site', 'rack_group'
).annotate(
@ -2196,7 +2224,8 @@ class PowerPanelListView(ObjectListView):
template_name = 'dcim/powerpanel_list.html'
class PowerPanelView(View):
class PowerPanelView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_powerpanel'
def get(self, request, pk):
@ -2253,7 +2282,8 @@ class PowerPanelBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Power feeds
#
class PowerFeedListView(ObjectListView):
class PowerFeedListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'dcim.view_powerfeed'
queryset = PowerFeed.objects.select_related(
'power_panel', 'rack'
)
@ -2263,7 +2293,8 @@ class PowerFeedListView(ObjectListView):
template_name = 'dcim/powerfeed_list.html'
class PowerFeedView(View):
class PowerFeedView(PermissionRequiredMixin, View):
permission_required = 'dcim.view_powerfeed'
def get(self, request, pk):

View File

@ -7,6 +7,7 @@ from django.urls import reverse
from dcim.models import Site
from extras.models import ConfigContext, ObjectChange, Tag
from utilities.testing import create_test_user
class TagTestCase(TestCase):
@ -35,8 +36,9 @@ class TagTestCase(TestCase):
class ConfigContextTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['extras.view_configcontext'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -70,11 +72,9 @@ class ConfigContextTestCase(TestCase):
class ObjectChangeTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['extras.view_objectchange'])
self.client = Client()
user = User(username='testuser', email='testuser@example.com')
user.save()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()

View File

@ -96,7 +96,8 @@ class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Config contexts
#
class ConfigContextListView(ObjectListView):
class ConfigContextListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'extras.view_configcontext'
queryset = ConfigContext.objects.all()
filter = filters.ConfigContextFilter
filter_form = ConfigContextFilterForm
@ -104,7 +105,8 @@ class ConfigContextListView(ObjectListView):
template_name = 'extras/configcontext_list.html'
class ConfigContextView(View):
class ConfigContextView(PermissionRequiredMixin, View):
permission_required = 'extras.view_configcontext'
def get(self, request, pk):
@ -173,7 +175,8 @@ class ObjectConfigContextView(View):
# Change logging
#
class ObjectChangeListView(ObjectListView):
class ObjectChangeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'extras.view_objectchange'
queryset = ObjectChange.objects.select_related('user', 'changed_object_type')
filter = filters.ObjectChangeFilter
filter_form = ObjectChangeFilterForm
@ -181,7 +184,8 @@ class ObjectChangeListView(ObjectListView):
template_name = 'extras/objectchange_list.html'
class ObjectChangeView(View):
class ObjectChangeView(PermissionRequiredMixin, View):
permission_required = 'extras.view_objectchange'
def get(self, request, pk):
@ -272,10 +276,11 @@ class ImageAttachmentDeleteView(PermissionRequiredMixin, ObjectDeleteView):
# Reports
#
class ReportListView(View):
class ReportListView(PermissionRequiredMixin, View):
"""
Retrieve all of the available reports from disk and the recorded ReportResult (if any) for each.
"""
permission_required = 'extras.view_reportresult'
def get(self, request):
@ -295,10 +300,11 @@ class ReportListView(View):
})
class ReportView(View):
class ReportView(PermissionRequiredMixin, View):
"""
Display a single Report and its associated ReportResult (if any).
"""
permission_required = 'extras.view_reportresult'
def get(self, request, name):

View File

@ -7,13 +7,15 @@ from django.urls import reverse
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
from ipam.constants import IP_PROTOCOL_TCP
from ipam.models import Aggregate, IPAddress, Prefix, RIR, Role, Service, VLAN, VLANGroup, VRF
from utilities.testing import create_test_user
class VRFTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_vrf'])
self.client = Client()
self.client.force_login(user)
VRF.objects.bulk_create([
VRF(name='VRF 1', rd='65000:1'),
@ -41,8 +43,9 @@ class VRFTestCase(TestCase):
class RIRTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_rir'])
self.client = Client()
self.client.force_login(user)
RIR.objects.bulk_create([
RIR(name='RIR 1', slug='rir-1'),
@ -57,18 +60,13 @@ class RIRTestCase(TestCase):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_rir(self):
rir = RIR.objects.first()
response = self.client.get(rir.get_absolute_url())
self.assertEqual(response.status_code, 200)
class AggregateTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_aggregate'])
self.client = Client()
self.client.force_login(user)
rir = RIR(name='RIR 1', slug='rir-1')
rir.save()
@ -99,8 +97,9 @@ class AggregateTestCase(TestCase):
class RoleTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_role'])
self.client = Client()
self.client.force_login(user)
Role.objects.bulk_create([
Role(name='Role 1', slug='role-1'),
@ -119,8 +118,9 @@ class RoleTestCase(TestCase):
class PrefixTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_prefix'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -151,8 +151,9 @@ class PrefixTestCase(TestCase):
class IPAddressTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_ipaddress'])
self.client = Client()
self.client.force_login(user)
vrf = VRF(name='VRF 1', rd='65000:1')
vrf.save()
@ -183,8 +184,9 @@ class IPAddressTestCase(TestCase):
class VLANGroupTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_vlangroup'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -209,8 +211,9 @@ class VLANGroupTestCase(TestCase):
class VLANTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_vlan'])
self.client = Client()
self.client.force_login(user)
vlangroup = VLANGroup(name='VLAN Group 1', slug='vlan-group-1')
vlangroup.save()
@ -241,8 +244,9 @@ class VLANTestCase(TestCase):
class ServiceTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['ipam.view_service'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()

View File

@ -113,7 +113,8 @@ def add_available_vlans(vlan_group, vlans):
# VRFs
#
class VRFListView(ObjectListView):
class VRFListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_vrf'
queryset = VRF.objects.select_related('tenant')
filter = filters.VRFFilter
filter_form = forms.VRFFilterForm
@ -121,7 +122,8 @@ class VRFListView(ObjectListView):
template_name = 'ipam/vrf_list.html'
class VRFView(View):
class VRFView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vrf'
def get(self, request, pk):
@ -180,7 +182,8 @@ class VRFBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# RIRs
#
class RIRListView(ObjectListView):
class RIRListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_rir'
queryset = RIR.objects.annotate(aggregate_count=Count('aggregates'))
filter = filters.RIRFilter
filter_form = forms.RIRFilterForm
@ -286,7 +289,8 @@ class RIRBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Aggregates
#
class AggregateListView(ObjectListView):
class AggregateListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_aggregate'
queryset = Aggregate.objects.select_related('rir').extra(select={
'child_count': 'SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix',
})
@ -312,7 +316,8 @@ class AggregateListView(ObjectListView):
}
class AggregateView(View):
class AggregateView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_aggregate'
def get(self, request, pk):
@ -398,7 +403,8 @@ class AggregateBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Prefix/VLAN roles
#
class RoleListView(ObjectListView):
class RoleListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_role'
queryset = Role.objects.all()
table = tables.RoleTable
template_name = 'ipam/role_list.html'
@ -433,7 +439,8 @@ class RoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Prefixes
#
class PrefixListView(ObjectListView):
class PrefixListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_prefix'
queryset = Prefix.objects.select_related('site', 'vrf__tenant', 'tenant', 'vlan', 'role')
filter = filters.PrefixFilter
filter_form = forms.PrefixFilterForm
@ -446,7 +453,8 @@ class PrefixListView(ObjectListView):
return self.queryset.annotate_depth(limit=limit)
class PrefixView(View):
class PrefixView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix'
def get(self, request, pk):
@ -489,7 +497,8 @@ class PrefixView(View):
})
class PrefixPrefixesView(View):
class PrefixPrefixesView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix'
def get(self, request, pk):
@ -531,7 +540,8 @@ class PrefixPrefixesView(View):
})
class PrefixIPAddressesView(View):
class PrefixIPAddressesView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_prefix'
def get(self, request, pk):
@ -617,7 +627,8 @@ class PrefixBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# IP addresses
#
class IPAddressListView(ObjectListView):
class IPAddressListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_ipaddress'
queryset = IPAddress.objects.select_related(
'vrf__tenant', 'tenant', 'nat_inside'
).prefetch_related(
@ -629,7 +640,8 @@ class IPAddressListView(ObjectListView):
template_name = 'ipam/ipaddress_list.html'
class IPAddressView(View):
class IPAddressView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_ipaddress'
def get(self, request, pk):
@ -788,7 +800,8 @@ class IPAddressBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# VLAN groups
#
class VLANGroupListView(ObjectListView):
class VLANGroupListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_vlangroup'
queryset = VLANGroup.objects.select_related('site').annotate(vlan_count=Count('vlans'))
filter = filters.VLANGroupFilter
filter_form = forms.VLANGroupFilterForm
@ -822,7 +835,9 @@ class VLANGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
default_return_url = 'ipam:vlangroup_list'
class VLANGroupVLANsView(View):
class VLANGroupVLANsView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlangroup'
def get(self, request, pk):
vlan_group = get_object_or_404(VLANGroup.objects.all(), pk=pk)
@ -861,7 +876,8 @@ class VLANGroupVLANsView(View):
# VLANs
#
class VLANListView(ObjectListView):
class VLANListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_vlan'
queryset = VLAN.objects.select_related('site', 'group', 'tenant', 'role').prefetch_related('prefixes')
filter = filters.VLANFilter
filter_form = forms.VLANFilterForm
@ -869,7 +885,8 @@ class VLANListView(ObjectListView):
template_name = 'ipam/vlan_list.html'
class VLANView(View):
class VLANView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlan'
def get(self, request, pk):
@ -886,7 +903,8 @@ class VLANView(View):
})
class VLANMembersView(View):
class VLANMembersView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_vlan'
def get(self, request, pk):
@ -954,7 +972,8 @@ class VLANBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Services
#
class ServiceListView(ObjectListView):
class ServiceListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'ipam.view_service'
queryset = Service.objects.select_related('device', 'virtual_machine')
filter = filters.ServiceFilter
filter_form = forms.ServiceFilterForm
@ -962,7 +981,8 @@ class ServiceListView(ObjectListView):
template_name = 'ipam/service_list.html'
class ServiceView(View):
class ServiceView(PermissionRequiredMixin, View):
permission_required = 'ipam.view_service'
def get(self, request, pk):

View File

@ -1,25 +1,19 @@
import urllib.parse
from django.contrib.auth import get_user_model
from django.test import Client, TestCase
from django.urls import reverse
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site
from secrets.models import Secret, SecretRole
from utilities.testing import create_test_user
class SecretRoleTestCase(TestCase):
def setUp(self):
TEST_USERNAME = 'testuser'
TEST_PASSWORD = 'testpassword'
User = get_user_model()
User.objects.create(username=TEST_USERNAME, email='testuser@example.com', password=TEST_PASSWORD)
user = create_test_user(permissions=['secrets.view_secretrole'])
self.client = Client()
self.client.login(username=TEST_USERNAME, password=TEST_PASSWORD)
self.client.force_login(user)
SecretRole.objects.bulk_create([
SecretRole(name='Secret Role 1', slug='secret-role-1'),
@ -29,7 +23,7 @@ class SecretRoleTestCase(TestCase):
def test_secretrole_list(self):
url = reverse('secrets:secret_list')
url = reverse('secrets:secretrole_list')
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 200)
@ -38,8 +32,9 @@ class SecretRoleTestCase(TestCase):
class SecretTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['secrets.view_secret'])
self.client = Client()
self.client.force_login(user)
site = Site(name='Site 1', slug='site-1')
site.save()
@ -75,7 +70,7 @@ class SecretTestCase(TestCase):
response = self.client.get('{}?{}'.format(url, urllib.parse.urlencode(params)), follow=True)
self.assertEqual(response.status_code, 200)
def test_configcontext(self):
def test_secret(self):
secret = Secret.objects.first()
response = self.client.get(secret.get_absolute_url(), follow=True)

View File

@ -32,7 +32,8 @@ def get_session_key(request):
# Secret roles
#
class SecretRoleListView(ObjectListView):
class SecretRoleListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'secrets.view_secretrole'
queryset = SecretRole.objects.annotate(secret_count=Count('secrets'))
table = tables.SecretRoleTable
template_name = 'secrets/secretrole_list.html'
@ -67,8 +68,8 @@ class SecretRoleBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Secrets
#
@method_decorator(login_required, name='dispatch')
class SecretListView(ObjectListView):
class SecretListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'secrets.view_secret'
queryset = Secret.objects.select_related('role', 'device')
filter = filters.SecretFilter
filter_form = forms.SecretFilterForm
@ -76,8 +77,8 @@ class SecretListView(ObjectListView):
template_name = 'secrets/secret_list.html'
@method_decorator(login_required, name='dispatch')
class SecretView(View):
class SecretView(PermissionRequiredMixin, View):
permission_required = 'secrets.view_secret'
def get(self, request, pk):
@ -198,7 +199,7 @@ class SecretDeleteView(PermissionRequiredMixin, ObjectDeleteView):
class SecretBulkImportView(BulkImportView):
permission_required = 'ipam.add_vlan'
permission_required = 'secrets.add_secret'
model_form = forms.SecretCSVForm
table = tables.SecretTable
template_name = 'secrets/secret_import.html'

View File

@ -4,13 +4,15 @@ from django.test import Client, TestCase
from django.urls import reverse
from tenancy.models import Tenant, TenantGroup
from utilities.testing import create_test_user
class TenantGroupTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['tenancy.view_tenantgroup'])
self.client = Client()
self.client.force_login(user)
TenantGroup.objects.bulk_create([
TenantGroup(name='Tenant Group 1', slug='tenant-group-1'),
@ -29,8 +31,9 @@ class TenantGroupTestCase(TestCase):
class TenantTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['tenancy.view_tenant'])
self.client = Client()
self.client.force_login(user)
tenantgroup = TenantGroup(name='Tenant Group 1', slug='tenant-group-1')
tenantgroup.save()

View File

@ -18,7 +18,8 @@ from .models import Tenant, TenantGroup
# Tenant groups
#
class TenantGroupListView(ObjectListView):
class TenantGroupListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'tenancy.view_tenantgroup'
queryset = TenantGroup.objects.annotate(tenant_count=Count('tenants'))
table = tables.TenantGroupTable
template_name = 'tenancy/tenantgroup_list.html'
@ -53,7 +54,8 @@ class TenantGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Tenants
#
class TenantListView(ObjectListView):
class TenantListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'tenancy.view_tenant'
queryset = Tenant.objects.select_related('group')
filter = filters.TenantFilter
filter_form = forms.TenantFilterForm
@ -61,7 +63,8 @@ class TenantListView(ObjectListView):
template_name = 'tenancy/tenant_list.html'
class TenantView(View):
class TenantView(PermissionRequiredMixin, View):
permission_required = 'tenancy.view_tenant'
def get(self, request, slug):

View File

@ -1,6 +1,5 @@
from django.contrib import messages
from django.contrib.auth import login as auth_login, logout as auth_logout, update_session_auth_hash
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
@ -74,8 +73,7 @@ class LogoutView(View):
# User profiles
#
@method_decorator(login_required, name='dispatch')
class ProfileView(View):
class ProfileView(LoginRequiredMixin, View):
template_name = 'users/profile.html'
def get(self, request):
@ -85,8 +83,7 @@ class ProfileView(View):
})
@method_decorator(login_required, name='dispatch')
class ChangePasswordView(View):
class ChangePasswordView(LoginRequiredMixin, View):
template_name = 'users/change_password.html'
def get(self, request):
@ -111,8 +108,7 @@ class ChangePasswordView(View):
})
@method_decorator(login_required, name='dispatch')
class UserKeyView(View):
class UserKeyView(LoginRequiredMixin, View):
template_name = 'users/userkey.html'
def get(self, request):
@ -127,10 +123,9 @@ class UserKeyView(View):
})
class UserKeyEditView(View):
class UserKeyEditView(LoginRequiredMixin, View):
template_name = 'users/userkey_edit.html'
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
try:
self.userkey = UserKey.objects.get(user=request.user)
@ -164,7 +159,6 @@ class UserKeyEditView(View):
})
@method_decorator(login_required, name='dispatch')
class SessionKeyDeleteView(LoginRequiredMixin, View):
def get(self, request):

View File

@ -1,4 +1,4 @@
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission, User
from rest_framework.test import APITestCase as _APITestCase
from users.models import Token
@ -22,3 +22,16 @@ class APITestCase(_APITestCase):
self.assertEqual(response.status_code, expected_status, err_message.format(
expected_status, response.status_code, response.data
))
def create_test_user(username='testuser', permissions=list()):
"""
Create a User with the given permissions.
"""
user = User.objects.create_user(username=username)
for perm_name in permissions:
app, codename = perm_name.split('.')
perm = Permission.objects.get(content_type__app_label=app, codename=codename)
user.user_permissions.add(perm)
return user

View File

@ -3,14 +3,16 @@ import urllib.parse
from django.test import Client, TestCase
from django.urls import reverse
from utilities.testing import create_test_user
from virtualization.models import Cluster, ClusterGroup, ClusterType, VirtualMachine
class ClusterGroupTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['virtualization.view_clustergroup'])
self.client = Client()
self.client.force_login(user)
ClusterGroup.objects.bulk_create([
ClusterGroup(name='Cluster Group 1', slug='cluster-group-1'),
@ -29,8 +31,9 @@ class ClusterGroupTestCase(TestCase):
class ClusterTypeTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['virtualization.view_clustertype'])
self.client = Client()
self.client.force_login(user)
ClusterType.objects.bulk_create([
ClusterType(name='Cluster Type 1', slug='cluster-type-1'),
@ -49,8 +52,9 @@ class ClusterTypeTestCase(TestCase):
class ClusterTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['virtualization.view_cluster'])
self.client = Client()
self.client.force_login(user)
clustergroup = ClusterGroup(name='Cluster Group 1', slug='cluster-group-1')
clustergroup.save()
@ -85,8 +89,9 @@ class ClusterTestCase(TestCase):
class VirtualMachineTestCase(TestCase):
def setUp(self):
user = create_test_user(permissions=['virtualization.view_virtualmachine'])
self.client = Client()
self.client.force_login(user)
clustertype = ClusterType(name='Cluster Type 1', slug='cluster-type-1')
clustertype.save()

View File

@ -22,7 +22,8 @@ from .models import Cluster, ClusterGroup, ClusterType, VirtualMachine
# Cluster types
#
class ClusterTypeListView(ObjectListView):
class ClusterTypeListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'virtualization.view_clustertype'
queryset = ClusterType.objects.annotate(cluster_count=Count('clusters'))
table = tables.ClusterTypeTable
template_name = 'virtualization/clustertype_list.html'
@ -57,7 +58,8 @@ class ClusterTypeBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Cluster groups
#
class ClusterGroupListView(ObjectListView):
class ClusterGroupListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'virtualization.view_clustergroup'
queryset = ClusterGroup.objects.annotate(cluster_count=Count('clusters'))
table = tables.ClusterGroupTable
template_name = 'virtualization/clustergroup_list.html'
@ -92,7 +94,8 @@ class ClusterGroupBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
# Clusters
#
class ClusterListView(ObjectListView):
class ClusterListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'virtualization.view_cluster'
queryset = Cluster.objects.select_related('type', 'group', 'site')
table = tables.ClusterTable
filter = filters.ClusterFilter
@ -100,7 +103,8 @@ class ClusterListView(ObjectListView):
template_name = 'virtualization/cluster_list.html'
class ClusterView(View):
class ClusterView(PermissionRequiredMixin, View):
permission_required = 'virtualization.view_cluster'
def get(self, request, pk):
@ -247,7 +251,8 @@ class ClusterRemoveDevicesView(PermissionRequiredMixin, View):
# Virtual machines
#
class VirtualMachineListView(ObjectListView):
class VirtualMachineListView(PermissionRequiredMixin, ObjectListView):
permission_required = 'virtualization.view_virtualmachine'
queryset = VirtualMachine.objects.select_related('cluster', 'tenant', 'role', 'primary_ip4', 'primary_ip6')
filter = filters.VirtualMachineFilter
filter_form = forms.VirtualMachineFilterForm
@ -255,7 +260,8 @@ class VirtualMachineListView(ObjectListView):
template_name = 'virtualization/virtualmachine_list.html'
class VirtualMachineView(View):
class VirtualMachineView(PermissionRequiredMixin, View):
permission_required = 'virtualization.view_virtualmachine'
def get(self, request, pk):
@ -270,7 +276,8 @@ class VirtualMachineView(View):
})
class VirtualMachineConfigContextView(ObjectConfigContextView):
class VirtualMachineConfigContextView(PermissionRequiredMixin, ObjectConfigContextView):
permission_required = 'virtualization.view_virtualmachine'
object_class = VirtualMachine
base_template = 'virtualization/virtualmachine.html'