Problem with views PasswordChangeView vs. password_change

601 views
Skip to first unread message

Carsten Fuchs

unread,
Jul 12, 2017, 2:48:25 PM7/12/17
to django...@googlegroups.com
Dear Django group,

using Django 1.11.3 with Python 2.7, please consider the following test:


def test_passwort_aendern(self):

u = User.objects.create_user("Test", "te...@example.com", "Passwort")
self.client.force_login(u)

response = self.client.post("/change_password/", {
"old_password": "Passwort",
"new_password1": "test",
"new_password2": "test",
}, follow=True)

self.assertContains(response, "Password successfully changed")


The test is successful if the URL maps to the (now unfortunately deprecated)
function-based view:


from django.contrib.auth import views as auth_views

urlpatterns = [
# ...
url(r'^change_password/$', auth_views.password_change,
{'post_change_redirect': 'lori:pwd_changed'}, name='change_password'),
]


If I use the class-based view in its place, the test fails:


urlpatterns = [
# ...
url(r'^change_password/$',
auth_views.PasswordChangeView.as_view(success_url='lori:pwd_changed'),
name='change_password'),
]


The test output is:
> AssertionError: Couldn't retrieve content: Response code was 400 (expected 200)

When called in the browser, the result is an error page with:
> SuspiciousOperation at /change_password/
> The request's session was deleted before the request completed. The user may have logged out in a concurrent request, for example.

The given stack trace is below.


I have so far not been able to figure out the relationship between the session
(which I guess the password change view expires intentionally?) and the error
message…

Any ideas what may be causing this problem?

Best regards,
Carsten


> Environment:
>
>
> Request Method: POST
> Request URL: http://localhost/lori/change_password/
>
> Django Version: 1.11.3
> Python Version: 2.7.3
> Installed Applications:
> ('django.contrib.admin',
> 'django.contrib.auth',
> 'django.contrib.contenttypes',
> 'django.contrib.sessions',
> 'django.contrib.staticfiles',
> 'django.contrib.messages',
> 'Lori',
> 'PerfMon',
> 'Spesen',
> 'Urlaubsantraege')
> Installed Middleware:
> ['django.contrib.sessions.middleware.SessionMiddleware',
> 'django.middleware.common.CommonMiddleware',
> 'django.middleware.csrf.CsrfViewMiddleware',
> 'django.contrib.auth.middleware.AuthenticationMiddleware',
> 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
> 'django.contrib.messages.middleware.MessageMiddleware',
> 'Lori.middleware.ggVerfallenMiddleware']
>
>
>
> Traceback:
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
> 41. response = get_response(request)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/lib/python2.7/site-packages/django/utils/deprecation.py" in __call__
> 142. response = self.process_response(request, response)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/lib/python2.7/site-packages/django/contrib/sessions/middleware.py" in process_response
> 61. "The request's session was deleted before the "
>
> Exception Type: SuspiciousOperation at /change_password/
> Exception Value: The request's session was deleted before the request completed. The user may have logged out in a concurrent request, for example.


Tim Graham

unread,
Jul 12, 2017, 4:00:48 PM7/12/17
to Django users
Hi, I can't reproduce that error. Can you provide a sample project? Maybe it has something to do with the ggVerfallenMiddleware?

Carsten Fuchs

unread,
Jul 13, 2017, 2:58:22 PM7/13/17
to django...@googlegroups.com
Hi Tim,

Am 12.07.2017 um 18:00 schrieb Tim Graham:
> Hi, I can't reproduce that error. Can you provide a sample project? Maybe it has
> something to do with the ggVerfallenMiddleware?

I'm still working on a sample project, but found in the same context another,
related problem:

In the app's Lori/urls.py file with contents


app_name = 'lori'

urlpatterns = [
url(r'^change_password/$',
auth_views.PasswordChangeView.as_view(success_url='lori:pwd_done'),
name='change_password'),
url(r'^pwd_done/$', auth_views.PasswordChangeDoneView.as_view(),
name='pwd_done'),
]


the success_url='lori:pwd_done' seems to be a problem, yielding an exception:

DisallowedRedirect at /change_password/
Unsafe redirect to URL with protocol 'lori'

This problem does not exist with the old view:

url(r'^change_password/$', auth_views.password_change,
{'post_change_redirect': 'lori:pwd_done'}, name='change_password'),

Please see the stack trace below; I'm also including the sample project that I
used to reproduce this (running the tests or starting at
http://127.0.0.1:8000/admin/login/?next=/change_password/ using 'carsten' as
username and 'a' as password).

Best regards,
Carsten



This is the stack trace:

> Environment:
>
>
> Request Method: POST
> Request URL: http://127.0.0.1:8000/change_password/
>
> Django Version: 1.11.3
> Python Version: 2.7.3
> Installed Applications:
> ['django.contrib.admin',
> 'django.contrib.auth',
> 'django.contrib.contenttypes',
> 'django.contrib.sessions',
> 'django.contrib.messages',
> 'django.contrib.staticfiles',
> 'Lori']
> Installed Middleware:
> ['django.middleware.security.SecurityMiddleware',
> 'django.contrib.sessions.middleware.SessionMiddleware',
> 'django.middleware.common.CommonMiddleware',
> 'django.middleware.csrf.CsrfViewMiddleware',
> 'django.contrib.auth.middleware.AuthenticationMiddleware',
> 'django.contrib.messages.middleware.MessageMiddleware',
> 'django.middleware.clickjacking.XFrameOptionsMiddleware']
>
>
>
> Traceback:
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
> 41. response = get_response(request)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
> 187. response = self.process_exception_by_middleware(e, request)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
> 185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/views/generic/base.py" in view
> 68. return self.dispatch(request, *args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
> 67. return bound_func(*args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/views/decorators/debug.py" in sensitive_post_parameters_wrapper
> 76. return view(request, *args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
> 63. return func.__get__(self, type(self))(*args2, **kwargs2)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
> 67. return bound_func(*args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
> 149. response = view_func(request, *args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
> 63. return func.__get__(self, type(self))(*args2, **kwargs2)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/utils/decorators.py" in _wrapper
> 67. return bound_func(*args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
> 23. return view_func(request, *args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/utils/decorators.py" in bound_func
> 63. return func.__get__(self, type(self))(*args2, **kwargs2)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/contrib/auth/views.py" in dispatch
> 589. return super(PasswordChangeView, self).dispatch(*args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/views/generic/base.py" in dispatch
> 88. return handler(request, *args, **kwargs)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/views/generic/edit.py" in post
> 183. return self.form_valid(form)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/contrib/auth/views.py" in form_valid
> 601. return super(PasswordChangeView, self).form_valid(form)
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/views/generic/edit.py" in form_valid
> 79. return HttpResponseRedirect(self.get_success_url())
>
> File "/home/carsten/.virtualenvs/Zeiterfassung/local/lib/python2.7/site-packages/django/http/response.py" in __init__
> 431. raise DisallowedRedirect("Unsafe redirect to URL with protocol '%s'" % parsed.scheme)
>
> Exception Type: DisallowedRedirect at /change_password/
> Exception Value: Unsafe redirect to URL with protocol 'lori'
change_pwd_test.zip

Carsten Fuchs

unread,
Jul 13, 2017, 3:44:43 PM7/13/17
to django...@googlegroups.com
Am 13.07.2017 um 16:56 schrieb Carsten Fuchs:
> the success_url='lori:pwd_done' seems to be a problem

This seems to be the root also of the original problem (SuspiciousOperation…):
If I insert a plain URL, e.g. success_url='/pwd_done/', then I can no longer
reproduce the original problem either.

Best regards,
Carsten

Tim Graham

unread,
Jul 13, 2017, 8:44:45 PM7/13/17
to Django users
The success_url of PasswordChangeView (actually the behavior comes from the inherited FormView) only accepts a URL, not a URL name. You can update to your code like this:

from django.urls import reverse_lazy
...
success_url=reverse_lazy('lori:pwd_done')

Carsten Fuchs

unread,
Jul 14, 2017, 2:57:45 PM7/14/17
to django...@googlegroups.com
Am 13.07.2017 um 22:44 schrieb Tim Graham:
> The success_url of PasswordChangeView (actually the behavior comes from the
> inherited FormView) only accepts a URL, not a URL name. You can update to your
> code like this:
>
> from django.urls import reverse_lazy
> ...
> success_url=reverse_lazy('lori:pwd_done')

That works!
Thank you very much! :-)

Best regards,
Carsten
Reply all
Reply to author
Forward
0 new messages