summaryrefslogtreecommitdiff
path: root/passlib/tests
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2011-12-28 12:42:06 -0500
committerEli Collins <elic@assurancetechnologies.com>2011-12-28 12:42:06 -0500
commit2c443dba95ecd986838dc315a3e590c7fe48695a (patch)
tree3bf5165968fbdca16803333df396ab579630fea6 /passlib/tests
parent0114171bb9d38ab35ac6e754059839fd58ffffc8 (diff)
parent73aefa5cdca69e1daf4297d8176d4a99434d33a2 (diff)
downloadpasslib-2c443dba95ecd986838dc315a3e590c7fe48695a.tar.gz
Merge from default
Diffstat (limited to 'passlib/tests')
-rw-r--r--passlib/tests/test_context.py321
-rw-r--r--passlib/tests/test_utils.py86
-rw-r--r--passlib/tests/test_utils_handlers.py35
-rw-r--r--passlib/tests/utils.py13
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
#============================================================