0003-misc-prevent-internal-URL-leak-in-browser-history-47.patch
src/authentic2/middleware.py | ||
---|---|---|
160 | 160 |
return response |
161 | 161 |
# Check if there is some messages to show |
162 | 162 |
storage = messages.get_messages(request) |
163 |
if not storage:
|
|
163 |
if not len(storage):
|
|
164 | 164 |
return response |
165 |
only_info = True |
|
166 |
some_message = False |
|
167 |
for message in storage: |
|
168 |
some_message = True |
|
169 |
if message.level != messages.INFO: |
|
170 |
# If there are warnin or error messages, the intermediate page must not redirect |
|
171 |
# automatically but should ask for an user confirmation |
|
172 |
only_info = False |
|
173 |
storage.used = False |
|
174 |
if not some_message: |
|
175 |
return response |
|
176 |
return render(request, 'authentic2/display_message_and_continue.html', |
|
177 |
{'url': url, 'only_info': only_info}) |
|
165 |
return utils.redirect(request, 'continue', resolve=True, params={'next': url}) |
|
178 | 166 | |
179 | 167 | |
180 | 168 |
class ServiceAccessControlMiddleware(MiddlewareMixin): |
src/authentic2/urls.py | ||
---|---|---|
114 | 114 |
url(r'^idp/', include('authentic2.idp.urls')), |
115 | 115 |
url(r'^manage/', include('authentic2.manager.urls')), |
116 | 116 |
url(r'^api/', include('authentic2.api_urls')), |
117 |
url(r'^continue/$', views.display_message_and_continue, name='continue'), |
|
117 | 118 |
] |
118 | 119 | |
119 | 120 |
try: |
src/authentic2/views.py | ||
---|---|---|
1349 | 1349 |
if message: |
1350 | 1350 |
messages.info(request, message) |
1351 | 1351 |
return utils.redirect(request, to=to) |
1352 | ||
1353 | ||
1354 |
class DisplayMessageAndContinueView(TemplateView): |
|
1355 |
template_name = 'authentic2/display_message_and_continue.html' |
|
1356 | ||
1357 |
def get(self, request, *args, **kwargs): |
|
1358 |
self.url = utils.select_next_url(self.request, reverse('account_management')) |
|
1359 |
self.only_info = True |
|
1360 | ||
1361 |
storage = messages.get_messages(request) |
|
1362 |
if not len(storage): |
|
1363 |
return utils.redirect(request, self.url, resolve=False) |
|
1364 | ||
1365 |
for message in storage: |
|
1366 |
if message.level != messages.INFO: |
|
1367 |
# If there are warning or error messages, the intermediate page must not redirect |
|
1368 |
# automatically but should ask for an user confirmation |
|
1369 |
self.only_info = False |
|
1370 |
storage.used = False |
|
1371 |
return super().get(request, *args, **kwargs) |
|
1372 | ||
1373 |
def get_context_data(self, **kwargs): |
|
1374 |
ctx = super().get_context_data(**kwargs) |
|
1375 |
ctx['url'] = self.url |
|
1376 |
ctx['only_info'] = self.only_info |
|
1377 |
return ctx |
|
1378 | ||
1379 |
display_message_and_continue = DisplayMessageAndContinueView.as_view() |
tests/auth_fc/test_auth_fc.py | ||
---|---|---|
191 | 191 |
response.form.set('new_password1', 'ikKL1234') |
192 | 192 |
response.form.set('new_password2', 'ikKL1234') |
193 | 193 |
response = response.form.submit(name='unlink') |
194 |
assert 'The link with the FranceConnect account has been deleted' in response.text |
|
195 | 194 |
assert models.FcAccount.objects.count() == 0 |
196 |
continue_url = response.pyquery('a#a2-continue').attr['href']
|
|
195 |
continue_url = response.location
|
|
197 | 196 |
state = urlparse.parse_qs(urlparse.urlparse(continue_url).query)['state'][0] |
198 | 197 |
assert app.session['fc_states'][state]['next'] == '/accounts/' |
199 |
response = app.get(reverse('fc-logout') + '?state=' + state) |
|
200 |
assert path(response['Location']) == '/accounts/' |
|
198 |
response = app.get(reverse('fc-logout') + '?state=' + state).maybe_follow() |
|
199 |
assert response.request.path == '/accounts/' |
|
200 |
assert 'The link with the FranceConnect account has been deleted' in response.text |
|
201 | 201 | |
202 | 202 | |
203 | 203 |
def test_login_email_is_unique(app, fc_settings, caplog): |
... | ... | |
435 | 435 |
response.form.set('new_password1', 'ikKL1234') |
436 | 436 |
response.form.set('new_password2', 'ikKL1234') |
437 | 437 |
response = response.form.submit(name='unlink') |
438 |
assert 'The link with the FranceConnect account has been deleted' in response.text |
|
439 | 438 |
assert models.FcAccount.objects.count() == 0 |
440 |
continue_url = response.pyquery('a#a2-continue').attr['href']
|
|
439 |
continue_url = response.location
|
|
441 | 440 |
state = urlparse.parse_qs(urlparse.urlparse(continue_url).query)['state'][0] |
442 | 441 |
assert app.session['fc_states'][state]['next'] == '/accounts/' |
443 |
response = app.get(reverse('fc-logout') + '?state=' + state) |
|
444 |
assert path(response['Location']) == '/accounts/' |
|
442 |
response = app.get(reverse('fc-logout') + '?state=' + state).maybe_follow() |
|
443 |
assert response.request.path == '/accounts/' |
|
444 |
assert 'The link with the FranceConnect account has been deleted' in response.text |
|
445 | 445 | |
446 | 446 | |
447 | 447 |
def test_registration2(app, fc_settings, caplog, hooks): |
... | ... | |
529 | 529 |
response.form.set('new_password1', 'ikKL1234') |
530 | 530 |
response.form.set('new_password2', 'ikKL1234') |
531 | 531 |
response = response.form.submit(name='unlink') |
532 |
assert 'The link with the FranceConnect account has been deleted' in response.text |
|
533 | 532 |
assert models.FcAccount.objects.count() == 0 |
534 |
continue_url = response.pyquery('a#a2-continue').attr['href']
|
|
533 |
continue_url = response.location
|
|
535 | 534 |
state = urlparse.parse_qs(urlparse.urlparse(continue_url).query)['state'][0] |
536 | 535 |
assert app.session['fc_states'][state]['next'] == '/accounts/' |
537 |
response = app.get(reverse('fc-logout') + '?state=' + state) |
|
538 |
assert path(response['Location']) == '/accounts/' |
|
536 |
response = app.get(reverse('fc-logout') + '?state=' + state).maybe_follow() |
|
537 |
assert response.request.path == '/accounts/' |
|
538 |
assert 'The link with the FranceConnect account has been deleted' in response.text |
|
539 | 539 | |
540 | 540 | |
541 | 541 |
def test_can_change_password(app, fc_settings, caplog, hooks): |
... | ... | |
653 | 653 |
response.form.set('new_password1', 'ikKL1234') |
654 | 654 |
response.form.set('new_password2', 'ikKL1234') |
655 | 655 |
response = response.form.submit(name='unlink') |
656 |
continue_url = response.pyquery('a#a2-continue').attr['href'] |
|
656 |
assert models.FcAccount.objects.count() == 0 |
|
657 |
continue_url = response.location |
|
657 | 658 |
state = urlparse.parse_qs(urlparse.urlparse(continue_url).query)['state'][0] |
658 | 659 |
assert app.session['fc_states'][state]['next'] == '/accounts/' |
659 |
response = app.get(reverse('fc-logout') + '?state=' + state) |
|
660 |
assert path(response['Location']) == '/accounts/'
|
|
661 |
response = response.follow()
|
|
660 |
response = app.get(reverse('fc-logout') + '?state=' + state).maybe_follow()
|
|
661 |
assert response.request.path == '/accounts/'
|
|
662 |
assert 'The link with the FranceConnect account has been deleted' in response.text
|
|
662 | 663 |
assert len(response.pyquery('[href*="password/change"]')) > 0 |
663 | 664 | |
664 | 665 |
tests/settings.py | ||
---|---|---|
56 | 56 |
A2_MAX_EMAILS_FOR_ADDRESS = None |
57 | 57 | |
58 | 58 |
A2_TOKEN_EXISTS_WARNING = False |
59 |
A2_REDIRECT_WHITELIST = ['http://sp.org/'] |
tests/test_all.py | ||
---|---|---|
494 | 494 |
# User side |
495 | 495 |
client = Client() |
496 | 496 |
activation_url = get_link_from_mail(mail.outbox[-1]) |
497 |
response = client.get(activation_url) |
|
497 |
response = client.get(activation_url, follow=True)
|
|
498 | 498 |
self.assertEqual(response.status_code, status.HTTP_200_OK) |
499 | 499 |
assert utils.make_url(return_url, params={'token': token}) in force_text(response.content) |
500 | 500 |
self.assertEqual(User.objects.count(), user_count + 1) |
... | ... | |
608 | 608 |
# User side - user click on email |
609 | 609 |
client = Client() |
610 | 610 |
activation_url = get_link_from_mail(mail.outbox[0]) |
611 |
response = client.get(activation_url) |
|
611 |
response = client.get(activation_url, follow=True)
|
|
612 | 612 |
self.assertEqual(response.status_code, status.HTTP_200_OK) |
613 | 613 |
assert utils.make_url(return_url, params={'token': token}) in force_text(response.content) |
614 | 614 |
self.assertEqual(User.objects.count(), user_count + 1) |
... | ... | |
714 | 714 |
# User side - user click on first email |
715 | 715 |
client = Client() |
716 | 716 |
activation_url = get_link_from_mail(activation_mail1) |
717 |
response = client.get(activation_url) |
|
717 |
response = client.get(activation_url, follow=True)
|
|
718 | 718 |
self.assertEqual(response.status_code, status.HTTP_200_OK) |
719 | 719 |
assert utils.make_url(return_url, params={'token': token}) in force_text(response.content) |
720 | 720 |
self.assertEqual(User.objects.count(), user_count + 1) |
tests/test_registration.py | ||
---|---|---|
74 | 74 |
# set valid password |
75 | 75 |
response.form.set('password1', 'T0==toto') |
76 | 76 |
response.form.set('password2', 'T0==toto') |
77 |
response = response.form.submit() |
|
77 |
response = response.form.submit().maybe_follow()
|
|
78 | 78 |
if good_next_url: |
79 | 79 |
assert 'You have just created an account.' in response.text |
80 | 80 |
assert next_url in response.text |
81 |
assert response.request.path == '/continue/' |
|
81 | 82 |
else: |
82 |
assert urlparse(response['Location']).path == '/' |
|
83 |
response = response.follow() |
|
83 |
assert response.request.path == '/' |
|
84 | 84 |
assert 'You have just created an account.' in response.text |
85 | 85 |
assert User.objects.count() == 1 |
86 | 86 |
assert len(mailoutbox) == 2 |
... | ... | |
132 | 132 |
response.form.set('username', 'toto') |
133 | 133 |
response.form.set('password1', 'T0==toto') |
134 | 134 |
response.form.set('password2', 'T0==toto') |
135 |
response = response.form.submit() |
|
135 |
response = response.form.submit().follow()
|
|
136 | 136 |
assert 'You have just created an account.' in response.text |
137 | 137 |
assert next_url in response.text |
138 | 138 |
assert len(mailoutbox) == 2 |
... | ... | |
570 | 570 |
response = app.get(link) |
571 | 571 |
response.form.set('password1', 'T0==toto') |
572 | 572 |
response.form.set('password2', 'T0==toto') |
573 |
response = response.form.submit() |
|
573 |
response = response.form.submit().follow()
|
|
574 | 574 | |
575 | 575 |
assert 'You have just created an account.' in response.text |
576 | 576 |
assert new_next_url in response.text |
... | ... | |
616 | 616 |
response = app.get(link) |
617 | 617 |
response.form.set('password1', 'T0==toto') |
618 | 618 |
response.form.set('password2', 'T0==toto') |
619 |
response = response.form.submit() |
|
619 |
response = response.form.submit().follow()
|
|
620 | 620 |
assert new_next_url in response.text |
621 | 621 | |
622 | 622 | |
623 |
- |