Projet

Général

Profil

0001-idp-saml2-use-sp-s-next-url-as-part-of-authn-display.patch

Paul Marillonnet, 16 juin 2022 10:43

Télécharger (6,47 ko)

Voir les différences:

Subject: [PATCH] idp/saml2: use sp's next url as part of authn display
 conditions (#65643)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

    · requires django-mellon's ADD_AUTHNREQUEST_NEXT_URL_EXTENSION
      setting to True on the SP side.
 src/authentic2/idp/saml/saml2_endpoints.py | 10 +++++
 src/authentic2/utils/evaluate.py           |  2 +
 tests/test_idp_saml2.py                    | 43 ++++++++++++++++++++--
 tests/test_utils_evaluate.py               |  5 +++
 4 files changed, 57 insertions(+), 3 deletions(-)
src/authentic2/idp/saml/saml2_endpoints.py
740 740

  
741 741
EO_NS = 'https://www.entrouvert.com/'
742 742
LOGIN_HINT = '{%s}login-hint' % EO_NS
743
NEXT_URL = '{%s}next_url' % EO_NS
743 744

  
744 745

  
745 746
def get_login_hints_extension(profile):
......
755 756
    return hints
756 757

  
757 758

  
759
def get_next_url_extension(profile):
760
    for node in get_extensions(profile):
761
        if node.tag == NEXT_URL:
762
            return node.text
763

  
764

  
758 765
def sso_after_process_request(
759 766
    request,
760 767
    login,
......
790 797

  
791 798
    if not passive and (user.is_anonymous or (force_authn and not did_auth)):
792 799
        logger.debug('login required')
800
        sp_next_url = get_next_url_extension(login)
801
        if sp_next_url:
802
            request.session['sp_next_url'] = sp_next_url
793 803
        return need_login(request, login, nid_format)
794 804

  
795 805
    # No user is authenticated and passive is True, deny request
src/authentic2/utils/evaluate.py
314 314
    if request:
315 315
        ctx['headers'] = HTTPHeaders(request)
316 316
        ctx['remote_addr'] = request.META.get('REMOTE_ADDR')
317
        if hasattr(request, 'session') and 'sp_next_url' in request.session:
318
            ctx['sp_next_url'] = request.session['sp_next_url']
317 319
    ctx.update(kwargs)
318 320
    return ctx
tests/test_idp_saml2.py
36 36
from authentic2.constants import NONCE_FIELD_NAME
37 37
from authentic2.custom_user.models import User
38 38
from authentic2.idp.saml import saml2_endpoints
39
from authentic2.idp.saml.saml2_endpoints import get_extensions, get_login_hints_extension
39
from authentic2.idp.saml.saml2_endpoints import (
40
    get_extensions,
41
    get_login_hints_extension,
42
    get_next_url_extension,
43
)
40 44
from authentic2.models import Attribute, Service
41 45
from authentic2.saml import models as saml_models
42 46
from authentic2.saml.models import SAMLAttribute
......
237 241
        sp_name_qualifier=None,
238 242
        name_id_policy=True,
239 243
        login_hints=None,
244
        next_url=None,
240 245
    ):
241 246
        server = self.get_server()
242 247
        login = self.login = lasso.Login(server)
......
262 267
            request.nameIdPolicy = None
263 268
        request.extensions = lasso.Samlp2Extensions()
264 269
        # extension with unicode characters !! test dumping in saml2_endpoints and continue_sso
265
        request.extensions.any = (force_str('<extension xmlns="http://example.com/">éé</extension>'),)
270
        extensions = [
271
            force_str('<extension xmlns="http://example.com/">éé</extension>'),
272
        ]
266 273
        if login_hints:
267
            request.extensions.any = (
274
            extensions.append(
268 275
                force_str(
269 276
                    '<login-hint xmlns="https://www.entrouvert.com/">%s</login-hint>' % ' '.join(login_hints)
270 277
                ),
271 278
            )
279
        if next_url:
280
            extensions.append(
281
                force_str('<eo:next_url xmlns:eo="https://www.entrouvert.com/">%s</eo:next_url>' % next_url),
282
            )
283
        request.extensions.any = tuple(ext for ext in extensions)
272 284
        login.buildAuthnRequestMsg()
273 285
        url_parsed = urllib.parse.urlparse(login.msgUrl)
274 286
        assert url_parsed.path == reverse('a2-idp-saml-sso'), 'msgUrl should target the sso endpoint'
......
632 644
    scenario.check_assertion(user=user)
633 645

  
634 646

  
647
def test_sso_redirect_artifact_next_url(app, user, keys):
648
    scenario = Scenario(
649
        app,
650
        sp_kwargs=dict(binding='artifact', keys=keys),
651
        make_authn_request_kwargs={'next_url': '/foobar/'},
652
    )
653
    scenario.launch_authn_request()
654
    assert app.session['sp_next_url'] == '/foobar/'
655
    scenario.login(user)
656
    scenario.handle_artifact_response()
657
    scenario.check_assertion(user=user)
658

  
659

  
635 660
@pytest.fixture
636 661
def add_attributes(rf):
637 662
    with mock.patch('authentic2.idp.saml.saml2_endpoints.get_attribute_definitions') as get_definitions:
......
760 785
    assert login_hints == {'backoffice', 'saint-machin-truc', 'toto@example.com'}
761 786

  
762 787

  
788
def test_get_next_url_extension(profile):
789
    assert get_next_url_extension(profile) is None
790
    extensions = [
791
        '<eo:next_url xmlns:eo="https://www.entrouvert.com/">/foobar/</eo:next_url>',
792
    ]
793

  
794
    profile.request.extensions = lasso.Samlp2Extensions()
795
    profile.request.extensions.any = tuple(force_str(ext) for ext in extensions)
796
    sp_next_url = get_next_url_extension(profile)
797
    assert sp_next_url == '/foobar/'
798

  
799

  
763 800
def test_make_edu_person_targeted_id(db, settings, rf):
764 801
    user = User.objects.create(username='a')
765 802
    provider = saml_models.LibertyProvider(entity_id='https://sp.com/')
tests/test_utils_evaluate.py
127 127
            )
128 128
            is False
129 129
        )
130

  
131

  
132
def test_sp_next_url():
133
    assert evaluate_condition('"foo" in sp_next_url', ctx={'sp_next_url': '/foobar'}) is True
134
    assert evaluate_condition('"baz" in sp_next_url', ctx={'sp_next_url': '/foobar'}) is False
130
-