Projet

Général

Profil

0001-misc-retry-passive-sso-when-session-expire-and-logou.patch

Benjamin Dauvergne, 06 juillet 2022 19:19

Télécharger (7,07 ko)

Voir les différences:

Subject: [PATCH 1/3] misc: retry passive sso when session expire and logout if
 current session does not match IDP_OPENED_SESSION cookie value (#67090)

 tests/test_saml_auth.py | 40 +++++++++++++++++++-
 wcs/root.py             | 84 +++++++++++++++++++++++++----------------
 2 files changed, 91 insertions(+), 33 deletions(-)
tests/test_saml_auth.py
22 22
from wcs.qommon.misc import get_lasso_server
23 23
from wcs.qommon.saml2 import Saml2Directory, SOAPException
24 24

  
25
from .test_fc_auth import get_session
25 26
from .test_hobo_notify import PROFILE
26 27
from .utilities import clean_temporary_pub, create_temporary_pub, get_app
27 28

  
......
600 601
    cookie_store = http.cookies.SimpleCookie()
601 602
    cookie_store.load(resp.headers['Set-Cookie'])
602 603
    assert list(cookie_store.keys()) == [cookie_name]
603
    assert 'Secure' in resp.headers['Set-Cookie']
604 604
    assert 'HttpOnly' in resp.headers['Set-Cookie']
605 605
    assert 'SameSite=None' in resp.headers['Set-Cookie']
606 606
    assert 'path=/' in resp.headers['Set-Cookie']
......
611 611
    )
612 612
    assert cookie_name in app.cookies
613 613

  
614
    # if we try again, no passive authentication occurs
615
    resp = app.get('/?parameter=value')
616
    assert resp.status_int != 302
617

  
618
    # if IDP_OPENED_SESSION is modified, then passive authentication is tried again
619
    app.set_cookie('IDP_OPENED_SESSION', '2')
620
    resp = app.get('/?parameter=value')
621
    assert resp.status_int == 302
622

  
623
    # simulate a saml login
624
    user = pub.user_class()
625
    user.store()
626
    request = mock.Mock()
627
    request.get_environ.return_value = '1.1.1.1'
628
    with mock.patch('quixote.session.get_request', return_value=request), mock.patch(
629
        'wcs.qommon.saml2', return_value=mock.Mock(cookies={'IDP_OPENED_SESSION': '2'})
630
    ):
631
        session = get_session_manager().session_class(id=None)
632
        session.set_user(user.id)
633
    session.opened_session_value = '2'
634
    session.id = 'abcd'
635
    session.store()
636
    app.set_cookie(pub.config.session_cookie_name, session.id)
637
    assert get_session(app).opened_session_value == '2'
638

  
639
    resp = app.get('/?parameter=value')
640
    assert resp.status_int == 200
641
    assert get_session(app).opened_session_value == '2'
642
    assert get_session(app).user == user.id
643
    # PASSIVE_TRIED_COOKIE was removed, since we logged in.
644
    assert cookie_name not in app.cookies
645

  
646
    # if OPENED_SESSION_COOKIE change then we are logged out
647
    app.set_cookie('IDP_OPENED_SESSION', '3')
648
    resp = app.get('/?parameter=value')
649
    assert not get_session(app)
650
    assert not get_session_manager().session_class.get(session.id, ignore_errors=True)
651

  
614 652

  
615 653
def test_no_opened_session_cookie(pub):
616 654
    app = get_app(pub)
wcs/root.py
353 353
    def _q_traverse(self, path):
354 354
        self.feed_substitution_parts()
355 355

  
356
        output = self.try_passive_sso()
357
        if output:
358
            return output
359

  
356 360
        response = get_response()
357 361
        if not hasattr(response, 'filter'):
358 362
            response.filter = {}
......
370 374
        except errors.TraversalError:
371 375
            pass
372 376

  
373
        output = root.RootDirectory()._q_traverse(path)
374
        return self.automatic_sso(output)
375

  
376
    def automatic_sso(self, output):
377
        request = get_request()
378
        response = get_response()
377
        return root.RootDirectory()._q_traverse(path)
379 378

  
379
    def try_passive_sso(self):
380 380
        publisher = get_publisher()
381 381
        OPENED_SESSION_COOKIE = publisher.get_site_option('idp_session_cookie_name')
382 382
        PASSIVE_TRIED_COOKIE = '%s-passive-auth-tried' % publisher.config.session_cookie_name
383
        if OPENED_SESSION_COOKIE not in request.cookies and PASSIVE_TRIED_COOKIE in request.cookies:
383

  
384
        if not OPENED_SESSION_COOKIE:
385
            return
386
        ident_methods = get_cfg('identification', {}).get('methods', [])
387
        idps = get_cfg('idp', {})
388
        if len(idps) != 1:
389
            return
390
        if ident_methods and 'idp' not in ident_methods:
391
            return
392

  
393
        request = get_request()
394
        cookies = request.cookies
395
        response = get_response()
396

  
397
        # expire PASSIVE_TRIED_COOKIE if already logged or if not equal to PASSIVE_TRIED_COOKIE
398
        if PASSIVE_TRIED_COOKIE in cookies and (
399
            request.user or cookies.get(PASSIVE_TRIED_COOKIE) != cookies.get(OPENED_SESSION_COOKIE)
400
        ):
384 401
            response.expire_cookie(PASSIVE_TRIED_COOKIE)
385
            return output
386
        elif OPENED_SESSION_COOKIE in request.cookies and PASSIVE_TRIED_COOKIE not in request.cookies:
387
            ident_methods = get_cfg('identification', {}).get('methods', [])
388
            idps = get_cfg('idp', {})
389
            if request.user:
390
                return output
391
            if len(idps) != 1:
392
                return output
393
            if ident_methods and 'idp' not in ident_methods:
394
                return output
395
            response.set_cookie(
396
                PASSIVE_TRIED_COOKIE,
397
                '1',
398
                secure=1,
399
                httponly=1,
400
                path=publisher.config.session_cookie_path,
401
                domain=publisher.config.session_cookie_domain,
402
            )
403
            url = request.get_url()
404
            query = request.get_query()
405
            if query:
406
                url += '?' + query
407
            return root.tryauth(url)
408
        else:
409
            return output
402

  
403
        if request.user:
404
            if request.session.opened_session_value and request.session.opened_session_value != cookies.get(
405
                OPENED_SESSION_COOKIE
406
            ):
407
                # logout current user if saved value for OPENED_SESSION_COOKIE differs from the current one
408
                get_session_manager().expire_session()
409
            else:
410
                # already logged, stop here.
411
                return
412
        if OPENED_SESSION_COOKIE not in cookies or cookies.get(OPENED_SESSION_COOKIE) == cookies.get(
413
            PASSIVE_TRIED_COOKIE
414
        ):
415
            # no session on the idp or passive sso already tried, stop here.
416
            return
417
        response.set_cookie(
418
            PASSIVE_TRIED_COOKIE,
419
            cookies.get(OPENED_SESSION_COOKIE),
420
            secure=request.scheme == 'https',
421
            httponly=1,
422
            path=publisher.config.session_cookie_path,
423
            domain=publisher.config.session_cookie_domain,
424
        )
425
        url = request.get_url()
426
        query = request.get_query()
427
        if query:
428
            url += '?' + query
429
        return root.tryauth(url)
410 430

  
411 431
    def _q_lookup(self, component):
412 432
        # is this a category ?
413
-