Implement RestrictedQuerySet as a manager

This commit is contained in:
Jeremy Stretch 2020-05-29 16:27:36 -04:00
parent 5b6a6fb63e
commit e23b2c4c4f
16 changed files with 118 additions and 21 deletions

View File

@ -8,6 +8,7 @@ from dcim.fields import ASNField
from dcim.models import CableTermination
from extras.models import CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.querysets import RestrictedQuerySet
from utilities.models import ChangeLoggedModel
from utilities.utils import serialize_object
from .choices import *
@ -66,9 +67,10 @@ class Provider(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
]
@ -115,6 +117,8 @@ class CircuitType(ChangeLoggedModel):
blank=True,
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'description']
class Meta:
@ -300,6 +304,8 @@ class CircuitTermination(CableTermination):
blank=True
)
objects = RestrictedQuerySet.as_manager()
class Meta:
ordering = ['circuit', 'term_side']
unique_together = ['circuit', 'term_side']

View File

@ -1,7 +1,9 @@
from django.db.models import OuterRef, QuerySet, Subquery
from django.db.models import OuterRef, Subquery
from utilities.querysets import RestrictedQuerySet
class CircuitQuerySet(QuerySet):
class CircuitQuerySet(RestrictedQuerySet):
def annotate_sites(self):
"""

View File

@ -25,7 +25,9 @@ from extras.models import ConfigContextModel, CustomFieldModel, ObjectChange, Ta
from extras.utils import extras_features
from utilities.choices import ColorChoices
from utilities.fields import ColorField, NaturalOrderingField
from utilities.querysets import RestrictedQuerySet
from utilities.models import ChangeLoggedModel
from utilities.mptt import TreeManager
from utilities.utils import serialize_object, to_meters
from utilities.validators import ExclusionValidator
from .device_component_templates import (
@ -103,6 +105,8 @@ class Region(MPTTModel, ChangeLoggedModel):
blank=True
)
objects = TreeManager()
csv_headers = ['name', 'slug', 'parent', 'description']
class MPTTMeta:
@ -244,6 +248,8 @@ class Site(ChangeLoggedModel, CustomFieldModel):
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'name', 'slug', 'status', 'region', 'tenant', 'facility', 'asn', 'time_zone', 'description', 'physical_address',
'shipping_address', 'latitude', 'longitude', 'contact_name', 'contact_phone', 'contact_email', 'comments',
@ -326,6 +332,8 @@ class RackGroup(MPTTModel, ChangeLoggedModel):
blank=True
)
objects = TreeManager()
csv_headers = ['site', 'parent', 'name', 'slug', 'description']
class Meta:
@ -388,6 +396,8 @@ class RackRole(ChangeLoggedModel):
blank=True,
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'color', 'description']
class Meta:
@ -526,6 +536,8 @@ class Rack(ChangeLoggedModel, CustomFieldModel):
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'site', 'group', 'name', 'facility_id', 'tenant', 'status', 'role', 'type', 'serial', 'asset_tag', 'width',
'u_height', 'desc_units', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
@ -821,6 +833,8 @@ class RackReservation(ChangeLoggedModel):
max_length=200
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['site', 'rack_group', 'rack', 'units', 'tenant', 'user', 'description']
class Meta:
@ -900,6 +914,8 @@ class Manufacturer(ChangeLoggedModel):
blank=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'description']
class Meta:
@ -982,9 +998,10 @@ class DeviceType(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
clone_fields = [
'manufacturer', 'u_height', 'is_full_depth', 'subdevice_role',
]
@ -1206,6 +1223,8 @@ class DeviceRole(ChangeLoggedModel):
blank=True,
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'color', 'vm_role', 'description']
class Meta:
@ -1263,6 +1282,8 @@ class Platform(ChangeLoggedModel):
blank=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description']
class Meta:
@ -1429,6 +1450,8 @@ class Device(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'name', 'device_role', 'tenant', 'manufacturer', 'device_type', 'platform', 'serial', 'asset_tag', 'status',
'site', 'rack_group', 'rack_name', 'position', 'face', 'comments',
@ -1741,9 +1764,10 @@ class VirtualChassis(ChangeLoggedModel):
max_length=30,
blank=True
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['master', 'domain']
class Meta:
@ -1813,6 +1837,8 @@ class PowerPanel(ChangeLoggedModel):
max_length=50
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['site', 'rack_group', 'name']
class Meta:
@ -1916,9 +1942,10 @@ class PowerFeed(ChangeLoggedModel, CableTermination, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'site', 'power_panel', 'rack_group', 'rack', 'name', 'status', 'type', 'supply', 'phase', 'voltage',
'amperage', 'max_utilization', 'comments',
@ -2084,6 +2111,8 @@ class Cable(ChangeLoggedModel):
null=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'termination_a_type', 'termination_a_id', 'termination_b_type', 'termination_b_id', 'type', 'status', 'label',
'color', 'length', 'length_unit',

View File

@ -6,6 +6,7 @@ from dcim.choices import *
from dcim.constants import *
from extras.models import ObjectChange
from utilities.fields import NaturalOrderingField
from utilities.querysets import RestrictedQuerySet
from utilities.ordering import naturalize_interface
from utilities.utils import serialize_object
from .device_components import (
@ -26,6 +27,7 @@ __all__ = (
class ComponentTemplateModel(models.Model):
objects = RestrictedQuerySet.as_manager()
class Meta:
abstract = True

View File

@ -16,6 +16,7 @@ from extras.models import ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.fields import NaturalOrderingField
from utilities.ordering import naturalize_interface
from utilities.querysets import RestrictedQuerySet
from utilities.query_functions import CollateAsChar
from utilities.utils import serialize_object
from virtualization.choices import VMInterfaceTypeChoices
@ -41,6 +42,8 @@ class ComponentModel(models.Model):
blank=True
)
objects = RestrictedQuerySet.as_manager()
class Meta:
abstract = True

View File

@ -12,6 +12,7 @@ from django.template import Template, Context
from django.urls import reverse
from rest_framework.utils.encoders import JSONEncoder
from utilities.querysets import RestrictedQuerySet
from utilities.utils import deepmerge, render_jinja2
from extras.choices import *
from extras.constants import *
@ -670,6 +671,8 @@ class ObjectChange(models.Model):
editable=False
)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'time', 'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id',
'related_object_type', 'related_object_id', 'object_repr', 'object_data',

View File

@ -6,6 +6,7 @@ from taggit.models import TagBase, GenericTaggedItemBase
from utilities.choices import ColorChoices
from utilities.fields import ColorField
from utilities.models import ChangeLoggedModel
from utilities.querysets import RestrictedQuerySet
#
@ -21,6 +22,8 @@ class Tag(TagBase, ChangeLoggedModel):
blank=True,
)
objects = RestrictedQuerySet.as_manager()
def get_absolute_url(self):
return reverse('extras:tag', args=[self.slug])

View File

@ -2,6 +2,8 @@ from collections import OrderedDict
from django.db.models import Q, QuerySet
from utilities.querysets import RestrictedQuerySet
class CustomFieldQueryset:
"""
@ -19,7 +21,7 @@ class CustomFieldQueryset:
yield obj
class ConfigContextQuerySet(QuerySet):
class ConfigContextQuerySet(RestrictedQuerySet):
def get_for_object(self, obj):
"""

View File

@ -1,6 +1,7 @@
from django.db import models
from ipam.lookups import Host, Inet
from utilities.querysets import RestrictedQuerySet
class IPAddressManager(models.Manager):
@ -13,5 +14,5 @@ class IPAddressManager(models.Manager):
then re-cast this value to INET() so that records will be ordered properly. We are essentially re-casting each
IP address as a /32 or /128.
"""
qs = super().get_queryset()
qs = RestrictedQuerySet(self.model, using=self._db)
return qs.order_by(Inet(Host('address')))

View File

@ -12,6 +12,7 @@ from dcim.models import Device, Interface
from extras.models import CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.models import ChangeLoggedModel
from utilities.querysets import RestrictedQuerySet
from utilities.utils import serialize_object
from virtualization.models import VirtualMachine
from .choices import *
@ -74,9 +75,10 @@ class VRF(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'rd', 'tenant', 'enforce_unique', 'description']
clone_fields = [
'tenant', 'enforce_unique', 'description',
@ -131,6 +133,8 @@ class RIR(ChangeLoggedModel):
blank=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'is_private', 'description']
class Meta:
@ -179,9 +183,10 @@ class Aggregate(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['prefix', 'rir', 'date_added', 'description']
clone_fields = [
'rir', 'date_added', 'description',
@ -274,6 +279,8 @@ class Role(ChangeLoggedModel):
blank=True,
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'weight', 'description']
class Meta:
@ -360,9 +367,9 @@ class Prefix(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = PrefixQuerySet.as_manager()
tags = TaggableManager(through=TaggedItem)
csv_headers = [
'prefix', 'vrf', 'tenant', 'site', 'vlan_group', 'vlan', 'status', 'role', 'is_pool', 'description',
@ -631,9 +638,9 @@ class IPAddress(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = IPAddressManager()
tags = TaggableManager(through=TaggedItem)
csv_headers = [
'address', 'vrf', 'tenant', 'status', 'role', 'device', 'virtual_machine', 'interface', 'is_primary',
@ -828,6 +835,8 @@ class VLANGroup(ChangeLoggedModel):
blank=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'site', 'description']
class Meta:
@ -923,9 +932,10 @@ class VLAN(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['site', 'group', 'vid', 'name', 'tenant', 'status', 'role', 'description']
clone_fields = [
'site', 'group', 'tenant', 'status', 'role', 'description',
@ -1039,9 +1049,10 @@ class Service(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['device', 'virtual_machine', 'name', 'protocol', 'port', 'description']
class Meta:

View File

@ -1,7 +1,7 @@
from django.db.models import QuerySet
from utilities.querysets import RestrictedQuerySet
class PrefixQuerySet(QuerySet):
class PrefixQuerySet(RestrictedQuerySet):
def annotate_depth(self, limit=None):
"""

View File

@ -17,6 +17,7 @@ from dcim.models import Device
from extras.models import CustomFieldModel, TaggedItem
from extras.utils import extras_features
from utilities.models import ChangeLoggedModel
from utilities.querysets import RestrictedQuerySet
from .exceptions import InvalidKey
from .hashers import SecretValidationHasher
from .querysets import UserKeyQuerySet
@ -268,6 +269,8 @@ class SecretRole(ChangeLoggedModel):
blank=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'description']
class Meta:
@ -333,9 +336,10 @@ class Secret(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
plaintext = None
csv_headers = ['device', 'role', 'name', 'plaintext']

View File

@ -7,6 +7,8 @@ from taggit.managers import TaggableManager
from extras.models import CustomFieldModel, ObjectChange, TaggedItem
from extras.utils import extras_features
from utilities.models import ChangeLoggedModel
from utilities.mptt import TreeManager
from utilities.querysets import RestrictedQuerySet
from utilities.utils import serialize_object
@ -40,6 +42,8 @@ class TenantGroup(MPTTModel, ChangeLoggedModel):
blank=True
)
objects = TreeManager()
csv_headers = ['name', 'slug', 'parent', 'description']
class Meta:
@ -104,9 +108,10 @@ class Tenant(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'group', 'description', 'comments']
clone_fields = [
'group', 'description',

19
netbox/utilities/mptt.py Normal file
View File

@ -0,0 +1,19 @@
from mptt.managers import TreeManager as TreeManager_
from mptt.querysets import TreeQuerySet as TreeQuerySet_
from django.db.models import Manager
from .querysets import RestrictedQuerySet
class TreeQuerySet(TreeQuerySet_, RestrictedQuerySet):
"""
Mate django-mptt's TreeQuerySet with our RestrictedQuerySet for permissions enforcement.
"""
pass
class TreeManager(Manager.from_queryset(TreeQuerySet), TreeManager_):
"""
Extend django-mptt's TreeManager to incorporate RestrictedQuerySet().
"""
pass

View File

@ -27,7 +27,7 @@ class RestrictedQuerySet(QuerySet):
# Determine what constraints (if any) have been placed on this user for this action and model
# TODO: Find a better way to ensure permissions are cached
if not hasattr(user, '_object_perm_cache'):
user.get_all_permisisons()
user.get_all_permissions()
obj_perm_attrs = user._object_perm_cache[permission_required]
# Filter the queryset to include only objects with allowed attributes

View File

@ -9,6 +9,7 @@ from dcim.models import Device
from extras.models import ConfigContextModel, CustomFieldModel, TaggedItem
from extras.utils import extras_features
from utilities.models import ChangeLoggedModel
from utilities.querysets import RestrictedQuerySet
from .choices import *
@ -40,6 +41,8 @@ class ClusterType(ChangeLoggedModel):
blank=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'description']
class Meta:
@ -79,6 +82,8 @@ class ClusterGroup(ChangeLoggedModel):
blank=True
)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'slug', 'description']
class Meta:
@ -145,9 +150,10 @@ class Cluster(ChangeLoggedModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = ['name', 'type', 'group', 'site', 'comments']
clone_fields = [
'type', 'group', 'tenant', 'site',
@ -269,9 +275,10 @@ class VirtualMachine(ChangeLoggedModel, ConfigContextModel, CustomFieldModel):
content_type_field='obj_type',
object_id_field='obj_id'
)
tags = TaggableManager(through=TaggedItem)
objects = RestrictedQuerySet.as_manager()
csv_headers = [
'name', 'status', 'role', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
]