def test_register_crypt_handler(self): """test register_crypt_handler()""" self.assertRaises(TypeError, register_crypt_handler, {}) self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name=None))) self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name="AB_CD"))) self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name="ab-cd"))) self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name="ab__cd"))) self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name="default"))) class dummy_1(uh.StaticHandler): name = "dummy_1" class dummy_1b(uh.StaticHandler): name = "dummy_1" self.assertTrue('dummy_1' not in list_crypt_handlers()) register_crypt_handler(dummy_1) register_crypt_handler(dummy_1) self.assertIs(get_crypt_handler("dummy_1"), dummy_1) self.assertRaises(KeyError, register_crypt_handler, dummy_1b) self.assertIs(get_crypt_handler("dummy_1"), dummy_1) register_crypt_handler(dummy_1b, force=True) self.assertIs(get_crypt_handler("dummy_1"), dummy_1b) self.assertTrue('dummy_1' in list_crypt_handlers())
def test_register_crypt_handler_path(self): """test register_crypt_handler_path()""" # NOTE: this messes w/ internals of registry, shouldn't be used publically. paths = registry._locations # check namespace is clear self.assertTrue("dummy_0" not in paths) self.assertFalse(hasattr(hash, "dummy_0")) # check invalid names are rejected self.assertRaises(ValueError, register_crypt_handler_path, "dummy_0", ".test_registry") self.assertRaises( ValueError, register_crypt_handler_path, "dummy_0", __name__ + ":dummy_0:xxx", ) self.assertRaises( ValueError, register_crypt_handler_path, "dummy_0", __name__ + ":dummy_0.xxx", ) # try lazy load register_crypt_handler_path("dummy_0", __name__) self.assertTrue("dummy_0" in list_crypt_handlers()) self.assertTrue("dummy_0" not in list_crypt_handlers(loaded_only=True)) self.assertIs(hash.dummy_0, dummy_0) self.assertTrue("dummy_0" in list_crypt_handlers(loaded_only=True)) unload_handler_name("dummy_0") # try lazy load w/ alt register_crypt_handler_path("dummy_0", __name__ + ":alt_dummy_0") self.assertIs(hash.dummy_0, alt_dummy_0) unload_handler_name("dummy_0") # check lazy load w/ wrong type fails register_crypt_handler_path("dummy_x", __name__) self.assertRaises(TypeError, get_crypt_handler, "dummy_x") # check lazy load w/ wrong name fails register_crypt_handler_path("alt_dummy_0", __name__) self.assertRaises(ValueError, get_crypt_handler, "alt_dummy_0") unload_handler_name("alt_dummy_0") # TODO: check lazy load which calls register_crypt_handler (warning should be issued) sys.modules.pop("passlib.tests._test_bad_register", None) register_crypt_handler_path("dummy_bad", "passlib.tests._test_bad_register") with warnings.catch_warnings(): warnings.filterwarnings("ignore", "xxxxxxxxxx", DeprecationWarning) h = get_crypt_handler("dummy_bad") from passlib.tests import _test_bad_register as tbr self.assertIs(h, tbr.alt_dummy_bad)
def test_handlers(self): "verify we have tests for all handlers" from passlib.registry import list_crypt_handlers from passlib.tests.test_handlers import get_handler_case for name in list_crypt_handlers(): if name.startswith("ldap_") and name[5:] in list_crypt_handlers(): continue if name in ["roundup_plaintext"]: continue self.assertTrue(get_handler_case(name))
def _load_master_config(): from passlib.registry import list_crypt_handlers # get master list schemes = list_crypt_handlers() # exclude the ones we know have ambiguous or greedy identify() methods. excluded = [ # frequently confused for eachother "bigcrypt", "crypt16", # no good identifiers "cisco_pix", "cisco_type7", "htdigest", "mysql323", "oracle10", # all have same size "lmhash", "msdcc", "msdcc2", "nthash", # plaintext handlers "plaintext", "ldap_plaintext", # disabled handlers "django_disabled", "unix_disabled", "unix_fallback", ] for name in excluded: schemes.remove(name) # return config return dict(schemes=schemes, default="sha256_crypt")
def test_register_crypt_handler_path(self): """test register_crypt_handler_path()""" # NOTE: this messes w/ internals of registry, shouldn't be used publically. paths = registry._locations # check namespace is clear self.assertTrue('dummy_0' not in paths) self.assertFalse(hasattr(hash, 'dummy_0')) # check invalid names are rejected self.assertRaises(ValueError, register_crypt_handler_path, "dummy_0", ".test_registry") self.assertRaises(ValueError, register_crypt_handler_path, "dummy_0", __name__ + ":dummy_0:xxx") self.assertRaises(ValueError, register_crypt_handler_path, "dummy_0", __name__ + ":dummy_0.xxx") # try lazy load register_crypt_handler_path('dummy_0', __name__) self.assertTrue('dummy_0' in list_crypt_handlers()) self.assertTrue('dummy_0' not in list_crypt_handlers(loaded_only=True)) self.assertIs(hash.dummy_0, dummy_0) self.assertTrue('dummy_0' in list_crypt_handlers(loaded_only=True)) unload_handler_name('dummy_0') # try lazy load w/ alt register_crypt_handler_path('dummy_0', __name__ + ':alt_dummy_0') self.assertIs(hash.dummy_0, alt_dummy_0) unload_handler_name('dummy_0') # check lazy load w/ wrong type fails register_crypt_handler_path('dummy_x', __name__) self.assertRaises(TypeError, get_crypt_handler, 'dummy_x') # check lazy load w/ wrong name fails register_crypt_handler_path('alt_dummy_0', __name__) self.assertRaises(ValueError, get_crypt_handler, "alt_dummy_0") unload_handler_name("alt_dummy_0") # TODO: check lazy load which calls register_crypt_handler (warning should be issued) sys.modules.pop("passlib.tests._test_bad_register", None) register_crypt_handler_path("dummy_bad", "passlib.tests._test_bad_register") with warnings.catch_warnings(): warnings.filterwarnings("ignore", "xxxxxxxxxx", DeprecationWarning) h = get_crypt_handler("dummy_bad") from passlib.tests import _test_bad_register as tbr self.assertIs(h, tbr.alt_dummy_bad)
def test_handlers(self): """verify we have tests for all builtin handlers""" from passlib.registry import list_crypt_handlers from passlib.tests.test_handlers import get_handler_case, conditionally_available_hashes for name in list_crypt_handlers(): # skip some wrappers that don't need independant testing if name.startswith("ldap_") and name[5:] in list_crypt_handlers(): continue if name in ["roundup_plaintext"]: continue # check the remaining ones all have a handler try: self.assertTrue(get_handler_case(name)) except exc.MissingBackendError: if name in conditionally_available_hashes: # expected to fail on some setups continue raise
def test_handlers(self): """verify we have tests for all builtin handlers""" from passlib.registry import list_crypt_handlers from passlib.tests.test_handlers import get_handler_case for name in list_crypt_handlers(): # skip some wrappers that don't need independant testing if name.startswith("ldap_") and name[5:] in list_crypt_handlers(): continue if name in ["roundup_plaintext"]: continue # check the remaining ones all have a handler try: self.assertTrue(get_handler_case(name)) except exc.MissingBackendError: if name in ["bcrypt", "bcrypt_sha256"]: # expected to fail on some setups continue raise
def test_list_crypt_handlers(self): """test list_crypt_handlers()""" from passlib.registry import list_crypt_handlers # check system & private names aren't returned hash.__dict__["_fake"] = "dummy" for name in list_crypt_handlers(): self.assertFalse(name.startswith("_"), "%r: " % name) unload_handler_name("_fake")
def test_list_crypt_handlers(self): """test list_crypt_handlers()""" from passlib.registry import list_crypt_handlers # check system & private names aren't returned import passlib.hash # ensure module imported, so py3.3 sets __package__ passlib.hash.__dict__["_fake"] = "dummy" # so behavior seen under py2x also for name in list_crypt_handlers(): self.assertFalse(name.startswith("_"), "%r: " % name)
def test_list_crypt_handlers(self): "test list_crypt_handlers()" from passlib.registry import list_crypt_handlers # check system & private names aren't returned import passlib.hash # ensure module imported, so py3.3 sets __package__ passlib.hash.__dict__[ "_fake"] = "dummy" # so behavior seen under py2x also for name in list_crypt_handlers(): self.assertFalse(name.startswith("_"), "%r: " % name)
def hasher_to_passlib_name(hasher_name): "convert hasher name -> passlib handler name" if hasher_name.startswith(PASSLIB_HASHER_PREFIX): return hasher_name[len(PASSLIB_HASHER_PREFIX):] for name in list_crypt_handlers(): if name.startswith(DJANGO_PASSLIB_PREFIX) or name in _other_django_hashes: handler = get_crypt_handler(name) if getattr(handler, "django_name", None) == hasher_name: return name # XXX: this should only happen for custom hashers that have been registered. # work in progress (below) that would take care of those. raise ValueError("can't translate hasher name to passlib name: %r" % hasher_name)
def hasher_to_passlib_name(hasher_name): "convert hasher name -> passlib handler name" if hasher_name.startswith(PASSLIB_HASHER_PREFIX): return hasher_name[len(PASSLIB_HASHER_PREFIX):] for name in list_crypt_handlers(): if name.startswith( DJANGO_PASSLIB_PREFIX) or name in _other_django_hashes: handler = get_crypt_handler(name) if getattr(handler, "django_name", None) == hasher_name: return name # XXX: this should only happen for custom hashers that have been registered. # _HasherHandler (below) is work in progress that would fix this. raise ValueError("can't translate hasher name to passlib name: %r" % hasher_name)
def get_password_context(): if User._ctx: return User._ctx schemes = registry.list_crypt_handlers() # scrypt throws a warning if the native wheels aren't found schemes.remove('scrypt') # we can't leave plaintext schemes as they will be misidentified for scheme in schemes: if scheme.endswith('plaintext'): schemes.remove(scheme) User._ctx = context.CryptContext( schemes=schemes, default='bcrypt_sha256', bcrypt_sha256__rounds=app.config['CREDENTIAL_ROUNDS'], deprecated='auto') return User._ctx
def hasher_to_passlib_name(hasher_name): """convert hasher name -> passlib handler name""" if hasher_name.startswith(PASSLIB_HASHER_PREFIX): return hasher_name[len(PASSLIB_HASHER_PREFIX):] if hasher_name == "unsalted_sha1": # django 1.4.6+ uses a separate hasher for "sha1$$digest" hashes, # but passlib just reuses the "sha1$salt$digest" handler. hasher_name = "sha1" for name in list_crypt_handlers(): if name.startswith(DJANGO_PASSLIB_PREFIX) or name in _other_django_hashes: handler = get_crypt_handler(name) if getattr(handler, "django_name", None) == hasher_name: return name # XXX: this should only happen for custom hashers that have been registered. # _HasherHandler (below) is work in progress that would fix this. raise ValueError("can't translate hasher name to passlib name: %r" % hasher_name)
def _load_master_config(): from passlib.registry import list_crypt_handlers # get master list schemes = list_crypt_handlers() # exclude the ones we know have ambiguous or greedy identify() methods. excluded = [ # frequently confused for eachother 'bigcrypt', 'crypt16', # no good identifiers 'cisco_pix', 'cisco_type7', 'htdigest', 'mysql323', 'oracle10', # all have same size 'lmhash', 'msdcc', 'msdcc2', 'nthash', # plaintext handlers 'plaintext', 'ldap_plaintext', # disabled handlers 'django_disabled', 'unix_disabled', 'unix_fallback', ] for name in excluded: schemes.remove(name) # return config return dict(schemes=schemes, default="sha256_crypt")
return self.method.name @property def class_name(self): return get_class_name(self.method) @property def description(self): return get_description(self.method) @property def require_password(self): return requires_password(self.method) @property def require_user(self): return requires_user(self.method) @property def supported(self): return is_supported(self.method) def __call__(self, password, **params): return make_hash(self.method, password, **params) # Init the hash mappings: # attr_name -> MethodWrapper(attr_name, implementation_class) methods = OrderedDict((name, MethodWrapper(get_crypt_handler(name))) for name in list_crypt_handlers())
def django_to_passlib(self, django_name, cached=True): """ Convert Django hasher / name to Passlib hasher / name. If present, CryptContext will be checked instead of main registry. :param django_name: Django hasher class or algorithm name. "default" allowed if context provided. :raises ValueError: if can't resolve hasher. :returns: passlib hasher or name """ # check for django hasher if hasattr(django_name, "algorithm"): # check for passlib adapter if isinstance(django_name, _PasslibHasherWrapper): return django_name.passlib_handler # resolve django hasher -> name django_name = django_name.algorithm # check cache if cached: cache = self._passlib_hasher_cache try: return cache[django_name] except KeyError: pass result = cache[django_name] = \ self.django_to_passlib(django_name, cached=False) return result # check if it's an obviously-wrapped name if django_name.startswith(PASSLIB_WRAPPER_PREFIX): passlib_name = django_name[len(PASSLIB_WRAPPER_PREFIX):] return self._get_passlib_hasher(passlib_name) # resolve default if django_name == "default": context = self.context if context is None: raise TypeError("can't determine default scheme w/ context") return context.handler() # special case: Django uses a separate hasher for "sha1$$digest" # hashes (unsalted_sha1) and "sha1$salt$digest" (sha1); # but passlib uses "django_salted_sha1" for both of these. if django_name == "unsalted_sha1": django_name = "sha1" # resolve name # XXX: bother caching these lists / mapping? # not needed in long-term due to cache above. context = self.context if context is None: # check registry # TODO: should make iteration via registry easier candidates = ( registry.get_crypt_handler(passlib_name) for passlib_name in registry.list_crypt_handlers() if passlib_name.startswith(DJANGO_COMPAT_PREFIX) or passlib_name in _other_django_hashes ) else: # check context candidates = context.schemes(resolve=True) for handler in candidates: if getattr(handler, "django_name", None) == django_name: return handler # give up # NOTE: this should only happen for custom django hashers that we don't # know the equivalents for. _HasherHandler (below) is work in # progress that would allow us to at least return a wrapper. raise ValueError("can't translate django name to passlib name: %r" % (django_name,))
def django_to_passlib(self, django_name, cached=True): """ Convert Django hasher / name to Passlib hasher / name. If present, CryptContext will be checked instead of main registry. :param django_name: Django hasher class or algorithm name. "default" allowed if context provided. :raises ValueError: if can't resolve hasher. :returns: passlib hasher or name """ # check for django hasher if hasattr(django_name, "algorithm"): # check for passlib adapter if isinstance(django_name, _PasslibHasherWrapper): return django_name.passlib_handler # resolve django hasher -> name django_name = django_name.algorithm # check cache if cached: cache = self._passlib_hasher_cache try: return cache[django_name] except KeyError: pass result = cache[django_name] = self.django_to_passlib(django_name, cached=False) return result # check if it's an obviously-wrapped name if django_name.startswith(PASSLIB_WRAPPER_PREFIX): passlib_name = django_name[len(PASSLIB_WRAPPER_PREFIX):] return self._get_passlib_hasher(passlib_name) # resolve default if django_name == "default": context = self.context if context is None: raise TypeError("can't determine default scheme w/ context") return context.handler() # special case: Django uses a separate hasher for "sha1$$digest" # hashes (unsalted_sha1) and "sha1$salt$digest" (sha1); # but passlib uses "django_salted_sha1" for both of these. if django_name == "unsalted_sha1": django_name = "sha1" # resolve name # XXX: bother caching these lists / mapping? # not needed in long-term due to cache above. context = self.context if context is None: # check registry # TODO: should make iteration via registry easier candidates = (registry.get_crypt_handler(passlib_name) for passlib_name in registry.list_crypt_handlers() if passlib_name.startswith(DJANGO_COMPAT_PREFIX) or passlib_name in _other_django_hashes) else: # check context candidates = context.schemes(resolve=True) for handler in candidates: if getattr(handler, "django_name", None) == django_name: return handler # give up # NOTE: this should only happen for custom django hashers that we don't # know the equivalents for. _HasherHandler (below) is work in # progress that would allow us to at least return a wrapper. raise ValueError("can't translate django name to passlib name: %r" % (django_name, ))