Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django : Case insensitive matching of username from auth user?

Tags:

Django by-default implements username as case sensitive, now for authentication I have written my own Authentication Backend to handle case insensitive usernames while authentication.

As shown in : http://blog.shopfiber.com/?p=220

Now, the problem is :

I have various views and util methods which compares username to some stings.

i.e.

request.user.username == username_from_some_other_system_as_str

Now, if username is yugal then:

request.user.username == 'Yugal' # Returns False

Now, it should return True [ What I wanted to achieve ]

For that I remember from C++ days, Operator Overloading. But I don't think simply doing that for django's auth user would be a good idea, since auth user is tightly bound with django. Also, overloading == will make it case-insensitive for the whole class not just for the username field.

So, how should I go about this username case-insensitivity even when compared throughout.

Note:

  • Creating a get_username method that returns lower-case username always is not possible, since it would require all code to be re-factored to use it. You can do it for your code for once, but not possible if you are using 3rd party django apps.

  • I know user.username.lower() = something.lower() is possible but is bug prone and not the write solution for something so often used in a multi-developer setup.

  • I have used SomeModel.objects.filter(username__iexact=username), wherever possible. But that still leaves the system vulnerable to a mistake by any of un-aware developer.

======================================

Figured out the solution conceptually, but could not make it work ( Help ) :

####### Custom CharField for username case-insensitivity #######
from django.db.models.fields import CharField
class iUnicode:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        if isinstance(other, str) or isinstance(other, unicode):
            return self.value.lower() == other.lower()
        if isinstance(other, self.__class__):
            return other == self.value

    def __unicode__(self):
        return unicode(self.value)
    def __str__(self):
        return self.__unicode__()


class UsernameCharField(CharField):
    def to_python(self, value):  # Its not getting called
        unicode_val = super(CharField, self).to_python(value)
        return iUnicode(unicode_val)

if User._meta.local_fields[1].name == 'username':
    User._meta.local_fields[1] = UsernameCharField(max_length=30)
    User._meta.local_fields[1].model = User
################################################################

I assume to_python is used to convert the value received from database to unicode in python. But, I guess my to_python is not getting called.

This will also ensure case-insensitivity in 3rd party apps and would not require any re-factoring. It will patch the User at its core. I will be adding this to __init__.py of my first INSTALLED_APP

What am I doing wrong ?

like image 583
Yugal Jindle Avatar asked Nov 02 '12 07:11

Yugal Jindle


People also ask

Is Django username case sensitive?

By default, Django does a case-sensitive check on usernames.

Is username unique in Django user model?

If you want to use django's default authentication backend you cannot make username non unique. You will have to implement a class with get_user(user_id) and authenticate(request, **credentials) methods for a custom backend.


2 Answers

As of Django 1.5, making usernames case insensitive is straightforward:

class MyUserManager(BaseUserManager):
    def get_by_natural_key(self, username):
        return self.get(username__iexact=username)

Sources: 1, 2

like image 67
Josh Kelley Avatar answered Sep 22 '22 18:09

Josh Kelley


I modified few lines in my registration and login process that seems to work for me. With my solution usernames will still be displayed like the user wrote them when registering, but it will not allow others to use the same username written differently. It also allows users to login without worrying about writing the case sensitive username.

I modified the registration form to search for case insensitive usernames.

This is line from my validation of username, it searches for user with this username.

User._default_manager.get(username__iexact=username)

Then I needed to allow users to login with case insensitive usernames.

From my login view:

username = request.POST['username']
password = request.POST['password']
caseSensitiveUsername = username
try:
  findUser = User._default_manager.get(username__iexact=username)
except User.DoesNotExist:
  findUser = None
if findUser is not None:
  caseSensitiveUsername = findUser.get_username
user = auth.authenticate(username=caseSensitiveUsername, password=password)
like image 27
Mazvél Avatar answered Sep 21 '22 18:09

Mazvél