From 4b5d64939df2b187306e58dcf313915968dbb3b8 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Mon, 11 May 2020 11:51:11 -0400 Subject: [PATCH] Introduced ObjectPermissionRequiredMixin --- netbox/dcim/views.py | 3 ++- netbox/netbox/authentication.py | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 netbox/netbox/authentication.py diff --git a/netbox/dcim/views.py b/netbox/dcim/views.py index cd1b4edf4..5afa46295 100644 --- a/netbox/dcim/views.py +++ b/netbox/dcim/views.py @@ -21,6 +21,7 @@ from extras.models import Graph from extras.views import ObjectConfigContextView from ipam.models import Prefix, VLAN from ipam.tables import InterfaceIPAddressTable, InterfaceVLANTable +from netbox.authentication import ObjectPermissionRequiredMixin from utilities.forms import ConfirmationForm from utilities.paginator import EnhancedPaginator from utilities.utils import csv_format @@ -185,7 +186,7 @@ class RegionBulkDeleteView(PermissionRequiredMixin, BulkDeleteView): # Sites # -class SiteListView(PermissionRequiredMixin, ObjectListView): +class SiteListView(ObjectPermissionRequiredMixin, ObjectListView): permission_required = 'dcim.view_site' queryset = Site.objects.prefetch_related('region', 'tenant') filterset = filters.SiteFilterSet diff --git a/netbox/netbox/authentication.py b/netbox/netbox/authentication.py new file mode 100644 index 000000000..58fd4380a --- /dev/null +++ b/netbox/netbox/authentication.py @@ -0,0 +1,40 @@ +from django.contrib.auth.mixins import AccessMixin +from django.contrib.contenttypes.models import ContentType +from django.db.models import Q + +from users.models import ObjectPermission + + +class ObjectPermissionRequiredMixin(AccessMixin): + permission_required = None + + def has_permission(self): + + # First, check whether the user has a model-level permission assigned + if self.request.user.has_perm(self.permission_required): + return True + + # If not, check for an object-level permission + app, codename = self.permission_required.split('.') + action, model_name = codename.split('_') + model = self.queryset.model + obj_permissions = ObjectPermission.objects.filter( + Q(users=self.request.user) | Q(groups__user=self.request.user), + model=ContentType.objects.get_for_model(model), + **{f'can_{action}': True} + ) + if obj_permissions: + + # Update the view's QuerySet to filter only the permitted objects + # TODO: Do this more efficiently + for perm in obj_permissions: + self.queryset = self.queryset.filter(**perm.attrs) + + return True + + return False + + def dispatch(self, request, *args, **kwargs): + if not self.has_permission(): + return self.handle_no_permission() + return super().dispatch(request, *args, **kwargs)