Commits

Kelvin Wong  committed 3037132 Draft

Move helper functions outside class for testing

  • Participants
  • Parent commits 6b78474

Comments (0)

Files changed (2)

File django_scrypt/hashers.py

 
 
 PYTHON3 = sys.version_info >= (3, 0)
+EMPTY = ''
+WHITESPACE = re.compile(r'\s+')
+
+
+def base64(s):
+    """Returns a Base64 encoded string"""
+    if PYTHON3:
+        import base64
+        return base64.b64encode(s)
+    else:
+        return s.encode('base64')
+
+
+def stripws(s):
+    """Strip whitespace using regular expressions"""
+    return re.sub(WHITESPACE, EMPTY, s)
+
+
+def stringify(s):
+    """Returns a string suitable for use with Django-Scrypt
+    """
+    if PYTHON3:
+        if type(s) is bytes:
+            return stripws(s.decode('utf-8'))
+        else:
+            return stripws(s)
+    else:
+        if type(s) is unicode:
+            return stripws(s.encode('utf-8'))
+        else:
+            return stripws(str(s))
+
+
+def liststringify(x):
+    """Stringify items in iterable x"""
+    return [stringify(i) for i in x]
 
 
 class ScryptPasswordHasher(BasePasswordHasher):
     Subclass to modify parameters for custom Scrypt tuning.
 
     The Py-Scrypt library must be installed separately. That library
-    depends on native C code and might cause portability issues.
+    depends on native C code and may cause portability issues.
 
     Class Attributes
 
     p = 1
     buflen = 64
 
-    def base64(self, s):
-        if PYTHON3:
-            import base64
-            return base64.b64encode(s)
-        else:
-            return s.encode('base64')
-
-    def stringify(self, s):
-        if PYTHON3:
-            if type(s) is bytes:
-                return self.stripws(s.decode('utf-8'))
-            else:
-                return self.stripws(s)
-        else:
-            if type(s) is unicode:
-                return self.stripws(s.encode('utf-8'))
-            else:
-                return self.stripws(str(s))
-
-    EMPTY = ''
-    WHITESPACE = re.compile(r'\s+')
-
-    def stripws(self, s):
-        return re.sub(self.WHITESPACE, self.EMPTY, s)
-
-    def liststringify(self, x):
-        return [self.stringify(i) for i in x]
-
     def verify(self, password, encoded):
         """
         Checks if the given password is correct
 
         """
         assert encoded
-        encoded = self.stripws(encoded)
+        encoded = stripws(encoded)
         algorithm, salt, Nexp, r, p, buflen, h = encoded.split('$')
         assert algorithm == self.algorithm
         # TODO: buflen is an experimental proposal in py-scrypt
 
         """
         assert password
-        password = self.stringify(password)
+        password = stringify(password)
         assert salt
-        salt = self.stringify(salt)
+        salt = stringify(salt)
         assert '$' not in salt
         hashed = [self.algorithm]
         hashed.append(salt)
         hashed.append(str(p))
         hashed.append(str(buflen))
         h = scrypt.hash(password, salt, 2 ** Nexp, r, p)
-        hashed.append(self.stringify(self.base64(h)))
+        hashed.append(stringify(base64(h)))
         return "$".join(hashed)
 
     def safe_summary(self, encoded):

File tests/test_django_scrypt.py

 # -*- coding: utf-8 -*-
 
 from __future__ import with_statement, unicode_literals
+import django
 from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
 from django.contrib.auth.hashers import (
     is_password_usable, check_password, make_password,
 
 
 PYTHON3 = sys.version_info >= (3, 0)
+is_django_15 = (django.VERSION > (1, 5, 0) and django.VERSION < (1, 6, 0))
 
 
 def utf8(b):
-    """Returns Unicode string given UTF-8 encoded bytes"""
-    if PYTHON3:
-        return b
-    else:
-        return b.decode('utf-8')
+    """Returns Unicode string given UTF-8 encoded string"""
+    return b.decode('utf-8')
+
+
+@skipUnless(utf8, "UTF-8 helper is required")
+class TestUTF8Helper(TestCase):
+    def setUp(self):
+        self.utf8_encoded = b'a\xc2\xac\xe1\x88\xb4\xe2\x82\xac\xe8\x80\x80'
+
+    @skipUnless(not PYTHON3, "Python 2.X required")
+    def test_unicode_from_python_2(self):
+        """Given UTF-8 encoded bytes obtain Unicode string"""
+        unicode_str = utf8(self.utf8_encoded)
+        self.assertTrue(isinstance(self.utf8_encoded, str))
+        self.assertTrue(isinstance(unicode_str, unicode))
+
+    @skipUnless(PYTHON3, "Python 3.X required")
+    def test_unicode_from_python_3(self):
+        """Given UTF-8 encoded bytes obtain Unicode string"""
+        unicode_str = utf8(self.utf8_encoded)
+        self.assertTrue(isinstance(self.utf8_encoded, bytes))
+        self.assertTrue(isinstance(unicode_str, str))
 
 
 @skipUnless(scrypt, "Uninstalled scrypt module needed to generate hash")
 
             self.assertTrue(check_password(self.password, encoded, setter))
             self.assertTrue(state['upgraded'])
+
+    @skipUnless(is_django_15, "Test requires Django 1.5")
+    def test_upgrade_14_to_15(self):
+        """Hash encoded with Django 1.4 works with Django 1.5"""
+        django_14_hash = utf8(
+            b"scrypt$xUHzJGn4lszk$14$8$1$64$dxfJjF/gDXSD4fnl7Dk5E0u4vwPtEyL1Fq"
+            b"7xqZ/p+b8UUspTXv+eTQweMVbVgZmNY6umZsA3u8VF2Okx/7mzKg==")
+        encoded = make_password(self.unicode_text)
+        self.assertTrue(check_password(self.unicode_text, encoded))