diff options
author | Eli Collins <elic@assurancetechnologies.com> | 2012-04-17 15:37:59 -0400 |
---|---|---|
committer | Eli Collins <elic@assurancetechnologies.com> | 2012-04-17 15:37:59 -0400 |
commit | 8341dc6179d656b898f944b0cd848c350c627aab (patch) | |
tree | dbdd0d4a2c4ea4cbf46320621a0cd1177bcb241b | |
parent | 6aafb5617e57a0145679e75f5cff023a95da18b2 (diff) | |
download | passlib-8341dc6179d656b898f944b0cd848c350c627aab.tar.gz |
CryptPolicy deprecation, part 5 - updated docs, changelog, benchmark script
-rw-r--r-- | CHANGES | 53 | ||||
-rw-r--r-- | admin/benchmarks.py | 257 | ||||
-rw-r--r-- | docs/lib/passlib.context-interface.rst | 17 | ||||
-rw-r--r-- | docs/lib/passlib.context-options.rst | 98 | ||||
-rw-r--r-- | docs/lib/passlib.context-usage.rst | 80 | ||||
-rw-r--r-- | docs/lib/passlib.ext.django.rst | 2 | ||||
-rw-r--r-- | docs/lib/passlib.hosts.rst | 4 |
7 files changed, 318 insertions, 193 deletions
@@ -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. |