Projet

Général

Profil

0002-use-MiddlewareMixin-on-middleware-36509.patch

Benjamin Dauvergne, 29 septembre 2019 16:23

Télécharger (7,78 ko)

Voir les différences:

Subject: [PATCH 2/2] use MiddlewareMixin on middleware (#36509)

Remove OPENED_SESSION_COOKIE_DOMAIN which has no use.
 mellon/app_settings.py |  1 -
 mellon/compat.py       | 27 +++++++++++++++++++++++++++
 mellon/middleware.py   | 37 +++++++++++++++----------------------
 tests/test_sso_slo.py  | 28 ++++++++++++++++++++++++++++
 tests/urls_tests.py    |  2 --
 testsettings.py        |  2 ++
 6 files changed, 72 insertions(+), 25 deletions(-)
 create mode 100644 mellon/compat.py
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
-