diff --git a/mellon/app_settings.py b/mellon/app_settings.py
index fdbf949..acf99ae 100644
--- a/mellon/app_settings.py
+++ b/mellon/app_settings.py
@@ -31,7 +31,6 @@ class AppSettings(object):
'DEFAULT_ASSERTION_CONSUMER_BINDING': 'post', # or artifact
'VERIFY_SSL_CERTIFICATE': True,
'OPENED_SESSION_COOKIE_NAME': None,
- 'OPENED_SESSION_COOKIE_DOMAIN': None,
'ORGANIZATION': None,
'CONTACT_PERSONS': [],
'TRANSIENT_FEDERATION_ATTRIBUTE': None,
diff --git a/mellon/compat.py b/mellon/compat.py
new file mode 100644
index 0000000..3ec9e6e
--- /dev/null
+++ b/mellon/compat.py
@@ -0,0 +1,27 @@
+# django-mellon - SAML2 authentication for Django
+# Copyright (C) 2014-2019 Entr'ouvert
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+import django
+if django.VERSION < (1, 11, 0):
+ from django.core.urlresolvers import reverse
+ MiddlewareClass = object
+
+ is_authenticated = lambda user: user.is_authenticated()
+else:
+ from django.urls import reverse
+ from django.utils.deprecation import MiddlewareMixin
+ MiddlewareClass = MiddlewareMixin
+
+ is_authenticated = lambda user: user.is_authenticated
diff --git a/mellon/middleware.py b/mellon/middleware.py
index d834b60..2a31a21 100644
--- a/mellon/middleware.py
+++ b/mellon/middleware.py
@@ -19,12 +19,12 @@ from django.utils.http import urlencode
from django.http import HttpResponseRedirect
from . import app_settings, utils
-from .compat import reverse
+from .compat import reverse, MiddlewareClass, is_authenticated
PASSIVE_TRIED_COOKIE = 'MELLON_PASSIVE_TRIED'
-class PassiveAuthenticationMiddleware(object):
+class PassiveAuthenticationMiddleware(MiddlewareClass):
def process_response(self, request, response):
# When unlogged remove the PASSIVE_TRIED cookie
if app_settings.OPENED_SESSION_COOKIE_NAME \
@@ -47,26 +47,19 @@ class PassiveAuthenticationMiddleware(object):
return
if not app_settings.OPENED_SESSION_COOKIE_NAME:
return
- if hasattr(request, 'user') and request.user.is_authenticated():
+ if hasattr(request, 'user') and is_authenticated(request.user):
return
if PASSIVE_TRIED_COOKIE in request.COOKIES:
return
- if app_settings.OPENED_SESSION_COOKIE_NAME in request.COOKIES:
- # get the common domain or guess
- common_domain = app_settings.OPENED_SESSION_COOKIE_DOMAIN
- if not common_domain:
- host = request.get_host()
- # accept automatic common domain selection if domain has at least three components
- # and is not an IP address
- if not host.count('.') > 1 or host.replace('.', '').isdigit():
- return
- common_domain = request.get_host().split('.', 1)[1]
- params = {
- 'next': request.build_absolute_uri(),
- 'passive': '',
- }
- url = reverse('mellon_login') + '?%s' % urlencode(params)
- response = HttpResponseRedirect(url)
- # prevent loops
- response.set_cookie(PASSIVE_TRIED_COOKIE, value='1', max_age=None)
- return response
+ if app_settings.OPENED_SESSION_COOKIE_NAME not in request.COOKIES:
+ return
+ # all is good, try passive login
+ params = {
+ 'next': request.build_absolute_uri(),
+ 'passive': '',
+ }
+ url = reverse('mellon_login') + '?%s' % urlencode(params)
+ response = HttpResponseRedirect(url)
+ # prevent loops
+ response.set_cookie(PASSIVE_TRIED_COOKIE, value='1', max_age=None)
+ return response
diff --git a/tests/test_sso_slo.py b/tests/test_sso_slo.py
index 2e68b37..bffbe22 100644
--- a/tests/test_sso_slo.py
+++ b/tests/test_sso_slo.py
@@ -373,3 +373,31 @@ def test_sso_slo_pass_login_hints_backoffice(db, app, idp, caplog, sp_settings):
login_hints = root.findall('.//{https://www.entrouvert.com/}login-hint')
assert len(login_hints) == 1, 'missing login hint'
assert login_hints[0].text == 'backoffice', 'login hint is not backoffice'
+
+
+def test_middleware_mixin_first_time(db, app, idp, caplog, settings):
+ settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION'
+ assert 'MELLON_PASSIVE_TRIED' not in app.cookies
+ # webtest-lint is against unicode
+ app.set_cookie(str('IDP_SESSION'), str('1'))
+ response = app.get('/', status=302)
+ assert urlparse.urlparse(response.location).path == '/login/'
+ assert (urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True)
+ == {'next': ['http://testserver/'], 'passive': ['']})
+
+ # simulate closing of session at IdP
+ app.cookiejar.clear('testserver.local', '/', 'IDP_SESSION')
+ assert 'IDP_SESSION' not in app.cookies
+
+ # verify MELLON_PASSIVE_TRIED is removed
+ assert 'MELLON_PASSIVE_TRIED' in app.cookies
+ response = app.get('/', status=200)
+ assert 'MELLON_PASSIVE_TRIED' not in app.cookies
+
+ # check passive authentication is tried again
+ app.set_cookie(str('IDP_SESSION'), str('1'))
+ response = app.get('/', status=302)
+ assert urlparse.urlparse(response.location).path == '/login/'
+ assert (urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True)
+ == {'next': ['http://testserver/'], 'passive': ['']})
+ assert 'MELLON_PASSIVE_TRIED' in app.cookies
diff --git a/tests/urls_tests.py b/tests/urls_tests.py
index 52500a4..651f131 100644
--- a/tests/urls_tests.py
+++ b/tests/urls_tests.py
@@ -13,8 +13,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-import django
-
from django.conf.urls import url, include
from django.http import HttpResponse
diff --git a/testsettings.py b/testsettings.py
index 0ccac2b..fc24f09 100644
--- a/testsettings.py
+++ b/testsettings.py
@@ -21,12 +21,14 @@ if hasattr(global_settings, 'MIDDLEWARE_CLASSES'):
MIDDLEWARE_CLASSES += (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'mellon.middleware.PassiveAuthenticationMiddleware',
)
else:
MIDDLEWARE = global_settings.MIDDLEWARE
MIDDLEWARE += (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'mellon.middleware.PassiveAuthenticationMiddleware',
)
AUTHENTICATION_BACKENDS = (