def test_get_crypt_handler(self): """test get_crypt_handler()""" class dummy_1(uh.StaticHandler): name = "dummy_1" # without available handler self.assertRaises(KeyError, get_crypt_handler, "dummy_1") self.assertIs(get_crypt_handler("dummy_1", None), None) # already loaded handler register_crypt_handler(dummy_1) self.assertIs(get_crypt_handler("dummy_1"), dummy_1) with catch_warnings(): warnings.filterwarnings( "ignore", "handler names should be lower-case, and use underscores instead of hyphens:.*", UserWarning) # already loaded handler, using incorrect name self.assertIs(get_crypt_handler("DUMMY-1"), dummy_1) # lazy load of unloaded handler, using incorrect name register_crypt_handler_path('dummy_0', __name__) self.assertIs(get_crypt_handler("DUMMY-0"), dummy_0) # check system & private names aren't returned import lib.passlib.hash # ensure module imported, so py3.3 sets __package__ passlib.hash.__dict__[ "_fake"] = "dummy" # so behavior seen under py2x also for name in ["_fake", "__package__"]: self.assertRaises(KeyError, get_crypt_handler, name) self.assertIs(get_crypt_handler(name, None), None)
def test_get_crypt_handler(self): """test get_crypt_handler()""" class dummy_1(uh.StaticHandler): name = "dummy_1" # without available handler self.assertRaises(KeyError, get_crypt_handler, "dummy_1") self.assertIs(get_crypt_handler("dummy_1", None), None) # already loaded handler register_crypt_handler(dummy_1) self.assertIs(get_crypt_handler("dummy_1"), dummy_1) with catch_warnings(): warnings.filterwarnings("ignore", "handler names should be lower-case, and use underscores instead of hyphens:.*", UserWarning) # already loaded handler, using incorrect name self.assertIs(get_crypt_handler("DUMMY-1"), dummy_1) # lazy load of unloaded handler, using incorrect name register_crypt_handler_path('dummy_0', __name__) self.assertIs(get_crypt_handler("DUMMY-0"), dummy_0) # check system & private names aren't returned import lib.passlib.hash # ensure module imported, so py3.3 sets __package__ passlib.hash.__dict__["_fake"] = "dummy" # so behavior seen under py2x also for name in ["_fake", "__package__"]: self.assertRaises(KeyError, get_crypt_handler, name) self.assertIs(get_crypt_handler(name, None), None)
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_hash_proxy(self): """test passlib.hash proxy object""" # check dir works dir(hash) # check repr works repr(hash) # check non-existent attrs raise error self.assertRaises(AttributeError, getattr, hash, 'fooey') # GAE tries to set __loader__, # make sure that doesn't call register_crypt_handler. old = getattr(hash, "__loader__", None) test = object() hash.__loader__ = test self.assertIs(hash.__loader__, test) if old is None: del hash.__loader__ self.assertFalse(hasattr(hash, "__loader__")) else: hash.__loader__ = old self.assertIs(hash.__loader__, old) # check storing attr calls register_crypt_handler class dummy_1(uh.StaticHandler): name = "dummy_1" hash.dummy_1 = dummy_1 self.assertIs(get_crypt_handler("dummy_1"), dummy_1) # check storing under wrong name results in error self.assertRaises(ValueError, setattr, hash, "dummy_1x", dummy_1)
def _get_passlib_hasher(self, passlib_name): """ resolve passlib hasher by name, using context if available. """ context = self.context if context is None: return registry.get_crypt_handler(passlib_name) else: return context.handler(passlib_name)
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 catch_warnings(): warnings.filterwarnings("ignore", "xxxxxxxxxx", DeprecationWarning) h = get_crypt_handler("dummy_bad") from lib.passlib.tests import _test_bad_register as tbr self.assertIs(h, tbr.alt_dummy_bad)
def _iter_os_crypt_schemes(): """helper which iterates over supported os_crypt schemes""" found = False for name in unix_crypt_schemes: handler = get_crypt_handler(name) if handler.has_backend("os_crypt"): found = True yield name if found: # only offer disabled handler if there's another scheme in front, # as this can't actually hash any passwords yield "unix_disabled" else: # pragma: no cover -- sanity check # no idea what OS this could happen on... warn("crypt.crypt() function is present, but doesn't support any " "formats known to passlib!", PasslibRuntimeWarning)
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 get_passlib_hasher(handler, algorithm=None): """create *Hasher*-compatible wrapper for specified passlib hash. This takes in the name of a passlib hash (or the handler object itself), and returns a wrapper instance which should be compatible with Django 1.4's Hashers framework. If the named hash corresponds to one of Django's builtin hashers, an instance of the real hasher class will be returned. Note that the format of the handler won't be altered, so will probably not be compatible with Django's algorithm format, so the monkeypatch provided by this plugin must have been applied. .. note:: This function requires Django 1.4 or later. """ if DJANGO_VERSION < (1,4): raise RuntimeError("get_passlib_hasher() requires Django >= 1.4") if isinstance(handler, str): handler = get_crypt_handler(handler) if hasattr(handler, "django_name"): # return native hasher instance # XXX: should add this to _hasher_cache[] name = handler.django_name if name == "sha1" and algorithm == "unsalted_sha1": # django 1.4.6+ uses a separate hasher for "sha1$$digest" hashes, # but passlib just reuses the "sha1$salt$digest" handler. # we want to resolve to correct django hasher. name = algorithm return _get_hasher(name) if handler.name == "django_disabled": raise ValueError("can't wrap unusable-password handler") try: return _hasher_cache[handler] except KeyError: name = "Passlib_%s_PasswordHasher" % handler.name.title() cls = type(name, (_HasherWrapper,), dict(passlib_handler=handler)) hasher = _hasher_cache[handler] = cls() return hasher
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 passlib_to_hasher_name(passlib_name): """convert passlib handler name -> hasher name""" handler = get_crypt_handler(passlib_name) if hasattr(handler, "django_name"): return handler.django_name return PASSLIB_HASHER_PREFIX + passlib_name
def __enter__(self): from lib.passlib import registry registry._unload_handler_name(self.name, locations=False) registry.register_crypt_handler(self.dummy) assert registry.get_crypt_handler(self.name) is self.dummy return self.dummy