Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
206 views
in Technique[技术] by (71.8m points)

python - Is it possible to implement a "change password at next logon" type feature in the django admin?

I want to be able to set an option in the user's settings that forces them to change their password upon the next login to the admin interface. Is this possible? How would it go about being implemented? I'm using the default auth model right now but not opposed to modifying or changing it. Thanks for any help.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'm actually in the process of doing this myself. You need three components: a user profile (if not already in use on your site), a middleware component, and a pre_save signal.

My code for this is in an app named 'accounts'.

# myproject/accounts/models.py

from django.db import models
from django.db.models import signals
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    force_password_change = models.BooleanField(default=False)

def create_user_profile_signal(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

def password_change_signal(sender, instance, **kwargs):
    try:
        user = User.objects.get(username=instance.username)
        if not user.password == instance.password:
          profile = user.get_profile()
          profile.force_password_change = False
          profile.save()
    except User.DoesNotExist:
        pass

signals.pre_save.connect(password_change_signal, sender=User, dispatch_uid='accounts.models')

signals.post_save.connect(create_user_profile_signal, sender=User, dispatch_uid='accounts.models')

First, we create a UserProfile with a foreign key to User. The force_password_change boolean will, as its name describes, be set to true for a user whenever you want to force them to change their password. You could do anything here though. In my organization, we also chose to implement a mandatory change every 90 days, so I also have a DateTimeField that stores the last time a user changed their password. You then set that in the pre_save signal, password_changed_signal.

Second, we have the create_user_profile_signal. This is mostly added just for completeness. If you're just now adding user profiles into your project, you'll need a post_save signal that will create a UserProfile every time a User is created. This accomplishes that task.

Third, we have the password_changed_signal. This is a pre_save signal because at this point in the process the actual row in the User table hasn't be updated. Therefore, we can access both the previous password and the new password about to be saved. If the two don't match, that means the user has changed their password, and we can then reset the force_password_change boolean. This would be the point, also where you would take care of any other things you've added such as setting the DateTimeField previously mentioned.

The last two lines attach the two functions to their appropriate signals.

If you haven't already, you will also need to add the following line to your project's settings.py (changing the app label and model name to match your setup):

AUTH_PROFILE_MODULE = 'accounts.UserProfile'

That covers the basics. Now we need a middleware component to check the status of our force_password_change flag (and any other necessary checks).

# myproject/accounts/middleware.py

from django.http import HttpResponseRedirect

import re

class PasswordChangeMiddleware:
    def process_request(self, request):
        if request.user.is_authenticated() and 
            re.match(r'^/admin/?', request.path) and 
            not re.match(r'^/admin/password_change/?', request.path):

            profile = request.user.get_profile()
            if profile.force_password_change:
                return HttpResponseRedirect('/admin/password_change/')

This very simple middleware hooks into the process_request stage of the page loading process. It checks that 1) the user has already logged in, 2) they are trying to access some page in the admin, and 3) the page they are accessing is not the password change page itself (otherwise, you'd get an infinite loop of redirects). If all of these are true and the force_password_change flag has been set to True, then the user is redirected to the password change page. They will not be able to navigate anywhere else until they change their password (firing the pre_save signal discussed previously).

Finally, you just need to add this middleware to your project's settings.py (again, changing the import path as necessary):

MIDDLEWARE_CLASSES = (
    # Other middleware here
    'myproject.accounts.middleware.PasswordChangeMiddleware',
)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...