diff options
Diffstat (limited to 'passlib/tests')
| -rw-r--r-- | passlib/tests/test_context.py | 66 | ||||
| -rw-r--r-- | passlib/tests/test_utils_handlers.py | 2 | ||||
| -rw-r--r-- | passlib/tests/utils.py | 65 |
3 files changed, 95 insertions, 38 deletions
diff --git a/passlib/tests/test_context.py b/passlib/tests/test_context.py index f9edafc..373d066 100644 --- a/passlib/tests/test_context.py +++ b/passlib/tests/test_context.py @@ -521,7 +521,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt #CryptContext #========================================================= class CryptContextTest(TestCase): - "test CryptContext object's behavior" + "test CryptContext class" descriptionPrefix = "CryptContext" #========================================================= @@ -893,10 +893,6 @@ class CryptContextTest(TestCase): self.assertEqual(cc.identify('$9$232323123$1287319827'), None) self.assertRaises(ValueError, cc.identify, '$9$232323123$1287319827', required=True) - #make sure "None" is accepted - self.assertEqual(cc.identify(None), None) - self.assertRaises(ValueError, cc.identify, None, required=True) - def test_22_verify(self): "test verify() scheme kwd" handlers = ["md5_crypt", "des_crypt", "bsdi_crypt"] @@ -915,14 +911,6 @@ class CryptContextTest(TestCase): #check verify using wrong alg self.assertRaises(ValueError, cc.verify, 'test', h, scheme='bsdi_crypt') - def test_23_verify_empty_hash(self): - "test verify() allows hash=None" - handlers = [hash.md5_crypt, hash.des_crypt, hash.bsdi_crypt] - cc = CryptContext(handlers, policy=None) - self.assertTrue(not cc.verify("test", None)) - for handler in handlers: - self.assertTrue(not cc.verify("test", None, scheme=handler.name)) - def test_24_min_verify_time(self): "test verify() honors min_verify_time" #NOTE: this whole test assumes time.sleep() and tick() @@ -1008,6 +996,58 @@ class CryptContextTest(TestCase): self.assertIs(new_hash, None) #========================================================= + # border cases + #========================================================= + def test_30_nonstring_hash(self): + "test non-string hash values cause error" + # + # test hash=None or some other non-string causes TypeError + # and that explicit-scheme code path behaves the same. + # + cc = CryptContext(["des_crypt"]) + for hash, kwds in [ + (None, {}), + (None, {"scheme": "des_crypt"}), + (1, {}), + ((), {}), + ]: + + self.assertRaises(TypeError, cc.identify, hash, **kwds) + self.assertRaises(TypeError, cc.genhash, 'stub', hash, **kwds) + self.assertRaises(TypeError, cc.verify, 'stub', hash, **kwds) + self.assertRaises(TypeError, cc.verify_and_update, 'stub', hash, **kwds) + self.assertRaises(TypeError, cc.hash_needs_update, hash, **kwds) + + # + # but genhash *should* accept None if default scheme lacks config string. + # + cc2 = CryptContext(["mysql323"]) + self.assertRaises(TypeError, cc2.identify, None) + self.assertIsInstance(cc2.genhash("stub", None), str) + self.assertRaises(TypeError, cc2.verify, 'stub', None) + self.assertRaises(TypeError, cc2.verify_and_update, 'stub', None) + self.assertRaises(TypeError, cc2.hash_needs_update, None) + + + def test_31_nonstring_secret(self): + "test non-string password values cause error" + cc = CryptContext(["des_crypt"]) + hash = cc.encrypt("stub") + # + # test secret=None, or some other non-string causes TypeError + # + for secret, kwds in [ + (None, {}), + (None, {"scheme": "des_crypt"}), + (1, {}), + ((), {}), + ]: + self.assertRaises(TypeError, cc.encrypt, secret, **kwds) + self.assertRaises(TypeError, cc.genhash, secret, hash, **kwds) + self.assertRaises(TypeError, cc.verify, secret, hash, **kwds) + self.assertRaises(TypeError, cc.verify_and_update, secret, hash, **kwds) + + #========================================================= # other #========================================================= def test_90_bcrypt_normhash(self): diff --git a/passlib/tests/test_utils_handlers.py b/passlib/tests/test_utils_handlers.py index 30194a6..5044d1e 100644 --- a/passlib/tests/test_utils_handlers.py +++ b/passlib/tests/test_utils_handlers.py @@ -14,7 +14,7 @@ from passlib.hash import ldap_md5, sha256_crypt from passlib.registry import _unload_handler_name as unload_handler_name, \ register_crypt_handler, get_crypt_handler from passlib.exc import MissingBackendError, PasslibHashWarning -from passlib.utils import getrandstr, JYTHON, rng, to_unicode +from passlib.utils import getrandstr, JYTHON, rng from passlib.utils.compat import b, bytes, bascii_to_str, str_to_uascii, \ uascii_to_str, unicode, PY_MAX_25 import passlib.utils.handlers as uh diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py index b7dce87..5665259 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, \ - sb_types, exc_err, u, unicode + base_string_types, exc_err, u, unicode import passlib.utils.handlers as uh #local __all__ = [ @@ -374,7 +374,7 @@ class TestCase(unittest.TestCase): # 3.0 and <= 2.6 didn't have this method at all def assertRegex(self, text, expected_regex, msg=None): """Fail the test unless the text matches the regular expression.""" - if isinstance(expected_regex, sb_types): + if isinstance(expected_regex, base_string_types): assert expected_regex, "expected_regex must not be empty." expected_regex = re.compile(expected_regex) if not expected_regex.search(text): @@ -662,6 +662,10 @@ class HandlerCase(TestCase): msg = "verify failed: secret=%r, hash=%r" % (secret, hash) raise self.failureException(msg) + def check_returned_native_str(self, result, func_name): + self.assertIsInstance(result, str, + "%s() failed to return native string: %r" % (func_name, result,)) + #========================================================= # internal class attrs #========================================================= @@ -765,8 +769,7 @@ class HandlerCase(TestCase): # encrypt should generate hash... result = self.do_encrypt(secret) - self.assertIsInstance(result, str, - "encrypt must return native str:") + self.check_returned_native_str(result, "encrypt") # which should be positively identifiable... self.assertTrue(self.do_identify(result)) @@ -1202,17 +1205,22 @@ class HandlerCase(TestCase): self.assertNotEqual(h2, h1, "genhash() should be case sensitive") - def test_62_secret_null(self): - "test password=None" - _, hash = self.get_sample_hash() + def test_62_secret_border(self): + "test non-string passwords are rejected" + hash = self.get_sample_hash()[1] + + # secret=None self.assertRaises(TypeError, self.do_encrypt, None) self.assertRaises(TypeError, self.do_genhash, None, hash) self.assertRaises(TypeError, self.do_verify, None, hash) - def test_63_max_password_size(self): + # secret=int (picked as example of entirely wrong class) + self.assertRaises(TypeError, self.do_encrypt, 1) + self.assertRaises(TypeError, self.do_genhash, 1, hash) + self.assertRaises(TypeError, self.do_verify, 1, hash) + + def test_63_large_secret(self): "test MAX_PASSWORD_SIZE is enforced" - if self.is_disabled_handler: - raise self.skipTest("not applicable") from passlib.exc import PasswordSizeError from passlib.utils import MAX_PASSWORD_SIZE secret = '.' * (1+MAX_PASSWORD_SIZE) @@ -1400,25 +1408,28 @@ class HandlerCase(TestCase): __msg__= "genhash() failed to throw error for hash " "belonging to %s: %r" % (name, hash)) - def test_76_none(self): - "test empty hashes" + def test_76_hash_border(self): + "test non-string hashes are rejected" # - # test hash=None + # test hash=None is rejected (except if config=None) # - # FIXME: allowing value or type error to simplify implementation, - # but TypeError is really the correct one here. - self.assertFalse(self.do_identify(None)) - self.assertRaises((ValueError, TypeError), self.do_verify, 'stub', None) + self.assertRaises(TypeError, self.do_identify, None) + self.assertRaises(TypeError, self.do_verify, 'stub', None) if self.supports_config_string: - self.assertRaises((ValueError, TypeError), self.do_genhash, - 'stub', None) + self.assertRaises(TypeError, self.do_genhash, 'stub', None) else: result = self.do_genhash('stub', None) - self.assertIsInstance(result, str, - "genhash() failed to return native string: %r" % (result,)) + self.check_returned_native_str(result, "genhash") # - # test hash='' + # test hash=int is rejected (picked as example of entirely wrong type) + # + self.assertRaises(TypeError, self.do_identify, 1) + self.assertRaises(TypeError, self.do_verify, 'stub', 1) + self.assertRaises(TypeError, self.do_genhash, 'stub', 1) + + # + # test hash='' is rejected for all but the plaintext hashes # for hash in [u(''), b('')]: if self.accepts_all_hashes: @@ -1426,9 +1437,9 @@ class HandlerCase(TestCase): self.assertTrue(self.do_identify(hash)) self.do_verify('stub', hash) result = self.do_genhash('stub', hash) - self.assertIsInstance(result, str, - "genhash() failed to return native string: %r" % (result,)) + self.check_returned_native_str(result, "genhash") else: + # otherwise it should reject them self.assertFalse(self.do_identify(hash), "identify() incorrectly identified empty hash") self.assertRaises(ValueError, self.do_verify, 'stub', hash, @@ -1436,6 +1447,12 @@ class HandlerCase(TestCase): self.assertRaises(ValueError, self.do_genhash, 'stub', hash, __msg__="genhash() failed to reject empty hash") + # + # test identify doesn't throw decoding errors on 8-bit input + # + self.do_identify('\xe2\x82\xac\xc2\xa5$') # utf-8 + self.do_identify('abc\x91\x00') # non-utf8 + #--------------------------------------------------------- # fuzz testing #--------------------------------------------------------- |
