Skip to content

Commit

Permalink
[#1530] Add tests, use utf-8 for FriendlyForms
Browse files Browse the repository at this point in the history
FriendlyForms defaults to latin-1 this means that request parameters
would get encoded as utf-8 strings and then interpreted by FriendlyForms
as latin-1, so you would end up with a python mangled unicode string
containing utf-8.
  • Loading branch information
joetsoi committed May 19, 2014
1 parent 9d2b19b commit 07fe6c4
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 2 deletions.
1 change: 1 addition & 0 deletions ckan/config/who.ini
Expand Up @@ -11,6 +11,7 @@ logout_handler_path = /user/logout
rememberer_name = auth_tkt
post_login_url = /user/logged_in
post_logout_url = /user/logged_out
charset = utf-8

#[plugin:basicauth]
#use = repoze.who.plugins.basicauth:make_plugin
Expand Down
4 changes: 2 additions & 2 deletions ckan/model/user.py
Expand Up @@ -161,8 +161,8 @@ def validate_password(self, password):
len(current_hash.salt) < plh.pbkdf2_sha512.default_salt_size):

return self._verify_and_upgrade_pbkdf2(password)

return plh.pbkdf2_sha512.verify(password, self.password)
else:
return plh.pbkdf2_sha512.verify(password, self.password)

password = property(_get_password, _set_password)

Expand Down
146 changes: 146 additions & 0 deletions ckan/new_tests/model/test_user.py
@@ -0,0 +1,146 @@
import os
import hashlib
import unittest

import nose.tools as nt
import passlib.hash as plh

import ckan.new_tests.factories as factories
import ckan.new_tests.helpers as helpers
import ckan.model as model


class TestPassword(unittest.TestCase):
@classmethod
def teardown_class(clss):
model.repo.rebuild_db()

def setup(self):
helpers.reset_db()

def teardown(self):
helpers.reset_db()

def _set_password(self, password):
'''Copy of the old password hashing function
This is needed to create old password hashes in the tests
'''
if isinstance(password, unicode):
password_8bit = password.encode('ascii', 'ignore')
else:
password_8bit = password

salt = hashlib.sha1(os.urandom(60))
hash = hashlib.sha1(password_8bit + salt.hexdigest())
hashed_password = salt.hexdigest() + hash.hexdigest()

if not isinstance(hashed_password, unicode):
hashed_password = hashed_password.decode('utf-8')
return hashed_password

def test_upgrade_from_sha(self):
user = factories.User()
user_obj = model.User.by_name(user['name'])

# setup our user with an old password hash
old_hash = self._set_password('testpass')
user_obj._password = old_hash
user_obj.save()

user_obj.validate_password('testpass')
nt.assert_not_equals(old_hash, user_obj.password)
nt.assert_true(plh.pbkdf2_sha512.identify(user_obj.password))
nt.assert_true(plh.pbkdf2_sha512.verify('testpass', user_obj.password))

def test_upgrade_from_sha_with_unicode_password(self):
user = factories.User()
password = u'testpassword\xc2\xa0'
user_obj = model.User.by_name(user['name'])

# setup our user with an old password hash
old_hash = self._set_password(password)
user_obj._password = old_hash
user_obj.save()

nt.assert_true(user_obj.validate_password(password))
nt.assert_not_equals(old_hash, user_obj.password)
nt.assert_true(plh.pbkdf2_sha512.identify(user_obj.password))
nt.assert_true(plh.pbkdf2_sha512.verify(password, user_obj.password))

# check that we now allow unicode characters
nt.assert_false(plh.pbkdf2_sha512.verify('testpassword',
user_obj.password))

def test_upgrade_from_sha_with_wrong_password_fails_to_upgrade(self):
user = factories.User()
password = u'testpassword'
user_obj = model.User.by_name(user['name'])

old_hash = self._set_password(password)
user_obj._password = old_hash
user_obj.save()

nt.assert_false(user_obj.validate_password('wrongpass'))
nt.assert_equals(old_hash, user_obj.password)
nt.assert_false(plh.pbkdf2_sha512.identify(user_obj.password))

def test_upgrade_from_pbkdf2_with_less_rounds(self):
'''set up a pbkdf key with less than the default rounds
If the number of default_rounds is increased in a later version of
passlib, ckan should upgrade the password hashes for people without
involvement from users'''
user = factories.User()
password = u'testpassword'
user_obj = model.User.by_name(user['name'])

# setup hash with salt/rounds less than the default
old_hash = plh.pbkdf2_sha512.encrypt(password, salt_size=2, rounds=10)
user_obj._password = old_hash
user_obj.save()

nt.assert_true(user_obj.validate_password(password.encode('utf-8')))
# check that the hash has been updated
nt.assert_not_equals(old_hash, user_obj.password)
new_hash = plh.pbkdf2_sha512.from_string(user_obj.password)

nt.assert_true(plh.pbkdf2_sha512.default_rounds > 10)
nt.assert_equals(plh.pbkdf2_sha512.default_rounds, new_hash.rounds)

nt.assert_true(plh.pbkdf2_sha512.default_salt_size, 2)
nt.assert_equals(plh.pbkdf2_sha512.default_salt_size,
len(new_hash.salt))
nt.assert_true(plh.pbkdf2_sha512.verify(password, user_obj.password))

def test_upgrade_from_pbkdf2_fails_with_wrong_password(self):
user = factories.User()
password = u'testpassword'
user_obj = model.User.by_name(user['name'])

# setup hash with salt/rounds less than the default
old_hash = plh.pbkdf2_sha512.encrypt(password, salt_size=2, rounds=10)
user_obj._password = old_hash
user_obj.save()

nt.assert_false(user_obj.validate_password('wrong_pass'))
# check that the hash has _not_ been updated
nt.assert_equals(old_hash, user_obj.password)

def test_pbkdf2_password_auth(self):
user = factories.User()
password = u'testpassword'
user_obj = model.User.by_name(user['name'])

user_obj._set_password(password)
user_obj.save()
nt.assert_true(user_obj.validate_password(password))

def test_pbkdf2_password_auth_unicode(self):
user = factories.User()
password = u'testpassword\xc2\xa0'
user_obj = model.User.by_name(user['name'])
user_obj._set_password(password)
user_obj.save()

nt.assert_true(user_obj.validate_password(password))

0 comments on commit 07fe6c4

Please sign in to comment.