Fixed #12103 -- Added AuthenticationForm.confirm_login_allowed to allow customizing the logic policy.

Thanks ejucovy and lasko for work on the patch.
This commit is contained in:
Tim Graham 2013-07-30 08:24:13 -04:00
parent 1c3c21b38d
commit a1889397a9
4 changed files with 93 additions and 5 deletions

View File

@ -191,13 +191,28 @@ class AuthenticationForm(forms.Form):
code='invalid_login',
params={'username': self.username_field.verbose_name},
)
elif not self.user_cache.is_active:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',
)
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
def confirm_login_allowed(self, user):
"""
Controls whether the given User may log in. This is a policy setting,
independent of end-user authentication. This default behavior is to
allow login by active users, and reject login by inactive users.
If the given user cannot log in, this method should raise a
``forms.ValidationError``.
If the given user may log in, this method should return None.
"""
if not user.is_active:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',
)
def get_user_id(self):
if self.user_cache:
return self.user_cache.id

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
import os
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
@ -131,6 +132,40 @@ class AuthenticationFormTest(TestCase):
self.assertEqual(form.non_field_errors(),
[force_text(form.error_messages['inactive'])])
def test_custom_login_allowed_policy(self):
# The user is inactive, but our custom form policy allows him to log in.
data = {
'username': 'inactive',
'password': 'password',
}
class AuthenticationFormWithInactiveUsersOkay(AuthenticationForm):
def confirm_login_allowed(self, user):
pass
form = AuthenticationFormWithInactiveUsersOkay(None, data)
self.assertTrue(form.is_valid())
# If we want to disallow some logins according to custom logic,
# we should raise a django.forms.ValidationError in the form.
class PickyAuthenticationForm(AuthenticationForm):
def confirm_login_allowed(self, user):
if user.username == "inactive":
raise forms.ValidationError(_("This user is disallowed."))
raise forms.ValidationError(_("Sorry, nobody's allowed in."))
form = PickyAuthenticationForm(None, data)
self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(), ['This user is disallowed.'])
data = {
'username': 'testclient',
'password': 'password',
}
form = PickyAuthenticationForm(None, data)
self.assertFalse(form.is_valid())
self.assertEqual(form.non_field_errors(), ["Sorry, nobody's allowed in."])
def test_success(self):
# The success case
data = {

View File

@ -101,6 +101,10 @@ Minor features
:class:`~django.middleware.http.ConditionalGetMiddleware` to handle
conditional ``GET`` requests for sitemaps which set ``lastmod``.
* You can override the new :meth:`AuthenticationForm.confirm_login_allowed()
<django.contrib.auth.forms.AuthenticationForm.confirm_login_allowed>` method
to more easily customize the login policy.
Backwards incompatible changes in 1.7
=====================================

View File

@ -959,6 +959,40 @@ provides several built-in forms located in :mod:`django.contrib.auth.forms`:
Takes ``request`` as its first positional argument, which is stored on the
form instance for use by sub-classes.
.. method:: confirm_login_allowed(user)
.. versionadded:: 1.7
By default, ``AuthenticationForm`` rejects users whose ``is_active`` flag
is set to ``False``. You may override this behavior with a custom policy to
determine which users can log in. Do this with a custom form that subclasses
``AuthenticationForm`` and overrides the ``confirm_login_allowed`` method.
This method should raise a :exc:`~django.core.exceptions.ValidationError`
if the given user may not log in.
For example, to allow all users to log in, regardless of "active" status::
from django.contrib.auth.forms import AuthenticationForm
class AuthenticationFormWithInactiveUsersOkay(AuthenticationForm):
def confirm_login_allowed(self, user):
pass
Or to allow only some active users to log in::
class PickyAuthenticationForm(AuthenticationForm):
def confirm_login_allowed(self, user):
if not user.is_active:
raise forms.ValidationError(
_("This account is inactive."),
code='inactive',
)
if user.username.startswith('b'):
raise forms.ValidationError(
_("Sorry, accounts starting with 'b' aren't welcome here."),
code='no_b_users',
)
.. class:: PasswordChangeForm
A form for allowing a user to change their password.