0002-use-MiddlewareMixin-on-middleware-36509.patch
mellon/app_settings.py | ||
---|---|---|
31 | 31 |
'DEFAULT_ASSERTION_CONSUMER_BINDING': 'post', # or artifact |
32 | 32 |
'VERIFY_SSL_CERTIFICATE': True, |
33 | 33 |
'OPENED_SESSION_COOKIE_NAME': None, |
34 |
'OPENED_SESSION_COOKIE_DOMAIN': None, |
|
35 | 34 |
'ORGANIZATION': None, |
36 | 35 |
'CONTACT_PERSONS': [], |
37 | 36 |
'TRANSIENT_FEDERATION_ATTRIBUTE': None, |
mellon/compat.py | ||
---|---|---|
1 |
# django-mellon - SAML2 authentication for Django |
|
2 |
# Copyright (C) 2014-2019 Entr'ouvert |
|
3 |
# This program is free software: you can redistribute it and/or modify |
|
4 |
# it under the terms of the GNU Affero General Public License as |
|
5 |
# published by the Free Software Foundation, either version 3 of the |
|
6 |
# License, or (at your option) any later version. |
|
7 | ||
8 |
# This program is distributed in the hope that it will be useful, |
|
9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 |
# GNU Affero General Public License for more details. |
|
12 | ||
13 |
# You should have received a copy of the GNU Affero General Public License |
|
14 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | ||
16 |
import django |
|
17 |
if django.VERSION < (1, 11, 0): |
|
18 |
from django.core.urlresolvers import reverse |
|
19 |
MiddlewareClass = object |
|
20 | ||
21 |
is_authenticated = lambda user: user.is_authenticated() |
|
22 |
else: |
|
23 |
from django.urls import reverse |
|
24 |
from django.utils.deprecation import MiddlewareMixin |
|
25 |
MiddlewareClass = MiddlewareMixin |
|
26 | ||
27 |
is_authenticated = lambda user: user.is_authenticated |
mellon/middleware.py | ||
---|---|---|
19 | 19 |
from django.http import HttpResponseRedirect |
20 | 20 | |
21 | 21 |
from . import app_settings, utils |
22 |
from .compat import reverse |
|
22 |
from .compat import reverse, MiddlewareClass, is_authenticated
|
|
23 | 23 | |
24 | 24 |
PASSIVE_TRIED_COOKIE = 'MELLON_PASSIVE_TRIED' |
25 | 25 | |
26 | 26 | |
27 |
class PassiveAuthenticationMiddleware(object):
|
|
27 |
class PassiveAuthenticationMiddleware(MiddlewareClass):
|
|
28 | 28 |
def process_response(self, request, response): |
29 | 29 |
# When unlogged remove the PASSIVE_TRIED cookie |
30 | 30 |
if app_settings.OPENED_SESSION_COOKIE_NAME \ |
... | ... | |
47 | 47 |
return |
48 | 48 |
if not app_settings.OPENED_SESSION_COOKIE_NAME: |
49 | 49 |
return |
50 |
if hasattr(request, 'user') and request.user.is_authenticated():
|
|
50 |
if hasattr(request, 'user') and is_authenticated(request.user):
|
|
51 | 51 |
return |
52 | 52 |
if PASSIVE_TRIED_COOKIE in request.COOKIES: |
53 | 53 |
return |
54 |
if app_settings.OPENED_SESSION_COOKIE_NAME in request.COOKIES: |
|
55 |
# get the common domain or guess |
|
56 |
common_domain = app_settings.OPENED_SESSION_COOKIE_DOMAIN |
|
57 |
if not common_domain: |
|
58 |
host = request.get_host() |
|
59 |
# accept automatic common domain selection if domain has at least three components |
|
60 |
# and is not an IP address |
|
61 |
if not host.count('.') > 1 or host.replace('.', '').isdigit(): |
|
62 |
return |
|
63 |
common_domain = request.get_host().split('.', 1)[1] |
|
64 |
params = { |
|
65 |
'next': request.build_absolute_uri(), |
|
66 |
'passive': '', |
|
67 |
} |
|
68 |
url = reverse('mellon_login') + '?%s' % urlencode(params) |
|
69 |
response = HttpResponseRedirect(url) |
|
70 |
# prevent loops |
|
71 |
response.set_cookie(PASSIVE_TRIED_COOKIE, value='1', max_age=None) |
|
72 |
return response |
|
54 |
if app_settings.OPENED_SESSION_COOKIE_NAME not in request.COOKIES: |
|
55 |
return |
|
56 |
# all is good, try passive login |
|
57 |
params = { |
|
58 |
'next': request.build_absolute_uri(), |
|
59 |
'passive': '', |
|
60 |
} |
|
61 |
url = reverse('mellon_login') + '?%s' % urlencode(params) |
|
62 |
response = HttpResponseRedirect(url) |
|
63 |
# prevent loops |
|
64 |
response.set_cookie(PASSIVE_TRIED_COOKIE, value='1', max_age=None) |
|
65 |
return response |
tests/test_sso_slo.py | ||
---|---|---|
373 | 373 |
login_hints = root.findall('.//{https://www.entrouvert.com/}login-hint') |
374 | 374 |
assert len(login_hints) == 1, 'missing login hint' |
375 | 375 |
assert login_hints[0].text == 'backoffice', 'login hint is not backoffice' |
376 | ||
377 | ||
378 |
def test_middleware_mixin_first_time(db, app, idp, caplog, settings): |
|
379 |
settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION' |
|
380 |
assert 'MELLON_PASSIVE_TRIED' not in app.cookies |
|
381 |
# webtest-lint is against unicode |
|
382 |
app.set_cookie(str('IDP_SESSION'), str('1')) |
|
383 |
response = app.get('/', status=302) |
|
384 |
assert urlparse.urlparse(response.location).path == '/login/' |
|
385 |
assert (urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) |
|
386 |
== {'next': ['http://testserver/'], 'passive': ['']}) |
|
387 | ||
388 |
# simulate closing of session at IdP |
|
389 |
app.cookiejar.clear('testserver.local', '/', 'IDP_SESSION') |
|
390 |
assert 'IDP_SESSION' not in app.cookies |
|
391 | ||
392 |
# verify MELLON_PASSIVE_TRIED is removed |
|
393 |
assert 'MELLON_PASSIVE_TRIED' in app.cookies |
|
394 |
response = app.get('/', status=200) |
|
395 |
assert 'MELLON_PASSIVE_TRIED' not in app.cookies |
|
396 | ||
397 |
# check passive authentication is tried again |
|
398 |
app.set_cookie(str('IDP_SESSION'), str('1')) |
|
399 |
response = app.get('/', status=302) |
|
400 |
assert urlparse.urlparse(response.location).path == '/login/' |
|
401 |
assert (urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) |
|
402 |
== {'next': ['http://testserver/'], 'passive': ['']}) |
|
403 |
assert 'MELLON_PASSIVE_TRIED' in app.cookies |
tests/urls_tests.py | ||
---|---|---|
13 | 13 |
# You should have received a copy of the GNU Affero General Public License |
14 | 14 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
15 | 15 | |
16 |
import django |
|
17 | ||
18 | 16 |
from django.conf.urls import url, include |
19 | 17 |
from django.http import HttpResponse |
20 | 18 |
testsettings.py | ||
---|---|---|
21 | 21 |
MIDDLEWARE_CLASSES += ( |
22 | 22 |
'django.contrib.sessions.middleware.SessionMiddleware', |
23 | 23 |
'django.contrib.auth.middleware.AuthenticationMiddleware', |
24 |
'mellon.middleware.PassiveAuthenticationMiddleware', |
|
24 | 25 |
) |
25 | 26 |
else: |
26 | 27 |
MIDDLEWARE = global_settings.MIDDLEWARE |
27 | 28 |
MIDDLEWARE += ( |
28 | 29 |
'django.contrib.sessions.middleware.SessionMiddleware', |
29 | 30 |
'django.contrib.auth.middleware.AuthenticationMiddleware', |
31 |
'mellon.middleware.PassiveAuthenticationMiddleware', |
|
30 | 32 |
) |
31 | 33 | |
32 | 34 |
AUTHENTICATION_BACKENDS = ( |
33 |
- |