Project

General

Profile

0003-idp_saml-handle-authentication-level-increase-reques.patch

Valentin Deniaud, 05 June 2019 02:30 PM

Download (5.13 KB)

View differences:

Subject: [PATCH 3/3] idp_saml: handle authentication level increase request

Because we don't tell SP which role grants a user superuser status, we
have to let them request a special 'staff' value instead of a role uuid.
 src/authentic2/idp/saml/saml2_endpoints.py | 58 ++++++++++++++++------
 1 file changed, 44 insertions(+), 14 deletions(-)
src/authentic2/idp/saml/saml2_endpoints.py
from django.contrib.auth import load_backend
from django.shortcuts import render, redirect
from django.contrib import messages
from django.db.models import Min
from django_rbac.utils import get_role_model
import authentic2.views as a2_views
from authentic2.saml.models import (
......
User = get_user_model()
Role = get_role_model()
logger = logging.getLogger(__name__)
......
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):
......
did_auth = find_authentication_event(request, nonce) is not None
force_authn = login.request.forceAuthn
passive = login.request.isPassive
service = LibertyServiceProvider.objects.get(
liberty_provider__entity_id=login.remoteProviderId).liberty_provider
prefix = 'https://entrouvert.com/authn-class-ref/role-uuid/' # TODO setting
requested_auth_level = 1
if login.request.requestedAuthnContext:
authn_classrefs = login.request.requestedAuthnContext.authnContextClassRef
roles = Role.objects.for_user(request.user, annotate=True)
role_uuids = set()
for classref in authn_classrefs:
if classref.startswith(prefix):
role_uuid = classref[len(prefix):]
if role_uuid == 'staff':
role_uuids.update(service.roles.values_list('uuid', flat=True))
else:
role_uuids.add(role_uuid)
requested_roles = roles.filter(uuid__in=role_uuids)
# TODO if empty, fail with error 'user do not have requested roles'
requested_auth_level = \
requested_roles.aggregate(min_lvl=Min('needed_auth_level'))['min_lvl']
logger.debug('NameIDFormat is %s', nid_format)
logger.debug('nonce is %s', nonce)
# check if user is authorized through this service
service = LibertyServiceProvider.objects.get(
liberty_provider__entity_id=login.remoteProviderId).liberty_provider
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():
current_auth_level = request.session.get('auth_level', 1)
if not passive:
if not user.is_anonymous() and requested_auth_level > current_auth_level:
logger.debug('authentication level increase required')
requested_auth_level = current_auth_level + 1 # progressively increase auth level
return need_login(request, login, nid_format, service, requested_auth_level)
if 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 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)
return finish_sso(request, login)