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.
- 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
- 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
decoratorUserPassesTestMixin
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.
0 comments:
Post a Comment