diff options
| -rw-r--r-- | docs/lib/passlib.ext.django.rst | 65 | ||||
| -rw-r--r-- | passlib/ext/django/__init__.py | 3 | ||||
| -rw-r--r-- | passlib/ext/django/models.py | 14 | ||||
| -rw-r--r-- | passlib/ext/django/utils.py | 26 | ||||
| -rw-r--r-- | passlib/tests/test_ext_django.py | 9 |
5 files changed, 68 insertions, 49 deletions
diff --git a/docs/lib/passlib.ext.django.rst b/docs/lib/passlib.ext.django.rst index 25147e5..225642e 100644 --- a/docs/lib/passlib.ext.django.rst +++ b/docs/lib/passlib.ext.django.rst @@ -8,10 +8,13 @@ .. warning:: - This module is currently under development. - It works and has good unittest coverage, - but has not seen very much real-world use; - *caveat emptor*. + This submodule should be considered "release candidate" quality. + It works, and has good unittest coverage, + but has not seen very much real-world use. + *caveat emptor*, and please report any issues. + + This module is currently not compatible with Django 1.4's new + password hashing system, or formats. .. todo:: @@ -23,36 +26,35 @@ Overview This module is intended for use with `Django <http://www.djangoproject.com>`_-based web applications. It contains a Django app which allows you to override -Django's builtin password hashing routine -with any Passlib :doc:`CryptContext <passlib.context>` instance. -By default, it comes configured to add support for -:class:`~passlib.hash.sha512_crypt`, and will automatically -upgrade all existing Django password hashes as your users log in. - -:doc:`SHA512-Crypt <passlib.hash.sha512_crypt>` -was chosen as the best choice for the average Django deployment: -accelerated implementations are available on most stock Linux systems, -as well as Google App Engine, and Passlib provides a pure-python -fallback for all other platforms. +Django's builtin password hashing routines +to use any Passlib :doc:`CryptContext <passlib.context>` configuration. +It provides the following features: + +* Custom configurations allow the use of any password hash supported by Passlib. +* Increased-strength hashing for staff and admin accounts. +* Automatically upgrading of deprecated and weaker hashes. +* Default configuration supports all standard Django hash formats, + and automatically upgrades all hashes to use :class:`~passlib.hash.sha512_crypt` + (upgrades only occur when the user logs in or changes their password). +* Tested against Django 0.9 - 1.3 Installation ============= -Installation is simple: just add ``"passlib.ext.django"`` to -Django's ``settings.INSTALLED_APPS``. This app will handle -everything else. - -Once done, when this app is imported by Django, -it will automatically monkeypatch -:class:`!django.contrib.auth.models.User` -to use a Passlib :class:`~passlib.context.CryptContext` instance -in place of the normal Django password authentication routines. - +Installation is simple: once Passlib is installed, just add +``"passlib.ext.django"`` to Django's ``settings.INSTALLED_APPS``. +This app will handle everything else. + +Once installed, when this app is imported by Django, it will automatically monkeypatch +:class:`!django.contrib.auth.models.User` to use a Passlib +:class:`~passlib.context.CryptContext` instance in place of the normal Django +password authentication routines. This provides hash migration, the ability to set stronger policies for superuser & staff passwords, and stronger password hashing schemes. Configuration ============= -Once installed, you can set the following options in django ``settings.py``: +While the default configuration should be secure, once installed, +you may set the following options in django ``settings.py``: ``PASSLIB_CONTEXT`` This may be one of a number of values: @@ -68,7 +70,7 @@ Once installed, you can set the following options in django ``settings.py``: The exact default policy used can be found in :data:`~passlib.ext.django.utils.DEFAULT_CTX`. - * ``None``, in which case this app will do nothing when Django is loaded. + * ``"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`. @@ -79,15 +81,14 @@ Once installed, you can set the following options in django ``settings.py``: ``PASSLIB_GET_CATEGORY`` By default, Passlib will invoke the specified context with a category - string that's dependant on the User instance. - superusers will be assigned to the ``superuser`` category, - staff to the ``staff`` category, and all other accounts - assigned to ``None``. + string that's dependant on the User instance. superusers will be assigned + to the ``superuser`` category, staff to the ``staff`` category, and all + other accounts assigned to ``None``. This configuration option allows overriding that logic by specifying an alternate function with the call signature ``get_category(user) -> category|None``. - + .. seealso:: See :ref:`user-categories` for more details about diff --git a/passlib/ext/django/__init__.py b/passlib/ext/django/__init__.py index b4fcb2c..a9e019b 100644 --- a/passlib/ext/django/__init__.py +++ b/passlib/ext/django/__init__.py @@ -2,8 +2,7 @@ .. warning:: - This code is experimental and subject to change, - and not officially documented in Passlib just yet + This code is experimental and subject to change (though it should work). see the Passlib documentation for details on how to use this app diff --git a/passlib/ext/django/models.py b/passlib/ext/django/models.py index 9ae17b7..2b3dd16 100644 --- a/passlib/ext/django/models.py +++ b/passlib/ext/django/models.py @@ -2,8 +2,7 @@ .. warning:: - This code is experimental and subject to change, - and not officially documented in Passlib just yet + This code is experimental and subject to change (though it should work). see the Passlib documentation for details on how to use this app @@ -29,18 +28,17 @@ def patch(): catfunc = getattr(settings, "PASSLIB_GET_CATEGORY", get_category) #parse & validate input value - if not ctx: + if ctx == "disabled": # remove any patching that was already set, just in case. set_django_password_context(None) return if ctx == "passlib-default": ctx = DEFAULT_CTX - if isinstance(ctx, sb_types): - ctx = CryptPolicy.from_string(ctx) - if isinstance(ctx, CryptPolicy): - ctx = CryptContext(policy=ctx) + if isinstance(ctx, str): + ctx = CryptContext(policy=CryptPolicy.from_string(ctx)) if not is_crypt_context(ctx): - raise TypeError("django settings.PASSLIB_CONTEXT must be CryptContext instance or config string: %r" % (ctx,)) + raise TypeError("django settings.PASSLIB_CONTEXT must be CryptContext " + "instance or configuration string: %r" % (ctx,)) #monkeypatch django.contrib.auth.models:User set_django_password_context(ctx, get_category=catfunc) diff --git a/passlib/ext/django/utils.py b/passlib/ext/django/utils.py index be8771c..32aa96c 100644 --- a/passlib/ext/django/utils.py +++ b/passlib/ext/django/utils.py @@ -2,9 +2,21 @@ .. warning:: - This code is experimental and subject to change, - and not officially documented in Passlib just yet + This code is experimental and subject to change (though it should work). + +Django 1.4 Notes +================ +they isolated the hashing code into auth.hashers. +public interface is check_password(), make_password(), is_password_unusable() +make_password(None) should return unusable. +User object uses these stubs. +will need to refactor monkeypatching quite a bit. +and their new hashers framework might not require passlib anymore anyways. + +as opposed to pre-1.4, which had everything in auth.models - +a check_password(), and User.set_password / check_password / set_unusable methods. +so if there is utility for this, will need to rethink. """ #=================================================================== #imports @@ -26,14 +38,20 @@ __all__ = [ #=================================================================== _has_django0 = None # old 0.9 django - lacks unusable_password support +_has_django14 = None # new django 1.4 with auth.hashers _dam = None #django.contrib.auth.models reference def _import_django(): - global _dam, _has_django0 + global _dam, _has_django0, _has_django4 if _dam is None: import django.contrib.auth.models as _dam from django import VERSION _has_django0 = VERSION < (1,0) + _has_django14 = VERISON >= (1,4) + if _has_django14: + # django 1.4 had a large rewrite that adds new stronger schemes, + # but changes how things work. our monkeypatching may not jive. + warn("passlib.ext.django may not work correctly with django >= 1.4") return _dam #=================================================================== @@ -58,7 +76,6 @@ DEFAULT_CTX = """ [passlib] schemes = sha512_crypt, - pbkdf2_sha256, django_salted_sha1, django_salted_md5, django_des_crypt, hex_md5, django_disabled @@ -66,7 +83,6 @@ schemes = default = sha512_crypt deprecated = - pbkdf2_sha256, django_salted_sha1, django_salted_md5, django_des_crypt, hex_md5 diff --git a/passlib/tests/test_ext_django.py b/passlib/tests/test_ext_django.py index 13085fc..96de69e 100644 --- a/passlib/tests/test_ext_django.py +++ b/passlib/tests/test_ext_django.py @@ -30,13 +30,16 @@ except ImportError: settings = None has_django = False -has_django0 = False #are we using django 0.9 release? -has_django1 = False #inverse - are we using django >= 1.0 +has_django0 = False # are we using django 0.9? +has_django1 = False # are we using django >= 1.0? +has_django14 = False # are we using django >= 1.4? if has_django: from django import VERSION + log.debug("found django %r installation", VERSION) has_django0 = (VERSION < (1,0)) has_django1 = (VERSION >= (1,0)) + has_django14 = (VERSION >= (1,4)) if not isinstance(settings, LazySettings): #this could mean django has been configured somehow, @@ -51,6 +54,8 @@ if has_django: else: if not settings.configured: settings.configure() +else: + log.debug("django installation not found") _NOTSET = object() |
