Last active
March 26, 2023 17:41
-
-
Save bartmachielsen/5571c85ff7eba1e4b6ea2ad2ba7001e1 to your computer and use it in GitHub Desktop.
Django Auditlogging in combination with Django rest framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import unicode_literals | |
import threading | |
import time | |
from django.conf import settings | |
from django.db.models.signals import pre_save | |
from django.utils.functional import curry | |
from django.apps import apps | |
from auditlog.models import LogEntry | |
from auditlog.compat import is_authenticated | |
# Use MiddlewareMixin when present (Django >= 1.10) | |
try: | |
from django.utils.deprecation import MiddlewareMixin | |
except ImportError: | |
MiddlewareMixin = object | |
threadlocal = threading.local() | |
class AuditlogMiddleware(MiddlewareMixin): | |
""" | |
Middleware to couple the request's user to log items. This is accomplished by currying the signal receiver with the | |
user from the request (or None if the user is not authenticated). | |
""" | |
def process_request(self, request): | |
""" | |
Gets the current user from the request and prepares and connects a signal receiver with the user already | |
attached to it. | |
""" | |
# Initialize thread local storage | |
threadlocal.auditlog = { | |
'signal_duid': (self.__class__, time.time()), | |
'remote_addr': request.META.get('REMOTE_ADDR'), | |
} | |
# In case of proxy, set 'original' address | |
if request.META.get('HTTP_X_FORWARDED_FOR'): | |
threadlocal.auditlog['remote_addr'] = request.META.get('HTTP_X_FORWARDED_FOR').split(',')[0] | |
# Connect signal for automatic logging | |
set_actor = curry(self.set_actor, request=request, signal_duid=threadlocal.auditlog['signal_duid']) | |
pre_save.connect(set_actor, sender=LogEntry, dispatch_uid=threadlocal.auditlog['signal_duid'], weak=False) | |
def process_response(self, request, response): | |
""" | |
Disconnects the signal receiver to prevent it from staying active. | |
""" | |
if hasattr(threadlocal, 'auditlog'): | |
pre_save.disconnect(sender=LogEntry, dispatch_uid=threadlocal.auditlog['signal_duid']) | |
return response | |
def process_exception(self, request, exception): | |
""" | |
Disconnects the signal receiver to prevent it from staying active in case of an exception. | |
""" | |
if hasattr(threadlocal, 'auditlog'): | |
pre_save.disconnect(sender=LogEntry, dispatch_uid=threadlocal.auditlog['signal_duid']) | |
return None | |
@staticmethod | |
def set_actor(request, sender, instance, signal_duid, **kwargs): | |
""" | |
Signal receiver with an extra, required 'user' kwarg. This method becomes a real (valid) signal receiver when | |
it is curried with the actor. | |
""" | |
if hasattr(threadlocal, 'auditlog'): | |
if not hasattr(request, 'user') or not is_authenticated(request.user): | |
return | |
if signal_duid != threadlocal.auditlog['signal_duid']: | |
return | |
try: | |
app_label, model_name = settings.AUTH_USER_MODEL.split('.') | |
auth_user_model = apps.get_model(app_label, model_name) | |
except ValueError: | |
auth_user_model = apps.get_model('auth', 'user') | |
if sender == LogEntry and isinstance(request.user, auth_user_model) and instance.actor is None: | |
instance.actor = request.user | |
instance.remote_addr = threadlocal.auditlog['remote_addr'] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Django Rest Framework (https://www.django-rest-framework.org/) in combination with Django-Auditlog (https://django-auditlog.readthedocs.io/) is giving issues that the user = "system" instead of the right username. This is caused by Rest framework that does not properly pass the user.
My change is to modify the auditlog middleware so that the user is retrieved at a later stadium when the user object is available instead of when it is not yet available. You can implement this by adding this middleware to your MIDDLEWARE list instead of the default auditlog one.