summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/lib/passlib.context.rst (renamed from docs/lib/passlib.base.rst)7
-rw-r--r--docs/lib/passlib.hash.hex_digests.rst2
-rw-r--r--docs/lib/passlib.hash.ldap_digests.rst2
-rw-r--r--docs/lib/passlib.hash.plaintext.rst2
-rw-r--r--docs/lib/passlib.hash.unix_fallback.rst2
-rw-r--r--docs/overview.rst2
-rw-r--r--docs/password_hash_api.rst2
-rw-r--r--passlib/apache.py2
-rw-r--r--passlib/context.py (renamed from passlib/base.py)253
-rw-r--r--passlib/hash.py2
-rw-r--r--passlib/hosts.py2
-rw-r--r--passlib/registry.py257
-rw-r--r--passlib/servers.py2
-rw-r--r--passlib/tests/_test_bad_register.py4
-rw-r--r--passlib/tests/genconfig.py2
-rw-r--r--passlib/tests/test_context.py (renamed from passlib/tests/test_base.py)126
-rw-r--r--passlib/tests/test_registry.py145
-rw-r--r--passlib/tests/test_utils.py2
18 files changed, 425 insertions, 391 deletions
diff --git a/docs/lib/passlib.base.rst b/docs/lib/passlib.context.rst
index 9c09eb2..138452e 100644
--- a/docs/lib/passlib.base.rst
+++ b/docs/lib/passlib.context.rst
@@ -1,8 +1,11 @@
=============================================
-:mod:`passlib.base` - Crypt Contexts
+:mod:`passlib.context` - CryptContext class
=============================================
-.. module:: passlib.base
+.. module:: passlib.context
+
+The :mod:`!passlib.base` module contains a number of core
+
.. autoclass:: CryptContext
diff --git a/docs/lib/passlib.hash.hex_digests.rst b/docs/lib/passlib.hash.hex_digests.rst
index cbd1b1a..9bc3653 100644
--- a/docs/lib/passlib.hash.hex_digests.rst
+++ b/docs/lib/passlib.hash.hex_digests.rst
@@ -14,7 +14,7 @@ PassLib provides wrappers for few of the common hashes.
Usage
=====
These classes all wrap the underlying hashlib implementations,
-and are mainly useful only for plugging them into a :class:`passlib.base.CryptContext`.
+and are mainly useful only for plugging them into a :class:`~passlib.context.CryptContext`.
However, they can be used directly as follows::
>>> from passlib.hash import hex_sha1 as hs
diff --git a/docs/lib/passlib.hash.ldap_digests.rst b/docs/lib/passlib.hash.ldap_digests.rst
index 7b9f418..a0730b7 100644
--- a/docs/lib/passlib.hash.ldap_digests.rst
+++ b/docs/lib/passlib.hash.ldap_digests.rst
@@ -17,7 +17,7 @@ are very insecure, and should not be used except when required.
Usage
=====
These classes all wrap the underlying hashlib implementations,
-and are mainly useful only for plugging them into a :class:`passlib.base.CryptContext`.
+and are mainly useful only for plugging them into a :class:`~passlib.context.CryptContext`.
However, they can be used directly as follows::
>>> from passlib.hash import ldap_salted_md5 as lsm
diff --git a/docs/lib/passlib.hash.plaintext.rst b/docs/lib/passlib.hash.plaintext.rst
index d0ad62d..d8ad3ac 100644
--- a/docs/lib/passlib.hash.plaintext.rst
+++ b/docs/lib/passlib.hash.plaintext.rst
@@ -11,7 +11,7 @@ existing applications. *It should not be used* for any other purpose.
Usage
=====
-This class is mainly useful only for plugging into a :class:`passlib.base.CryptContext`.
+This class is mainly useful only for plugging into a :class:`~passlib.context.CryptContext`.
When used, it should always be the last scheme in the list,
as it will recognize all hashes.
It can be used directly as follows::
diff --git a/docs/lib/passlib.hash.unix_fallback.rst b/docs/lib/passlib.hash.unix_fallback.rst
index d534546..982e55f 100644
--- a/docs/lib/passlib.hash.unix_fallback.rst
+++ b/docs/lib/passlib.hash.unix_fallback.rst
@@ -10,7 +10,7 @@ password fields as found in unix ``/etc/shadow`` files.
Usage
=====
-This class is mainly useful only for plugging into a :class:`passlib.base.CryptContext`.
+This class is mainly useful only for plugging into a :class:`~passlib.context.CryptContext`.
When used, it should always be the last scheme in the list,
as it is designed to provide a fallback behavior.
It can be used directly as follows::
diff --git a/docs/overview.rst b/docs/overview.rst
index 8102020..c9ce82a 100644
--- a/docs/overview.rst
+++ b/docs/overview.rst
@@ -52,7 +52,7 @@ deprecated schemes, hashes using strong schemes but weak individual configuratio
and other border cases.
PassLib provides an advanced support framework, based around
-the :doc:`CryptContext <lib/passlib.base>` class, which takes care of
+the :doc:`CryptContext <lib/passlib.context>` class, which takes care of
many of these issues. Each :class:`!CryptContext` instance can be configured
with a list of known hashes, as well as configuration of policy requirements
such as which hash is the default, which ones are deprecated, and other features.
diff --git a/docs/password_hash_api.rst b/docs/password_hash_api.rst
index b932096..37e36fc 100644
--- a/docs/password_hash_api.rst
+++ b/docs/password_hash_api.rst
@@ -34,7 +34,7 @@ The `optional informational attributes`_
Usage
=====
-While most uses of PassLib are done through a :class:`~passlib.base.CryptContext` class,
+While most uses of PassLib are done through a :class:`~passlib.context.CryptContext` class,
the various :class:`!PasswordHash` classes can be used directly to manipulate
passwords::
diff --git a/passlib/apache.py b/passlib/apache.py
index 0f8ba8d..9b99203 100644
--- a/passlib/apache.py
+++ b/passlib/apache.py
@@ -31,7 +31,7 @@ import logging; log = logging.getLogger(__name__)
import os
#site
#libs
-from passlib.base import CryptContext
+from passlib.context import CryptContext
#pkg
#local
__all__ = [
diff --git a/passlib/base.py b/passlib/context.py
index 6345c3c..0658c33 100644
--- a/passlib/base.py
+++ b/passlib/context.py
@@ -1,14 +1,4 @@
-"""passlib - implementation of various password hashing functions
-
-Context options
-
- schemes - list of names or handler instances which should be recognized by context
- deprecated - list names of handlers which should context should only use to validate *old* hashes
- default - optional name of handler to use for encrypting new hashes.
-
-Many schemes support their own options, such as min/max/default rounds.
-
-"""
+"""passlib.context - CryptContext implementation"""
#=========================================================
#imports
#=========================================================
@@ -27,255 +17,16 @@ from warnings import warn
#site
from pkg_resources import resource_string
#libs
+from passlib.registry import get_crypt_handler
from passlib.utils import Undef, is_crypt_handler, splitcomma, rng
#pkg
#local
__all__ = [
- #registry interface
- "register_crypt_handler_path",
- "register_crypt_handler",
- "get_crypt_handler",
- "list_crypt_handlers",
-
- #contexts
'CryptPolicy',
'CryptContext',
]
#=========================================================
-#registry proxy object
-#=========================================================
-class PasslibRegistryProxy(object):
- """proxy module passlib.hash
-
- this module is in fact an object which lazy-loads
- the requested password hash algorithm from wherever it has been stored.
- it acts as a thin wrapper around :func:`passlib.base.get_crypt_handler`.
- """
- __name__ = "passlib.hash"
- __package__ = None
-
- def __getattr__(self, attr):
- if attr.startswith("_"):
- raise AttributeError, "missing attribute: %r" % (attr,)
- handler = get_crypt_handler(attr, None)
- if handler:
- return handler
- else:
- raise AttributeError, "unknown password hash: %r" % (attr,)
-
- def __setattr__(self, attr, value):
- register_crypt_handler(value, name=attr)
-
- def __repr__(self):
- return "<proxy module 'passlib.hash'>"
-
- def __dir__(self):
- #add in handlers that will be lazy-loaded,
- #otherwise this is std dir implementation
- attrs = set(dir(self.__class__))
- attrs.update(self.__dict__)
- attrs.update(_handler_locations)
- return sorted(attrs)
-
- #=========================================================
- #eoc
- #=========================================================
-
-#singleton instance
-_proxy = PasslibRegistryProxy()
-
-#==========================================================
-#internal registry state
-#==========================================================
-
-#: dict mapping name -> handler for all loaded handlers. uses proxy's dict so they stay in sync.
-_handlers = _proxy.__dict__
-
-#: dict mapping name -> (module path, attribute) for lazy-loading of handlers
-_handler_locations = {
- #NOTE: this is a hardcoded list of the handlers built into passlib,
- #applications should call register_crypt_handler_location() to add their own
- "apr_md5_crypt": ("passlib.drivers.md5_crypt", "apr_md5_crypt"),
- "bcrypt": ("passlib.drivers.bcrypt", "bcrypt"),
- "bigcrypt": ("passlib.drivers.des_crypt", "bigcrypt"),
- "bsdi_crypt": ("passlib.drivers.des_crypt", "bsdi_crypt"),
- "crypt16": ("passlib.drivers.des_crypt", "crypt16"),
- "des_crypt": ("passlib.drivers.des_crypt", "des_crypt"),
- "hex_md4": ("passlib.drivers.digests", "hex_md4"),
- "hex_md5": ("passlib.drivers.digests", "hex_md5"),
- "hex_sha1": ("passlib.drivers.digests", "hex_sha1"),
- "hex_sha256": ("passlib.drivers.digests", "hex_sha256"),
- "hex_sha512": ("passlib.drivers.digests", "hex_sha512"),
- "ldap_cleartext": ("passlib.drivers.ldap_digests","ldap_cleartext"),
- "ldap_md5": ("passlib.drivers.ldap_digests","ldap_md5"),
- "ldap_sha1": ("passlib.drivers.ldap_digests","ldap_sha1"),
- "ldap_salted_md5": ("passlib.drivers.ldap_digests","ldap_salted_md5"),
- "ldap_salted_sha1": ("passlib.drivers.ldap_digests","ldap_salted_sha1"),
- "md5_crypt": ("passlib.drivers.md5_crypt", "md5_crypt"),
- "mysql323": ("passlib.drivers.mysql", "mysql323"),
- "mysql41": ("passlib.drivers.mysql", "mysql41"),
- "nthash": ("passlib.drivers.nthash", "nthash"),
- "oracle10": ("passlib.drivers.oracle", "oracle10"),
- "oracle11": ("passlib.drivers.oracle", "oracle11"),
- "phpass": ("passlib.drivers.phpass", "phpass"),
- "plaintext": ("passlib.drivers.misc", "plaintext"),
- "postgres_md5": ("passlib.drivers.postgres", "postgres_md5"),
- "sha1_crypt": ("passlib.drivers.sha1_crypt", "sha1_crypt"),
- "sha256_crypt": ("passlib.drivers.sha2_crypt", "sha256_crypt"),
- "sha512_crypt": ("passlib.drivers.sha2_crypt", "sha512_crypt"),
- "sun_md5_crypt": ("passlib.drivers.sun_md5_crypt","sun_md5_crypt"),
- "unix_fallback": ("passlib.drivers.misc", "unix_fallback"),
-}
-
-#: master regexp for detecting valid handler names
-_name_re = re.compile("^[a-z][_a-z0-9]{2,}$")
-
-#==========================================================
-#registry frontend functions
-#==========================================================
-def register_crypt_handler_path(name, path):
- """register location to lazy-load handler when requested.
-
- custom hashes may be registered via :func:`register_crypt_handler`,
- or they may be registered by this function,
- which will delay actually importing and loading the handler
- until a call to :func:`get_crypt_handler` is made for the specified name.
-
- :arg name: name of handler
- :arg path: module import path
-
- the specified module path should contain a password hash handler
- called :samp:`{name}`, or the path may contain a semicolon,
- specifying the module and module attribute to use.
- """
- global _handler_locations
- if ':' in path:
- modname, modattr = path.split(":")
- else:
- modname, modattr = path, name
- _handler_locations[name] = (modname, modattr)
-
-def register_crypt_handler(handler, force=False, name=None):
- """register password hash handler.
-
- this method registers a handler with the internal passlib registry,
- so that it will be returned by :func:`get_crypt_handler` when requested.
-
- :arg handler: the password hash handler to register
- :param force: force override of existing handler (defaults to False)
-
- :raises KeyError:
- if a (different) handler was already registered with
- the same name, and ``force=True`` was not specified.
- """
- global _handlers, _name_re
-
- #validate handler
- if not is_crypt_handler(handler):
- raise TypeError, "object does not appear to be a crypt handler: %r" % (handler,)
- assert handler, "crypt handlers must be boolean True: %r" % (handler,)
-
- #if name specified, make sure it matched
- #(this is mainly used as a check to help __setattr__)
- if name:
- if name != handler.name:
- raise ValueError, "handlers must be stored only under their own name"
- else:
- name = handler.name
-
- #validate name
- if not name:
- raise ValueError, "name is null: %r" % (name,)
- if name.lower() != name:
- raise ValueError, "name must be lower-case: %r" % (name,)
- if not _name_re.match(name):
- raise ValueError, "invalid characters in name (must be 3+ characters, begin with a-z, and contain only underscore, a-z, 0-9): %r" % (name,)
-
- #check for existing handler
- other = _handlers.get(name)
- if other:
- if other is handler:
- return #already registered
- if force:
- log.warning("overriding previous handler registered to name %r: %r", name, other)
- else:
- raise KeyError, "a handler has already registered for the name %r: %r (use force=True to override)" % (name, other)
-
- #register handler in dict
- _handlers[name] = handler
- log.info("registered crypt handler %r: %r", name, handler)
-
-def get_crypt_handler(name, default=Undef):
- """return handler for specified password hash scheme.
-
- this method looks up a handler for the specified scheme.
- if the handler is not already loaded,
- it checks if the location of one is known (:func:`register_crypt_handler`)
- and loads it first.
-
- :arg name: name of handler to return
- :param default: if specified, returns default value if no handler found.
-
- :raises KeyError: if no handler matching that name is found, and no default specified
-
- :returns: handler attached to name, or default if specified
- """
- global _handlers, _handler_locations
-
- #check if handler loaded
- handler = _handlers.get(name, None)
- if handler:
- return handler
-
- #normalize name (and if changed, check dict again)
- alt = name.replace("-","_").lower()
- if alt != name:
- warn("handler names be lower-case, and use underscores instead of hyphens: %r => %r" % (name, alt))
- name = alt
-
- #check if handler loaded
- handler = _handlers.get(name)
- if handler:
- return handler
-
- #check if lazy load mapping has been specified for this driver
- route = _handler_locations.get(name)
- if route:
- modname, modattr = route
-
- #try to load the module - any import errors indicate runtime config,
- # either missing packages, or bad path provided to register_crypt_handler_path()
- mod = __import__(modname, None, None, ['dummy'], 0)
-
- #first check if importing module triggered register_crypt_handler(),
- #(though this is discouraged due to it's magical implicitness)
- handler = _handlers.get(name)
- if handler:
- #XXX: issue deprecation warning here?
- assert is_crypt_handler(handler), "unexpected object: name=%r object=%r" % (name, handler)
- return handler
-
- #then get real handler & register it
- handler = getattr(mod, modattr)
- register_crypt_handler(handler, name=name)
- return handler
-
- #fail!
- if default is Undef:
- raise KeyError, "no crypt handler found for algorithm: %r" % (name,)
- else:
- return default
-
-def list_crypt_handlers(loaded_only=False):
- "return sorted list of all known crypt handler names"
- global _handlers, _handler_locations
- names = set(_handlers)
- if not loaded_only:
- names.update(_handler_locations)
- return sorted(names)
-
-#=========================================================
#crypt policy
#=========================================================
def _parse_policy_key(key):
diff --git a/passlib/hash.py b/passlib/hash.py
index a246996..176dbfc 100644
--- a/passlib/hash.py
+++ b/passlib/hash.py
@@ -19,7 +19,7 @@ NOTE:
#import proxy object, and replace this module with it.
#this should cause any import commands to return that object,
#not this module
-from passlib.base import _proxy
+from passlib.registry import _proxy
import sys
sys.modules['passlib.hash'] = _proxy
del sys, _proxy
diff --git a/passlib/hosts.py b/passlib/hosts.py
index 78ae649..b6a3d03 100644
--- a/passlib/hosts.py
+++ b/passlib/hosts.py
@@ -3,7 +3,7 @@
#imports
#=========================================================
#pkg
-from passlib.base import CryptContext
+from passlib.context import CryptContext
#local
__all__ = [
"default_context",
diff --git a/passlib/registry.py b/passlib/registry.py
new file mode 100644
index 0000000..e7a8c3e
--- /dev/null
+++ b/passlib/registry.py
@@ -0,0 +1,257 @@
+"""passlib.registry - registry for password hash handlers"""
+#=========================================================
+#imports
+#=========================================================
+#core
+import inspect
+import re
+import logging; log = logging.getLogger(__name__)
+from warnings import warn
+#site
+#libs
+from passlib.utils import Undef, is_crypt_handler
+#pkg
+#local
+__all__ = [
+ "register_crypt_handler_path",
+ "register_crypt_handler",
+ "get_crypt_handler",
+ "list_crypt_handlers",
+]
+
+#=========================================================
+#registry proxy object
+#=========================================================
+class PasslibRegistryProxy(object):
+ """proxy module passlib.hash
+
+ this module is in fact an object which lazy-loads
+ the requested password hash algorithm from wherever it has been stored.
+ it acts as a thin wrapper around :func:`passlib.registry.get_crypt_handler`.
+ """
+ __name__ = "passlib.hash"
+ __package__ = None
+
+ def __getattr__(self, attr):
+ if attr.startswith("_"):
+ raise AttributeError, "missing attribute: %r" % (attr,)
+ handler = get_crypt_handler(attr, None)
+ if handler:
+ return handler
+ else:
+ raise AttributeError, "unknown password hash: %r" % (attr,)
+
+ def __setattr__(self, attr, value):
+ register_crypt_handler(value, name=attr)
+
+ def __repr__(self):
+ return "<proxy module 'passlib.hash'>"
+
+ def __dir__(self):
+ #add in handlers that will be lazy-loaded,
+ #otherwise this is std dir implementation
+ attrs = set(dir(self.__class__))
+ attrs.update(self.__dict__)
+ attrs.update(_handler_locations)
+ return sorted(attrs)
+
+ #=========================================================
+ #eoc
+ #=========================================================
+
+#singleton instance
+_proxy = PasslibRegistryProxy()
+
+#==========================================================
+#internal registry state
+#==========================================================
+
+#: dict mapping name -> handler for all loaded handlers. uses proxy's dict so they stay in sync.
+_handlers = _proxy.__dict__
+
+#: dict mapping name -> (module path, attribute) for lazy-loading of handlers
+_handler_locations = {
+ #NOTE: this is a hardcoded list of the handlers built into passlib,
+ #applications should call register_crypt_handler_location() to add their own
+ "apr_md5_crypt": ("passlib.drivers.md5_crypt", "apr_md5_crypt"),
+ "bcrypt": ("passlib.drivers.bcrypt", "bcrypt"),
+ "bigcrypt": ("passlib.drivers.des_crypt", "bigcrypt"),
+ "bsdi_crypt": ("passlib.drivers.des_crypt", "bsdi_crypt"),
+ "crypt16": ("passlib.drivers.des_crypt", "crypt16"),
+ "des_crypt": ("passlib.drivers.des_crypt", "des_crypt"),
+ "hex_md4": ("passlib.drivers.digests", "hex_md4"),
+ "hex_md5": ("passlib.drivers.digests", "hex_md5"),
+ "hex_sha1": ("passlib.drivers.digests", "hex_sha1"),
+ "hex_sha256": ("passlib.drivers.digests", "hex_sha256"),
+ "hex_sha512": ("passlib.drivers.digests", "hex_sha512"),
+ "ldap_cleartext": ("passlib.drivers.ldap_digests","ldap_cleartext"),
+ "ldap_md5": ("passlib.drivers.ldap_digests","ldap_md5"),
+ "ldap_sha1": ("passlib.drivers.ldap_digests","ldap_sha1"),
+ "ldap_salted_md5": ("passlib.drivers.ldap_digests","ldap_salted_md5"),
+ "ldap_salted_sha1": ("passlib.drivers.ldap_digests","ldap_salted_sha1"),
+ "md5_crypt": ("passlib.drivers.md5_crypt", "md5_crypt"),
+ "mysql323": ("passlib.drivers.mysql", "mysql323"),
+ "mysql41": ("passlib.drivers.mysql", "mysql41"),
+ "nthash": ("passlib.drivers.nthash", "nthash"),
+ "oracle10": ("passlib.drivers.oracle", "oracle10"),
+ "oracle11": ("passlib.drivers.oracle", "oracle11"),
+ "phpass": ("passlib.drivers.phpass", "phpass"),
+ "plaintext": ("passlib.drivers.misc", "plaintext"),
+ "postgres_md5": ("passlib.drivers.postgres", "postgres_md5"),
+ "sha1_crypt": ("passlib.drivers.sha1_crypt", "sha1_crypt"),
+ "sha256_crypt": ("passlib.drivers.sha2_crypt", "sha256_crypt"),
+ "sha512_crypt": ("passlib.drivers.sha2_crypt", "sha512_crypt"),
+ "sun_md5_crypt": ("passlib.drivers.sun_md5_crypt","sun_md5_crypt"),
+ "unix_fallback": ("passlib.drivers.misc", "unix_fallback"),
+}
+
+#: master regexp for detecting valid handler names
+_name_re = re.compile("^[a-z][_a-z0-9]{2,}$")
+
+#==========================================================
+#registry frontend functions
+#==========================================================
+def register_crypt_handler_path(name, path):
+ """register location to lazy-load handler when requested.
+
+ custom hashes may be registered via :func:`register_crypt_handler`,
+ or they may be registered by this function,
+ which will delay actually importing and loading the handler
+ until a call to :func:`get_crypt_handler` is made for the specified name.
+
+ :arg name: name of handler
+ :arg path: module import path
+
+ the specified module path should contain a password hash handler
+ called :samp:`{name}`, or the path may contain a semicolon,
+ specifying the module and module attribute to use.
+ """
+ global _handler_locations
+ if ':' in path:
+ modname, modattr = path.split(":")
+ else:
+ modname, modattr = path, name
+ _handler_locations[name] = (modname, modattr)
+
+def register_crypt_handler(handler, force=False, name=None):
+ """register password hash handler.
+
+ this method registers a handler with the internal passlib registry,
+ so that it will be returned by :func:`get_crypt_handler` when requested.
+
+ :arg handler: the password hash handler to register
+ :param force: force override of existing handler (defaults to False)
+
+ :raises KeyError:
+ if a (different) handler was already registered with
+ the same name, and ``force=True`` was not specified.
+ """
+ global _handlers, _name_re
+
+ #validate handler
+ if not is_crypt_handler(handler):
+ raise TypeError, "object does not appear to be a crypt handler: %r" % (handler,)
+ assert handler, "crypt handlers must be boolean True: %r" % (handler,)
+
+ #if name specified, make sure it matched
+ #(this is mainly used as a check to help __setattr__)
+ if name:
+ if name != handler.name:
+ raise ValueError, "handlers must be stored only under their own name"
+ else:
+ name = handler.name
+
+ #validate name
+ if not name:
+ raise ValueError, "name is null: %r" % (name,)
+ if name.lower() != name:
+ raise ValueError, "name must be lower-case: %r" % (name,)
+ if not _name_re.match(name):
+ raise ValueError, "invalid characters in name (must be 3+ characters, begin with a-z, and contain only underscore, a-z, 0-9): %r" % (name,)
+
+ #check for existing handler
+ other = _handlers.get(name)
+ if other:
+ if other is handler:
+ return #already registered
+ if force:
+ log.warning("overriding previous handler registered to name %r: %r", name, other)
+ else:
+ raise KeyError, "a handler has already registered for the name %r: %r (use force=True to override)" % (name, other)
+
+ #register handler in dict
+ _handlers[name] = handler
+ log.info("registered crypt handler %r: %r", name, handler)
+
+def get_crypt_handler(name, default=Undef):
+ """return handler for specified password hash scheme.
+
+ this method looks up a handler for the specified scheme.
+ if the handler is not already loaded,
+ it checks if the location of one is known (:func:`register_crypt_handler`)
+ and loads it first.
+
+ :arg name: name of handler to return
+ :param default: if specified, returns default value if no handler found.
+
+ :raises KeyError: if no handler matching that name is found, and no default specified
+
+ :returns: handler attached to name, or default if specified
+ """
+ global _handlers, _handler_locations
+
+ #check if handler loaded
+ handler = _handlers.get(name, None)
+ if handler:
+ return handler
+
+ #normalize name (and if changed, check dict again)
+ alt = name.replace("-","_").lower()
+ if alt != name:
+ warn("handler names be lower-case, and use underscores instead of hyphens: %r => %r" % (name, alt))
+ name = alt
+
+ #check if handler loaded
+ handler = _handlers.get(name)
+ if handler:
+ return handler
+
+ #check if lazy load mapping has been specified for this driver
+ route = _handler_locations.get(name)
+ if route:
+ modname, modattr = route
+
+ #try to load the module - any import errors indicate runtime config,
+ # either missing packages, or bad path provided to register_crypt_handler_path()
+ mod = __import__(modname, None, None, ['dummy'], 0)
+
+ #first check if importing module triggered register_crypt_handler(),
+ #(though this is discouraged due to it's magical implicitness)
+ handler = _handlers.get(name)
+ if handler:
+ #XXX: issue deprecation warning here?
+ assert is_crypt_handler(handler), "unexpected object: name=%r object=%r" % (name, handler)
+ return handler
+
+ #then get real handler & register it
+ handler = getattr(mod, modattr)
+ register_crypt_handler(handler, name=name)
+ return handler
+
+ #fail!
+ if default is Undef:
+ raise KeyError, "no crypt handler found for algorithm: %r" % (name,)
+ else:
+ return default
+
+def list_crypt_handlers(loaded_only=False):
+ "return sorted list of all known crypt handler names"
+ global _handlers, _handler_locations
+ names = set(_handlers)
+ if not loaded_only:
+ names.update(_handler_locations)
+ return sorted(names)
+
+#=========================================================
+# eof
+#=========================================================
diff --git a/passlib/servers.py b/passlib/servers.py
index f0243a4..ef5ee12 100644
--- a/passlib/servers.py
+++ b/passlib/servers.py
@@ -7,7 +7,7 @@ import platform
import logging; log = logging.getLogger(__name__)
#site
#libs
-from passlib.base import CryptContext
+from passlib.context import CryptContext
#pkg
#local
__all__ = [
diff --git a/passlib/tests/_test_bad_register.py b/passlib/tests/_test_bad_register.py
index 8554ee5..1a4a861 100644
--- a/passlib/tests/_test_bad_register.py
+++ b/passlib/tests/_test_bad_register.py
@@ -1,6 +1,6 @@
-"helper for method in test_base.py"
+"helper for method in test_registry.py"
-from passlib.base import register_crypt_handler
+from passlib.registry import register_crypt_handler
from passlib.utils.drivers import BaseHash
class dummy_bad(BaseHash):
diff --git a/passlib/tests/genconfig.py b/passlib/tests/genconfig.py
index 257282c..c836b80 100644
--- a/passlib/tests/genconfig.py
+++ b/passlib/tests/genconfig.py
@@ -15,7 +15,7 @@ import time
import sys
#site
#pkg
-from passlib.base import get_crypt_handler
+from passlib.registry import get_crypt_handler
#local
log = logging.getLogger(__name__)
#=========================================================
diff --git a/passlib/tests/test_base.py b/passlib/tests/test_context.py
index ba0132b..1870c80 100644
--- a/passlib/tests/test_base.py
+++ b/passlib/tests/test_context.py
@@ -12,10 +12,8 @@ import warnings
import sys
#site
#pkg
-from passlib import hash, base
-from passlib.base import CryptContext, CryptPolicy, \
- register_crypt_handler, register_crypt_handler_path, \
- get_crypt_handler, list_crypt_handlers
+from passlib import hash
+from passlib.context import CryptContext, CryptPolicy
from passlib.utils.drivers import BaseHash
from passlib.tests.utils import TestCase, mktemp, catch_warnings
from passlib.drivers.md5_crypt import md5_crypt as AnotherHash
@@ -24,126 +22,6 @@ from passlib.tests.test_utils_drivers import UnsaltedHash, SaltedHash
log = getLogger(__name__)
#=========================================================
-#test registry
-#=========================================================
-class dummy_0(BaseHash):
- name = "dummy_0"
- setting_kwds = ()
-
-class alt_dummy_0(BaseHash):
- name = "dummy_0"
- setting_kwds = ()
-
-dummy_x = 1
-
-def unload_handler_name(name):
- if hasattr(hash, name):
- delattr(hash, name)
-
- #NOTE: this messes w/ internals of registry, shouldn't be used publically.
- paths = base._handler_locations
- if name in paths:
- del paths[name]
-
-class RegistryTest(TestCase):
-
- case_prefix = "passlib registry"
-
- def tearDown(self):
- for name in ("dummy_0", "dummy_1", "dummy_x", "dummy_bad"):
- unload_handler_name(name)
-
- def test_hash_proxy(self):
- "test passlib.hash proxy object"
- dir(hash)
- repr(hash)
- self.assertRaises(AttributeError, getattr, hash, 'fooey')
-
- 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 = base._handler_locations
-
- #check namespace is clear
- self.assertTrue('dummy_0' not in paths)
- self.assertFalse(hasattr(hash, 'dummy_0'))
-
- #try lazy load
- register_crypt_handler_path('dummy_0', 'passlib.tests.test_base')
- 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', 'passlib.tests.test_base: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', 'passlib.tests.test_base')
- self.assertRaises(TypeError, get_crypt_handler, 'dummy_x')
-
- #check lazy load w/ wrong name fails
- register_crypt_handler_path('alt_dummy_0', 'passlib.tests.test_base')
- self.assertRaises(ValueError, get_crypt_handler, "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 passlib.tests import _test_bad_register as tbr
- self.assertIs(h, tbr.alt_dummy_bad)
-
- def test_register_crypt_handler(self):
- "test register_crypt_handler()"
-
- self.assertRaises(TypeError, register_crypt_handler, {})
-
- self.assertRaises(ValueError, register_crypt_handler, BaseHash)
- self.assertRaises(ValueError, register_crypt_handler, type('x', (BaseHash,), dict(name="AB_CD")))
- self.assertRaises(ValueError, register_crypt_handler, type('x', (BaseHash,), dict(name="ab-cd")))
-
- class dummy_1(BaseHash):
- name = "dummy_1"
-
- class dummy_1b(BaseHash):
- 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_get_crypt_handler(self):
- "test get_crypt_handler()"
-
- class dummy_1(BaseHash):
- name = "dummy_1"
-
- self.assertRaises(KeyError, get_crypt_handler, "dummy_1")
-
- register_crypt_handler(dummy_1)
- self.assertIs(get_crypt_handler("dummy_1"), dummy_1)
-
- with catch_warnings():
- warnings.filterwarnings("ignore", "handler names be lower-case, and use underscores instead of hyphens:.*", UserWarning)
- self.assertIs(get_crypt_handler("DUMMY-1"), dummy_1)
-
-#=========================================================
#
#=========================================================
class CryptPolicyTest(TestCase):
diff --git a/passlib/tests/test_registry.py b/passlib/tests/test_registry.py
new file mode 100644
index 0000000..e5999d7
--- /dev/null
+++ b/passlib/tests/test_registry.py
@@ -0,0 +1,145 @@
+"""tests for passlib.pwhash -- (c) Assurance Technologies 2003-2009"""
+#=========================================================
+#imports
+#=========================================================
+from __future__ import with_statement
+#core
+import hashlib
+from logging import getLogger
+import os
+import time
+import warnings
+import sys
+#site
+#pkg
+from passlib import hash, registry
+from passlib.registry import register_crypt_handler, register_crypt_handler_path, \
+ get_crypt_handler, list_crypt_handlers
+from passlib.utils.drivers import BaseHash
+from passlib.tests.utils import TestCase, mktemp, catch_warnings
+#module
+log = getLogger(__name__)
+
+#=========================================================
+#test registry
+#=========================================================
+class dummy_0(BaseHash):
+ name = "dummy_0"
+ setting_kwds = ()
+
+class alt_dummy_0(BaseHash):
+ name = "dummy_0"
+ setting_kwds = ()
+
+dummy_x = 1
+
+def unload_handler_name(name):
+ if hasattr(hash, name):
+ delattr(hash, name)
+
+ #NOTE: this messes w/ internals of registry, shouldn't be used publically.
+ paths = registry._handler_locations
+ if name in paths:
+ del paths[name]
+
+class RegistryTest(TestCase):
+
+ case_prefix = "passlib registry"
+
+ def tearDown(self):
+ for name in ("dummy_0", "dummy_1", "dummy_x", "dummy_bad"):
+ unload_handler_name(name)
+
+ def test_hash_proxy(self):
+ "test passlib.hash proxy object"
+ dir(hash)
+ repr(hash)
+ self.assertRaises(AttributeError, getattr, hash, 'fooey')
+
+ 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._handler_locations
+
+ #check namespace is clear
+ self.assertTrue('dummy_0' not in paths)
+ self.assertFalse(hasattr(hash, 'dummy_0'))
+
+ #try lazy load
+ register_crypt_handler_path('dummy_0', 'passlib.tests.test_registry')
+ 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', 'passlib.tests.test_registry: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', 'passlib.tests.test_registry')
+ self.assertRaises(TypeError, get_crypt_handler, 'dummy_x')
+
+ #check lazy load w/ wrong name fails
+ register_crypt_handler_path('alt_dummy_0', 'passlib.tests.test_registry')
+ self.assertRaises(ValueError, get_crypt_handler, "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 passlib.tests import _test_bad_register as tbr
+ self.assertIs(h, tbr.alt_dummy_bad)
+
+ def test_register_crypt_handler(self):
+ "test register_crypt_handler()"
+
+ self.assertRaises(TypeError, register_crypt_handler, {})
+
+ self.assertRaises(ValueError, register_crypt_handler, BaseHash)
+ self.assertRaises(ValueError, register_crypt_handler, type('x', (BaseHash,), dict(name="AB_CD")))
+ self.assertRaises(ValueError, register_crypt_handler, type('x', (BaseHash,), dict(name="ab-cd")))
+
+ class dummy_1(BaseHash):
+ name = "dummy_1"
+
+ class dummy_1b(BaseHash):
+ 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_get_crypt_handler(self):
+ "test get_crypt_handler()"
+
+ class dummy_1(BaseHash):
+ name = "dummy_1"
+
+ self.assertRaises(KeyError, get_crypt_handler, "dummy_1")
+
+ register_crypt_handler(dummy_1)
+ self.assertIs(get_crypt_handler("dummy_1"), dummy_1)
+
+ with catch_warnings():
+ warnings.filterwarnings("ignore", "handler names be lower-case, and use underscores instead of hyphens:.*", UserWarning)
+ self.assertIs(get_crypt_handler("DUMMY-1"), dummy_1)
+
+#=========================================================
+#EOF
+#=========================================================
diff --git a/passlib/tests/test_utils.py b/passlib/tests/test_utils.py
index ad9dfaf..f0a5686 100644
--- a/passlib/tests/test_utils.py
+++ b/passlib/tests/test_utils.py
@@ -10,7 +10,7 @@ import random
#pkg
#module
from passlib import utils
-from passlib.base import CryptContext
+from passlib.context import CryptContext
from passlib.utils import h64, des, Undef
from passlib.utils.md4 import md4
from passlib.tests.utils import TestCase, Params as ak, enable_option