Fixed #13316 -- Modified the default behavior of PasswordInput to prevent 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
This commit is contained in:
parent
69d1e71fad
commit
d3ba8cb88b
|
@ -229,7 +229,7 @@ class TextInput(Input):
|
||||||
class PasswordInput(Input):
|
class PasswordInput(Input):
|
||||||
input_type = 'password'
|
input_type = 'password'
|
||||||
|
|
||||||
def __init__(self, attrs=None, render_value=True):
|
def __init__(self, attrs=None, render_value=False):
|
||||||
super(PasswordInput, self).__init__(attrs)
|
super(PasswordInput, self).__init__(attrs)
|
||||||
self.render_value = render_value
|
self.render_value = render_value
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,12 @@ commonly used groups of widgets:
|
||||||
.. attribute:: PasswordInput.render_value
|
.. attribute:: PasswordInput.render_value
|
||||||
|
|
||||||
Determines whether the widget will have a value filled in when the
|
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
|
.. class:: HiddenInput
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,31 @@ fixes and an easy upgrade path from Django 1.2.
|
||||||
Backwards-incompatible changes in 1.3
|
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
|
Features deprecated in 1.3
|
||||||
|
|
|
@ -705,13 +705,13 @@ Form.clean() is required to return a dictionary of all clean data.
|
||||||
>>> print f.as_table()
|
>>> print f.as_table()
|
||||||
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
<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>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>Password1:</th><td><input type="password" name="password1" /></td></tr>
|
||||||
<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
|
<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
|
||||||
>>> print f.as_ul()
|
>>> print f.as_ul()
|
||||||
<li><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></li>
|
<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>Username: <input type="text" name="username" value="adrian" maxlength="10" /></li>
|
||||||
<li>Password1: <input type="password" name="password1" value="foo" /></li>
|
<li>Password1: <input type="password" name="password1" /></li>
|
||||||
<li>Password2: <input type="password" name="password2" value="bar" /></li>
|
<li>Password2: <input type="password" name="password2" /></li>
|
||||||
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
|
@ -1589,8 +1589,8 @@ Case 2: POST with erroneous data (a redisplayed form, with errors).
|
||||||
<table>
|
<table>
|
||||||
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
<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>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>Password1:</th><td><input type="password" name="password1" /></td></tr>
|
||||||
<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
|
<tr><th>Password2:</th><td><input type="password" name="password2" /></td></tr>
|
||||||
</table>
|
</table>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
|
@ -1719,8 +1719,8 @@ the list of errors is empty). You can also use it in {% if %} statements.
|
||||||
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))
|
>>> print t.render(Context({'form': UserRegistration({'username': 'django', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)}))
|
||||||
<form action="">
|
<form action="">
|
||||||
<p><label>Your username: <input type="text" name="username" value="django" maxlength="10" /></label></p>
|
<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: <input type="password" name="password1" /></label></p>
|
||||||
<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
|
<p><label>Password (again): <input type="password" name="password2" /></label></p>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
>>> t = Template('''<form action="">
|
>>> t = Template('''<form action="">
|
||||||
|
@ -1734,8 +1734,8 @@ the list of errors is empty). You can also use it in {% if %} statements.
|
||||||
<form action="">
|
<form action="">
|
||||||
<ul class="errorlist"><li>Please make sure your passwords match.</li></ul>
|
<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>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: <input type="password" name="password1" /></label></p>
|
||||||
<p><label>Password (again): <input type="password" name="password2" value="bar" /></label></p>
|
<p><label>Password (again): <input type="password" name="password2" /></label></p>
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,17 @@ u'<input type="text" class="special" name="email" />'
|
||||||
u'<input type="password" name="email" />'
|
u'<input type="password" name="email" />'
|
||||||
>>> w.render('email', None)
|
>>> w.render('email', None)
|
||||||
u'<input type="password" name="email" />'
|
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')
|
>>> w.render('email', 'test@example.com')
|
||||||
u'<input type="password" name="email" value="test@example.com" />'
|
u'<input type="password" name="email" value="test@example.com" />'
|
||||||
>>> w.render('email', 'some "quoted" & ampersanded value')
|
>>> w.render('email', 'some "quoted" & ampersanded value')
|
||||||
|
@ -70,36 +81,20 @@ u'<input type="password" name="email" value="some "quoted" & amper
|
||||||
u'<input type="password" name="email" value="test@example.com" class="fun" />'
|
u'<input type="password" name="email" value="test@example.com" class="fun" />'
|
||||||
|
|
||||||
You can also pass 'attrs' to the constructor:
|
You can also pass 'attrs' to the constructor:
|
||||||
>>> w = PasswordInput(attrs={'class': 'fun'})
|
>>> w = PasswordInput(attrs={'class': 'fun'}, render_value=True)
|
||||||
>>> w.render('email', '')
|
>>> w.render('email', '')
|
||||||
u'<input type="password" class="fun" name="email" />'
|
u'<input type="password" class="fun" name="email" />'
|
||||||
>>> w.render('email', 'foo@example.com')
|
>>> w.render('email', 'foo@example.com')
|
||||||
u'<input type="password" class="fun" value="foo@example.com" name="email" />'
|
u'<input type="password" class="fun" value="foo@example.com" name="email" />'
|
||||||
|
|
||||||
'attrs' passed to render() get precedence over those passed to the constructor:
|
'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'})
|
>>> w.render('email', '', attrs={'class': 'special'})
|
||||||
u'<input type="password" class="special" name="email" />'
|
u'<input type="password" class="special" name="email" />'
|
||||||
|
|
||||||
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
|
||||||
u'<input type="password" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
|
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 ############################################################
|
# HiddenInput Widget ############################################################
|
||||||
|
|
||||||
>>> w = HiddenInput()
|
>>> w = HiddenInput()
|
||||||
|
|
Loading…
Reference in New Issue