From 1be4a57bd4dc38873b59bca21781e92205c4e240 Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Thu, 1 Jul 2021 15:33:39 -0400 Subject: [PATCH] Closes #6345: Introduce PermissionsViolation exception for use in generic views --- netbox/netbox/views/generic.py | 36 ++++++++++++++++++---------------- netbox/utilities/exceptions.py | 8 ++++++++ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/netbox/netbox/views/generic.py b/netbox/netbox/views/generic.py index 957979e4c..c43519e01 100644 --- a/netbox/netbox/views/generic.py +++ b/netbox/netbox/views/generic.py @@ -18,7 +18,7 @@ from django_tables2.export import TableExport from extras.models import CustomField, ExportTemplate from utilities.error_handlers import handle_protectederror -from utilities.exceptions import AbortTransaction +from utilities.exceptions import AbortTransaction, PermissionsViolation from utilities.forms import ( BootstrapMixin, BulkRenameForm, ConfirmationForm, CSVDataField, ImportForm, TableConfigForm, restrict_form_fields, ) @@ -290,7 +290,8 @@ class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): obj = form.save() # Check that the new object conforms with any assigned object-level permissions - self.queryset.get(pk=obj.pk) + if not self.queryset.filter(pk=obj.pk).first(): + raise PermissionsViolation() msg = '{} {}'.format( 'Created' if object_created else 'Modified', @@ -318,7 +319,7 @@ class ObjectEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): else: return redirect(self.get_return_url(request, obj)) - except ObjectDoesNotExist: + except PermissionsViolation: msg = "Object save failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) @@ -480,7 +481,7 @@ class BulkCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): # Enforce object-level permissions if self.queryset.filter(pk__in=[obj.pk for obj in new_objs]).count() != len(new_objs): - raise ObjectDoesNotExist + raise PermissionsViolation # If we make it to this point, validation has succeeded on all new objects. msg = "Added {} {}".format(len(new_objs), model._meta.verbose_name_plural) @@ -494,7 +495,7 @@ class BulkCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): except IntegrityError: pass - except ObjectDoesNotExist: + except PermissionsViolation: msg = "Object creation failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) @@ -565,7 +566,8 @@ class ObjectImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): obj = model_form.save() # Enforce object-level permissions - self.queryset.get(pk=obj.pk) + if not self.queryset.filter(pk=obj.pk).first(): + raise PermissionsViolation() logger.debug(f"Created {obj} (PK: {obj.pk})") @@ -601,7 +603,7 @@ class ObjectImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): except AbortTransaction: pass - except ObjectDoesNotExist: + except PermissionsViolation: msg = "Object creation failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) @@ -712,7 +714,7 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): # Enforce object-level permissions if self.queryset.filter(pk__in=[obj.pk for obj in new_objs]).count() != len(new_objs): - raise ObjectDoesNotExist + raise PermissionsViolation # Compile a table containing the imported objects obj_table = self.table(new_objs) @@ -730,7 +732,7 @@ class BulkImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): except ValidationError: pass - except ObjectDoesNotExist: + except PermissionsViolation: msg = "Object import failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) @@ -845,7 +847,7 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): # Enforce object-level permissions if self.queryset.filter(pk__in=[obj.pk for obj in updated_objects]).count() != len(updated_objects): - raise ObjectDoesNotExist + raise PermissionsViolation if updated_objects: msg = 'Updated {} {}'.format(len(updated_objects), model._meta.verbose_name_plural) @@ -857,7 +859,7 @@ class BulkEditView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): except ValidationError as e: messages.error(self.request, "{} failed validation: {}".format(obj, e)) - except ObjectDoesNotExist: + except PermissionsViolation: msg = "Object update failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) @@ -952,7 +954,7 @@ class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): # Enforce constrained permissions if self.queryset.filter(pk__in=renamed_pks).count() != len(selected_objects): - raise ObjectDoesNotExist + raise PermissionsViolation messages.success(request, "Renamed {} {}".format( len(selected_objects), @@ -960,7 +962,7 @@ class BulkRenameView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View): )) return redirect(self.get_return_url(request)) - except ObjectDoesNotExist: + except PermissionsViolation: msg = "Object update failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) @@ -1146,7 +1148,7 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View # Enforce object-level permissions if self.queryset.filter(pk__in=[obj.pk for obj in new_objs]).count() != len(new_objs): - raise ObjectDoesNotExist + raise PermissionsViolation messages.success(request, "Added {} {}".format( len(new_components), self.queryset.model._meta.verbose_name_plural @@ -1156,7 +1158,7 @@ class ComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View else: return redirect(self.get_return_url(request)) - except ObjectDoesNotExist: + except PermissionsViolation: msg = "Component creation failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) @@ -1238,12 +1240,12 @@ class BulkComponentCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, # Enforce object-level permissions if self.queryset.filter(pk__in=[obj.pk for obj in new_components]).count() != len(new_components): - raise ObjectDoesNotExist + raise PermissionsViolation except IntegrityError: pass - except ObjectDoesNotExist: + except PermissionsViolation: msg = "Component creation failed due to object-level permissions violation" logger.debug(msg) form.add_error(None, msg) diff --git a/netbox/utilities/exceptions.py b/netbox/utilities/exceptions.py index 77a915d9c..4ba62bc01 100644 --- a/netbox/utilities/exceptions.py +++ b/netbox/utilities/exceptions.py @@ -9,6 +9,14 @@ class AbortTransaction(Exception): pass +class PermissionsViolation(Exception): + """ + Raised when an operation was prevented because it would violate the + allowed permissions. + """ + pass + + class RQWorkerNotRunningException(APIException): """ Indicates the temporary inability to enqueue a new task (e.g. custom script execution) because no RQ worker