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:
parent
1c3c21b38d
commit
a1889397a9
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
=====================================
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue