Esempio n. 1
0
    def test_response_authn_error_1(self):
        """
        Test SP asking for PASSWORD, authn at AL2 (non-existant).
        Expect AL2.
        """
        req_authn_ctx = requested_authn_context(PASSWORD)
        actual_authn = {'class_ref': EDUID_INTERNAL_2_NAME, 'authn_auth': 'me'}
        _response_contexts = {
            EDUID_INTERNAL_1_NAME: TEST_AL1,
            EDUID_INTERNAL_2_NAME: TEST_AL2,
            PASSWORD: {
                EDUID_INTERNAL_1_NAME: TEST_AL1,
            },
        }

        auth_levels = [
            EDUID_INTERNAL_1_NAME, EDUID_INTERNAL_2_NAME, EDUID_INTERNAL_3_NAME
        ]
        response_ctx = eduid_idp.assurance.response_authn(
            req_authn_ctx,
            actual_authn,
            auth_levels,
            self.logger,
            response_contexts=_response_contexts)
        self.assertEqual(TEST_AL2, response_ctx['class_ref'])
Esempio n. 2
0
def canonical_req_authn_context(req_authn_ctx,
                                logger,
                                contexts=_context_to_internal):
    """
    Return internal representation (canonical form) of RequestedAuthnContext requested by SP.

    :param req_authn_ctx: RequestedAuthnContext from SAML request
    :param logger: logging logger
    :param contexts: context class_ref lookup table (dict)
    :return: Canonical RequestedAuthnContext

    :type req_authn_ctx: saml2.samlp.RequestedAuthnContext
    :type logger: logging.Logger
    :type contexts: dict
    :rtype: saml2.samlp.RequestedAuthnContext
    """
    try:
        class_ref = req_authn_ctx.authn_context_class_ref[0].text
    except AttributeError:
        class_ref = None

    new_ctx = _canonical_ctx(class_ref, contexts, logger)
    if new_ctx is None:
        return None

    # turn AuthnContext() into RequestedAuthnContext()
    new_class_ref = new_ctx.authn_context_class_ref.text
    new_req_authn_ctx = requested_authn_context(new_class_ref)
    logger.debug("Translated AuthnContext {!r} to {!r} ({!r})".format(
        class_ref, new_class_ref, new_req_authn_ctx))
    return new_req_authn_ctx
Esempio n. 3
0
    def _get_requested_authn_context(self, ticket):
        """
        Check if this SP has explicit Authn preferences in the metadata (some SPs are not
        capable of conveying this preference in the RequestedAuthnContext)

        :param ticket: State for this request
        :return: Requested Authn Context

        :type ticket: SSOLoginData
        :rtype: RequestedAuthnContext
        """
        req_authn_context = ticket.req_info.message.requested_authn_context
        try:
            attributes = self.IDP.metadata.entity_attributes(
                ticket.req_info.message.issuer.text)
        except KeyError:
            attributes = {}
        if 'http://www.swamid.se/assurance-requirement' in attributes:
            # XXX don't just pick the first one from the list - choose the most applicable one somehow.
            new_authn = attributes[
                'http://www.swamid.se/assurance-requirement'][0]
            requested = None
            if req_authn_context and req_authn_context.authn_context_class_ref:
                requested = req_authn_context.authn_context_class_ref[0].text
            self.logger.debug(
                "Entity {!r} has AuthnCtx preferences in metadata. Overriding {!r} -> {!r}"
                .format(ticket.req_info.message.issuer.text, requested,
                        new_authn))
            req_authn_context = requested_authn_context(new_authn)
        return req_authn_context
Esempio n. 4
0
def sp_initiated(idp_name):

    app.logger.debug('Space key: {}'.format(""))
    app.logger.debug('idp_name key: {}'.format(idp_name))
    saml_client = saml_client_for(idp_name)

    reqCtx = requested_authn_context([config.get('authn_request_level')],
                                     comparison='exact')

    srvs = saml_client.metadata.single_sign_on_service(
        config.get('test_idp_url'), BINDING_HTTP_POST)

    reqid, req = saml_client.create_authn_request_spid(
        srvs[0]["location"],
        requested_authn_context=reqCtx,
        issuer=Issuer(
            name_qualifier=saml_client.config.getattr('organization').get(
                'url'),
            format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity",
            text=config.get('hostname')),
        sign=False,
        allow_create=None,
        nsprefix={
            "saml": saml.NAMESPACE,
            "samlp": samlp.NAMESPACE
        })

    app.logger.debug('reqid: {}'.format(reqid))
    app.logger.debug('req: {}'.format(req))
    redirect_url = None

    # Select the IdP URL to send the AuthN request to

    signer = saml_client.sec.sec_backend.get_signer(SIG_RSA_SHA256)
    # signer = saml_client.sec.sec_backend.get_signer(SIG_RSA_SHA1)

    info = http_redirect_message(req,
                                 srvs[0]["location"],
                                 relay_state="user",
                                 typ="SAMLRequest",
                                 sigalg=SIG_RSA_SHA256,
                                 signer=signer)

    # info = http_redirect_message(
    #     req, srvs[0]["location"], relay_state="RS",
    #     typ="SAMLRequest")

    app.logger.debug('info: {}'.format(info))
    for key, value in info['headers']:
        if key is 'Location':
            redirect_url = value

    response = redirect(redirect_url, code=302)
    response.headers['Cache-Control'] = 'no-cache, no-store'
    response.headers['Pragma'] = 'no-cache'
    return response
def test_authn_1():
    ac = authn_context_class_ref(PASSWORDPROTECTEDTRANSPORT)
    rac = requested_authn_context(PASSWORDPROTECTEDTRANSPORT)
    authn = AuthnBroker()
    target = "https://example.org/login"
    authn.add(ac, target, 1, "http://www.example.com")

    result = authn.pick(rac)
    assert len(result) == 1
    method, reference = result[0]
    assert target == method
Esempio n. 6
0
def test_authn_1():
    ac = authn_context_class_ref(PASSWORDPROTECTEDTRANSPORT)
    rac = requested_authn_context(PASSWORDPROTECTEDTRANSPORT)
    authn = AuthnBroker()
    target = "https://example.org/login"
    authn.add(ac, target, 1, "http://www.example.com")

    result = authn.pick(rac)
    assert len(result) == 1
    method, reference = result[0]
    assert target == method
Esempio n. 7
0
    def test_canonical_req_authn_context(self):
        """
        Test straight forward translations for each AL level.
        """
        req_authn_ctx = requested_authn_context(PASSWORD)
        canon_ctx = eduid_idp.assurance.canonical_req_authn_context(
            req_authn_ctx, self.logger, self._context_to_internal)
        self.assertEqual(canon_ctx.authn_context_class_ref[0].text,
                         EDUID_INTERNAL_1_NAME)

        req_authn_ctx = requested_authn_context(MOBILETWOFACTORCONTRACT)
        canon_ctx = eduid_idp.assurance.canonical_req_authn_context(
            req_authn_ctx, self.logger, self._context_to_internal)
        self.assertEqual(canon_ctx.authn_context_class_ref[0].text,
                         EDUID_INTERNAL_2_NAME)

        req_authn_ctx = requested_authn_context(TEST_AL3)
        canon_ctx = eduid_idp.assurance.canonical_req_authn_context(
            req_authn_ctx, self.logger, self._context_to_internal)
        self.assertEqual(canon_ctx.authn_context_class_ref[0].text,
                         EDUID_INTERNAL_3_NAME)
Esempio n. 8
0
 def test_response_authn_AL2_2(self):
     """
     Test SP asking for MOBILE (AL2), authn at AL3.
     Expect AuthnContext MOBILE in response.
     """
     req_authn_ctx = requested_authn_context(MOBILETWOFACTORCONTRACT)
     actual_authn = {'class_ref': EDUID_INTERNAL_3_NAME, 'authn_auth': 'me'}
     auth_levels = [EDUID_INTERNAL_2_NAME, EDUID_INTERNAL_3_NAME]
     response_ctx = eduid_idp.assurance.response_authn(
         req_authn_ctx,
         actual_authn,
         auth_levels,
         self.logger,
         response_contexts=self._response_translation)
     self.assertEqual(response_ctx['class_ref'], MOBILETWOFACTORCONTRACT)
Esempio n. 9
0
 def test_response_authn_AL2_3(self):
     """
     Test SP asking for MOBILE (AL2), authn at AL1.
     Expect AuthnContext AL1 default class_ref in response.
     """
     req_authn_ctx = requested_authn_context(MOBILETWOFACTORCONTRACT)
     actual_authn = {'class_ref': EDUID_INTERNAL_1_NAME, 'authn_auth': 'me'}
     auth_levels = [EDUID_INTERNAL_2_NAME, EDUID_INTERNAL_3_NAME]
     with self.assertRaises(eduid_idp.error.Forbidden):
         eduid_idp.assurance.response_authn(
             req_authn_ctx,
             actual_authn,
             auth_levels,
             self.logger,
             response_contexts=self._response_translation)
Esempio n. 10
0
 def test_response_authn_AL3_1(self):
     """
     Test SP asking for AL3, authn at AL3.
     Expect AuthnContext AL3 in response.
     """
     req_authn_ctx = requested_authn_context(TEST_AL3)
     actual_authn = {'class_ref': EDUID_INTERNAL_3_NAME, 'authn_auth': 'me'}
     auth_levels = [EDUID_INTERNAL_3_NAME]
     response_ctx = eduid_idp.assurance.response_authn(
         req_authn_ctx,
         actual_authn,
         auth_levels,
         self.logger,
         response_contexts=self._response_translation)
     self.assertEqual(response_ctx['class_ref'], TEST_AL3)
Esempio n. 11
0
def test_basic():
    sp = Saml2Client(config_file="servera_conf")
    with closing(Server(config_file="idp_all_conf")) as idp:
        srvs = sp.metadata.authn_query_service(idp.config.entityid)

        destination = srvs[0]["location"]
        authn_context = requested_authn_context(INTERNETPROTOCOLPASSWORD)

        subject = Subject(text="abc",
                          name_id=NameID(format=NAMEID_FORMAT_TRANSIENT))

        _id, aq = sp.create_authn_query(subject, destination, authn_context)

        print(aq)

        assert isinstance(aq, AuthnQuery)
Esempio n. 12
0
def test_basic():
    sp = Saml2Client(config_file="servera_conf")
    with closing(Server(config_file="idp_all_conf")) as idp:
        srvs = sp.metadata.authn_query_service(idp.config.entityid)

        destination = srvs[0]["location"]
        authn_context = requested_authn_context(INTERNETPROTOCOLPASSWORD)

        subject = Subject(text="abc",
                          name_id=NameID(format=NAMEID_FORMAT_TRANSIENT))

        _id, aq = sp.create_authn_query(subject, destination, authn_context)

        print(aq)

        assert isinstance(aq, AuthnQuery)
Esempio n. 13
0
    def test_response_authn_error_2(self):
        """
        Test SP asking for AL2, authn at AL2 (non-existant).
        Expect AL2.
        """
        req_authn_ctx = requested_authn_context(EDUID_INTERNAL_2_NAME)
        actual_authn = {'class_ref': EDUID_INTERNAL_2_NAME, 'authn_auth': 'me'}
        _response_contexts = {}

        auth_levels = [EDUID_INTERNAL_1_NAME]
        with self.assertRaises(eduid_idp.error.ServiceError):
            eduid_idp.assurance.response_authn(
                req_authn_ctx,
                actual_authn,
                auth_levels,
                self.logger,
                response_contexts=_response_contexts)
Esempio n. 14
0
    def construct_requested_authn_context(self, entity_id):
        if not self.acr_mapping:
            return None

        acr_entry = util.get_dict_defaults(self.acr_mapping, entity_id)
        if not acr_entry:
            return None

        if type(acr_entry) is not dict:
            acr_entry = {
                "class_ref": acr_entry,
                "comparison": self.VALUE_ACR_COMPARISON_DEFAULT,
            }

        authn_context = requested_authn_context(
            acr_entry['class_ref'], comparison=acr_entry.get(
                'comparison', self.VALUE_ACR_COMPARISON_DEFAULT))

        return authn_context
Esempio n. 15
0
    def test_unknown_canonical_req_authn_context(self):
        """
        Test SP requesting unknown AuthnContext.
        """
        _context_to_internal = self._context_to_internal.copy()

        req_authn_ctx = requested_authn_context(
            'http://www.example.org/homemadeassurancce')
        canon_ctx = eduid_idp.assurance.canonical_req_authn_context(
            req_authn_ctx, self.logger, _context_to_internal)
        self.assertEqual(canon_ctx.authn_context_class_ref[0].text,
                         EDUID_INTERNAL_1_NAME)

        # now, remove the 'undefined' entry
        del _context_to_internal['undefined']

        canon_ctx = eduid_idp.assurance.canonical_req_authn_context(
            req_authn_ctx, self.logger, _context_to_internal)
        self.assertEqual(canon_ctx, None)
def test_authn_3():
    authn = AuthnBroker()
    level = 0
    for ref in [AL1, AL2, AL3, AL4]:
        level += 4
        ac = authn_context_class_ref(ref)

        authn.add(ac, REF2METHOD[ref], level,
                  "https://www.example.com/%s" % "al%d" % level)

    rac = requested_authn_context(AL1, "minimum")

    info = authn.pick(rac)
    assert len(info) == 4
    method, ref = info[0]
    assert REF2METHOD[AL1] == method

    rac = requested_authn_context(AL2, "minimum")

    info = authn.pick(rac)
    assert len(info) == 3
    method, ref = info[0]
    assert REF2METHOD[AL2] == method

    rac = requested_authn_context(AL3, "minimum")

    info = authn.pick(rac)
    assert len(info) == 2
    method, ref = info[0]
    assert REF2METHOD[AL3] == method

    rac = requested_authn_context(AL4, "minimum")

    info = authn.pick(rac)
    assert len(info) == 1
    method, ref = info[0]
    assert REF2METHOD[AL4] == method

    rac = requested_authn_context(AL1, "exact")

    info = authn.pick(rac)
    assert len(info) == 1
    method, ref = info[0]
    assert REF2METHOD[AL1] == method

    rac = requested_authn_context(AL1, "better")

    info = authn.pick(rac)
    assert len(info) == 3
Esempio n. 17
0
def test_authn_3():
    authn = AuthnBroker()
    level = 0
    for ref in [AL1, AL2, AL3, AL4]:
        level += 4
        ac = authn_context_class_ref(ref)

        authn.add(ac, REF2METHOD[ref], level,
                  "https://www.example.com/%s" % "al%d" % level)

    rac = requested_authn_context(AL1, "minimum")

    info = authn.pick(rac)
    assert len(info) == 4
    method, ref = info[0]
    assert REF2METHOD[AL1] == method

    rac = requested_authn_context(AL2, "minimum")

    info = authn.pick(rac)
    assert len(info) == 3
    method, ref = info[0]
    assert REF2METHOD[AL2] == method

    rac = requested_authn_context(AL3, "minimum")

    info = authn.pick(rac)
    assert len(info) == 2
    method, ref = info[0]
    assert REF2METHOD[AL3] == method

    rac = requested_authn_context(AL4, "minimum")

    info = authn.pick(rac)
    assert len(info) == 1
    method, ref = info[0]
    assert REF2METHOD[AL4] == method

    rac = requested_authn_context(AL1, "exact")

    info = authn.pick(rac)
    assert len(info) == 1
    method, ref = info[0]
    assert REF2METHOD[AL1] == method

    rac = requested_authn_context(AL1, "better")

    info = authn.pick(rac)
    assert len(info) == 3
Esempio n. 18
0
def spid_sp_authn_request(conf,
                          selected_idp,
                          binding,
                          name_id_format,
                          authn_context,
                          sig_alg,
                          dig_alg,
                          next_url=''):
    client = Saml2Client(conf)

    logger.debug(f'Redirecting user to the IdP via {binding} binding.')
    # use the html provided by pysaml2 if no template was specified or it didn't exist
    # SPID want the fqdn of the IDP, not the SSO endpoint
    location_fixed = selected_idp
    location = client.sso_location(selected_idp, binding)

    authn_req = saml2.samlp.AuthnRequest()
    authn_req.destination = location_fixed
    # spid-testenv2 preleva l'attribute consumer service dalla authnRequest (anche se questo sta già nei metadati...)
    authn_req.attribute_consuming_service_index = "0"

    # issuer
    issuer = saml2.saml.Issuer()
    issuer.name_qualifier = client.config.entityid
    issuer.text = client.config.entityid
    issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
    authn_req.issuer = issuer

    # message id
    authn_req.id = saml2.s_utils.sid()
    authn_req.version = saml2.VERSION  # "2.0"
    authn_req.issue_instant = saml2.time_util.instant()

    name_id_policy = saml2.samlp.NameIDPolicy()
    # del(name_id_policy.allow_create)
    name_id_policy.format = name_id_format  # settings.SPID_NAMEID_FORMAT
    authn_req.name_id_policy = name_id_policy

    # settings.SPID_AUTH_CONTEXT
    authn_context = requested_authn_context(class_ref=authn_context)
    authn_req.requested_authn_context = authn_context

    # force_auth = true only if SpidL >= 2
    # if 'SpidL1' in authn_context.authn_context_class_ref[0].text:
    # force_authn = 'false'
    # else:
    force_authn = 'true'
    authn_req.force_authn = force_authn
    # end force authn

    # settings.SPID_DEFAULT_BINDING
    authn_req.protocol_binding = binding

    assertion_consumer_service_url = client.config._sp_endpoints[
        'assertion_consumer_service'][0][0]
    authn_req.assertion_consumer_service_url = assertion_consumer_service_url

    authn_req_signed = client.sign(
        authn_req,
        sign_prepare=False,
        sign_alg=sig_alg,
        digest_alg=dig_alg,
    )
    logger.debug(f'AuthRequest to {selected_idp}: {authn_req_signed}')
    relay_state = next_url or reverse('djangosaml2:saml2_echo_attributes')
    http_info = client.apply_binding(binding,
                                     authn_req_signed,
                                     location,
                                     sign=True,
                                     sigalg=sig_alg,
                                     relay_state=relay_state)
    return dict(http_response=http_info,
                authn_request=authn_req_signed,
                relay_state=relay_state,
                session_id=authn_req.id)
Esempio n. 19
0
def spid_sp_authn_request(conf, selected_idp, next_url=""):
    client = Saml2Client(conf)

    logger.debug(f"Redirecting user to the IdP via {SAML2_DEFAULT_BINDING} binding.")

    # use the html provided by pysaml2 if no template was specified or it didn't exist
    # SPID want the fqdn of the IDP, not the SSO endpoint
    location_fixed = selected_idp
    location = client.sso_location(selected_idp, SAML2_DEFAULT_BINDING)

    authn_req = saml2.samlp.AuthnRequest()
    authn_req.destination = location_fixed
    # spid-testenv2 preleva l'attribute consumer service dalla authnRequest (anche se questo sta già nei metadati...)
    authn_req.attribute_consuming_service_index = "0"

    # issuer
    issuer = saml2.saml.Issuer()
    issuer.name_qualifier = client.config.entityid
    issuer.text = client.config.entityid
    issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
    authn_req.issuer = issuer

    # message id
    authn_req.id = saml2.s_utils.sid()
    authn_req.version = saml2.VERSION  # "2.0"
    authn_req.issue_instant = saml2.time_util.instant()

    name_id_policy = saml2.samlp.NameIDPolicy()
    name_id_policy.format = settings.SPID_NAMEID_FORMAT
    authn_req.name_id_policy = name_id_policy

    authn_context = requested_authn_context(class_ref=settings.SPID_AUTH_CONTEXT)
    authn_req.requested_authn_context = authn_context

    # if SPID authentication level is > 1 then forceauthn must be True
    authn_req.force_authn = settings.SPID_ACR_FAUTHN_MAP[settings.SPID_AUTH_CONTEXT]

    authn_req.protocol_binding = SAML2_DEFAULT_BINDING

    assertion_consumer_service_url = client.config._sp_endpoints[
        "assertion_consumer_service"
    ][0][0]
    authn_req.assertion_consumer_service_url = assertion_consumer_service_url

    authn_req_signed = client.sign(
        authn_req,
        sign_prepare=False,
        sign_alg=settings.SPID_SIG_ALG,
        digest_alg=settings.SPID_DIG_ALG,
    )

    logger.debug(f"AuthRequest to {selected_idp}: {authn_req_signed}")

    relay_state = next_url or reverse("djangosaml2:saml2_echo_attributes")
    http_info = client.apply_binding(
        SAML2_DEFAULT_BINDING,
        authn_req_signed,
        location,
        sign=True,
        sigalg=settings.SPID_SIG_ALG,
        relay_state=relay_state,
    )

    return dict(
        http_response=http_info,
        authn_request=authn_req_signed,
        relay_state=relay_state,
        session_id=authn_req.id,
    )
Esempio n. 20
0
def spid_login(request,
          config_loader_path=None,
          wayf_template='djangosaml2/wayf.html',
          authorization_error_template='djangosaml2/auth_error.html'):
    """SAML Authorization Request initiator

    This view initiates the SAML2 Authorization handshake
    using the pysaml2 library to create the AuthnRequest.
    It uses the SAML 2.0 Http POST protocol binding.
    """
    logger.debug('SPID Login process started')

    came_from = request.GET.get('next', settings.LOGIN_REDIRECT_URL)
    if not came_from:
        logger.warning('The next parameter exists but is empty')
        came_from = settings.LOGIN_REDIRECT_URL

    # Ensure the user-originating redirection url is safe.
    if not is_safe_url_compat(url=came_from, allowed_hosts={request.get_host()}):
        came_from = settings.LOGIN_REDIRECT_URL

    # if the user is already authenticated that maybe because of two reasons:
    # A) He has this URL in two browser windows and in the other one he
    #    has already initiated the authenticated session.
    # B) He comes from a view that (incorrectly) send him here because
    #    he does not have enough permissions. That view should have shown
    #    an authorization error in the first place.
    # We can only make one thing here and that is configurable with the
    # SAML_IGNORE_AUTHENTICATED_USERS_ON_LOGIN setting. If that setting
    # is True (default value) we will redirect him to the came_from view.
    # Otherwise, we will show an (configurable) authorization error.
    if callable(request.user.is_authenticated):
        redirect_authenticated_user = getattr(settings,
                                              'SAML_IGNORE_AUTHENTICATED_USERS_ON_LOGIN',
                                              True)
        if redirect_authenticated_user:
            return HttpResponseRedirect(came_from)
        else:
            logger.debug('User is already logged in')
            return render(request, authorization_error_template, {
                    'came_from': came_from})

    # this works only if request came from wayf
    selected_idp = request.GET.get('idp', None)

    conf = get_config(config_loader_path, request)

    # is a embedded wayf needed?
    idps = available_idps(conf)
    if selected_idp is None and len(idps) > 1:
        logger.debug('A discovery process is needed')
        return render(request, wayf_template, {
                'available_idps': idps.items(),
                'came_from': came_from})
    else:
        # otherwise is the first one
        try:
            selected_idp = list(idps.keys())[0]
        except TypeError as e:
            logger.error('Unable to know which IdP to use')
            return HttpResponse(text_type(e))

    # choose a binding to try first
    # sign_requests = getattr(conf, '_sp_authn_requests_signed', False)

    binding = BINDING_HTTP_POST
    logger.debug('Trying binding %s for IDP %s', binding, selected_idp)

    # ensure our selected binding is supported by the IDP
    supported_bindings = get_idp_sso_supported_bindings(selected_idp, config=conf)
    if binding != BINDING_HTTP_POST:
            raise UnsupportedBinding('IDP %s does not support %s or %s',
                                     selected_idp, BINDING_HTTP_POST, BINDING_HTTP_REDIRECT)

    client = Saml2Client(conf)

    logger.debug('Redirecting user to the IdP via %s binding.', binding)
    # use the html provided by pysaml2 if no template was specified or it didn't exist
    # SPID want the fqdn of the IDP, not the SSO endpoint
    location_fixed = selected_idp
    location = client.sso_location(selected_idp, binding)
    # ...hope to see the SSO endpoint soon in spid-testenv2

    authn_req = saml2.samlp.AuthnRequest()
    authn_req.destination = location_fixed
    # spid-testenv2 preleva l'attribute consumer service dalla authnRequest (anche se questo sta già nei metadati...)
    authn_req.attribute_consuming_service_index = "0"

    # import pdb; pdb.set_trace()
    issuer = saml2.saml.Issuer()
    issuer.name_qualifier = client.config.entityid
    issuer.text = client.config.entityid
    issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
    authn_req.issuer = issuer

    # message id
    authn_req.id = saml2.s_utils.sid()
    authn_req.version = saml2.VERSION # "2.0"
    authn_req.issue_instant = saml2.time_util.instant()

    name_id_policy = saml2.samlp.NameIDPolicy()
    # del(name_id_policy.allow_create)
    name_id_policy.format = settings.SPID_NAMEID_FORMAT
    authn_req.name_id_policy  = name_id_policy

    authn_context = requested_authn_context(class_ref=settings.SPID_AUTH_CONTEXT)
    authn_req.requested_authn_context = authn_context

    authn_req.protocol_binding = settings.SPID_DEFAULT_BINDING

    assertion_consumer_service_url = client.config._sp_endpoints['assertion_consumer_service'][0][0]
    authn_req.assertion_consumer_service_url = assertion_consumer_service_url #'http://sp1.testunical.it:8000/saml2/acs/'

    authn_req_signed = client.sign(authn_req, sign_prepare=False,
                                   sign_alg=settings.SPID_ENC_ALG,
                                   digest_alg=settings.SPID_DIG_ALG)
    session_id = authn_req.id

    _req_str = authn_req_signed
    logger.debug('AuthRequest to {}: {}'.format(selected_idp, (_req_str)))
    http_info = client.apply_binding(binding,
                                     _req_str, location,
                                     sign=True,
                                     sigalg=settings.SPID_ENC_ALG)

    # success, so save the session ID and return our response
    logger.debug('Saving the session_id in the OutstandingQueries cache')
    oq_cache = OutstandingQueriesCache(request.session)
    oq_cache.set(session_id, came_from)
    return HttpResponse(http_info['data'])
Esempio n. 21
0
def test_flow():
    sp = Saml2Client(config_file="servera_conf")

    with closing(Server(config_file="idp_all_conf")) as idp:
        relay_state = "FOO"
        # -- dummy request ---
        orig_req = AuthnRequest(
            issuer=sp._issuer(),
            name_id_policy=NameIDPolicy(allow_create="true",
                                        format=NAMEID_FORMAT_TRANSIENT))

        # == Create an AuthnRequest response

        name_id = idp.ident.transient_nameid(sp.config.entityid, "id12")
        binding, destination = idp.pick_binding("assertion_consumer_service",
                                                entity_id=sp.config.entityid)
        resp = idp.create_authn_response({"eduPersonEntitlement": "Short stop",
                                          "surName": "Jeter",
                                          "givenName": "Derek",
                                          "mail": "*****@*****.**",
                                          "title": "The man"},
                                         "id-123456789",
                                         destination,
                                         sp.config.entityid,
                                         name_id=name_id,
                                         authn=AUTHN)

        hinfo = idp.apply_binding(binding, "%s" % resp, destination, relay_state)

        # ------- @SP ----------

        xmlstr = get_msg(hinfo, binding)
        aresp = sp.parse_authn_request_response(xmlstr, binding,
                                                {resp.in_response_to: "/"})

        binding, destination = sp.pick_binding("authn_query_service",
                                               entity_id=idp.config.entityid)

        authn_context = requested_authn_context(INTERNETPROTOCOLPASSWORD)

        subject = aresp.assertion.subject

        aq_id, aq = sp.create_authn_query(subject, destination, authn_context)

        print(aq)

        assert isinstance(aq, AuthnQuery)
        binding = BINDING_SOAP

        hinfo = sp.apply_binding(binding, "%s" % aq, destination, "state2")

        # -------- @IDP ----------

        xmlstr = get_msg(hinfo, binding)

        pm = idp.parse_authn_query(xmlstr, binding)

        msg = pm.message
        assert msg.id == aq.id

        p_res = idp.create_authn_query_response(msg.subject, msg.session_index,
                                                msg.requested_authn_context)

        print(p_res)

        hinfo = idp.apply_binding(binding, "%s" % p_res, "", "state2",
                                  response=True)

        # ------- @SP ----------

        xmlstr = get_msg(hinfo, binding)

        final = sp.parse_authn_query_response(xmlstr, binding)

        print(final)

        assert final.response.id == p_res.id
Esempio n. 22
0
    def authn_request(self, context, entity_id):
        """
        Do an authorization request on idp with given entity id.
        This is the start of the authorization.

        :type context: satosa.context.Context
        :type entity_id: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param entity_id: Target IDP entity id
        :return: response to the user agent
        """
        self.check_blacklist()

        kwargs = {}
        # fetch additional kwargs
        kwargs.update(self.get_kwargs_sign_dig_algs())

        authn_context = self.construct_requested_authn_context(entity_id)
        requested_authn_context = authn_context or requested_authn_context(
            class_ref=self._authn_context)

        # force_auth = true only if SpidL >= 2
        if 'SpidL1' in authn_context.authn_context_class_ref[0].text:
            force_authn = 'false'
        else:
            force_authn = 'true'

        try:
            binding = saml2.BINDING_HTTP_POST
            destination = context.request['entityID']
            # SPID CUSTOMIZATION
            #client = saml2.client.Saml2Client(conf)
            client = self.sp

            satosa_logging(
                logger, logging.DEBUG,
                "binding: %s, destination: %s" % (binding, destination),
                context.state)

            # acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]
            # req_id, req = self.sp.create_authn_request(
            # destination, binding=response_binding, **kwargs)

            logger.debug('Redirecting user to the IdP via %s binding.',
                         binding)
            # use the html provided by pysaml2 if no template was specified or it didn't exist

            # SPID want the fqdn of the IDP as entityID, not the SSO endpoint
            # 'http://idpspid.testunical.it:8088'
            # dovrebbe essere destination ma nel caso di spid-testenv2 è entityid...
            # binding, destination = self.sp.pick_binding("single_sign_on_service", None, "idpsso", entity_id=entity_id)
            # location = client.sso_location(destination, binding)
            location = client.sso_location(entity_id, binding)
            location_fixed = entity_id
            # ...hope to see the SSO endpoint soon in spid-testenv2
            # returns 'http://idpspid.testunical.it:8088/sso'
            # fixed: https://github.com/italia/spid-testenv2/commit/6041b986ec87ab8515dd0d43fed3619ab4eebbe9

            # verificare qui
            # acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]

            authn_req = saml2.samlp.AuthnRequest()
            authn_req.force_authn = force_authn
            authn_req.destination = location
            # spid-testenv2 preleva l'attribute consumer service dalla authnRequest (anche se questo sta già nei metadati...)
            authn_req.attribute_consuming_service_index = "0"

            # import pdb; pdb.set_trace()
            issuer = saml2.saml.Issuer()
            issuer.name_qualifier = client.config.entityid
            issuer.text = client.config.entityid
            issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
            authn_req.issuer = issuer

            # message id
            authn_req.id = saml2.s_utils.sid()
            authn_req.version = saml2.VERSION  # "2.0"
            authn_req.issue_instant = saml2.time_util.instant()

            name_id_policy = saml2.samlp.NameIDPolicy()
            # del(name_id_policy.allow_create)
            name_id_policy.format = NAMEID_FORMAT_TRANSIENT
            authn_req.name_id_policy = name_id_policy

            # TODO: use a parameter instead
            authn_req.requested_authn_context = requested_authn_context
            authn_req.protocol_binding = binding

            assertion_consumer_service_url = client.config._sp_endpoints[
                'assertion_consumer_service'][0][0]
            authn_req.assertion_consumer_service_url = assertion_consumer_service_url  #'http://sp-fqdn/saml2/acs/'

            authn_req_signed = client.sign(authn_req,
                                           sign_prepare=False,
                                           sign_alg=kwargs['sign_alg'],
                                           digest_alg=kwargs['digest_alg'])
            session_id = authn_req.id

            _req_str = authn_req_signed
            logger.debug('AuthRequest to {}: {}'.format(
                destination, (_req_str)))

            relay_state = util.rndstr()
            ht_args = client.apply_binding(binding,
                                           _req_str,
                                           location,
                                           sign=True,
                                           sigalg=kwargs['sign_alg'],
                                           relay_state=relay_state)

            if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
                if authn_req.id in self.outstanding_queries:
                    errmsg = "Request with duplicate id {}".format(req_id)
                    satosa_logging(logger, logging.DEBUG, errmsg,
                                   context.state)
                    raise SATOSAAuthenticationError(context.state, errmsg)
                self.outstanding_queries[authn_req.id] = authn_req_signed

            context.state[self.name] = {"relay_state": relay_state}

            satosa_logging(logger, logging.DEBUG, "ht_args: %s" % ht_args,
                           context.state)
            return make_saml_response(binding, ht_args)

        except Exception as exc:
            satosa_logging(logger,
                           logging.DEBUG,
                           "Failed to construct the AuthnRequest for state",
                           context.state,
                           exc_info=True)
            raise SATOSAAuthenticationError(
                context.state, "Failed to construct the AuthnRequest") from exc
Esempio n. 23
0
def test_flow():
    sp = Saml2Client(config_file="servera_conf")

    with closing(Server(config_file="idp_all_conf")) as idp:
        relay_state = "FOO"
        # -- dummy request ---
        orig_req = AuthnRequest(issuer=sp._issuer(),
                                name_id_policy=NameIDPolicy(
                                    allow_create="true",
                                    format=NAMEID_FORMAT_TRANSIENT))

        # == Create an AuthnRequest response

        name_id = idp.ident.transient_nameid(sp.config.entityid, "id12")
        binding, destination = idp.pick_binding("assertion_consumer_service",
                                                entity_id=sp.config.entityid)
        resp = idp.create_authn_response(
            {
                "eduPersonEntitlement": "Short stop",
                "surName": "Jeter",
                "givenName": "Derek",
                "mail": "*****@*****.**",
                "title": "The man"
            },
            "id-123456789",
            destination,
            sp.config.entityid,
            name_id=name_id,
            authn=AUTHN)

        hinfo = idp.apply_binding(binding, "%s" % resp, destination,
                                  relay_state)

        # ------- @SP ----------

        xmlstr = get_msg(hinfo, binding)
        aresp = sp.parse_authn_request_response(xmlstr, binding,
                                                {resp.in_response_to: "/"})

        binding, destination = sp.pick_binding("authn_query_service",
                                               entity_id=idp.config.entityid)

        authn_context = requested_authn_context(INTERNETPROTOCOLPASSWORD)

        subject = aresp.assertion.subject

        aq_id, aq = sp.create_authn_query(subject, destination, authn_context)

        print(aq)

        assert isinstance(aq, AuthnQuery)
        binding = BINDING_SOAP

        hinfo = sp.apply_binding(binding, "%s" % aq, destination, "state2")

        # -------- @IDP ----------

        xmlstr = get_msg(hinfo, binding)

        pm = idp.parse_authn_query(xmlstr, binding)

        msg = pm.message
        assert msg.id == aq.id

        p_res = idp.create_authn_query_response(msg.subject, msg.session_index,
                                                msg.requested_authn_context)

        print(p_res)

        hinfo = idp.apply_binding(binding,
                                  "%s" % p_res,
                                  "",
                                  "state2",
                                  response=True)

        # ------- @SP ----------

        xmlstr = get_msg(hinfo, binding)

        final = sp.parse_authn_query_response(xmlstr, binding)

        print(final)

        assert final.response.id == p_res.id
Esempio n. 24
0
    def authn_request(self, context, entity_id):
        """
        Do an authorization request on idp with given entity id.
        This is the start of the authorization.

        :type context: satosa.context.Context
        :type entity_id: str
        :rtype: satosa.response.Response

        :param context: The current context
        :param entity_id: Target IDP entity id
        :return: response to the user agent
        """
        self.check_blacklist(context, entity_id)

        kwargs = {}
        # fetch additional kwargs
        kwargs.update(self.get_kwargs_sign_dig_algs())

        authn_context = self.construct_requested_authn_context(entity_id)
        req_authn_context = authn_context or requested_authn_context(
            class_ref=self._authn_context)
        req_authn_context.comparison = self.config.get("spid_acr_comparison",
                                                       "minimum")

        # force_auth = true only if SpidL >= 2
        if "SpidL1" in authn_context.authn_context_class_ref[0].text:
            force_authn = "false"
        else:
            force_authn = "true"

        try:
            binding = saml2.BINDING_HTTP_POST
            destination = context.internal_data.get("target_entity_id",
                                                    entity_id)
            # SPID CUSTOMIZATION
            # client = saml2.client.Saml2Client(conf)
            client = self.sp

            logger.debug(f"binding: {binding}, destination: {destination}")

            # acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]
            # req_id, req = self.sp.create_authn_request(
            # destination, binding=response_binding, **kwargs)

            logger.debug(f"Redirecting user to the IdP via {binding} binding.")
            # use the html provided by pysaml2 if no template was specified or it didn't exist

            # SPID want the fqdn of the IDP as entityID, not the SSO endpoint
            # 'http://idpspid.testunical.it:8088'
            # dovrebbe essere destination ma nel caso di spid-testenv2 è entityid...
            # binding, destination = self.sp.pick_binding("single_sign_on_service", None, "idpsso", entity_id=entity_id)
            location = client.sso_location(destination, binding)
            # location = client.sso_location(entity_id, binding)

            # not used anymore thanks to avviso 11
            # location_fixed = destination  # entity_id
            # ...hope to see the SSO endpoint soon in spid-testenv2
            # returns 'http://idpspid.testunical.it:8088/sso'
            # fixed: https://github.com/italia/spid-testenv2/commit/6041b986ec87ab8515dd0d43fed3619ab4eebbe9

            # verificare qui
            # acs_endp, response_binding = self.sp.config.getattr("endpoints", "sp")["assertion_consumer_service"][0]

            authn_req = saml2.samlp.AuthnRequest()
            authn_req.force_authn = force_authn
            authn_req.destination = location
            # spid-testenv2 preleva l'attribute consumer service dalla authnRequest
            # (anche se questo sta già nei metadati...)
            authn_req.attribute_consuming_service_index = "0"

            issuer = saml2.saml.Issuer()
            issuer.name_qualifier = client.config.entityid
            issuer.text = client.config.entityid
            issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
            authn_req.issuer = issuer

            # message id
            authn_req.id = saml2.s_utils.sid()
            authn_req.version = saml2.VERSION  # "2.0"
            authn_req.issue_instant = saml2.time_util.instant()

            name_id_policy = saml2.samlp.NameIDPolicy()
            # del(name_id_policy.allow_create)
            name_id_policy.format = NAMEID_FORMAT_TRANSIENT
            authn_req.name_id_policy = name_id_policy

            # TODO: use a parameter instead
            authn_req.requested_authn_context = req_authn_context
            authn_req.protocol_binding = binding

            assertion_consumer_service_url = client.config._sp_endpoints[
                "assertion_consumer_service"][0][0]
            authn_req.assertion_consumer_service_url = (
                assertion_consumer_service_url  # 'http://sp-fqdn/saml2/acs/'
            )

            authn_req_signed = client.sign(
                authn_req,
                sign_prepare=False,
                sign_alg=kwargs["sign_alg"],
                digest_alg=kwargs["digest_alg"],
            )
            authn_req.id

            _req_str = authn_req_signed
            logger.debug(f"AuthRequest to {destination}: {_req_str}")

            relay_state = util.rndstr()
            ht_args = client.apply_binding(
                binding,
                _req_str,
                location,
                sign=True,
                sigalg=kwargs["sign_alg"],
                relay_state=relay_state,
            )

            if self.sp.config.getattr("allow_unsolicited", "sp") is False:
                if authn_req.id in self.outstanding_queries:
                    errmsg = "Request with duplicate id {}".format(
                        authn_req.id)
                    logger.debug(errmsg)
                    raise SATOSAAuthenticationError(context.state, errmsg)
                self.outstanding_queries[authn_req.id] = authn_req_signed

            context.state[self.name] = {"relay_state": relay_state}
            # these will give the way to check compliances between the req and resp
            context.state["req_args"] = {"id": authn_req.id}

            logger.info(f"SAMLRequest: {ht_args}")
            return make_saml_response(binding, ht_args)

        except Exception as exc:
            logger.debug("Failed to construct the AuthnRequest for state")
            raise SATOSAAuthenticationError(
                context.state, "Failed to construct the AuthnRequest") from exc