diff options
author | Eli Collins <elic@assurancetechnologies.com> | 2012-02-09 15:19:20 -0500 |
---|---|---|
committer | Eli Collins <elic@assurancetechnologies.com> | 2012-02-09 15:19:20 -0500 |
commit | 9bcfe9d440c56774a7e138aafcbaccb6b0ce6626 (patch) | |
tree | 720249f82734323f1f3f566256dd8768269442b9 /passlib | |
parent | 4b3590ab0d73e9defb70c97763a1b69a07a7f9be (diff) | |
download | passlib-9bcfe9d440c56774a7e138aafcbaccb6b0ce6626.tar.gz |
consolidated some unit tests
Diffstat (limited to 'passlib')
-rw-r--r-- | passlib/handlers/md5_crypt.py | 6 | ||||
-rw-r--r-- | passlib/handlers/sha2_crypt.py | 16 | ||||
-rw-r--r-- | passlib/tests/test_handlers.py | 150 | ||||
-rw-r--r-- | passlib/tests/utils.py | 115 |
4 files changed, 126 insertions, 161 deletions
diff --git a/passlib/handlers/md5_crypt.py b/passlib/handlers/md5_crypt.py index 50f1c0d..7dc2ed8 100644 --- a/passlib/handlers/md5_crypt.py +++ b/passlib/handlers/md5_crypt.py @@ -40,7 +40,7 @@ def extend(source, size_ref): else: return source*m -def raw_md5_crypt(password, salt, apr=False): +def _raw_md5_crypt(password, salt, apr=False): """perform raw md5-crypt calculation :arg password: @@ -230,7 +230,7 @@ class md5_crypt(uh.HasManyBackends, _Md5Common): return test_crypt("test", '$1$test$pi/xDtU5WFVRqYS6BMU8X/') def _calc_checksum_builtin(self, secret): - return raw_md5_crypt(secret, self.salt) + return _raw_md5_crypt(secret, self.salt) def _calc_checksum_os_crypt(self, secret): hash = safe_crypt(secret, self.ident + self.salt) @@ -268,7 +268,7 @@ class apr_md5_crypt(_Md5Common): #primary interface #========================================================= def _calc_checksum(self, secret): - return raw_md5_crypt(secret, self.salt, apr=True) + return _raw_md5_crypt(secret, self.salt, apr=True) #========================================================= #eoc diff --git a/passlib/handlers/sha2_crypt.py b/passlib/handlers/sha2_crypt.py index bce7980..64274d9 100644 --- a/passlib/handlers/sha2_crypt.py +++ b/passlib/handlers/sha2_crypt.py @@ -42,7 +42,7 @@ def extend(source, size_ref): else: return source*m -def raw_sha_crypt(secret, salt, rounds, hash): +def _raw_sha_crypt(secret, salt, rounds, hash): """perform raw sha crypt :arg secret: password to encode (if unicode, encoded to utf-8) @@ -59,6 +59,8 @@ def raw_sha_crypt(secret, salt, rounds, hash): raise TypeError("secret must be encoded as bytes") #validate rounds + # XXX: this should be taken care of by handler, + # change this to an assertion? if rounds < 1000: rounds = 1000 if rounds > 999999999: #pragma: no cover @@ -152,10 +154,10 @@ def raw_sha_crypt(secret, salt, rounds, hash): #return unencoded result, along w/ normalized config values return c, salt, rounds -def raw_sha256_crypt(secret, salt, rounds): +def _raw_sha256_crypt(secret, salt, rounds): "perform raw sha256-crypt; returns encoded checksum, normalized salt & rounds" #run common crypt routine - result, salt, rounds = raw_sha_crypt(secret, salt, rounds, sha256) + result, salt, rounds = _raw_sha_crypt(secret, salt, rounds, sha256) out = h64.encode_transposed_bytes(result, _256_offsets) assert len(out) == 43, "wrong length: %r" % (out,) return out, salt, rounds @@ -174,10 +176,10 @@ _256_offsets = ( 30, 31, ) -def raw_sha512_crypt(secret, salt, rounds): +def _raw_sha512_crypt(secret, salt, rounds): "perform raw sha512-crypt; returns encoded checksum, normalized salt & rounds" #run common crypt routine - result, salt, rounds = raw_sha_crypt(secret, salt, rounds, sha512) + result, salt, rounds = _raw_sha_crypt(secret, salt, rounds, sha512) ###encode result out = h64.encode_transposed_bytes(result, _512_offsets) @@ -340,7 +342,7 @@ class sha256_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandl def _calc_checksum_builtin(self, secret): if isinstance(secret, unicode): secret = secret.encode("utf-8") - checksum, salt, rounds = raw_sha256_crypt(secret, + checksum, salt, rounds = _raw_sha256_crypt(secret, self.salt.encode("ascii"), self.rounds) assert salt == self.salt.encode("ascii"), \ @@ -498,7 +500,7 @@ class sha512_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandl def _calc_checksum_builtin(self, secret): if isinstance(secret, unicode): secret = secret.encode("utf-8") - checksum, salt, rounds = raw_sha512_crypt(secret, + checksum, salt, rounds = _raw_sha512_crypt(secret, self.salt.encode("ascii"), self.rounds) assert salt == self.salt.encode("ascii"), \ diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py index 5c94b73..cdb1d50 100644 --- a/passlib/tests/test_handlers.py +++ b/passlib/tests/test_handlers.py @@ -12,7 +12,7 @@ import warnings from passlib import hash from passlib.utils.compat import irange from passlib.tests.utils import TestCase, HandlerCase, create_backend_case, \ - enable_option, b, catch_warnings + enable_option, b, catch_warnings, UserHandlerMixin from passlib.utils.compat import u #module @@ -104,22 +104,6 @@ class _BCryptTest(HandlerCase): "bcryptor: hash_key(%r,%r):" % (secret, hash)) yield check_bcryptor - def test_90_idents(self): - "test identifier validation" - handler = self.handler - - kwds = dict(checksum='8CIhhFCj15KqqFvo/n.Jatx8dJ92f82', - salt='VlsfIX9.apXuQBr6tego0.', - rounds=12, ident="2a") - - handler(**kwds) - - kwds.update(ident=None) - self.assertRaises(TypeError, handler, **kwds) - - kwds.update(use_defaults=True, ident='Q') - self.assertRaises(ValueError, handler, **kwds) - #=============================================================== # see issue 25 - https://code.google.com/p/passlib/issues/detail?id=25 # bcrypt's salt ends with 4 padding bits. @@ -268,7 +252,8 @@ class BigCryptTest(HandlerCase): ] #omit des_crypt from known other, it looks like bigcrypt - known_other_hashes = [row for row in HandlerCase.known_other_hashes if row[0] != "des_crypt"] + known_other_hashes = [row for row in HandlerCase.known_other_hashes + if row[0] != "des_crypt"] #========================================================= #bsdi crypt @@ -312,6 +297,7 @@ class Crypt16Test(HandlerCase): ("LOLOAQIC", "/.FcK3mad6JwYelhbtlysKy6"), ("L", "/.CIu/PzYCkl6elhbtlysKy6"), ] + #========================================================= #des crypt #========================================================= @@ -644,6 +630,7 @@ class _Md5CryptTest(HandlerCase): ('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', '$1$jQS7o98J$V6iTcr71CGgwW2laf17pi1'), ('test', '$1$SuMrG47N$ymvzYjr7QcEQjaK5m1PGx1'), (b('test'), '$1$SuMrG47N$ymvzYjr7QcEQjaK5m1PGx1'), + (u('s'), '$1$ssssssss$YgmLTApYTv12qgTwBoj8i/'), ] known_malformed_hashes = [ @@ -651,9 +638,6 @@ class _Md5CryptTest(HandlerCase): '$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o!', ] - def test_raw(self): - self.assertEqual(raw_md5_crypt(u('s'),u('s')*16), u('YgmLTApYTv12qgTwBoj8i/')) - OsCrypt_Md5CryptTest = create_backend_case(_Md5CryptTest, "os_crypt") Builtin_Md5CryptTest = create_backend_case(_Md5CryptTest, "builtin") @@ -707,24 +691,12 @@ class NTHashTest(HandlerCase): '$3$$7f8fe03093cc84b267b109625f6bbfxb', ] - def test_idents(self): - handler = self.handler - - kwds = dict(checksum='7f8fe03093cc84b267b109625f6bbf4b', ident="3") - handler(**kwds) - - kwds.update(ident=None) - self.assertRaises(TypeError, handler, **kwds) - - kwds.update(use_defaults=True, ident='Q') - self.assertRaises(ValueError, handler, **kwds) - #========================================================= #oracle 10 & 11 #========================================================= from passlib.handlers.oracle import oracle10, oracle11 -class Oracle10Test(HandlerCase): +class Oracle10Test(UserHandlerMixin, HandlerCase): handler = oracle10 known_correct_hashes = [ @@ -742,46 +714,6 @@ class Oracle10Test(HandlerCase): 'F894844C34402B6Z', ] - def test_user(self): - "check user kwd is required for encrypt/verify" - self.assertRaises(TypeError, self.handler.encrypt, 'mypass') - self.assertRaises(ValueError, self.handler.encrypt, 'mypass', None) - self.assertRaises(TypeError, self.handler.verify, 'mypass', 'CC60FA650C497E52') - - #NOTE: all of the methods below are merely to override - # the default test harness in order to insert a default username - # when encrypt/verify/etc are called. - - def create_mismatch(self, secret): - if isinstance(secret, tuple): - secret, user = secret - return 'x' + secret, user - else: - return 'x' + secret - - def do_encrypt(self, secret, **kwds): - if isinstance(secret, tuple): - secret, user = secret - else: - user = 'default' - assert 'user' not in kwds - kwds['user'] = user - return self.handler.encrypt(secret, **kwds) - - def do_verify(self, secret, hash): - if isinstance(secret, tuple): - secret, user = secret - else: - user = 'default' - return self.handler.verify(secret, hash, user=user) - - def do_genhash(self, secret, config): - if isinstance(secret, tuple): - secret, user = secret - else: - user = 'default' - return self.handler.genhash(secret, config, user=user) - class Oracle11Test(HandlerCase): handler = oracle11 known_correct_hashes = [ @@ -913,19 +845,6 @@ class PHPassTest(HandlerCase): '$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r!L0', ] - def test_idents(self): - handler = self.handler - - kwds = dict(checksum='eRo7ud9Fh4E2PdI0S3r.L0', salt='IQRaTwmf', - rounds=9, ident="P") - handler(**kwds) - - kwds.update(ident=None) - self.assertRaises(TypeError, handler, **kwds) - - kwds.update(use_defaults=True, ident='Q') - self.assertRaises(ValueError, handler, **kwds) - #========================================================= #plaintext #========================================================= @@ -948,7 +867,7 @@ class PlaintextTest(HandlerCase): #========================================================= from passlib.handlers.postgres import postgres_md5 -class PostgresMD5CryptTest(HandlerCase): +class PostgresMD5CryptTest(UserHandlerMixin, HandlerCase): handler = postgres_md5 known_correct_hashes = [ # ((secret,user),hash) @@ -961,51 +880,6 @@ class PostgresMD5CryptTest(HandlerCase): 'md54zc31989b20437833f697e485811254b', ] - #NOTE: used to support secret=(password, user) format, but removed it for now. - ##def test_tuple_mode(self): - ## "check tuple mode works for encrypt/verify" - ## self.assertEqual(self.handler.encrypt(('mypass', 'postgres')), - ## 'md55fba2ea04fd36069d2574ea71c8efe9d') - ## self.assertEqual(self.handler.verify(('mypass', 'postgres'), - ## 'md55fba2ea04fd36069d2574ea71c8efe9d'), True) - - def test_user(self): - "check user kwd is required for encrypt/verify" - self.handler.encrypt("mypass", u('user')) - self.assertRaises(TypeError, self.handler.encrypt, 'mypass') - self.assertRaises(ValueError, self.handler.encrypt, 'mypass', None) - self.assertRaises(TypeError, self.handler.verify, 'mypass', 'md55fba2ea04fd36069d2574ea71c8efe9d') - - def create_mismatch(self, secret): - if isinstance(secret, tuple): - secret, user = secret - return 'x' + secret, user - else: - return 'x' + secret - - def do_encrypt(self, secret, **kwds): - if isinstance(secret, tuple): - secret, user = secret - else: - user = 'default' - assert 'user' not in kwds - kwds['user'] = user - return self.handler.encrypt(secret, **kwds) - - def do_verify(self, secret, hash): - if isinstance(secret, tuple): - secret, user = secret - else: - user = 'default' - return self.handler.verify(secret, hash, user=user) - - def do_genhash(self, secret, config): - if isinstance(secret, tuple): - secret, user = secret - else: - user = 'default' - return self.handler.genhash(secret, config, user=user) - #========================================================= # scram hash #========================================================= @@ -1375,16 +1249,6 @@ class _SHA256CryptTest(HandlerCase): filter_config_warnings = True # rounds too low, salt too small - def test_raw(self): - #run some tests on raw backend func to ensure it works right - self.assertEqual( - raw_sha_crypt(b('secret'), b('salt')*10, 1, hashlib.md5), - (b('\x1f\x96\x1cO\x11\xa9h\x12\xc4\xf3\x9c\xee\xf5\x93\xf3\xdd'), - b('saltsaltsaltsalt'), - 1000) - ) - self.assertRaises(ValueError, raw_sha_crypt, b('secret'), b('$'), 1, hashlib.md5) - OsCrypt_SHA256CryptTest = create_backend_case(_SHA256CryptTest, "os_crypt") Builtin_SHA256CryptTest = create_backend_case(_SHA256CryptTest, "builtin") diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py index c4019ab..f853874 100644 --- a/passlib/tests/utils.py +++ b/passlib/tests/utils.py @@ -170,12 +170,22 @@ class TestCase(unittest.TestCase): @classproperty def __unittest_skip__(cls): + # make this mirror nose's '__test__' attr return not getattr(cls, "__test__", True) @classproperty def __test__(cls): - #so nose won't auto run *this* cls, but it will for subclasses - return cls is not TestCase and not cls.__name__.startswith("_") + # nose uses to this to skip tests. overridding this to + # skip classes with '__<cls>_unittest_skip' set - that way + # we can omit specific classes without affecting subclasses. + name = cls.__name__ + if name.startswith("_"): + return False + if getattr(cls, "_%s__unittest_skip" % name, False): + return False + return True + + __unittest_skip = True #============================================================ # tweak msg formatting for some assert methods @@ -663,23 +673,23 @@ class HandlerCase(TestCase): if not isinstance(cls, type) or not issubclass(cls, uh.HasManyIdents): raise self.skipTest("handler doesn't derive from HasManyIdents") - #check settings + # check settings self.assertTrue('ident' in cls.setting_kwds) - #check ident_values list + # check ident_values list for value in cls.ident_values: self.assertIsInstance(value, unicode, "cls.ident_values must be unicode:") self.assertTrue(len(cls.ident_values)>1, "cls.ident_values must have 2+ elements:") - #check default_ident value + # check default_ident value self.assertIsInstance(cls.default_ident, unicode, "cls.default_ident must be unicode:") self.assertTrue(cls.default_ident in cls.ident_values, "cls.default_ident must specify member of cls.ident_values") - #check optional aliases list + # check optional aliases list if cls.ident_aliases: for alias, ident in iteritems(cls.ident_aliases): self.assertIsInstance(alias, unicode, @@ -689,6 +699,27 @@ class HandlerCase(TestCase): self.assertTrue(ident in cls.ident_values, "cls.ident_aliases must map to cls.ident_values members: %r" % (ident,)) + # check constructor validates ident correctly. + handler = cls + if self.known_correct_hashes: + hash = self.known_correct_hashes[0][1] + else: + hash = self.do_encrypt("stub") + kwds = _hobj_to_dict(handler.from_string(hash)) + del kwds['ident'] + + # ... accepts good ident + handler(ident=cls.default_ident, **kwds) + + # ... requires ident w/o defaults + self.assertRaises(TypeError, handler, **kwds) + + # ... supplies default ident + handler(use_defaults=True, **kwds) + + # ... rejects bad ident + self.assertRaises(ValueError, handler, ident='xXx', **kwds) + RESERVED_BACKEND_NAMES = [ "any", "default", None ] def test_04_backend_handler(self): @@ -954,7 +985,7 @@ class HandlerCase(TestCase): self.assertRaises(ValueError, handler.genhash, 'secret', None) #========================================================= - #encrypt() + # encrypt() #========================================================= def test_50_encrypt_plain(self): "test encrypt() basic behavior" @@ -1029,7 +1060,7 @@ class HandlerCase(TestCase): helper(secret, hash) #========================================================= - #test max password size + # misc tests #========================================================= def test_60_secret_chars(self): "test secret_chars limit" @@ -1073,6 +1104,63 @@ class HandlerCase(TestCase): #eoc #========================================================= +class UserHandlerMixin(HandlerCase): + """helper for handlers w/ 'user' context kwd; mixin for HandlerCase + + this overrides the HandlerCase test harness methods + so that a username is automatically inserted to encrypt/verify + calls. as well, passing in a pair of strings as the password + will be interpreted as (secret,user) + """ + __unittest_skip = True + + def test_70_user(self): + "test user context keyword is required" + handler = self.handler + password = 'stub' + hash = self.known_correct_hashes[0][1] + + handler.encrypt(password, u('user')) + + self.assertRaises(TypeError, handler.encrypt, password) + self.assertRaises(TypeError, handler.encrypt, password, None) + + self.assertRaises(TypeError, handler.genhash, password, hash) + self.assertRaises(TypeError, handler.genhash, password, hash, None) + + self.assertRaises(TypeError, handler.verify, password, hash) + self.assertRaises(TypeError, handler.verify, password, hash, None) + + def create_mismatch(self, secret): + if isinstance(secret, tuple): + secret, user = secret + return 'x' + secret, user + else: + return 'x' + secret + + def do_encrypt(self, secret, **kwds): + if isinstance(secret, tuple): + secret, user = secret + else: + user = 'default' + assert 'user' not in kwds + kwds['user'] = user + return self.handler.encrypt(secret, **kwds) + + def do_verify(self, secret, hash): + if isinstance(secret, tuple): + secret, user = secret + else: + user = 'default' + return self.handler.verify(secret, hash, user=user) + + def do_genhash(self, secret, config): + if isinstance(secret, tuple): + secret, user = secret + else: + user = 'default' + return self.handler.genhash(secret, config, user=user) + #========================================================= #backend test helpers #========================================================= @@ -1125,6 +1213,17 @@ def _has_relaxed_setting(handler): return 'relaxed' in handler.setting_kwds or issubclass(handler, uh.GenericHandler) +def _hobj_to_dict(hobj): + "hack to convert handler instance to dict" + # FIXME: would be good to distinguish config-string keywords + # from generation options (e.g. salt size) in programmatic manner. + exclude_keys = ["salt_size", "relaxed"] + return dict( + (key, getattr(hobj, key)) + for key in hobj.setting_kwds + if key not in exclude_keys + ) + def create_backend_case(base, name, module="passlib.tests.test_handlers"): "create a test case for specific backend of a multi-backend handler" #get handler, figure out if backend should be tested |