diff --git a/netbox/utilities/api.py b/netbox/utilities/api.py index 41002dd20..ef2650535 100644 --- a/netbox/utilities/api.py +++ b/netbox/utilities/api.py @@ -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') diff --git a/netbox/utilities/permissions.py b/netbox/utilities/permissions.py index 80d564db4..be5c0189e 100644 --- a/netbox/utilities/permissions.py +++ b/netbox/utilities/permissions.py @@ -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) diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index e73a55dc7..a86b5ccc5 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -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