Projet

Général

Profil

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

Valentin Deniaud, 05 juin 2019 14:30

Télécharger (5,13 ko)

Voir les différences:

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
59 59
from django.contrib.auth import load_backend
60 60
from django.shortcuts import render, redirect
61 61
from django.contrib import messages
62
from django.db.models import Min
62 63

  
64
from django_rbac.utils import get_role_model
63 65

  
64 66
import authentic2.views as a2_views
65 67
from authentic2.saml.models import (
......
101 103

  
102 104

  
103 105
User = get_user_model()
106
Role = get_role_model()
104 107

  
105 108
logger = logging.getLogger(__name__)
106 109

  
......
539 542
    return sso_after_process_request(request, login, nid_format=nid_format)
540 543

  
541 544

  
542
def need_login(request, login, nid_format, service):
545
def need_login(request, login, nid_format, service, auth_level=None):
543 546
    """Redirect to the login page with a nonce parameter to verify later that
544 547
       the login form was submitted
545 548
    """
546 549
    nonce = login.request.id or get_nonce()
547 550
    save_key_values(nonce, login.dump(), False, nid_format)
548
    next_url = make_url(continue_sso, params={NONCE_FIELD_NAME: nonce})
551
    params = {
552
        NONCE_FIELD_NAME: nonce
553
    }
554
    next_url = make_url(continue_sso, params=params)
555
    if auth_level:
556
        params['auth_level'] = auth_level
549 557
    logger.debug('redirect to login page with next url %s', next_url)
550
    return login_require(request, next_url=next_url, params={NONCE_FIELD_NAME: nonce},
551
                         service=service)
558
    return login_require(request, next_url=next_url, params=params, service=service)
552 559

  
553 560

  
554 561
def get_url_with_nonce(request, function, nonce):
......
645 652
    did_auth = find_authentication_event(request, nonce) is not None
646 653
    force_authn = login.request.forceAuthn
647 654
    passive = login.request.isPassive
655
    service = LibertyServiceProvider.objects.get(
656
        liberty_provider__entity_id=login.remoteProviderId).liberty_provider
657

  
658
    prefix = 'https://entrouvert.com/authn-class-ref/role-uuid/' # TODO setting
659
    requested_auth_level = 1
660
    if login.request.requestedAuthnContext:
661
        authn_classrefs = login.request.requestedAuthnContext.authnContextClassRef
662
        roles = Role.objects.for_user(request.user, annotate=True)
663
        role_uuids = set()
664
        for classref in authn_classrefs:
665
            if classref.startswith(prefix):
666
                role_uuid = classref[len(prefix):]
667
                if role_uuid == 'staff':
668
                    role_uuids.update(service.roles.values_list('uuid', flat=True))
669
                else:
670
                    role_uuids.add(role_uuid)
671
        requested_roles = roles.filter(uuid__in=role_uuids)
672
        # TODO if empty, fail with error 'user do not have requested roles'
673
        requested_auth_level = \
674
            requested_roles.aggregate(min_lvl=Min('needed_auth_level'))['min_lvl']
648 675

  
649 676
    logger.debug('NameIDFormat is %s', nid_format)
650 677
    logger.debug('nonce is %s', nonce)
651 678

  
652 679
    # check if user is authorized through this service
653
    service = LibertyServiceProvider.objects.get(
654
        liberty_provider__entity_id=login.remoteProviderId).liberty_provider
655

  
656
    if not passive and \
657
            (user.is_anonymous() or (force_authn and not did_auth)):
658
        logger.debug('login required')
659
        return need_login(request, login, nid_format, service)
660

  
661
    # No user is authenticated and passive is True, deny request
662
    if passive and user.is_anonymous():
680
    current_auth_level = request.session.get('auth_level', 1)
681
    if not passive:
682
        if not user.is_anonymous() and requested_auth_level > current_auth_level:
683
            logger.debug('authentication level increase required')
684
            requested_auth_level = current_auth_level + 1  # progressively increase auth level
685
            return need_login(request, login, nid_format, service, requested_auth_level)
686
        if user.is_anonymous() or (force_authn and not did_auth):
687
            logger.debug('login required')
688
            return need_login(request, login, nid_format, service)
689

  
690
    # No user is authenticated or authentication level is too low and passive
691
    # is True, deny request
692
    if passive and (user.is_anonymous() or requested_auth_level > current_auth_level):
663 693
        logger.debug('no user connected and passive request, returning NoPassive')
664 694
        set_saml2_response_responder_status_code(login.response, lasso.SAML2_STATUS_CODE_NO_PASSIVE)
665 695
        return finish_sso(request, login)
666
-