Monday, December 25, 2017

How to check django staff user first time login in admin panel?

Leave a Comment

I am working on a django project and I am using the default auth app for authentication.
I know that there is a last_login field in user model which stores the user's last login time.

When a staff user logs in first time into the admin panel, I want to check if last_login field is none & redirect him to the change password page.

Where should I put this check?


What I have tried so far:

I have tried to use a custom login form and override the default confirm_login_allowed method on it, but it seems like I can only raise a validation error to block login attempt using these.

I also tried using django.contrib.auth.signals.user_logged_in Signal but that also does not allow me to return a redirect response when last_login is None.

I want to know how I can return a redirect response after the user has been authenticated.

3 Answers

Answers 1

Customise Django admin using AdminSite and use login_form attribute to give the custom login form for the Admin login page.

admin.py

class MyAdminSite(AdminSite):     login_form = CustomAdminLoginForm  admin_site = MyAdminSite(name='myadmin') admin_site.register(User) admin_site.register(Group 

urls.py

When overriding the Admin we have to get rid of Django default admin

from app.admin import admin_site  url(r'^admin/', admin_site.urls) 

forms.py

AuthenticationForm have the confirm_login_allowed method use this to grant permission to login in or not login in.

class CustomAdminLoginForm(AuthenticationForm):     def confirm_login_allowed(self, user):         if user.last_login:             raise ValidationError(mark_safe('Hey first time user please reset your password here... <a href="/test">test</a>'), code='inactive') 

Note: There is lot of edge cases you have to consider for this approach.

  1. What if user not set the password in the first time and how you're going to handle second attempt..? This time last_long not None. Although date_joined comes rescue. last_login == date_joined
  2. But What if the user not set the password in first day and comes next day ?

Edit:

You can use signal to check the logged in user and apply the config_login_allowed logic here...?

from django.contrib.auth.signals import user_logged_in   def change_password_first_time(sender, user, request, **kwargs):     # Your business logic here...  user_logged_in.connect(change_password_first_time) 

Answers 2

I believe that you can subclass the AdminSite and use the user_passes_test decorator UserPassesTestMixin to enforce the test on the login view.

[ - Create your test:

    def last_login_check(user):         return user.last_login is not None 

I would suggest making the test more robust because this one seems a bit flimsy (but answers your initial request)... ]

UserPassesTestMixin inherits from AccessMixin which has a login_url attribute that allows us to set the redirect url.

Override AdminSite:

from django.contrib.admin import AdminSite from django.contrib.auth.decorators import user_passes_test  class MyAdminSite(UserPassesTestMixin, AdminSite):     login_url='/your/redirect/url/'      def test_func(self):         return self.request.user.last_login is not None 

You don't need to add anything else.

To use MyAdminSite (documentation link):

admin_site = MyAdminSite(name='my_project_admin')  admin_site.register(MyModel)... 

Answers 3

Django admin is not that configurable. You should hook into its internal views. Login/logout views are inside Django AdminSite class (the one you usually access by admin.site). My implementation is a bit hacky but small:

Paste at the top of urls.py

from django.contrib import admin from django.contrib.admin import AdminSite from django.contrib.auth.models import User from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect  class MyAdminSite(AdminSite):     def login(self, request, extra_context=None):         new_user = False         user = None          username = request.POST.get('username')  # Hack to find user before its last_login set to now.         if username:             user = User.objects.filter(username=username).first()             if user:                 new_user = user.last_login is None          r = super(MyAdminSite, self).login(request, extra_context)          if new_user and request.user == user and isinstance(r, HttpResponseRedirect):  # Successful logins will result in a redirect.             return HttpResponseRedirect(reverse('admin:password_change'))         return r  admin.site = MyAdminSite() 

If you want a cleaner solution I suggest to use a boolean inside user model instead of relying on last_login so you could just check request.user instead of my hack into request.POST.

You could read AdminSite.login and django.contrib.auth.views.login to see what is actually happening inside Django.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment