Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Migrating a password field to Django

I have used Django before (version 1.2) and generally I like it... it is especially good at getting a brand new project up and running quickly. But, in this case, I'm rewriting and existing system and moving it to Python/Django. So, I already have a MySQL database that has a "users" table in it... this table stores the user's password with the MySQL SHA1 function (no salt, etc).

As part of the migration, I'm going to fix some of the data modeling flaws and port to PostgreSQL.

I would really like to use django.contrib.auth, but I'm unclear what I need to do. I have read the documentation, and know that I can separate the required user information and the "extra" information I have and put it into UserProfile.

But, how to handle the passwords stored in the MySQL db?

Has anyone handled this before? What approach did you take?

like image 313
David S Avatar asked Nov 18 '11 23:11

David S


2 Answers

Here is what I did to get things working. I created a custom authentication backend. Note: I'm using the email address as the username.

Here is my code:

from django.db.models import get_model
from django.contrib.auth.models import User
from hashlib import sha1

class MyUserAuthBackend(object):

    def check_legacy_password(self, db_password, supplied_password):
        return constant_time_compare(sha1(supplied_password).hexdigest(), db_password)


    def authenticate(self, username=None, password=None):
        """ Authenticate a user based on email address as the user name. """
        try:
            user = User.objects.get(email=username)

            if '$' not in user.password:
                if self.check_legacy_password(user.password, password):
                    user.set_password(password)
                    user.save()
                    return user
                else:
                    return None

            else:
                if user.check_password(password):
                    return user

        except User.DoesNotExist:
            return None


    def get_user(self, user_id):
        """ Get a User object from the user_id. """
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

Then I added the following to settings.py:

AUTHENTICATION_BACKENDS = (
    'my_website.my_app.my_file.MyUserAuthBackend',
)

The suggestion from @Dougal appears to be for the next release of Django and was not available for me (I'm using 1.3.1). However, it seems like it will be a better solution.

like image 193
David S Avatar answered Oct 17 '22 14:10

David S


You can probably put it straight into the user_password field - see the Django docs. Since you don't have a salt, try using the format sha1$$password_hash. I haven't investigated to see that it'll work with a blank salt, but that's probably the only way you're going to be able to migrate it without hacking django.contrib.auth or writing your own authentication backend.

Otherwise, you could just set an unusable password (the canonical thing to do is set the field to !) for users and point them to the forgot-password system.

like image 23
Danica Avatar answered Oct 17 '22 14:10

Danica