Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to extend django authentication/authorization

I'm adding in a Terms of Service acceptance requirement to our site and am trying to figure out the best way to handle this within Django's authentication framework.

For simplicity's sake, here's a UserProfile model:

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    accepted_tos_at = models.DateTimeField(default=None, editable=False, null=True)

So basically what I want to do is to check that accepted_tos_at is not None (or in reality greater than the date of the last TOS revision). If it passes this test then we authenticate normally, but if it is None all views except login and tos_display are inaccessible.

What I'm hung up on is how should you go about doing this globally? I would rather not add in user_passes_test decorators to every one of my views and likewise I'd like to avoid testing for this permission in every one of my views. There must be a cleaner way.

like image 296
AndrewJesaitis Avatar asked Dec 27 '22 18:12

AndrewJesaitis


1 Answers

Generally, when you're talking about something that should apply to every view, then, you're talking about middleware. In your case, this is relatively straight-forward:

class AcceptTOSMiddleware(object):
    def process_request(request):
        login_url = reverse('login')
        tos_url = reverse('tos_display')
        if request.path not in [login_url, tos_url]:
            profile = request.user.get_profile()
            if profile.accepted_tos_at is None or \
               profile.accepted_tos_at < settings.LAST_TOS_REVISION:
                return HttpResponseRedirect(tos_url)
        return None

First, this checks if the requested URL is not the login or TOS views. This prevents infinite loops if a redirect is necessary. Then, you check the accepted_tos_at. I assumed you're simply going to store the last revision date as a setting, so you'll need to modify that if you have other plans. If the TOS needs to be accepted, the user is redirected to the TOS view, otherwise, the middleware returns None which tells Django to keep processing the request as normal.

Just add the middleware to MIDDLEWARE_CLASSES and you're golden.

like image 168
Chris Pratt Avatar answered Jan 06 '23 01:01

Chris Pratt