Просмотр исходного кода

Update ObjectImportView to support ObjectPermissions

Jeremy Stretch 5 лет назад
Родитель
Сommit
cae412d280
2 измененных файлов с 32 добавлено и 8 удалено
  1. 3 3
      netbox/dcim/views.py
  2. 29 5
      netbox/utilities/views.py

+ 3 - 3
netbox/dcim/views.py

@@ -627,8 +627,8 @@ class DeviceTypeDeleteView(ObjectDeleteView):
     default_return_url = 'dcim:devicetype_list'
 
 
-class DeviceTypeImportView(PermissionRequiredMixin, ObjectImportView):
-    permission_required = [
+class DeviceTypeImportView(ObjectImportView):
+    additional_permissions = [
         'dcim.add_devicetype',
         'dcim.add_consoleporttemplate',
         'dcim.add_consoleserverporttemplate',
@@ -639,7 +639,7 @@ class DeviceTypeImportView(PermissionRequiredMixin, ObjectImportView):
         'dcim.add_rearporttemplate',
         'dcim.add_devicebaytemplate',
     ]
-    model = DeviceType
+    queryset = DeviceType.objects.all()
     model_form = forms.DeviceTypeImportForm
     related_object_forms = OrderedDict((
         ('console-ports', forms.ConsolePortTemplateImportForm),

+ 29 - 5
netbox/utilities/views.py

@@ -571,21 +571,29 @@ class BulkCreateView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
         })
 
 
-class ObjectImportView(GetReturnURLMixin, View):
+class ObjectImportView(GetReturnURLMixin, ObjectPermissionRequiredMixin, View):
     """
     Import a single object (YAML or JSON format).
+
+    queryset: Base queryset for the objects being created
+    model_form: The ModelForm used to create individual objects
+    related_object_forms: A dictionary mapping of forms to be used for the creation of related (child) objects
+    template_name: The name of the template
     """
-    model = None
+    queryset = None
     model_form = None
     related_object_forms = dict()
     template_name = 'utilities/obj_import.html'
 
+    def get_required_permission(self):
+        return get_permission_for_model(self.queryset.model, 'add')
+
     def get(self, request):
         form = ImportForm()
 
         return render(request, self.template_name, {
             'form': form,
-            'obj_type': self.model._meta.verbose_name,
+            'obj_type': self.queryset.model._meta.verbose_name,
             'return_url': self.get_return_url(request),
         })
 
@@ -615,12 +623,17 @@ class ObjectImportView(GetReturnURLMixin, View):
 
                         # Save the primary object
                         obj = model_form.save()
+
+                        # Enforce object-level permissions
+                        self.queryset.get(pk=obj.pk)
+
                         logger.debug(f"Created {obj} (PK: {obj.pk})")
 
                         # Iterate through the related object forms (if any), validating and saving each instance.
                         for field_name, related_object_form in self.related_object_forms.items():
                             logger.debug("Processing form for related objects: {related_object_form}")
 
+                            related_obj_pks = []
                             for i, rel_obj_data in enumerate(data.get(field_name, list())):
 
                                 f = related_object_form(obj, rel_obj_data)
@@ -630,7 +643,8 @@ class ObjectImportView(GetReturnURLMixin, View):
                                         f.data[subfield_name] = field.initial
 
                                 if f.is_valid():
-                                    f.save()
+                                    related_obj = f.save()
+                                    related_obj_pks.append(related_obj.pk)
                                 else:
                                     # Replicate errors on the related object form to the primary form for display
                                     for subfield_name, errors in f.errors.items():
@@ -639,9 +653,19 @@ class ObjectImportView(GetReturnURLMixin, View):
                                             model_form.add_error(None, err_msg)
                                     raise AbortTransaction()
 
+                            # Enforce object-level permissions on related objects
+                            model = related_object_form.Meta.model
+                            if model.objects.filter(pk__in=related_obj_pks).count() != len(related_obj_pks):
+                                raise ObjectDoesNotExist
+
                 except AbortTransaction:
                     pass
 
+                except ObjectDoesNotExist:
+                    msg = "Object creation failed due to object-level permissions violation"
+                    logger.debug(msg)
+                    form.add_error(None, msg)
+
             if not model_form.errors:
                 logger.info(f"Import object {obj} (PK: {obj.pk})")
                 messages.success(request, mark_safe('Imported object: <a href="{}">{}</a>'.format(
@@ -673,7 +697,7 @@ class ObjectImportView(GetReturnURLMixin, View):
 
         return render(request, self.template_name, {
             'form': form,
-            'obj_type': self.model._meta.verbose_name,
+            'obj_type': self.queryset.model._meta.verbose_name,
             'return_url': self.get_return_url(request),
         })