DjangoRestFramework TokenAuthentication + django-auditlog = AnonymousUser/None as Actor
See original GitHub issue[QUESTION] I think this must be expected behavior, but I wanted to be sure. Django-auditlog works fine in my GUI, properly logging any model change that I register using Django’s built-in SessionAuthentication. However, I have an API set up with DRF using their TokenAuthentication. When using the API, the django-auditlog entries have the Actor set to ‘None’ in the logs, and troubleshooting inside of django-auditlog’s middleware shows user set to AnonymousUser and is_authenticated() set to False.
From my basic troubleshooting, it seems that django-auditlog’s middleware runs BEFORE DRF’s TokenAuthentication gets ran (which sets request.user).
I solved this, by making a simple middleware that I run just before django-auditlog’s middleware. This is the code I used:
from re import sub
from rest_framework.authtoken.models import Token
class SetUserIfTokenAuth(object):
""" django-auditlog works via middleware. Unfortunately, middleware triggers before DjangoRestFramework's
TokenAuthentication. This middleware checks for a Token and sets the User prior to Auditlog running.
"""
def process_request(self, request):
header_token = request.META.get('HTTP_AUTHORIZATION', None)
if header_token is not None:
try:
token = sub('Token ', '', request.META.get('HTTP_AUTHORIZATION', None))
token_obj = Token.objects.get(key=token)
request.user = token_obj.user
except Token.DoesNotExist:
pass
return None
My settings.py looks like this:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'<projectname>.middleware.auth.SetUserIfTokenAuth',
'auditlog.middleware.AuditlogMiddleware',
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.DjangoFilterBackend',
'rest_framework_filters.backends.DjangoFilterBackend',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
}
So, did I solve this the right way? Is this expected? Did I do something hilariously wrong? Thanks in advance guys!
Oh It’s worth noting that I’m running Django 1.9.8, django-auditlog 0.4.3 (i tried 0.3.3 as well), and DjangoRestFramework 3.4.0.
Issue Analytics
- State:
- Created 6 years ago
- Comments:5 (2 by maintainers)
Thanks for your help!
For anyone else that stumbles upon this, here is the code I used to test that Auditlog is working through the API and setting actor correctly (There might be a few unused imports, sorry!):
@Jaredn Doesn’t that duplicate the process of setting the auth token ? the custom middleware will run, and then drf will reset the token ?