Beispiel #1
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(instance, OBJECTCHANGE_ACTION_DELETE)
Beispiel #2
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.
    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(instance, request.user, request.id, OBJECTCHANGE_ACTION_DELETE)
Beispiel #3
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.
    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(instance, OBJECTCHANGE_ACTION_DELETE)
Beispiel #4
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 #5
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()

        # Signals don't include the request context, so we're currying it into the post_delete function ahead of time.
        record_object_deleted = curry(_record_object_deleted, request)

        # Connect our receivers to the post_save and post_delete signals.
        post_save.connect(cache_changed_object,
                          dispatch_uid='record_object_saved')
        post_delete.connect(record_object_deleted,
                            dispatch_uid='record_object_deleted')

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

        # Create records for any cached objects that were created/updated.
        for obj, action in _thread_locals.changed_objects:

            # Record the change
            if hasattr(obj, 'log_change'):
                obj.log_change(request.user, request.id, action)

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

            # Increment metric counters
            if action == OBJECTCHANGE_ACTION_CREATE:
                model_inserts.labels(obj._meta.model_name).inc()
            elif action == OBJECTCHANGE_ACTION_UPDATE:
                model_updates.labels(obj._meta.model_name).inc()

        # Housekeeping: 1% chance of clearing out expired ObjectChanges
        if _thread_locals.changed_objects and 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 #6
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 #7
0
    def test_webhooks_worker(self):

        request_id = uuid.uuid4()

        def dummy_send(_, request, **kwargs):
            """
            A dummy implementation of Session.send() to be used for testing.
            Always returns a 200 HTTP response.
            """
            webhook = Webhook.objects.get(type_create=True)
            signature = generate_signature(request.body, webhook.secret)

            # Validate the outgoing request headers
            self.assertEqual(request.headers['Content-Type'], webhook.http_content_type)
            self.assertEqual(request.headers['X-Hook-Signature'], signature)
            self.assertEqual(request.headers['X-Foo'], 'Bar')

            # Validate the outgoing request body
            body = json.loads(request.body)
            self.assertEqual(body['event'], 'created')
            self.assertEqual(body['timestamp'], job.args[4])
            self.assertEqual(body['model'], 'site')
            self.assertEqual(body['username'], 'testuser')
            self.assertEqual(body['request_id'], str(request_id))
            self.assertEqual(body['data']['name'], 'Site 1')

            return HttpResponse()

        # Enqueue a webhook for processing
        site = Site.objects.create(name='Site 1', slug='site-1')
        enqueue_webhooks(
            instance=site,
            user=self.user,
            request_id=request_id,
            action=ObjectChangeActionChoices.ACTION_CREATE
        )

        # Retrieve the job from queue
        job = self.queue.jobs[0]

        # Patch the Session object with our dummy_send() method, then process the webhook for sending
        with patch.object(Session, 'send', dummy_send) as mock_send:
            process_webhook(*job.args)
Beispiel #8
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()

        # Signals don't include the request context, so we're currying it into the pre_delete function ahead of time.
        record_object_deleted = curry(_record_object_deleted, request)

        # Connect our receivers to the post_save and pre_delete signals.
        post_save.connect(cache_changed_object, dispatch_uid='record_object_saved')
        post_delete.connect(record_object_deleted, dispatch_uid='record_object_deleted')

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

        # Create records for any cached objects that were created/updated.
        for obj, action in _thread_locals.changed_objects:

            # Record the change
            if hasattr(obj, 'log_change'):
                obj.log_change(request.user, request.id, action)

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

        # Housekeeping: 1% chance of clearing out expired ObjectChanges
        if _thread_locals.changed_objects and 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