From 980cbf4bd029174e8c1ba646230be8d93ba7593a Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Tue, 16 Apr 2019 10:14:23 +0200 Subject: [PATCH 5/5] idp_saml: handle authentication level increase request --- src/authentic2/idp/saml/saml2_endpoints.py | 29 +++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/authentic2/idp/saml/saml2_endpoints.py b/src/authentic2/idp/saml/saml2_endpoints.py index 8d947c1d..5b830bd7 100644 --- a/src/authentic2/idp/saml/saml2_endpoints.py +++ b/src/authentic2/idp/saml/saml2_endpoints.py @@ -350,6 +350,8 @@ def build_assertion(request, login, nid_format='transient'): lasso.SAML2_AUTHN_CONTEXT_PASSWORD_PROTECTED_TRANSPORT elif how == 'ssl': authn_context = lasso.SAML2_AUTHN_CONTEXT_X509 + elif event.get('auth_level'): + authn_context = app_settings.AUTHN_CLASSREF_LEVELS + event['auth_level'] else: raise NotImplementedError('Unknown authentication method %s', how) @@ -523,16 +525,20 @@ def sso(request): return sso_after_process_request(request, login, nid_format=nid_format) -def need_login(request, login, nid_format, service): +def need_login(request, login, nid_format, service, auth_level=None): """Redirect to the login page with a nonce parameter to verify later that the login form was submitted """ nonce = login.request.id or get_nonce() save_key_values(nonce, login.dump(), False, nid_format) - next_url = make_url(continue_sso, params={NONCE_FIELD_NAME: nonce}) + params = { + NONCE_FIELD_NAME: nonce + } + next_url = make_url(continue_sso, params=params) + if auth_level: + params['auth_level'] = auth_level logger.debug('redirect to login page with next url %s', next_url) - return login_require(request, next_url=next_url, params={NONCE_FIELD_NAME: nonce}, - service=service) + return login_require(request, next_url=next_url, params=params, service=service) def get_url_with_nonce(request, function, nonce): @@ -634,6 +640,11 @@ def sso_after_process_request(request, login, consent_obtained=False, did_auth = find_authentication_event(request, nonce) is not None force_authn = login.request.forceAuthn passive = login.request.isPassive + requested_auth_level = False + if login.request.requestedAuthnContext: + authn_classref = login.request.requestedAuthnContext.authnContextClassRef + if authn_classref and authn_classref[0].startswith(app_settings.AUTHN_CLASSREF_LEVELS): + requested_auth_level = int(authn_classref[0].split('/')[-1]) logger.debug('NameIDFormat is %s', nid_format) logger.debug('nonce is %s', nonce) @@ -642,13 +653,19 @@ def sso_after_process_request(request, login, consent_obtained=False, service = LibertyServiceProvider.objects.get( liberty_provider__entity_id=login.remoteProviderId).liberty_provider + current_auth_level = request.session.get('auth_level', 1) + if not user.is_anonymous() and requested_auth_level > current_auth_level: + requested_auth_level = current_auth_level + 1 # progressively increase auth level + return need_login(request, login, nid_format, service, requested_auth_level) + if not passive and \ (user.is_anonymous() or (force_authn and not did_auth)): logger.debug('login required') return need_login(request, login, nid_format, service) - # No user is authenticated and passive is True, deny request - if passive and user.is_anonymous(): + # No user is authenticated or authentication level is too low and passive + # is True, deny request + if passive and (user.is_anonymous() or requested_auth_level > current_auth_level): logger.debug('no user connected and passive request, returning NoPassive') set_saml2_response_responder_status_code(login.response, lasso.SAML2_STATUS_CODE_NO_PASSIVE) -- 2.20.1