Преглед на файлове

Implement RestrictedQuerySet as a manager

Jeremy Stretch преди 5 години
родител
ревизия
e23b2c4c4f

+ 7 - 1
netbox/circuits/models.py

@@ -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']

+ 4 - 2
netbox/circuits/querysets.py

@@ -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):
         """

+ 32 - 3
netbox/dcim/models/__init__.py

@@ -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',

+ 2 - 0
netbox/dcim/models/device_component_templates.py

@@ -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

+ 3 - 0
netbox/dcim/models/device_components.py

@@ -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
 

+ 3 - 0
netbox/extras/models/models.py

@@ -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',

+ 3 - 0
netbox/extras/models/tags.py

@@ -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])
 

+ 3 - 1
netbox/extras/querysets.py

@@ -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):
         """

+ 2 - 1
netbox/ipam/managers.py

@@ -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')))

+ 17 - 6
netbox/ipam/models.py

@@ -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:

+ 2 - 2
netbox/ipam/querysets.py

@@ -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):
         """

+ 5 - 1
netbox/secrets/models.py

@@ -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']
 

+ 6 - 1
netbox/tenancy/models.py

@@ -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 - 0
netbox/utilities/mptt.py

@@ -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

+ 1 - 1
netbox/utilities/querysets.py

@@ -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

+ 9 - 2
netbox/virtualization/models.py

@@ -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',
     ]