You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1172 lines
				
				45 KiB
			
		
		
			
		
	
	
					1172 lines
				
				45 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"""tests for passlib.util"""
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# imports
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								from __future__ import with_statement
							 | 
						||
| 
								 | 
							
								# core
							 | 
						||
| 
								 | 
							
								from functools import partial
							 | 
						||
| 
								 | 
							
								import warnings
							 | 
						||
| 
								 | 
							
								# site
							 | 
						||
| 
								 | 
							
								# pkg
							 | 
						||
| 
								 | 
							
								# module
							 | 
						||
| 
								 | 
							
								from passlib.utils import is_ascii_safe, to_bytes
							 | 
						||
| 
								 | 
							
								from passlib.utils.compat import irange, PY2, PY3, u, unicode, join_bytes, PYPY
							 | 
						||
| 
								 | 
							
								from passlib.tests.utils import TestCase, hb, run_with_fixed_seeds
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# byte funcs
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								class MiscTest(TestCase):
							 | 
						||
| 
								 | 
							
								    """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"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import compat
							 | 
						||
| 
								 | 
							
								        # "<module 'passlib.utils.compat' from 'passlib/utils/compat.pyc'>"
							 | 
						||
| 
								 | 
							
								        self.assertRegex(repr(compat),
							 | 
						||
| 
								 | 
							
								                         r"^<module 'passlib.utils.compat' from '.*?'>$")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test synthentic dir()
							 | 
						||
| 
								 | 
							
								        dir(compat)
							 | 
						||
| 
								 | 
							
								        self.assertTrue('UnicodeIO' in dir(compat))
							 | 
						||
| 
								 | 
							
								        self.assertTrue('irange' in dir(compat))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_classproperty(self):
							 | 
						||
| 
								 | 
							
								        from passlib.utils.decor import classproperty
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class test(object):
							 | 
						||
| 
								 | 
							
								            xvar = 1
							 | 
						||
| 
								 | 
							
								            @classproperty
							 | 
						||
| 
								 | 
							
								            def xprop(cls):
							 | 
						||
| 
								 | 
							
								                return cls.xvar
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(test.xprop, 1)
							 | 
						||
| 
								 | 
							
								        prop = test.__dict__['xprop']
							 | 
						||
| 
								 | 
							
								        self.assertIs(prop.im_func, prop.__func__)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_deprecated_function(self):
							 | 
						||
| 
								 | 
							
								        from passlib.utils.decor import deprecated_function
							 | 
						||
| 
								 | 
							
								        # NOTE: not comprehensive, just tests the basic behavior
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        @deprecated_function(deprecated="1.6", removed="1.8")
							 | 
						||
| 
								 | 
							
								        def test_func(*args):
							 | 
						||
| 
								 | 
							
								            """test docstring"""
							 | 
						||
| 
								 | 
							
								            return args
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertTrue(".. deprecated::" in test_func.__doc__)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with self.assertWarningList(dict(category=DeprecationWarning,
							 | 
						||
| 
								 | 
							
								                message="the function passlib.tests.test_utils.test_func() "
							 | 
						||
| 
								 | 
							
								                        "is deprecated as of Passlib 1.6, and will be "
							 | 
						||
| 
								 | 
							
								                        "removed in Passlib 1.8."
							 | 
						||
| 
								 | 
							
								                )):
							 | 
						||
| 
								 | 
							
								            self.assertEqual(test_func(1,2), (1,2))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_memoized_property(self):
							 | 
						||
| 
								 | 
							
								        from passlib.utils.decor import memoized_property
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        class dummy(object):
							 | 
						||
| 
								 | 
							
								            counter = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            @memoized_property
							 | 
						||
| 
								 | 
							
								            def value(self):
							 | 
						||
| 
								 | 
							
								                value = self.counter
							 | 
						||
| 
								 | 
							
								                self.counter = value+1
							 | 
						||
| 
								 | 
							
								                return value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        d = dummy()
							 | 
						||
| 
								 | 
							
								        self.assertEqual(d.value, 0)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(d.value, 0)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(d.counter, 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        prop = dummy.value
							 | 
						||
| 
								 | 
							
								        if not PY3:
							 | 
						||
| 
								 | 
							
								            self.assertIs(prop.im_func, prop.__func__)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_getrandbytes(self):
							 | 
						||
| 
								 | 
							
								        """getrandbytes()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import getrandbytes
							 | 
						||
| 
								 | 
							
								        wrapper = partial(getrandbytes, self.getRandom())
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(wrapper(0)), 0)
							 | 
						||
| 
								 | 
							
								        a = wrapper(10)
							 | 
						||
| 
								 | 
							
								        b = wrapper(10)
							 | 
						||
| 
								 | 
							
								        self.assertIsInstance(a, bytes)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(a), 10)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(b), 10)
							 | 
						||
| 
								 | 
							
								        self.assertNotEqual(a, b)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @run_with_fixed_seeds(count=1024)
							 | 
						||
| 
								 | 
							
								    def test_getrandstr(self, seed):
							 | 
						||
| 
								 | 
							
								        """getrandstr()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import getrandstr
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        wrapper = partial(getrandstr, self.getRandom(seed=seed))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # count 0
							 | 
						||
| 
								 | 
							
								        self.assertEqual(wrapper('abc',0), '')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # count <0
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, wrapper, 'abc', -1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # letters 0
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, wrapper, '', 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # letters 1
							 | 
						||
| 
								 | 
							
								        self.assertEqual(wrapper('a', 5), 'aaaaa')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # NOTE: the following parts are non-deterministic,
							 | 
						||
| 
								 | 
							
								        #       with a small chance of failure (outside chance it may pick
							 | 
						||
| 
								 | 
							
								        #       a string w/o one char, even more remote chance of picking
							 | 
						||
| 
								 | 
							
								        #       same string).  to combat this, we run it against multiple
							 | 
						||
| 
								 | 
							
								        #       fixed seeds (using run_with_fixed_seeds decorator),
							 | 
						||
| 
								 | 
							
								        #       and hope that they're sufficient to test the range of behavior.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # letters
							 | 
						||
| 
								 | 
							
								        x = wrapper(u('abc'), 32)
							 | 
						||
| 
								 | 
							
								        y = wrapper(u('abc'), 32)
							 | 
						||
| 
								 | 
							
								        self.assertIsInstance(x, unicode)
							 | 
						||
| 
								 | 
							
								        self.assertNotEqual(x,y)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sorted(set(x)), [u('a'),u('b'),u('c')])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # bytes
							 | 
						||
| 
								 | 
							
								        x = wrapper(b'abc', 32)
							 | 
						||
| 
								 | 
							
								        y = wrapper(b'abc', 32)
							 | 
						||
| 
								 | 
							
								        self.assertIsInstance(x, bytes)
							 | 
						||
| 
								 | 
							
								        self.assertNotEqual(x,y)
							 | 
						||
| 
								 | 
							
								        # NOTE: decoding this due to py3 bytes
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sorted(set(x.decode("ascii"))), [u('a'),u('b'),u('c')])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_generate_password(self):
							 | 
						||
| 
								 | 
							
								        """generate_password()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import generate_password
							 | 
						||
| 
								 | 
							
								        warnings.filterwarnings("ignore", "The function.*generate_password\(\) is deprecated")
							 | 
						||
| 
								 | 
							
								        self.assertEqual(len(generate_password(15)), 15)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_is_crypt_context(self):
							 | 
						||
| 
								 | 
							
								        """test is_crypt_context()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import is_crypt_context
							 | 
						||
| 
								 | 
							
								        from passlib.context import CryptContext
							 | 
						||
| 
								 | 
							
								        cc = CryptContext(["des_crypt"])
							 | 
						||
| 
								 | 
							
								        self.assertTrue(is_crypt_context(cc))
							 | 
						||
| 
								 | 
							
								        self.assertFalse(not is_crypt_context(cc))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_genseed(self):
							 | 
						||
| 
								 | 
							
								        """test genseed()"""
							 | 
						||
| 
								 | 
							
								        import random
							 | 
						||
| 
								 | 
							
								        from passlib.utils import genseed
							 | 
						||
| 
								 | 
							
								        rng = random.Random(genseed())
							 | 
						||
| 
								 | 
							
								        a = rng.randint(0, 10**10)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        rng = random.Random(genseed())
							 | 
						||
| 
								 | 
							
								        b = rng.randint(0, 10**10)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertNotEqual(a,b)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        rng.seed(genseed(rng))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_crypt(self):
							 | 
						||
| 
								 | 
							
								        """test crypt.crypt() wrappers"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import has_crypt, safe_crypt, test_crypt
							 | 
						||
| 
								 | 
							
								        from passlib.registry import get_supported_os_crypt_schemes, get_crypt_handler
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test everything is disabled
							 | 
						||
| 
								 | 
							
								        supported = get_supported_os_crypt_schemes()
							 | 
						||
| 
								 | 
							
								        if not has_crypt:
							 | 
						||
| 
								 | 
							
								            self.assertEqual(supported, ())
							 | 
						||
| 
								 | 
							
								            self.assertEqual(safe_crypt("test", "aa"), None)
							 | 
						||
| 
								 | 
							
								            self.assertFalse(test_crypt("test", "aaqPiZY5xR5l."))  # des_crypt() hash of "test"
							 | 
						||
| 
								 | 
							
								            raise self.skipTest("crypt.crypt() not available")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # expect there to be something supported, if crypt() is present
							 | 
						||
| 
								 | 
							
								        if not supported:
							 | 
						||
| 
								 | 
							
								            # NOTE: failures here should be investigated.  usually means one of:
							 | 
						||
| 
								 | 
							
								            # 1) at least one of passlib's os_crypt detection routines is giving false negative
							 | 
						||
| 
								 | 
							
								            # 2) crypt() ONLY supports some hash alg which passlib doesn't know about
							 | 
						||
| 
								 | 
							
								            # 3) crypt() is present but completely disabled (never encountered this yet)
							 | 
						||
| 
								 | 
							
								            raise self.fail("crypt() present, but no supported schemes found!")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # pick cheap alg if possible, with minimum rounds, to speed up this test.
							 | 
						||
| 
								 | 
							
								        # NOTE: trusting hasher class works properly (should have been verified using it's own UTs)
							 | 
						||
| 
								 | 
							
								        for scheme in ("md5_crypt", "sha256_crypt"):
							 | 
						||
| 
								 | 
							
								            if scheme in supported:
							 | 
						||
| 
								 | 
							
								                break
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            scheme = supported[-1]
							 | 
						||
| 
								 | 
							
								        hasher = get_crypt_handler(scheme)
							 | 
						||
| 
								 | 
							
								        if getattr(hasher, "min_rounds", None):
							 | 
						||
| 
								 | 
							
								            hasher = hasher.using(rounds=hasher.min_rounds)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # helpers to generate hashes & config strings to work with
							 | 
						||
| 
								 | 
							
								        def get_hash(secret):
							 | 
						||
| 
								 | 
							
								            assert isinstance(secret, unicode)
							 | 
						||
| 
								 | 
							
								            hash = hasher.hash(secret)
							 | 
						||
| 
								 | 
							
								            if isinstance(hash, bytes):  # py2
							 | 
						||
| 
								 | 
							
								                hash = hash.decode("utf-8")
							 | 
						||
| 
								 | 
							
								            assert isinstance(hash, unicode)
							 | 
						||
| 
								 | 
							
								            return hash
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test ascii password & return type
							 | 
						||
| 
								 | 
							
								        s1 = u("test")
							 | 
						||
| 
								 | 
							
								        h1 = get_hash(s1)
							 | 
						||
| 
								 | 
							
								        result = safe_crypt(s1, h1)
							 | 
						||
| 
								 | 
							
								        self.assertIsInstance(result, unicode)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(result, h1)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(safe_crypt(to_bytes(s1), to_bytes(h1)), h1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # make sure crypt doesn't just blindly return h1 for whatever we pass in
							 | 
						||
| 
								 | 
							
								        h1x = h1[:-2] + 'xx'
							 | 
						||
| 
								 | 
							
								        self.assertEqual(safe_crypt(s1, h1x), h1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test utf-8 / unicode password
							 | 
						||
| 
								 | 
							
								        s2 = u('test\u1234')
							 | 
						||
| 
								 | 
							
								        h2 = get_hash(s2)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(safe_crypt(s2, h2), h2)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(safe_crypt(to_bytes(s2), to_bytes(h2)), h2)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test rejects null chars in password
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, safe_crypt, '\x00', h1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check test_crypt()
							 | 
						||
| 
								 | 
							
								        self.assertTrue(test_crypt("test", h1))
							 | 
						||
| 
								 | 
							
								        self.assertFalse(test_crypt("test", h1x))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check crypt returning variant error indicators
							 | 
						||
| 
								 | 
							
								        # some platforms return None on errors, others empty string,
							 | 
						||
| 
								 | 
							
								        # The BSDs in some cases return ":"
							 | 
						||
| 
								 | 
							
								        import passlib.utils as mod
							 | 
						||
| 
								 | 
							
								        orig = mod._crypt
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            retval = None
							 | 
						||
| 
								 | 
							
								            mod._crypt = lambda secret, hash: retval
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for retval in [None, "", ":", ":0", "*0"]:
							 | 
						||
| 
								 | 
							
								                self.assertEqual(safe_crypt("test", h1), None)
							 | 
						||
| 
								 | 
							
								                self.assertFalse(test_crypt("test", h1))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            retval = 'xxx'
							 | 
						||
| 
								 | 
							
								            self.assertEqual(safe_crypt("test", h1), "xxx")
							 | 
						||
| 
								 | 
							
								            self.assertFalse(test_crypt("test", h1))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            mod._crypt = orig
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_consteq(self):
							 | 
						||
| 
								 | 
							
								        """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, str_consteq
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # ensure error raises for wrong types
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, u(''), b'')
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, u(''), 1)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, u(''), None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, b'', u(''))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, b'', 1)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, b'', None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, None, u(''))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, None, b'')
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, 1, u(''))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, consteq, 1, b'')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        def consteq_supports_string(value):
							 | 
						||
| 
								 | 
							
								            # under PY2, it supports all unicode strings (when present at all),
							 | 
						||
| 
								 | 
							
								            # under PY3, compare_digest() only supports ascii unicode strings.
							 | 
						||
| 
								 | 
							
								            # confirmed for: cpython 2.7.9, cpython 3.4, pypy, pypy3, pyston
							 | 
						||
| 
								 | 
							
								            return (consteq is str_consteq or PY2 or is_ascii_safe(value))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check equal inputs compare correctly
							 | 
						||
| 
								 | 
							
								        for value in [
							 | 
						||
| 
								 | 
							
								                u("a"),
							 | 
						||
| 
								 | 
							
								                u("abc"),
							 | 
						||
| 
								 | 
							
								                u("\xff\xa2\x12\x00")*10,
							 | 
						||
| 
								 | 
							
								            ]:
							 | 
						||
| 
								 | 
							
								            if consteq_supports_string(value):
							 | 
						||
| 
								 | 
							
								                self.assertTrue(consteq(value, value), "value %r:" % (value,))
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.assertRaises(TypeError, consteq, value, value)
							 | 
						||
| 
								 | 
							
								            self.assertTrue(str_consteq(value, value), "value %r:" % (value,))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            value = value.encode("latin-1")
							 | 
						||
| 
								 | 
							
								            self.assertTrue(consteq(value, value), "value %r:" % (value,))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check non-equal inputs compare correctly
							 | 
						||
| 
								 | 
							
								        for l,r in [
							 | 
						||
| 
								 | 
							
								                # check same-size comparisons with differing contents fail.
							 | 
						||
| 
								 | 
							
								                (u("a"),         u("c")),
							 | 
						||
| 
								 | 
							
								                (u("abcabc"),    u("zbaabc")),
							 | 
						||
| 
								 | 
							
								                (u("abcabc"),    u("abzabc")),
							 | 
						||
| 
								 | 
							
								                (u("abcabc"),    u("abcabz")),
							 | 
						||
| 
								 | 
							
								                ((u("\xff\xa2\x12\x00")*10)[:-1] + u("\x01"),
							 | 
						||
| 
								 | 
							
								                    u("\xff\xa2\x12\x00")*10),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # check different-size comparisons fail.
							 | 
						||
| 
								 | 
							
								                (u(""),       u("a")),
							 | 
						||
| 
								 | 
							
								                (u("abc"),    u("abcdef")),
							 | 
						||
| 
								 | 
							
								                (u("abc"),    u("defabc")),
							 | 
						||
| 
								 | 
							
								                (u("qwertyuiopasdfghjklzxcvbnm"), u("abc")),
							 | 
						||
| 
								 | 
							
								            ]:
							 | 
						||
| 
								 | 
							
								            if consteq_supports_string(l) and consteq_supports_string(r):
							 | 
						||
| 
								 | 
							
								                self.assertFalse(consteq(l, r), "values %r %r:" % (l,r))
							 | 
						||
| 
								 | 
							
								                self.assertFalse(consteq(r, l), "values %r %r:" % (r,l))
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.assertRaises(TypeError, consteq, l, r)
							 | 
						||
| 
								 | 
							
								                self.assertRaises(TypeError, consteq, r, l)
							 | 
						||
| 
								 | 
							
								            self.assertFalse(str_consteq(l, r), "values %r %r:" % (l,r))
							 | 
						||
| 
								 | 
							
								            self.assertFalse(str_consteq(r, l), "values %r %r:" % (r,l))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            l = l.encode("latin-1")
							 | 
						||
| 
								 | 
							
								            r = r.encode("latin-1")
							 | 
						||
| 
								 | 
							
								            self.assertFalse(consteq(l, r), "values %r %r:" % (l,r))
							 | 
						||
| 
								 | 
							
								            self.assertFalse(consteq(r, l), "values %r %r:" % (r,l))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # TODO: add some tests to ensure we take THETA(strlen) time.
							 | 
						||
| 
								 | 
							
								        # this might be hard to do reproducably.
							 | 
						||
| 
								 | 
							
								        # NOTE: below code was used to generate stats for analysis
							 | 
						||
| 
								 | 
							
								        ##from math import log as logb
							 | 
						||
| 
								 | 
							
								        ##import timeit
							 | 
						||
| 
								 | 
							
								        ##multipliers = [ 1<<s for s in irange(9)]
							 | 
						||
| 
								 | 
							
								        ##correct =   u"abcdefgh"*(1<<4)
							 | 
						||
| 
								 | 
							
								        ##incorrect = u"abcdxfgh"
							 | 
						||
| 
								 | 
							
								        ##print
							 | 
						||
| 
								 | 
							
								        ##first = True
							 | 
						||
| 
								 | 
							
								        ##for run in irange(1):
							 | 
						||
| 
								 | 
							
								        ##    times = []
							 | 
						||
| 
								 | 
							
								        ##    chars = []
							 | 
						||
| 
								 | 
							
								        ##    for m in multipliers:
							 | 
						||
| 
								 | 
							
								        ##        supplied = incorrect * m
							 | 
						||
| 
								 | 
							
								        ##        def test():
							 | 
						||
| 
								 | 
							
								        ##            self.assertFalse(consteq(supplied,correct))
							 | 
						||
| 
								 | 
							
								        ##            ##self.assertFalse(supplied == correct)
							 | 
						||
| 
								 | 
							
								        ##        times.append(timeit.timeit(test, number=100000))
							 | 
						||
| 
								 | 
							
								        ##        chars.append(len(supplied))
							 | 
						||
| 
								 | 
							
								        ##    # output for wolfram alpha
							 | 
						||
| 
								 | 
							
								        ##    print ", ".join("{%r, %r}" % (c,round(t,4)) for c,t in zip(chars,times))
							 | 
						||
| 
								 | 
							
								        ##    def scale(c):
							 | 
						||
| 
								 | 
							
								        ##        return logb(c,2)
							 | 
						||
| 
								 | 
							
								        ##    print ", ".join("{%r, %r}" % (scale(c),round(t,4)) for c,t in zip(chars,times))
							 | 
						||
| 
								 | 
							
								        ##    # output for spreadsheet
							 | 
						||
| 
								 | 
							
								        ##    ##if first:
							 | 
						||
| 
								 | 
							
								        ##    ##    print "na, " + ", ".join(str(c) for c in chars)
							 | 
						||
| 
								 | 
							
								        ##    ##    first = False
							 | 
						||
| 
								 | 
							
								        ##    ##print ", ".join(str(c) for c in [run] + times)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_saslprep(self):
							 | 
						||
| 
								 | 
							
								        """test saslprep() unicode normalizer"""
							 | 
						||
| 
								 | 
							
								        self.require_stringprep()
							 | 
						||
| 
								 | 
							
								        from passlib.utils import saslprep as sp
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # invalid types
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, sp, None)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, sp, 1)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, sp, b'')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # empty strings
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u('')), u(''))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u('\u00AD')), u(''))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # verify B.1 chars are stripped,
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("$\u00AD$\u200D$")), u("$$$"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # verify C.1.2 chars are replaced with space
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("$ $\u00A0$\u3000$")), u("$ $ $ $"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # verify normalization to KC
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("a\u0300")), u("\u00E0"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("\u00E0")), u("\u00E0"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # verify various forbidden characters
							 | 
						||
| 
								 | 
							
								            # control chars
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u0000"))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u007F"))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u180E"))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\uFFF9"))
							 | 
						||
| 
								 | 
							
								            # private use
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\uE000"))
							 | 
						||
| 
								 | 
							
								            # non-characters
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\uFDD0"))
							 | 
						||
| 
								 | 
							
								            # surrogates
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\uD800"))
							 | 
						||
| 
								 | 
							
								            # non-plaintext chars
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\uFFFD"))
							 | 
						||
| 
								 | 
							
								            # non-canon
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u2FF0"))
							 | 
						||
| 
								 | 
							
								            # change display properties
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u200E"))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u206F"))
							 | 
						||
| 
								 | 
							
								            # unassigned code points (as of unicode 3.2)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u0900"))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\uFFF8"))
							 | 
						||
| 
								 | 
							
								            # tagging characters
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\U000e0001"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # verify bidi behavior
							 | 
						||
| 
								 | 
							
								            # if starts with R/AL -- must end with R/AL
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u0627\u0031"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("\u0627")), u("\u0627"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("\u0627\u0628")), u("\u0627\u0628"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("\u0627\u0031\u0628")), u("\u0627\u0031\u0628"))
							 | 
						||
| 
								 | 
							
								            # if starts with R/AL --  cannot contain L
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u0627\u0041\u0628"))
							 | 
						||
| 
								 | 
							
								            # if doesn't start with R/AL -- can contain R/AL, but L & EN allowed
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("x\u0627z"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("x\u0041z")), u("x\u0041z"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #------------------------------------------------------
							 | 
						||
| 
								 | 
							
								        # examples pulled from external sources, to be thorough
							 | 
						||
| 
								 | 
							
								        #------------------------------------------------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # rfc 4031 section 3 examples
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("I\u00ADX")), u("IX")) # strip SHY
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("user")), u("user")) # unchanged
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("USER")), u("USER")) # case preserved
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("\u00AA")), u("a")) # normalize to KC form
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("\u2168")), u("IX")) # normalize to KC form
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u0007")) # forbid control chars
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u0627\u0031")) # invalid bidi
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # rfc 3454 section 6 examples
							 | 
						||
| 
								 | 
							
								            # starts with RAL char, must end with RAL char
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, sp, u("\u0627\u0031"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(sp(u("\u0627\u0031\u0628")), u("\u0627\u0031\u0628"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_splitcomma(self):
							 | 
						||
| 
								 | 
							
								        from passlib.utils import splitcomma
							 | 
						||
| 
								 | 
							
								        self.assertEqual(splitcomma(""), [])
							 | 
						||
| 
								 | 
							
								        self.assertEqual(splitcomma(","), [])
							 | 
						||
| 
								 | 
							
								        self.assertEqual(splitcomma("a"), ['a'])
							 | 
						||
| 
								 | 
							
								        self.assertEqual(splitcomma(" a , "), ['a'])
							 | 
						||
| 
								 | 
							
								        self.assertEqual(splitcomma(" a , b"), ['a', 'b'])
							 | 
						||
| 
								 | 
							
								        self.assertEqual(splitcomma(" a, b, "), ['a', 'b'])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_utf8_truncate(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        utf8_truncate()
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        from passlib.utils import utf8_truncate
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # run through a bunch of reference strings,
							 | 
						||
| 
								 | 
							
								        # and make sure they truncate properly across all possible indexes
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        for source in [
							 | 
						||
| 
								 | 
							
								            # empty string
							 | 
						||
| 
								 | 
							
								            b"",
							 | 
						||
| 
								 | 
							
								            # strings w/ only single-byte chars
							 | 
						||
| 
								 | 
							
								            b"1",
							 | 
						||
| 
								 | 
							
								            b"123",
							 | 
						||
| 
								 | 
							
								            b'\x1a',
							 | 
						||
| 
								 | 
							
								            b'\x1a' * 10,
							 | 
						||
| 
								 | 
							
								            b'\x7f',
							 | 
						||
| 
								 | 
							
								            b'\x7f' * 10,
							 | 
						||
| 
								 | 
							
								            # strings w/ properly formed UTF8 continuation sequences
							 | 
						||
| 
								 | 
							
								            b'a\xc2\xa0\xc3\xbe\xc3\xbe',
							 | 
						||
| 
								 | 
							
								            b'abcdefghjusdfaoiu\xc2\xa0\xc3\xbe\xc3\xbedsfioauweoiruer',
							 | 
						||
| 
								 | 
							
								        ]:
							 | 
						||
| 
								 | 
							
								            source.decode("utf-8") # sanity check - should always be valid UTF8
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            end = len(source)
							 | 
						||
| 
								 | 
							
								            for idx in range(end + 16):
							 | 
						||
| 
								 | 
							
								                prefix = "source=%r index=%r: " % (source, idx)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                result = utf8_truncate(source, idx)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # result should always be valid utf-8
							 | 
						||
| 
								 | 
							
								                result.decode("utf-8")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # result should never be larger than source
							 | 
						||
| 
								 | 
							
								                self.assertLessEqual(len(result), end, msg=prefix)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # result should always be in range(idx, idx+4)
							 | 
						||
| 
								 | 
							
								                self.assertGreaterEqual(len(result), min(idx, end), msg=prefix)
							 | 
						||
| 
								 | 
							
								                self.assertLess(len(result), min(idx + 4, end + 1), msg=prefix)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # should be strict prefix of source
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result, source[:len(result)], msg=prefix)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # malformed utf8 --
							 | 
						||
| 
								 | 
							
								        # strings w/ only initial chars (should cut just like single-byte chars)
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        for source in [
							 | 
						||
| 
								 | 
							
								            b'\xca',
							 | 
						||
| 
								 | 
							
								            b'\xca' * 10,
							 | 
						||
| 
								 | 
							
								            # also test null bytes (not valid utf8, but this func should treat them like ascii)
							 | 
						||
| 
								 | 
							
								            b'\x00',
							 | 
						||
| 
								 | 
							
								            b'\x00' * 10,
							 | 
						||
| 
								 | 
							
								        ]:
							 | 
						||
| 
								 | 
							
								            end = len(source)
							 | 
						||
| 
								 | 
							
								            for idx in range(end + 16):
							 | 
						||
| 
								 | 
							
								                prefix = "source=%r index=%r: " % (source, idx)
							 | 
						||
| 
								 | 
							
								                result = utf8_truncate(source, idx)
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result, source[:idx], msg=prefix)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # malformed utf8 --
							 | 
						||
| 
								 | 
							
								        # strings w/ only continuation chars (should cut at index+3)
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        for source in [
							 | 
						||
| 
								 | 
							
								            b'\xaa',
							 | 
						||
| 
								 | 
							
								            b'\xaa' * 10,
							 | 
						||
| 
								 | 
							
								        ]:
							 | 
						||
| 
								 | 
							
								            end = len(source)
							 | 
						||
| 
								 | 
							
								            for idx in range(end + 16):
							 | 
						||
| 
								 | 
							
								                prefix = "source=%r index=%r: " % (source, idx)
							 | 
						||
| 
								 | 
							
								                result = utf8_truncate(source, idx)
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result, source[:idx+3], msg=prefix)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # string w/ some invalid utf8 --
							 | 
						||
| 
								 | 
							
								        # * \xaa byte is too many continuation byte after \xff start byte
							 | 
						||
| 
								 | 
							
								        # * \xab byte doesn't have preceding start byte
							 | 
						||
| 
								 | 
							
								        # XXX: could also test continuation bytes w/o start byte, WITHIN the string.
							 | 
						||
| 
								 | 
							
								        #      but think this covers edges well enough...
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        source = b'MN\xff\xa0\xa1\xa2\xaaOP\xab'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 0), b'')  # index="M", stops there
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 1), b'M')  # index="N", stops there
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 2), b'MN')  # index="\xff", stops there
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 3),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2')  # index="\xa0", runs out after index+3="\xa2"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 4),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2\xaa')  # index="\xa1", runs out after index+3="\xaa"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 5),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2\xaa')  # index="\xa2", stops before "O"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 6),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2\xaa')  # index="\xaa", stops before "O"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 7),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2\xaa')  # index="O", stops there
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 8),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2\xaaO')  # index="P", stops there
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 9),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2\xaaOP\xab')  # index="\xab", runs out at end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 10),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2\xaaOP\xab')  # index=end
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertEqual(utf8_truncate(source, 11),
							 | 
						||
| 
								 | 
							
								                         b'MN\xff\xa0\xa1\xa2\xaaOP\xab')  # index=end+1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# byte/unicode helpers
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								class CodecTest(TestCase):
							 | 
						||
| 
								 | 
							
								    """tests bytes/unicode helpers in passlib.utils"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_bytes(self):
							 | 
						||
| 
								 | 
							
								        """test b() helper, bytes and native str type"""
							 | 
						||
| 
								 | 
							
								        if PY3:
							 | 
						||
| 
								 | 
							
								            import builtins
							 | 
						||
| 
								 | 
							
								            self.assertIs(bytes, builtins.bytes)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            import __builtin__ as builtins
							 | 
						||
| 
								 | 
							
								            self.assertIs(bytes, builtins.str)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertIsInstance(b'', bytes)
							 | 
						||
| 
								 | 
							
								        self.assertIsInstance(b'\x00\xff', bytes)
							 | 
						||
| 
								 | 
							
								        if PY3:
							 | 
						||
| 
								 | 
							
								            self.assertEqual(b'\x00\xff'.decode("latin-1"), "\x00\xff")
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self.assertEqual(b'\x00\xff', "\x00\xff")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_to_bytes(self):
							 | 
						||
| 
								 | 
							
								        """test to_bytes()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import to_bytes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check unicode inputs
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_bytes(u('abc')),                  b'abc')
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_bytes(u('\x00\xff')),             b'\x00\xc3\xbf')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check unicode w/ encodings
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_bytes(u('\x00\xff'), 'latin-1'),  b'\x00\xff')
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, to_bytes, u('\x00\xff'), 'ascii')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check bytes inputs
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_bytes(b'abc'),                b'abc')
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_bytes(b'\x00\xff'),           b'\x00\xff')
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_bytes(b'\x00\xc3\xbf'),       b'\x00\xc3\xbf')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check byte inputs ignores enocding
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_bytes(b'\x00\xc3\xbf', "latin-1"),
							 | 
						||
| 
								 | 
							
								                                                            b'\x00\xc3\xbf')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check bytes transcoding
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_bytes(b'\x00\xc3\xbf', "latin-1", "", "utf-8"),
							 | 
						||
| 
								 | 
							
								                                                            b'\x00\xff')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check other
							 | 
						||
| 
								 | 
							
								        self.assertRaises(AssertionError, to_bytes, 'abc', None)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, to_bytes, None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_to_unicode(self):
							 | 
						||
| 
								 | 
							
								        """test to_unicode()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import to_unicode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check unicode inputs
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_unicode(u('abc')),                u('abc'))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_unicode(u('\x00\xff')),           u('\x00\xff'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check unicode input ignores encoding
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_unicode(u('\x00\xff'), "ascii"),  u('\x00\xff'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check bytes input
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_unicode(b'abc'),              u('abc'))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_unicode(b'\x00\xc3\xbf'),     u('\x00\xff'))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_unicode(b'\x00\xff', 'latin-1'),
							 | 
						||
| 
								 | 
							
								                                                            u('\x00\xff'))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, to_unicode, b'\x00\xff')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check other
							 | 
						||
| 
								 | 
							
								        self.assertRaises(AssertionError, to_unicode, 'abc', None)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, to_unicode, None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_to_native_str(self):
							 | 
						||
| 
								 | 
							
								        """test to_native_str()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import to_native_str
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test plain ascii
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_native_str(u('abc'), 'ascii'), 'abc')
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_native_str(b'abc', 'ascii'), 'abc')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test invalid ascii
							 | 
						||
| 
								 | 
							
								        if PY3:
							 | 
						||
| 
								 | 
							
								            self.assertEqual(to_native_str(u('\xE0'), 'ascii'), '\xE0')
							 | 
						||
| 
								 | 
							
								            self.assertRaises(UnicodeDecodeError, to_native_str, b'\xC3\xA0',
							 | 
						||
| 
								 | 
							
								                              'ascii')
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self.assertRaises(UnicodeEncodeError, to_native_str, u('\xE0'),
							 | 
						||
| 
								 | 
							
								                              'ascii')
							 | 
						||
| 
								 | 
							
								            self.assertEqual(to_native_str(b'\xC3\xA0', 'ascii'), '\xC3\xA0')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test latin-1
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_native_str(u('\xE0'), 'latin-1'), '\xE0')
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_native_str(b'\xE0', 'latin-1'), '\xE0')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test utf-8
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_native_str(u('\xE0'), 'utf-8'),
							 | 
						||
| 
								 | 
							
								                         '\xE0' if PY3 else '\xC3\xA0')
							 | 
						||
| 
								 | 
							
								        self.assertEqual(to_native_str(b'\xC3\xA0', 'utf-8'),
							 | 
						||
| 
								 | 
							
								                         '\xE0' if PY3 else '\xC3\xA0')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # other types rejected
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, to_native_str, None, 'ascii')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_is_ascii_safe(self):
							 | 
						||
| 
								 | 
							
								        """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")))
							 | 
						||
| 
								 | 
							
								        self.assertFalse(is_ascii_safe(b"\x00abc\x80"))
							 | 
						||
| 
								 | 
							
								        self.assertFalse(is_ascii_safe(u("\x00abc\x80")))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_is_same_codec(self):
							 | 
						||
| 
								 | 
							
								        """test is_same_codec()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils import is_same_codec
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertTrue(is_same_codec(None, None))
							 | 
						||
| 
								 | 
							
								        self.assertFalse(is_same_codec(None, 'ascii'))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertTrue(is_same_codec("ascii", "ascii"))
							 | 
						||
| 
								 | 
							
								        self.assertTrue(is_same_codec("ascii", "ASCII"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertTrue(is_same_codec("utf-8", "utf-8"))
							 | 
						||
| 
								 | 
							
								        self.assertTrue(is_same_codec("utf-8", "utf8"))
							 | 
						||
| 
								 | 
							
								        self.assertTrue(is_same_codec("utf-8", "UTF_8"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertFalse(is_same_codec("ascii", "utf-8"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# base64engine
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								class Base64EngineTest(TestCase):
							 | 
						||
| 
								 | 
							
								    """test standalone parts of Base64Engine"""
							 | 
						||
| 
								 | 
							
								    # NOTE: most Base64Engine testing done via _Base64Test subclasses below.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_constructor(self):
							 | 
						||
| 
								 | 
							
								        from passlib.utils.binary import Base64Engine, AB64_CHARS
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # bad charmap type
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, Base64Engine, 1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # bad charmap size
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, Base64Engine, AB64_CHARS[:-1])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # dup charmap letter
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, Base64Engine, AB64_CHARS[:-1] + "A")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_ab64_decode(self):
							 | 
						||
| 
								 | 
							
								        """ab64_decode()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils.binary import ab64_decode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # accept bytes or unicode
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_decode(b"abc"), hb("69b7"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_decode(u("abc")), hb("69b7"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # reject non-ascii unicode
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, ab64_decode, u("ab\xff"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # underlying a2b_ascii treats non-base64 chars as "Incorrect padding"
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, ab64_decode, b"ab\xff")
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, ab64_decode, b"ab!")
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, ab64_decode, u("ab!"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # insert correct padding, handle dirty padding bits
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_decode(b"abcd"), hb("69b71d"))  # 0 mod 4
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, ab64_decode, b"abcde")  # 1 mod 4
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_decode(b"abcdef"), hb("69b71d79"))  # 2 mod 4, dirty padding bits
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_decode(b"abcdeQ"), hb("69b71d79"))  # 2 mod 4, clean padding bits
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_decode(b"abcdefg"), hb("69b71d79f8"))  # 3 mod 4, clean padding bits
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # support "./" or "+/" altchars
							 | 
						||
| 
								 | 
							
								        # (lets us transition to "+/" representation, merge w/ b64s_decode)
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_decode(b"ab+/"), hb("69bfbf"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_decode(b"ab./"), hb("69bfbf"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_ab64_encode(self):
							 | 
						||
| 
								 | 
							
								        """ab64_encode()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils.binary import ab64_encode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # accept bytes
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_encode(hb("69b7")), b"abc")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # reject unicode
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError if PY3 else UnicodeEncodeError,
							 | 
						||
| 
								 | 
							
								                          ab64_encode, hb("69b7").decode("latin-1"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # insert correct padding before decoding
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_encode(hb("69b71d")), b"abcd")  # 0 mod 4
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_encode(hb("69b71d79")), b"abcdeQ")  # 2 mod 4
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_encode(hb("69b71d79f8")), b"abcdefg")  # 3 mod 4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # output "./" altchars
							 | 
						||
| 
								 | 
							
								        self.assertEqual(ab64_encode(hb("69bfbf")), b"ab./")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_b64s_decode(self):
							 | 
						||
| 
								 | 
							
								        """b64s_decode()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils.binary import b64s_decode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # accept bytes or unicode
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_decode(b"abc"), hb("69b7"))
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_decode(u("abc")), hb("69b7"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # reject non-ascii unicode
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, b64s_decode, u("ab\xff"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # underlying a2b_ascii treats non-base64 chars as "Incorrect padding"
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, b64s_decode, b"ab\xff")
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, b64s_decode, b"ab!")
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, b64s_decode, u("ab!"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # insert correct padding, handle dirty padding bits
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_decode(b"abcd"), hb("69b71d"))  # 0 mod 4
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, b64s_decode, b"abcde")  # 1 mod 4
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_decode(b"abcdef"), hb("69b71d79"))  # 2 mod 4, dirty padding bits
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_decode(b"abcdeQ"), hb("69b71d79"))  # 2 mod 4, clean padding bits
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_decode(b"abcdefg"), hb("69b71d79f8"))  # 3 mod 4, clean padding bits
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_b64s_encode(self):
							 | 
						||
| 
								 | 
							
								        """b64s_encode()"""
							 | 
						||
| 
								 | 
							
								        from passlib.utils.binary import b64s_encode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # accept bytes
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_encode(hb("69b7")), b"abc")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # reject unicode
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError if PY3 else UnicodeEncodeError,
							 | 
						||
| 
								 | 
							
								                          b64s_encode, hb("69b7").decode("latin-1"))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # insert correct padding before decoding
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_encode(hb("69b71d")), b"abcd")  # 0 mod 4
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_encode(hb("69b71d79")), b"abcdeQ")  # 2 mod 4
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_encode(hb("69b71d79f8")), b"abcdefg")  # 3 mod 4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # output "+/" altchars
							 | 
						||
| 
								 | 
							
								        self.assertEqual(b64s_encode(hb("69bfbf")), b"ab+/")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _Base64Test(TestCase):
							 | 
						||
| 
								 | 
							
								    """common tests for all Base64Engine instances"""
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # class attrs
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Base64Engine instance to test
							 | 
						||
| 
								 | 
							
								    engine = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # pairs of (raw, encoded) bytes to test - should encode/decode correctly
							 | 
						||
| 
								 | 
							
								    encoded_data = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # tuples of (encoded, value, bits) for known integer encodings
							 | 
						||
| 
								 | 
							
								    encoded_ints = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # invalid encoded byte
							 | 
						||
| 
								 | 
							
								    bad_byte = b"?"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # helper to generate bytemap-specific strings
							 | 
						||
| 
								 | 
							
								    def m(self, *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"""
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        encode = engine.encode_bytes
							 | 
						||
| 
								 | 
							
								        for raw, encoded in self.encoded_data:
							 | 
						||
| 
								 | 
							
								            result = encode(raw)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(result, encoded, "encode %r:" % (raw,))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_encode_bytes_bad(self):
							 | 
						||
| 
								 | 
							
								        """test encode_bytes() with bad input"""
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        encode = engine.encode_bytes
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, encode, u('\x00'))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, encode, None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # test decode_bytes
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def test_decode_bytes(self):
							 | 
						||
| 
								 | 
							
								        """test decode_bytes() against reference inputs"""
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        decode = engine.decode_bytes
							 | 
						||
| 
								 | 
							
								        for raw, encoded in self.encoded_data:
							 | 
						||
| 
								 | 
							
								            result = decode(encoded)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(result, raw, "decode %r:" % (encoded,))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_decode_bytes_padding(self):
							 | 
						||
| 
								 | 
							
								        """test decode_bytes() ignores padding bits"""
							 | 
						||
| 
								 | 
							
								        bchr = (lambda v: bytes([v])) if PY3 else chr
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        m = self.m
							 | 
						||
| 
								 | 
							
								        decode = engine.decode_bytes
							 | 
						||
| 
								 | 
							
								        BNULL = b"\x00"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # length == 2 mod 4: 4 bits of padding
							 | 
						||
| 
								 | 
							
								        self.assertEqual(decode(m(0,0)), BNULL)
							 | 
						||
| 
								 | 
							
								        for i in range(0,6):
							 | 
						||
| 
								 | 
							
								            if engine.big: # 4 lsb padding
							 | 
						||
| 
								 | 
							
								                correct = BNULL if i < 4 else bchr(1<<(i-4))
							 | 
						||
| 
								 | 
							
								            else: # 4 msb padding
							 | 
						||
| 
								 | 
							
								                correct = bchr(1<<(i+6)) if i < 2 else BNULL
							 | 
						||
| 
								 | 
							
								            self.assertEqual(decode(m(0,1<<i)), correct, "%d/4 bits:" % i)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # length == 3 mod 4: 2 bits of padding
							 | 
						||
| 
								 | 
							
								        self.assertEqual(decode(m(0,0,0)), BNULL*2)
							 | 
						||
| 
								 | 
							
								        for i in range(0,6):
							 | 
						||
| 
								 | 
							
								            if engine.big: # 2 lsb are padding
							 | 
						||
| 
								 | 
							
								                correct = BNULL if i < 2 else bchr(1<<(i-2))
							 | 
						||
| 
								 | 
							
								            else: # 2 msg are padding
							 | 
						||
| 
								 | 
							
								                correct = bchr(1<<(i+4)) if i < 4 else BNULL
							 | 
						||
| 
								 | 
							
								            self.assertEqual(decode(m(0,0,1<<i)), BNULL + correct,
							 | 
						||
| 
								 | 
							
								                             "%d/2 bits:" % i)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_decode_bytes_bad(self):
							 | 
						||
| 
								 | 
							
								        """test decode_bytes() with bad input"""
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        decode = engine.decode_bytes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # wrong size (1 % 4)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, decode, engine.bytemap[:5])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # wrong char
							 | 
						||
| 
								 | 
							
								        self.assertTrue(self.bad_byte not in engine.bytemap)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, decode, self.bad_byte*4)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # wrong type
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, decode, engine.charmap[:4])
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, decode, None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # encode_bytes+decode_bytes
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def test_codec(self):
							 | 
						||
| 
								 | 
							
								        """test encode_bytes/decode_bytes against random data"""
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        from passlib.utils import getrandbytes, getrandstr
							 | 
						||
| 
								 | 
							
								        rng = self.getRandom()
							 | 
						||
| 
								 | 
							
								        saw_zero = False
							 | 
						||
| 
								 | 
							
								        for i in irange(500):
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            # test raw -> encode() -> decode() -> raw
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # generate some random bytes
							 | 
						||
| 
								 | 
							
								            size = rng.randint(1 if saw_zero else 0, 12)
							 | 
						||
| 
								 | 
							
								            if not size:
							 | 
						||
| 
								 | 
							
								                saw_zero = True
							 | 
						||
| 
								 | 
							
								            enc_size = (4*size+2)//3
							 | 
						||
| 
								 | 
							
								            raw = getrandbytes(rng, size)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # encode them, check invariants
							 | 
						||
| 
								 | 
							
								            encoded = engine.encode_bytes(raw)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(len(encoded), enc_size)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # make sure decode returns original
							 | 
						||
| 
								 | 
							
								            result = engine.decode_bytes(encoded)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(result, raw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            # test encoded -> decode() -> encode() -> encoded
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # generate some random encoded data
							 | 
						||
| 
								 | 
							
								            if size % 4 == 1:
							 | 
						||
| 
								 | 
							
								                size += rng.choice([-1,1,2])
							 | 
						||
| 
								 | 
							
								            raw_size = 3*size//4
							 | 
						||
| 
								 | 
							
								            encoded = getrandstr(rng, engine.bytemap, size)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # decode them, check invariants
							 | 
						||
| 
								 | 
							
								            raw = engine.decode_bytes(encoded)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(len(raw), raw_size, "encoded %d:" % size)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # make sure encode returns original (barring padding bits)
							 | 
						||
| 
								 | 
							
								            result = engine.encode_bytes(raw)
							 | 
						||
| 
								 | 
							
								            if size % 4:
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result[:-1], encoded[:-1])
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result, encoded)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_repair_unused(self):
							 | 
						||
| 
								 | 
							
								        """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 getrandstr
							 | 
						||
| 
								 | 
							
								        rng = self.getRandom()
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        check_repair_unused = self.engine.check_repair_unused
							 | 
						||
| 
								 | 
							
								        i = 0
							 | 
						||
| 
								 | 
							
								        while i < 300:
							 | 
						||
| 
								 | 
							
								            size = rng.randint(0,23)
							 | 
						||
| 
								 | 
							
								            cdata = getrandstr(rng, engine.charmap, size).encode("ascii")
							 | 
						||
| 
								 | 
							
								            if size & 3 == 1:
							 | 
						||
| 
								 | 
							
								                # should throw error
							 | 
						||
| 
								 | 
							
								                self.assertRaises(ValueError, check_repair_unused, cdata)
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            rdata = engine.encode_bytes(engine.decode_bytes(cdata))
							 | 
						||
| 
								 | 
							
								            if rng.random() < .5:
							 | 
						||
| 
								 | 
							
								                cdata = cdata.decode("ascii")
							 | 
						||
| 
								 | 
							
								                rdata = rdata.decode("ascii")
							 | 
						||
| 
								 | 
							
								            if cdata == rdata:
							 | 
						||
| 
								 | 
							
								                # should leave unchanged
							 | 
						||
| 
								 | 
							
								                ok, result = check_repair_unused(cdata)
							 | 
						||
| 
								 | 
							
								                self.assertFalse(ok)
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result, rdata)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                # should repair bits
							 | 
						||
| 
								 | 
							
								                self.assertNotEqual(size % 4, 0)
							 | 
						||
| 
								 | 
							
								                ok, result = check_repair_unused(cdata)
							 | 
						||
| 
								 | 
							
								                self.assertTrue(ok)
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result, rdata)
							 | 
						||
| 
								 | 
							
								            i += 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # test transposed encode/decode - encoding independant
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # NOTE: these tests assume normal encode/decode has been tested elsewhere.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    transposed = [
							 | 
						||
| 
								 | 
							
								        # orig, result, transpose map
							 | 
						||
| 
								 | 
							
								        (b"\x33\x22\x11", b"\x11\x22\x33",[2,1,0]),
							 | 
						||
| 
								 | 
							
								        (b"\x22\x33\x11", b"\x11\x22\x33",[1,2,0]),
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    transposed_dups = [
							 | 
						||
| 
								 | 
							
								        # orig, result, transpose projection
							 | 
						||
| 
								 | 
							
								        (b"\x11\x11\x22", b"\x11\x22\x33",[0,0,1]),
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_encode_transposed_bytes(self):
							 | 
						||
| 
								 | 
							
								        """test encode_transposed_bytes()"""
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        for result, input, offsets in self.transposed + self.transposed_dups:
							 | 
						||
| 
								 | 
							
								            tmp = engine.encode_transposed_bytes(input, offsets)
							 | 
						||
| 
								 | 
							
								            out = engine.decode_bytes(tmp)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(out, result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, engine.encode_transposed_bytes, u("a"), [])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_decode_transposed_bytes(self):
							 | 
						||
| 
								 | 
							
								        """test decode_transposed_bytes()"""
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        for input, result, offsets in self.transposed:
							 | 
						||
| 
								 | 
							
								            tmp = engine.encode_bytes(input)
							 | 
						||
| 
								 | 
							
								            out = engine.decode_transposed_bytes(tmp, offsets)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(out, result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_decode_transposed_bytes_bad(self):
							 | 
						||
| 
								 | 
							
								        """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)
							 | 
						||
| 
								 | 
							
								            self.assertRaises(TypeError, engine.decode_transposed_bytes, tmp,
							 | 
						||
| 
								 | 
							
								                              offsets)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # test 6bit handling
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    def check_int_pair(self, bits, encoded_pairs):
							 | 
						||
| 
								 | 
							
								        """helper to check encode_intXX & decode_intXX functions"""
							 | 
						||
| 
								 | 
							
								        rng = self.getRandom()
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        encode = getattr(engine, "encode_int%s" % bits)
							 | 
						||
| 
								 | 
							
								        decode = getattr(engine, "decode_int%s" % bits)
							 | 
						||
| 
								 | 
							
								        pad = -bits % 6
							 | 
						||
| 
								 | 
							
								        chars = (bits+pad)//6
							 | 
						||
| 
								 | 
							
								        upper = 1<<bits
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test encode func
							 | 
						||
| 
								 | 
							
								        for value, encoded in encoded_pairs:
							 | 
						||
| 
								 | 
							
								            result = encode(value)
							 | 
						||
| 
								 | 
							
								            self.assertIsInstance(result, bytes)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(result, encoded)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, encode, -1)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, encode, upper)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test decode func
							 | 
						||
| 
								 | 
							
								        for value, encoded in encoded_pairs:
							 | 
						||
| 
								 | 
							
								            self.assertEqual(decode(encoded), value, "encoded %r:" % (encoded,))
							 | 
						||
| 
								 | 
							
								        m = self.m
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, decode, m(0)*(chars+1))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, decode, m(0)*(chars-1))
							 | 
						||
| 
								 | 
							
								        self.assertRaises(ValueError, decode, self.bad_byte*chars)
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, decode, engine.charmap[0])
							 | 
						||
| 
								 | 
							
								        self.assertRaises(TypeError, decode, None)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # do random testing.
							 | 
						||
| 
								 | 
							
								        from passlib.utils import getrandstr
							 | 
						||
| 
								 | 
							
								        for i in irange(100):
							 | 
						||
| 
								 | 
							
								            # generate random value, encode, and then decode
							 | 
						||
| 
								 | 
							
								            value = rng.randint(0, upper-1)
							 | 
						||
| 
								 | 
							
								            encoded = encode(value)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(len(encoded), chars)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(decode(encoded), value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # generate some random encoded data, decode, then encode.
							 | 
						||
| 
								 | 
							
								            encoded = getrandstr(rng, engine.bytemap, chars)
							 | 
						||
| 
								 | 
							
								            value = decode(encoded)
							 | 
						||
| 
								 | 
							
								            self.assertGreaterEqual(value, 0, "decode %r out of bounds:" % encoded)
							 | 
						||
| 
								 | 
							
								            self.assertLess(value, upper, "decode %r out of bounds:" % encoded)
							 | 
						||
| 
								 | 
							
								            result = encode(value)
							 | 
						||
| 
								 | 
							
								            if pad:
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result[:-2], encoded[:-2])
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.assertEqual(result, encoded)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_int6(self):
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        m = self.m
							 | 
						||
| 
								 | 
							
								        self.check_int_pair(6, [(0, m(0)), (63, m(63))])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_int12(self):
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        m = self.m
							 | 
						||
| 
								 | 
							
								        self.check_int_pair(12,[(0, m(0,0)),
							 | 
						||
| 
								 | 
							
								            (63, m(0,63) if engine.big else m(63,0)), (0xFFF, m(63,63))])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_int24(self):
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        m = self.m
							 | 
						||
| 
								 | 
							
								        self.check_int_pair(24,[(0, m(0,0,0,0)),
							 | 
						||
| 
								 | 
							
								            (63, m(0,0,0,63) if engine.big else m(63,0,0,0)),
							 | 
						||
| 
								 | 
							
								            (0xFFFFFF, m(63,63,63,63))])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_int64(self):
							 | 
						||
| 
								 | 
							
								        # NOTE: this isn't multiple of 6, it has 2 padding bits appended
							 | 
						||
| 
								 | 
							
								        # before encoding.
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        m = self.m
							 | 
						||
| 
								 | 
							
								        self.check_int_pair(64, [(0, m(0,0,0,0, 0,0,0,0, 0,0,0)),
							 | 
						||
| 
								 | 
							
								                (63, m(0,0,0,0, 0,0,0,0, 0,3,60) if engine.big else
							 | 
						||
| 
								 | 
							
								                     m(63,0,0,0, 0,0,0,0, 0,0,0)),
							 | 
						||
| 
								 | 
							
								                ((1<<64)-1, m(63,63,63,63, 63,63,63,63, 63,63,60) if engine.big
							 | 
						||
| 
								 | 
							
								                    else m(63,63,63,63, 63,63,63,63, 63,63,15))])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def test_encoded_ints(self):
							 | 
						||
| 
								 | 
							
								        """test against reference integer encodings"""
							 | 
						||
| 
								 | 
							
								        if not self.encoded_ints:
							 | 
						||
| 
								 | 
							
								            raise self.skipTests("none defined for class")
							 | 
						||
| 
								 | 
							
								        engine = self.engine
							 | 
						||
| 
								 | 
							
								        for data, value, bits in self.encoded_ints:
							 | 
						||
| 
								 | 
							
								            encode = getattr(engine, "encode_int%d" % bits)
							 | 
						||
| 
								 | 
							
								            decode = getattr(engine, "decode_int%d" % bits)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(encode(value), data)
							 | 
						||
| 
								 | 
							
								            self.assertEqual(decode(data), value)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								    # eoc
							 | 
						||
| 
								 | 
							
								    #===================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# NOTE: testing H64 & H64Big should be sufficient to verify
							 | 
						||
| 
								 | 
							
								# that Base64Engine() works in general.
							 | 
						||
| 
								 | 
							
								from passlib.utils.binary import h64, h64big
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class H64_Test(_Base64Test):
							 | 
						||
| 
								 | 
							
								    """test H64 codec functions"""
							 | 
						||
| 
								 | 
							
								    engine = h64
							 | 
						||
| 
								 | 
							
								    descriptionPrefix = "h64 codec"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    encoded_data = [
							 | 
						||
| 
								 | 
							
								        # test lengths 0..6 to ensure tail is encoded properly
							 | 
						||
| 
								 | 
							
								        (b"",b""),
							 | 
						||
| 
								 | 
							
								        (b"\x55",b"J/"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa",b"Jd8"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55",b"JdOJ"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaa",b"JdOJe0"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaa\x55",b"JdOJeK3"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaa\x55\xaa",b"JdOJeKZe"),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test padding bits are null
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaf",b"JdOJj0"), # len = 1 mod 3
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaa\x5f",b"JdOJey3"), # len = 2 mod 3
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    encoded_ints = [
							 | 
						||
| 
								 | 
							
								        (b"z.", 63, 12),
							 | 
						||
| 
								 | 
							
								        (b".z", 4032, 12),
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class H64Big_Test(_Base64Test):
							 | 
						||
| 
								 | 
							
								    """test H64Big codec functions"""
							 | 
						||
| 
								 | 
							
								    engine = h64big
							 | 
						||
| 
								 | 
							
								    descriptionPrefix = "h64big codec"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    encoded_data = [
							 | 
						||
| 
								 | 
							
								        # test lengths 0..6 to ensure tail is encoded properly
							 | 
						||
| 
								 | 
							
								        (b"",b""),
							 | 
						||
| 
								 | 
							
								        (b"\x55",b"JE"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa",b"JOc"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55",b"JOdJ"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaa",b"JOdJeU"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaa\x55",b"JOdJeZI"),
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaa\x55\xaa",b"JOdJeZKe"),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # test padding bits are null
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaf",b"JOdJfk"), # len = 1 mod 3
							 | 
						||
| 
								 | 
							
								        (b"\x55\xaa\x55\xaa\x5f",b"JOdJeZw"), # len = 2 mod 3
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    encoded_ints = [
							 | 
						||
| 
								 | 
							
								        (b".z", 63, 12),
							 | 
						||
| 
								 | 
							
								        (b"z.", 4032, 12),
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 | 
						||
| 
								 | 
							
								# eof
							 | 
						||
| 
								 | 
							
								#=============================================================================
							 |