question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

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:closed
  • Created 6 years ago
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
Jaredncommented, Aug 25, 2017

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!):

import json
from django.core.urlresolvers import reverse
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase, APIRequestFactory, APIClient
from rest_framework.authtoken.models import Token
from auditlog.models import LogEntry

from model_mommy import mommy

User = get_user_model()


class GenericSetupMixin(object):
    def setUp(self):
        # Superuser Credentials
        self.superuser_name = 'the_django_test_user'
        self.superuser = User.objects.create_superuser(self.superuser_name, 'john@snow.com', 'testpass')
        self.superuser.save()
        self.token = Token.objects.get_or_create(user=self.superuser)
        self.token = Token.objects.get(user=self.superuser)
        self.client = APIClient()
        self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key)


class DjangoAuditlog(GenericSetupMixin, APITestCase):
    def test_create_generates_full_auditlog_entry(self):
        url = '/some/url/here/'
        name = 'imarealboy'
        data = {
            'name': name,
        }
        response = self.client.post(url, data=data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertTrue(name in response.content.decode('utf-8'))

        # now check auditlog history
        logentry = LogEntry.objects.latest('timestamp')
        self.assertTrue(name in logentry.changes_str)
        self.assertTrue(self.superuser_name == logentry.actor.username)
0reactions
ao1000commented, Oct 25, 2017

@Jaredn Doesn’t that duplicate the process of setting the auth token ? the custom middleware will run, and then drf will reset the token ?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Authentication - Django REST framework
This authentication scheme uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native ...
Read more >
How to Implement Token Authentication using Django REST ...
In this tutorial you are going to learn how to implement Token-based authentication using Django REST Framework (DRF).
Read more >
Automatic Logging doesn't log the Actor · Issue #115 - GitHub
I have installed the plugin and on an Django Rest Framework instance. All the logging is working as expected, except the Logging of...
Read more >
Django Rest Framework - Authentication credentials were not ...
If it is indeed an anonymous user, these steps might help: Make sure you have 'rest_framework. authtoken' in INSTALLED_APPS in your settings.py ....
Read more >
Token Authentication for django-rest-framework - /var/
Then you can use the “Test auth” button that works only on authenticated users and returns their username. Finally, notice that after you...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found