Django + Tastypie - user_logged_in signal doesn

2019-06-03 06:00发布

问题:

I have a Tastypie ModelResource defining various endpoints which work as expected.

I've now configured this ModelResource to have BasicAuthentication:

class Meta:
    authentication = BasicAuthentication()

I've defined a couple of test users through the Django Admin Interface.

As per the Django 1.7 Documentation, I've created a signals.py in which I register a couple of test signals:

from django.core.signals import request_finished
from django.contrib.auth.signals import user_logged_in
from django.dispatch import receiver

@receiver(user_logged_in)
def on_login(sender, request, user, **kwargs):
    print('******* LOGIN DETECTED *********')

@receiver(request_finished)
def on_request_finished(sender, **kwargs):
    print('******* REQUEST FINISHED *******')

This is loaded successfully by my AppConfig in apps.py:

from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'myapp'
    verbose_name = 'verbose description of myapp'

    def ready(self):
        import myapp.signals

I use the Requests library to successfully communicate with my API, providing basic authentication credentials for one of my test users:

auth = HTTPBasicAuth(username, getpass('Enter password: '))
response = requests.get(self.url(endpoint), auth=self.auth, params = params)

The REQUEST FINISHED print shows in the Django server's output, but LOGIN DETECTED does not.

Do we have to manually fire a login signal when using Tastypie, or use some other inbuilt/custom Authentication class besides BasicAuthentication? In other words, is it expected that the user_logged_in signal wouldn't fire automatically?

Any info would be greatly appreciated.

回答1:

Having inspected the Tastypie source code, it ties into the Django auth backend by calling the authenticate method, thus doesn't trigger the usual login cycle of which authenticate is one component. Consequently the login method is never called, and thus the user_logged_in signal never fires.

I ended up providing the signal myself by extending BasicAuthentication and overriding is_authenticated like so:

class MyBasicAuthentication(BasicAuthentication):
    def is_authenticated(self, request, **kwargs):
        orig_user = request.user
        has_authenticated = super(MyBasicAuthentication, self).is_authenticated(request, **kwargs)
        if has_authenticated:
            was_authenticated = orig_user == request.user
            if not was_authenticated:
                user_logged_in.send(sender=self.__class__, request=request, user=request.user)
        return has_authenticated