Best way to extend django authentication / authorization

I am adding Terms of Service requirements to our website and trying to find the best way to handle this in the Django authentication framework.

For simplicity, here is a UserProfile model:

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

So basically I want to check that accepted_tos_at not None (or actually more than the date of the latest TOS version). If it passes this test, we usually authenticate, but if it is None , all views except login and tos_display are not available.

What I hung is how you should do it globally? I would prefer not to add user_passes_test to the decorators for each of my views, and also would like to avoid checking this permission in each of my views. There must be a cleaner way.

+4
source share
2 answers

Typically, when you talk about what should apply to all views, you are talking about middleware. In your case, this is relatively straightforward:

 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, it checks if the requested URL is not a login or TOS. This prevents endless loops if redirection is needed. Then you check accepted_tos_at . I suggested that you are just going to keep the latest revision date as a parameter, so you will have to change this if you have other plans. If TOS needs to be accepted, the user is redirected to the TOS view, otherwise the middleware returns None , in which Django will continue to process the request as usual.

Just add middleware to MIDDLEWARE_CLASSES and you will be gold.

+7
source

I recently did authentication using class and mixin views. See LoginRequiredMixin in django-braces for an example.

This means that all of your views (which you want to protect) should include a common mix, but this is the purest way I know this.

Usage (from docs ) is as follows:

 from django.views.generic import TemplateView from braces.views import LoginRequiredMixin class SomeSecretView(LoginRequiredMixin, TemplateView): template_name = "path/to/template.html" def get(self, request): return self.render_to_response({}) 

You probably want to define your own mixin, which might look like this (not verified):

 class AcceptedTOSRequiredMixin(object): def dispatch(self, request, *args, **kwargs): profile = request.user.get_profile() if not profile or profile.accepted_tos_at is None: return HttpResponseForbidden() # return a forbidden response. return super(AcceptedTOSRequiredMixin, self).dispatch(request, *args, **kwargs) 

Other ways to do this include decorating them at the URL level, but this is ugly in my opinion (I would be happy to dig up an example of what it looks like if that helps).

+1
source

Source: https://habr.com/ru/post/1432362/


All Articles