Django superuser login trapdoor

Friday 13 August 2010This is 14 years old. Be careful.

I added an admin trapdoor login to a project the other day. This is the technique where a superuser can log in to a site as any other user. My preferred way to do this is to use the standard login form in a clever way: enter the desired user’s name as the username, and both your superuser name and superuser password into the password field.

But this project was modern enough that I could use a Django authentication backend to get the job done:

from django.contrib.auth import login, authenticate
from django.contrib.auth.models import User

# So I can invoked authenticate recursively below
django_authenticate = authenticate

class SuperuserLoginAuthenticationBackend(object):
    """ Let superusers login as regular users. """
    def authenticate(self, username=None, password=None):
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            return None
        # The password should be name/password
        if "@" not in password:
            return None
        supername, superpass = password.split("@", 1)
        superuser = django_authenticate(username=supername, password=superpass)
        if superuser and superuser.is_superuser:
            return user

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

Very nice.

Comments

[gravatar]
Very nice, I've been meaning to cook up something like this for my site, but the implementation here is far nicer than the direction I was thinking of going in, thank you very much
[gravatar]
Why is django_authenticate required?
[gravatar]
@None: because I wanted to call the Django standard authenticate() function, but I'm inside a method called "authenticate" which is shadowing what I want. By assigning the function I want to a new name, I can invoke it without fear of calling myself by mistake.
[gravatar]
Wouldn't you have to call self.authenticate for that to happen? I'm not seeing this shadowing behavior.
def func():
  print "global"

class A(object):
  def func(self):
    print "method"
    func()

A().func()
This prints:
method
global
[gravatar]
Well what do you know? There's always room to learn more...
[gravatar]
Hmm, where's the code to authenticate the user normally? I only see the superuser authentication code here.
[gravatar]
Also, won't this fail if a user's password contains a @?
[gravatar]
@Stavros: this is just one authentication backend, you should also continue to use the existing backends, the ones that currently authenticate your users. This backend should be added to the end of the list of authentication backends. I should have mentioned that in the post. If the user has an @ in their password, they'll still authenticate normally with the earlier backend, only falling through to this one if the other backends can't log them in.
[gravatar]
There's another option that doesn't require any extra UI, just doing a /su/username URL: http://copiousfreetime.blogspot.com/2006/12/django-su.html
[gravatar]
@Leo: your solution is also nice. It will require more steps (log in as staff, then visit su url), and in my experience, it will mean I have to remind people what the su url is, but it's also UI-free, I like it.

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.