Introduce restrict_queryset()

This commit is contained in:
Jeremy Stretch 2020-05-29 14:12:24 -04:00
parent 8786bb25c5
commit 58989b85c8
3 changed files with 24 additions and 17 deletions

View File

@ -6,7 +6,7 @@ from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist, PermissionDenied
from django.db import transaction
from django.db.models import ManyToManyField, ProtectedError, Q
from django.db.models import ManyToManyField, ProtectedError
from django.urls import reverse
from rest_framework.exceptions import APIException
from rest_framework.permissions import BasePermission
@ -16,7 +16,7 @@ from rest_framework.serializers import Field, ModelSerializer, ValidationError
from rest_framework.viewsets import ModelViewSet as _ModelViewSet
from netbox.api import TokenPermissions
from users.models import ObjectPermission
from utilities.permissions import restrict_queryset
from .utils import dict_to_filter_params, dynamic_import
@ -340,12 +340,7 @@ class ModelViewSet(_ModelViewSet):
permission_required = TokenPermissions.perms_map[request.method][0] % kwargs
# Update the view's QuerySet to filter only the permitted objects
obj_perm_attrs = request.user._object_perm_cache[permission_required]
attrs = Q()
for perm_attrs in obj_perm_attrs:
if perm_attrs:
attrs |= Q(**perm_attrs)
self.queryset = self.queryset.filter(attrs)
self.queryset = restrict_queryset(self.queryset, request.user, permission_required)
def dispatch(self, request, *args, **kwargs):
logger = logging.getLogger('netbox.api.views.ModelViewSet')

View File

@ -1,4 +1,5 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
def get_permission_for_model(model, action):
@ -33,3 +34,20 @@ def resolve_permission(name):
raise ValueError(f"Unknown app/model for {name}")
return content_type, action
def restrict_queryset(queryset, user, permission_required):
"""
Filters a QuerySet to return only the objects on which the specified user has been granted the specified
permission.
:param queryset: Base QuerySet to be restricted
:param user: User instance
:param permission_required: Name of the required permission (e.g. "dcim.view_site")
"""
obj_perm_attrs = user._object_perm_cache[permission_required]
attrs = Q()
for perm_attrs in obj_perm_attrs:
if perm_attrs:
attrs |= Q(**perm_attrs)
return queryset.filter(attrs)

View File

@ -8,7 +8,7 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.mixins import AccessMixin
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured, ObjectDoesNotExist, ValidationError
from django.db import transaction, IntegrityError
from django.db.models import ManyToManyField, ProtectedError, Q
from django.db.models import ManyToManyField, ProtectedError
from django.forms import Form, ModelMultipleChoiceField, MultipleHiddenInput, Textarea
from django.http import HttpResponse, HttpResponseServerError
from django.shortcuts import get_object_or_404, redirect, render
@ -26,10 +26,9 @@ from django_tables2 import RequestConfig
from extras.models import CustomField, CustomFieldValue, ExportTemplate
from extras.querysets import CustomFieldQueryset
from users.models import ObjectPermission
from utilities.exceptions import AbortTransaction
from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm
from utilities.permissions import get_permission_for_model
from utilities.permissions import get_permission_for_model, restrict_queryset
from utilities.utils import csv_format, prepare_cloned_fields
from .error_handlers import handle_protectederror
from .forms import ConfirmationForm, ImportForm
@ -67,12 +66,7 @@ class ObjectPermissionRequiredMixin(AccessMixin):
# Update the view's QuerySet to filter only the permitted objects
if user.is_authenticated and not user.is_superuser:
obj_perm_attrs = user._object_perm_cache[permission_required]
attrs = Q()
for perm_attrs in obj_perm_attrs:
if perm_attrs:
attrs |= Q(**perm_attrs)
self.queryset = self.queryset.filter(attrs)
self.queryset = restrict_queryset(self.queryset, user, permission_required)
return True