changeset 5055:a2b7cfa8d7e9

Fixed changes file comments, merged with upstream.
author John Rouillard <rouilj@ieee.org>
date Sat, 16 Apr 2016 22:58:13 -0400
parents f0f5293d9939 (diff) 737d3edee3ab (current diff)
children d995ee7d49bf
files CHANGES.txt
diffstat 4 files changed, 43 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Mon Apr 11 09:18:21 2016 +0200
+++ b/CHANGES.txt	Sat Apr 16 22:58:13 2016 -0400
@@ -17,6 +17,11 @@
 Features:
 
 - issue2550894: migrate test suite and run_test.py to py.test (John Kristensen)
+- issue2550880: Ability to choose password store scheme and SSHA
+  support. Discussion on devel list is tending in favor of this patch.
+  Embedded test works, my manual test with a SSHA password
+  assigned to a user allowed the user to log in.   Ran the test suite
+  and the tests that were not skipped passed. (applied by John Rouillard)
 
 Fixed:
 
@@ -30,10 +35,10 @@
   customizing tracker section of doc/customizing.txt (John Rouillard)
 - issue2550601: gsoc-2009 "bug" class doesn't have "patches" property
   Added multilink to patches to the bug schema in the devel template.
-  (John Rouillard)
+  (applied by John Rouillard)
 - issue2550748: Crash when creating new issues with non-existing
   multilink values (in classic template). Applied patch so it
-  now errors the same way as an update does.  (John Rouillard)
+  now errors the same way as an update does. (applied by John Rouillard)
 
 2016-01-11: 1.5.1
 
--- a/roundup/cgi/form_parser.py	Mon Apr 11 09:18:21 2016 +0200
+++ b/roundup/cgi/form_parser.py	Sat Apr 16 22:58:13 2016 -0400
@@ -386,7 +386,8 @@
                     raise FormError, self._('Password and confirmation text '
                         'do not match')
                 try:
-                    value = password.Password(value, config=self.db.config)
+                    value = password.Password(value, scheme = proptype.scheme,
+                                              config=self.db.config)
                 except hyperdb.HyperdbValueError, msg:
                     raise FormError, msg
 
--- a/roundup/hyperdb.py	Mon Apr 11 09:18:21 2016 +0200
+++ b/roundup/hyperdb.py	Sat Apr 16 22:58:13 2016 -0400
@@ -71,11 +71,15 @@
 
 class Password(_Type):
     """An object designating a Password property."""
+    def __init__(self, scheme=None, required=False, default_value = None):
+        super(Password, self).__init__(required, default_value)
+        self.scheme = scheme
+
     def from_raw(self, value, **kw):
         if not value:
             return None
         try:
-            return password.Password(encrypted=value, strict=True)
+            return password.Password(encrypted=value, scheme=self.scheme, strict=True)
         except password.PasswordValueError, message:
             raise HyperdbValueError, \
                     _('property %s: %s')%(kw['propname'], message)
--- a/roundup/password.py	Mon Apr 11 09:18:21 2016 +0200
+++ b/roundup/password.py	Sat Apr 16 22:58:13 2016 -0400
@@ -20,6 +20,7 @@
 __docformat__ = 'restructuredtext'
 
 import re, string, random
+import os
 from base64 import b64encode, b64decode
 from hashlib import md5, sha1
 
@@ -81,6 +82,16 @@
             out += block
         return out[:keylen]
 
+def ssha(password, salt):
+    ''' Make ssha digest from password and salt.
+    Based on code of Roberto Aguilar <roberto@baremetal.io>
+    https://gist.github.com/rca/7217540
+    '''
+    shaval = sha1(password)
+    shaval.update( salt )
+    ssha_digest = b64encode( '{}{}'.format(shaval.digest(), salt) ).strip()
+    return ssha_digest
+
 def pbkdf2(password, salt, rounds, keylen):
     """pkcs#5 password-based key derivation v2.0
 
@@ -149,6 +160,16 @@
             raise PasswordValueError, "invalid PBKDF2 hash (rounds too low)"
         raw_digest = pbkdf2(plaintext, raw_salt, rounds, 20)
         return "%d$%s$%s" % (rounds, salt, h64encode(raw_digest))
+    elif scheme == 'SSHA':
+        if other:
+            raw_other = b64decode(other)
+            salt = raw_other[20:]
+        else:
+            #new password
+            # variable salt length
+            salt_len = random.randrange(36, 52)
+            salt = os.urandom(salt_len)
+        s = ssha(plaintext, salt)
     elif scheme == 'SHA':
         s = sha1(plaintext).hexdigest()
     elif scheme == 'MD5':
@@ -241,7 +262,7 @@
     #TODO: code to migrate from old password schemes.
 
     deprecated_schemes = ["SHA", "MD5", "crypt", "plaintext"]
-    known_schemes = ["PBKDF2"] + deprecated_schemes
+    known_schemes = ["PBKDF2", "SSHA"] + deprecated_schemes
 
     def __init__(self, plaintext=None, scheme=None, encrypted=None, strict=False, config=None):
         """Call setPassword if plaintext is not None."""
@@ -319,6 +340,13 @@
         assert 'sekrit' == p
         assert 'not sekrit' != p
 
+    # SSHA
+    p = Password('sekrit', 'SSHA')
+    assert p == 'sekrit'
+    assert p != 'not sekrit'
+    assert 'sekrit' == p
+    assert 'not sekrit' != p
+
     # PBKDF2 - low level function
     from binascii import unhexlify
     k = pbkdf2("password", "ATHENA.MIT.EDUraeburn", 1200, 32)

Roundup Issue Tracker: http://roundup-tracker.org/