diff --git a/CHANGES.txt b/CHANGES.txt index 6271f285..46b201f9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,9 @@ 1.0b1 (unreleased) ================== +- Fix password check for users created prior to switching to ``bcrypt``. + See https://github.com/Pylons/substanced/pull/316 + - Fix ``tznames_widget``, b0rken seemingly forever under Python3. See https://github.com/Pylons/substanced/pull/314 diff --git a/substanced/principal/__init__.py b/substanced/principal/__init__.py index 9a00b657..c5116bf3 100644 --- a/substanced/principal/__init__.py +++ b/substanced/principal/__init__.py @@ -407,17 +407,23 @@ def timezone(self): return pytz.timezone(self.tzname) def check_password(self, password): - """ Checks if the plaintext password passed as ``password`` matches - this user's stored, encrypted password. Returns ``True`` or - ``False``.""" + """Check plaintext ``password`` against stored, hashed password. + """ statsd_gauge('check_password', 1) + if len(password) > 4096: # avoid DOS ala # https://www.djangoproject.com/weblog/2013/sep/15/security/ raise ValueError('Not checking password > 4096 bytes') + if isinstance(password, str): password = password.encode('utf-8') - return bcrypt.checkpw(password, self.password) + + hashed = self.password + if isinstance(hashed, str): + hashed = hashed.encode('utf-8') + + return bcrypt.checkpw(password, hashed) def set_password(self, password): self.password = self.hash_new_password(password) diff --git a/substanced/principal/tests/test_principal.py b/substanced/principal/tests/test_principal.py index 36090cfc..5d83a1c4 100644 --- a/substanced/principal/tests/test_principal.py +++ b/substanced/principal/tests/test_principal.py @@ -376,6 +376,14 @@ def test_check_password(self): self.assertTrue(inst.check_password('abc')) self.assertFalse(inst.check_password('abcdef')) + def test_check_password_w_str(self): + inst = self._makeOne('abc') + assert isinstance(inst.password, bytes) + # See https://github.com/Pylons/substanced/issues/315 + inst.password = inst.password.decode('utf8') + self.assertTrue(inst.check_password('abc')) + self.assertFalse(inst.check_password('abcdef')) + def test_check_password_gt_4096_bytes(self): inst = self._makeOne('abc') self.assertRaises(ValueError, inst.check_password, 'a'*4097)