1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Update ObjectImportView to support ObjectPermissions

This commit is contained in:
Jeremy Stretch
2020-06-02 14:19:08 -04:00
parent a62b98ac50
commit cae412d280
2 changed files with 32 additions and 8 deletions

View File

@ -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),

View File

@ -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),
})