summaryrefslogtreecommitdiff
path: root/passlib/tests/utils.py
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-04-12 21:52:26 -0400
committerEli Collins <elic@assurancetechnologies.com>2012-04-12 21:52:26 -0400
commitc0f420bf7d7659ee110432f7cbb0233554dfd32a (patch)
treed8416c7cd9b5f5d54e5fcb58fafa02f64da07352 /passlib/tests/utils.py
parente71ddce83853566311effebf68b9bbbdebf4c2ab (diff)
downloadpasslib-c0f420bf7d7659ee110432f7cbb0233554dfd32a.tar.gz
assorted bugfixes, tweaks, and tests added; based on coverage examination
* test os_crypt backend has functional fallback * test handler methods accept all unicode/bytes combinations for secret & hash * fixed some incorrect error messages & types being caught & raised * other minor cleanups
Diffstat (limited to 'passlib/tests/utils.py')
-rw-r--r--passlib/tests/utils.py123
1 files changed, 107 insertions, 16 deletions
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py
index 713824e..a75f428 100644
--- a/passlib/tests/utils.py
+++ b/passlib/tests/utils.py
@@ -42,7 +42,7 @@ from passlib.utils import has_rounds_info, has_salt_info, rounds_cost_values, \
classproperty, rng, getrandstr, is_ascii_safe, to_native_str, \
repeat_string
from passlib.utils.compat import b, bytes, iteritems, irange, callable, \
- base_string_types, exc_err, u, unicode
+ base_string_types, exc_err, u, unicode, PY2
import passlib.utils.handlers as uh
#local
__all__ = [
@@ -134,6 +134,18 @@ def get_file(path):
with open(path, "rb") as fh:
return fh.read()
+def tonn(source):
+ "convert native string to non-native string"
+ if not isinstance(source, str):
+ return source
+ elif PY3:
+ return source.encode("utf-8")
+ else:
+ try:
+ return source.decode("utf-8")
+ except UnicodeDecodeError:
+ return source.decode("latin-1")
+
#=========================================================
#custom test base
#=========================================================
@@ -741,7 +753,7 @@ class HandlerCase(TestCase):
# XXX: any more checks needed?
- def test_02_config(self):
+ def test_02_config_workflow(self):
"""test basic config-string workflow
this tests that genconfig() returns the expected types,
@@ -785,7 +797,7 @@ class HandlerCase(TestCase):
else:
self.assertRaises(TypeError, self.do_identify, config)
- def test_03_hash(self):
+ def test_03_hash_workflow(self):
"""test basic hash-string workflow.
this tests that encrypt()'s hashes are accepted
@@ -835,8 +847,36 @@ class HandlerCase(TestCase):
#
self.assertTrue(self.do_identify(result))
+ def test_04_hash_types(self):
+ "test hashes can be unicode or bytes"
+ # this runs through workflow similar to 03, but wraps
+ # everything using tonn() so we test unicode under py2,
+ # and bytes under py3.
+
+ # encrypt using non-native secret
+ result = self.do_encrypt(tonn('stub'))
+ self.check_returned_native_str(result, "encrypt")
+
+ # verify using non-native hash
+ self.check_verify('stub', tonn(result))
+
+ # verify using non-native hash AND secret
+ self.check_verify(tonn('stub'), tonn(result))
+
+ # genhash using non-native hash
+ other = self.do_genhash('stub', tonn(result))
+ self.check_returned_native_str(other, "genhash")
+ self.assertEqual(other, result)
- def test_04_backends(self):
+ # genhash using non-native hash AND secret
+ other = self.do_genhash(tonn('stub'), tonn(result))
+ self.check_returned_native_str(other, "genhash")
+ self.assertEqual(other, result)
+
+ # identify using non-native hash
+ self.assertTrue(self.do_identify(tonn(result)))
+
+ def test_05_backends(self):
"test multi-backend support"
handler = self.handler
if not hasattr(handler, "set_backend"):
@@ -1065,6 +1105,34 @@ class HandlerCase(TestCase):
self.assertRaises(ValueError, self.do_genconfig, salt=c*chunk,
__msg__="invalid salt char %r:" % (c,))
+ @property
+ def salt_type(self):
+ "hack to determine salt keyword's datatype"
+ # NOTE: cisco_type7 uses 'int'
+ if getattr(self.handler, "_salt_is_bytes", False):
+ return bytes
+ else:
+ return unicode
+
+ def test_15_salt_type(self):
+ "test non-string salt values"
+ self.require_salt()
+ salt_type = self.salt_type
+
+ # should always throw error for random class.
+ class fake(object):
+ pass
+ self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=fake())
+
+ # unicode should be accepted only if salt_type is unicode.
+ if salt_type is not unicode:
+ self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=u('x'))
+
+ # bytes should be accepted only if salt_type is bytes,
+ # OR if salt type is unicode and running PY2 - to allow native strings.
+ if not (salt_type is bytes or (PY2 and salt_type is unicode)):
+ self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=b('x'))
+
#==============================================================
# rounds
#==============================================================
@@ -1672,11 +1740,8 @@ class HandlerCase(TestCase):
return rng.choice(handler.ident_values)
#=========================================================
- # test 8x - mixin tests
- # test 9x - handler-specific tests
- #=========================================================
-
- #=========================================================
+ # test 8x - mixin tests
+ # test 9x - handler-specific tests
# eoc
#=========================================================
@@ -1746,20 +1811,27 @@ class OsCryptMixin(HandlerCase):
#=========================================================
# custom tests
#=========================================================
- def test_80_faulty_crypt(self):
- "test with faulty crypt()"
- # patch safe_crypt to return mock value.
+ def _use_mock_crypt(self):
+ "patch safe_crypt() so it returns mock value"
import passlib.utils as mod
- self.addCleanup(setattr, mod, "_crypt", mod._crypt)
+ if not self.using_patched_crypt:
+ self.addCleanup(setattr, mod, "_crypt", mod._crypt)
crypt_value = [None]
mod._crypt = lambda secret, config: crypt_value[0]
+ def setter(value):
+ crypt_value[0] = value
+ return setter
- # prepare framework
+ def test_80_faulty_crypt(self):
+ "test with faulty crypt()"
hash = self.get_sample_hash()[1]
exc_types = (AssertionError,)
+ setter = self._use_mock_crypt()
def test(value):
- crypt_value[0] = value
+ # set safe_crypt() to return specified value, and
+ # make sure assertion error is raised by handler.
+ setter(value)
self.assertRaises(exc_types, self.do_genhash, "stub", hash)
self.assertRaises(exc_types, self.do_encrypt, "stub")
self.assertRaises(exc_types, self.do_verify, "stub", hash)
@@ -1768,7 +1840,26 @@ class OsCryptMixin(HandlerCase):
test(hash[:-1]) # detect too short
test(hash + 'x') # detect too long
- def test_81_crypt_support(self):
+ def test_81_crypt_fallback(self):
+ "test per-call crypt() fallback"
+ # set safe_crypt to return None
+ setter = self._use_mock_crypt()
+ setter(None)
+ if _find_alternate_backend(self.handler, "os_crypt"):
+ # handler should have a fallback to use
+ h1 = self.do_encrypt("stub")
+ h2 = self.do_genhash("stub", h1)
+ self.assertEqual(h2, h1)
+ self.assertTrue(self.do_verify("stub", h1))
+ else:
+ # handler should give up
+ from passlib.exc import MissingBackendError
+ hash = self.get_sample_hash()[1]
+ self.assertRaises(MissingBackendError, self.do_encrypt, 'stub')
+ self.assertRaises(MissingBackendError, self.do_genhash, 'stub', hash)
+ self.assertRaises(MissingBackendError, self.do_verify, 'stub', hash)
+
+ def test_82_crypt_support(self):
"test crypt support detection"
platform = sys.platform
for name, flag in self.platform_crypt_support.items():