summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-04-17 15:37:59 -0400
committerEli Collins <elic@assurancetechnologies.com>2012-04-17 15:37:59 -0400
commit8341dc6179d656b898f944b0cd848c350c627aab (patch)
treedbdd0d4a2c4ea4cbf46320621a0cd1177bcb241b
parent6aafb5617e57a0145679e75f5cff023a95da18b2 (diff)
downloadpasslib-8341dc6179d656b898f944b0cd848c350c627aab.tar.gz
CryptPolicy deprecation, part 5 - updated docs, changelog, benchmark script
-rw-r--r--CHANGES53
-rw-r--r--admin/benchmarks.py257
-rw-r--r--docs/lib/passlib.context-interface.rst17
-rw-r--r--docs/lib/passlib.context-options.rst98
-rw-r--r--docs/lib/passlib.context-usage.rst80
-rw-r--r--docs/lib/passlib.ext.django.rst2
-rw-r--r--docs/lib/passlib.hosts.rst4
7 files changed, 318 insertions, 193 deletions
diff --git a/CHANGES b/CHANGES
index 3e93e0a..4e6f7a2 100644
--- a/CHANGES
+++ b/CHANGES
@@ -69,32 +69,45 @@ Release History
.. currentmodule:: passlib.context
* :class:`~CryptContext` now supports a :ref:`passprep <passprep>` option,
- which runs all passwords through SASLPrep (:rfc:`4013`)
+ which can be used to run all passwords through SASLPrep (:rfc:`4013`),
in order to normalize their unicode representation before hashing
[issue 24].
- * Internals of :class:`CryptPolicy` have been
- re-written drastically. Should now be stricter (and more informative)
- about invalid values, and common :class:`CryptContext`
- operations should all have much shorter code-paths.
+ * The :class:`!CryptContext` option
+ :ref:`min_verify_time <min-verify-time>` has been deprecated,
+ will be ignored in release 1.7, and will be removed in release 1.8.
- * Config parsing now done with :class:`SafeConfigParser`.
- :meth:`CryptPolicy.from_path` and :meth:`CryptPolicy.from_string`
- previously used :class:`!ConfigParser` interpolation.
- Release 1.5 switched to :class:`SafeConfigParser`,
+ * The internals of :class:`!CryptContext` have been rewritten
+ drastically. It's methods should now be stricter and more informative
+ about invalid values; and common :class:`!CryptContext` operations
+ should be faster, and have shorter internal code paths.
+
+ * The :attr:`!CryptContext.policy` attr, and the supporting
+ :class:`!CryptPolicy` class, have been deprecated in their entirety.
+
+ They will not be removed until Passlib 1.8, to give applications
+ which used these features time to migrate. Applications which did
+ not use either of these features explicitly should be unaffected by
+ this change.
+
+ The functionality of :class:`!CryptPolicy` has been merged
+ into the :class:`CryptContext` class, in order to simplify
+ the exposed interface. Information on migrating can be found
+ in the :class:`CryptPolicy` documentation, as well as in
+ the :exc:`DeprecationWarning` messages issued when a :class:`!CryptPolicy`
+ is invoked.
+
+ * :meth:`CryptContext.from_path` and :meth:`CryptContext.from_string`
+ (and the legacy :class:`CryptPolicy` object) now use stdlib's
+ :class:`!SafeConfigParser`.
+
+ Previous releases used the original :class:`!ConfigParser` interpolation.
+ Passlib 1.5 switched to :class:`SafeConfigParser`,
but kept support for the old format as a (deprecated) fallback.
This fallback has been removed in 1.6; any
- legacy config files may need to escape raw ``%`` characters
+ legacy config files may need to double any raw ``%`` characters
in order to load successfully.
- * The main CryptContext methods (e.g. :meth:`~CryptContext.encrypt`,
- and :meth:`~CryptContext.verify`) will now consistently raise
- a :exc:`TypeError` when called with ``hash=None`` or another
- non-string type, to match the :doc:`password-hash-api`.
- Under previous releases, they might return ``False``,
- raise :exc:`ValueError`, or raise :exc:`TypeError`,
- depending on the specific method and context settings.
-
Utils
.. currentmodule:: passlib.utils.handlers
@@ -137,10 +150,6 @@ Release History
* deprecated some unused functions in :mod:`!passlib.utils`,
they will be removed in release 1.7.
- * The :class:`!CryptContext` option
- :ref:`min_verify_time <min-verify-time>` has been deprecated,
- will be ignored in release 1.7, and will be removed in release 1.8.
-
Other
* The api for the :mod:`passlib.apache` module has been updated
diff --git a/admin/benchmarks.py b/admin/benchmarks.py
index 5cba45e..4b73ef2 100644
--- a/admin/benchmarks.py
+++ b/admin/benchmarks.py
@@ -7,15 +7,15 @@ parsing was being sped up. it could definitely be improved.
# init script env
#=============================================================================
import os, sys
-root_dir = os.path.join(os.path.dirname(__file__), os.path.pardir)
-sys.path.insert(0, root_dir)
+root = os.path.join(os.path.dirname(__file__), os.path.pardir)
+sys.path.insert(0, root)
#=============================================================================
# imports
#=============================================================================
# core
import logging; log = logging.getLogger(__name__)
-from timeit import Timer
+import os
import warnings
# site
# pkg
@@ -26,18 +26,99 @@ except ImportError:
import passlib.utils.handlers as uh
from passlib.utils.compat import u, print_, unicode
# local
-__all__ = [
-]
+
+#=============================================================================
+# benchmarking support
+#=============================================================================
+class benchmark:
+ "class to hold various benchmarking helpers"
+
+ @classmethod
+ def constructor(cls, **defaults):
+ """mark callable as something which should be benchmarked.
+ callable should return a function will be timed.
+ """
+ def marker(func):
+ if func.__doc__:
+ name = func.__doc__.splitlines()[0]
+ else:
+ name = func.__name__
+ func._benchmark_task = ("ctor", name, defaults)
+ return func
+ return marker
+
+ @classmethod
+ def run(cls, source, **defaults):
+ """run benchmark for all tasks in source, yielding result records"""
+ for obj in source.values():
+ for record in cls._run_object(obj, defaults):
+ yield record
+
+ @classmethod
+ def _run_object(cls, obj, defaults):
+ args = getattr(obj, "_benchmark_task", None)
+ if not args:
+ return
+ mode, name, options = args
+ kwds = defaults.copy()
+ kwds.update(options)
+ if mode == "ctor":
+ itr = obj()
+ if not hasattr(itr, "next"):
+ itr = [itr]
+ for func in itr:
+ # TODO: per function name & options
+ secs, precision = cls.measure(func, None, **kwds)
+ yield name, secs, precision
+ else:
+ raise ValueError("invalid mode: %r" % (mode,))
+
+ @staticmethod
+ def measure(func, setup=None, maxtime=1, bestof=3):
+ """timeit() wrapper which tries to get as accurate a measurement as
+ possible w/in maxtime seconds.
+
+ :returns:
+ ``(avg_seconds_per_call, log10_number_of_repetitions)``
+ """
+ from timeit import Timer
+ from math import log
+ timer = Timer(func, setup=setup or '')
+ number = 1
+ while True:
+ delta = min(timer.repeat(bestof, number))
+ maxtime -= delta*bestof
+ if maxtime < 0:
+ return delta/number, int(log(number, 10))
+ number *= 10
+
+ @staticmethod
+ def pptime(secs, precision=3):
+ """helper to pretty-print fractional seconds values"""
+ usec = int(secs * 1e6)
+ if usec < 1000:
+ return "%.*g usec" % (precision, usec)
+ msec = usec / 1000
+ if msec < 1000:
+ return "%.*g msec" % (precision, msec)
+ sec = msec / 1000
+ return "%.*g sec" % (precision, sec)
#=============================================================================
# utils
#=============================================================================
+sample_config_1p = os.path.join(root, "passlib", "tests", "sample_config_1s.cfg")
-class BlankHandler(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
+from passlib.context import CryptContext
+if hasattr(CryptContext, "from_path"):
+ CryptPolicy = None
+else:
+ from passlib.context import CryptPolicy
- setting_kwds = ("rounds", "salt", "salt_size")
+class BlankHandler(uh.HasRounds, uh.HasSalt, uh.GenericHandler):
name = "blank"
ident = u("$b$")
+ setting_kwds = ("rounds", "salt", "salt_size")
checksum_size = 1
min_salt_size = max_salt_size = 1
@@ -62,99 +143,109 @@ class AnotherHandler(BlankHandler):
name = "another"
ident = u("$a$")
+SECRET = u("toomanysecrets")
+OTHER = u("setecastronomy")
+
#=============================================================================
-# crypt context tests
+# CryptContext benchmarks
#=============================================================================
-def setup_policy():
- import os
- from passlib.context import CryptPolicy
- test_path = os.path.join(root_dir, "passlib", "tests", "sample_config_1s.cfg")
-
- def test_policy_creation():
- with open(test_path, "rb") as fh:
- policy1 = CryptPolicy.from_string(fh.read())
- yield test_policy_creation
-
- default = CryptPolicy.from_path(test_path)
- def test_policy_composition():
- policy2 = default.replace(
- schemes = [ "sha512_crypt", "sha256_crypt", "md5_crypt",
- "des_crypt", "unix_fallback" ],
- deprecated = [ "des_crypt" ],
- )
- yield test_policy_composition
-
-secret = u("secret")
-other = u("other")
-
-def setup_context():
- from passlib.context import CryptContext
-
- def test_context_init():
- return CryptContext(
+@benchmark.constructor()
+def test_context_from_path():
+ "test speed of CryptContext.from_path()"
+ path = sample_config_1p
+ if CryptPolicy:
+ def helper():
+ CryptPolicy.from_path(path)
+ else:
+ def helper():
+ CryptContext.from_path(path)
+ return helper
+
+@benchmark.constructor()
+def test_context_update():
+ "test speed of CryptContext.update()"
+ kwds = dict(
+ schemes = [ "sha512_crypt", "sha256_crypt", "md5_crypt",
+ "des_crypt", "unix_fallback" ],
+ deprecated = [ "des_crypt" ],
+ sha512_crypt__min_rounds=4000,
+ )
+ if CryptPolicy:
+ policy=CryptPolicy.from_path(sample_config_1p)
+ def helper():
+ policy.replace(**kwds)
+ else:
+ ctx = CryptContext.from_path(sample_config_1p)
+ def helper():
+ ctx.copy(**kwds)
+ return helper
+
+@benchmark.constructor()
+def test_context_init():
+ "test speed of CryptContext() constructor"
+ kwds = dict(
schemes=[BlankHandler, AnotherHandler],
default="another",
blank__min_rounds=1500,
blank__max_rounds=2500,
another__vary_rounds=100,
- )
- yield test_context_init
+ )
+ def helper():
+ CryptContext(**kwds)
+ return helper
- ctx = test_context_init()
- def test_context_calls():
- hash = ctx.encrypt(secret, rounds=2001)
- ctx.verify(secret, hash)
- ctx.verify_and_update(secret, hash)
- ctx.verify_and_update(other, hash)
- yield test_context_calls
+@benchmark.constructor()
+def test_context_calls():
+ "test speed of CryptContext password methods"
+ ctx = CryptContext(
+ schemes=[BlankHandler, AnotherHandler],
+ default="another",
+ blank__min_rounds=1500,
+ blank__max_rounds=2500,
+ another__vary_rounds=100,
+ )
+ def helper():
+ hash = ctx.encrypt(SECRET, rounds=2001)
+ ctx.verify(SECRET, hash)
+ ctx.verify_and_update(SECRET, hash)
+ ctx.verify_and_update(OTHER, hash)
+ return helper
-def setup_handlers():
+#=============================================================================
+# handler benchmarks
+#=============================================================================
+@benchmark.constructor()
+def test_md5_crypt_builtin():
+ "test test md5_crypt builtin backend"
from passlib.hash import md5_crypt
md5_crypt.set_backend("builtin")
- def test_md5_crypt():
- hash = md5_crypt.encrypt(secret)
- md5_crypt.verify(secret, hash)
- md5_crypt.verify(other, hash)
- yield test_md5_crypt
+ def helper():
+ hash = md5_crypt.encrypt(SECRET)
+ md5_crypt.verify(SECRET, hash)
+ md5_crypt.verify(OTHER, hash)
+ yield helper
+
+@benchmark.constructor()
+def test_ldap_salted_md5():
+ "test ldap_salted_md5"
+ from passlib.hash import ldap_salted_md5 as handler
+ def helper():
+ hash = handler.encrypt(SECRET, salt='....')
+ handler.verify(SECRET, hash)
+ handler.verify(OTHER, hash)
+ yield helper
#=============================================================================
# main
#=============================================================================
-def pptime(secs):
- precision = 3
- usec = int(secs * 1e6)
- if usec < 1000:
- return "%.*g usec" % (precision, usec)
- msec = usec / 1000
- if msec < 1000:
- return "%.*g msec" % (precision, msec)
- sec = msec / 1000
- return "%.*g sec" % (precision, sec)
-
def main(*args):
- names = args
source = globals()
- for key in sorted(source):
- if not key.startswith("setup_"):
- continue
- sname = key[6:]
- setup = source[key]
- for test in setup():
- name = test.__name__
- if name.startswith("test_"):
- name = name[5:]
- if names and name not in names:
- continue
- timer = Timer(test)
- number = 1
- while True:
- t = timer.timeit(number)
- if t > .2:
- break
- number *= 10
- repeat = 3
- best = min(timer.repeat(repeat, number)) / number
- print_("%30s %s" % (name, pptime(best)))
+ if args:
+ orig = source
+ source = dict((k,orig[k]) for k in orig if k in args)
+ helper = benchmark.run(source, maxtime=2, bestof=3)
+ for name, secs, precision in helper:
+ print_("%-50s %9s (%d)" % (name, benchmark.pptime(secs), precision))
if __name__ == "__main__":
import sys
diff --git a/docs/lib/passlib.context-interface.rst b/docs/lib/passlib.context-interface.rst
index 8331992..a760d06 100644
--- a/docs/lib/passlib.context-interface.rst
+++ b/docs/lib/passlib.context-interface.rst
@@ -8,8 +8,7 @@
.. currentmodule:: passlib.context
-This details all the constructors and methods provided by :class:`!CryptContext`
-and :class:`!CryptPolicy`.
+This details all the constructors and methods provided by :class:`!CryptContext`.
.. seealso::
@@ -19,12 +18,14 @@ and :class:`!CryptPolicy`.
The Context Object
==================
-.. autoclass:: CryptContext(schemes=None, policy=<default policy>, \*\*kwds)
-
-The Policy Object
-=================
-.. autoclass:: CryptPolicy(\*\*kwds)
+.. autoclass:: CryptContext(schemes=None, \*\*kwds)
Other Helpers
=============
-.. autoclass:: LazyCryptContext([schemes=None,] **kwds [, create_policy=None])
+.. autoclass:: LazyCryptContext([schemes=None,] \*\*kwds [, onload=None])
+
+.. rst-class:: html-toggle
+
+(deprecated) The CryptPolicy Class
+==================================
+.. autoclass:: CryptPolicy
diff --git a/docs/lib/passlib.context-options.rst b/docs/lib/passlib.context-options.rst
index 4f0bcbe..36cb89e 100644
--- a/docs/lib/passlib.context-options.rst
+++ b/docs/lib/passlib.context-options.rst
@@ -9,9 +9,13 @@
.. currentmodule:: passlib.context
The :class:`CryptContext` accepts a number of keyword options.
-These are divides into the "context options", which affect
-the context instance directly, and the "hash options",
-which affect the context treats a particular type of hash:
+These can be provided to any of the CryptContext constructor methods,
+as well as the :meth:`CryptContext.update` method, or any configuration
+string or INI file passed to :meth:`CryptContext.load`.
+
+The options are divided into two categories: "context options", which directly
+affect the :class:`!CryptContext` object itself; and "hash options", which
+affect the behavior of a particular password hashing scheme.
.. seealso::
@@ -21,8 +25,7 @@ which affect the context treats a particular type of hash:
Context Options
===============
-The following keyword options are accepted by both the :class:`CryptContext`
-and :class:`CryptPolicy` constructors, and directly affect the behavior
+The following keyword options directly affect the behavior
of the :class:`!CryptContext` instance itself:
``schemes``
@@ -41,15 +44,14 @@ of the :class:`!CryptContext` instance itself:
``deprecated``
List of handler names which should be considered deprecated by the CryptContext.
- This should be a subset of the names of the handlers listed in schemes.
- This is optional, if not specified, no handlers will be considered deprecated.
+ This should be a subset of the names of the handlers listed in *schemes*.
+ This is optional, and if not specified, no handlers will be considered deprecated.
- For use in INI files, this may also be specified as a single comma-separated string
+ For INI files, this may also be specified as a single comma-separated string
of handler names.
- This is primarily used by :meth:`CryptContext.hash_needs_update` and
- :meth:`CryptPolicy.handler_is_deprecated`. If the application does not use
- these methods, this option can be ignored.
+ This is primarily used by :meth:`CryptContext.hash_needs_update`.
+ If the application does not use this method, this option can be ignored.
Example: ``deprecated=["des_crypt"]``.
@@ -77,25 +79,22 @@ of the :class:`!CryptContext` instance itself:
For symmetry with the format of the hash option keywords (below),
all of the above context option keywords may also be specified
- using the format :samp:`context__{option}` (note double underscores),
- or :samp:`context.{option}` within INI files.
+ using the format :samp:`context__{option}` (note double underscores).
.. note::
To override context options for a particular :ref:`user category <user-categories>`,
- use the format :samp:`{category}__context__{option}`,
- or :samp:`{category}.context.{option}` within an INI file.
+ use the format :samp:`{category}__context__{option}`.
Hash Options
============
-The following keyword options are accepted by both the :class:`CryptContext`
-and :class:`CryptPolicy` constructors, and affect how a :class:`!CryptContext` instance
-treats hashes belonging to a particular hash scheme, as identified by the hash's handler name.
+The following keyword option affect how a :class:`!CryptContext` instance
+treats hashes belonging to a particular hash scheme,
+as identified by the scheme's name.
All hash option keywords should be specified using the format :samp:`{hash}__{option}`
(note double underscores); where :samp:`{hash}` is the name of the hash's handler,
and :samp:`{option}` is the name of the specific options being set.
-Within INI files, this may be specified using the alternate format :samp:`{hash}.{option}`.
:samp:`{hash}__default_rounds`
@@ -111,11 +110,9 @@ Within INI files, this may be specified using the alternate format :samp:`{hash}
to have a rounds value random chosen from the range :samp:`{default_rounds} +/- {vary_rounds}`.
This may be specified as an integer value, or as a string containing an integer
- with a percent suffix (eg: ``"10%"``). if specified as a percent,
+ with a percent suffix (eg: ``"10%"``). If specified as a percent,
the amount varied will be calculated as a percentage of the :samp:`{default_rounds}` value.
- The default Passlib policy sets this to ``"10%"``.
-
.. note::
If this is specified as a percentage, and the hash algorithm
@@ -217,48 +214,61 @@ of the category string it wants to use, and add an additional separator to the k
the need to use a different hash for a particular category
can instead be acheived by overridden the ``default`` context option.
-Sample Policy File
+Sample Config File
==================
-A sample policy file:
+A sample config file:
.. code-block:: ini
[passlib]
- #configure what schemes the context supports (note the "context." prefix is implied for these keys)
+ # configure what schemes the context supports
+ # (note that the "context__" prefix is implied for these keys)
schemes = md5_crypt, sha512_crypt, bcrypt
deprecated = md5_crypt
default = sha512_crypt
- #set some common options for all schemes
- all.vary_rounds = 10%%
- ; NOTE the '%' above has to be escaped due to configparser interpolation
+ # set some common options for all schemes
+ # (this particular setting causes the rounds value to be varied
+ # +/- 10% for each encrypt call)
+ all__vary_rounds = 0.1
- #setup some hash-specific defaults
- sha512_crypt.min_rounds = 40000
- bcrypt.min_rounds = 10
+ # setup some hash-specific defaults
+ sha512_crypt__min_rounds = 40000
+ bcrypt__min_rounds = 10
- #create a "admin" category, which uses bcrypt by default, and has stronger hashes
- admin.context.default = bcrypt
- admin.sha512_crypt.min_rounds = 100000
- admin.bcrypt.min_rounds = 13
+ # create an "admin" category which uses bcrypt by default,
+ # and has stronger default cost
+ admin__context__default = bcrypt
+ admin__sha512_crypt__min_rounds = 100000
+ admin__bcrypt__min_rounds = 13
-And the equivalent as a set of python keyword options::
+This can be turned into a :class:`!CryptContext` via :meth:`CryptContext.from_path`,
+or loaded into an existing object via :meth:`CryptContext.load`.
+
+And the equivalent of the above, as a set of Python keyword options::
dict(
- #configure what schemes the context supports (note the "context." prefix is implied for these keys)
+ # configure what schemes the context supports
+ # (note the "context__" prefix is implied for these keys)
schemes = ["md5_crypt", "sha512_crypt", "bcrypt" ],
deprecated = ["md5_crypt"],
default = "sha512_crypt",
- #set some common options for all schemes
- all__vary_rounds = "10%",
+ # set some common options for all schemes
+ # (this particular setting causes the rounds value to be varied
+ # +/- 10% for each encrypt call)
+ all__vary_rounds = 0.1,
- #setup some hash-specific defaults
+ # setup some hash-specific defaults
sha512_crypt__min_rounds = 40000,
bcrypt__min_rounds = 10,
- #create a "admin" category, which uses bcrypt by default, and has stronger hashes
- admin__context__default = bcrypt
- admin__sha512_crypt__min_rounds = 100000
- admin__bcrypt__min_rounds = 13
+ # create a "admin" category which uses bcrypt by default,
+ # and has stronger default cost
+ admin__context__default = bcrypt,
+ admin__sha512_crypt__min_rounds = 100000,
+ admin__bcrypt__min_rounds = 13,
)
+
+This can be turned into a :class:`CryptContext` via the class constructor,
+or loaded into an existing object via :meth:`CryptContext.load`.
diff --git a/docs/lib/passlib.context-usage.rst b/docs/lib/passlib.context-usage.rst
index 9832203..4d897d4 100644
--- a/docs/lib/passlib.context-usage.rst
+++ b/docs/lib/passlib.context-usage.rst
@@ -77,28 +77,39 @@ 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 = ldap_context.copy(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::
+are accessible through various methods of the object:
>>> 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()
+ >>> # get a list of schemes recognized in this context:
+ >>> myctx.schemes()
[ 'md5-crypt', 'bcrypt' ]
- >>> #get the default handler class :
- >>> myctx.policy.get_handler()
+ >>> # get the default handler object:
+ >>> myctx.handler("default")
<class 'passlib.handlers.md5_crypt.md5_crypt'>
-See the :class:`CryptPolicy` class for more details on it's interface.
+ >>> # the results of a CryptContext object can be serialized as a dict,
+ >>> # suitable for passing to CryptContext's class constructor.
+ >>> myctx.to_dict()
+ {'schemes': ['md5_crypt, 'des_crypt'], 'deprecated': 'des_crypt'}
+
+ >>> # or serialized to an INI-style string, suitable for passing to
+ >>> # CryptContext's from_string() method.
+ >>> print myctx.to_string()
+ [passlib]
+ schemes = md5_crypt, des_crypt
+ deprecated = des_crypt
+
+See the :class:`CryptContext` reference for more details on it's interface.
Full Integration Example
========================
@@ -123,31 +134,32 @@ applications with advanced policy requirements may want to create a hash policy
[passlib]
- ;setup the context to support pbkdf2_sha1, along with legacy md5_crypt hashes:
+ ; 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)
+ ; flag md5_crypt as deprecated
+ ; (existing md5_crypt hashes will be flagged as needs-updating)
deprecated = md5_crypt
- ;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%%
- ; NOTE the '%' above has to be doubled due to configparser interpolation
-
- ;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
+ ; 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 % of the default (in this case,
+ ; 20000 +/- 10% or 2000).
+ pbkdf2_sha1__default_rounds = 20000
+ pbkdf2_sha1__vary_rounds = 0.1
+
+ ; 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
-----------------------------
@@ -172,7 +184,6 @@ the configuration once the application starts:
#
from myapp.model.security import user_pwd_context
- from passlib.context import CryptPolicy
def myapp_startup():
@@ -180,10 +191,13 @@ the configuration once the application starts:
# ... other code ...
#
- # vars:
- # policy_path - path to policy file defined in previous step
#
- user_pwd_context.policy = CryptPolicy.from_path(policy_path)
+ # load configuration from some application-specified path.
+ # the load() method also supports loading from a string,
+ # or from dictionary, and other options.
+ #
+ ##user_pwd_context.load(policy_config_string)
+ user_pwd_context.load_path(policy_config_path)
#
#if you want to reconfigure the context without restarting the application,
diff --git a/docs/lib/passlib.ext.django.rst b/docs/lib/passlib.ext.django.rst
index 225642e..d797e23 100644
--- a/docs/lib/passlib.ext.django.rst
+++ b/docs/lib/passlib.ext.django.rst
@@ -73,7 +73,7 @@ you may set the following options in django ``settings.py``:
* ``"disabled"``, in which case this app will do nothing when Django is loaded.
* A multiline configuration string suitable for passing to
- :meth:`passlib.context.CryptPolicy.from_string`.
+ :meth:`passlib.context.CryptContext.from_string`.
It is *strongly* recommended to use a configuration which will support
the existing Django hashes
(see :data:`~passlib.ext.django.utils.STOCK_CTX`).
diff --git a/docs/lib/passlib.hosts.rst b/docs/lib/passlib.hosts.rst
index 5ca13db..85514e8 100644
--- a/docs/lib/passlib.hosts.rst
+++ b/docs/lib/passlib.hosts.rst
@@ -52,7 +52,7 @@ for the following Unix variants:
All of the above contexts include the :class:`~passlib.hash.unix_disabled` handler
as a final fallback. This special handler treats all strings as invalid passwords,
particularly the common strings ``!`` and ``*`` which are used to indicate
- that an account has been disabled [#shadow]_.
+ that an account has been disabled [#shadow]_.
A quick usage example, using the :data:`!linux_context` instance::
@@ -81,7 +81,7 @@ Current Host OS
The main differences between this object and :func:`!crypt`:
* this object provides introspection about *which* schemes
- are available on a given system (via ``host_context.policy.schemes()``).
+ are available on a given system (via ``host_context.schemes()``).
* it defaults to the strongest algorithm available,
automatically configured to an appropriate strength
for encrypting new passwords.