diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2011-04-26 16:24:07 -0400 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2011-04-26 16:24:07 -0400 |
| commit | aa38d176519ae0744851e865b948e994ca8e89b3 (patch) | |
| tree | 2a02e5fccbef5431225fbfea5d849e742e752bc1 /docs | |
| parent | 6d54c06e246400e985e582b4d73ad1b4e3d62f37 (diff) | |
| download | passlib-aa38d176519ae0744851e865b948e994ca8e89b3.tar.gz | |
rearranged/expanded CryptContext docs
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/contents.rst | 2 | ||||
| -rw-r--r-- | docs/lib/passlib.apps.rst | 4 | ||||
| -rw-r--r-- | docs/lib/passlib.context-interface.rst | 12 | ||||
| -rw-r--r-- | docs/lib/passlib.context-options.rst | 10 | ||||
| -rw-r--r-- | docs/lib/passlib.context-usage.rst | 310 | ||||
| -rw-r--r-- | docs/lib/passlib.context.rst | 266 | ||||
| -rw-r--r-- | docs/lib/passlib.hash.ldap_digests.rst | 1 | ||||
| -rw-r--r-- | docs/password_hash_api.rst | 1 |
8 files changed, 348 insertions, 258 deletions
diff --git a/docs/contents.rst b/docs/contents.rst index 4912478..0388691 100644 --- a/docs/contents.rst +++ b/docs/contents.rst @@ -9,8 +9,6 @@ Table Of Contents overview lib/passlib.context - lib/passlib.context-interface - lib/passlib.context-options lib/passlib.apps lib/passlib.apache lib/passlib.hosts diff --git a/docs/lib/passlib.apps.rst b/docs/lib/passlib.apps.rst index 2ee37b4..27e2058 100644 --- a/docs/lib/passlib.apps.rst +++ b/docs/lib/passlib.apps.rst @@ -7,8 +7,8 @@ This lists a number of :class:`!CryptContext` instances that are predefined by PassLib for easily handling the multiple formats used by various applications. -(For details about how to *use* a :class:`!CryptContext` instance, -see the documentation for the :class:`CryptContext` class itself). +(For details about how to use a :class:`!CryptContext` instance, +see :doc:`passlib.context-usage`). .. _quickstart-custom-applications: diff --git a/docs/lib/passlib.context-interface.rst b/docs/lib/passlib.context-interface.rst index 0c9d2e6..8331992 100644 --- a/docs/lib/passlib.context-interface.rst +++ b/docs/lib/passlib.context-interface.rst @@ -3,7 +3,7 @@ .. _cryptcontext-interface: =============================================== -:mod:`passlib.context` - CryptContext Interface +:mod:`passlib.context` - Module Contents =============================================== .. currentmodule:: passlib.context @@ -13,12 +13,18 @@ and :class:`!CryptPolicy`. .. seealso:: - * :doc:`passlib.context-options` -- for a list of all the keyword options accepted by these classes. + * :doc:`passlib.context-usage` - * :doc:`passlib.context` -- for an overview and some usage examples. + * :doc:`passlib.context-options` +The Context Object +================== .. autoclass:: CryptContext(schemes=None, policy=<default policy>, \*\*kwds) +The Policy Object +================= .. autoclass:: CryptPolicy(\*\*kwds) +Other Helpers +============= .. autoclass:: LazyCryptContext([schemes=None,] **kwds [, create_policy=None]) diff --git a/docs/lib/passlib.context-options.rst b/docs/lib/passlib.context-options.rst index b15dc5d..e799cdf 100644 --- a/docs/lib/passlib.context-options.rst +++ b/docs/lib/passlib.context-options.rst @@ -3,7 +3,7 @@ .. _cryptcontext-options: ============================================= -:mod:`passlib.context` - CryptContext options +:mod:`passlib.context` - Constructor Options ============================================= .. currentmodule:: passlib.context @@ -15,7 +15,9 @@ which affect the context treats a particular type of hash: .. seealso:: - :doc:`passlib.context` -- for an overview of the classes and some usage examples. + * :doc:`passlib.context-usage` + + * :doc:`passlib.context-interface` Context Options =============== @@ -190,7 +192,9 @@ or viewed in the source code under ``$SOURCE/passlib/default.cfg``. Sample Policy File ================== -A sample policy file:: +A sample policy file: + +.. code-block:: ini [passlib] #configure what schemes the context supports (note the "context." prefix is implied for these keys) diff --git a/docs/lib/passlib.context-usage.rst b/docs/lib/passlib.context-usage.rst new file mode 100644 index 0000000..8464a1e --- /dev/null +++ b/docs/lib/passlib.context-usage.rst @@ -0,0 +1,310 @@ +.. index:: CryptContext; usage examples + +.. _cryptcontext-examples: + +==================================================== +:mod:`passlib.context` - Usage Examples +==================================================== + +.. currentmodule:: passlib.context + +This section gives examples on how to use the :class:`CryptContext` object +for a number of different use cases. + +.. seealso:: + + * :doc:`passlib.context-interface` + + * :doc:`passlib.context-options` + +Basic Usage +=========== +To start off with a simple example of how to create and use a CryptContext:: + + >>> from passlib.context import CryptContext + + >>> #create a new context that only understands Md5Crypt & DesCrypt: + >>> myctx = CryptContext([ "md5_crypt", "des_crypt" ]) + + >>> #unless overidden, the first hash listed + >>> #will be used as the default for encrypting + >>> #(in this case, md5_crypt): + >>> hash1 = myctx.encrypt("too many secrets") + >>> hash1 + '$1$nH3CrcVr$pyYzik1UYyiZ4Bvl1uCtb.' + + >>> #the scheme may be forced explicitly, + >>> #though it must be one of the ones recognized by the context: + >>> hash2 = myctx.encrypt("too many secrets", scheme="des_crypt") + >>> hash2 + 'm9pvLj4.hWxJU' + + >>> #verification will autodetect the correct type of hash: + >>> myctx.verify("too many secrets", hash1) + True + >>> myctx.verify("too many secrets", hash2) + True + >>> myctx.verify("too many socks", hash2) + False + + >>> #you can also have it identify the algorithm in use: + >>> myctx.identify(hash1) + 'md5_crypt' + + >>> #or just return the handler instance directly: + >>> myctx.identify(hash1, resolve=True) + <class 'passlib.handlers.md5_crypt.md5_crypt'> + +.. _using-predefined-contexts: + +Using Predefined CryptContexts +============================== +Passlib contains a number of pre-made :class:`!CryptContext` instances, +configured for various purposes +(see :mod:`passlib.apps` and :mod:`passlib.hosts`). +These can be used directly by importing them from passlib, +such as the following example: + + >>> from passlib.apps import ldap_context as pwd_context + >>> pwd_context.encrypt("somepass") + '{SSHA}k4Ap0wYJWMrkgNhptlntsPGETBBwEoSH' + +However, applications which use the predefined contexts will frequently +find they need to modify the context in some way, such as selecting +a different default hash scheme. This is best done by importing +the original context, and then making an application-specific +copy; using the :meth:`CryptContext.replace` method to create +a mutated copy of the original object:: + + >>> from passlib.apps import ldap_context + >>> pwd_context = ldap_context.replace(default="ldap_md5_crypt") + >>> pwd_context.encrypt("somepass") + '{CRYPT}$1$Cw7t4sbP$dwRgCMc67mOwwus9m33z71' + +Examining a CryptContext Instance +================================= +All configuration options for a :class:`!CryptContext` instance +are stored in a :class:`!CryptPolicy` instance accessible through +the :attr:`CryptContext.policy` attribute:: + + >>> from passlib.context import CryptContext + >>> myctx = CryptContext([ "md5_crypt", "des_crypt" ], deprecated="des_crypt") + + >>> #get a list of schemes recognized in this context: + >>> myctx.policy.schemes() + [ 'md5-crypt', 'bcrypt' ] + + >>> #get the default handler class : + >>> myctx.policy.get_handler() + <class 'passlib.handlers.md5_crypt.md5_crypt'> + +See the :class:`CryptPolicy` class for more details on it's interface. + +Full Integration Example +======================== +The following is an extended example of how PassLib can be integrated into an existing +application to provide runtime policy changes, deprecated hash migration, +and other features. This is example uses a lot of different features, +and many developers will want to pick and choose what they need from this example. +The totality of this example is overkill for most simple applications. + +Policy Configuration File +------------------------- + +While it is possible to create a CryptContext instance manually, or to import an existing one, +applications with advanced policy requirements may want to create a hash policy file +(options show below are detailed in :ref:`cryptcontext-options`): + +.. code-block:: ini + + ; the options file uses the INI file format, + ; and passlib will only read the section named "passlib", + ; so it can be included along with other application configuration. + + [passlib] + + ;setup the context to support pbkdf2_sha1, along with legacy md5_crypt hashes: + schemes = pbkdf2_sha1, md5_crypt + + ;flag md5_crypt as deprecated + ; (existing md5_crypt hashes will be flagged as needs-updating) + deprecated = md5_crypt + + ;set verify to always take at least 1/10th of a second + min_verify_time = 0.1 + + ;set boundaries for pbkdf2 rounds parameter + ; (pbkdf2 hashes outside this range will be flagged as needs-updating) + pbkdf2_sha1.min_rounds = 10000 + pbkdf2_sha1.max_rounds = 50000 + + ;set the default rounds to use when encrypting new passwords. + ;the 'vary' field will cause each new hash to randomly vary + ;from the default by the specified %. + pbkdf2_sha1.default_rounds = 20000 + pbkdf2_sha1.vary_rounds = 10% + + ;applications can choose to treat certain user accounts differently, + ;by assigning different types of account to a 'user category', + ;and setting special policy options for that category. + ;this create a category named 'admin', which will have a larger default rounds value. + admin.pbkdf2_sha1.min_rounds = 40000 + admin.pbkdf2_sha1.default_rounds = 50000 + +Initializing the CryptContext +----------------------------- +Applications which choose to use a policy file will typically want +to create the CryptContext at the module level, and then load +the configuration once the application starts: + +1. Within a common module in your application (eg ``myapp.model.security``):: + + # + #create a crypt context that can be imported and used wherever is needed... + #the instance will be configured later. + # + from passlib.context import CryptContext + user_pwd_context = CryptContext() + +2. Within some startup function within your application:: + + # + #when the app starts, import the context from step 1 and + #configure it... such as by loading a policy file (see above) + # + + from myapp.model.security import user_pwd_context + from passlib.context import CryptPolicy + + def myapp_startup(): + + # + # ... other code ... + # + + # vars: + # policy_path - path to policy file defined in previous step + # + user_pwd_context.policy = CryptPolicy.from_path(policy_path) + + # + #if you want to reconfigure the context without restarting the application, + #simply repeat the above step at another point. + # + + # + # ... other code ... + # + +Encrypting New Passwords +------------------------ +When it comes time to create a new user's password, insert +the following code in the correct function:: + + from myapp.model.security import user_pwd_context + + def handle_user_creation(): + + # + # ... other code ... + # + + # vars: + # 'secret' containing the putative password + # 'category' containing a category assigned to the user account + # + + hash = user_pwd_context.encrypt(secret, category=category) + + #... perform appropriate actions to store hash... + + # + # ... other code ... + # + +.. note:: + + In the above code, the 'category' kwd can be omitted entirely, *OR* + set to a string matching a user category specified in the policy file. + In the latter case, any category-specific policy settings will be enforced. + For this example, assume it's ``None`` for most users, and ``"admin"`` for special users. + this namespace is entirely application chosen, it just has to match the policy file. + + See :ref:`user-categories` for more details. + +Verifying Existing Passwords +---------------------------- +Finally, when it comes time to check a users' password, insert +the following code at the correct place:: + + from myapp.model.security import user_pwd_context + + def handle_user_login(): + + # + # ... other code ... + # + + # + #vars: + # 'hash' containing the specified user's hash, + # 'secret' containing the putative password + # 'category' containing a category assigned to the user account + # + #see note in "Encrypting New Passwords" about the category kwd + # + + ok = user_pwd_context.verify(secret, hash, category=category) + if not ok: + #... password did not match. do mean things ... + pass + + else: + #... password matched ... + #... do successful login actions ... + pass + +Verifying & Migrating Existing Passwords +---------------------------------------- +The CryptContext object offers the ability to deprecate schemes, +set lower strength bounds, and then flag any existing hashes which +violate these limits. +Applications which want to re-encrypt any deprecated hashes +found in their database should use the following template +instead of the one found in the previous step:: + + from myapp.model.security import user_pwd_context + + def handle_user_login(): + + # + # ... other code ... + # + + # + #this example both checks the user's password AND upgrades deprecated hashes... + #given the following variables: + # + #vars: + # 'hash' containing the specified user's hash, + # 'secret' containing the putative password + # 'category' containing a category assigned to the user account + # + #see note in "Encrypting New Passwords" about the category kwd + # + + ok, new_hash = user_pwd_context.verify_and_update(secret, hash, category=category) + if not ok: + #... password did not match. do mean things ... + pass + + else: + #... password matched ... + + if new_hash: + # old hash was deprecated by policy. + + # ... replace hash w/ new_hash for user account ... + pass + + #... do successful login actions ... diff --git a/docs/lib/passlib.context.rst b/docs/lib/passlib.context.rst index 769815c..ecc795e 100644 --- a/docs/lib/passlib.context.rst +++ b/docs/lib/passlib.context.rst @@ -9,258 +9,30 @@ .. module:: passlib.context :synopsis: CryptContext class for managing multiple password hash schemes -Overview -======== -Different storage contexts (eg: linux shadow files vs openbsd shadow files) -may use different sets and subsets of the available algorithms. -Similarly, over time, applications may need to deprecate password schemes -in favor of newer ones, or raise the number of rounds required -by existing hashes. +Though there is a wide range of password hashing schemes, +within a specific context (like a linux "shadow" file) +only a select list of schemes will be used. +As time goes on, new schemes are added and made the default, +the strength of existing schemes is tweaked, and other schemes are deprecated entirely. +Throughout all this, existing password hashes that don't comply +with the new policies must be detected and rehashed using the +new default configuration. In order to automate as much of these tasks as possible, +this module provides the :class:`CryptContext` class. -This module provides the :class:`CryptContext` class, which is designed -to handle (as much as possible) of these tasks for an application. Essentially, a :class:`!CryptContext` instance contains a list of hash handlers that it should recognize, along with information about which ones are deprecated, which is the default, and what configuration constraints an application has placed -on a particular hash. +on a particular scheme. While contexts can be created explicitly, +Passlib also offers a number of predefined :class:`!CryptContext` instances +which can be used out-of-the box (see :mod:`passlib.apps` and :mod:`passlib.hosts`), +or :ref:`modified <using-predefined-contexts>` to suit the application. -.. seealso:: +New users should see the usage examples +to get a feel for how the :class:`!CryptContext` class works. - * :doc:`passlib.context-interface` -- for a list of all class and instance methods +.. toctree:: - * :doc:`passlib.context-options` -- for a list of all the keyword options accepted by these classes. - -Usage Examples -============== - -Basic Usage ------------ -To start off with a simple example of how to create and use a CryptContext:: - - >>> from passlib.context import CryptContext - - >>> #create a new context that only understands Md5Crypt & DesCrypt: - >>> myctx = CryptContext([ "md5_crypt", "des_crypt" ]) - - >>> #unless overidden, the first hash listed - >>> #will be used as the default for encrypting - >>> #(in this case, md5_crypt): - >>> hash1 = myctx.encrypt("too many secrets") - >>> hash1 - '$1$nH3CrcVr$pyYzik1UYyiZ4Bvl1uCtb.' - - >>> #the scheme may be forced explicitly, - >>> #though it must be one of the ones recognized by the context: - >>> hash2 = myctx.encrypt("too many secrets", scheme="des_crypt") - >>> hash2 - 'm9pvLj4.hWxJU' - - >>> #verification will autodetect the correct type of hash: - >>> myctx.verify("too many secrets", hash1) - True - >>> myctx.verify("too many secrets", hash2) - True - >>> myctx.verify("too many socks", hash2) - False - - >>> #you can also have it identify the algorithm in use: - >>> myctx.identify(hash1) - 'md5_crypt' - - >>> #or just return the handler instance directly: - >>> myctx.identify(hash1, resolve=True) - <class 'passlib.handlers.md5_crypt.md5_crypt'> - -Policy Examination ------------------- -If introspection of a :class:`!CryptContext` instance -is needed, all configuration options are stored in a :class:`!CryptPolicy` instance accessible through -their ``policy`` attribute:: - - >>> from passlib.context import CryptContext - >>> myctx = CryptContext([ "md5_crypt", "des_crypt" ], deprecated="des_crypt") - - >>> #get a list of schemes recognized in this context: - >>> myctx.policy.schemes() - [ 'md5-crypt', 'bcrypt' ] - - >>> #get the default handler class : - >>> myctx.policy.get_handler() - <class 'passlib.handlers.md5_crypt.md5_crypt'> - -See the :class:`CryptPolicy` class for more details on it's interface. - -Full Integration ----------------- -The following is an extended example of how PassLib can be integrated into an existing -application to provide runtime policy changes, deprecated hash migration, -and other features. This is example uses a lot of different features, -and many developers will want to pick and choose what they need from this example. - -Policy Options File -................... -Instead of creating a CryptContext instance manually, -or importing an existing one (eg :data:`~passlib.apps.custom_app_context`), -applications with advanced policy requirements may want to create a hash policy file -(options show below are detailed in :ref:`cryptcontext-options`):: - - ; the options file uses the INI file format, - ; and passlib will only read the section named "passlib", - ; so it can be included along with other application configuration. - - [passlib] - - ;setup the context to support pbkdf2_sha1, along with legacy md5_crypt hashes: - schemes = pbkdf2_sha1, md5_crypt - - ;flag md5_crypt as deprecated - ; (existing md5_crypt hashes will be flagged as needs-updating) - deprecated = md5_crypt - - ;set verify to always take at least 1/10th of a second - min_verify_time = 0.1 - - ;set boundaries for pbkdf2 rounds parameter - ; (pbkdf2 hashes outside this range will be flagged as needs-updating) - pbkdf2_sha1.min_rounds = 10000 - pbkdf2_sha1.max_rounds = 50000 - - ;set the default rounds to use when encrypting new passwords. - ;the 'vary' field will cause each new hash to randomly vary - ;from the default by the specified %. - pbkdf2_sha1.default_rounds = 20000 - pbkdf2_sha1.vary_rounds = 10% - - ;applications can choose to treat certain user accounts differently, - ;by assigning different types of account to a 'user category', - ;and setting special policy options for that category. - ;this create a category named 'admin', which will have a larger default rounds value. - admin.pbkdf2_sha1.min_rounds = 40000 - admin.pbkdf2_sha1.default_rounds = 50000 - -Integrating a CryptContext --------------------------- -Integrating a crypt context is merely a matter of adding the following -bits of code to your application. - -1. Within a common module in your application (eg ``myapp.model.security``):: - - # - #create a crypt context that can be imported and used wherever is needed... - #the instance will be configured later. - # - from passlib.context import CryptContext - user_pwd_context = CryptContext() - -2. Within some startup function within your application:: - - # - #when the app starts, import the context from step 1 and - #configure it... such as by loading a policy file (see above) - # - - from myapp.model.security import user_pwd_context - from passlib.context import CryptPolicy - - def myapp_startup(): - - # - # ... other code ... - # - - user_pwd_context.policy = CryptPolicy.from_path(path_to_policy_file) - - # - #if you want to reconfigure the context without restarting the application, - #simply repeat the above step at another point. - # - - # - # ... other code ... - # - - -3. When it comes time to create a new user's password, insert - the following code in the correct function:: - - - from myapp.model.security import user_pwd_context - - def handle_user_creation(): - - # - # ... other code ... - # - - # - # 'secret' containing the putative password - # 'category' containing a category assigned to the user account - # - #the 'category' kwd can be omitted, OR: - #set to a string matching a user category specified in the policy file, - #in which case the category-specific policy settings will be enforced. - #for this example, assume it's None for most users, and "admin" for special users. - #this namespace is entirely application chosen, it just has to match the policy file. - # - - hash = user_pwd_context.encrypt(secret, category=category) - - #... perform appropriate actions to store hash... - - # - # ... other code ... - # - -4. Finally, when it comes time to check a users' password, insert - the following code at the correct place:: - - from myapp.model.security import user_pwd_context - - def handle_user_login(): - - # - # ... other code ... - # - - # - #this example both checks the user's password AND upgrades deprecated hashes... - #given the following variables: - # 'hash' containing the specified user's hash, - # 'secret' containing the putative password - # 'category' containing a category assigned to the user account - # - #see note in step 3 about the category kwd - # - - - ok, new_hash = user_pwd_context.verify_and_update(secret, hash, category=category) - if not ok: - #... password did not match. do mean things ... - else: - #... password matched ... - - if new_hash: - # old hash was deprecated by policy. - - # ... replace hash w/ new_hash for user account ... - - #... do successful login actions ... - - For those who don't want to use any of the hash update features, - the following template can be used instead:: - - from myapp.model.security import user_pwd_context - - def handle_user_login(): - - # - # ... other code ... - # - - ok = user_pwd_context.verify(secret, hash, category=category) - if not ok: - #... password did not match. do mean things ... - else: - #... password matched ... - #... do successful login actions ... + passlib.context-usage + passlib.context-interface + passlib.context-options diff --git a/docs/lib/passlib.hash.ldap_digests.rst b/docs/lib/passlib.hash.ldap_digests.rst index 8ed38e0..28f02a0 100644 --- a/docs/lib/passlib.hash.ldap_digests.rst +++ b/docs/lib/passlib.hash.ldap_digests.rst @@ -67,6 +67,7 @@ All of these classes follow a single basic format [#rfc]_: ldap_md5, ldap_sha1 These hashes have the format :samp:`{prefix}{checksum}`. + * :samp:`{prefix}` is `{MD5}` for ldap_md5, and `{SHA}` for ldap_sha1. * :samp:`{checksum}` is the base64 encoding diff --git a/docs/password_hash_api.rst b/docs/password_hash_api.rst index 1491280..c450c41 100644 --- a/docs/password_hash_api.rst +++ b/docs/password_hash_api.rst @@ -428,7 +428,6 @@ the following attributes are usually exposed. in that situtation should be documentated. .. - not yet documentated, want to make sure this is how we want to do things: .. attribute:: PasswordHash.default_salt_size |
