diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2011-12-28 12:42:06 -0500 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2011-12-28 12:42:06 -0500 |
| commit | 2c443dba95ecd986838dc315a3e590c7fe48695a (patch) | |
| tree | 3bf5165968fbdca16803333df396ab579630fea6 /passlib/tests | |
| parent | 0114171bb9d38ab35ac6e754059839fd58ffffc8 (diff) | |
| parent | 73aefa5cdca69e1daf4297d8176d4a99434d33a2 (diff) | |
| download | passlib-2c443dba95ecd986838dc315a3e590c7fe48695a.tar.gz | |
Merge from default
Diffstat (limited to 'passlib/tests')
| -rw-r--r-- | passlib/tests/test_context.py | 321 | ||||
| -rw-r--r-- | passlib/tests/test_utils.py | 86 | ||||
| -rw-r--r-- | passlib/tests/test_utils_handlers.py | 35 | ||||
| -rw-r--r-- | passlib/tests/utils.py | 13 |
4 files changed, 357 insertions, 98 deletions
diff --git a/passlib/tests/test_context.py b/passlib/tests/test_context.py index 187dff4..712f92c 100644 --- a/passlib/tests/test_context.py +++ b/passlib/tests/test_context.py @@ -18,7 +18,7 @@ except ImportError: #pkg from passlib import hash from passlib.context import CryptContext, CryptPolicy, LazyCryptContext -from passlib.utils import to_bytes, to_unicode +from passlib.utils import to_bytes, to_unicode, PasslibPolicyWarning from passlib.utils.compat import irange import passlib.utils.handlers as uh from passlib.tests.utils import TestCase, mktemp, catch_warnings, \ @@ -88,7 +88,7 @@ sha512_crypt.min_rounds = 40000 sample_config_1prd = dict( schemes = [ hash.des_crypt, hash.md5_crypt, hash.bsdi_crypt, hash.sha512_crypt], - default = hash.md5_crypt, + default = "md5_crypt", # NOTE: passlib <= 1.5 was handler obj. all__vary_rounds = "10%", bsdi_crypt__max_rounds = 30000, bsdi_crypt__default_rounds = 25000, @@ -201,16 +201,16 @@ admin__context__deprecated = des_crypt, bsdi_crypt policy = CryptPolicy(**self.sample_config_1pd) self.assertEqual(policy.to_dict(), self.sample_config_1pd) - #check with bad key - self.assertRaises(KeyError, CryptPolicy, + #check key with too many separators is rejected + self.assertRaises(TypeError, CryptPolicy, schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"], bad__key__bsdi_crypt__max_rounds = 30000, ) - #check with bad handler - self.assertRaises(TypeError, CryptPolicy, schemes=[uh.StaticHandler]) + #check nameless handler rejected + self.assertRaises(ValueError, CryptPolicy, schemes=[uh.StaticHandler]) - #check with multiple handlers + #check name conflicts are rejected class dummy_1(uh.StaticHandler): name = 'dummy_1' self.assertRaises(KeyError, CryptPolicy, schemes=[dummy_1, dummy_1]) @@ -452,7 +452,19 @@ admin__context__deprecated = des_crypt, bsdi_crypt self.assertTrue(pb.handler_is_deprecated("des_crypt", "admin")) self.assertTrue(pb.handler_is_deprecated("bsdi_crypt", "admin")) + # check deprecation is overridden per category + pc = CryptPolicy( + schemes=["md5_crypt", "des_crypt"], + deprecated=["md5_crypt"], + user__context__deprecated=["des_crypt"], + ) + self.assertTrue(pc.handler_is_deprecated("md5_crypt")) + self.assertFalse(pc.handler_is_deprecated("des_crypt")) + self.assertFalse(pc.handler_is_deprecated("md5_crypt", "user")) + self.assertTrue(pc.handler_is_deprecated("des_crypt", "user")) + def test_15_min_verify_time(self): + "test get_min_verify_time() method" pa = CryptPolicy() self.assertEqual(pa.get_min_verify_time(), 0) self.assertEqual(pa.get_min_verify_time('admin'), 0) @@ -469,10 +481,6 @@ admin__context__deprecated = des_crypt, bsdi_crypt self.assertEqual(pd.get_min_verify_time(), .1) self.assertEqual(pd.get_min_verify_time('admin'), .2) - #TODO: test this. - ##def test_gen_min_verify_time(self): - ## "test get_min_verify_time() method" - #========================================================= #serialization #========================================================= @@ -577,11 +585,15 @@ class CryptContextTest(TestCase): nthash__ident = "NT", ) - def test_10_genconfig_settings(self): - "test genconfig() honors policy settings" - cc = CryptContext(policy=None, **self.sample_policy_1) + def test_10_01_genconfig_settings(self): + "test genconfig() settings" + cc = CryptContext(policy=None, + schemes=["md5_crypt", "nthash"], + nthash__ident="NT", + ) # hash specific settings + self.assertTrue(cc.genconfig().startswith("$1$")) self.assertEqual( cc.genconfig(scheme="nthash"), '$NT$00000000000000000000000000000000', @@ -591,73 +603,192 @@ class CryptContextTest(TestCase): '$3$$00000000000000000000000000000000', ) + def test_10_02_genconfig_rounds_limits(self): + "test genconfig() policy rounds limits" + cc = CryptContext(policy=None, + schemes=["sha256_crypt"], + all__min_rounds=2000, + all__max_rounds=3000, + all__default_rounds=2500, + ) + # min rounds - self.assertEqual( - cc.genconfig(rounds=1999, salt="nacl"), - '$5$rounds=2000$nacl$', - ) - self.assertEqual( - cc.genconfig(rounds=2001, salt="nacl"), - '$5$rounds=2001$nacl$' - ) + with catch_warnings(record=True) as wlog: - #max rounds - self.assertEqual( - cc.genconfig(rounds=2999, salt="nacl"), - '$5$rounds=2999$nacl$', - ) - self.assertEqual( - cc.genconfig(rounds=3001, salt="nacl"), - '$5$rounds=3000$nacl$' - ) + # set below handler min + c2 = cc.replace(all__min_rounds=500, all__max_rounds=None, + all__default_rounds=500) + self.assertWarningMatches(wlog.pop(), category=PasslibPolicyWarning) + self.assertWarningMatches(wlog.pop(), category=PasslibPolicyWarning) + self.assertEqual(c2.genconfig(salt="nacl"), "$5$rounds=1000$nacl$") + self.assertNoWarnings(wlog) - #default rounds - specified - self.assertEqual( - cc.genconfig(scheme="bsdi_crypt", salt="nacl"), - '_N...nacl', - ) + # below + self.assertEqual( + cc.genconfig(rounds=1999, salt="nacl"), + '$5$rounds=2000$nacl$', + ) + self.assertWarningMatches(wlog.pop(), category=PasslibPolicyWarning) + self.assertNoWarnings(wlog) - #default rounds - fall back to max rounds - self.assertEqual( - cc.genconfig(salt="nacl"), - '$5$rounds=3000$nacl$', - ) + # equal + self.assertEqual( + cc.genconfig(rounds=2000, salt="nacl"), + '$5$rounds=2000$nacl$', + ) + self.assertNoWarnings(wlog) - #default rounds - out of bounds - cc2 = CryptContext(policy=cc.policy.replace( - bsdi_crypt__default_rounds=35)) - self.assertEqual( - cc2.genconfig(scheme="bsdi_crypt", salt="nacl"), - '_S...nacl', - ) + # above + self.assertEqual( + cc.genconfig(rounds=2001, salt="nacl"), + '$5$rounds=2001$nacl$' + ) + self.assertNoWarnings(wlog) - # default+vary rounds - # this runs enough times the min and max *should* be hit, - # though there's a faint chance it will randomly fail. - from passlib.hash import bsdi_crypt as bc - cc3 = CryptContext(policy=cc.policy.replace( - bsdi_crypt__vary_rounds = 3)) - seen = set() - for i in irange(3*2*50): - h = cc3.genconfig("bsdi_crypt", salt="nacl") - r = bc.from_string(h).rounds - seen.add(r) - self.assertTrue(min(seen)==22) - self.assertTrue(max(seen)==28) + # max rounds + with catch_warnings(record=True) as wlog: + # set above handler max + c2 = cc.replace(all__max_rounds=int(1e9)+500, all__min_rounds=None, + all__default_rounds=int(1e9)+500) + self.assertWarningMatches(wlog.pop(), category=PasslibPolicyWarning) + self.assertWarningMatches(wlog.pop(), category=PasslibPolicyWarning) + self.assertEqual(c2.genconfig(salt="nacl"), + "$5$rounds=999999999$nacl$") + self.assertNoWarnings(wlog) + + # above + self.assertEqual( + cc.genconfig(rounds=3001, salt="nacl"), + '$5$rounds=3000$nacl$' + ) + self.assertWarningMatches(wlog.pop(), category=PasslibPolicyWarning) + self.assertNoWarnings(wlog) + + # equal + self.assertEqual( + cc.genconfig(rounds=3000, salt="nacl"), + '$5$rounds=3000$nacl$' + ) + self.assertNoWarnings(wlog) + + # below + self.assertEqual( + cc.genconfig(rounds=2999, salt="nacl"), + '$5$rounds=2999$nacl$', + ) + self.assertNoWarnings(wlog) + + # explicit default rounds + self.assertEqual(cc.genconfig(salt="nacl"), '$5$rounds=2500$nacl$') + + # fallback default rounds - use handler's + c2 = cc.replace(all__default_rounds=None, all__max_rounds=50000) + self.assertEqual(c2.genconfig(salt="nacl"), '$5$rounds=40000$nacl$') - # default+vary % rounds - # this runs enough times the min and max *should* be hit, + # fallback default rounds - use handler's, but clipped to max rounds + c2 = cc.replace(all__default_rounds=None, all__max_rounds=3000) + self.assertEqual(c2.genconfig(salt="nacl"), '$5$rounds=3000$nacl$') + + # TODO: test default falls back to mx / mn if handler has no default. + + #default rounds - out of bounds + self.assertRaises(ValueError, cc.replace, all__default_rounds=1999) + cc.policy.replace(all__default_rounds=2000) + cc.policy.replace(all__default_rounds=3000) + self.assertRaises(ValueError, cc.replace, all__default_rounds=3001) + + # invalid min/max bounds + c2 = CryptContext(policy=None, schemes=["sha256_crypt"]) + self.assertRaises(ValueError, c2.replace, all__min_rounds=-1) + self.assertRaises(ValueError, c2.replace, all__max_rounds=-1) + self.assertRaises(ValueError, c2.replace, all__min_rounds=2000, + all__max_rounds=1999) + + def test_10_03_genconfig_linear_vary_rounds(self): + "test genconfig() linear vary rounds" + cc = CryptContext(policy=None, + schemes=["sha256_crypt"], + all__min_rounds=1995, + all__max_rounds=2005, + all__default_rounds=2000, + ) + + # test negative + self.assertRaises(ValueError, cc.replace, all__vary_rounds=-1) + self.assertRaises(ValueError, cc.replace, all__vary_rounds="-1%") + self.assertRaises(ValueError, cc.replace, all__vary_rounds="101%") + + # test static + c2 = cc.replace(all__vary_rounds=0) + self.assert_rounds_range(c2, "sha256_crypt", 2000, 2000) + + c2 = cc.replace(all__vary_rounds="0%") + self.assert_rounds_range(c2, "sha256_crypt", 2000, 2000) + + # test absolute + c2 = cc.replace(all__vary_rounds=1) + self.assert_rounds_range(c2, "sha256_crypt", 1999, 2001) + c2 = cc.replace(all__vary_rounds=100) + self.assert_rounds_range(c2, "sha256_crypt", 1995, 2005) + + # test relative + c2 = cc.replace(all__vary_rounds="0.1%") + self.assert_rounds_range(c2, "sha256_crypt", 1998, 2002) + c2 = cc.replace(all__vary_rounds="100%") + self.assert_rounds_range(c2, "sha256_crypt", 1995, 2005) + + def test_10_03_genconfig_log2_vary_rounds(self): + "test genconfig() log2 vary rounds" + cc = CryptContext(policy=None, + schemes=["bcrypt"], + all__min_rounds=15, + all__max_rounds=25, + all__default_rounds=20, + ) + + # test negative + self.assertRaises(ValueError, cc.replace, all__vary_rounds=-1) + self.assertRaises(ValueError, cc.replace, all__vary_rounds="-1%") + self.assertRaises(ValueError, cc.replace, all__vary_rounds="101%") + + # test static + c2 = cc.replace(all__vary_rounds=0) + self.assert_rounds_range(c2, "bcrypt", 20, 20) + + c2 = cc.replace(all__vary_rounds="0%") + self.assert_rounds_range(c2, "bcrypt", 20, 20) + + # test absolute + c2 = cc.replace(all__vary_rounds=1) + self.assert_rounds_range(c2, "bcrypt", 19, 21) + c2 = cc.replace(all__vary_rounds=100) + self.assert_rounds_range(c2, "bcrypt", 15, 25) + + # test relative - should shift over at 50% mark + c2 = cc.replace(all__vary_rounds="1%") + self.assert_rounds_range(c2, "bcrypt", 20, 20) + + c2 = cc.replace(all__vary_rounds="49%") + self.assert_rounds_range(c2, "bcrypt", 20, 20) + + c2 = cc.replace(all__vary_rounds="50%") + self.assert_rounds_range(c2, "bcrypt", 19, 20) + + c2 = cc.replace(all__vary_rounds="100%") + self.assert_rounds_range(c2, "bcrypt", 15, 21) + + def assert_rounds_range(self, context, scheme, lower, upper, salt="."*22): + "helper to check vary_rounds covers specified range" + # NOTE: this runs enough times the min and max *should* be hit, # though there's a faint chance it will randomly fail. - from passlib.hash import sha256_crypt as sc - cc4 = CryptContext(policy=cc.policy.replace( - all__vary_rounds = "1%")) + handler = context.policy.get_handler(scheme) seen = set() - for i in irange(30*50): - h = cc4.genconfig(salt="nacl") - r = sc.from_string(h).rounds + for i in irange(300): + h = context.genconfig(scheme, salt=salt) + r = handler.from_string(h).rounds seen.add(r) - self.assertTrue(min(seen)==2970) - self.assertTrue(max(seen)==3000) #NOTE: would be 3030, but clipped by max_rounds + self.assertEqual(min(seen), lower, "vary_rounds lower bound:") + self.assertEqual(max(seen), upper, "vary_rounds upper bound:") def test_11_encrypt_settings(self): "test encrypt() honors policy settings" @@ -673,33 +804,29 @@ class CryptContextTest(TestCase): '$3$$8846f7eaee8fb117ad06bdd830b7586c', ) + # NOTE: more thorough job of rounds limits done in genconfig() test, + # which is much cheaper, and shares the same codebase. + # min rounds - self.assertEqual( - cc.encrypt("password", rounds=1999, salt="nacl"), - '$5$rounds=2000$nacl$9/lTZ5nrfPuz8vphznnmHuDGFuvjSNvOEDsGmGfsS97', - ) - self.assertEqual( - cc.encrypt("password", rounds=2001, salt="nacl"), - '$5$rounds=2001$nacl$8PdeoPL4aXQnJ0woHhqgIw/efyfCKC2WHneOpnvF.31' - ) + with catch_warnings(record=True) as wlog: + self.assertEqual( + cc.encrypt("password", rounds=1999, salt="nacl"), + '$5$rounds=2000$nacl$9/lTZ5nrfPuz8vphznnmHuDGFuvjSNvOEDsGmGfsS97', + ) + self.assertWarningMatches(wlog.pop(), category=PasslibPolicyWarning) + self.assertFalse(wlog) - #TODO: - # max rounds - # default rounds - # falls back to max, then min. - # specified - # outside of min/max range - # default+vary rounds - # default+vary % rounds - - #make sure default > max doesn't cause error when vary is set - cc2 = cc.replace(sha256_crypt__default_rounds=4000) - with catch_warnings(): - warnings.filterwarnings("ignore", "vary default rounds: lower bound > upper bound.*", UserWarning) self.assertEqual( - cc2.encrypt("password", salt="nacl"), - '$5$rounds=3000$nacl$oH831OVMbkl.Lbw1SXflly4dW8L3mSxpxDz1u1CK/B0', + cc.encrypt("password", rounds=2001, salt="nacl"), + '$5$rounds=2001$nacl$8PdeoPL4aXQnJ0woHhqgIw/efyfCKC2WHneOpnvF.31' ) + self.assertFalse(wlog) + + # max rounds, etc tested in genconfig() + + # make default > max throws error if attempted + self.assertRaises(ValueError, cc.replace, + sha256_crypt__default_rounds=4000) def test_12_hash_needs_update(self): "test hash_needs_update() method" @@ -707,14 +834,14 @@ class CryptContextTest(TestCase): #check deprecated scheme self.assertTrue(cc.hash_needs_update('9XXD4trGYeGJA')) - self.assertTrue(not cc.hash_needs_update('$1$J8HC2RCr$HcmM.7NxB2weSvlw2FgzU0')) + self.assertFalse(cc.hash_needs_update('$1$J8HC2RCr$HcmM.7NxB2weSvlw2FgzU0')) #check min rounds self.assertTrue(cc.hash_needs_update('$5$rounds=1999$jD81UCoo.zI.UETs$Y7qSTQ6mTiU9qZB4fRr43wRgQq4V.5AAf7F97Pzxey/')) - self.assertTrue(not cc.hash_needs_update('$5$rounds=2000$228SSRje04cnNCaQ$YGV4RYu.5sNiBvorQDlO0WWQjyJVGKBcJXz3OtyQ2u8')) + self.assertFalse(cc.hash_needs_update('$5$rounds=2000$228SSRje04cnNCaQ$YGV4RYu.5sNiBvorQDlO0WWQjyJVGKBcJXz3OtyQ2u8')) #check max rounds - self.assertTrue(not cc.hash_needs_update('$5$rounds=3000$fS9iazEwTKi7QPW4$VasgBC8FqlOvD7x2HhABaMXCTh9jwHclPA9j5YQdns.')) + self.assertFalse(cc.hash_needs_update('$5$rounds=3000$fS9iazEwTKi7QPW4$VasgBC8FqlOvD7x2HhABaMXCTh9jwHclPA9j5YQdns.')) self.assertTrue(cc.hash_needs_update('$5$rounds=3001$QlFHHifXvpFX4PLs$/0ekt7lSs/lOikSerQ0M/1porEHxYq7W/2hdFpxA3fA')) #========================================================= diff --git a/passlib/tests/test_utils.py b/passlib/tests/test_utils.py index 26d6386..70090e8 100644 --- a/passlib/tests/test_utils.py +++ b/passlib/tests/test_utils.py @@ -143,6 +143,16 @@ class MiscTest(TestCase): else: self.assertEqual(ret, (True, u('aaOx.5nbTU/.M'))) + # test safe_os_crypt() handles os_crypt() returning None + # (Python's Modules/_cryptmodule.c notes some platforms may do this + # when algorithm is not supported) + orig = utils.os_crypt + try: + utils.os_crypt = lambda secret, hash: None + self.assertEqual(safe_os_crypt(u'test', u'aa'), (False,None)) + finally: + utils.os_crypt = orig + def test_consteq(self): "test consteq()" # NOTE: this test is kind of over the top, but that's only because @@ -227,6 +237,82 @@ class MiscTest(TestCase): ## ## first = False ## ##print ", ".join(str(c) for c in [run] + times) + def test_saslprep(self): + "test saslprep() unicode normalizer" + from passlib.utils import saslprep as sp + + # invalid types + self.assertRaises(TypeError, sp, None) + self.assertRaises(TypeError, sp, 1) + self.assertRaises(TypeError, sp, b('')) + + # empty strings + self.assertEqual(sp(u''), u'') + self.assertEqual(sp(u'\u00AD'), u'') + + # verify B.1 chars are stripped, + self.assertEqual(sp(u"$\u00AD$\u200D$"), u"$$$") + + # verify C.1.2 chars are replaced with space + self.assertEqual(sp(u"$ $\u00A0$\u3000$"), u"$ $ $ $") + + # verify normalization to KC + self.assertEqual(sp(u"a\u0300"), u"\u00E0") + self.assertEqual(sp(u"\u00E0"), u"\u00E0") + + # verify various forbidden characters + # control chars + self.assertRaises(ValueError, sp, u"\u0000") + self.assertRaises(ValueError, sp, u"\u007F") + self.assertRaises(ValueError, sp, u"\u180E") + self.assertRaises(ValueError, sp, u"\uFFF9") + # private use + self.assertRaises(ValueError, sp, u"\uE000") + # non-characters + self.assertRaises(ValueError, sp, u"\uFDD0") + # surrogates + self.assertRaises(ValueError, sp, u"\uD800") + # non-plaintext chars + self.assertRaises(ValueError, sp, u"\uFFFD") + # non-canon + self.assertRaises(ValueError, sp, u"\u2FF0") + # change display properties + self.assertRaises(ValueError, sp, u"\u200E") + self.assertRaises(ValueError, sp, u"\u206F") + # unassigned code points (as of unicode 3.2) + self.assertRaises(ValueError, sp, u"\u0900") + self.assertRaises(ValueError, sp, u"\uFFF8") + + # verify bidi behavior + # if starts with R/AL -- must end with R/AL + self.assertRaises(ValueError, sp, u"\u0627\u0031") + self.assertEqual(sp(u"\u0627"), u"\u0627") + self.assertEqual(sp(u"\u0627\u0628"), u"\u0627\u0628") + self.assertEqual(sp(u"\u0627\u0031\u0628"), u"\u0627\u0031\u0628") + # if starts with R/AL -- cannot contain L + self.assertRaises(ValueError, sp, u"\u0627\u0041\u0628") + # if doesn't start with R/AL -- can contain R/AL, but L & EN allowed + self.assertRaises(ValueError, sp, u"x\u0627z") + self.assertEqual(sp(u"x\u0041z"), u"x\u0041z") + + #------------------------------------------------------ + # examples pulled from external sources, to be thorough + #------------------------------------------------------ + + # rfc 4031 section 3 examples + self.assertEqual(sp(u"I\u00ADX"), u"IX") # strip SHY + self.assertEqual(sp(u"user"), u"user") # unchanged + self.assertEqual(sp(u"USER"), u"USER") # case preserved + self.assertEqual(sp(u"\u00AA"), u"a") # normalize to KC form + self.assertEqual(sp(u"\u2168"), u"IX") # normalize to KC form + self.assertRaises(ValueError, sp, u"\u0007") # forbid control chars + self.assertRaises(ValueError, sp, u"\u0627\u0031") # invalid bidi + + # rfc 3454 section 6 examples + # starts with RAL char, must end with RAL char + self.assertRaises(ValueError, sp, u"\u0627\u0031") + self.assertEqual(sp(u"\u0627\u0031\u0628"), u"\u0627\u0031\u0628") + #========================================================= #byte/unicode helpers #========================================================= diff --git a/passlib/tests/test_utils_handlers.py b/passlib/tests/test_utils_handlers.py index 30c4061..4ed5f06 100644 --- a/passlib/tests/test_utils_handlers.py +++ b/passlib/tests/test_utils_handlers.py @@ -10,7 +10,7 @@ from logging import getLogger import warnings #site #pkg -from passlib.hash import ldap_md5 +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.utils import rng, getrandstr, handlers as uh, bytes, b, \ @@ -332,6 +332,11 @@ class PrefixWrapperTest(TestCase): d1 = uh.PrefixWrapper("d1", "ldap_md5", "{XXX}", "{MD5}") self.assertEqual(d1.name, "d1") self.assertIs(d1.setting_kwds, ldap_md5.setting_kwds) + self.assertFalse('max_rounds' in dir(d1)) + + d2 = uh.PrefixWrapper("d2", "sha256_crypt", "{XXX}") + self.assertIs(d2.setting_kwds, sha256_crypt.setting_kwds) + self.assertTrue('max_rounds' in dir(d2)) def test_11_wrapped_methods(self): d1 = uh.PrefixWrapper("d1", "ldap_md5", "{XXX}", "{MD5}") @@ -357,6 +362,34 @@ class PrefixWrapperTest(TestCase): self.assertRaises(ValueError, d1.verify, "password", lph) self.assertTrue(d1.verify("password", dph)) + def test_12_ident(self): + # test ident is proxied + h = uh.PrefixWrapper("h2", "ldap_md5", "{XXX}") + self.assertEqual(h.ident, u"{XXX}{MD5}") + self.assertIs(h.ident_values, None) + + # test orig_prefix disabled ident proxy + h = uh.PrefixWrapper("h1", "ldap_md5", "{XXX}", "{MD5}") + self.assertIs(h.ident, None) + self.assertIs(h.ident_values, None) + + # test custom ident overrides default + h = uh.PrefixWrapper("h3", "ldap_md5", "{XXX}", ident="{X") + self.assertEqual(h.ident, u"{X") + self.assertIs(h.ident_values, None) + + # test custom ident must match + h = uh.PrefixWrapper("h3", "ldap_md5", "{XXX}", ident="{XXX}A") + self.assertRaises(ValueError, uh.PrefixWrapper, "h3", "ldap_md5", + "{XXX}", ident="{XY") + self.assertRaises(ValueError, uh.PrefixWrapper, "h3", "ldap_md5", + "{XXX}", ident="{XXXX") + + # test ident_values is proxied + h = uh.PrefixWrapper("h4", "bcrypt", "{XXX}") + self.assertIs(h.ident, None) + self.assertEqual(h.ident_values, [ u"{XXX}$2$", u"{XXX}$2a$" ]) + #========================================================= #sample algorithms - these serve as known quantities # to test the unittests themselves, as well as other diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py index 058f98e..cb72143 100644 --- a/passlib/tests/utils.py +++ b/passlib/tests/utils.py @@ -367,6 +367,19 @@ class TestCase(unittest.TestCase): ## raise TypeError("can't read lineno from warning object") ## self.assertEqual(wmsg.lineno, lineno, msg) + def assertNoWarnings(self, wlist, msg=None): + "assert that list (e.g. from catch_warnings) contains no warnings" + if not wlist: + return + wout = [self._formatWarning(w.message) for w in wlist] + std = "AssertionError: unexpected warnings: " + ", ".join(wout) + msg = self._formatMessage(msg, std) + raise self.failureException(msg) + + def _formatWarning(self, entry): + cls = type(entry) + return "<%s.%s %r>" % (cls.__module__,cls.__name__, str(entry)) + #============================================================ #eoc #============================================================ |
