Browse Source

Fixes #3489: Prevent exception triggered by webhook upon object deletion

Jeremy Stretch 6 năm trước cách đây
mục cha
commit
355910e182
2 tập tin đã thay đổi với 26 bổ sung12 xóa
  1. 4 0
      CHANGELOG.md
  2. 22 12
      netbox/extras/middleware.py

+ 4 - 0
CHANGELOG.md

@@ -7,6 +7,10 @@ v2.6.4 (FUTURE)
 * [#3318](https://github.com/netbox-community/netbox/issues/3318) - Increase length of platform name and slug to 64 characters
 * [#3318](https://github.com/netbox-community/netbox/issues/3318) - Increase length of platform name and slug to 64 characters
 * [#3341](https://github.com/netbox-community/netbox/issues/3341) - Add Inline Vlan Editing
 * [#3341](https://github.com/netbox-community/netbox/issues/3341) - Add Inline Vlan Editing
 
 
+## Bug Fixes
+
+* [#3489](https://github.com/netbox-community/netbox/issues/3489) - Prevent exception triggered by webhook upon object deletion
+
 v2.6.3 (2019-09-04)
 v2.6.3 (2019-09-04)
 
 
 ## New Features
 ## New Features

+ 22 - 12
netbox/extras/middleware.py

@@ -6,6 +6,7 @@ from datetime import timedelta
 from django.conf import settings
 from django.conf import settings
 from django.db.models.signals import post_delete, post_save
 from django.db.models.signals import post_delete, post_save
 from django.utils import timezone
 from django.utils import timezone
+from django.utils.functional import curry
 from django_prometheus.models import model_deletes, model_inserts, model_updates
 from django_prometheus.models import model_deletes, model_inserts, model_updates
 
 
 from .constants import (
 from .constants import (
@@ -18,10 +19,11 @@ from .webhooks import enqueue_webhooks
 _thread_locals = threading.local()
 _thread_locals = threading.local()
 
 
 
 
-def cache_changed_object(sender, instance, **kwargs):
+def handle_changed_object(sender, instance, **kwargs):
     """
     """
-    Cache an object being created or updated for the changelog.
+    Fires when an object is created or updated
     """
     """
+    # Queue the object and a new ObjectChange for processing once the request completes
     if hasattr(instance, 'to_objectchange'):
     if hasattr(instance, 'to_objectchange'):
         action = OBJECTCHANGE_ACTION_CREATE if kwargs['created'] else OBJECTCHANGE_ACTION_UPDATE
         action = OBJECTCHANGE_ACTION_CREATE if kwargs['created'] else OBJECTCHANGE_ACTION_UPDATE
         objectchange = instance.to_objectchange(action)
         objectchange = instance.to_objectchange(action)
@@ -30,15 +32,22 @@ def cache_changed_object(sender, instance, **kwargs):
         )
         )
 
 
 
 
-def cache_deleted_object(sender, instance, **kwargs):
+def _handle_deleted_object(request, sender, instance, **kwargs):
     """
     """
-    Cache an object being deleted for the changelog.
+    Fires when an object is deleted
     """
     """
+    # Record an Object Change
     if hasattr(instance, 'to_objectchange'):
     if hasattr(instance, 'to_objectchange'):
         objectchange = instance.to_objectchange(OBJECTCHANGE_ACTION_DELETE)
         objectchange = instance.to_objectchange(OBJECTCHANGE_ACTION_DELETE)
-        _thread_locals.changed_objects.append(
-            (instance, objectchange)
-        )
+        objectchange.user = request.user
+        objectchange.request_id = request.id
+        objectchange.save()
+
+    # Enqueue webhooks
+    enqueue_webhooks(instance, request.user, request.id, OBJECTCHANGE_ACTION_DELETE)
+
+    # Increment metric counters
+    model_deletes.labels(instance._meta.model_name).inc()
 
 
 
 
 def purge_objectchange_cache(sender, **kwargs):
 def purge_objectchange_cache(sender, **kwargs):
@@ -54,7 +63,7 @@ class ObjectChangeMiddleware(object):
 
 
         1. Create an ObjectChange to reflect the modification to the object in the changelog.
         1. Create an ObjectChange to reflect the modification to the object in the changelog.
         2. Enqueue any relevant webhooks.
         2. Enqueue any relevant webhooks.
-        3. Increment metric counter for the event type
+        3. Increment the metric counter for the event type.
 
 
     The post_save and post_delete signals are employed to catch object modifications, however changes are recorded a bit
     The post_save and post_delete signals are employed to catch object modifications, however changes are recorded a bit
     differently for each. Objects being saved are cached into thread-local storage for action *after* the response has
     differently for each. Objects being saved are cached into thread-local storage for action *after* the response has
@@ -74,9 +83,12 @@ class ObjectChangeMiddleware(object):
         # the same request.
         # the same request.
         request.id = uuid.uuid4()
         request.id = uuid.uuid4()
 
 
+        # Signals don't include the request context, so we're currying it into the post_delete function ahead of time.
+        handle_deleted_object = curry(_handle_deleted_object, request)
+
         # Connect our receivers to the post_save and post_delete signals.
         # Connect our receivers to the post_save and post_delete signals.
-        post_save.connect(cache_changed_object, dispatch_uid='cache_changed_object')
-        post_delete.connect(cache_deleted_object, dispatch_uid='cache_deleted_object')
+        post_save.connect(handle_changed_object, dispatch_uid='cache_changed_object')
+        post_delete.connect(handle_deleted_object, dispatch_uid='cache_deleted_object')
 
 
         # Provide a hook for purging the change cache
         # Provide a hook for purging the change cache
         purge_changelog.connect(purge_objectchange_cache)
         purge_changelog.connect(purge_objectchange_cache)
@@ -104,8 +116,6 @@ class ObjectChangeMiddleware(object):
                 model_inserts.labels(obj._meta.model_name).inc()
                 model_inserts.labels(obj._meta.model_name).inc()
             elif objectchange.action == OBJECTCHANGE_ACTION_UPDATE:
             elif objectchange.action == OBJECTCHANGE_ACTION_UPDATE:
                 model_updates.labels(obj._meta.model_name).inc()
                 model_updates.labels(obj._meta.model_name).inc()
-            elif objectchange.action == OBJECTCHANGE_ACTION_DELETE:
-                model_deletes.labels(obj._meta.model_name).inc()
 
 
         # Housekeeping: 1% chance of clearing out expired ObjectChanges. This applies only to requests which result in
         # Housekeeping: 1% chance of clearing out expired ObjectChanges. This applies only to requests which result in
         # one or more changes being logged.
         # one or more changes being logged.