diff --git a/netbox/extras/api/serializers_/scripts.py b/netbox/extras/api/serializers_/scripts.py index bed98cdf1..e3052e021 100644 --- a/netbox/extras/api/serializers_/scripts.py +++ b/netbox/extras/api/serializers_/scripts.py @@ -98,19 +98,26 @@ class ScriptModuleSerializer(ValidatedModelSerializer): self._save_upload(upload_file, validated_data) elif data_file := validated_data.get('data_file'): self._sync_data_file(data_file, validated_data) + created = False try: - return super().create(validated_data) + instance = super().create(validated_data) + created = True + return instance except IntegrityError: # ManagedFile has a single unique constraint: (file_root, file_path), so an # IntegrityError here always means a duplicate file name regardless of which # path (upload or data_file sync) set validated_data['file_path']. - # Clean up the file written to disk before the failed DB insert. - if file_path := validated_data.get('file_path'): - storage = storages.create_storage(storages.backends["scripts"]) - storage.delete(file_path) raise serializers.ValidationError( _("A script module with this file name already exists.") ) + finally: + # On any failure, remove the file written to disk so no orphans are left behind. + # Uses best-effort deletion (ignores errors) to avoid masking the original exception. + if not created and (file_path := validated_data.get('file_path')): + try: + storages.create_storage(storages.backends["scripts"]).delete(file_path) + except Exception: + pass class ScriptSerializer(ValidatedModelSerializer): diff --git a/netbox/netbox/authentication/__init__.py b/netbox/netbox/authentication/__init__.py index b41a1d3ea..792976adb 100644 --- a/netbox/netbox/authentication/__init__.py +++ b/netbox/netbox/authentication/__init__.py @@ -142,7 +142,11 @@ class ObjectPermissionMixin: # Also accept permissions for proxy models whose concrete model matches the object's. model = obj._meta.concrete_model if model._meta.label_lower != '.'.join((app_label, model_name)): - if apps.get_model(app_label, model_name)._meta.concrete_model != model: + try: + perm_model = apps.get_model(app_label, model_name) + except LookupError: + perm_model = None + if not perm_model or perm_model._meta.concrete_model != model: raise ValueError(_("Invalid permission {permission} for model {model}").format( permission=perm, model=model ))