password management for amo Users
This commit is contained in:
Родитель
817e5b42b2
Коммит
ae8f7349de
|
@ -1,4 +1,7 @@
|
|||
from datetime import datetime
|
||||
import hashlib
|
||||
import random
|
||||
import string
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
|
@ -7,6 +10,20 @@ import amo
|
|||
from translations.fields import TranslatedField
|
||||
|
||||
|
||||
def get_hexdigest(algorithm, salt, raw_password):
|
||||
return hashlib.new(algorithm, salt + raw_password).hexdigest()
|
||||
|
||||
|
||||
def rand_string(length):
|
||||
return ''.join(random.choice(string.letters) for i in xrange(length))
|
||||
|
||||
|
||||
def create_password(algorithm, raw_password):
|
||||
salt = get_hexdigest(algorithm, rand_string(12), rand_string(12))[:64]
|
||||
hsh = get_hexdigest(algorithm, salt, raw_password)
|
||||
return '$'.join([algorithm, salt, hsh])
|
||||
|
||||
|
||||
class UserProfile(amo.ModelBase):
|
||||
|
||||
nickname = models.CharField(max_length=255, unique=True, default='')
|
||||
|
@ -69,3 +86,18 @@ class UserProfile(amo.ModelBase):
|
|||
self.resetcode_expires = datetime.now()
|
||||
|
||||
super(UserProfile, self).save(force_insert, force_update)
|
||||
|
||||
def check_password(self, raw_password):
|
||||
if '$' not in self.password:
|
||||
valid = (get_hexdigest('md5', '', raw_password) == self.password)
|
||||
if valid:
|
||||
# Upgrade an old password.
|
||||
self.set_password(raw_password)
|
||||
self.save()
|
||||
return valid
|
||||
|
||||
algo, salt, hsh = self.password.split('$')
|
||||
return hsh == get_hexdigest(algo, salt, raw_password)
|
||||
|
||||
def set_password(self, raw_password, algorithm='sha512'):
|
||||
self.password = create_password(algorithm, raw_password)
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
from nose.tools import eq_
|
||||
|
||||
from .helpers import user_link
|
||||
from .models import UserProfile
|
||||
|
||||
|
||||
def test_user_link():
|
||||
u = UserProfile(firstname='John', lastname='Connor', pk=1)
|
||||
eq_(user_link(u), """<a href="/users/1">John Connor</a>""")
|
||||
|
||||
|
||||
def test_display_name_nickname():
|
||||
u = UserProfile(nickname='Terminator', pk=1)
|
||||
eq_(u.display_name, 'Terminator')
|
||||
|
||||
|
||||
def test_welcome_name():
|
||||
u1 = UserProfile(lastname='Connor', pk=1)
|
||||
u2 = UserProfile(firstname='Sarah', nickname='sc', lastname='Connor', pk=1)
|
||||
u3 = UserProfile(nickname='sc', lastname='Connor', pk=1)
|
||||
u4 = UserProfile(pk=1)
|
||||
eq_(u1.welcome_name, 'Connor')
|
||||
eq_(u2.welcome_name, 'Sarah')
|
||||
eq_(u3.welcome_name, 'sc')
|
||||
eq_(u4.welcome_name, '')
|
||||
|
||||
|
||||
def test_resetcode_expires():
|
||||
"""
|
||||
For some reasone resetcode is required, and we default it to
|
||||
'0000-00-00 00:00' in mysql, but that doesn't fly in Django since it's an
|
||||
invalid date. If Django reads this from the db, it interprets this as
|
||||
resetcode_expires as None
|
||||
"""
|
||||
|
||||
u = UserProfile(lastname='Connor', pk=2, resetcode_expires=None,
|
||||
email='j.connor@sky.net')
|
||||
u.save()
|
||||
assert(u.resetcode_expires)
|
|
@ -0,0 +1,9 @@
|
|||
from nose.tools import eq_
|
||||
|
||||
from users.helpers import user_link
|
||||
from users.models import UserProfile
|
||||
|
||||
|
||||
def test_user_link():
|
||||
u = UserProfile(firstname='John', lastname='Connor', pk=1)
|
||||
eq_(user_link(u), """<a href="/users/1">John Connor</a>""")
|
|
@ -0,0 +1,63 @@
|
|||
import hashlib
|
||||
|
||||
from django import test
|
||||
|
||||
from nose.tools import eq_
|
||||
|
||||
from users.models import UserProfile, get_hexdigest
|
||||
|
||||
|
||||
def test_display_name_nickname():
|
||||
u = UserProfile(nickname='Terminator', pk=1)
|
||||
eq_(u.display_name, 'Terminator')
|
||||
|
||||
|
||||
def test_welcome_name():
|
||||
u1 = UserProfile(lastname='Connor', pk=1)
|
||||
u2 = UserProfile(firstname='Sarah', nickname='sc', lastname='Connor', pk=1)
|
||||
u3 = UserProfile(nickname='sc', lastname='Connor', pk=1)
|
||||
u4 = UserProfile(pk=1)
|
||||
eq_(u1.welcome_name, 'Connor')
|
||||
eq_(u2.welcome_name, 'Sarah')
|
||||
eq_(u3.welcome_name, 'sc')
|
||||
eq_(u4.welcome_name, '')
|
||||
|
||||
|
||||
def test_resetcode_expires():
|
||||
"""
|
||||
For some reasone resetcode is required, and we default it to
|
||||
'0000-00-00 00:00' in mysql, but that doesn't fly in Django since it's an
|
||||
invalid date. If Django reads this from the db, it interprets this as
|
||||
resetcode_expires as None
|
||||
"""
|
||||
|
||||
u = UserProfile(lastname='Connor', pk=2, resetcode_expires=None,
|
||||
email='j.connor@sky.net')
|
||||
u.save()
|
||||
assert u.resetcode_expires
|
||||
|
||||
|
||||
class TestPasswords(test.TestCase):
|
||||
|
||||
def test_invalid_old_password(self):
|
||||
u = UserProfile(password='sekrit')
|
||||
assert u.check_password('sekrit') is False
|
||||
|
||||
def test_invalid_new_password(self):
|
||||
u = UserProfile()
|
||||
u.set_password('sekrit')
|
||||
assert u.check_password('wrong') is False
|
||||
|
||||
def test_valid_old_password(self):
|
||||
hsh = hashlib.md5('sekrit').hexdigest()
|
||||
u = UserProfile(password=hsh)
|
||||
assert u.check_password('sekrit') is True
|
||||
# Make sure we updated the old password.
|
||||
algo, salt, hsh = u.password.split('$')
|
||||
eq_(algo, 'sha512')
|
||||
eq_(hsh, get_hexdigest(algo, salt, 'sekrit'))
|
||||
|
||||
def test_valid_new_password(self):
|
||||
u = UserProfile()
|
||||
u.set_password('sekrit')
|
||||
assert u.check_password('sekrit') is True
|
Загрузка…
Ссылка в новой задаче