Projet

Général

Profil

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

Benjamin Dauvergne, 15 décembre 2022 17:55

Télécharger (7,06 ko)

Voir les différences:

Subject: [PATCH 2/5] 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

  
......
604 605
    cookie_store = http.cookies.SimpleCookie()
605 606
    cookie_store.load(resp.headers['Set-Cookie'])
606 607
    assert list(cookie_store.keys()) == [cookie_name]
607
    assert 'Secure' in resp.headers['Set-Cookie']
608 608
    assert 'HttpOnly' in resp.headers['Set-Cookie']
609 609
    assert 'SameSite=None' in resp.headers['Set-Cookie']
610 610
    assert 'path=/' in resp.headers['Set-Cookie']
......
615 615
    )
616 616
    assert cookie_name in app.cookies
617 617

  
618
    # if we try again, no passive authentication occurs
619
    resp = app.get('/?parameter=value')
620
    assert resp.status_int != 302
621

  
622
    # if IDP_OPENED_SESSION is modified, then passive authentication is tried again
623
    app.set_cookie('IDP_OPENED_SESSION', '2')
624
    resp = app.get('/?parameter=value')
625
    assert resp.status_int == 302
626

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

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

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

  
618 656

  
619 657
def test_no_opened_session_cookie(pub):
620 658
    app = get_app(pub)
wcs/root.py
287 287
        self.forced_language = False
288 288
        self.feed_substitution_parts()
289 289

  
290
        output = self.try_passive_sso()
291
        if output:
292
            return output
293

  
290 294
        response = get_response()
291 295
        if not hasattr(response, 'filter'):
292 296
            response.filter = {}
......
304 308
        except errors.TraversalError:
305 309
            pass
306 310

  
307
        output = root.RootDirectory()._q_traverse(path)
308
        return self.automatic_sso(output)
309

  
310
    def automatic_sso(self, output):
311
        request = get_request()
312
        response = get_response()
311
        return root.RootDirectory()._q_traverse(path)
313 312

  
313
    def try_passive_sso(self):
314 314
        publisher = get_publisher()
315 315
        OPENED_SESSION_COOKIE = publisher.get_site_option('idp_session_cookie_name')
316 316
        PASSIVE_TRIED_COOKIE = '%s-passive-auth-tried' % publisher.config.session_cookie_name
317
        if OPENED_SESSION_COOKIE not in request.cookies and PASSIVE_TRIED_COOKIE in request.cookies:
317

  
318
        if not OPENED_SESSION_COOKIE:
319
            return
320
        ident_methods = get_cfg('identification', {}).get('methods', [])
321
        idps = get_cfg('idp', {})
322
        if len(idps) != 1:
323
            return
324
        if ident_methods and 'idp' not in ident_methods:
325
            return
326

  
327
        request = get_request()
328
        cookies = request.cookies
329
        response = get_response()
330

  
331
        # expire PASSIVE_TRIED_COOKIE if already logged or if not equal to PASSIVE_TRIED_COOKIE
332
        if PASSIVE_TRIED_COOKIE in cookies and (
333
            request.user or cookies.get(PASSIVE_TRIED_COOKIE) != cookies.get(OPENED_SESSION_COOKIE)
334
        ):
318 335
            response.expire_cookie(PASSIVE_TRIED_COOKIE)
319
            return output
320
        elif OPENED_SESSION_COOKIE in request.cookies and PASSIVE_TRIED_COOKIE not in request.cookies:
321
            ident_methods = get_cfg('identification', {}).get('methods', [])
322
            idps = get_cfg('idp', {})
323
            if request.user:
324
                return output
325
            if len(idps) != 1:
326
                return output
327
            if ident_methods and 'idp' not in ident_methods:
328
                return output
329
            response.set_cookie(
330
                PASSIVE_TRIED_COOKIE,
331
                '1',
332
                secure=1,
333
                httponly=1,
334
                path=publisher.config.session_cookie_path,
335
                domain=publisher.config.session_cookie_domain,
336
            )
337
            url = request.get_url()
338
            query = request.get_query()
339
            if query:
340
                url += '?' + query
341
            return root.tryauth(url)
342
        else:
343
            return output
336

  
337
        if request.user:
338
            if request.session.opened_session_value and request.session.opened_session_value != cookies.get(
339
                OPENED_SESSION_COOKIE
340
            ):
341
                # logout current user if saved value for OPENED_SESSION_COOKIE differs from the current one
342
                get_session_manager().expire_session()
343
            else:
344
                # already logged, stop here.
345
                return
346
        if OPENED_SESSION_COOKIE not in cookies or cookies.get(OPENED_SESSION_COOKIE) == cookies.get(
347
            PASSIVE_TRIED_COOKIE
348
        ):
349
            # no session on the idp or passive sso already tried, stop here.
350
            return
351
        response.set_cookie(
352
            PASSIVE_TRIED_COOKIE,
353
            cookies.get(OPENED_SESSION_COOKIE),
354
            secure=request.scheme == 'https',
355
            httponly=1,
356
            path=publisher.config.session_cookie_path,
357
            domain=publisher.config.session_cookie_domain,
358
        )
359
        url = request.get_url()
360
        query = request.get_query()
361
        if query:
362
            url += '?' + query
363
        return root.tryauth(url)
344 364

  
345 365
    def _q_lookup(self, component):
346 366
        if (
347
-