summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/lib/passlib.ext.django.rst65
-rw-r--r--passlib/ext/django/__init__.py3
-rw-r--r--passlib/ext/django/models.py14
-rw-r--r--passlib/ext/django/utils.py26
-rw-r--r--passlib/tests/test_ext_django.py9
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()