update googleapiclient, httplib2, oauth2client and passlib to latest versions

This commit is contained in:
Jay Lee
2015-09-30 09:07:28 -04:00
parent 6ba62b66b4
commit 8b19040e45
85 changed files with 6642 additions and 4911 deletions

View File

@@ -1,4 +1,4 @@
"helper for method in test_registry.py"
"""helper for method in test_registry.py"""
from passlib.registry import register_crypt_handler
import passlib.utils.handlers as uh

View File

@@ -11,6 +11,7 @@ import time
# site
# pkg
from passlib import apache
from passlib.exc import MissingBackendError
from passlib.utils.compat import irange, unicode
from passlib.tests.utils import TestCase, get_file, set_file, catch_warnings, ensure_mtime_changed
from passlib.utils.compat import b, bytes, u
@@ -18,7 +19,7 @@ from passlib.utils.compat import b, bytes, u
log = getLogger(__name__)
def backdate_file_mtime(path, offset=10):
"backdate file's mtime by specified amount"
"""backdate file's mtime by specified amount"""
# NOTE: this is used so we can test code which detects mtime changes,
# without having to actually *pause* for that long.
atime = os.path.getatime(path)
@@ -29,7 +30,7 @@ def backdate_file_mtime(path, offset=10):
# htpasswd
#=============================================================================
class HtpasswdFileTest(TestCase):
"test HtpasswdFile class"
"""test HtpasswdFile class"""
descriptionPrefix = "HtpasswdFile"
# sample with 4 users
@@ -54,8 +55,17 @@ class HtpasswdFileTest(TestCase):
sample_dup = b('user1:pass1\nuser1:pass2\n')
# sample with bcrypt & sha256_crypt hashes
sample_05 = b('user2:2CHkkwa2AtqGs\n'
'user3:{SHA}3ipNV1GrBtxPmHFC21fCbVCSXIo=\n'
'user4:pass4\n'
'user1:$apr1$t4tc7jTh$GPIWVUo8sQKJlUdV8V5vu0\n'
'user5:$2a$12$yktDxraxijBZ360orOyCOePFGhuis/umyPNJoL5EbsLk.s6SWdrRO\n'
'user6:$5$rounds=110000$cCRp/xUUGVgwR4aP$'
'p0.QKFS5qLNRqw1/47lXYiAcgIjJK.WjCO8nrEKuUK.\n')
def test_00_constructor_autoload(self):
"test constructor autoload"
"""test constructor autoload"""
# check with existing file
path = self.mktemp()
set_file(path, self.sample_01)
@@ -97,7 +107,7 @@ class HtpasswdFileTest(TestCase):
self.assertFalse(ht.mtime)
def test_01_delete(self):
"test delete()"
"""test delete()"""
ht = apache.HtpasswdFile.from_string(self.sample_01)
self.assertTrue(ht.delete("user1")) # should delete both entries
self.assertTrue(ht.delete("user2"))
@@ -121,7 +131,7 @@ class HtpasswdFileTest(TestCase):
self.assertEqual(get_file(path), b("user2:pass2\n"))
def test_02_set_password(self):
"test set_password()"
"""test set_password()"""
ht = apache.HtpasswdFile.from_string(
self.sample_01, default_scheme="plaintext")
self.assertTrue(ht.set_password("user2", "pass2x"))
@@ -156,8 +166,29 @@ class HtpasswdFileTest(TestCase):
ht.set_password("user1", "pass2")
self.assertEqual(get_file(path), b("user1:pass2\n"))
def test_02_set_password_default_scheme(self):
"""test set_password() -- default_scheme"""
def check(scheme):
ht = apache.HtpasswdFile(default_scheme=scheme)
ht.set_password("user1", "pass1")
return ht.context.identify(ht.get_hash("user1"))
# explicit scheme
self.assertEqual(check("sha256_crypt"), "sha256_crypt")
self.assertEqual(check("des_crypt"), "des_crypt")
# unknown scheme
self.assertRaises(KeyError, check, "xxx")
# portable alias
self.assertEqual(check("portable"), apache.portable_scheme)
# default -- currently same as portable, will be host-specific under passlib 1.7.
self.assertEqual(check(None), "apr_md5_crypt")
def test_03_users(self):
"test users()"
"""test users()"""
ht = apache.HtpasswdFile.from_string(self.sample_01)
ht.set_password("user5", "pass5")
ht.delete("user3")
@@ -166,14 +197,23 @@ class HtpasswdFileTest(TestCase):
"user3"])
def test_04_check_password(self):
"test check_password()"
ht = apache.HtpasswdFile.from_string(self.sample_01)
self.assertRaises(TypeError, ht.check_password, 1, 'pass5')
self.assertTrue(ht.check_password("user5","pass5") is None)
for i in irange(1,5):
"""test check_password()"""
ht = apache.HtpasswdFile.from_string(self.sample_05)
self.assertRaises(TypeError, ht.check_password, 1, 'pass9')
self.assertTrue(ht.check_password("user9","pass9") is None)
# users 1..6 of sample_01 run through all the main hash formats,
# to make sure they're recognized.
for i in irange(1, 7):
i = str(i)
self.assertTrue(ht.check_password("user"+i, "pass"+i))
self.assertTrue(ht.check_password("user"+i, "pass5") is False)
try:
self.assertTrue(ht.check_password("user"+i, "pass"+i))
self.assertTrue(ht.check_password("user"+i, "pass9") is False)
except MissingBackendError:
if i == "5":
# user5 uses bcrypt, which is apparently not available right now
continue
raise
self.assertRaises(ValueError, ht.check_password, "user:", "pass")
@@ -183,7 +223,7 @@ class HtpasswdFileTest(TestCase):
self.assertFalse(ht.verify("user1", "pass2"))
def test_05_load(self):
"test load()"
"""test load()"""
# setup empty file
path = self.mktemp()
set_file(path, "")
@@ -220,7 +260,7 @@ class HtpasswdFileTest(TestCase):
# NOTE: load_string() tested via from_string(), which is used all over this file
def test_06_save(self):
"test save()"
"""test save()"""
# load from file
path = self.mktemp()
set_file(path, self.sample_01)
@@ -242,7 +282,7 @@ class HtpasswdFileTest(TestCase):
self.assertEqual(get_file(path), b("user1:pass1\n"))
def test_07_encodings(self):
"test 'encoding' kwd"
"""test 'encoding' kwd"""
# test bad encodings cause failure in constructor
self.assertRaises(ValueError, apache.HtpasswdFile, encoding="utf-16")
@@ -262,7 +302,7 @@ class HtpasswdFileTest(TestCase):
self.assertEqual(ht.users(), [ u("user\u00e6") ])
def test_08_get_hash(self):
"test get_hash()"
"""test get_hash()"""
ht = apache.HtpasswdFile.from_string(self.sample_01)
self.assertEqual(ht.get_hash("user3"), b("{SHA}3ipNV1GrBtxPmHFC21fCbVCSXIo="))
self.assertEqual(ht.get_hash("user4"), b("pass4"))
@@ -272,7 +312,7 @@ class HtpasswdFileTest(TestCase):
self.assertEqual(ht.find("user4"), b("pass4"))
def test_09_to_string(self):
"test to_string"
"""test to_string"""
# check with known sample
ht = apache.HtpasswdFile.from_string(self.sample_01)
@@ -305,7 +345,7 @@ class HtpasswdFileTest(TestCase):
# htdigest
#=============================================================================
class HtdigestFileTest(TestCase):
"test HtdigestFile class"
"""test HtdigestFile class"""
descriptionPrefix = "HtdigestFile"
# sample with 4 users
@@ -330,7 +370,7 @@ class HtdigestFileTest(TestCase):
sample_04_latin1 = b('user\xe6:realm\xe6:549d2a5f4659ab39a80dac99e159ab19\n')
def test_00_constructor_autoload(self):
"test constructor autoload"
"""test constructor autoload"""
# check with existing file
path = self.mktemp()
set_file(path, self.sample_01)
@@ -348,7 +388,7 @@ class HtdigestFileTest(TestCase):
# NOTE: default_realm option checked via other tests.
def test_01_delete(self):
"test delete()"
"""test delete()"""
ht = apache.HtdigestFile.from_string(self.sample_01)
self.assertTrue(ht.delete("user1", "realm"))
self.assertTrue(ht.delete("user2", "realm"))
@@ -377,7 +417,7 @@ class HtdigestFileTest(TestCase):
self.assertEqual(get_file(path), self.sample_02)
def test_02_set_password(self):
"test update()"
"""test update()"""
ht = apache.HtdigestFile.from_string(self.sample_01)
self.assertTrue(ht.set_password("user2", "realm", "pass2x"))
self.assertFalse(ht.set_password("user5", "realm", "pass5"))
@@ -405,7 +445,7 @@ class HtdigestFileTest(TestCase):
# TODO: test set_password autosave
def test_03_users(self):
"test users()"
"""test users()"""
ht = apache.HtdigestFile.from_string(self.sample_01)
ht.set_password("user5", "realm", "pass5")
ht.delete("user3", "realm")
@@ -415,7 +455,7 @@ class HtdigestFileTest(TestCase):
self.assertRaises(TypeError, ht.users, 1)
def test_04_check_password(self):
"test check_password()"
"""test check_password()"""
ht = apache.HtdigestFile.from_string(self.sample_01)
self.assertRaises(TypeError, ht.check_password, 1, 'realm', 'pass5')
self.assertRaises(TypeError, ht.check_password, 'user', 1, 'pass5')
@@ -440,7 +480,7 @@ class HtdigestFileTest(TestCase):
self.assertRaises(ValueError, ht.check_password, "user:", "realm", "pass")
def test_05_load(self):
"test load()"
"""test load()"""
# setup empty file
path = self.mktemp()
set_file(path, "")
@@ -481,7 +521,7 @@ class HtdigestFileTest(TestCase):
self.assertEqual(ha.to_string(), b(""))
def test_06_save(self):
"test save()"
"""test save()"""
# load from file
path = self.mktemp()
set_file(path, self.sample_01)
@@ -503,7 +543,7 @@ class HtdigestFileTest(TestCase):
self.assertEqual(get_file(path), hb.to_string())
def test_07_realms(self):
"test realms() & delete_realm()"
"""test realms() & delete_realm()"""
ht = apache.HtdigestFile.from_string(self.sample_01)
self.assertEqual(ht.delete_realm("x"), 0)
@@ -514,7 +554,7 @@ class HtdigestFileTest(TestCase):
self.assertEqual(ht.to_string(), b(""))
def test_08_get_hash(self):
"test get_hash()"
"""test get_hash()"""
ht = apache.HtdigestFile.from_string(self.sample_01)
self.assertEqual(ht.get_hash("user3", "realm"), "a500bb8c02f6a9170ae46af10c898744")
self.assertEqual(ht.get_hash("user4", "realm"), "ab7b5d5f28ccc7666315f508c7358519")
@@ -524,7 +564,7 @@ class HtdigestFileTest(TestCase):
self.assertEqual(ht.find("user4", "realm"), "ab7b5d5f28ccc7666315f508c7358519")
def test_09_encodings(self):
"test encoding parameter"
"""test encoding parameter"""
# test bad encodings cause failure in constructor
self.assertRaises(ValueError, apache.HtdigestFile, encoding="utf-16")
@@ -539,7 +579,7 @@ class HtdigestFileTest(TestCase):
self.assertEqual(ht.users(u("realm\u00e6")), [ u("user\u00e6") ])
def test_10_to_string(self):
"test to_string()"
"""test to_string()"""
# check sample
ht = apache.HtdigestFile.from_string(self.sample_01)

View File

@@ -15,7 +15,7 @@ from passlib.tests.utils import TestCase
# test predefined app contexts
#=============================================================================
class AppsTest(TestCase):
"perform general tests to make sure contexts work"
"""perform general tests to make sure contexts work"""
# NOTE: these tests are not really comprehensive,
# since they would do little but duplicate
# the presets in apps.py

View File

@@ -178,7 +178,7 @@ sha512_crypt__min_rounds = 45000
# constructors
#===================================================================
def test_01_constructor(self):
"test class constructor"
"""test class constructor"""
# test blank constructor works correctly
ctx = CryptContext()
@@ -200,8 +200,12 @@ sha512_crypt__min_rounds = 45000
ctx = CryptContext(**self.sample_3_dict)
self.assertEqual(ctx.to_dict(), self.sample_3_dict)
# test unicode scheme names (issue 54)
ctx = CryptContext(schemes=[u("sha256_crypt")])
self.assertEqual(ctx.schemes(), ("sha256_crypt",))
def test_02_from_string(self):
"test from_string() constructor"
"""test from_string() constructor"""
# test sample 1 unicode
ctx = CryptContext.from_string(self.sample_1_unicode)
self.assertEqual(ctx.to_dict(), self.sample_1_dict)
@@ -231,7 +235,7 @@ sha512_crypt__min_rounds = 45000
self.sample_1_unicode, section="fakesection")
def test_03_from_path(self):
"test from_path() constructor"
"""test from_path() constructor"""
# make sure sample files exist
if not os.path.exists(self.sample_1_path):
raise RuntimeError("can't find data file: %r" % self.sample_1_path)
@@ -258,7 +262,7 @@ sha512_crypt__min_rounds = 45000
self.sample_1_path, section="fakesection")
def test_04_copy(self):
"test copy() method"
"""test copy() method"""
cc1 = CryptContext(**self.sample_1_dict)
# overlay sample 2 onto copy
@@ -287,7 +291,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(cc4.to_dict(), self.sample_12_dict)
def test_09_repr(self):
"test repr()"
"""test repr()"""
cc1 = CryptContext(**self.sample_1_dict)
self.assertRegex(repr(cc1), "^<CryptContext at 0x[0-9a-f]+>$")
@@ -295,9 +299,9 @@ sha512_crypt__min_rounds = 45000
# modifiers
#===================================================================
def test_10_load(self):
"test load() / load_path() method"
"""test load() / load_path() method"""
# NOTE: load() is the workhorse that handles all policy parsing,
# compilation, and validation. most of it's features are tested
# compilation, and validation. most of its features are tested
# elsewhere, since all the constructors and modifiers are just
# wrappers for it.
@@ -338,7 +342,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(ctx.to_dict(), self.sample_2_dict)
def test_11_load_rollback(self):
"test load() errors restore old state"
"""test load() errors restore old state"""
# create initial context
cc = CryptContext(["des_crypt", "sha256_crypt"],
sha256_crypt__default_rounds=5000,
@@ -362,7 +366,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(cc.to_string(), result)
def test_12_update(self):
"test update() method"
"""test update() method"""
# empty overlay
ctx = CryptContext(**self.sample_1_dict)
@@ -399,7 +403,7 @@ sha512_crypt__min_rounds = 45000
# option parsing
#===================================================================
def test_20_options(self):
"test basic option parsing"
"""test basic option parsing"""
def parse(**kwds):
return CryptContext(**kwds).to_dict()
@@ -475,7 +479,7 @@ sha512_crypt__min_rounds = 45000
all__salt="xx")
def test_21_schemes(self):
"test 'schemes' context option parsing"
"""test 'schemes' context option parsing"""
# schemes can be empty
cc = CryptContext(schemes=None)
@@ -511,7 +515,7 @@ sha512_crypt__min_rounds = 45000
admin__context__schemes=["md5_crypt"])
def test_22_deprecated(self):
"test 'deprecated' context option parsing"
"""test 'deprecated' context option parsing"""
def getdep(ctx, category=None):
return [name for name in ctx.schemes()
if ctx._is_deprecated_scheme(name, category)]
@@ -603,7 +607,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(getdep(cc, "admin"), [])
def test_23_default(self):
"test 'default' context option parsing"
"""test 'default' context option parsing"""
# anything allowed if no schemes
self.assertEqual(CryptContext(default="md5_crypt").to_dict(),
@@ -640,7 +644,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(ctx.default_scheme("admin"), "md5_crypt")
def test_24_vary_rounds(self):
"test 'vary_rounds' hash option parsing"
"""test 'vary_rounds' hash option parsing"""
def parse(v):
return CryptContext(all__vary_rounds=v).to_dict()['all__vary_rounds']
@@ -659,7 +663,7 @@ sha512_crypt__min_rounds = 45000
# inspection & serialization
#===================================================================
def test_30_schemes(self):
"test schemes() method"
"""test schemes() method"""
# NOTE: also checked under test_21
# test empty
@@ -677,7 +681,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(ctx.schemes(), ())
def test_31_default_scheme(self):
"test default_scheme() method"
"""test default_scheme() method"""
# NOTE: also checked under test_23
# test empty
@@ -700,7 +704,7 @@ sha512_crypt__min_rounds = 45000
# categories tested under test_23
def test_32_handler(self):
"test handler() method"
"""test handler() method"""
# default for empty
ctx = CryptContext()
@@ -729,7 +733,7 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(ctx.handler(category=u("admin")), hash.md5_crypt)
def test_33_options(self):
"test internal _get_record_options() method"
"""test internal _get_record_options() method"""
def options(ctx, scheme, category=None):
return ctx._config._get_record_options_with_flag(scheme, category)[0]
@@ -804,14 +808,14 @@ sha512_crypt__min_rounds = 45000
))
def test_34_to_dict(self):
"test to_dict() method"
"""test to_dict() method"""
# NOTE: this is tested all throughout this test case.
ctx = CryptContext(**self.sample_1_dict)
self.assertEqual(ctx.to_dict(), self.sample_1_dict)
self.assertEqual(ctx.to_dict(resolve=True), self.sample_1_resolved_dict)
def test_35_to_string(self):
"test to_string() method"
"""test to_string() method"""
# create ctx and serialize
ctx = CryptContext(**self.sample_1_dict)
@@ -834,7 +838,6 @@ sha512_crypt__min_rounds = 45000
self.assertEqual(other, dump.replace("[passlib]","[password-security]"))
# test unmanaged handler warning
from passlib import hash
from passlib.tests.test_utils_handlers import UnsaltedHash
ctx3 = CryptContext([UnsaltedHash, "md5_crypt"])
dump = ctx3.to_string()
@@ -852,7 +855,7 @@ sha512_crypt__min_rounds = 45000
]
def test_40_basic(self):
"test basic encrypt/identify/verify functionality"
"""test basic encrypt/identify/verify functionality"""
handlers = [hash.md5_crypt, hash.des_crypt, hash.bsdi_crypt]
cc = CryptContext(handlers, bsdi_crypt__default_rounds=5)
@@ -878,7 +881,7 @@ sha512_crypt__min_rounds = 45000
self.assertRaises(ValueError, cc.genhash, 'secret', cc.genconfig(), scheme="des_crypt")
def test_41_genconfig(self):
"test genconfig() method"
"""test genconfig() method"""
cc = CryptContext(schemes=["md5_crypt", "phpass"],
phpass__ident="H",
phpass__default_rounds=7,
@@ -927,7 +930,7 @@ sha512_crypt__min_rounds = 45000
def test_42_genhash(self):
"test genhash() method"
"""test genhash() method"""
#--------------------------------------------------------------
# border cases
@@ -960,7 +963,7 @@ sha512_crypt__min_rounds = 45000
def test_43_encrypt(self):
"test encrypt() method"
"""test encrypt() method"""
cc = CryptContext(**self.sample_4_dict)
# hash specific settings
@@ -1015,7 +1018,7 @@ sha512_crypt__min_rounds = 45000
def test_44_identify(self):
"test identify() border cases"
"""test identify() border cases"""
handlers = ["md5_crypt", "des_crypt", "bsdi_crypt"]
cc = CryptContext(handlers, bsdi_crypt__default_rounds=5)
@@ -1041,7 +1044,7 @@ sha512_crypt__min_rounds = 45000
self.assertRaises(TypeError, cc.identify, None, category=1)
def test_45_verify(self):
"test verify() scheme kwd"
"""test verify() scheme kwd"""
handlers = ["md5_crypt", "des_crypt", "bsdi_crypt"]
cc = CryptContext(handlers, bsdi_crypt__default_rounds=5)
@@ -1087,7 +1090,7 @@ sha512_crypt__min_rounds = 45000
self.assertRaises(TypeError, cc.verify, 'secret', refhash, category=1)
def test_46_needs_update(self):
"test needs_update() method"
"""test needs_update() method"""
cc = CryptContext(**self.sample_4_dict)
# check deprecated scheme
@@ -1167,7 +1170,7 @@ sha512_crypt__min_rounds = 45000
self.assertRaises(TypeError, cc.needs_update, refhash, category=1)
def test_47_verify_and_update(self):
"test verify_and_update()"
"""test verify_and_update()"""
cc = CryptContext(**self.sample_4_dict)
# create some hashes
@@ -1227,7 +1230,7 @@ sha512_crypt__min_rounds = 45000
# genconfig(). it's assumed encrypt() takes the same codepath.
def test_50_rounds_limits(self):
"test rounds limits"
"""test rounds limits"""
cc = CryptContext(schemes=["sha256_crypt"],
all__min_rounds=2000,
all__max_rounds=3000,
@@ -1344,7 +1347,7 @@ sha512_crypt__min_rounds = 45000
self.assertRaises(TypeError, CryptContext, "sha256_crypt", all__default_rounds=bad)
def test_51_linear_vary_rounds(self):
"test linear vary rounds"
"""test linear vary rounds"""
cc = CryptContext(schemes=["sha256_crypt"],
all__min_rounds=1995,
all__max_rounds=2005,
@@ -1376,7 +1379,7 @@ sha512_crypt__min_rounds = 45000
self.assert_rounds_range(c2, "sha256_crypt", 1995, 2005)
def test_52_log2_vary_rounds(self):
"test log2 vary rounds"
"""test log2 vary rounds"""
cc = CryptContext(schemes=["bcrypt"],
all__min_rounds=15,
all__max_rounds=25,
@@ -1415,7 +1418,7 @@ sha512_crypt__min_rounds = 45000
self.assert_rounds_range(c2, "bcrypt", 15, 21)
def assert_rounds_range(self, context, scheme, lower, upper):
"helper to check vary_rounds covers specified range"
"""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.
handler = context.handler(scheme)
@@ -1432,7 +1435,7 @@ sha512_crypt__min_rounds = 45000
# feature tests
#===================================================================
def test_60_min_verify_time(self):
"test verify() honors min_verify_time"
"""test verify() honors min_verify_time"""
delta = .05
if TICK_RESOLUTION >= delta/10:
raise self.skipTest("timer not accurate enough")
@@ -1441,7 +1444,7 @@ sha512_crypt__min_rounds = 45000
max_delay = 8*delta
class TimedHash(uh.StaticHandler):
"psuedo hash that takes specified amount of time"
"""psuedo hash that takes specified amount of time"""
name = "timed_hash"
delay = 0
@@ -1498,7 +1501,7 @@ sha512_crypt__min_rounds = 45000
self.assertRaises(ValueError, CryptContext, min_verify_time=-1)
def test_61_autodeprecate(self):
"test deprecated='auto' is handled correctly"
"""test deprecated='auto' is handled correctly"""
def getstate(ctx, category=None):
return [ctx._is_deprecated_scheme(scheme, category) for scheme in ctx.schemes()]
@@ -1533,7 +1536,7 @@ sha512_crypt__min_rounds = 45000
# handler deprecation detectors
#===================================================================
def test_62_bcrypt_update(self):
"test verify_and_update / needs_update corrects bcrypt padding"
"""test verify_and_update / needs_update corrects bcrypt padding"""
# see issue 25.
bcrypt = hash.bcrypt
@@ -1554,7 +1557,7 @@ sha512_crypt__min_rounds = 45000
self.assertTrue(new_hash and new_hash != BAD1)
def test_63_bsdi_crypt_update(self):
"test verify_and_update / needs_update corrects bsdi even rounds"
"""test verify_and_update / needs_update corrects bsdi even rounds"""
even_hash = '_Y/../cG0zkJa6LY6k4c'
odd_hash = '_Z/..TgFg0/ptQtpAgws'
secret = 'test'
@@ -1588,7 +1591,7 @@ class LazyCryptContextTest(TestCase):
self.addCleanup(unload_handler_name, "dummy_2")
def test_kwd_constructor(self):
"test plain kwds"
"""test plain kwds"""
self.assertFalse(has_crypt_handler("dummy_2"))
register_crypt_handler_path("dummy_2", "passlib.tests.test_context")

View File

@@ -41,7 +41,7 @@ log = getLogger(__name__)
#
#=============================================================================
class CryptPolicyTest(TestCase):
"test CryptPolicy object"
"""test CryptPolicy object"""
# TODO: need to test user categories w/in all this
@@ -220,7 +220,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
r"the method.*hash_needs_update.*is deprecated")
def test_00_constructor(self):
"test CryptPolicy() constructor"
"""test CryptPolicy() constructor"""
policy = CryptPolicy(**self.sample_config_1pd)
self.assertEqual(policy.to_dict(), self.sample_config_1pd)
@@ -260,7 +260,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
default='md5_crypt')
def test_01_from_path_simple(self):
"test CryptPolicy.from_path() constructor"
"""test CryptPolicy.from_path() constructor"""
# NOTE: this is separate so it can also run under GAE
# test preset stored in existing file
@@ -272,7 +272,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertRaises(EnvironmentError, CryptPolicy.from_path, path + 'xxx')
def test_01_from_path(self):
"test CryptPolicy.from_path() constructor with encodings"
"""test CryptPolicy.from_path() constructor with encodings"""
path = self.mktemp()
# test "\n" linesep
@@ -292,7 +292,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertEqual(policy.to_dict(), self.sample_config_1pd)
def test_02_from_string(self):
"test CryptPolicy.from_string() constructor"
"""test CryptPolicy.from_string() constructor"""
# test "\n" linesep
policy = CryptPolicy.from_string(self.sample_config_1s)
self.assertEqual(policy.to_dict(), self.sample_config_1pd)
@@ -317,7 +317,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertEqual(policy.to_dict(), self.sample_config_4pd)
def test_03_from_source(self):
"test CryptPolicy.from_source() constructor"
"""test CryptPolicy.from_source() constructor"""
# pass it a path
policy = CryptPolicy.from_source(self.sample_config_1s_path)
self.assertEqual(policy.to_dict(), self.sample_config_1pd)
@@ -339,7 +339,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertRaises(TypeError, CryptPolicy.from_source, [])
def test_04_from_sources(self):
"test CryptPolicy.from_sources() constructor"
"""test CryptPolicy.from_sources() constructor"""
# pass it empty list
self.assertRaises(ValueError, CryptPolicy.from_sources, [])
@@ -358,7 +358,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertEqual(policy.to_dict(), self.sample_config_123pd)
def test_05_replace(self):
"test CryptPolicy.replace() constructor"
"""test CryptPolicy.replace() constructor"""
p1 = CryptPolicy(**self.sample_config_1pd)
@@ -375,7 +375,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertEqual(p3.to_dict(), self.sample_config_123pd)
def test_06_forbidden(self):
"test CryptPolicy() forbidden kwds"
"""test CryptPolicy() forbidden kwds"""
# salt not allowed to be set
self.assertRaises(KeyError, CryptPolicy,
@@ -397,7 +397,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
# reading
#===================================================================
def test_10_has_schemes(self):
"test has_schemes() method"
"""test has_schemes() method"""
p1 = CryptPolicy(**self.sample_config_1pd)
self.assertTrue(p1.has_schemes())
@@ -406,7 +406,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertTrue(not p3.has_schemes())
def test_11_iter_handlers(self):
"test iter_handlers() method"
"""test iter_handlers() method"""
p1 = CryptPolicy(**self.sample_config_1pd)
s = self.sample_config_1prd['schemes']
@@ -416,7 +416,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertEqual(list(p3.iter_handlers()), [])
def test_12_get_handler(self):
"test get_handler() method"
"""test get_handler() method"""
p1 = CryptPolicy(**self.sample_config_1pd)
@@ -431,7 +431,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertIs(p1.get_handler(), hash.md5_crypt)
def test_13_get_options(self):
"test get_options() method"
"""test get_options() method"""
p12 = CryptPolicy(**self.sample_config_12pd)
@@ -470,7 +470,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
))
def test_14_handler_is_deprecated(self):
"test handler_is_deprecated() method"
"""test handler_is_deprecated() method"""
pa = CryptPolicy(**self.sample_config_1pd)
pb = CryptPolicy(**self.sample_config_5pd)
@@ -500,7 +500,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
self.assertTrue(pc.handler_is_deprecated("des_crypt", "user"))
def test_15_min_verify_time(self):
"test get_min_verify_time() method"
"""test get_min_verify_time() method"""
# silence deprecation warnings for min verify time
warnings.filterwarnings("ignore", category=DeprecationWarning)
@@ -524,20 +524,20 @@ admin__context__deprecated = des_crypt, bsdi_crypt
# serialization
#===================================================================
def test_20_iter_config(self):
"test iter_config() method"
"""test iter_config() method"""
p5 = CryptPolicy(**self.sample_config_5pd)
self.assertEqual(dict(p5.iter_config()), self.sample_config_5pd)
self.assertEqual(dict(p5.iter_config(resolve=True)), self.sample_config_5prd)
self.assertEqual(dict(p5.iter_config(ini=True)), self.sample_config_5pid)
def test_21_to_dict(self):
"test to_dict() method"
"""test to_dict() method"""
p5 = CryptPolicy(**self.sample_config_5pd)
self.assertEqual(p5.to_dict(), self.sample_config_5pd)
self.assertEqual(p5.to_dict(resolve=True), self.sample_config_5prd)
def test_22_to_string(self):
"test to_string() method"
"""test to_string() method"""
pa = CryptPolicy(**self.sample_config_5pd)
s = pa.to_string() # NOTE: can't compare string directly, ordering etc may not match
pb = CryptPolicy.from_string(s)
@@ -554,7 +554,7 @@ admin__context__deprecated = des_crypt, bsdi_crypt
# CryptContext
#=============================================================================
class CryptContextTest(TestCase):
"test CryptContext class"
"""test CryptContext class"""
descriptionPrefix = "CryptContext"
def setUp(self):
@@ -571,7 +571,7 @@ class CryptContextTest(TestCase):
# constructor
#===================================================================
def test_00_constructor(self):
"test constructor"
"""test constructor"""
# create crypt context using handlers
cc = CryptContext([hash.md5_crypt, hash.bsdi_crypt, hash.des_crypt])
c,b,a = cc.policy.iter_handlers()
@@ -600,7 +600,7 @@ class CryptContextTest(TestCase):
self.assertRaises(TypeError, CryptContext, policy='x')
def test_01_replace(self):
"test replace()"
"""test replace()"""
cc = CryptContext(["md5_crypt", "bsdi_crypt", "des_crypt"])
self.assertIs(cc.policy.get_handler(), hash.md5_crypt)
@@ -617,7 +617,7 @@ class CryptContextTest(TestCase):
self.assertIs(cc3.policy.get_handler(), hash.bsdi_crypt)
def test_02_no_handlers(self):
"test no handlers"
"""test no handlers"""
# check constructor...
cc = CryptContext()
@@ -653,7 +653,7 @@ class CryptContextTest(TestCase):
)
def test_12_hash_needs_update(self):
"test hash_needs_update() method"
"""test hash_needs_update() method"""
cc = CryptContext(**self.sample_policy_1)
# check deprecated scheme
@@ -672,7 +672,7 @@ class CryptContextTest(TestCase):
# border cases
#===================================================================
def test_30_nonstring_hash(self):
"test non-string hash values cause error"
"""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.
@@ -716,7 +716,7 @@ class LazyCryptContextTest(TestCase):
warnings.filterwarnings("ignore", ".*(CryptPolicy|context\.policy).*(has|have) been deprecated.*")
def test_kwd_constructor(self):
"test plain kwds"
"""test plain kwds"""
self.assertFalse(has_crypt_handler("dummy_2"))
register_crypt_handler_path("dummy_2", "passlib.tests.test_context")
@@ -730,7 +730,7 @@ class LazyCryptContextTest(TestCase):
self.assertTrue(has_crypt_handler("dummy_2", True))
def test_callable_constructor(self):
"test create_policy() hook, returning CryptPolicy"
"""test create_policy() hook, returning CryptPolicy"""
self.assertFalse(has_crypt_handler("dummy_2"))
register_crypt_handler_path("dummy_2", "passlib.tests.test_context")

View File

@@ -2,7 +2,10 @@
#=============================================================================
# imports
#=============================================================================
# NOTE: double __future__ is workaround for py2.5.0 bug,
# per https://bitbucket.org/ecollins/passlib/issues/58#comment-20589295
from __future__ import with_statement
from __future__ import absolute_import
# core
import logging; log = logging.getLogger(__name__)
import sys
@@ -76,7 +79,7 @@ if has_django:
from django.contrib.auth.models import User
class FakeUser(User):
"mock user object for use in testing"
"""mock user object for use in testing"""
# NOTE: this mainly just overrides .save() to test commit behavior.
@memoized_property
@@ -109,7 +112,27 @@ def create_mock_setter():
# work up stock django config
#=============================================================================
sample_hashes = {} # override sample hashes used in test cases
if DJANGO_VERSION >= (1,6):
if DJANGO_VERSION >= (1,8):
stock_config = django16_context.to_dict()
stock_config.update(
deprecated="auto",
django_pbkdf2_sha1__default_rounds=20000,
django_pbkdf2_sha256__default_rounds=20000,
)
sample_hashes.update(
django_pbkdf2_sha256=("not a password", "pbkdf2_sha256$20000$arJ31mmmlSmO$XNBTUKe4UCUGPeHTmXpYjaKmJaDGAsevd0LWvBtzP18="),
)
elif DJANGO_VERSION >= (1,7):
stock_config = django16_context.to_dict()
stock_config.update(
deprecated="auto",
django_pbkdf2_sha1__default_rounds=15000,
django_pbkdf2_sha256__default_rounds=15000,
)
sample_hashes.update(
django_pbkdf2_sha256=("not a password", "pbkdf2_sha256$15000$xb2YnidpItz1$uHvLChIjUDc5HVUfQnE6lDMbgkTAiSYknGCtjuX4AVo="),
)
elif DJANGO_VERSION >= (1,6):
stock_config = django16_context.to_dict()
stock_config.update(
deprecated="auto",
@@ -139,7 +162,7 @@ else:
# test utils
#=============================================================================
class _ExtensionSupport(object):
"support funcs for loading/unloading extension"
"""support funcs for loading/unloading extension"""
#===================================================================
# support funcs
#===================================================================
@@ -182,7 +205,7 @@ class _ExtensionSupport(object):
# verify current patch state
#===================================================================
def assert_unpatched(self):
"test that django is in unpatched state"
"""test that django is in unpatched state"""
# make sure we aren't currently patched
mod = sys.modules.get("passlib.ext.django.models")
self.assertFalse(mod and mod._patched, "patch should not be enabled")
@@ -199,7 +222,7 @@ class _ExtensionSupport(object):
(obj, attr, source))
def assert_patched(self, context=None):
"helper to ensure django HAS been patched, and is using specified config"
"""helper to ensure django HAS been patched, and is using specified config"""
# make sure we're currently patched
mod = sys.modules.get("passlib.ext.django.models")
self.assertTrue(mod and mod._patched, "patch should have been enabled")
@@ -225,9 +248,8 @@ class _ExtensionSupport(object):
# load / unload the extension (and verify it worked)
#===================================================================
_config_keys = ["PASSLIB_CONFIG", "PASSLIB_CONTEXT", "PASSLIB_GET_CATEGORY"]
def load_extension(self, check=True, **kwds):
"helper to load extension with specified config & patch django"
"""helper to load extension with specified config & patch django"""
self.unload_extension()
if check:
config = kwds.get("PASSLIB_CONFIG") or kwds.get("PASSLIB_CONTEXT")
@@ -239,7 +261,7 @@ class _ExtensionSupport(object):
self.assert_patched(context=config)
def unload_extension(self):
"helper to remove patches and unload extension"
"""helper to remove patches and unload extension"""
# remove patches and unload module
mod = sys.modules.get("passlib.ext.django.models")
if mod:
@@ -275,7 +297,7 @@ class _ExtensionTest(TestCase, _ExtensionSupport):
# extension tests
#=============================================================================
class DjangoBehaviorTest(_ExtensionTest):
"tests model to verify it matches django's behavior"
"""tests model to verify it matches django's behavior"""
descriptionPrefix = "verify django behavior"
patched = False
config = stock_config
@@ -593,7 +615,9 @@ class DjangoBehaviorTest(_ExtensionTest):
if testcase.is_disabled_handler:
continue
if not has_active_backend(handler):
assert scheme == "django_bcrypt"
# TODO: move this above get_handler_case(),
# and omit MissingBackendError check.
assert scheme in ["django_bcrypt", "django_bcrypt_sha256"], "%r scheme should always have active backend" % scheme
continue
try:
secret, hash = sample_hashes[scheme]
@@ -680,7 +704,7 @@ class DjangoBehaviorTest(_ExtensionTest):
self.assertEqual(name, scheme)
class ExtensionBehaviorTest(DjangoBehaviorTest):
"test model to verify passlib.ext.django conforms to it"
"""test model to verify passlib.ext.django conforms to it"""
descriptionPrefix = "verify extension behavior"
patched = True
config = dict(
@@ -700,7 +724,7 @@ class DjangoExtensionTest(_ExtensionTest):
# monkeypatch testing
#===================================================================
def test_00_patch_control(self):
"test set_django_password_context patch/unpatch"
"""test set_django_password_context patch/unpatch"""
# check config="disabled"
self.load_extension(PASSLIB_CONFIG="disabled", check=False)
@@ -726,7 +750,7 @@ class DjangoExtensionTest(_ExtensionTest):
self.unload_extension()
def test_01_overwrite_detection(self):
"test detection of foreign monkeypatching"
"""test detection of foreign monkeypatching"""
# NOTE: this sets things up, and spot checks two methods,
# this should be enough to verify patch manager is working.
# TODO: test unpatch behavior honors flag.
@@ -756,7 +780,7 @@ class DjangoExtensionTest(_ExtensionTest):
models.check_password = orig
def test_02_handler_wrapper(self):
"test Hasher-compatible handler wrappers"
"""test Hasher-compatible handler wrappers"""
if not has_django14:
raise self.skipTest("Django >= 1.4 not installed")
from passlib.ext.django.utils import get_passlib_hasher
@@ -795,7 +819,7 @@ class DjangoExtensionTest(_ExtensionTest):
# PASSLIB_CONFIG settings
#===================================================================
def test_11_config_disabled(self):
"test PASSLIB_CONFIG='disabled'"
"""test PASSLIB_CONFIG='disabled'"""
# test config=None (deprecated)
with self.assertWarningList("PASSLIB_CONFIG=None is deprecated"):
self.load_extension(PASSLIB_CONFIG=None, check=False)
@@ -806,7 +830,7 @@ class DjangoExtensionTest(_ExtensionTest):
self.assert_unpatched()
def test_12_config_presets(self):
"test PASSLIB_CONFIG='<preset>'"
"""test PASSLIB_CONFIG='<preset>'"""
# test django presets
self.load_extension(PASSLIB_CONTEXT="django-default", check=False)
if DJANGO_VERSION >= (1,6):
@@ -824,7 +848,7 @@ class DjangoExtensionTest(_ExtensionTest):
self.assert_patched(django14_context)
def test_13_config_defaults(self):
"test PASSLIB_CONFIG default behavior"
"""test PASSLIB_CONFIG default behavior"""
# check implicit default
from passlib.ext.django.utils import PASSLIB_DEFAULT
default = CryptContext.from_string(PASSLIB_DEFAULT)
@@ -840,7 +864,7 @@ class DjangoExtensionTest(_ExtensionTest):
self.assert_patched(PASSLIB_DEFAULT)
def test_14_config_invalid(self):
"test PASSLIB_CONFIG type checks"
"""test PASSLIB_CONFIG type checks"""
update_settings(PASSLIB_CONTEXT=123, PASSLIB_CONFIG=UNSET)
self.assertRaises(TypeError, __import__, 'passlib.ext.django.models')
@@ -852,7 +876,7 @@ class DjangoExtensionTest(_ExtensionTest):
# PASSLIB_GET_CATEGORY setting
#===================================================================
def test_21_category_setting(self):
"test PASSLIB_GET_CATEGORY parameter"
"""test PASSLIB_GET_CATEGORY parameter"""
# define config where rounds can be used to detect category
config = dict(
schemes = ["sha256_crypt"],
@@ -863,7 +887,7 @@ class DjangoExtensionTest(_ExtensionTest):
from passlib.hash import sha256_crypt
def run(**kwds):
"helper to take in user opts, return rounds used in password"
"""helper to take in user opts, return rounds used in password"""
user = FakeUser(**kwds)
user.set_password("stub")
return sha256_crypt.from_string(user.password).rounds
@@ -920,14 +944,42 @@ class ContextWithHook(CryptContext):
# hack up the some of the real django tests to run w/ extension loaded,
# to ensure we mimic their behavior.
if has_django14:
from passlib.tests.utils import patchAttr
if DJANGO_VERSION >= (1,6):
from django.contrib.auth.tests import test_hashers as _thmod
# however, the django tests were moved out of the package, and into a source-only location
# as of django 1.7. so we disable tests from that point on unless test-runner specifies
test_hashers_mod = None
hashers_skip_msg = None
if TEST_MODE(max="quick"):
hashers_skip_msg = "requires >= 'default' test mode"
elif DJANGO_VERSION >= (1, 7):
import os
import sys
source_path = os.environ.get("PASSLIB_TESTS_DJANGO_SOURCE_PATH")
if source_path:
if not os.path.exists(source_path):
raise EnvironmentError("django source path not found: %r" % source_path)
if not all(os.path.exists(os.path.join(source_path, name))
for name in ["django", "tests"]):
raise EnvironmentError("invalid django source path: %r" % source_path)
log.info("using django tests from source path: %r", source_path)
tests_path = os.path.join(source_path, "tests")
sys.path.insert(0, tests_path)
from auth_tests import test_hashers as test_hashers_mod
sys.path.remove(tests_path)
else:
from django.contrib.auth.tests import hashers as _thmod
hashers_skip_msg = "requires PASSLIB_TESTS_DJANGO_SOURCE_PATH to be set for django 1.7+"
elif DJANGO_VERSION >= (1, 6):
from django.contrib.auth.tests import test_hashers as test_hashers_mod
elif DJANGO_VERSION >= (1, 4):
from django.contrib.auth.tests import hashers as test_hashers_mod
else:
hashers_skip_msg = "requires django 1.4+ to be present"
class HashersTest(_thmod.TestUtilsHashPass, _ExtensionSupport):
# hack up the some of the real django tests to run w/ extension loaded,
# to ensure we mimic their behavior.
if test_hashers_mod:
from passlib.tests.utils import patchAttr
class HashersTest(test_hashers_mod.TestUtilsHashPass, _ExtensionSupport):
"""run django's hasher unittests against passlib's extension
and workalike implementations"""
def setUp(self):
@@ -942,17 +994,17 @@ if has_django14:
"check_password",
"identify_hasher",
"get_hasher"]:
patchAttr(self, _thmod, attr, getattr(hashers, attr))
patchAttr(self, test_hashers_mod, attr, getattr(hashers, attr))
# django 1.5 tests expect empty django_des_crypt salt field
if DJANGO_VERSION > (1,4):
# django 1.4 tests expect empty django_des_crypt salt field
if DJANGO_VERSION >= (1,4):
from passlib.hash import django_des_crypt
patchAttr(self, django_des_crypt, "use_duplicate_salt", False)
# hack: need password_context to keep up to date with hasher.iterations
if DJANGO_VERSION >= (1,6):
def update_hook(self):
rounds = _thmod.get_hasher("pbkdf2_sha256").iterations
rounds = test_hashers_mod.get_hasher("pbkdf2_sha256").iterations
self.update(
django_pbkdf2_sha256__min_rounds=rounds,
django_pbkdf2_sha256__default_rounds=rounds,
@@ -967,9 +1019,12 @@ if has_django14:
def tearDown(self):
self.unload_extension()
super(HashersTest, self).tearDown()
else:
class HashersTest(TestCase):
HashersTest = skipUnless(TEST_MODE("default"),
"requires >= 'default' test mode")(HashersTest)
def test_external_django_hasher_tests(self):
"""external django hasher tests"""
raise self.skipTest(hashers_skip_msg)
#=============================================================================
# eof

View File

@@ -30,7 +30,7 @@ UPASS_TABLE = u("t\u00e1\u0411\u2113\u0259")
PASS_TABLE_UTF8 = b('t\xc3\xa1\xd0\x91\xe2\x84\x93\xc9\x99') # utf-8
def get_handler_case(scheme):
"return HandlerCase instance for scheme, used by other tests"
"""return HandlerCase instance for scheme, used by other tests"""
from passlib.registry import get_crypt_handler
handler = get_crypt_handler(scheme)
if hasattr(handler, "backends") and not hasattr(handler, "wrapped") and handler.name != "django_bcrypt_sha256":
@@ -122,7 +122,7 @@ class bigcrypt_test(HandlerCase):
# bsdi crypt
#=============================================================================
class _bsdi_crypt_test(HandlerCase):
"test BSDiCrypt algorithm"
"""test BSDiCrypt algorithm"""
handler = hash.bsdi_crypt
known_correct_hashes = [
@@ -291,7 +291,7 @@ class cisco_type7_test(HandlerCase):
]
def test_90_decode(self):
"test cisco_type7.decode()"
"""test cisco_type7.decode()"""
from passlib.utils import to_unicode, to_bytes
handler = self.handler
@@ -305,7 +305,7 @@ class cisco_type7_test(HandlerCase):
'0958EDC8A9F495F6F8A5FD', 'ascii')
def test_91_salt(self):
"test salt value border cases"
"""test salt value border cases"""
handler = self.handler
self.assertRaises(TypeError, handler, salt=None)
handler(salt=None, use_defaults=True)
@@ -348,7 +348,7 @@ class crypt16_test(HandlerCase):
# des crypt
#=============================================================================
class _des_crypt_test(HandlerCase):
"test des-crypt algorithm"
"""test des-crypt algorithm"""
handler = hash.des_crypt
secret_size = 8
@@ -396,7 +396,7 @@ des_crypt_os_crypt_test, des_crypt_builtin_test = \
# fshp
#=============================================================================
class fshp_test(HandlerCase):
"test fshp algorithm"
"""test fshp algorithm"""
handler = hash.fshp
known_correct_hashes = [
@@ -449,7 +449,7 @@ class fshp_test(HandlerCase):
]
def test_90_variant(self):
"test variant keyword"
"""test variant keyword"""
handler = self.handler
kwds = dict(salt=b('a'), rounds=1)
@@ -546,7 +546,7 @@ class htdigest_test(UserHandlerMixin, HandlerCase):
raise self.skipTest("test case doesn't support 'realm' keyword")
def populate_context(self, secret, kwds):
"insert username into kwds"
"""insert username into kwds"""
if isinstance(secret, tuple):
secret, user, realm = secret
else:
@@ -702,7 +702,7 @@ ldap_sha1_crypt_os_crypt_test, = _ldap_sha1_crypt_test.create_backend_cases(["os
class ldap_pbkdf2_test(TestCase):
def test_wrappers(self):
"test ldap pbkdf2 wrappers"
"""test ldap pbkdf2 wrappers"""
self.assertTrue(
hash.ldap_pbkdf2_sha1.verify(
@@ -768,7 +768,7 @@ class lmhash_test(EncodingHandlerMixin, HandlerCase):
]
def test_90_raw(self):
"test lmhash.raw() method"
"""test lmhash.raw() method"""
from binascii import unhexlify
from passlib.utils.compat import str_to_bascii
lmhash = self.handler
@@ -1134,7 +1134,7 @@ class mysql323_test(HandlerCase):
]
def test_90_whitespace(self):
"check whitespace is ignored per spec"
"""check whitespace is ignored per spec"""
h = self.do_encrypt("mypass")
h2 = self.do_encrypt("my pass")
self.assertEqual(h, h2)
@@ -1575,7 +1575,7 @@ class scram_test(HandlerCase):
warnings.filterwarnings("ignore", r"norm_hash_name\(\): unknown hash")
def test_90_algs(self):
"test parsing of 'algs' setting"
"""test parsing of 'algs' setting"""
defaults = dict(salt=b('A')*10, rounds=1000)
def parse(algs, **kwds):
for k in defaults:
@@ -1605,7 +1605,7 @@ class scram_test(HandlerCase):
checksum={"sha-1": b("\x00"*20)})
def test_90_checksums(self):
"test internal parsing of 'checksum' keyword"
"""test internal parsing of 'checksum' keyword"""
# check non-bytes checksum values are rejected
self.assertRaises(TypeError, self.handler, use_defaults=True,
checksum={'sha-1': u('X')*20})
@@ -1617,7 +1617,7 @@ class scram_test(HandlerCase):
# XXX: anything else that's not tested by the other code already?
def test_91_extract_digest_info(self):
"test scram.extract_digest_info()"
"""test scram.extract_digest_info()"""
edi = self.handler.extract_digest_info
# return appropriate value or throw KeyError
@@ -1635,7 +1635,7 @@ class scram_test(HandlerCase):
self.assertRaises(ValueError, edi, c, "ddd")
def test_92_extract_digest_algs(self):
"test scram.extract_digest_algs()"
"""test scram.extract_digest_algs()"""
eda = self.handler.extract_digest_algs
self.assertEqual(eda('$scram$4096$QSXCR.Q6sek8bf92$'
@@ -1653,10 +1653,9 @@ class scram_test(HandlerCase):
["sha-1","sha-256","sha-512"])
def test_93_derive_digest(self):
"test scram.derive_digest()"
"""test scram.derive_digest()"""
# NOTE: this just does a light test, since derive_digest
# is used by encrypt / verify, and is tested pretty well via those.
hash = self.handler.derive_digest
# check various encodings of password work.
@@ -1679,7 +1678,7 @@ class scram_test(HandlerCase):
self.assertRaises(TypeError, hash, "IX", u('\x01'), 1000, 'md5')
def test_94_saslprep(self):
"test encrypt/verify use saslprep"
"""test encrypt/verify use saslprep"""
# NOTE: this just does a light test that saslprep() is being
# called in various places, relying in saslpreps()'s tests
# to verify full normalization behavior.
@@ -1699,7 +1698,7 @@ class scram_test(HandlerCase):
self.assertRaises(ValueError, self.do_verify, u("\uFDD0"), h)
def test_95_context_algs(self):
"test handling of 'algs' in context object"
"""test handling of 'algs' in context object"""
handler = self.handler
from passlib.context import CryptContext
c1 = CryptContext(["scram"], scram__algs="sha1,md5")
@@ -1715,7 +1714,7 @@ class scram_test(HandlerCase):
self.assertTrue(c2.needs_update(h))
def test_96_full_verify(self):
"test verify(full=True) flag"
"""test verify(full=True) flag"""
def vpart(s, h):
return self.handler.verify(s, h)
def vfull(s, h):
@@ -1803,7 +1802,6 @@ sha1_crypt_os_crypt_test, sha1_crypt_builtin_test = \
# NOTE: all roundup hashes use PrefixWrapper,
# so there's nothing natively to test.
# so we just have a few quick cases...
from passlib.handlers import roundup
class RoundupTest(TestCase):
@@ -2125,7 +2123,6 @@ class sun_md5_crypt_test(HandlerCase):
("solaris", True),
("freebsd|openbsd|netbsd|linux|darwin", False),
]
def do_verify(self, secret, hash):
# override to fake error for "$..." hash strings listed in known_config.
# these have to be hash strings, in order to test bare salt issue.
@@ -2162,7 +2159,7 @@ class unix_disabled_test(HandlerCase):
super(unix_disabled_test, self).test_76_hash_border()
def test_90_special(self):
"test marker option & special behavior"
"""test marker option & special behavior"""
handler = self.handler
# preserve hash if provided
@@ -2194,16 +2191,16 @@ class unix_fallback_test(HandlerCase):
warnings.filterwarnings("ignore", "'unix_fallback' is deprecated")
def test_90_wildcard(self):
"test enable_wildcard flag"
"""test enable_wildcard flag"""
h = self.handler
self.assertTrue(h.verify('password','', enable_wildcard=True))
self.assertFalse(h.verify('password',''))
for c in ("!*x"):
for c in "!*x":
self.assertFalse(h.verify('password',c, enable_wildcard=True))
self.assertFalse(h.verify('password',c))
def test_91_preserves_existing(self):
"test preserves existing disabled hash"
"""test preserves existing disabled hash"""
handler = self.handler
# use marker if no hash

View File

@@ -70,6 +70,20 @@ class _bcrypt_test(HandlerCase):
(b('\xa3'),
'$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq'),
#
# bsd wraparound bug (fixed in 2b)
#
# NOTE: if backend is vulnerable, password will hash the same as '0'*72
# ("$2a$04$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6"),
# rather than same as ("0123456789"*8)[:72]
# 255 should be sufficient, but checking
(("0123456789"*26)[:254], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'),
(("0123456789"*26)[:255], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'),
(("0123456789"*26)[:256], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'),
(("0123456789"*26)[:257], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'),
#
# from py-bcrypt tests
#
@@ -88,6 +102,11 @@ class _bcrypt_test(HandlerCase):
# ensures utf-8 used for unicode
(UPASS_TABLE,
'$2a$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG'),
# ensure 2b support
(UPASS_TABLE,
'$2b$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG'),
]
if TEST_MODE("full"):
@@ -116,7 +135,7 @@ class _bcrypt_test(HandlerCase):
known_unidentified_hashes = [
# invalid minor version
"$2b$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q",
"$2f$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q",
"$2`$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q",
]
@@ -156,6 +175,7 @@ class _bcrypt_test(HandlerCase):
self.addCleanup(os.environ.__delitem__, key)
os.environ[key] = "enabled"
super(_bcrypt_test, self).setUp()
warnings.filterwarnings("ignore", ".*backend is vulnerable to the bsd wraparound bug.*")
def populate_settings(self, kwds):
# builtin is still just way too slow.
@@ -167,11 +187,13 @@ class _bcrypt_test(HandlerCase):
# fuzz testing
#===================================================================
def os_supports_ident(self, hash):
"check if OS crypt is expected to support given ident"
"""check if OS crypt is expected to support given ident"""
if hash is None:
return True
# most OSes won't support 2x/2y
# XXX: definitely not the BSDs, but what about the linux variants?
# XXX: replace this all with 'handler._lacks_2{x}_support' feature detection?
# could even just do call to safe_crypt(ident + salt) and see what we get
from passlib.handlers.bcrypt import IDENT_2X, IDENT_2Y
if hash.startswith(IDENT_2X) or hash.startswith(IDENT_2Y):
return False
@@ -179,21 +201,22 @@ class _bcrypt_test(HandlerCase):
def fuzz_verifier_bcrypt(self):
# test against bcrypt, if available
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2X, IDENT_2Y
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2B, IDENT_2X, IDENT_2Y, _detect_pybcrypt
from passlib.utils import to_native_str, to_bytes
try:
import bcrypt
except ImportError:
return
if not hasattr(bcrypt, "_ffi"):
if _detect_pybcrypt():
return
def check_bcrypt(secret, hash):
"bcrypt"
"""bcrypt"""
secret = to_bytes(secret, self.fuzz_password_encoding)
#if hash.startswith(IDENT_2Y):
# hash = IDENT_2A + hash[4:]
if hash.startswith(IDENT_2):
# bcryptor doesn't support $2$ hashes; but we can fake it
if hash.startswith(IDENT_2B):
# bcrypt <1.1 lacks 2b support
hash = IDENT_2A + hash[4:]
elif hash.startswith(IDENT_2):
# bcrypt doesn't support $2$ hashes; but we can fake it
# using the $2a$ algorithm, by repeating the password until
# it's 72 chars in length.
hash = IDENT_2A + hash[3:]
@@ -203,23 +226,25 @@ class _bcrypt_test(HandlerCase):
try:
return bcrypt.hashpw(secret, hash) == hash
except ValueError:
raise ValueError("bcrypt rejected hash: %r" % (hash,))
raise ValueError("bcrypt rejected hash: %r (secret=%r)" % (hash, secret))
return check_bcrypt
def fuzz_verifier_pybcrypt(self):
# test against py-bcrypt, if available
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2X, IDENT_2Y
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2B, IDENT_2X, IDENT_2Y, _detect_pybcrypt
from passlib.utils import to_native_str
try:
import bcrypt
except ImportError:
return
if hasattr(bcrypt, "_ffi"):
if not _detect_pybcrypt():
return
def check_pybcrypt(secret, hash):
"pybcrypt"
"""pybcrypt"""
secret = to_native_str(secret, self.fuzz_password_encoding)
if hash.startswith(IDENT_2Y):
if len(secret) > 200: # vulnerable to wraparound bug
secret = secret[:200]
if hash.startswith((IDENT_2B, IDENT_2Y)):
hash = IDENT_2A + hash[4:]
try:
return bcrypt.hashpw(secret, hash) == hash
@@ -229,16 +254,16 @@ class _bcrypt_test(HandlerCase):
def fuzz_verifier_bcryptor(self):
# test against bcryptor, if available
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2Y
from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2Y, IDENT_2B
from passlib.utils import to_native_str
try:
from bcryptor.engine import Engine
except ImportError:
return
def check_bcryptor(secret, hash):
"bcryptor"
"""bcryptor"""
secret = to_native_str(secret, self.fuzz_password_encoding)
if hash.startswith(IDENT_2Y):
if hash.startswith((IDENT_2B, IDENT_2Y)):
hash = IDENT_2A + hash[4:]
elif hash.startswith(IDENT_2):
# bcryptor doesn't support $2$ hashes; but we can fake it
@@ -297,7 +322,7 @@ class _bcrypt_test(HandlerCase):
]
def test_90_bcrypt_padding(self):
"test passlib correctly handles bcrypt padding bits"
"""test passlib correctly handles bcrypt padding bits"""
self.require_TEST_MODE("full")
#
# prevents reccurrence of issue 25 (https://code.google.com/p/passlib/issues/detail?id=25)
@@ -327,10 +352,12 @@ class _bcrypt_test(HandlerCase):
self.assertEqual(hash, "$2a$05$" + "." * 22)
#
# make sure genhash() corrects input
# test public methods against good & bad hashes
#
samples = self.known_incorrect_padding
for pwd, bad, good in samples:
# make sure genhash() corrects bad configs, leaves good unchanged
with self.assertWarningList([corr_desc]):
self.assertEqual(bcrypt.genhash(pwd, bad), good)
with self.assertWarningList([]):
@@ -437,6 +464,7 @@ class _bcrypt_sha256_test(HandlerCase):
self.addCleanup(os.environ.__delitem__, key)
os.environ[key] = "enabled"
super(_bcrypt_sha256_test, self).setUp()
warnings.filterwarnings("ignore", ".*backend is vulnerable to the bsd wraparound bug.*")
def populate_settings(self, kwds):
# builtin is still just way too slow.

View File

@@ -43,7 +43,7 @@ class _DjangoHelper(object):
return None
from django.contrib.auth.models import check_password
def verify_django(secret, hash):
"django/check_password"
"""django/check_password"""
if (1,4) <= DJANGO_VERSION < (1,6) and not secret:
return "skip"
if self.handler.name == "django_bcrypt" and hash.startswith("bcrypt$$2y$"):
@@ -57,7 +57,7 @@ class _DjangoHelper(object):
return verify_django
def test_90_django_reference(self):
"run known correct hashes through Django's check_password()"
"""run known correct hashes through Django's check_password()"""
from passlib.tests.test_ext_django import DJANGO_VERSION
# check_password() not added until 1.0
min_django_version = max(self.min_django_version, (1,0))
@@ -81,7 +81,7 @@ class _DjangoHelper(object):
django_has_encoding_glitch = False
def test_91_django_generation(self):
"test against output of Django's make_password()"
"""test against output of Django's make_password()"""
from passlib.tests.test_ext_django import DJANGO_VERSION
# make_password() not added until 1.4
min_django_version = max(self.min_django_version, (1,4))
@@ -106,7 +106,7 @@ class _DjangoHelper(object):
self.assertFalse(self.do_verify(other, hash))
class django_disabled_test(HandlerCase):
"test django_disabled"
"""test django_disabled"""
handler = hash.django_disabled
is_disabled_handler = True
@@ -123,7 +123,7 @@ class django_disabled_test(HandlerCase):
]
class django_des_crypt_test(HandlerCase, _DjangoHelper):
"test django_des_crypt"
"""test django_des_crypt"""
handler = hash.django_des_crypt
secret_size = 8
@@ -164,7 +164,7 @@ class django_des_crypt_test(HandlerCase, _DjangoHelper):
]
class django_salted_md5_test(HandlerCase, _DjangoHelper):
"test django_salted_md5"
"""test django_salted_md5"""
handler = hash.django_salted_md5
django_has_encoding_glitch = True
@@ -204,7 +204,7 @@ class django_salted_md5_test(HandlerCase, _DjangoHelper):
return randintgauss(lower, upper, default, default*.5)
class django_salted_sha1_test(HandlerCase, _DjangoHelper):
"test django_salted_sha1"
"""test django_salted_sha1"""
handler = hash.django_salted_sha1
django_has_encoding_glitch = True
@@ -236,7 +236,7 @@ class django_salted_sha1_test(HandlerCase, _DjangoHelper):
fuzz_setting_salt_size = get_method_function(django_salted_md5_test.fuzz_setting_salt_size)
class django_pbkdf2_sha256_test(HandlerCase, _DjangoHelper):
"test django_pbkdf2_sha256"
"""test django_pbkdf2_sha256"""
handler = hash.django_pbkdf2_sha256
min_django_version = (1,4)
@@ -251,7 +251,7 @@ class django_pbkdf2_sha256_test(HandlerCase, _DjangoHelper):
]
class django_pbkdf2_sha1_test(HandlerCase, _DjangoHelper):
"test django_pbkdf2_sha1"
"""test django_pbkdf2_sha1"""
handler = hash.django_pbkdf2_sha1
min_django_version = (1,4)
@@ -266,7 +266,7 @@ class django_pbkdf2_sha1_test(HandlerCase, _DjangoHelper):
]
class django_bcrypt_test(HandlerCase, _DjangoHelper):
"test django_bcrypt"
"""test django_bcrypt"""
handler = hash.django_bcrypt
secret_size = 72
min_django_version = (1,4)
@@ -303,7 +303,7 @@ django_bcrypt_test = skipUnless(hash.bcrypt.has_backend(),
"no bcrypt backends available")(django_bcrypt_test)
class django_bcrypt_sha256_test(HandlerCase, _DjangoHelper):
"test django_bcrypt_sha256"
"""test django_bcrypt_sha256"""
handler = hash.django_bcrypt_sha256
min_django_version = (1,6)
forbidden_characters = None

View File

@@ -17,7 +17,7 @@ from passlib.tests.utils import TestCase
# test predefined app contexts
#=============================================================================
class HostsTest(TestCase):
"perform general tests to make sure contexts work"
"""perform general tests to make sure contexts work"""
# NOTE: these tests are not really comprehensive,
# since they would do little but duplicate
# the presets in apps.py

View File

@@ -12,7 +12,7 @@ import warnings
import sys
# site
# pkg
from passlib import hash, registry
from passlib import hash, registry, exc
from passlib.registry import register_crypt_handler, register_crypt_handler_path, \
get_crypt_handler, list_crypt_handlers, _unload_handler_name as unload_handler_name
import passlib.utils.handlers as uh
@@ -40,14 +40,23 @@ dummy_x = 1
#=============================================================================
class RegistryTest(TestCase):
descriptionPrefix = "passlib registry"
descriptionPrefix = "passlib.registry"
def tearDown(self):
for name in ("dummy_0", "dummy_1", "dummy_x", "dummy_bad"):
unload_handler_name(name)
def setUp(self):
super(RegistryTest, self).setUp()
# backup registry state & restore it after test.
locations = dict(registry._locations)
handlers = dict(registry._handlers)
def restore():
registry._locations.clear()
registry._locations.update(locations)
registry._handlers.clear()
registry._handlers.update(handlers)
self.addCleanup(restore)
def test_hash_proxy(self):
"test passlib.hash proxy object"
"""test passlib.hash proxy object"""
# check dir works
dir(hash)
@@ -80,7 +89,7 @@ class RegistryTest(TestCase):
self.assertRaises(ValueError, setattr, hash, "dummy_1x", dummy_1)
def test_register_crypt_handler_path(self):
"test register_crypt_handler_path()"
"""test register_crypt_handler_path()"""
# NOTE: this messes w/ internals of registry, shouldn't be used publically.
paths = registry._locations
@@ -116,6 +125,7 @@ class RegistryTest(TestCase):
# check lazy load w/ wrong name fails
register_crypt_handler_path('alt_dummy_0', __name__)
self.assertRaises(ValueError, get_crypt_handler, "alt_dummy_0")
unload_handler_name("alt_dummy_0")
# TODO: check lazy load which calls register_crypt_handler (warning should be issued)
sys.modules.pop("passlib.tests._test_bad_register", None)
@@ -127,7 +137,7 @@ class RegistryTest(TestCase):
self.assertIs(h, tbr.alt_dummy_bad)
def test_register_crypt_handler(self):
"test register_crypt_handler()"
"""test register_crypt_handler()"""
self.assertRaises(TypeError, register_crypt_handler, {})
@@ -158,7 +168,7 @@ class RegistryTest(TestCase):
self.assertTrue('dummy_1' in list_crypt_handlers())
def test_get_crypt_handler(self):
"test get_crypt_handler()"
"""test get_crypt_handler()"""
class dummy_1(uh.StaticHandler):
name = "dummy_1"
@@ -189,7 +199,7 @@ class RegistryTest(TestCase):
self.assertIs(get_crypt_handler(name, None), None)
def test_list_crypt_handlers(self):
"test list_crypt_handlers()"
"""test list_crypt_handlers()"""
from passlib.registry import list_crypt_handlers
# check system & private names aren't returned
@@ -197,17 +207,25 @@ class RegistryTest(TestCase):
passlib.hash.__dict__["_fake"] = "dummy" # so behavior seen under py2x also
for name in list_crypt_handlers():
self.assertFalse(name.startswith("_"), "%r: " % name)
unload_handler_name("_fake")
def test_handlers(self):
"verify we have tests for all handlers"
"""verify we have tests for all builtin handlers"""
from passlib.registry import list_crypt_handlers
from passlib.tests.test_handlers import get_handler_case
for name in list_crypt_handlers():
# skip some wrappers that don't need independant testing
if name.startswith("ldap_") and name[5:] in list_crypt_handlers():
continue
if name in ["roundup_plaintext"]:
continue
self.assertTrue(get_handler_case(name))
# check the remaining ones all have a handler
try:
self.assertTrue(get_handler_case(name))
except exc.MissingBackendError:
if name in ["bcrypt", "bcrypt_sha256"]: # expected to fail on some setups
continue
raise
#=============================================================================
# eof

View File

@@ -22,12 +22,12 @@ def hb(source):
# byte funcs
#=============================================================================
class MiscTest(TestCase):
"tests various parts of utils module"
"""tests various parts of utils module"""
# NOTE: could test xor_bytes(), but it's exercised well enough by pbkdf2 test
def test_compat(self):
"test compat's lazymodule"
"""test compat's lazymodule"""
from passlib.utils import compat
# "<module 'passlib.utils.compat' from 'passlib/utils/compat.pyc'>"
self.assertRegex(repr(compat),
@@ -58,7 +58,7 @@ class MiscTest(TestCase):
@deprecated_function(deprecated="1.6", removed="1.8")
def test_func(*args):
"test docstring"
"""test docstring"""
return args
self.assertTrue(".. deprecated::" in test_func.__doc__)
@@ -91,7 +91,7 @@ class MiscTest(TestCase):
self.assertIs(prop.im_func, prop.__func__)
def test_getrandbytes(self):
"test getrandbytes()"
"""test getrandbytes()"""
from passlib.utils import getrandbytes, rng
def f(*a,**k):
return getrandbytes(rng, *a, **k)
@@ -104,7 +104,7 @@ class MiscTest(TestCase):
self.assertNotEqual(a, b)
def test_getrandstr(self):
"test getrandstr()"
"""test getrandstr()"""
from passlib.utils import getrandstr, rng
def f(*a,**k):
return getrandstr(rng, *a, **k)
@@ -141,7 +141,7 @@ class MiscTest(TestCase):
self.assertEqual(len(generate_password(15)), 15)
def test_is_crypt_context(self):
"test is_crypt_context()"
"""test is_crypt_context()"""
from passlib.utils import is_crypt_context
from passlib.context import CryptContext
cc = CryptContext(["des_crypt"])
@@ -149,7 +149,7 @@ class MiscTest(TestCase):
self.assertFalse(not is_crypt_context(cc))
def test_genseed(self):
"test genseed()"
"""test genseed()"""
import random
from passlib.utils import genseed
rng = random.Random(genseed())
@@ -163,7 +163,7 @@ class MiscTest(TestCase):
rng.seed(genseed(rng))
def test_crypt(self):
"test crypt.crypt() wrappers"
"""test crypt.crypt() wrappers"""
from passlib.utils import has_crypt, safe_crypt, test_crypt
# test everything is disabled
@@ -220,7 +220,7 @@ class MiscTest(TestCase):
mod._crypt = orig
def test_consteq(self):
"test consteq()"
"""test consteq()"""
# NOTE: this test is kind of over the top, but that's only because
# this is used for the critical task of comparing hashes for equality.
from passlib.utils import consteq
@@ -304,7 +304,7 @@ class MiscTest(TestCase):
## ##print ", ".join(str(c) for c in [run] + times)
def test_saslprep(self):
"test saslprep() unicode normalizer"
"""test saslprep() unicode normalizer"""
self.require_stringprep()
from passlib.utils import saslprep as sp
@@ -395,10 +395,10 @@ class MiscTest(TestCase):
# byte/unicode helpers
#=============================================================================
class CodecTest(TestCase):
"tests bytes/unicode helpers in passlib.utils"
"""tests bytes/unicode helpers in passlib.utils"""
def test_bytes(self):
"test b() helper, bytes and native str type"
"""test b() helper, bytes and native str type"""
if PY3:
import builtins
self.assertIs(bytes, builtins.bytes)
@@ -414,7 +414,7 @@ class CodecTest(TestCase):
self.assertEqual(b('\x00\xff'), "\x00\xff")
def test_to_bytes(self):
"test to_bytes()"
"""test to_bytes()"""
from passlib.utils import to_bytes
# check unicode inputs
@@ -443,7 +443,7 @@ class CodecTest(TestCase):
self.assertRaises(TypeError, to_bytes, None)
def test_to_unicode(self):
"test to_unicode()"
"""test to_unicode()"""
from passlib.utils import to_unicode
# check unicode inputs
@@ -465,7 +465,7 @@ class CodecTest(TestCase):
self.assertRaises(TypeError, to_unicode, None)
def test_to_native_str(self):
"test to_native_str()"
"""test to_native_str()"""
from passlib.utils import to_native_str
# test plain ascii
@@ -496,7 +496,7 @@ class CodecTest(TestCase):
self.assertRaises(TypeError, to_native_str, None, 'ascii')
def test_is_ascii_safe(self):
"test is_ascii_safe()"
"""test is_ascii_safe()"""
from passlib.utils import is_ascii_safe
self.assertTrue(is_ascii_safe(b("\x00abc\x7f")))
self.assertTrue(is_ascii_safe(u("\x00abc\x7f")))
@@ -504,7 +504,7 @@ class CodecTest(TestCase):
self.assertFalse(is_ascii_safe(u("\x00abc\x80")))
def test_is_same_codec(self):
"test is_same_codec()"
"""test is_same_codec()"""
from passlib.utils import is_same_codec
self.assertTrue(is_same_codec(None, None))
@@ -523,7 +523,7 @@ class CodecTest(TestCase):
# base64engine
#=============================================================================
class Base64EngineTest(TestCase):
"test standalone parts of Base64Engine"
"""test standalone parts of Base64Engine"""
# NOTE: most Base64Engine testing done via _Base64Test subclasses below.
def test_constructor(self):
@@ -547,7 +547,7 @@ class Base64EngineTest(TestCase):
self.assertRaises(ValueError, ab64_decode, "abcde")
class _Base64Test(TestCase):
"common tests for all Base64Engine instances"
"""common tests for all Base64Engine instances"""
#===================================================================
# class attrs
#===================================================================
@@ -566,14 +566,14 @@ class _Base64Test(TestCase):
# helper to generate bytemap-specific strings
def m(self, *offsets):
"generate byte string from offsets"
"""generate byte string from offsets"""
return join_bytes(self.engine.bytemap[o:o+1] for o in offsets)
#===================================================================
# test encode_bytes
#===================================================================
def test_encode_bytes(self):
"test encode_bytes() against reference inputs"
"""test encode_bytes() against reference inputs"""
engine = self.engine
encode = engine.encode_bytes
for raw, encoded in self.encoded_data:
@@ -581,7 +581,7 @@ class _Base64Test(TestCase):
self.assertEqual(result, encoded, "encode %r:" % (raw,))
def test_encode_bytes_bad(self):
"test encode_bytes() with bad input"
"""test encode_bytes() with bad input"""
engine = self.engine
encode = engine.encode_bytes
self.assertRaises(TypeError, encode, u('\x00'))
@@ -591,7 +591,7 @@ class _Base64Test(TestCase):
# test decode_bytes
#===================================================================
def test_decode_bytes(self):
"test decode_bytes() against reference inputs"
"""test decode_bytes() against reference inputs"""
engine = self.engine
decode = engine.decode_bytes
for raw, encoded in self.encoded_data:
@@ -599,7 +599,7 @@ class _Base64Test(TestCase):
self.assertEqual(result, raw, "decode %r:" % (encoded,))
def test_decode_bytes_padding(self):
"test decode_bytes() ignores padding bits"
"""test decode_bytes() ignores padding bits"""
bchr = (lambda v: bytes([v])) if PY3 else chr
engine = self.engine
m = self.m
@@ -626,7 +626,7 @@ class _Base64Test(TestCase):
"%d/2 bits:" % i)
def test_decode_bytes_bad(self):
"test decode_bytes() with bad input"
"""test decode_bytes() with bad input"""
engine = self.engine
decode = engine.decode_bytes
@@ -645,7 +645,7 @@ class _Base64Test(TestCase):
# encode_bytes+decode_bytes
#===================================================================
def test_codec(self):
"test encode_bytes/decode_bytes against random data"
"""test encode_bytes/decode_bytes against random data"""
engine = self.engine
from passlib.utils import getrandbytes, getrandstr
saw_zero = False
@@ -691,7 +691,7 @@ class _Base64Test(TestCase):
self.assertEqual(result, encoded)
def test_repair_unused(self):
"test repair_unused()"
"""test repair_unused()"""
# NOTE: this test relies on encode_bytes() always returning clear
# padding bits - which should be ensured by test vectors.
from passlib.utils import rng, getrandstr
@@ -739,7 +739,7 @@ class _Base64Test(TestCase):
]
def test_encode_transposed_bytes(self):
"test encode_transposed_bytes()"
"""test encode_transposed_bytes()"""
engine = self.engine
for result, input, offsets in self.transposed + self.transposed_dups:
tmp = engine.encode_transposed_bytes(input, offsets)
@@ -749,7 +749,7 @@ class _Base64Test(TestCase):
self.assertRaises(TypeError, engine.encode_transposed_bytes, u("a"), [])
def test_decode_transposed_bytes(self):
"test decode_transposed_bytes()"
"""test decode_transposed_bytes()"""
engine = self.engine
for input, result, offsets in self.transposed:
tmp = engine.encode_bytes(input)
@@ -757,7 +757,7 @@ class _Base64Test(TestCase):
self.assertEqual(out, result)
def test_decode_transposed_bytes_bad(self):
"test decode_transposed_bytes() fails if map is a one-way"
"""test decode_transposed_bytes() fails if map is a one-way"""
engine = self.engine
for input, _, offsets in self.transposed_dups:
tmp = engine.encode_bytes(input)
@@ -768,7 +768,7 @@ class _Base64Test(TestCase):
# test 6bit handling
#===================================================================
def check_int_pair(self, bits, encoded_pairs):
"helper to check encode_intXX & decode_intXX functions"
"""helper to check encode_intXX & decode_intXX functions"""
engine = self.engine
encode = getattr(engine, "encode_int%s" % bits)
decode = getattr(engine, "decode_int%s" % bits)
@@ -795,7 +795,7 @@ class _Base64Test(TestCase):
self.assertRaises(TypeError, decode, None)
# do random testing.
from passlib.utils import getrandbytes, getrandstr
from passlib.utils import getrandstr
for i in irange(100):
# generate random value, encode, and then decode
value = random.randint(0, upper-1)
@@ -844,7 +844,7 @@ class _Base64Test(TestCase):
else m(63,63,63,63, 63,63,63,63, 63,63,15))])
def test_encoded_ints(self):
"test against reference integer encodings"
"""test against reference integer encodings"""
if not self.encoded_ints:
raise self.skipTests("none defined for class")
engine = self.engine
@@ -863,7 +863,7 @@ class _Base64Test(TestCase):
from passlib.utils import h64, h64big
class H64_Test(_Base64Test):
"test H64 codec functions"
"""test H64 codec functions"""
engine = h64
descriptionPrefix = "h64 codec"
@@ -888,7 +888,7 @@ class H64_Test(_Base64Test):
]
class H64Big_Test(_Base64Test):
"test H64Big codec functions"
"""test H64Big codec functions"""
engine = h64big
descriptionPrefix = "h64big codec"

View File

@@ -31,7 +31,7 @@ def hb(source):
# test assorted crypto helpers
#=============================================================================
class CryptoTest(TestCase):
"test various crypto functions"
"""test various crypto functions"""
ndn_formats = ["hashlib", "iana"]
ndn_values = [
@@ -48,7 +48,7 @@ class CryptoTest(TestCase):
]
def test_norm_hash_name(self):
"test norm_hash_name()"
"""test norm_hash_name()"""
from itertools import chain
from passlib.utils.pbkdf2 import norm_hash_name, _nhn_hash_names
@@ -123,7 +123,7 @@ class DesTest(TestCase):
]
def test_01_expand(self):
"test expand_des_key()"
"""test expand_des_key()"""
from passlib.utils.des import expand_des_key, shrink_des_key, \
_KDATA_MASK, INT_56_MASK
@@ -147,7 +147,7 @@ class DesTest(TestCase):
self.assertRaises(ValueError, expand_des_key, b("\x00")*6)
def test_02_shrink(self):
"test shrink_des_key()"
"""test shrink_des_key()"""
from passlib.utils.des import expand_des_key, shrink_des_key, \
INT_64_MASK
from passlib.utils import random, getrandbytes
@@ -172,13 +172,13 @@ class DesTest(TestCase):
self.assertRaises(ValueError, shrink_des_key, b("\x00")*7)
def _random_parity(self, key):
"randomize parity bits"
"""randomize parity bits"""
from passlib.utils.des import _KDATA_MASK, _KPARITY_MASK, INT_64_MASK
from passlib.utils import rng
return (key & _KDATA_MASK) | (rng.randint(0,INT_64_MASK) & _KPARITY_MASK)
def test_03_encrypt_bytes(self):
"test des_encrypt_block()"
"""test des_encrypt_block()"""
from passlib.utils.des import (des_encrypt_block, shrink_des_key,
_pack64, _unpack64)
@@ -224,8 +224,8 @@ class DesTest(TestCase):
self.assertRaises(ValueError, des_encrypt_block, stub, stub, 0, rounds=0)
def test_04_encrypt_ints(self):
"test des_encrypt_int_block()"
from passlib.utils.des import (des_encrypt_int_block, shrink_des_key)
"""test des_encrypt_int_block()"""
from passlib.utils.des import des_encrypt_int_block
# run through test vectors
for key, plaintext, correct in self.des_test_vectors:
@@ -285,7 +285,7 @@ class _MD4_Test(TestCase):
]
def test_md4_update(self):
"test md4 update"
"""test md4 update"""
from passlib.utils.md4 import md4
h = md4(b(''))
self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0")
@@ -302,21 +302,21 @@ class _MD4_Test(TestCase):
self.assertEqual(h.hexdigest(), "d79e1c308aa5bbcdeea8ed63df412da9")
def test_md4_hexdigest(self):
"test md4 hexdigest()"
"""test md4 hexdigest()"""
from passlib.utils.md4 import md4
for input, hex in self.vectors:
out = md4(input).hexdigest()
self.assertEqual(out, hex)
def test_md4_digest(self):
"test md4 digest()"
"""test md4 digest()"""
from passlib.utils.md4 import md4
for input, hex in self.vectors:
out = bascii_to_str(hexlify(md4(input).digest()))
self.assertEqual(out, hex)
def test_md4_copy(self):
"test md4 copy()"
"""test md4 copy()"""
from passlib.utils.md4 import md4
h = md4(b('abc'))
@@ -342,7 +342,7 @@ MD4_Builtin_Test = skipUnless(TEST_MODE("full") or not has_native_md4,
# test PBKDF1 support
#=============================================================================
class Pbkdf1_Test(TestCase):
"test kdf helpers"
"""test kdf helpers"""
descriptionPrefix = "pbkdf1"
pbkdf1_tests = [
@@ -369,14 +369,14 @@ class Pbkdf1_Test(TestCase):
)
def test_known(self):
"test reference vectors"
"""test reference vectors"""
from passlib.utils.pbkdf2 import pbkdf1
for secret, salt, rounds, keylen, digest, correct in self.pbkdf1_tests:
result = pbkdf1(secret, salt, rounds, keylen, digest)
self.assertEqual(result, correct)
def test_border(self):
"test border cases"
"""test border cases"""
from passlib.utils.pbkdf2 import pbkdf1
def helper(secret=b('secret'), salt=b('salt'), rounds=1, keylen=1, hash='md5'):
return pbkdf1(secret, salt, rounds, keylen, hash)
@@ -402,7 +402,7 @@ class Pbkdf1_Test(TestCase):
# test PBKDF2 support
#=============================================================================
class _Pbkdf2_Test(TestCase):
"test pbkdf2() support"
"""test pbkdf2() support"""
_disable_m2crypto = False
def setUp(self):
@@ -533,7 +533,7 @@ class _Pbkdf2_Test(TestCase):
]
def test_known(self):
"test reference vectors"
"""test reference vectors"""
from passlib.utils.pbkdf2 import pbkdf2
for row in self.pbkdf2_test_vectors:
correct, secret, salt, rounds, keylen = row[:5]
@@ -542,7 +542,7 @@ class _Pbkdf2_Test(TestCase):
self.assertEqual(result, correct)
def test_border(self):
"test border cases"
"""test border cases"""
from passlib.utils.pbkdf2 import pbkdf2
def helper(secret=b('password'), salt=b('salt'), rounds=1, keylen=None, prf="hmac-sha1"):
return pbkdf2(secret, salt, rounds, keylen, prf)
@@ -568,7 +568,7 @@ class _Pbkdf2_Test(TestCase):
self.assertRaises(TypeError, helper, prf=5)
def test_default_keylen(self):
"test keylen==None"
"""test keylen==None"""
from passlib.utils.pbkdf2 import pbkdf2
def helper(secret=b('password'), salt=b('salt'), rounds=1, keylen=None, prf="hmac-sha1"):
return pbkdf2(secret, salt, rounds, keylen, prf)
@@ -576,7 +576,7 @@ class _Pbkdf2_Test(TestCase):
self.assertEqual(len(helper(prf='hmac-sha256')), 32)
def test_custom_prf(self):
"test custom prf function"
"""test custom prf function"""
from passlib.utils.pbkdf2 import pbkdf2
def prf(key, msg):
return hashlib.md5(key+msg+b('fooey')).digest()

View File

@@ -18,7 +18,7 @@ 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, SUPPORTS_DIR_METHOD
import passlib.utils.handlers as uh
from passlib.tests.utils import HandlerCase, TestCase, catch_warnings
from passlib.tests.utils import HandlerCase, TestCase, catch_warnings, patchAttr
from passlib.utils.compat import u, PY3
# module
log = getLogger(__name__)
@@ -27,7 +27,7 @@ log = getLogger(__name__)
# utils
#=============================================================================
def _makelang(alphabet, size):
"generate all strings of given size using alphabet"
"""generate all strings of given size using alphabet"""
def helper(size):
if size < 2:
for char in alphabet:
@@ -42,13 +42,15 @@ def _makelang(alphabet, size):
# test GenericHandler & associates mixin classes
#=============================================================================
class SkeletonTest(TestCase):
"test hash support classes"
"""test hash support classes"""
patchAttr = patchAttr
#===================================================================
# StaticHandler
#===================================================================
def test_00_static_handler(self):
"test StaticHandler class"
"""test StaticHandler class"""
class d1(uh.StaticHandler):
name = "d1"
@@ -94,7 +96,7 @@ class SkeletonTest(TestCase):
self.assertEqual(d1.encrypt('s', flag=True), '_b')
def test_01_calc_checksum_hack(self):
"test StaticHandler legacy attr"
"""test StaticHandler legacy attr"""
# release 1.5 StaticHandler required genhash(),
# not _calc_checksum, be implemented. we have backward compat wrapper,
# this tests that it works.
@@ -103,7 +105,7 @@ class SkeletonTest(TestCase):
name = "d1"
@classmethod
def identify(self, hash):
def identify(cls, hash):
if not hash or len(hash) != 40:
return False
try:
@@ -111,7 +113,6 @@ class SkeletonTest(TestCase):
except ValueError:
return False
return True
@classmethod
def genhash(cls, secret, hash):
if secret is None:
@@ -121,7 +122,6 @@ class SkeletonTest(TestCase):
if hash is not None and not cls.identify(hash):
raise ValueError("invalid hash")
return hashlib.sha1(b("xyz") + secret).hexdigest()
@classmethod
def verify(cls, secret, hash):
if hash is None:
@@ -144,9 +144,8 @@ class SkeletonTest(TestCase):
# GenericHandler & mixins
#===================================================================
def test_10_identify(self):
"test GenericHandler.identify()"
"""test GenericHandler.identify()"""
class d1(uh.GenericHandler):
@classmethod
def from_string(cls, hash):
if isinstance(hash, bytes):
@@ -180,7 +179,7 @@ class SkeletonTest(TestCase):
del d1.ident
def test_11_norm_checksum(self):
"test GenericHandler checksum handling"
"""test GenericHandler checksum handling"""
# setup helpers
class d1(uh.GenericHandler):
name = 'd1'
@@ -216,7 +215,7 @@ class SkeletonTest(TestCase):
self.assertIs(norm_checksum(u('zzzz')), None)
def test_12_norm_checksum_raw(self):
"test GenericHandler + HasRawChecksum mixin"
"""test GenericHandler + HasRawChecksum mixin"""
class d1(uh.HasRawChecksum, uh.GenericHandler):
name = 'd1'
checksum_size = 4
@@ -236,7 +235,7 @@ class SkeletonTest(TestCase):
self.assertIs(norm_checksum(b('0')*4), None)
def test_20_norm_salt(self):
"test GenericHandler + HasSalt mixin"
"""test GenericHandler + HasSalt mixin"""
# setup helpers
class d1(uh.HasSalt, uh.GenericHandler):
name = 'd1'
@@ -312,7 +311,7 @@ class SkeletonTest(TestCase):
# TODO: test HasRawSalt mixin
def test_30_norm_rounds(self):
"test GenericHandler + HasRounds mixin"
"""test GenericHandler + HasRounds mixin"""
# setup helpers
class d1(uh.HasRounds, uh.GenericHandler):
name = 'd1'
@@ -359,7 +358,7 @@ class SkeletonTest(TestCase):
self.assertRaises(TypeError, norm_rounds, use_defaults=True)
def test_40_backends(self):
"test GenericHandler + HasManyBackends mixin"
"""test GenericHandler + HasManyBackends mixin"""
class d1(uh.HasManyBackends, uh.GenericHandler):
name = 'd1'
setting_kwds = ()
@@ -412,7 +411,7 @@ class SkeletonTest(TestCase):
self.assertRaises(ValueError, d1.has_backend, 'c')
def test_50_norm_ident(self):
"test GenericHandler + HasManyIdents"
"""test GenericHandler + HasManyIdents"""
# setup helpers
class d1(uh.HasManyIdents, uh.GenericHandler):
name = 'd1'
@@ -458,7 +457,7 @@ class SkeletonTest(TestCase):
# but way work correctly for some hashes
#===================================================================
def test_91_parsehash(self):
"test parsehash()"
"""test parsehash()"""
# NOTE: this just tests some existing GenericHandler classes
from passlib import hash
@@ -514,7 +513,7 @@ class SkeletonTest(TestCase):
))
def test_92_bitsize(self):
"test bitsize()"
"""test bitsize()"""
# NOTE: this just tests some existing GenericHandler classes
from passlib import hash
@@ -527,10 +526,14 @@ class SkeletonTest(TestCase):
{'checksum': 186, 'salt': 132})
# linear rounds
# NOTE: +3 comes from int(math.log(.1,2)),
# where 0.1 = 10% = default allowed variation in rounds
self.patchAttr(hash.sha256_crypt, "default_rounds", 1 << (14 + 3))
self.assertEqual(hash.sha256_crypt.bitsize(),
{'checksum': 258, 'rounds': 14, 'salt': 96})
# raw checksum
self.patchAttr(hash.pbkdf2_sha1, "default_rounds", 1 << (13 + 3))
self.assertEqual(hash.pbkdf2_sha1.bitsize(),
{'checksum': 160, 'rounds': 13, 'salt': 128})
@@ -546,7 +549,7 @@ class SkeletonTest(TestCase):
# PrefixWrapper
#=============================================================================
class dummy_handler_in_registry(object):
"context manager that inserts dummy handler in registry"
"""context manager that inserts dummy handler in registry"""
def __init__(self, name):
self.name = name
self.dummy = type('dummy_' + name, (uh.GenericHandler,), dict(
@@ -566,10 +569,10 @@ class dummy_handler_in_registry(object):
registry._unload_handler_name(self.name, locations=False)
class PrefixWrapperTest(TestCase):
"test PrefixWrapper class"
"""test PrefixWrapper class"""
def test_00_lazy_loading(self):
"test PrefixWrapper lazy loading of handler"
"""test PrefixWrapper lazy loading of handler"""
d1 = uh.PrefixWrapper("d1", "ldap_md5", "{XXX}", "{MD5}", lazy=True)
# check base state
@@ -585,7 +588,7 @@ class PrefixWrapperTest(TestCase):
self.assertIs(d1.wrapped, ldap_md5)
def test_01_active_loading(self):
"test PrefixWrapper active loading of handler"
"""test PrefixWrapper active loading of handler"""
d1 = uh.PrefixWrapper("d1", "ldap_md5", "{XXX}", "{MD5}")
# check base state
@@ -598,7 +601,7 @@ class PrefixWrapperTest(TestCase):
self.assertIs(d1.wrapped, ldap_md5)
def test_02_explicit(self):
"test PrefixWrapper with explicitly specified handler"
"""test PrefixWrapper with explicitly specified handler"""
d1 = uh.PrefixWrapper("d1", ldap_md5, "{XXX}", "{MD5}")
@@ -696,7 +699,7 @@ class PrefixWrapperTest(TestCase):
self.assertEqual(h.ident, None)
def test_13_repr(self):
"test repr()"
"""test repr()"""
h = uh.PrefixWrapper("h2", "md5_crypt", "{XXX}", orig_prefix="$1$")
self.assertRegex(repr(h),
r"""(?x)^PrefixWrapper\(
@@ -707,7 +710,7 @@ class PrefixWrapperTest(TestCase):
\)$""")
def test_14_bad_hash(self):
"test orig_prefix sanity check"
"""test orig_prefix sanity check"""
# shoudl throw InvalidHashError if wrapped hash doesn't begin
# with orig_prefix.
h = uh.PrefixWrapper("h2", "md5_crypt", orig_prefix="$6$")
@@ -719,7 +722,7 @@ class PrefixWrapperTest(TestCase):
# parts of passlib. they shouldn't be used as actual password schemes.
#=============================================================================
class UnsaltedHash(uh.StaticHandler):
"test algorithm which lacks a salt"
"""test algorithm which lacks a salt"""
name = "unsalted_test_hash"
checksum_chars = uh.LOWER_HEX_CHARS
checksum_size = 40
@@ -731,7 +734,7 @@ class UnsaltedHash(uh.StaticHandler):
return str_to_uascii(hashlib.sha1(data).hexdigest())
class SaltedHash(uh.HasSalt, uh.GenericHandler):
"test algorithm with a salt"
"""test algorithm with a salt"""
name = "salted_test_hash"
setting_kwds = ("salt",)

View File

@@ -15,7 +15,7 @@ from passlib.utils.compat import u
#
#=============================================================================
class UtilTest(TestCase):
"test util funcs in passlib.win32"
"""test util funcs in passlib.win32"""
##test hashes from http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx
## among other places

View File

@@ -25,7 +25,7 @@ __all__ = [
TH_PATH = "passlib.tests.test_handlers"
def do_hash_tests(*args):
"return list of hash algorithm tests that match regexes"
"""return list of hash algorithm tests that match regexes"""
if not args:
print(TH_PATH)
return
@@ -44,7 +44,7 @@ def do_hash_tests(*args):
return not names
def do_preset_tests(name):
"return list of preset test names"
"""return list of preset test names"""
if name == "django" or name == "django-hashes":
do_hash_tests("django_.*_test", "hex_md5_test")
if name == "django":
@@ -53,7 +53,7 @@ def do_preset_tests(name):
raise ValueError("unknown name: %r" % name)
def do_setup_gae(path, runtime):
"write fake GAE ``app.yaml`` to current directory so nosegae will work"
"""write fake GAE ``app.yaml`` to current directory so nosegae will work"""
from passlib.tests.utils import set_file
set_file(os.path.join(path, "app.yaml"), """\
application: fake-app

View File

@@ -50,7 +50,7 @@ else:
GAE = True
def ensure_mtime_changed(path):
"ensure file's mtime has changed"
"""ensure file's mtime has changed"""
# NOTE: this is hack to deal w/ filesystems whose mtime resolution is >= 1s,
# when a test needs to be sure the mtime changed after writing to the file.
last = os.path.getmtime(path)
@@ -103,14 +103,14 @@ def TEST_MODE(min=None, max=None):
# hash object inspection
#=============================================================================
def has_crypt_support(handler):
"check if host's crypt() supports this natively"
"""check if host's crypt() supports this natively"""
if hasattr(handler, "orig_prefix"):
# ignore wrapper classes
return False
return 'os_crypt' in getattr(handler, "backends", ()) and handler.has_backend("os_crypt")
def has_relaxed_setting(handler):
"check if handler supports 'relaxed' kwd"
"""check if handler supports 'relaxed' kwd"""
# FIXME: I've been lazy, should probably just add 'relaxed' kwd
# to all handlers that derive from GenericHandler
@@ -122,7 +122,7 @@ def has_relaxed_setting(handler):
uh.GenericHandler)
def has_active_backend(handler):
"return active backend for handler, if any"
"""return active backend for handler, if any"""
if not hasattr(handler, "get_backend"):
return "builtin"
try:
@@ -131,7 +131,7 @@ def has_active_backend(handler):
return None
def is_default_backend(handler, backend):
"check if backend is the default for source"
"""check if backend is the default for source"""
try:
orig = handler.get_backend()
except MissingBackendError:
@@ -142,7 +142,12 @@ def is_default_backend(handler, backend):
handler.set_backend(orig)
class temporary_backend(object):
"temporarily set handler to specific backend"
"""
temporarily set handler to specific backend
"""
_orig = None
def __init__(self, handler, backend=None):
self.handler = handler
self.backend = backend
@@ -160,19 +165,19 @@ class temporary_backend(object):
# misc helpers
#=============================================================================
def set_file(path, content):
"set file to specified bytes"
"""set file to specified bytes"""
if isinstance(content, unicode):
content = content.encode("utf-8")
with open(path, "wb") as fh:
fh.write(content)
def get_file(path):
"read file as bytes"
"""read file as bytes"""
with open(path, "rb") as fh:
return fh.read()
def tonn(source):
"convert native string to non-native string"
"""convert native string to non-native string"""
if not isinstance(source, str):
return source
elif PY3:
@@ -191,11 +196,11 @@ def limit(value, lower, upper):
return value
def randintgauss(lower, upper, mu, sigma):
"hack used by fuzz testing"
"""hack used by fuzz testing"""
return int(limit(rng.normalvariate(mu, sigma), lower, upper))
def quicksleep(delay):
"because time.sleep() doesn't even have 10ms accuracy on some OSes"
"""because time.sleep() doesn't even have 10ms accuracy on some OSes"""
start = tick()
while tick()-start < delay:
pass
@@ -241,7 +246,7 @@ class TestCase(_TestCase):
descriptionPrefix = None
def shortDescription(self):
"wrap shortDescription() method to prepend descriptionPrefix"
"""wrap shortDescription() method to prepend descriptionPrefix"""
desc = super(TestCase, self).shortDescription()
prefix = self.descriptionPrefix
if prefix:
@@ -282,7 +287,7 @@ class TestCase(_TestCase):
self.setUpWarnings()
def setUpWarnings(self):
"helper to init warning filters before subclass setUp()"
"""helper to init warning filters before subclass setUp()"""
if self.resetWarningState:
ctx = reset_warnings()
ctx.__enter__()
@@ -445,7 +450,7 @@ class TestCase(_TestCase):
# capability tests
#===================================================================
def require_stringprep(self):
"helper to skip test if stringprep is missing"
"""helper to skip test if stringprep is missing"""
from passlib.utils import stringprep
if not stringprep:
from passlib.utils import _stringprep_missing_reason
@@ -453,12 +458,12 @@ class TestCase(_TestCase):
_stringprep_missing_reason)
def require_TEST_MODE(self, level):
"skip test for all PASSLIB_TEST_MODE values below <level>"
"""skip test for all PASSLIB_TEST_MODE values below <level>"""
if not TEST_MODE(level):
raise self.skipTest("requires >= %r test mode" % level)
def require_writeable_filesystem(self):
"skip test if writeable FS not available"
"""skip test if writeable FS not available"""
if GAE:
return self.skipTest("GAE doesn't offer read/write filesystem access")
@@ -468,7 +473,7 @@ class TestCase(_TestCase):
_mktemp_queue = None
def mktemp(self, *args, **kwds):
"create temp file that's cleaned up at end of test"
"""create temp file that's cleaned up at end of test"""
self.require_writeable_filesystem()
fd, path = tempfile.mkstemp(*args, **kwds)
os.close(fd)
@@ -628,7 +633,7 @@ class HandlerCase(TestCase):
@classmethod
def iter_known_hashes(cls):
"iterate through known (secret, hash) pairs"
"""iterate through known (secret, hash) pairs"""
for secret, hash in cls.known_correct_hashes:
yield secret, hash
for config, secret, hash in cls.known_correct_configs:
@@ -637,7 +642,7 @@ class HandlerCase(TestCase):
yield secret, hash
def get_sample_hash(self):
"test random sample secret/hash pair"
"""test random sample secret/hash pair"""
known = list(self.iter_known_hashes())
return rng.choice(known)
@@ -645,7 +650,7 @@ class HandlerCase(TestCase):
# test helpers
#---------------------------------------------------------------
def check_verify(self, secret, hash, msg=None, negate=False):
"helper to check verify() outcome, honoring is_disabled_handler"
"""helper to check verify() outcome, honoring is_disabled_handler"""
result = self.do_verify(secret, hash)
self.assertTrue(result is True or result is False,
"verify() returned non-boolean value: %r" % (result,))
@@ -672,7 +677,7 @@ class HandlerCase(TestCase):
# so that subclasses can fill in defaults and account for other specialized behavior
#---------------------------------------------------------------
def populate_settings(self, kwds):
"subclassable method to populate default settings"
"""subclassable method to populate default settings"""
# use lower rounds settings for certain test modes
handler = self.handler
if 'rounds' in handler.setting_kwds and 'rounds' not in kwds:
@@ -687,35 +692,35 @@ class HandlerCase(TestCase):
if getattr(handler, "rounds_cost", None) == "log2":
df -= factor
else:
df = df//(1<<factor)
df //= (1<<factor)
kwds['rounds'] = max(3, mn, df)
def populate_context(self, secret, kwds):
"subclassable method allowing 'secret' to be encode context kwds"
"""subclassable method allowing 'secret' to be encode context kwds"""
return secret
def do_encrypt(self, secret, **kwds):
"call handler's encrypt method with specified options"
"""call handler's encrypt method with specified options"""
secret = self.populate_context(secret, kwds)
self.populate_settings(kwds)
return self.handler.encrypt(secret, **kwds)
def do_verify(self, secret, hash, **kwds):
"call handler's verify method"
"""call handler's verify method"""
secret = self.populate_context(secret, kwds)
return self.handler.verify(secret, hash, **kwds)
def do_identify(self, hash):
"call handler's identify method"
"""call handler's identify method"""
return self.handler.identify(hash)
def do_genconfig(self, **kwds):
"call handler's genconfig method with specified options"
"""call handler's genconfig method with specified options"""
self.populate_settings(kwds)
return self.handler.genconfig(**kwds)
def do_genhash(self, secret, config, **kwds):
"call handler's genhash method with specified options"
"""call handler's genhash method with specified options"""
secret = self.populate_context(secret, kwds)
return self.handler.genhash(secret, config, **kwds)
@@ -725,7 +730,7 @@ class HandlerCase(TestCase):
#---------------------------------------------------------------
@classmethod
def _enable_backend_case(cls, backend):
"helper for create_backend_cases(); returns reason to skip backend, or None"
"""helper for create_backend_cases(); returns reason to skip backend, or None"""
handler = cls.handler
if not is_default_backend(handler, backend) and not TEST_MODE("full"):
return "only default backend is being tested"
@@ -769,10 +774,16 @@ class HandlerCase(TestCase):
yield subcls
@classmethod
def find_crypt_replacement(cls):
"find other backend which can be used to mock the os_crypt backend"
def find_crypt_replacement(cls, fallback=False):
"""find other backend which can be used to mock the os_crypt backend"""
handler = cls.handler
for name in handler.backends:
assert "os_crypt" in handler.backends, "expected os_crypt to be present"
if fallback:
# NOTE: using list() because tuples lack .index under py25 (issue 58)
idx = list(handler.backends).index("os_crypt") + 1
else:
idx = 0
for name in handler.backends[idx:]:
if name != "os_crypt" and handler.has_backend(name):
return name
return None
@@ -796,7 +807,7 @@ class HandlerCase(TestCase):
# basic tests
#===================================================================
def test_01_required_attributes(self):
"validate required attributes"
"""validate required attributes"""
handler = self.handler
def ga(name):
return getattr(handler, name, None)
@@ -922,7 +933,7 @@ class HandlerCase(TestCase):
self.assertTrue(self.do_identify(result))
def test_04_hash_types(self):
"test hashes can be unicode or bytes"
"""test hashes can be unicode or bytes"""
# this runs through workflow similar to 03, but wraps
# everything using tonn() so we test unicode under py2,
# and bytes under py3.
@@ -951,7 +962,7 @@ class HandlerCase(TestCase):
self.assertTrue(self.do_identify(tonn(result)))
def test_05_backends(self):
"test multi-backend support"
"""test multi-backend support"""
handler = self.handler
if not hasattr(handler, "set_backend"):
raise self.skipTest("handler only has one backend")
@@ -998,9 +1009,8 @@ class HandlerCase(TestCase):
raise self.skipTest("handler doesn't provide salt info")
def test_10_optional_salt_attributes(self):
"validate optional salt attributes"
"""validate optional salt attributes"""
self.require_salt_info()
AssertionError = self.failureException
cls = self.handler
@@ -1042,7 +1052,7 @@ class HandlerCase(TestCase):
@property
def salt_bits(self):
"calculate number of salt bits in hash"
"""calculate number of salt bits in hash"""
# XXX: replace this with bitsize() method?
handler = self.handler
assert has_salt_info(handler), "need explicit bit-size for " + handler.name
@@ -1053,7 +1063,7 @@ class HandlerCase(TestCase):
log(len(handler.default_salt_chars), 2))
def test_11_unique_salt(self):
"test encrypt() / genconfig() creates new salt each time"
"""test encrypt() / genconfig() creates new salt each time"""
self.require_salt()
# odds of picking 'n' identical salts at random is '(.5**salt_bits)**n'.
# we want to pick the smallest N needed s.t. odds are <1/1000, just
@@ -1074,7 +1084,7 @@ class HandlerCase(TestCase):
sampler(lambda : self.do_encrypt("stub"))
def test_12_min_salt_size(self):
"test encrypt() / genconfig() honors min_salt_size"
"""test encrypt() / genconfig() honors min_salt_size"""
self.require_salt_info()
handler = self.handler
@@ -1100,7 +1110,7 @@ class HandlerCase(TestCase):
salt_size=min_size-1)
def test_13_max_salt_size(self):
"test encrypt() / genconfig() honors max_salt_size"
"""test encrypt() / genconfig() honors max_salt_size"""
self.require_salt_info()
handler = self.handler
@@ -1156,14 +1166,14 @@ class HandlerCase(TestCase):
fuzz_salts_need_bcrypt_repair = False
def prepare_salt(self, salt):
"prepare generated salt"
"""prepare generated salt"""
if self.fuzz_salts_need_bcrypt_repair:
from passlib.utils import bcrypt64
salt = bcrypt64.repair_unused(salt)
return salt
def test_14_salt_chars(self):
"test genconfig() honors salt_chars"
"""test genconfig() honors salt_chars"""
self.require_salt_info()
handler = self.handler
@@ -1193,7 +1203,7 @@ class HandlerCase(TestCase):
@property
def salt_type(self):
"hack to determine salt keyword's datatype"
"""hack to determine salt keyword's datatype"""
# NOTE: cisco_type7 uses 'int'
if getattr(self.handler, "_salt_is_bytes", False):
return bytes
@@ -1201,7 +1211,7 @@ class HandlerCase(TestCase):
return unicode
def test_15_salt_type(self):
"test non-string salt values"
"""test non-string salt values"""
self.require_salt()
salt_type = self.salt_type
@@ -1227,7 +1237,7 @@ class HandlerCase(TestCase):
raise self.skipTest("handler lacks rounds attributes")
def test_20_optional_rounds_attributes(self):
"validate optional rounds attributes"
"""validate optional rounds attributes"""
self.require_rounds_info()
cls = self.handler
@@ -1257,7 +1267,7 @@ class HandlerCase(TestCase):
raise AssertionError("unknown rounds cost constant: %r" % (cls.rounds_cost,))
def test_21_rounds_limits(self):
"test encrypt() / genconfig() honors rounds limits"
"""test encrypt() / genconfig() honors rounds limits"""
self.require_rounds_info()
handler = self.handler
min_rounds = handler.min_rounds
@@ -1294,7 +1304,7 @@ class HandlerCase(TestCase):
# idents
#===================================================================
def test_30_HasManyIdents(self):
"validate HasManyIdents configuration"
"""validate HasManyIdents configuration"""
cls = self.handler
if not isinstance(cls, type) or not issubclass(cls, uh.HasManyIdents):
raise self.skipTest("handler doesn't derive from HasManyIdents")
@@ -1349,7 +1359,7 @@ class HandlerCase(TestCase):
# passwords
#===================================================================
def test_60_secret_size(self):
"test password size limits"
"""test password size limits"""
sc = self.secret_size
base = "too many secrets" # 16 chars
alt = 'x' # char that's not in base string
@@ -1387,7 +1397,7 @@ class HandlerCase(TestCase):
"full password not used in digest")
def test_61_secret_case_sensitive(self):
"test password case sensitivity"
"""test password case sensitivity"""
hash_insensitive = self.secret_case_insensitive is True
verify_insensitive = self.secret_case_insensitive in [True,
"verify-only"]
@@ -1411,7 +1421,7 @@ class HandlerCase(TestCase):
"genhash() should be case sensitive")
def test_62_secret_border(self):
"test non-string passwords are rejected"
"""test non-string passwords are rejected"""
hash = self.get_sample_hash()[1]
# secret=None
@@ -1425,7 +1435,7 @@ class HandlerCase(TestCase):
self.assertRaises(TypeError, self.do_verify, 1, hash)
def test_63_large_secret(self):
"test MAX_PASSWORD_SIZE is enforced"
"""test MAX_PASSWORD_SIZE is enforced"""
from passlib.exc import PasswordSizeError
from passlib.utils import MAX_PASSWORD_SIZE
secret = '.' * (1+MAX_PASSWORD_SIZE)
@@ -1435,7 +1445,7 @@ class HandlerCase(TestCase):
self.assertRaises(PasswordSizeError, self.do_verify, secret, hash)
def test_64_forbidden_chars(self):
"test forbidden characters not allowed in password"
"""test forbidden characters not allowed in password"""
chars = self.forbidden_characters
if not chars:
raise self.skipTest("none listed")
@@ -1454,8 +1464,20 @@ class HandlerCase(TestCase):
secret = self.populate_context(secret, {})
return not is_ascii_safe(secret)
def expect_os_crypt_failure(self, secret):
"""
check if we're expecting potential verify failure due to crypt.crypt() encoding limitation
"""
if PY3 and self.backend == "os_crypt" and isinstance(secret, bytes):
try:
secret.decode("utf-8")
except UnicodeDecodeError:
return True
return False
def test_70_hashes(self):
"test known hashes"
"""test known hashes"""
# sanity check
self.assertTrue(self.known_correct_hashes or self.known_correct_configs,
"test must set at least one of 'known_correct_hashes' "
@@ -1471,27 +1493,34 @@ class HandlerCase(TestCase):
self.assertTrue(self.do_identify(hash),
"identify() failed to identify hash: %r" % (hash,))
# secret should verify successfully against hash
self.check_verify(secret, hash, "verify() of known hash failed: "
"secret=%r, hash=%r" % (secret, hash))
# check if what we're about to do is expected to fail due to crypt.crypt() limitation.
expect_os_crypt_failure = self.expect_os_crypt_failure(secret)
try:
# genhash() should reproduce same hash
result = self.do_genhash(secret, hash)
self.assertIsInstance(result, str,
"genhash() failed to return native string: %r" % (result,))
self.assertEqual(result, hash, "genhash() failed to reproduce "
"known hash: secret=%r, hash=%r: result=%r" %
(secret, hash, result))
# secret should verify successfully against hash
self.check_verify(secret, hash, "verify() of known hash failed: "
"secret=%r, hash=%r" % (secret, hash))
# genhash() should reproduce same hash
result = self.do_genhash(secret, hash)
self.assertIsInstance(result, str,
"genhash() failed to return native string: %r" % (result,))
self.assertEqual(result, hash, "genhash() failed to reproduce "
"known hash: secret=%r, hash=%r: result=%r" %
(secret, hash, result))
except MissingBackendError:
if not expect_os_crypt_failure:
raise
# would really like all handlers to have at least one 8-bit test vector
if not saw8bit:
warn("%s: no 8-bit secrets tested" % self.__class__)
def test_71_alternates(self):
"test known alternate hashes"
"""test known alternate hashes"""
if not self.known_alternate_hashes:
raise self.skipTest("no alternate hashes provided")
for alt, secret, hash in self.known_alternate_hashes:
# hash should be positively identified by handler
@@ -1512,7 +1541,7 @@ class HandlerCase(TestCase):
"result=%r" % (secret, alt, hash, result))
def test_72_configs(self):
"test known config strings"
"""test known config strings"""
# special-case handlers without settings
if not self.handler.setting_kwds:
self.assertFalse(self.known_correct_configs,
@@ -1547,7 +1576,7 @@ class HandlerCase(TestCase):
"result=%r" % (secret, config, hash, result))
def test_73_unidentified(self):
"test known unidentifiably-mangled strings"
"""test known unidentifiably-mangled strings"""
if not self.known_unidentified_hashes:
raise self.skipTest("no unidentified hashes provided")
for hash in self.known_unidentified_hashes:
@@ -1568,7 +1597,7 @@ class HandlerCase(TestCase):
"hash: %r" % (hash,))
def test_74_malformed(self):
"test known identifiable-but-malformed strings"
"""test known identifiable-but-malformed strings"""
if not self.known_malformed_hashes:
raise self.skipTest("no malformed hashes provided")
for hash in self.known_malformed_hashes:
@@ -1589,7 +1618,7 @@ class HandlerCase(TestCase):
"hash: %r" % (hash,))
def test_75_foreign(self):
"test known foreign hashes"
"""test known foreign hashes"""
if self.accepts_all_hashes:
raise self.skipTest("not applicable")
if not self.known_other_hashes:
@@ -1627,7 +1656,7 @@ class HandlerCase(TestCase):
"belonging to %s: %r" % (name, hash))
def test_76_hash_border(self):
"test non-string hashes are rejected"
"""test non-string hashes are rejected"""
#
# test hash=None is rejected (except if config=None)
#
@@ -1755,7 +1784,7 @@ class HandlerCase(TestCase):
@property
def max_fuzz_time(self):
"amount of time to spend on fuzz testing"
"""amount of time to spend on fuzz testing"""
value = float(os.environ.get("PASSLIB_TEST_FUZZ_TIME") or 0)
if value:
return value
@@ -1767,7 +1796,7 @@ class HandlerCase(TestCase):
return 5
def os_supports_ident(self, ident):
"whether native OS crypt() supports particular ident value"
"""whether native OS crypt() supports particular ident value"""
return True
#---------------------------------------------------------------
@@ -1819,13 +1848,13 @@ class HandlerCase(TestCase):
return check_default
def fuzz_verifier_crypt(self):
"test results against OS crypt()"
"""test results against OS crypt()"""
handler = self.handler
if self.using_patched_crypt or not has_crypt_support(handler):
return None
from crypt import crypt
def check_crypt(secret, hash):
"stdlib-crypt"
"""stdlib-crypt"""
if not self.os_supports_ident(hash):
return "skip"
secret = to_native_str(secret, self.fuzz_password_encoding)
@@ -1836,7 +1865,7 @@ class HandlerCase(TestCase):
# fuzz settings generation
#---------------------------------------------------------------
def get_fuzz_settings(self):
"generate random password and options for fuzz testing"
"""generate random password and options for fuzz testing"""
prefix = "fuzz_setting_"
kwds = {}
for name in dir(self):
@@ -1885,7 +1914,7 @@ class HandlerCase(TestCase):
# fuzz password generation
#---------------------------------------------------------------
def get_fuzz_password(self):
"generate random passwords for fuzz testing"
"""generate random passwords for fuzz testing"""
# occasionally try an empty password
if rng.random() < .0001:
return u('')
@@ -1897,11 +1926,11 @@ class HandlerCase(TestCase):
return getrandstr(rng, self.fuzz_password_alphabet, size)
def accept_fuzz_pair(self, secret, other):
"verify fuzz pair contains different passwords"
"""verify fuzz pair contains different passwords"""
return secret != other
def get_fuzz_password_pair(self):
"generate random password, and non-matching alternate password"
"""generate random password, and non-matching alternate password"""
secret = self.get_fuzz_password()
while True:
other = self.get_fuzz_password()
@@ -1977,12 +2006,23 @@ class OsCryptMixin(HandlerCase):
alt_backend = self.find_crypt_replacement()
if not alt_backend:
raise AssertionError("handler has no available backends!")
# create subclass of handler, which we swap to an alternate backend.
# NOTE: not switching original class's backend, since classes like bcrypt
# run some checks when backend is set, that can cause recursion error
# when orig backend is restored.
alt_handler = type('%s_%s_wrapper' % (handler.name, alt_backend), (handler,), {})
alt_handler._backend = None # ensure full backend load into subclass
alt_handler.set_backend(alt_backend)
import passlib.utils as mod
def crypt_stub(secret, hash):
with temporary_backend(handler, alt_backend):
hash = handler.genhash(secret, hash)
# with temporary_backend(alt_handler, alt_backend):
hash = alt_handler.genhash(secret, hash)
assert isinstance(hash, str)
return hash
self.addCleanup(setattr, mod, "_crypt", mod._crypt)
mod._crypt = crypt_stub
self.using_patched_crypt = True
@@ -1991,7 +2031,7 @@ class OsCryptMixin(HandlerCase):
# custom tests
#===================================================================
def _use_mock_crypt(self):
"patch safe_crypt() so it returns mock value"
"""patch safe_crypt() so it returns mock value"""
import passlib.utils as mod
if not self.using_patched_crypt:
self.addCleanup(setattr, mod, "_crypt", mod._crypt)
@@ -2002,7 +2042,7 @@ class OsCryptMixin(HandlerCase):
return setter
def test_80_faulty_crypt(self):
"test with faulty crypt()"
"""test with faulty crypt()"""
hash = self.get_sample_hash()[1]
exc_types = (AssertionError,)
setter = self._use_mock_crypt()
@@ -2020,11 +2060,11 @@ class OsCryptMixin(HandlerCase):
test(hash + 'x') # detect too long
def test_81_crypt_fallback(self):
"test per-call crypt() fallback"
"""test per-call crypt() fallback"""
# set safe_crypt to return None
setter = self._use_mock_crypt()
setter(None)
if self.find_crypt_replacement():
if self.find_crypt_replacement(fallback=True):
# handler should have a fallback to use
h1 = self.do_encrypt("stub")
h2 = self.do_genhash("stub", h1)
@@ -2039,7 +2079,7 @@ class OsCryptMixin(HandlerCase):
self.assertRaises(MissingBackendError, self.do_verify, 'stub', hash)
def test_82_crypt_support(self):
"test platform-specific crypt() support detection"
"""test platform-specific crypt() support detection"""
# NOTE: this is mainly just a sanity check to ensure the runtime
# detection is functioning correctly on some known platforms,
# so that I can feel more confident it'll work right on unknown ones.
@@ -2091,7 +2131,7 @@ class UserHandlerMixin(HandlerCase):
# custom tests
#===================================================================
def test_80_user(self):
"test user context keyword"
"""test user context keyword"""
handler = self.handler
password = 'stub'
hash = handler.encrypt(password, user=self.default_user)
@@ -2107,7 +2147,7 @@ class UserHandlerMixin(HandlerCase):
handler.verify(password, hash)
def test_81_user_case(self):
"test user case sensitivity"
"""test user case sensitivity"""
lower = self.default_user.lower()
upper = lower.upper()
hash = self.do_encrypt('stub', user=lower)
@@ -2119,7 +2159,7 @@ class UserHandlerMixin(HandlerCase):
"user should be case sensitive")
def test_82_user_salt(self):
"test user used as salt"
"""test user used as salt"""
config = self.do_genconfig()
h1 = self.do_genhash('stub', config, user='admin')
h2 = self.do_genhash('stub', config, user='admin')
@@ -2133,7 +2173,7 @@ class UserHandlerMixin(HandlerCase):
# override test helpers
#===================================================================
def populate_context(self, secret, kwds):
"insert username into kwds"
"""insert username into kwds"""
if isinstance(secret, tuple):
secret, user = secret
elif not self.requires_user:
@@ -2182,7 +2222,7 @@ class EncodingHandlerMixin(HandlerCase):
fuzz_password_alphabet = u('qwerty1234<>.@*#! \u00AC')
def populate_context(self, secret, kwds):
"insert encoding into kwds"
"""insert encoding into kwds"""
if isinstance(secret, tuple):
secret, encoding = secret
kwds.setdefault('encoding', encoding)