1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

newforms-admin: Fixed #6083 -- Updated django.contrib.auth to use newforms. A big thanks to Greg Turner, Michael Newman and Petr Marhoun.

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7191 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Brian Rosner 2008-03-03 20:37:41 +00:00
parent 61b9c8fe00
commit ba2d0feb72
10 changed files with 416 additions and 160 deletions

View File

@ -8,18 +8,21 @@
<fieldset class="module aligned">
<div class="form-row">
{{ form.username.html_error_list }}
{{ form.username.errors }}
{# TODO: get required class on label_tag #}
<label for="id_username" class="required">{% trans 'Username' %}:</label> {{ form.username }}
<p class="help">{{ username_help_text }}</p>
<p class="help">{{ form.username.help_text }}</p>
</div>
<div class="form-row">
{{ form.password1.html_error_list }}
{{ form.password1.errors }}
{# TODO: get required class on label_tag #}
<label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }}
</div>
<div class="form-row">
{{ form.password2.html_error_list }}
{{ form.password2.errors }}
{# TODO: get required class on label_tag #}
<label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }}
<p class="help">{% trans 'Enter the same password as above, for verification.' %}</p>
</div>

View File

@ -18,9 +18,9 @@
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
<div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
{% if form.error_dict %}
{% if form.errors %}
<p class="errornote">
{% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
{% blocktrans count form.errors.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p>
{% endif %}
@ -29,12 +29,14 @@
<fieldset class="module aligned">
<div class="form-row">
{{ form.password1.html_error_list }}
{{ form.password1.errors }}
{# TODO: get required class on label_tag #}
<label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }}
</div>
<div class="form-row">
{{ form.password2.html_error_list }}
{{ form.password2.errors }}
{# TODO: get required class on label_tag #}
<label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }}
<p class="help">{% trans 'Enter the same password as above, for verification.' %}</p>
</div>

View File

@ -27,12 +27,10 @@ class UserAdmin(admin.ModelAdmin):
def add_view(self, request):
if not self.has_change_permission(request):
raise PermissionDenied
manipulator = UserCreationForm()
if request.method == 'POST':
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
new_user = manipulator.save(new_data)
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': 'user', 'obj': new_user}
if "_addanother" in request.POST:
request.user.message_set.create(message=msg)
@ -41,8 +39,7 @@ class UserAdmin(admin.ModelAdmin):
request.user.message_set.create(message=msg + ' ' + ugettext("You may edit it again below."))
return HttpResponseRedirect('../%s/' % new_user.id)
else:
errors = new_data = {}
form = oldforms.FormWrapper(manipulator, new_data, errors)
form = UserCreationForm()
return render_to_response('admin/auth/user/add_form.html', {
'title': _('Add user'),
'form': form,

View File

@ -3,63 +3,81 @@ from django.contrib.auth import authenticate
from django.contrib.sites.models import Site
from django.template import Context, loader
from django.core import validators
from django import oldforms
from django import newforms as forms
from django.utils.translation import ugettext as _
class UserCreationForm(oldforms.Manipulator):
"A form that creates a user, with no privileges, from the given username and password."
def __init__(self):
self.fields = (
oldforms.TextField(field_name='username', length=30, max_length=30, is_required=True,
validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
oldforms.PasswordField(field_name='password1', length=30, max_length=60, is_required=True),
oldforms.PasswordField(field_name='password2', length=30, max_length=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
)
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and password.
"""
username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^\w+$',
help_text = _("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."),
error_message = _("This value must contain only letters, numbers and underscores."))
password1 = forms.CharField(label=_("Password"), max_length=60, widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password confirmation"), max_length=60, widget=forms.PasswordInput)
def isValidUsername(self, field_data, all_data):
class Meta:
model = User
fields = ("username",)
def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=field_data)
User.objects.get(username=username)
except User.DoesNotExist:
return
raise validators.ValidationError, _('A user with that username already exists.')
return username
raise forms.ValidationError(_("A user with that username already exists."))
def save(self, new_data):
"Creates the user."
return User.objects.create_user(new_data['username'], '', new_data['password1'])
def clean_password2(self):
password1 = self.cleaned_data["password1"]
password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
class AuthenticationForm(oldforms.Manipulator):
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class AuthenticationForm(forms.Form):
"""
Base class for authenticating users. Extend this to get a form that accepts
username/password logins.
"""
def __init__(self, request=None):
username = forms.CharField(max_length=30)
password = forms.CharField(max_length=30, widget=forms.PasswordInput)
def __init__(self, request=None, *args, **kwargs):
"""
If request is passed in, the manipulator will validate that cookies are
If request is passed in, the form will validate that cookies are
enabled. Note that the request (a HttpRequest object) must have set a
cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before
running this validator.
running this validation.
"""
self.request = request
self.fields = [
oldforms.TextField(field_name="username", length=15, max_length=30, is_required=True,
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
oldforms.PasswordField(field_name="password", length=15, max_length=30, is_required=True),
]
self.user_cache = None
super(AuthenticationForm, self).__init__(*args, **kwargs)
def hasCookiesEnabled(self, field_data, all_data):
if self.request and not self.request.session.test_cookie_worked():
raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.")
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
def isValidUser(self, field_data, all_data):
username = field_data
password = all_data.get('password', None)
self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None:
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
elif not self.user_cache.is_active:
raise validators.ValidationError, _("This account is inactive.")
if username and password:
self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None:
raise forms.ValidationError(_("Please enter a correct username and password. Note that both fields are case-sensitive."))
elif not self.user_cache.is_active:
raise forms.ValidationError(_("This account is inactive."))
# TODO: determine whether this should move to its own method.
if self.request:
if not self.request.session.test_cookie_worked():
raise forms.ValidationError(_("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in."))
return self.cleaned_data
def get_user_id(self):
if self.user_cache:
@ -69,22 +87,22 @@ class AuthenticationForm(oldforms.Manipulator):
def get_user(self):
return self.user_cache
class PasswordResetForm(oldforms.Manipulator):
"A form that lets a user request a password reset"
def __init__(self):
self.fields = (
oldforms.EmailField(field_name="email", length=40, is_required=True,
validator_list=[self.isValidUserEmail]),
)
class PasswordResetForm(forms.Form):
email = forms.EmailField(max_length=40)
def isValidUserEmail(self, new_data, all_data):
"Validates that a user exists with the given e-mail address"
self.users_cache = list(User.objects.filter(email__iexact=new_data))
def clean_email(self):
"""
Validates that a user exists with the given e-mail address.
"""
email = self.cleaned_data["email"]
self.users_cache = User.objects.filter(email__iexact=email)
if len(self.users_cache) == 0:
raise validators.ValidationError, _("That e-mail address doesn't have an associated user account. Are you sure you've registered?")
raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?"))
def save(self, domain_override=None, email_template_name='registration/password_reset_email.html'):
"Calculates a new password randomly and sends it to the user"
"""
Calculates a new password randomly and sends it to the user.
"""
from django.core.mail import send_mail
for user in self.users_cache:
new_pass = User.objects.make_random_password()
@ -103,42 +121,69 @@ class PasswordResetForm(oldforms.Manipulator):
'domain': domain,
'site_name': site_name,
'user': user,
}
send_mail(_('Password reset on %s') % site_name, t.render(Context(c)), None, [user.email])
}
send_mail(_("Password reset on %s") % site_name,
t.render(Context(c)), None, [user.email])
class PasswordChangeForm(oldforms.Manipulator):
"A form that lets a user change his password."
def __init__(self, user):
class PasswordChangeForm(forms.Form):
"""
A form that lets a user change his/her password.
"""
old_password = forms.CharField(max_length=30, widget=forms.PasswordInput)
new_password1 = forms.CharField(max_length=30, widget=forms.PasswordInput)
new_password2 = forms.CharField(max_length=30, widget=forms.PasswordInput)
def __init__(self, user, *args, **kwargs):
self.user = user
self.fields = (
oldforms.PasswordField(field_name="old_password", length=30, max_length=30, is_required=True,
validator_list=[self.isValidOldPassword]),
oldforms.PasswordField(field_name="new_password1", length=30, max_length=30, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
oldforms.PasswordField(field_name="new_password2", length=30, max_length=30, is_required=True),
)
super(PasswordChangeForm, self).__init__(*args, **kwargs)
def isValidOldPassword(self, new_data, all_data):
"Validates that the old_password field is correct."
if not self.user.check_password(new_data):
raise validators.ValidationError, _("Your old password was entered incorrectly. Please enter it again.")
def clean_old_password(self):
"""
Validates that the old_password field is correct.
"""
old_password = self.cleaned_data["old_password"]
if not self.user.check_password(old_password):
raise forms.ValidationError(_("Your old password was entered incorrectly. Please enter it again."))
return old_password
def save(self, new_data):
"Saves the new password."
self.user.set_password(new_data['new_password1'])
self.user.save()
def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2')
if password1 and password2:
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
class AdminPasswordChangeForm(oldforms.Manipulator):
"A form used to change the password of a user in the admin interface."
def __init__(self, user):
def save(self, commit=True):
self.user.set_password(self.cleaned_data['new_password1'])
if commit:
self.user.save()
return self.user
class AdminPasswordChangeForm(forms.Form):
"""
A form used to change the password of a user in the admin interface.
"""
password1 = forms.CharField(max_length=60, widget=forms.PasswordInput)
password2 = forms.CharField(max_length=60, widget=forms.PasswordInput)
def __init__(self, user, *args, **kwargs):
self.user = user
self.fields = (
oldforms.PasswordField(field_name='password1', length=30, max_length=60, is_required=True),
oldforms.PasswordField(field_name='password2', length=30, max_length=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
)
super(AdminPasswordChangeForm, self).__init__(*args, **kwargs)
def save(self, new_data):
"Saves the new password."
self.user.set_password(new_data['password1'])
self.user.save()
def clean_password2(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2:
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2
def save(self, commit=True):
"""
Saves the new password.
"""
self.user.set_password(self.cleaned_data["password1"])
if commit:
self.user.save()
return self.user

View File

@ -0,0 +1,8 @@
from django.contrib.auth.tests.basic import BASIC_TESTS
from django.contrib.auth.tests.forms import FORM_TESTS, PasswordResetFormTestCase
__test__ = {
'BASIC_TESTS': BASIC_TESTS,
'PASSWORDRESET_TESTS': PasswordResetFormTestCase,
'FORM_TESTS': FORM_TESTS,
}

View File

@ -1,5 +1,6 @@
"""
>>> from models import User, AnonymousUser
BASIC_TESTS = """
>>> from django.contrib.auth.models import User, AnonymousUser
>>> u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
>>> u.has_usable_password()
True

View File

@ -0,0 +1,164 @@
from django.core import mail
from django.test import TestCase
from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordResetForm
class PasswordResetFormTestCase(TestCase):
def testValidUser(self):
data = {
'email': 'nonexistent@example.com',
}
form = PasswordResetForm(data)
self.assertEqual(form.is_valid(), False)
self.assertEqual(form["email"].errors, [u"That e-mail address doesn't have an associated user account. Are you sure you've registered?"])
def testEmail(self):
# TODO: remove my email address from the test ;)
User.objects.create_user('atestuser', 'atestuser@example.com', 'test789')
data = {
'email': 'atestuser@example.com',
}
form = PasswordResetForm(data)
self.assertEqual(form.is_valid(), True)
# TODO: look at why using contrib.sites breaks other tests
form.save(domain_override="example.com")
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, u'Password reset on example.com')
# TODO: test mail body. need to figure out a way to get the password in plain text
# self.assertEqual(mail.outbox[0].body, '')
FORM_TESTS = """
>>> from django.contrib.auth.models import User
>>> from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
>>> from django.contrib.auth.forms import PasswordChangeForm
The user already exists.
>>> user = User.objects.create_user("jsmith", "jsmith@example.com", "test123")
>>> data = {
... 'username': 'jsmith',
... 'password1': 'test123',
... 'password2': 'test123',
... }
>>> form = UserCreationForm(data)
>>> form.is_valid()
False
>>> form["username"].errors
[u'A user with that username already exists.']
The username contains invalid data.
>>> data = {
... 'username': 'jsmith@example.com',
... 'password1': 'test123',
... 'password2': 'test123',
... }
>>> form = UserCreationForm(data)
>>> form.is_valid()
False
>>> form["username"].errors
[u'This value must contain only letters, numbers and underscores.']
The verification password is incorrect.
>>> data = {
... 'username': 'jsmith2',
... 'password1': 'test123',
... 'password2': 'test',
... }
>>> form = UserCreationForm(data)
>>> form.is_valid()
False
>>> form["password2"].errors
[u"The two password fields didn't match."]
The success case.
>>> data = {
... 'username': 'jsmith2',
... 'password1': 'test123',
... 'password2': 'test123',
... }
>>> form = UserCreationForm(data)
>>> form.is_valid()
True
>>> form.save()
<User: jsmith2>
The user submits an invalid username.
>>> data = {
... 'username': 'jsmith_does_not_exist',
... 'password': 'test123',
... }
>>> form = AuthenticationForm(None, data)
>>> form.is_valid()
False
>>> form.non_field_errors()
[u'Please enter a correct username and password. Note that both fields are case-sensitive.']
The user is inactive.
>>> data = {
... 'username': 'jsmith',
... 'password': 'test123',
... }
>>> user.is_active = False
>>> user.save()
>>> form = AuthenticationForm(None, data)
>>> form.is_valid()
False
>>> form.non_field_errors()
[u'This account is inactive.']
>>> user.is_active = True
>>> user.save()
The success case
>>> form = AuthenticationForm(None, data)
>>> form.is_valid()
True
>>> form.non_field_errors()
[]
The old password is incorrect.
>>> data = {
... 'old_password': 'test',
... 'new_password1': 'abc123',
... 'new_password2': 'abc123',
... }
>>> form = PasswordChangeForm(user, data)
>>> form.is_valid()
False
>>> form["old_password"].errors
[u'Your old password was entered incorrectly. Please enter it again.']
The two new passwords do not match.
>>> data = {
... 'old_password': 'test123',
... 'new_password1': 'abc123',
... 'new_password2': 'abc',
... }
>>> form = PasswordChangeForm(user, data)
>>> form.is_valid()
False
>>> form["new_password2"].errors
[u"The two password fields didn't match."]
The success case.
>>> data = {
... 'old_password': 'test123',
... 'new_password1': 'abc123',
... 'new_password2': 'abc123',
... }
>>> form = PasswordChangeForm(user, data)
>>> form.is_valid()
True
"""

View File

@ -1,7 +1,6 @@
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import PasswordResetForm, PasswordChangeForm, AdminPasswordChangeForm
from django.core.exceptions import PermissionDenied
from django import oldforms
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.contrib.sites.models import Site, RequestSite
@ -14,30 +13,27 @@ from django.contrib.auth.models import User
def login(request, template_name='registration/login.html', redirect_field_name=REDIRECT_FIELD_NAME):
"Displays the login form and handles the login action."
manipulator = AuthenticationForm(request)
redirect_to = request.REQUEST.get(redirect_field_name, '')
if request.POST:
errors = manipulator.get_validation_errors(request.POST)
if not errors:
if request.method == "POST":
form = AuthenticationForm(request, request.POST)
if form.is_valid():
# Light security check -- make sure redirect_to isn't garbage.
if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
from django.conf import settings
redirect_to = settings.LOGIN_REDIRECT_URL
from django.contrib.auth import login
login(request, manipulator.get_user())
login(request, form.get_user())
request.session.delete_test_cookie()
return HttpResponseRedirect(redirect_to)
else:
errors = {}
form = AuthenticationForm(request)
request.session.set_test_cookie()
if Site._meta.installed:
current_site = Site.objects.get_current()
else:
current_site = RequestSite(request)
return render_to_response(template_name, {
'form': oldforms.FormWrapper(manipulator, request.POST, errors),
'form': form,
redirect_field_name: redirect_to,
'site_name': current_site.name,
}, context_instance=RequestContext(request))
@ -68,55 +64,54 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N
def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',
email_template_name='registration/password_reset_email.html'):
new_data, errors = {}, {}
form = PasswordResetForm()
if request.POST:
new_data = request.POST.copy()
errors = form.get_validation_errors(new_data)
if not errors:
if request.method == "POST":
form = PasswordResetForm(request.POST)
if form.is_valid():
if is_admin_site:
form.save(domain_override=request.META['HTTP_HOST'])
else:
form.save(email_template_name=email_template_name)
return HttpResponseRedirect('%sdone/' % request.path)
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
context_instance=RequestContext(request))
else:
form = PasswordResetForm()
return render_to_response(template_name, {
'form': form,
}, context_instance=RequestContext(request))
def password_reset_done(request, template_name='registration/password_reset_done.html'):
return render_to_response(template_name, context_instance=RequestContext(request))
def password_change(request, template_name='registration/password_change_form.html'):
new_data, errors = {}, {}
form = PasswordChangeForm(request.user)
if request.POST:
new_data = request.POST.copy()
errors = form.get_validation_errors(new_data)
if not errors:
form.save(new_data)
if request.method == "POST":
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('%sdone/' % request.path)
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
context_instance=RequestContext(request))
else:
form = PasswordChangeForm(request.user)
return render_to_response(template_name, {
'form': form,
}, context_instance=RequestContext(request))
password_change = login_required(password_change)
def password_change_done(request, template_name='registration/password_change_done.html'):
return render_to_response(template_name, context_instance=RequestContext(request))
# TODO: move to admin.py in the ModelAdmin
def user_change_password(request, id):
from django import oldforms
if not request.user.has_perm('auth.change_user'):
raise PermissionDenied
user = get_object_or_404(User, pk=id)
manipulator = AdminPasswordChangeForm(user)
if request.method == 'POST':
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
new_user = manipulator.save(new_data)
form = AdminPasswordChangeForm(user, request.POST)
if form.is_valid():
new_user = form.save()
msg = _('Password changed successfully.')
request.user.message_set.create(message=msg)
return HttpResponseRedirect('..')
else:
errors = new_data = {}
form = oldforms.FormWrapper(manipulator, new_data, errors)
form = AdminPasswordChangeForm(user)
return render_to_response('admin/auth/user/change_password.html', {
'title': _('Change password: %s') % escape(user.username),
'form': form,

View File

@ -17,6 +17,47 @@ import base64, datetime
COMMENTS_PER_PAGE = 20
class AuthenticationForm(oldforms.Manipulator):
"""
Base class for authenticating users. Extend this to get a form that accepts
username/password logins.
"""
def __init__(self, request=None):
"""
If request is passed in, the manipulator will validate that cookies are
enabled. Note that the request (a HttpRequest object) must have set a
cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before
running this validator.
"""
self.request = request
self.fields = [
oldforms.TextField(field_name="username", length=15, max_length=30, is_required=True,
validator_list=[self.isValidUser, self.hasCookiesEnabled]),
oldforms.PasswordField(field_name="password", length=15, max_length=30, is_required=True),
]
self.user_cache = None
def hasCookiesEnabled(self, field_data, all_data):
if self.request and not self.request.session.test_cookie_worked():
raise validators.ValidationError, _("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in.")
def isValidUser(self, field_data, all_data):
username = field_data
password = all_data.get('password', None)
self.user_cache = authenticate(username=username, password=password)
if self.user_cache is None:
raise validators.ValidationError, _("Please enter a correct username and password. Note that both fields are case-sensitive.")
elif not self.user_cache.is_active:
raise validators.ValidationError, _("This account is inactive.")
def get_user_id(self):
if self.user_cache:
return self.user_cache.id
return None
def get_user(self):
return self.user_cache
class PublicCommentManipulator(AuthenticationForm):
"Manipulator that handles public registered comments"
def __init__(self, user, ratings_required, ratings_range, num_rating_choices):

View File

@ -666,29 +666,29 @@ successful login.
* ``login_url``: The URL of the login page to redirect to. This
will default to ``settings.LOGIN_URL`` if not supplied.
Built-in manipulators
---------------------
Built-in forms
--------------
**New in Django development version.**
If you don't want to use the built-in views, but want the convenience
of not having to write manipulators for this functionality, the
authentication system provides several built-in manipulators:
of not having to write forms for this functionality, the authentication
system provides several built-in forms:
* ``django.contrib.auth.forms.AdminPasswordChangeForm``: A
manipulator used in the admin interface to change a user's
password.
* ``django.contrib.auth.forms.AdminPasswordChangeForm``: A form used in
the admin interface to change a user's password.
* ``django.contrib.auth.forms.AuthenticationForm``: A manipulator
for logging a user in.
* ``django.contrib.auth.forms.AuthenticationForm``: A form for logging a
user in.
* ``django.contrib.auth.forms.PasswordChangeForm``: A manipulator
for allowing a user to change their password.
* ``django.contrib.auth.forms.PasswordChangeForm``: A form for allowing a
user to change their password.
* ``django.contrib.auth.forms.PasswordResetForm``: A manipulator
for resetting a user's password and emailing the new password to
them.
* ``django.contrib.auth.forms.PasswordResetForm``: A form for resetting a
user's password and emailing the new password to them.
* ``django.contrib.auth.forms.UserCreationForm``: A manipulator
for creating a new user.
* ``django.contrib.auth.forms.UserCreationForm``: A form for creating a
new user.
Limiting access to logged-in users that pass a test
---------------------------------------------------