Beispiel #1
0
    def __call__(self, request):

        # Initialize an empty list to cache objects being saved.
        _thread_locals.changed_objects = []

        # Assign a random unique ID to the request. This will be used to associate multiple object changes made during
        # the same request.
        request.id = uuid.uuid4()

        # Connect our receivers to the post_save and post_delete signals.
        post_save.connect(handle_changed_object, dispatch_uid='handle_changed_object')
        pre_delete.connect(handle_deleted_object, dispatch_uid='handle_deleted_object')

        # Provide a hook for purging the change cache
        purge_changelog.connect(purge_objectchange_cache)

        # Process the request
        response = self.get_response(request)

        # If the change cache is empty, there's nothing more we need to do.
        if not _thread_locals.changed_objects:
            return response

        # Create records for any cached objects that were changed.
        for instance, action in _thread_locals.changed_objects:

            # Refresh cached custom field values
            if action in [ObjectChangeActionChoices.ACTION_CREATE, ObjectChangeActionChoices.ACTION_UPDATE]:
                if hasattr(instance, 'cache_custom_fields'):
                    instance.cache_custom_fields()

            # Record an ObjectChange if applicable
            if hasattr(instance, 'to_objectchange'):
                objectchange = instance.to_objectchange(action)
                objectchange.user = request.user
                objectchange.request_id = request.id
                objectchange.save()

            # Enqueue webhooks
            enqueue_webhooks(instance, request.user, request.id, action)

            # Increment metric counters
            if action == ObjectChangeActionChoices.ACTION_CREATE:
                model_inserts.labels(instance._meta.model_name).inc()
            elif action == ObjectChangeActionChoices.ACTION_UPDATE:
                model_updates.labels(instance._meta.model_name).inc()
            elif action == ObjectChangeActionChoices.ACTION_DELETE:
                model_deletes.labels(instance._meta.model_name).inc()

        # Housekeeping: 1% chance of clearing out expired ObjectChanges. This applies only to requests which result in
        # one or more changes being logged.
        if settings.CHANGELOG_RETENTION and random.randint(1, 100) == 1:
            cutoff = timezone.now() - timedelta(days=settings.CHANGELOG_RETENTION)
            purged_count, _ = ObjectChange.objects.filter(
                time__lt=cutoff
            ).delete()

        return response
Beispiel #2
0
def _record_object_deleted(request, instance, **kwargs):

    # Record that the object was deleted
    if hasattr(instance, 'log_change'):
        instance.log_change(request.user, request.id,
                            OBJECTCHANGE_ACTION_DELETE)

    # Enqueue webhooks
    enqueue_webhooks(instance, request.user, request.id,
                     OBJECTCHANGE_ACTION_DELETE)

    # Increment metric counters
    model_deletes.labels(instance._meta.model_name).inc()
Beispiel #3
0
def _handle_deleted_object(request, sender, instance, **kwargs):
    """
    Fires when an object is deleted.
    """
    # Record an ObjectChange if applicable
    if hasattr(instance, 'to_objectchange'):
        objectchange = instance.to_objectchange(ObjectChangeActionChoices.ACTION_DELETE)
        objectchange.user = request.user
        objectchange.request_id = request.id
        objectchange.save()

    # Enqueue webhooks
    enqueue_webhooks(instance, request.user, request.id, ObjectChangeActionChoices.ACTION_DELETE)

    # Increment metric counters
    model_deletes.labels(instance._meta.model_name).inc()
Beispiel #4
0
def _record_object_deleted(request, instance, **kwargs):

    # Force resolution of request.user in case it's still a SimpleLazyObject. This seems to happen
    # occasionally during tests, but haven't been able to determine why.
    # Note: We exclude SAML2 login flow deletions, as the call to request.session.flush() throws this assertion
    if request.path_info != '/saml2_auth/acs/':
        assert request.user.is_authenticated

    # Record that the object was deleted
    if hasattr(instance, 'log_change'):
        instance.log_change(request.user, request.id,
                            OBJECTCHANGE_ACTION_DELETE)

    # Enqueue webhooks
    enqueue_webhooks(instance, request.user, request.id,
                     OBJECTCHANGE_ACTION_DELETE)

    # Increment metric counters
    model_deletes.labels(instance._meta.model_name).inc()
Beispiel #5
0
def _handle_deleted_object(request, sender, instance, **kwargs):
    """
    Fires when an object is deleted.
    """
    # Ignore object without the right method
    if not hasattr(instance, "to_objectchange"):
        return

    # Record an object change
    change = instance.to_objectchange(ObjectChangeAction.DELETE)
    change.user = request.user
    change.request_id = request.id
    change.save()

    # Enqueue webhooks
    enqueue_webhooks(instance, request.user, request.id, ObjectChangeAction.DELETE)

    # Increment metric counters
    model_deletes.labels(instance._meta.model_name).inc()