summaryrefslogtreecommitdiff
path: root/passlib
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-02-09 15:19:20 -0500
committerEli Collins <elic@assurancetechnologies.com>2012-02-09 15:19:20 -0500
commit9bcfe9d440c56774a7e138aafcbaccb6b0ce6626 (patch)
tree720249f82734323f1f3f566256dd8768269442b9 /passlib
parent4b3590ab0d73e9defb70c97763a1b69a07a7f9be (diff)
downloadpasslib-9bcfe9d440c56774a7e138aafcbaccb6b0ce6626.tar.gz
consolidated some unit tests
Diffstat (limited to 'passlib')
-rw-r--r--passlib/handlers/md5_crypt.py6
-rw-r--r--passlib/handlers/sha2_crypt.py16
-rw-r--r--passlib/tests/test_handlers.py150
-rw-r--r--passlib/tests/utils.py115
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