0003-idp_saml-handle-authentication-level-increase-reques.patch
| 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)
|
||