Skip to content
Browse files

Fixed #13316 -- Modified the default behavior of PasswordInput to pre…

…vent reflecting passwords on form failure. Thanks to clouserw for the report.

Although this changes nothing at a functional level, this is BACKWARDS INCOMPATIBLE from a UX perspective for anyone that wants passwords to be reflected to the user on form failure. See the 1.3 release notes for details.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@13498 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 69d1e71 commit d3ba8cb88b2f3939398c9c54e5aa5c6e7495e56f @freakboy3742 freakboy3742 committed
View
2 django/forms/widgets.py
@@ -229,7 +229,7 @@ class TextInput(Input):
class PasswordInput(Input):
input_type = 'password'
- def __init__(self, attrs=None, render_value=True):
+ def __init__(self, attrs=None, render_value=False):
super(PasswordInput, self).__init__(attrs)
self.render_value = render_value
View
43 docs/ref/forms/widgets.txt
@@ -6,7 +6,7 @@ Widgets
.. module:: django.forms.widgets
:synopsis: Django's built-in form widgets.
-
+
.. currentmodule:: django.forms
A widget is Django's representation of a HTML input element. The widget
@@ -29,7 +29,12 @@ commonly used groups of widgets:
.. attribute:: PasswordInput.render_value
Determines whether the widget will have a value filled in when the
- form is re-displayed after a validation error (default is ``True``).
+ form is re-displayed after a validation error (default is ``False``).
+
+.. versionchanged:: 1.3
+ The default value for
+ :attr:`~PasswordInput.render_value` was
+ changed from ``True`` to ``False``
.. class:: HiddenInput
@@ -50,7 +55,7 @@ commonly used groups of widgets:
Date input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
-
+
.. attribute:: DateInput.format
The format in which this field's initial value will be displayed.
@@ -64,11 +69,11 @@ commonly used groups of widgets:
Date/time input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
-
+
.. attribute:: DateTimeInput.format
-
+
The format in which this field's initial value will be displayed.
-
+
If no ``format`` argument is provided, the default format is ``'%Y-%m-%d
%H:%M:%S'``.
@@ -77,11 +82,11 @@ commonly used groups of widgets:
Time input as a simple text box: ``<input type='text' ...>``
Takes one optional argument:
-
+
.. attribute:: TimeInput.format
-
+
The format in which this field's initial value will be displayed.
-
+
If no ``format`` argument is provided, the default format is ``'%H:%M:%S'``.
.. versionchanged:: 1.1
@@ -98,15 +103,15 @@ commonly used groups of widgets:
Takes one optional argument:
.. attribute:: CheckboxInput.check_test
-
- A callable that takes the value of the CheckBoxInput
+
+ A callable that takes the value of the CheckBoxInput
and returns ``True`` if the checkbox should be checked for
- that value.
+ that value.
.. class:: Select
Select widget: ``<select><option ...>...</select>``
-
+
Requires that your field provides :attr:`~Field.choices`.
.. class:: NullBooleanSelect
@@ -123,22 +128,22 @@ commonly used groups of widgets:
.. class:: RadioSelect
A list of radio buttons:
-
+
.. code-block:: html
-
+
<ul>
<li><input type='radio' ...></li>
...
</ul>
-
+
Requires that your field provides :attr:`~Field.choices`.
.. class:: CheckboxSelectMultiple
A list of checkboxes:
-
+
.. code-block:: html
-
+
<ul>
<li><input type='checkbox' ...></li>
...
@@ -155,7 +160,7 @@ commonly used groups of widgets:
Takes two optional arguments, ``date_format`` and ``time_format``, which
work just like the ``format`` argument for ``DateInput`` and ``TimeInput``.
-
+
.. versionchanged:: 1.1
The ``date_format`` and ``time_format`` arguments were not supported in Django 1.0.
View
25 docs/releases/1.3.txt
@@ -18,6 +18,31 @@ fixes and an easy upgrade path from Django 1.2.
Backwards-incompatible changes in 1.3
=====================================
+PasswordInput default rendering behavior
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Prior to Django 1.3, a :class:`~django.forms.PasswordInput` would render
+data values like any other form. If a form submission raised an error,
+the password that was submitted would be reflected to the client as form
+data populating the form for resubmission.
+
+This had the potential to leak passwords, as any failed password
+attempt would cause the password that was typed to be sent back to the
+client.
+
+In Django 1.3, the default behavior of
+:class:`~django.forms.PasswordInput` is to suppress the display of
+password values. This change doesn't alter the way form data is
+validated or handled. It only affects the user experience with
+passwords on a form when they make an error submitting form data (such
+as on unsuccessful logins, or when completing a registration form).
+
+If you want restore the pre-Django 1.3 behavior, you need to pass in a
+custom widget to your form that sets the ``render_value`` argument::
+
+ class LoginForm(forms.Form):
+ username = forms.CharField(max_length=100)
+ password = forms.PasswordField(widget=forms.PasswordInput(render_value=True))
Features deprecated in 1.3
View
20 tests/regressiontests/forms/forms.py
@@ -705,13 +705,13 @@
>>> print f.as_table()
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>
-<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr>
-<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
+<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr>
+<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
>>> print f.as_ul()
<li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li>
<li>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
-<li>Password1: <input type="password" name="password1" value="foo" /></li>
-<li>Password2: <input type="password" name="password2" value="bar" /></li>
+<li>Password1: <input type="password" name="password1" /></li>
+<li>Password2: <input type="password" name="password2" /></li>
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
@@ -1589,8 +1589,8 @@
<table>
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters (it has 23).</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
-<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr>
-<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
+<tr><th>Password1:</th><td><input type="password" name="password1" /></td></tr>
+<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
</table>
<input type="submit" />
</form>
@@ -1719,8 +1719,8 @@
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))
<form action="">
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
-<p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
-<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
+<p><label>Password: <input type="password" name="password1" /></label></p>
+<p><label>Password (again): <input type="password" name="password2" /></label></p>
<input type="submit" />
</form>
>>> t = Template('''<form action="">
@@ -1734,8 +1734,8 @@
<form action="">
<ul class="errorlist"><li>Please make sure your passwords match.</li></ul>
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
-<p><label>Password: <input type="password" name="password1" value="foo" /></label></p>
-<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
+<p><label>Password: <input type="password" name="password1" /></label></p>
+<p><label>Password (again): <input type="password" name="password2" /></label></p>
<input type="submit" />
</form>
View
33 tests/regressiontests/forms/widgets.py
@@ -62,6 +62,17 @@
u'<input type="password" name="email" />'
>>> w.render('email', None)
u'<input type="password" name="email" />'
+>>> w.render('email', 'secret')
+u'<input type="password" name="email" />'
+
+The render_value argument lets you specify whether the widget should render
+its value. For security reasons, this is off by default.
+
+>>> w = PasswordInput(render_value=True)
+>>> w.render('email', '')
+u'<input type="password" name="email" />'
+>>> w.render('email', None)
+u'<input type="password" name="email" />'
>>> w.render('email', 'test@example.com')
u'<input type="password" name="email" value="test@example.com" />'
>>> w.render('email', 'some "quoted" & ampersanded value')
@@ -70,36 +81,20 @@
u'<input type="password" name="email" value="test@example.com" class="fun" />'
You can also pass 'attrs' to the constructor:
->>> w = PasswordInput(attrs={'class': 'fun'})
+>>> w = PasswordInput(attrs={'class': 'fun'}, render_value=True)
>>> w.render('email', '')
u'<input type="password" class="fun" name="email" />'
>>> w.render('email', 'foo@example.com')
u'<input type="password" class="fun" value="foo@example.com" name="email" />'
'attrs' passed to render() get precedence over those passed to the constructor:
->>> w = PasswordInput(attrs={'class': 'pretty'})
+>>> w = PasswordInput(attrs={'class': 'pretty'}, render_value=True)
>>> w.render('email', '', attrs={'class': 'special'})
u'<input type="password" class="special" name="email" />'
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
-The render_value argument lets you specify whether the widget should render
-its value. You may want to do this for security reasons.
->>> w = PasswordInput(render_value=True)
->>> w.render('email', 'secret')
-u'<input type="password" name="email" value="secret" />'
->>> w = PasswordInput(render_value=False)
->>> w.render('email', '')
-u'<input type="password" name="email" />'
->>> w.render('email', None)
-u'<input type="password" name="email" />'
->>> w.render('email', 'secret')
-u'<input type="password" name="email" />'
->>> w = PasswordInput(attrs={'class': 'fun'}, render_value=False)
->>> w.render('email', 'secret')
-u'<input type="password" class="fun" name="email" />'
-
# HiddenInput Widget ############################################################
>>> w = HiddenInput()
@@ -1286,7 +1281,7 @@ def __init__(self, choices=[]):
forms.TextInput
]
super(SelectAndTextWidget, self).__init__(widgets)
-
+
def _set_choices(self, choices):
"""
When choices are set for this widget, we want to pass those along to the Select widget

0 comments on commit d3ba8cb

Please sign in to comment.
Something went wrong with that request. Please try again.