0002-misc-improve-passive-sso-on-state-change-67090.patch
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-auth-tried' cookie was removed, since we logged in. |
|
648 |
assert cookie_name not in app.cookies |
|
649 | ||
650 |
# if the IDP_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/qommon/saml2.py | ||
---|---|---|
377 | 377 |
user = self.lookup_user(session, login) |
378 | 378 |
if user: |
379 | 379 |
session.set_user(user.id) |
380 |
# save value of idp_session_cookie_name for wcs.root.RootDirectory.try_passive_sso() |
|
381 |
idp_session_cookie_name = get_publisher().get_site_option('idp_session_cookie_name') |
|
382 |
if idp_session_cookie_name: |
|
383 |
if idp_session_cookie_name in get_request().cookies: |
|
384 |
session.opened_session_value = get_request().cookies[idp_session_cookie_name] |
|
380 | 385 |
else: |
381 | 386 |
return error_page('Error associating user on SSO') |
382 | 387 |
session.lasso_identity_provider_id = login.remoteProviderId |
wcs/qommon/sessions.py | ||
---|---|---|
88 | 88 |
forced = False |
89 | 89 |
# should only be overwritten by authentication methods |
90 | 90 |
extra_user_variables = None |
91 |
opened_session_value = None |
|
91 | 92 | |
92 | 93 |
username = None # only set on password authentication |
93 | 94 |
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) |
|
311 |
return root.RootDirectory()._q_traverse(path) |
|
312 | ||
313 |
def try_passive_sso(self): |
|
314 |
publisher = get_publisher() |
|
315 |
idp_session_cookie_name = publisher.get_site_option('idp_session_cookie_name') |
|
316 |
passive_tried_cookie_name = '%s-passive-auth-tried' % publisher.config.session_cookie_name |
|
317 | ||
318 |
if not idp_session_cookie_name: |
|
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 |
|
309 | 326 | |
310 |
def automatic_sso(self, output): |
|
311 | 327 |
request = get_request() |
328 |
cookies = request.cookies |
|
312 | 329 |
response = get_response() |
313 | 330 | |
314 |
publisher = get_publisher() |
|
315 |
OPENED_SESSION_COOKIE = publisher.get_site_option('idp_session_cookie_name') |
|
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: |
|
318 |
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 |
|
331 |
# expire passive_tried_cookie_name if already logged or if not equal to passive_tried_cookie_name |
|
332 |
if passive_tried_cookie_name in cookies and ( |
|
333 |
request.user or cookies.get(passive_tried_cookie_name) != cookies.get(idp_session_cookie_name) |
|
334 |
): |
|
335 |
response.expire_cookie(passive_tried_cookie_name) |
|
336 | ||
337 |
if request.user: |
|
338 |
if request.session.opened_session_value and request.session.opened_session_value != cookies.get( |
|
339 |
idp_session_cookie_name |
|
340 |
): |
|
341 |
# logout current user if saved value for idp_session_cookie_name differs from the current one |
|
342 |
get_session_manager().expire_session() |
|
343 |
get_request()._user = () |
|
344 |
else: |
|
345 |
# already logged, stop here. |
|
346 |
return |
|
347 |
if idp_session_cookie_name not in cookies or cookies.get(idp_session_cookie_name) == cookies.get( |
|
348 |
passive_tried_cookie_name |
|
349 |
): |
|
350 |
# no session on the idp or passive sso already tried, stop here. |
|
351 |
return |
|
352 |
response.set_cookie( |
|
353 |
passive_tried_cookie_name, |
|
354 |
cookies.get(idp_session_cookie_name), |
|
355 |
secure=request.scheme == 'https', |
|
356 |
httponly=1, |
|
357 |
path=publisher.config.session_cookie_path, |
|
358 |
domain=publisher.config.session_cookie_domain, |
|
359 |
) |
|
360 |
url = request.get_url() |
|
361 |
query = request.get_query() |
|
362 |
if query: |
|
363 |
url += '?' + query |
|
364 |
return root.tryauth(url) |
|
344 | 365 | |
345 | 366 |
def _q_lookup(self, component): |
346 | 367 |
if ( |
347 |
- |