Login

Email or username authentication with masquerading

Author:
petrilli
Posted:
June 23, 2009
Language:
Python
Version:
1.0
Score:
7 (after 7 ratings)

This backend will allow you to have users login using either their username or the email address as it is in the User model. In addition, it will allow anyone with the staff priveleges to login as another user. The method is to user the user you wish to masquerade as (either email/username) as the username and then a string of the format username/password as the password, where username is the username of the staff member, and password is their password.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
from django.forms.fields import email_re

class EmailBackend(ModelBackend):
    """Allows a user to login using their email address, and not just their
    username. This is a lot more common than a new username. Some things that
    it takes care of for you are:

    - Allow _either_ username or email to be used
    - Allow anyone marked as staff in the database to mascquerade as another
      user by using the user they want to masquerade as as the username and
      using <username>/<password> in the password field, where <username>
      is _their_ username."""

    def _lookup_user(self, username):
        try:
            if email_re.search(username):
                # Looks like an email. Since emails are not case sensitive
                # and many users have a habit of typing them in mixed
                # cases, we will normalize them to lower case. This assumes
                # that the database has done the same thing.
                user = User.objects.get(email=username.lower())
            else:
                user = User.objects.get(username=username)
        except User.DoesNotExist:
                return None

        return user
            
    def authenticate(self, username=None, password=None):
        user = self._lookup_user(username)

        if user:
            if user.check_password(password):
                return user
            elif '/' in password:
                proposed_user = user    # Who we want to be
                (username, password) = password.split('/', 1)
                user = self._lookup_user(username)
                if user and user.is_staff:
                    if user.check_password(password):
                        return proposed_user
        return None
            

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

christian.oudard (on June 26, 2009):

Good snippet. One issue, however; line 39 should use

username, password) = password.split('/', 1)

in case there is a / in the password.

Also, the email_re is unnecessary here. If you just check whether the username matches an email, the validation on the email database field will take care of it:

try:
    user = User.objects.get(email=username.lower())
except User.DoesNotExist:
    user = User.objects.get(username=username)

#

petrilli (on June 28, 2009):

Thanks for the changes. I'm not sure about the expense of hitting the database v. the regex, since it's compiled. I'll do some little micro-benchmarks. I'm not sure it matters, in the end since it's only done on log-in. You're right about the password bit, though.

#

akaihola (on November 15, 2011):

Can't you just do a User.objects.get(email__iexact=username) on line 23?

#

akaihola (on November 15, 2011):

Also, nowadays the correct import for email_re is from django.core.validators import email_re.

#

Please login first before commenting.