Users in initial data fixture
Asked Answered
F

7

75

I'm creating a few users by default in my fixtures/initial_data.json so as to have some testing "subjects." The problem I'm experiencing is password generation. I could set the password in the 'fields', but that won't generate a hashed password:

[
    { "model": "auth.user",
        "pk": 1,
        "fields": {
            "username": "user1",
            "password": "password"
        }
    }
]

I need a way to generate the user's password. Do I have to do this manually and generate a string like {hash_method}${salt}${hashed_password} like Django does?

Foin answered 5/11, 2011 at 0:18 Comment(1)
I was using "django.contrib.auth.models.user" and it did NOT work!Diesel
O
113

What might be easier in this case (and if you only need a few users) is to create some fake user accounts through the admin (including passwords) and then dump the users to a fixtures file using dumpdata:

$ python manage.py dumpdata auth.User --indent 4 > users.json

which will automatically create the fixtures for you and can be used later with loaddata

(You could just create one fake account and use the hash in the rest of your fixtures if you needed lots of test users)

https://docs.djangoproject.com/en/dev/ref/django-admin/#dumpdata-appname-appname-appname-model

Owen answered 5/11, 2011 at 0:55 Comment(5)
Hmm - trying to do this for me results in a blank dump here.Beehive
Okay - works from a local sqlite db, but not the main mysql db. Is this a security feature?Beehive
"You could just create one fake account and use the hash in the rest of your fixtures if you needed lots of test users" -> this tip has saved me! Thanks a lot :)Peddler
This worked for me with PostgreSQL. I was also able to use an online JSON->YAML converter since I like using YAML for fixtures. Make sure everything is indented properly, though.Pharmaceutical
This is what I use, but I'm feeling bad about it. Besides, I have to extract my "fake user" from a gigant dump file (real-world application, mind you). Then I remove the personal data from the dump (maybe leaving date_joined), and this is my fixture for the tests. However, using JSON, I can't even comment next to the password hash, the plaintext value... I'm considering throwing in passwords as are, and then in test's setUp method running User.set_password(...) for User.objects.all().Chromogen
C
27

OK, I agree with the answers, but let me answer the original questions.

How to get the password "as Django would have hashed it"?

Let's look a the file django/contrib/auth/hashers.py:

def make_password(password, salt=None, hasher='default'):
    """
    Turn a plain-text password into a hash for database storage

    Same as encode() but generates a new random salt.  If
    password is None or blank then UNUSABLE_PASSWORD will be
    returned which disallows logins.
    """
    # ...

See this example session:

./manage.py shell

>>> from django.contrib.auth.hashers import make_password, HASHERS
>>> make_password('test')
'pbkdf2_sha256$10000$vkRy7QauoLLj$ry+3xm3YX+YrSXbri8s3EcXDIrx5ceM+xQjtpLdw2oE='

# fix salt:
>>> make_password('test', 'abc')
'pbkdf2_sha256$10000$abc$MqJS5OAgSmf9SD9mfoY8fgLo8sSKmEcef0AjjMp1Q7w='

# use different (maybe faster, maybe unsafe!) hasher

In [12]: HASHERS
Out[12]:
{'bcrypt': <django.contrib.auth.hashers.BCryptPasswordHasher object at 0x29c6d50>,
 'crypt': <django.contrib.auth.hashers.CryptPasswordHasher object at 0x29c6f50>,
 'md5': <django.contrib.auth.hashers.MD5PasswordHasher object at 0x29c6e10>,
 'pbkdf2_sha1': <django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher object at 0x29c6bd0>,
 'pbkdf2_sha256': <django.contrib.auth.hashers.PBKDF2PasswordHasher object at 0x29c6cd0>,
 'sha1': <django.contrib.auth.hashers.SHA1PasswordHasher object at 0x29c6dd0>,
 'unsalted_md5': <django.contrib.auth.hashers.UnsaltedMD5PasswordHasher object at 0x29c6ed0>,
 'unsalted_sha1': <django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher object at 0x29c6e50>}

In [14]: [make_password('test', hasher=name) for name in HASHERS]
Out[14]:
['sha1$LdKsAbJRjlVP$2eb2346387cc510f576f2f11eebdfe18b20d1be1',
 'pbkdf2_sha256$10000$Ck8gtWQJnJ9x$M/OqP548d5KcPqFuVRgXb84unjYbYDH6oyimbDndE3k=',
 'pbkdf2_sha1$10000$BJqRu5OwylVF$hUvMLIzBujt9kPbML/dei1vLiMQ=',
 'crypt$$d9grSeqDhMFek',
 '098f6bcd4621d373cade4e832627b4f6',
 'sha1$$a94a8fe5ccb19ba61c4c0873d391e987982fbbd3',
 'bcrypt$$2a$12$WlJP5zm2lmdJ4g/pSE1xF.d/8w.XRT5mo/vGlkKdglBtzcxKw7XJS',
 'md5$txHYmSYJKhD4$69286d4a1abd348fbddc9df7687e2ed4']

You can also manually use the hasher's encode method, but the above utility function has you covered in an easier way.

Chromogen answered 5/5, 2014 at 11:51 Comment(0)
T
16

I came across the same problem while writing fixtures for tests. Here's how I'm handling this in unit tests.

Creating data from admin and dumping them into fixture works but I don't quite like being dependent on manually doing it. So here's what I do -

Create the fixture just as you've done and then in the setUp method, set_passwords for the users.

user_fixture.json

[
    { "model": "auth.user",
        "pk": 1,
        "fields": {
            "username": "user1",
            "password": "password"
        }
    }
]

test_user.py

def setUp(self):
    self.User = get_user_model()

    # Fix the passwords of fixtures
    for user in self.User.objects.all():
        user.set_password(user.password)
        user.save()

This way I can cleanly write the passwords in fixture and refer back to them when I need to and create more fixtures simply by editing the fixture file.

Toneme answered 16/6, 2016 at 8:14 Comment(1)
Upvoted this one because even if I use Tomasz' answer (which is still a good one btw and answers the original question as well), I still will need a helper script. So, in my initial setup.sh I use ./manage.py createsuperuser ... but use a helper script like Gaurav's to set the password. itsafire's answer is good too but I prefer using a script like this. Just my own preference.Dingus
S
6

Adding to the answer of @GauravButola, I created a custom management command to load the fixture and fix the passwords in one step. This is useful when not using a testing framework to do the setup of the passwords. The example is working with django 1.11 and probably earlier versions.

In your app providing the fixture add your management command (this is python3 times, so I omitted the init pys):

yourapp/
    models.py
    fixtures/
        initial_data.json
    management/
        commands/
            initdata.py

With initdata.py looking like this:

from django.core.management import BaseCommand, call_command
from django.contrib.auth.models import User
# from yourapp.models import User # if you have a custom user


class Command(BaseCommand):
    help = "DEV COMMAND: Fill databasse with a set of data for testing purposes"

    def handle(self, *args, **options):
        call_command('loaddata','initial_data')
        # Fix the passwords of fixtures
        for user in User.objects.all():
            user.set_password(user.password)
            user.save()

Now you can load your initial_data and have valid passwords by calling:

./manage.py initdata
Smashing answered 19/5, 2017 at 8:10 Comment(0)
F
5

Dump Users information from DataBase:

$ python manage.py dumpdata auth.User --indent 4 > users.json

Import / load data especific JSON fixture:

$ python manage.py loaddata users.json
Fant answered 2/3, 2018 at 8:42 Comment(0)
G
0

Use the method make_password(admin_password). It's in django.contrib.auth.hashers

Goldoni answered 28/11, 2023 at 15:10 Comment(0)
S
-1

pretty sure you do. it shouldn't be that difficult. easiest way would be to look at the function user.set_password i think it is, and look how they do it. you can probably call the function from a python terminal and then copy it into your initial data!

Sabinesabino answered 5/11, 2011 at 0:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.