Exemplo n.º 1
0
    def setup_class(self):
        server = Server("idp_conf")
        name_id = server.ident.transient_nameid("urn:mace:example.com:saml:roland:sp", "id12")

        self._resp_ = server.create_authn_response(
            IDENTITY,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",
            # consumer_url
            "urn:mace:example.com:saml:roland:sp",
            # sp_entity_id
            name_id=name_id,
        )

        self._sign_resp_ = server.create_authn_response(
            IDENTITY,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=name_id,
            sign_assertion=True,
        )

        self._resp_authn = server.create_authn_response(
            IDENTITY,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=name_id,
            authn=AUTHN,
        )

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.conf = conf
Exemplo n.º 2
0
def test_enc1():
    server = Server("idp_conf")
    name_id = server.ident.transient_nameid(
        "urn:mace:example.com:saml:roland:sp", "id12")

    resp_ = server.create_authn_response(
        IDENTITY, "id12", "http://lingon.catalogix.se:8087/",
        "urn:mace:example.com:saml:roland:sp", name_id=name_id)

    statement = pre_encrypt_assertion(resp_)

    tmpl = "enc_tmpl.xml"
    # tmpl_file = open(tmpl, "w")
    # tmpl_file.write("%s" % pre_encryption_part())
    # tmpl_file.close()

    data = "pre_enc.xml"
    # data_file = open(data, "w")
    # data_file.write("%s" % statement)
    # data_file.close()

    key_type = "des-192"
    com_list = [xmlsec_path, "encrypt", "--pubkey-cert-pem", "pubkey.pem",
                "--session-key", key_type, "--xml-data", data,
                "--node-xpath", ASSERT_XPATH]

    crypto = CryptoBackendXmlSec1(xmlsec_path)
    (_stdout, _stderr, output) = crypto._run_xmlsec(
        com_list, [tmpl], exception=EncryptError, validate_output=False)

    print output
    assert _stderr == ""
    assert _stdout == ""
Exemplo n.º 3
0
    def test_slo_soap(self):
        soon = time_util.in_a_while(days=1)
        sinfo = {
            "name_id": nid,
            "issuer": "urn:mace:example.com:saml:roland:idp",
            "not_on_or_after": soon,
            "user": {
                "givenName": "Leo",
                "surName": "Laport",
            }
        }

        sp = client.Saml2Client(config_file="server_conf")
        sp.users.add_information_about_person(sinfo)

        req_id, logout_request = sp.create_logout_request(
            name_id=nid, destination="http://localhost:8088/slo",
            issuer_entity_id="urn:mace:example.com:saml:roland:idp",
            reason="I'm tired of this")

        #_ = s_utils.deflate_and_base64_encode("%s" % (logout_request,))

        saml_soap = make_soap_enveloped_saml_thingy(logout_request)
        self.server.ident.close()
        idp = Server("idp_soap_conf")
        request = idp.parse_logout_request(saml_soap)
        idp.ident.close()
        assert request
Exemplo n.º 4
0
    def setup_class(self):
        server = Server(dotname("idp_conf"))
        name_id = server.ident.transient_nameid(
                            "urn:mace:example.com:saml:roland:sp","id12")

        self._resp_ = server.create_authn_response(
                            IDENTITY,
                            "id12",                       # in_response_to
                            "http://lingon.catalogix.se:8087/",   # consumer_url
                            "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                            name_id=name_id,
                            authn=AUTHN)
                
        self._sign_resp_ = server.create_authn_response(
                            IDENTITY,
                            "id12",                       # in_response_to
                            "http://lingon.catalogix.se:8087/",   # consumer_url
                            "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                            name_id=name_id, sign_assertion=True,
                            authn=AUTHN)

        self._resp_authn = server.create_authn_response(
                            IDENTITY,
                            "id12",                       # in_response_to
                            "http://lingon.catalogix.se:8087/",   # consumer_url
                            "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                            name_id=name_id,
                            authn=AUTHN)

        self.conf = config_factory("sp", dotname("server_conf"))
        self.conf.only_use_keys_in_metadata = False
        self.ar = authn_response(self.conf, "http://lingon.catalogix.se:8087/")
Exemplo n.º 5
0
    def setup_class(self):
        server = Server("idp_conf")
        name_id = server.ident.transient_nameid(
                                "urn:mace:example.com:saml:roland:sp",
                                "id12")

        self._resp_ = server.do_response(
                    "id12",                       # in_response_to
                    "http://lingon.catalogix.se:8087/",   # consumer_url
                    "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                    {"eduPersonEntitlement":"Jeter"},
                    name_id = name_id
                )
                
        self._sign_resp_ = server.do_response(
                    "id12",                       # in_response_to
                    "http://lingon.catalogix.se:8087/",   # consumer_url
                    "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                    {"eduPersonEntitlement":"Jeter"},
                    name_id = name_id,
                    sign=True
                )

        self._resp_authn = server.do_response(
                    "id12",                       # in_response_to
                    "http://lingon.catalogix.se:8087/",   # consumer_url
                    "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                    {"eduPersonEntitlement":"Jeter"},
                    name_id = name_id,
                    authn=(saml.AUTHN_PASSWORD, "http://www.example.com/login")
                )
        
        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.conf = conf
Exemplo n.º 6
0
def test_create_artifact_resolve():
    b64art = create_artifact(SP, "aabbccddeeffgghhiijj", 1)
    artifact = base64.b64decode(b64art)

    #assert artifact[:2] == '\x00\x04'
    #assert int(artifact[2:4]) == 0
    #
    s = sha1(SP)
    assert artifact[4:24] == s.digest()

    idp = Server(config_file="idp_all_conf")

    typecode = artifact[:2]
    assert typecode == ARTIFACT_TYPECODE

    destination = idp.artifact2destination(b64art, "spsso")

    msg = idp.create_artifact_resolve(b64art, destination, sid())

    print msg

    args = idp.use_soap(msg, destination, None, False)

    sp = Saml2Client(config_file="servera_conf")

    ar = sp.parse_artifact_resolve(args["data"])

    print ar

    assert ar.artifact.text == b64art
Exemplo n.º 7
0
class TestSP():
    def setup_class(self):
        self.sp = make_plugin("rem", saml_conf="server_conf")
        self.server = Server(config_file="idp_conf")

    def teardown_class(self):
        self.server.close()

    def test_setup(self):
        assert self.sp

    def test_identify(self):
        # Create a SAMLResponse
        ava = {"givenName": ["Derek"], "surName": ["Jeter"],
               "mail": ["*****@*****.**"], "title": ["The man"]}

        resp_str = "%s" % self.server.create_authn_response(
            ava, "id1", "http://lingon.catalogix.se:8087/",
            "urn:mace:example.com:saml:roland:sp", trans_name_policy,
            "*****@*****.**", authn=AUTHN)

        resp_str = base64.encodestring(resp_str)
        self.sp.outstanding_queries = {"id1": "http://www.example.com/service"}
        session_info = self.sp._eval_authn_response(
            {}, {"SAMLResponse": [resp_str]})

        assert len(session_info) > 1
        assert session_info["came_from"] == 'http://www.example.com/service'
        assert session_info["ava"] == {'givenName': ['Derek'],
                                       'mail': ['*****@*****.**'],
                                       'sn': ['Jeter'],
                                       'title': ['The man']}
Exemplo n.º 8
0
class TestServer2():
    def setup_class(self):
        self.server = Server("restrictive_idp_conf")

    def teardown_class(self):
        self.server.close()

    def test_do_attribute_reponse(self):
        aa_policy = self.server.config.getattr("policy", "idp")
        print(aa_policy.__dict__)
        response = self.server.create_attribute_response(
            IDENTITY.copy(), "aaa", "http://example.com/sp/",
            "http://www.example.com/roland/sp")

        assert response is not None
        assert response.destination == "http://example.com/sp/"
        assert response.in_response_to == "aaa"
        assert response.version == "2.0"
        assert response.issuer.text == "urn:mace:example.com:saml:roland:idpr"
        assert response.status.status_code.value == samlp.STATUS_SUCCESS
        assert response.assertion
        assertion = response.assertion
        assert assertion.version == "2.0"
        subject = assertion.subject
        #assert subject.name_id.format == saml.NAMEID_FORMAT_TRANSIENT
        assert subject.subject_confirmation
        subject_conf = subject.subject_confirmation[0]
        assert subject_conf.subject_confirmation_data.in_response_to == "aaa"
Exemplo n.º 9
0
    def setup_class(self):
        server = Server("idp_conf")
        name_id = server.ident.transient_nameid(
                            "urn:mace:example.com:saml:roland:sp","id12")
        policy = server.conf.getattr("policy", "idp")
        self._resp_ = server.create_response(
                    "id12",                       # in_response_to
                    "http://lingon.catalogix.se:8087/",   # consumer_url
                    "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                    IDENTITY, name_id = name_id, policy=policy)
                
        self._sign_resp_ = server.create_response(
                    "id12",                       # in_response_to
                    "http://lingon.catalogix.se:8087/",   # consumer_url
                    "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                    IDENTITY,
                    name_id = name_id, sign_assertion=True, policy=policy)

        self._resp_authn = server.create_response(
                    "id12",                       # in_response_to
                    "http://lingon.catalogix.se:8087/",   # consumer_url
                    "urn:mace:example.com:saml:roland:sp", # sp_entity_id
                    IDENTITY,
                    name_id = name_id,
                    authn=(saml.AUTHN_PASSWORD, "http://www.example.com/login"),
                    policy=policy)

        self.conf = config_factory("sp", "server_conf")
        self.conf.only_use_keys_in_metadata = False
        self.ar = authn_response(self.conf, "http://lingon.catalogix.se:8087/")
Exemplo n.º 10
0
 def test_1(self):
     server = Server("idp_slo_redirect_conf")
     request = _logout_request("sp_slo_redirect_conf")
     print request
     bindings = [BINDING_HTTP_REDIRECT]
     (resp, headers, message) = server.logout_response(request, bindings)
     assert resp == '302 Found'
     assert len(headers) == 1
     assert headers[0][0] == "Location"
     assert message == ['']
Exemplo n.º 11
0
def test_reshuffle_response():
    server = Server("idp_conf")
    name_id = server.ident.transient_nameid("urn:mace:example.com:saml:roland:sp", "id12")

    resp_ = server.create_authn_response(
        IDENTITY, "id12", "http://lingon.catalogix.se:8087/", "urn:mace:example.com:saml:roland:sp", name_id=name_id
    )

    resp2 = pre_encrypt_assertion(resp_)

    print resp2
    assert resp2.encrypted_assertion.extension_elements
Exemplo n.º 12
0
def test_enc2():
    crypto = CryptoBackendXmlSec1(xmlsec_path)

    server = Server("idp_conf")
    name_id = server.ident.transient_nameid("urn:mace:example.com:saml:roland:sp", "id12")

    resp_ = server.create_authn_response(
        IDENTITY, "id12", "http://lingon.catalogix.se:8087/", "urn:mace:example.com:saml:roland:sp", name_id=name_id
    )

    enc_resp = crypto.encrypt_assertion(resp_, full_path("pubkey.pem"), pre_encryption_part())

    print enc_resp
    assert enc_resp
Exemplo n.º 13
0
    def test_encrypted_response_9(self):
        _server = Server("idp_conf_sp_no_encrypt")

        _resp = _server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            #encrypted_advice_attributes=True,
            pefim=True,
        )

        self.verify_assertion(_resp.assertion.advice.assertion)

        _resp = _server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=False,
            encrypt_assertion_self_contained=True,
            #encrypted_advice_attributes=True,
            pefim=True
        )

        self.verify_assertion(_resp.assertion.advice.assertion)

        _resp = _server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            encrypted_advice_attributes=False,
        )

        self.verify_assertion([_resp.assertion])
Exemplo n.º 14
0
class TestSP():
    def setup_class(self):
        self.sp = make_plugin("rem", saml_conf="server_conf")
        self.server = Server(config_file="idp_conf")

    def test_setup(self):
        assert self.sp
        
    def test_identify(self):

        # Create a SAMLResponse
        ava = { "givenName": ["Derek"], "surname": ["Jeter"], 
                "mail": ["*****@*****.**"]}

        resp_str = "\n".join(self.server.authn_response(ava, 
                    "id1", "http://lingon.catalogix.se:8087/", 
                    "urn:mace:example.com:saml:roland:sp",
                    samlp.NameIDPolicy(format=saml.NAMEID_FORMAT_TRANSIENT,
                                        allow_create="true"),
                    "*****@*****.**"))

        resp_str = base64.encodestring(resp_str)
        self.sp.outstanding_queries = {"id1":"http://www.example.com/service"}
        session_info = self.sp._eval_authn_response({},{"SAMLResponse":resp_str})
        
        assert len(session_info) > 1
        assert session_info["came_from"] == 'http://www.example.com/service'
        assert session_info["ava"] == {'givenName': ['Derek'], 
                                        'mail': ['*****@*****.**'], 
                                        'sn': ['Jeter']}
Exemplo n.º 15
0
def create_authn_response(session_id, identity=dict(), sign=True):
    config = IdPConfig()
    config.load(idp_config)
    idp_server = Server(config=config)
    idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident'))
    authn_response = str(idp_server.authn_response(
        identity=identity,
        in_response_to=session_id,
        destination='https://foo.example.com/sp/acs',
        sp_entity_id='https://foo.example.com/sp/metadata',
        name_id_policy=None,
        userid='Irrelevent',
        sign=sign,
        instance=True))
    response = samlp.response_from_string(authn_response)
    return response.assertion[0].subject.name_id.text, authn_response
Exemplo n.º 16
0
    def test_1(self):
        server = Server("idp_slo_redirect_conf")
        req_id, request = _logout_request("sp_slo_redirect_conf")
        print(request)
        bindings = [BINDING_HTTP_REDIRECT]
        response = server.create_logout_response(request, bindings)
        binding, destination = server.pick_binding("single_logout_service",
                                                   bindings, "spsso",
                                                   request)

        http_args = server.apply_binding(binding, "%s" % response, destination,
                                         "relay_state", response=True)

        assert len(http_args) == 4
        assert http_args["headers"][0][0] == "Location"
        assert http_args["data"] == []
Exemplo n.º 17
0
    def test_authn_response_0(self):
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = client.Saml2Client(conf)

        ava = {"givenName": ["Derek"], "surName": ["Jeter"],
               "mail": ["*****@*****.**"], "title": "The man"}

        npolicy = samlp.NameIDPolicy(format=saml.NAMEID_FORMAT_TRANSIENT,
                                     allow_create="true")
        resp_str = "%s" % self.server.create_authn_response(
            ava, "id1", "http://*****:*****@example.com", authn=AUTHN)

        response = samlp.response_from_string(resp_str)
        print((response.keyswv()))
        assert _eq(response.keyswv(), ['status', 'destination', 'assertion',
                                       'in_response_to', 'issue_instant',
                                       'version', 'issuer', 'id'])
        print((response.assertion[0].keyswv()))
        assert len(response.assertion) == 1
        assert _eq(response.assertion[0].keyswv(), ['attribute_statement',
                                                    'issue_instant', 'version',
                                                    'subject', 'conditions',
                                                    'id', 'issuer',
                                                    'authn_statement'])
        assertion = response.assertion[0]
        assert len(assertion.attribute_statement) == 1
        astate = assertion.attribute_statement[0]
        print(astate)
        assert len(astate.attribute) == 4
Exemplo n.º 18
0
class SamlServer(object):
    """
    SAML Wrapper around pysaml2.

    Implements SAML2 Identity Provider functionality for Flask.
    """
    def __init__(self, config, attribute_map=None):
        """Initialize SAML Identity Provider.

        Args:
            config (dict): Identity Provider config info in dict form
            attribute_map (dict): Mapping of attribute keys to user data
        """
        self._config = IdPConfig()
        self._config.load(config)
        self._server = Server(config=self._config)
        self.attribute_map = {}
        if attribute_map is not None:
            self.attribute_map = attribute_map

    def handle_authn_request(self, request, login_form_cb):
        """Handles authentication request.

        TODO: create default login_form_cb, with unstyled login form?

        Args:
            request (Request): Flask request object for this HTTP transaction.
            login_form_cb (function): Function that displays login form with 
                username and password fields. Takes a single parameter which
                is the service_provider_id so the form may be styled accordingly.
        """
        if 'SAMLRequest' in request.values:
            details = self._server.parse_authn_request(request.details,
                BINDING_HTTP_REDIRECT)
            # TODO: check session for already authenticated user
            # and send authn_response immediately.
            # TODO: otherwise render login form login_form_cb(service_provider_id)
        else:
            pass # TODO: bad request?

    def get_service_provider_id(self, request):
        # TODO: pull service_provider_id from session
        pass

    def authn_response(self, userid):
        service_provider_id = get_service_provider_id()
        # TODO: send authn_response
        pass

    def get_metadata(self):
        """Returns SAML Identity Provider Metadata"""
        edesc = entity_descriptor(self._config, 24)
        if self._config.key_file:
            edesc = sign_entity_descriptor(edesc, 24, None, security_context(self._config))
        response = make_response(str(edesc))
        response.headers['Content-type'] = 'text/xml; charset=utf-8'
        return response
Exemplo n.º 19
0
    def setup_class(self):
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = client.Saml2Client(conf)
        self.name_id = self.server.ident.transient_nameid(
            "urn:mace:example.com:saml:roland:sp", "id12")
        self.ava = {"givenName": ["Derek"], "sn": ["Jeter"],
               "mail": ["*****@*****.**"], "title": "The man"}
Exemplo n.º 20
0
def test_request_response():
    sp = Saml2Client(config_file="servera_conf")
    idp = Server(config_file="idp_all_conf")

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

    policy = NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT,
                          sp_name_qualifier="urn:mace:swamid:junk",
                          allow_create="true")

    nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar")

    nmr = sp.create_name_id_mapping_request(policy, nameid, destination)

    print nmr

    args = sp.use_soap(nmr, destination)

    # ------- IDP ------------

    req = idp.parse_name_id_mapping_request(args["data"], binding)

    in_response_to = req.message.id
    name_id = NameID(format=NAMEID_FORMAT_PERSISTENT, text="foobar")

    idp_response = idp.create_name_id_mapping_response(name_id,
                                                       in_response_to=in_response_to)

    print idp_response

    ht_args = sp.use_soap(idp_response)

    # ------- SP ------------

    _resp = sp.parse_name_id_mapping_request_response(ht_args["data"], binding)

    print _resp.response

    r_name_id = _resp.response.name_id

    assert r_name_id.format == NAMEID_FORMAT_PERSISTENT
    assert r_name_id.text == "foobar"
Exemplo n.º 21
0
def test_flow():
    sp = Saml2Client(config_file="servera_conf")
    idp1 = Server(config_file="idp_conf_mdb")
    idp2 = Server(config_file="idp_conf_mdb")

    # clean out database
    idp1.ident.mdb.db.drop()

    # -- dummy request ---
    req_id, orig_req = sp.create_authn_request(idp1.config.entityid)

    # == Create an AuthnRequest response

    rinfo = idp1.response_args(orig_req, [BINDING_HTTP_POST])

    # name_id = idp1.ident.transient_nameid("id12", rinfo["sp_entity_id"])
    resp = idp1.create_authn_response(
        {
            "eduPersonEntitlement": "Short stop",
            "surName": "Jeter",
            "givenName": "Derek",
            "mail": "*****@*****.**",
            "title": "The man",
        },
        userid="jeter",
        authn=AUTHN,
        **rinfo
    )

    # What's stored away is the assertion
    a_info = idp2.session_db.get_assertion(resp.assertion.id)
    # Make sure what I got back from MongoDB is the same as I put in
    assert a_info["assertion"] == resp.assertion

    # By subject
    nid = resp.assertion.subject.name_id
    _assertion = idp2.session_db.get_assertions_by_subject(nid)
    assert len(_assertion) == 1
    assert _assertion[0] == resp.assertion

    nids = idp2.ident.find_nameid("jeter")
    assert len(nids) == 1
Exemplo n.º 22
0
def test_flow():
    sp = Saml2Client(config_file="servera_conf")
    idp = Server(config_file="idp_all_conf")

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

    nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar")
    newid = NewID(text="Barfoo")

    mid, midq = sp.create_manage_name_id_request(destination, name_id=nameid,
                                                 new_id=newid)

    print midq
    rargs = sp.apply_binding(binding, "%s" % midq, destination, "")

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

    _req = idp.parse_manage_name_id_request(rargs["data"], binding)

    print _req.message

    mnir = idp.create_manage_name_id_response(_req.message, [binding])

    if binding != BINDING_SOAP:
        binding, destination = idp.pick_binding("manage_name_id_service",
                                                entity_id=sp.config.entityid)
    else:
        destination = ""

    respargs = idp.apply_binding(binding, "%s" % mnir, destination, "")

    print respargs

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

    _response = sp.parse_manage_name_id_request_response(respargs["data"],
                                                         binding)

    print _response.response

    assert _response.response.id == mnir.id
Exemplo n.º 23
0
def auth_response(identity, in_response_to, sp_conf):
    """Generates a fresh signed authentication response"""
    sp_entity_id = sp_conf.entityid
    idp_entity_id = sp_conf.idps().keys()[0]
    acs = sp_conf.endpoint('assertion_consumer_service')[0]
    issuer = saml.Issuer(text=idp_entity_id, format=saml.NAMEID_FORMAT_ENTITY)
    response = response_factory(issuer=issuer,
                                in_response_to=in_response_to,
                                destination=acs,
                                status=success_status_factory())
    idp_conf = IdPConfig()
    name_form = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
    idp_conf.load({
            'entityid': idp_entity_id,
            'xmlsec_binary': sp_conf.xmlsec_binary,
            'attribute_map_dir': os.path.join(BASEDIR, 'attribute-maps'),
            'service': {
                'idp': {
                    'endpoints': tuple(),
                    'policy':  {
                        'default': {
                            "lifetime": {"minutes": 15},
                            "attribute_restrictions": None,
                            "name_form": name_form,
                            }
                        }
                    },
                },
            'key_file': os.path.join(BASEDIR, 'idpcert.key'),
            'cert_file': os.path.join(BASEDIR, 'idpcert.pem'),
            'metadata': {
                'local': [os.path.join(BASEDIR, 'sp_metadata.xml')],
                },
            })
    server = Server("", idp_conf)
    server.ident = Identifier(FakeDb())

    userid = 'irrelevant'
    response = server.authn_response(identity, in_response_to, acs,
                                     sp_entity_id, None, userid)
    return '\n'.join(response)
Exemplo n.º 24
0
 def __init__(self, environ, start_response, conf, cache, incoming):
     """
     Constructor for the class.
     :param environ: WSGI environ
     :param start_response: WSGI start response function
     :param conf: The SAML configuration
     :param cache: Cache with active sessions
     """
     service.Service.__init__(self, environ, start_response)
     self.response_bindings = None
     self.idp = Server(config=conf, cache=cache)
     self.incoming = incoming
Exemplo n.º 25
0
def test_handle_logout_soap():
    sp = Saml2Client(config_file="servera_conf")
    idp = Server(config_file="idp_all_conf")

    policy = NameIDPolicy(format=NAMEID_FORMAT_TRANSIENT,
                          sp_name_qualifier=sp.config.entityid,
                          allow_create="true")

    name_id = idp.ident.construct_nameid("subject", name_id_policy=policy)

    binding, destination = idp.pick_binding("single_logout_service",
                                            [BINDING_SOAP],
                                            entity_id=sp.config.entityid)

    rid, request = idp.create_logout_request(destination, idp.config.entityid,
                                             name_id=name_id)
    args = idp.apply_binding(BINDING_SOAP, "%s" % request, destination)

    # register the user
    session_info = {
        "name_id": name_id, "issuer": idp.config.entityid,
        "not_on_or_after": in_a_while(minutes=15),
        "ava": {
            "givenName": "Anders",
            "surName": "Andersson",
            "mail": "*****@*****.**"
        }
    }
    sp.users.add_information_about_person(session_info)

    reply_args = sp.handle_logout_request(args["data"], name_id, binding,
                                          sign=False)

    print(reply_args)
    assert reply_args["method"] == "POST"
    assert reply_args["headers"] == [('content-type', 'application/soap+xml')]


#if __name__ == "__main__":
#    test_handle_logout_soap()
Exemplo n.º 26
0
    def test_encrypted_response_6(self):
        _server = Server("idp_conf_verify_cert")

        cert_str_advice, cert_key_str_advice = generate_cert()

        cert_str_assertion, cert_key_str_assertion = generate_cert()

        _resp = _server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            #encrypted_advice_attributes=True,
            pefim=True,
            encrypt_cert_advice=cert_str_advice,
            encrypt_cert_assertion=cert_str_assertion
        )

        sresponse = response_from_string(_resp)

        assert sresponse.signature is None

        _, key_file = make_temp("%s" % cert_key_str_assertion, decode=False)

        decr_text_1 = _server.sec.decrypt(_resp, key_file)

        _, key_file = make_temp("%s" % cert_key_str_advice, decode=False)

        decr_text_2 = _server.sec.decrypt(decr_text_1, key_file)

        resp = samlp.response_from_string(decr_text_2)

        resp.assertion = extension_elements_to_elements(resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        self.verify_advice_assertion(resp, decr_text_2)
Exemplo n.º 27
0
def test_basic():
    sp = Saml2Client(config_file="servera_conf")
    idp = Server(config_file="idp_all_conf")

    # -------- @SP ------------
    binding, destination = sp.pick_binding("manage_name_id_service", entity_id=idp.config.entityid)

    nameid = NameID(format=NAMEID_FORMAT_TRANSIENT, text="foobar")
    newid = NewID(text="Barfoo")

    mid, mreq = sp.create_manage_name_id_request(destination, name_id=nameid, new_id=newid)

    print mreq
    rargs = sp.apply_binding(binding, "%s" % mreq, destination, "")

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

    _req = idp.parse_manage_name_id_request(rargs["data"], binding)

    print _req.message

    assert mid == _req.message.id
Exemplo n.º 28
0
    def __init__(self, config, attribute_map=None):
        """Initialize SAML Identity Provider.

        Args:
            config (dict): Identity Provider config info in dict form
            attribute_map (dict): Mapping of attribute keys to user data
        """
        self._config = IdPConfig()
        self._config.load(config)
        self._server = Server(config=self._config)
        self.attribute_map = {}
        if attribute_map is not None:
            self.attribute_map = attribute_map
Exemplo n.º 29
0
def create_logout_response(subject_id, destination, issuer_entity_id,
        req_entity_id, sign=True):
    config = IdPConfig()
    config.load(idp_config)
    idp_server = Server(config=config)
    # construct a request
    logout_request = create_logout_request(
        subject_id=subject_id,
        destination=destination,
        issuer_entity_id=issuer_entity_id,
        req_entity_id=req_entity_id)
    #idp_server.ident = Identifier(auth.AuthDictCache(dict(), '_ident'))
    resp, headers, message = idp_server.logout_response(
        request=logout_request,
        bindings=[BINDING_HTTP_REDIRECT],
        sign=sign)
    location = dict(headers).get('Location')
    url = urlparse.urlparse(location)
    params = urlparse.parse_qs(url.query)
    logout_response_xml = decode_base64_and_inflate(params['SAMLResponse'][0])
    response = samlp.logout_response_from_string(logout_response_xml)
    return response.in_response_to, logout_response_xml
Exemplo n.º 30
0
    def test_unsolicited_response(self):
        """

        """
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = Saml2Client(conf)

        for subject in self.client.users.subjects():
            self.client.users.remove_person(subject)
            
        IDP = "urn:mace:example.com:saml:roland:idp"

        ava = { "givenName": ["Derek"], "surName": ["Jeter"],
                "mail": ["*****@*****.**"]}

        resp_str = "%s" % self.server.create_authn_response(
                                identity=ava,
                                in_response_to="id1",
                                destination="http://lingon.catalogix.se:8087/",
                                sp_entity_id="urn:mace:example.com:saml:roland:sp",
                                name_id_policy=samlp.NameIDPolicy(
                                        format=saml.NAMEID_FORMAT_PERSISTENT),
                                userid="*****@*****.**")

        resp_str = base64.encodestring(resp_str)

        self.client.allow_unsolicited = True
        authn_response = self.client.authn_request_response(
                                                {"SAMLResponse":resp_str}, ())
                            
        assert authn_response is not None
        assert authn_response.issuer() == IDP
        assert authn_response.response.assertion[0].issuer.text == IDP
        session_info = authn_response.session_info()

        print session_info
        assert session_info["ava"] == {'mail': ['*****@*****.**'],
                                       'givenName': ['Derek'],
                                       'surName': ['Jeter']}
        assert session_info["issuer"] == IDP
        assert session_info["came_from"] == ""
        response = samlp.response_from_string(authn_response.xmlstr)
        assert response.destination == "http://lingon.catalogix.se:8087/"

        # One person in the cache
        assert len(self.client.users.subjects()) ==  1
Exemplo n.º 31
0
 def setup_class(self):
     self.server = Server("restrictive_idp_conf")
Exemplo n.º 32
0
 def setup_class(self):
     self.sp = make_plugin("rem", saml_conf="server_conf")
     # Explicitly allow unsigned responses for this test
     self.sp.saml_client.want_response_signed = False
     self.server = Server(config_file="idp_conf")
Exemplo n.º 33
0
class TestServer1():
    def setup_class(self):
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = client.Saml2Client(conf)
        self.name_id = self.server.ident.transient_nameid(
            "urn:mace:example.com:saml:roland:sp", "id12")
        self.ava = {
            "givenName": ["Derek"],
            "surName": ["Jeter"],
            "mail": ["*****@*****.**"],
            "title": "The man"
        }

    def teardown_class(self):
        self.server.close()

    def verify_assertion(self, assertion):
        assert assertion
        assert assertion[0].attribute_statement

        ava = ava = get_ava(assertion[0])

        assert ava ==\
               {'mail': ['*****@*****.**'], 'givenName': ['Derek'],
                'surName': ['Jeter'], 'title': ['The man']}

    def verify_encrypted_assertion(self, assertion, decr_text):
        self.verify_assertion(assertion)
        assert assertion[0].signature is None

        assert 'EncryptedAssertion><encas1:Assertion xmlns:encas0="http://www.w3.org/2001/XMLSchema-instance" ' \
               'xmlns:encas1="urn:oasis:names:tc:SAML:2.0:assertion"' in decr_text

    def verify_advice_assertion(self, resp, decr_text):
        assert resp.assertion[0].signature is None

        assert resp.assertion[0].advice.encrypted_assertion[
            0].extension_elements

        assertion = extension_elements_to_elements(
            resp.assertion[0].advice.encrypted_assertion[0].extension_elements,
            [saml, samlp])
        self.verify_encrypted_assertion(assertion, decr_text)

    def test_issuer(self):
        issuer = self.server._issuer()
        assert isinstance(issuer, saml.Issuer)
        assert _eq(issuer.keyswv(), ["text", "format"])
        assert issuer.format == saml.NAMEID_FORMAT_ENTITY
        assert issuer.text == self.server.config.entityid

    def test_assertion(self):
        assertion = s_utils.assertion_factory(
            subject=factory(saml.Subject,
                            text="_aaa",
                            name_id=factory(
                                saml.NameID,
                                format=saml.NAMEID_FORMAT_TRANSIENT)),
            attribute_statement=do_attribute_statement({
                ("", "", "surName"): ("Jeter", ""),
                ("", "", "givenName"): ("Derek", ""),
            }),
            issuer=self.server._issuer(),
        )

        assert _eq(assertion.keyswv(), [
            'attribute_statement', 'issuer', 'id', 'subject', 'issue_instant',
            'version'
        ])
        assert assertion.version == "2.0"
        assert assertion.issuer.text == "urn:mace:example.com:saml:roland:idp"
        #
        assert assertion.attribute_statement
        attribute_statement = assertion.attribute_statement
        assert len(attribute_statement.attribute) == 2
        attr0 = attribute_statement.attribute[0]
        attr1 = attribute_statement.attribute[1]
        if attr0.attribute_value[0].text == "Derek":
            assert attr0.friendly_name == "givenName"
            assert attr1.friendly_name == "surName"
            assert attr1.attribute_value[0].text == "Jeter"
        else:
            assert attr1.friendly_name == "givenName"
            assert attr1.attribute_value[0].text == "Derek"
            assert attr0.friendly_name == "surName"
            assert attr0.attribute_value[0].text == "Jeter"
        #
        subject = assertion.subject
        assert _eq(subject.keyswv(), ["text", "name_id"])
        assert subject.text == "_aaa"
        assert subject.name_id.format == saml.NAMEID_FORMAT_TRANSIENT

    def test_response(self):
        response = sigver.response_factory(
            in_response_to="_012345",
            destination="https:#www.example.com",
            status=s_utils.success_status_factory(),
            assertion=s_utils.assertion_factory(
                subject=factory(saml.Subject,
                                text="_aaa",
                                name_id=saml.NAMEID_FORMAT_TRANSIENT),
                attribute_statement=do_attribute_statement({
                    ("", "", "surName"): ("Jeter", ""),
                    ("", "", "givenName"): ("Derek", ""),
                }),
                issuer=self.server._issuer(),
            ),
            issuer=self.server._issuer(),
        )

        print(response.keyswv())
        assert _eq(response.keyswv(), [
            'destination', 'assertion', 'status', 'in_response_to',
            'issue_instant', 'version', 'issuer', 'id'
        ])
        assert response.version == "2.0"
        assert response.issuer.text == "urn:mace:example.com:saml:roland:idp"
        assert response.destination == "https:#www.example.com"
        assert response.in_response_to == "_012345"
        #
        status = response.status
        print(status)
        assert status.status_code.value == samlp.STATUS_SUCCESS

    def test_parse_faulty_request(self):
        req_id, authn_request = self.client.create_authn_request(
            destination="http://www.example.com", id="id1")

        # should raise an error because faulty spentityid
        binding = BINDING_HTTP_REDIRECT
        htargs = self.client.apply_binding(binding, "%s" % authn_request,
                                           "http://www.example.com", "abcd")
        _dict = parse_qs(htargs["headers"][0][1].split('?')[1])
        print(_dict)
        raises(OtherError, self.server.parse_authn_request,
               _dict["SAMLRequest"][0], binding)

    def test_parse_faulty_request_to_err_status(self):
        req_id, authn_request = self.client.create_authn_request(
            destination="http://www.example.com")

        binding = BINDING_HTTP_REDIRECT
        htargs = self.client.apply_binding(binding, "%s" % authn_request,
                                           "http://www.example.com", "abcd")
        _dict = parse_qs(htargs["headers"][0][1].split('?')[1])
        print(_dict)

        try:
            self.server.parse_authn_request(_dict["SAMLRequest"][0], binding)
            status = None
        except OtherError as oe:
            print(oe.args)
            status = s_utils.error_status_factory(oe)

        assert status
        print(status)
        assert _eq(status.keyswv(), ["status_code", "status_message"])
        assert status.status_message.text == 'Not destined for me!'
        status_code = status.status_code
        assert _eq(status_code.keyswv(), ["status_code", "value"])
        assert status_code.value == samlp.STATUS_RESPONDER
        assert status_code.status_code.value == samlp.STATUS_UNKNOWN_PRINCIPAL

    def test_parse_ok_request(self):
        req_id, authn_request = self.client.create_authn_request(
            message_id="id1", destination="http://*****:*****@nyy.mlb.com",
                "title": "The man"
            },
            "id12",  # in_response_to
            "http://*****:*****@nyy.mlb.com"],
            "title": "The man"
        }

        npolicy = samlp.NameIDPolicy(format=saml.NAMEID_FORMAT_TRANSIENT,
                                     allow_create="true")
        resp_str = "%s" % self.server.create_authn_response(
            ava,
            "id1",
            "http://*****:*****@example.com",
            authn=AUTHN)

        response = samlp.response_from_string(resp_str)
        print(response.keyswv())
        assert _eq(response.keyswv(), [
            'status', 'destination', 'assertion', 'in_response_to',
            'issue_instant', 'version', 'issuer', 'id'
        ])
        print(response.assertion[0].keyswv())
        assert len(response.assertion) == 1
        assert _eq(response.assertion[0].keyswv(), [
            'attribute_statement', 'issue_instant', 'version', 'subject',
            'conditions', 'id', 'issuer', 'authn_statement'
        ])
        assertion = response.assertion[0]
        assert len(assertion.attribute_statement) == 1
        astate = assertion.attribute_statement[0]
        print(astate)
        assert len(astate.attribute) == 4

    def test_signed_response(self):
        name_id = self.server.ident.transient_nameid(
            "urn:mace:example.com:saml:roland:sp", "id12")
        ava = {
            "givenName": ["Derek"],
            "surName": ["Jeter"],
            "mail": ["*****@*****.**"],
            "title": "The man"
        }

        signed_resp = self.server.create_authn_response(
            ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=name_id,
            sign_assertion=True)

        print(signed_resp)
        assert signed_resp

        sresponse = response_from_string(signed_resp)
        # It's the assertions that are signed not the response per se
        assert len(sresponse.assertion) == 1
        assertion = sresponse.assertion[0]

        # Since the reponse is created dynamically I don't know the signature
        # value. Just that there should be one
        assert assertion.signature.signature_value.text != ""

    def test_signed_response_1(self):

        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=True,
            sign_assertion=True,
        )

        sresponse = response_from_string(signed_resp)

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
            node_id=sresponse.id,
            id_attr="")
        assert valid

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
            node_id=sresponse.assertion[0].id,
            id_attr="")
        assert valid

        self.verify_assertion(sresponse.assertion)

    def test_signed_response_2(self):
        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=True,
            sign_assertion=False,
        )

        sresponse = response_from_string(signed_resp)

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
            node_id=sresponse.id,
            id_attr="")
        assert valid

        assert sresponse.assertion[0].signature == None

    def test_signed_response_3(self):

        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=True,
        )

        sresponse = response_from_string(signed_resp)

        assert sresponse.signature == None

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
            node_id=sresponse.assertion[0].id,
            id_attr="")
        assert valid

        self.verify_assertion(sresponse.assertion)

    def test_encrypted_signed_response_1(self):

        cert_str, cert_key_str = generate_cert()

        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=True,
            sign_assertion=True,
            encrypt_assertion=False,
            encrypt_assertion_self_contained=True,
            pefim=True,
            encrypt_cert_advice=cert_str,
        )

        sresponse = response_from_string(signed_resp)

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
            node_id=sresponse.id,
            id_attr="")

        assert valid

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
            node_id=sresponse.assertion[0].id,
            id_attr="")

        assert valid

        _, key_file = make_temp(cert_key_str, decode=False)

        decr_text = self.server.sec.decrypt(signed_resp, key_file)

        resp = samlp.response_from_string(decr_text)

        assert resp.assertion[0].advice.encrypted_assertion[
            0].extension_elements

        assertion = extension_elements_to_elements(
            resp.assertion[0].advice.encrypted_assertion[0].extension_elements,
            [saml, samlp])

        self.verify_assertion(assertion)

        #PEFIM never signs assertions.
        assert assertion[0].signature is None
        #valid = self.server.sec.verify_signature(decr_text,
        #                                         self.server.config.cert_file,
        #                                         node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
        #                                         node_id=assertion[0].id,
        #                                         id_attr="")
        assert valid

    def test_encrypted_signed_response_2(self):
        cert_str, cert_key_str = generate_cert()

        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=True,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
        )

        sresponse = response_from_string(signed_resp)

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
            node_id=sresponse.id,
            id_attr="")
        assert valid

        decr_text_old = copy.deepcopy("%s" % signed_resp)

        decr_text = self.server.sec.decrypt(
            signed_resp, self.client.config.encryption_keypairs[0]["key_file"])

        assert decr_text == decr_text_old

        decr_text = self.server.sec.decrypt(
            signed_resp, self.client.config.encryption_keypairs[1]["key_file"])

        assert decr_text != decr_text_old

        resp = samlp.response_from_string(decr_text)

        resp.assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        assert resp.assertion[0].signature == None

        self.verify_assertion(resp.assertion)

    def test_encrypted_signed_response_3(self):
        cert_str, cert_key_str = generate_cert()

        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=True,
            sign_assertion=True,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=False,
            encrypt_cert_assertion=cert_str,
        )

        sresponse = response_from_string(signed_resp)

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
            node_id=sresponse.id,
            id_attr="")
        assert valid

        _, key_file = make_temp(cert_key_str, decode=False)

        decr_text = self.server.sec.decrypt(signed_resp, key_file)

        resp = samlp.response_from_string(decr_text)

        resp.assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        valid = self.server.sec.verify_signature(
            decr_text,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
            node_id=resp.assertion[0].id,
            id_attr="")

        assert valid

        self.verify_assertion(resp.assertion)

        assert 'xmlns:encas' not in decr_text

    def test_encrypted_signed_response_4(self):

        cert_str, cert_key_str = generate_cert()

        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=True,
            sign_assertion=True,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            pefim=True,
            encrypt_cert_advice=cert_str,
        )

        sresponse = response_from_string(signed_resp)

        valid = self.server.sec.verify_signature(
            signed_resp,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
            node_id=sresponse.id,
            id_attr="")
        assert valid

        decr_text = self.server.sec.decrypt(
            signed_resp, self.client.config.encryption_keypairs[1]["key_file"])

        resp = samlp.response_from_string(decr_text)

        resp.assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        valid = self.server.sec.verify_signature(
            decr_text,
            self.server.config.cert_file,
            node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
            node_id=resp.assertion[0].id,
            id_attr="")

        assert valid

        _, key_file = make_temp(cert_key_str, decode=False)

        decr_text = self.server.sec.decrypt(decr_text, key_file)

        resp = samlp.response_from_string(decr_text)

        assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])
        assertion = \
             extension_elements_to_elements(assertion[0].advice.encrypted_assertion[0].extension_elements,[saml, samlp])
        self.verify_assertion(assertion)

        #PEFIM never signs assertion in advice
        assert assertion[0].signature is None
        #valid = self.server.sec.verify_signature(decr_text,
        #                                         self.server.config.cert_file,
        #                                         node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
        #                                         node_id=assertion[0].id,
        #                                         id_attr="")
        assert valid

    def test_encrypted_response_1(self):
        cert_str_advice, cert_key_str_advice = generate_cert()

        _resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=False,
            encrypt_assertion_self_contained=True,
            pefim=True,
            encrypt_cert_advice=cert_str_advice,
        )

        _resp = "%s" % _resp

        sresponse = response_from_string(_resp)

        assert sresponse.signature is None

        _, key_file = make_temp(cert_key_str_advice, decode=False)

        decr_text = self.server.sec.decrypt(_resp, key_file)

        resp = samlp.response_from_string(decr_text)

        self.verify_advice_assertion(resp, decr_text)

    def test_encrypted_response_2(self):

        cert_str_advice, cert_key_str_advice = generate_cert()

        _resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            pefim=True,
            encrypt_cert_advice=cert_str_advice,
        )

        sresponse = response_from_string(_resp)

        assert sresponse.signature is None

        decr_text_1 = self.server.sec.decrypt(
            _resp, self.client.config.encryption_keypairs[1]["key_file"])

        _, key_file = make_temp(cert_key_str_advice, decode=False)

        decr_text_2 = self.server.sec.decrypt(decr_text_1, key_file)

        resp = samlp.response_from_string(decr_text_2)

        resp.assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        self.verify_advice_assertion(resp, decr_text_2)

    def test_encrypted_response_3(self):
        cert_str_assertion, cert_key_str_assertion = generate_cert()

        _resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            encrypted_advice_attributes=False,
            encrypt_cert_assertion=cert_str_assertion)

        sresponse = response_from_string(_resp)

        assert sresponse.signature is None

        _, key_file = make_temp(cert_key_str_assertion, decode=False)

        decr_text = self.server.sec.decrypt(_resp, key_file)

        resp = samlp.response_from_string(decr_text)

        assert resp.encrypted_assertion[0].extension_elements

        assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        self.verify_encrypted_assertion(assertion, decr_text)

    def test_encrypted_response_4(self):
        _resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            encrypted_advice_attributes=False,
        )

        sresponse = response_from_string(_resp)

        assert sresponse.signature is None

        decr_text = self.server.sec.decrypt(
            _resp, self.client.config.encryption_keypairs[1]["key_file"])

        resp = samlp.response_from_string(decr_text)

        assert resp.encrypted_assertion[0].extension_elements

        assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        self.verify_encrypted_assertion(assertion, decr_text)

    def test_encrypted_response_5(self):
        _resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=False,
            encrypt_assertion_self_contained=True,
            pefim=True)

        _resp = "%s" % _resp

        sresponse = response_from_string(_resp)

        assert sresponse.signature is None

        decr_text = self.server.sec.decrypt(
            _resp, self.client.config.encryption_keypairs[1]["key_file"])

        resp = samlp.response_from_string(decr_text)

        self.verify_advice_assertion(resp, decr_text)

    def test_encrypted_response_6(self):
        _server = Server("idp_conf_verify_cert")

        cert_str_advice, cert_key_str_advice = generate_cert()

        cert_str_assertion, cert_key_str_assertion = generate_cert()

        _resp = _server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            pefim=True,
            encrypt_cert_advice=cert_str_advice,
            encrypt_cert_assertion=cert_str_assertion)

        sresponse = response_from_string(_resp)

        assert sresponse.signature is None

        _, key_file = make_temp(cert_key_str_assertion, decode=False)

        decr_text_1 = _server.sec.decrypt(_resp, key_file)

        _, key_file = make_temp(cert_key_str_advice, decode=False)

        decr_text_2 = _server.sec.decrypt(decr_text_1, key_file)

        resp = samlp.response_from_string(decr_text_2)

        resp.assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        self.verify_advice_assertion(resp, decr_text_2)

    def test_encrypted_response_7(self):
        _resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            pefim=True)

        sresponse = response_from_string(_resp)

        assert sresponse.signature is None

        decr_text_1 = self.server.sec.decrypt(
            _resp, self.client.config.encryption_keypairs[1]["key_file"])

        decr_text_2 = self.server.sec.decrypt(
            decr_text_1, self.client.config.encryption_keypairs[1]["key_file"])

        resp = samlp.response_from_string(decr_text_2)

        resp.assertion = extension_elements_to_elements(
            resp.encrypted_assertion[0].extension_elements, [saml, samlp])

        self.verify_advice_assertion(resp, decr_text_2)

    def test_encrypted_response_8(self):
        try:
            _resp = self.server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=True,
                encrypt_assertion_self_contained=True,
                pefim=True,
                encrypt_cert_advice="whatever",
                encrypt_cert_assertion="whatever")
            assert False, "Must throw an exception"
        except EncryptError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        try:
            _resp = self.server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=False,
                encrypt_assertion_self_contained=True,
                pefim=True,
                encrypt_cert_advice="whatever",
            )
            assert False, "Must throw an exception"
        except EncryptError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        try:
            _resp = self.server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=True,
                encrypt_assertion_self_contained=True,
                encrypted_advice_attributes=False,
                encrypt_cert_assertion="whatever")
            assert False, "Must throw an exception"
        except EncryptError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        _server = Server("idp_conf_verify_cert")

        try:
            _resp = _server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=True,
                encrypt_assertion_self_contained=True,
                pefim=True,
                encrypt_cert_advice="whatever",
                encrypt_cert_assertion="whatever")
            assert False, "Must throw an exception"
        except CertificateError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        try:
            _resp = _server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=False,
                encrypt_assertion_self_contained=True,
                pefim=True,
                encrypt_cert_advice="whatever",
            )
            assert False, "Must throw an exception"
        except CertificateError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        try:
            _resp = _server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=True,
                encrypt_assertion_self_contained=True,
                encrypted_advice_attributes=False,
                encrypt_cert_assertion="whatever")
            assert False, "Must throw an exception"
        except CertificateError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

    def test_encrypted_response_9(self):
        _server = Server("idp_conf_sp_no_encrypt")

        _resp = _server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            pefim=True,
        )

        self.verify_assertion(_resp.assertion.advice.assertion)

        _resp = _server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=False,
            encrypt_assertion_self_contained=True,
            pefim=True)

        self.verify_assertion(_resp.assertion.advice.assertion)

        _resp = _server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=False,
            sign_assertion=False,
            encrypt_assertion=True,
            encrypt_assertion_self_contained=True,
            encrypted_advice_attributes=False,
        )

        self.verify_assertion([_resp.assertion])

    def test_slo_http_post(self):
        soon = time_util.in_a_while(days=1)
        sinfo = {
            "name_id": nid,
            "issuer": "urn:mace:example.com:saml:roland:idp",
            "not_on_or_after": soon,
            "user": {
                "givenName": "Leo",
                "surName": "Laport",
            }
        }
        self.client.users.add_information_about_person(sinfo)

        req_id, logout_request = self.client.create_logout_request(
            destination="http://localhost:8088/slop",
            name_id=nid,
            issuer_entity_id="urn:mace:example.com:saml:roland:idp",
            reason="I'm tired of this")

        intermed = base64.b64encode(str(logout_request).encode('utf-8'))

        #saml_soap = make_soap_enveloped_saml_thingy(logout_request)
        request = self.server.parse_logout_request(intermed, BINDING_HTTP_POST)
        assert request

    def test_slo_soap(self):
        soon = time_util.in_a_while(days=1)
        sinfo = {
            "name_id": nid,
            "issuer": "urn:mace:example.com:saml:roland:idp",
            "not_on_or_after": soon,
            "user": {
                "givenName": "Leo",
                "surName": "Laport",
            }
        }

        sp = client.Saml2Client(config_file="server_conf")
        sp.users.add_information_about_person(sinfo)

        req_id, logout_request = sp.create_logout_request(
            name_id=nid,
            destination="http://localhost:8088/slo",
            issuer_entity_id="urn:mace:example.com:saml:roland:idp",
            reason="I'm tired of this")

        #_ = s_utils.deflate_and_base64_encode("%s" % (logout_request,))

        saml_soap = make_soap_enveloped_saml_thingy(logout_request)
        self.server.ident.close()

        with closing(Server("idp_soap_conf")) as idp:
            request = idp.parse_logout_request(saml_soap)
            idp.ident.close()
            assert request
Exemplo n.º 34
0
    def setup_class(self):
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = Saml2Client(conf)
Exemplo n.º 35
0
class TestSignedResponse():

    def setup_class(self):
        self.server = Server("idp_conf")
        sign_alg = Mock()
        sign_alg.return_value = ds.SIG_RSA_SHA512
        digest_alg = Mock()
        digest_alg.return_value = ds.DIGEST_SHA512
        self.restet_default = ds.DefaultSignature
        ds.DefaultSignature = MagicMock()
        ds.DefaultSignature().get_sign_alg = sign_alg
        ds.DefaultSignature().get_digest_alg = digest_alg
        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = client.Saml2Client(conf)
        self.name_id = self.server.ident.transient_nameid(
            "urn:mace:example.com:saml:roland:sp", "id12")
        self.ava = {"givenName": ["Derek"], "surName": ["Jeter"],
               "mail": ["*****@*****.**"], "title": "The man"}

    def teardown_class(self):
        ds.DefaultSignature = self.restet_default
        self.server.close()

    def verify_assertion(self, assertion):
        assert assertion
        assert assertion[0].attribute_statement

        ava = ava = get_ava(assertion[0])

        assert ava ==\
               {'mail': ['*****@*****.**'], 'givenName': ['Derek'],
                'surName': ['Jeter'], 'title': ['The man']}

    def test_signed_response(self):

        print(ds.DefaultSignature().get_digest_alg())
        name_id = self.server.ident.transient_nameid(
            "urn:mace:example.com:saml:roland:sp", "id12")
        ava = {"givenName": ["Derek"], "surName": ["Jeter"],
               "mail": ["*****@*****.**"], "title": "The man"}

        signed_resp = self.server.create_authn_response(
            ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=name_id,
            sign_assertion=True
        )

        print(signed_resp)
        assert signed_resp

        sresponse = response_from_string(signed_resp)
        assert ds.SIG_RSA_SHA512 in str(sresponse), "Not correctly signed!"
        assert ds.DIGEST_SHA512 in str(sresponse), "Not correctly signed!"

    def test_signed_response_1(self):

        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=True,
            sign_assertion=True,
        )

        sresponse = response_from_string(signed_resp)
        assert ds.SIG_RSA_SHA512 in str(sresponse), "Not correctly signed!"
        assert ds.DIGEST_SHA512 in str(sresponse), "Not correctly signed!"
        valid = self.server.sec.verify_signature(signed_resp,
                                                 self.server.config.cert_file,
                                                 node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
                                                 node_id=sresponse.id,
                                                 id_attr="")
        assert valid
        assert ds.SIG_RSA_SHA512 in str(sresponse.assertion[0]), "Not correctly signed!"
        assert ds.DIGEST_SHA512 in str(sresponse.assertion[0]), "Not correctly signed!"
        valid = self.server.sec.verify_signature(signed_resp,
                                                 self.server.config.cert_file,
                                                 node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
                                                 node_id=sresponse.assertion[0].id,
                                                 id_attr="")
        assert valid

        self.verify_assertion(sresponse.assertion)

    def test_signed_response_2(self):

        signed_resp = self.server.create_authn_response(
            self.ava,
            "id12",  # in_response_to
            "http://lingon.catalogix.se:8087/",  # consumer_url
            "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
            name_id=self.name_id,
            sign_response=True,
            sign_assertion=True,
            sign_alg=ds.SIG_RSA_SHA256,
            digest_alg=ds.DIGEST_SHA256
        )

        sresponse = response_from_string(signed_resp)
        assert ds.SIG_RSA_SHA256 in str(sresponse), "Not correctly signed!"
        assert ds.DIGEST_SHA256 in str(sresponse), "Not correctly signed!"
        valid = self.server.sec.verify_signature(signed_resp,
                                                 self.server.config.cert_file,
                                                 node_name='urn:oasis:names:tc:SAML:2.0:protocol:Response',
                                                 node_id=sresponse.id,
                                                 id_attr="")
        assert valid
        assert ds.SIG_RSA_SHA256 in str(sresponse.assertion[0]), "Not correctly signed!"
        assert ds.DIGEST_SHA256 in str(sresponse.assertion[0]), "Not correctly signed!"
        valid = self.server.sec.verify_signature(signed_resp,
                                                 self.server.config.cert_file,
                                                 node_name='urn:oasis:names:tc:SAML:2.0:assertion:Assertion',
                                                 node_id=sresponse.assertion[0].id,
                                                 id_attr="")
        assert valid

        self.verify_assertion(sresponse.assertion)
Exemplo n.º 36
0
class TestClient:
    def setup_class(self):
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = Saml2Client(conf)

    def test_create_attribute_query1(self):
        req = self.client.create_attribute_query(
            "https://idp.example.com/idp/",
            "E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
            nameid_format=saml.NAMEID_FORMAT_PERSISTENT,
            id="id1")
        reqstr = "%s" % req.to_string()

        assert req.destination == "https://idp.example.com/idp/"
        assert req.id == "id1"
        assert req.version == "2.0"
        subject = req.subject
        name_id = subject.name_id
        assert name_id.format == saml.NAMEID_FORMAT_PERSISTENT
        assert name_id.text == "E8042FB4-4D5B-48C3-8E14-8EDD852790DD"
        issuer = req.issuer
        assert issuer.text == "urn:mace:example.com:saml:roland:sp"

        attrq = samlp.attribute_query_from_string(reqstr)

        print attrq.keyswv()
        assert _leq(attrq.keyswv(), [
            'destination', 'subject', 'issue_instant', 'version', 'id',
            'issuer'
        ])

        assert attrq.destination == req.destination
        assert attrq.id == req.id
        assert attrq.version == req.version
        assert attrq.issuer.text == issuer.text
        assert attrq.issue_instant == req.issue_instant
        assert attrq.subject.name_id.format == name_id.format
        assert attrq.subject.name_id.text == name_id.text

    def test_create_attribute_query2(self):
        req = self.client.create_attribute_query(
            "https://idp.example.com/idp/",
            "E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
            attribute={
                ("urn:oid:2.5.4.42", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "givenName"):
                None,
                ("urn:oid:2.5.4.4", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "surname"):
                None,
                ("urn:oid:1.2.840.113549.1.9.1", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"):
                None,
            },
            nameid_format=saml.NAMEID_FORMAT_PERSISTENT,
            id="id1")

        print req.to_string()
        assert req.destination == "https://idp.example.com/idp/"
        assert req.id == "id1"
        assert req.version == "2.0"
        subject = req.subject
        name_id = subject.name_id
        assert name_id.format == saml.NAMEID_FORMAT_PERSISTENT
        assert name_id.text == "E8042FB4-4D5B-48C3-8E14-8EDD852790DD"
        assert len(req.attribute) == 3
        # one is givenName
        seen = []
        for attribute in req.attribute:
            if attribute.name == "urn:oid:2.5.4.42":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                assert attribute.friendly_name == "givenName"
                seen.append("givenName")
            elif attribute.name == "urn:oid:2.5.4.4":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                assert attribute.friendly_name == "surname"
                seen.append("surname")
            elif attribute.name == "urn:oid:1.2.840.113549.1.9.1":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                if getattr(attribute, "friendly_name"):
                    assert False
                seen.append("email")
        assert _leq(seen, ["givenName", "surname", "email"])

    def test_create_attribute_query_3(self):
        req = self.client.create_attribute_query(
            "https://aai-demo-idp.switch.ch/idp/shibboleth",
            "_e7b68a04488f715cda642fbdd90099f5",
            nameid_format=saml.NAMEID_FORMAT_TRANSIENT,
            id="id1")

        assert isinstance(req, samlp.AttributeQuery)
        assert req.destination == "https://aai-demo-idp.switch.ch/idp/shibboleth"
        assert req.id == "id1"
        assert req.version == "2.0"
        assert req.issue_instant
        assert req.issuer.text == "urn:mace:example.com:saml:roland:sp"
        nameid = req.subject.name_id
        assert nameid.format == saml.NAMEID_FORMAT_TRANSIENT
        assert nameid.text == "_e7b68a04488f715cda642fbdd90099f5"

    # def test_idp_entry(self):
    #     idp_entry = self.client.idp_entry(name="Umeå Universitet",
    #                         location="https://idp.umu.se/")
    #
    #     assert idp_entry.name == "Umeå Universitet"
    #     assert idp_entry.loc == "https://idp.umu.se/"
    #
    # def test_scope(self):
    #     entity_id = "urn:mace:example.com:saml:roland:idp"
    #     locs = self.client.metadata.single_sign_on_services(entity_id)
    #     scope = self.client.scoping_from_metadata(entity_id, locs)
    #
    #     assert scope.idp_list
    #     assert len(scope.idp_list.idp_entry) == 1
    #     idp_entry = scope.idp_list.idp_entry[0]
    #     assert idp_entry.name == 'Exempel AB'
    #     assert idp_entry.loc == ['http://*****:*****@nyy.mlb.com"],
            "title": ["The man"]
        }

        nameid_policy = samlp.NameIDPolicy(
            allow_create="false", format=saml.NAMEID_FORMAT_PERSISTENT)

        resp = self.server.create_authn_response(
            identity=ava,
            in_response_to="id1",
            destination="http://lingon.catalogix.se:8087/",
            sp_entity_id="urn:mace:example.com:saml:roland:sp",
            name_id_policy=nameid_policy,
            userid="*****@*****.**",
            authn=AUTHN)

        resp_str = "%s" % resp

        resp_str = base64.encodestring(resp_str)

        authn_response = self.client.authn_request_response(
            {"SAMLResponse": resp_str},
            {"id1": "http://foo.example.com/service"})

        assert authn_response is not None
        assert authn_response.issuer() == IDP
        assert authn_response.response.assertion[0].issuer.text == IDP
        session_info = authn_response.session_info()

        print session_info
        assert session_info["ava"] == {
            'mail': ['*****@*****.**'],
            'givenName': ['Derek'],
            'sn': ['Jeter'],
            'title': ["The man"]
        }
        assert session_info["issuer"] == IDP
        assert session_info["came_from"] == "http://foo.example.com/service"
        response = samlp.response_from_string(authn_response.xmlstr)
        assert response.destination == "http://lingon.catalogix.se:8087/"

        # One person in the cache
        assert len(self.client.users.subjects()) == 1
        subject_id = self.client.users.subjects()[0]
        print "||||", self.client.users.get_info_from(subject_id, IDP)
        # The information I have about the subject comes from one source
        assert self.client.users.issuers_of_info(subject_id) == [IDP]

        # --- authenticate another person

        ava = {
            "givenName": ["Alfonson"],
            "surName": ["Soriano"],
            "mail": ["*****@*****.**"],
            "title": ["outfielder"]
        }

        resp_str = "%s" % self.server.create_authn_response(
            identity=ava,
            in_response_to="id2",
            destination="http://lingon.catalogix.se:8087/",
            sp_entity_id="urn:mace:example.com:saml:roland:sp",
            name_id_policy=nameid_policy,
            userid="*****@*****.**",
            authn=AUTHN)

        resp_str = base64.encodestring(resp_str)

        self.client.authn_request_response(
            {"SAMLResponse": resp_str},
            {"id2": "http://foo.example.com/service"})

        # Two persons in the cache
        assert len(self.client.users.subjects()) == 2
        issuers = [
            self.client.users.issuers_of_info(s)
            for s in self.client.users.subjects()
        ]
        # The information I have about the subjects comes from the same source
        print issuers
        assert issuers == [[IDP], [IDP]]

    def test_init_values(self):
        entityid = self.client.config.entityid
        print entityid
        assert entityid == "urn:mace:example.com:saml:roland:sp"
        print self.client.metadata.with_descriptor("idpsso")
        location = self.client._sso_location()
        print location
        assert location == 'http://localhost:8088/sso'
        service_url = self.client.service_url()
        print service_url
        assert service_url == "http://lingon.catalogix.se:8087/"
        my_name = self.client._my_name()
        print my_name
        assert my_name == "urn:mace:example.com:saml:roland:sp"
Exemplo n.º 37
0
 def __init__(self, config_file=""):
     Server.__init__(self, config_file)
Exemplo n.º 38
0
class IdPHandlerViewMixin:
    """ Contains some methods used by multiple views """

    error_view = import_string(
        getattr(settings, 'SAML_IDP_ERROR_VIEW_CLASS',
                'djangosaml2idp.error_views.SamlIDPErrorView'))

    def handle_error(self, request, **kwargs):
        # Log the exception and the statuscode
        logger.error(kwargs)
        return self.error_view.as_view()(request, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        """ Construct IDP server with config from settings dict
        """
        conf = IdPConfig()
        try:
            conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG))
            self.IDP = Server(config=conf)
        except Exception as e:
            return self.handle_error(request, exception=e)
        return super().dispatch(request, *args, **kwargs)

    def get_sp_config(self, sp_entity_id):
        """ Get a dict with the configuration for a SP according to the SAML_IDP_SPCONFIG settings.
            Raises an exception if no SP matching the given entity id can be found.
        """
        d = {'id': sp_entity_id}
        try:
            d['config'] = settings.SAML_IDP_SPCONFIG[sp_entity_id]
        except KeyError:
            raise ImproperlyConfigured(
                _("No config for SP {} defined in SAML_IDP_SPCONFIG").format(
                    sp_entity_id))
        return d

    def get_processor(self, sp_entity_id: str,
                      processor_class_path: str) -> BaseProcessor:
        """ Instantiate user-specified processor or default to an all-access base processor.
            Raises an exception if the processor class can not be found or initialized.
        """
        if processor_class_path:
            try:
                processor_cls = import_string(processor_class_path)
            except ImportError as e:
                msg = _("Failed to import processor class {}").format(
                    processor_class_path)
                logger.error(msg, exc_info=True)
                raise ImproperlyConfigured(msg) from e
        else:
            processor_cls = BaseProcessor

        try:
            processor_instance = processor_cls(sp_entity_id)
        except Exception as e:
            msg = _("Failed to instantiate processor: {} - {}").format(
                processor_cls, e)
            logger.error(msg, exc_info=True)
            raise
        return processor_instance

    def verify_request_signature(self, req_info):
        """ Signature verification for authn request signature_check is at
            saml2.sigver.SecurityContext.correctly_signed_authn_request
        """
        # TODO: Add unit tests for this
        if not req_info.signature_check(req_info.xmlstr):
            raise ValueError(_("Message signature verification failure"))

    def check_access(self, processor, request):
        """ Check if user has access to the service of this SP. Raises a PermissionDenied exception if not.
        """
        if not processor.has_access(request):
            raise PermissionDenied(
                _("You do not have access to this resource"))

    def get_authn(self, req_info=None):
        req_authn_context = req_info.message.requested_authn_context if req_info else PASSWORD
        broker = AuthnBroker()
        broker.add(authn_context_class_ref(req_authn_context), "")
        return broker.get_authn_by_accr(req_authn_context)

    def build_authn_response(self, user, authn, resp_args,
                             processor: BaseProcessor, sp_config: dict):
        """ pysaml2 server.Server.create_authn_response wrapper
        """
        sp_config['name_id_format'] = resp_args.get(
            'name_id_policy').format or NAMEID_FORMAT_UNSPECIFIED
        idp_name_id_format_list = self.IDP.config.getattr(
            "name_id_format", "idp") or [NAMEID_FORMAT_UNSPECIFIED]

        if sp_config['name_id_format'] not in idp_name_id_format_list:
            raise ImproperlyConfigured(
                _('SP requested a name_id_format that is not supported in the IDP'
                  ))

        user_id = processor.get_user_id(user, sp_config, self.IDP.config)
        name_id = NameID(format=sp_config['name_id_format'],
                         sp_name_qualifier=sp_config['id'],
                         text=user_id)

        authn_resp = self.IDP.create_authn_response(
            authn=authn,
            identity=processor.create_identity(
                user, sp_config.get('attribute_mapping')),
            name_id=name_id,
            userid=user_id,
            sp_entity_id=sp_config['id'],
            # Signing
            sign_response=sp_config['config'].get("sign_response")
            or self.IDP.config.getattr("sign_response", "idp") or False,
            sign_assertion=sp_config['config'].get("sign_assertion")
            or self.IDP.config.getattr("sign_assertion", "idp") or False,
            sign_alg=sp_config['config'].get("signing_algorithm") or getattr(
                settings, "SAML_AUTHN_SIGN_ALG", xmldsig.SIG_RSA_SHA256),
            digest_alg=sp_config['config'].get("digest_algorithm") or getattr(
                settings, "SAML_AUTHN_DIGEST_ALG", xmldsig.DIGEST_SHA256),
            # Encryption
            encrypt_assertion=sp_config['config'].get('encrypt_saml_responses')
            or getattr(settings, 'SAML_ENCRYPT_AUTHN_RESPONSE', False),
            encrypted_advice_attributes=sp_config['config'].get(
                'encrypt_saml_responses')
            or getattr(settings, 'SAML_ENCRYPT_AUTHN_RESPONSE', False),
            **resp_args)
        return authn_resp

    def render_login_html_to_string(self,
                                    template_name,
                                    context=None,
                                    request=None,
                                    using=None):
        """ Render the html response for the login action. Can be using a custom html template if set on the view. """
        default_login_template_name = 'djangosaml2idp/login.html'
        custom_login_template_name = getattr(self, 'login_html_template')
        if custom_login_template_name:
            try:
                template = get_template(custom_login_template_name,
                                        using=using)
            except (TemplateDoesNotExist, TemplateSyntaxError) as e:
                logger.error(
                    'Specified template {} cannot be used due to: {}. Falling back to default login template'
                    .format(custom_login_template_name, str(e)))
                template = get_template(default_login_template_name,
                                        using=using)
        else:
            template = get_template(default_login_template_name, using=using)
        return template.render(context, request)

    def create_html_response(self, request, binding, authn_resp, destination,
                             relay_state):
        """ Login form for SSO
        """
        if binding == BINDING_HTTP_POST:
            context = {
                "acs_url":
                destination,
                "saml_response":
                base64.b64encode(str(authn_resp).encode()).decode(),
                "relay_state":
                relay_state,
            }
            html_response = {
                "data":
                self.render_login_html_to_string(context=context,
                                                 request=request),
                "type":
                "POST",
            }
        else:
            http_args = self.IDP.apply_binding(binding=binding,
                                               msg_str=authn_resp,
                                               destination=destination,
                                               relay_state=relay_state,
                                               response=True)

            logger.debug('http args are: %s' % http_args)
            html_response = {
                "data": http_args['headers'][0][1],
                "type": "REDIRECT",
            }
        return html_response

    def render_response(self,
                        request,
                        html_response,
                        processor: BaseProcessor = None):
        """ Return either a response as redirect to MultiFactorView or as html with self-submitting form to log in.
        """
        if not processor:
            # In case of SLO, where processor isn't relevant
            if html_response['type'] == 'POST':
                return HttpResponse(html_response['data'])
            else:
                return HttpResponseRedirect(html_response['data'])

        request.session['saml_data'] = html_response

        if processor.enable_multifactor(request.user):
            logger.debug("Redirecting to process_multi_factor")
            return HttpResponseRedirect(
                reverse('djangosaml2idp:saml_multi_factor'))

        # No multifactor
        logger.debug("Performing SAML redirect")
        if html_response['type'] == 'POST':
            return HttpResponse(html_response['data'])
        else:
            return HttpResponseRedirect(html_response['data'])
Exemplo n.º 39
0
class IdpServer(object):

    ticket = {}
    responses = {}
    challenges = {}
    _binding_mapping = {
        'http-redirect': BINDING_HTTP_REDIRECT,
        'http-post': BINDING_HTTP_POST
    }
    _endpoint_types = ['single_sign_on_service', 'single_logout_service']
    _spid_levels = SPID_LEVELS
    _spid_attributes = SPID_ATTRIBUTES.copy()
    # digitalAddress => PEC
    challenges_timeout = CHALLENGES_TIMEOUT

    def __init__(self, app, conf=None, *args, **kwargs):
        """
        :param app: Flask instance
        :param conf: config.Config instance
        :param args:
        :param kwargs:
        """
        # bind Flask app
        self.app = app
        self.user_manager = JsonUserManager()
        # setup
        self._config = conf or config.params
        self.app.secret_key = 'sosecret'
        handler = RotatingFileHandler(
            'spid.log', maxBytes=500000, backupCount=1
        )
        self.app.logger.addHandler(handler)
        self._prepare_server()

    @property
    def _mode(self):
        return 'https' if self._config.https else 'http'

    def _setup_app_routes(self):
        """
        Setup Flask routes
        """
        # Setup SSO and SLO endpoints
        endpoints = self._config.endpoints
        if endpoints:
            for ep_type in self._endpoint_types:
                _url = endpoints.get(ep_type)
                if _url:
                    for _binding in self._binding_mapping.keys():
                        self.app.add_url_rule(
                            _url,
                            ep_type,
                            getattr(self, ep_type),
                            methods=['GET', 'POST']
                        )
        self.app.add_url_rule('/', 'index', self.index, methods=['GET'])
        self.app.add_url_rule(
            '/login', 'login', self.login, methods=['POST', 'GET']
        )
        # Endpoint for user add action
        self.app.add_url_rule(
            '/users', 'users', self.users, methods=['GET', 'POST']
        )
        self.app.add_url_rule(
            '/continue-response', 'continue_response',
            self.continue_response, methods=['POST']
        )
        self.app.add_url_rule(
            '/metadata', 'metadata', self.metadata, methods=['POST', 'GET']
        )

    def _prepare_server(self):
        """
        Setup server
        """
        # FIXME: remove after pysaml2 drop
        from saml2.config import Config as Saml2Config
        self.idp_config = Saml2Config()
        self.idp_config.load(cnf=self._config.pysaml2compat)
        self.server = Server(config=self.idp_config)
        #
        self._setup_app_routes()

    def _verify_spid(self, level, verify=False, **kwargs):
        """
        :param level: integer, SPID level
        :param verify: boolean, if True verify
            spid extra challenge (otp etc.), if False prepare the challenge
        :param kwargs: dictionary, extra arguments
        """
        level = self._spid_levels.index(level)
        self.app.logger.debug('spid level {} - verifica ({})'.format(level, verify))
        if verify:
            # Verify the challenge
            if level == 2:
                # spid level 2
                otp = kwargs.get('data').get('otp')
                key = kwargs.get('key')
                if key and key not in self.challenges or not otp:
                    return False
                total_seconds = (
                    datetime.now() - self.challenges[key][1]
                ).total_seconds()
                # Check that opt value is equal and not expired
                _is_expired = total_seconds > self.challenges_timeout
                if self.challenges[key][0] != otp or _is_expired:
                    del self.challenges[key]
                    return False
            return True
        else:
            # Prepare the challenge
            if level == 2:
                # spid level 2
                # very simple otp implementation,
                # while opt is a random 6 digits string
                # with a lifetime setup in the server instance
                key = kwargs.get('key')
                otp = ''.join(random.choice(string.digits) for _ in range(6))
                self.challenges[key] = [otp, datetime.now()]
                extra_challenge = '<span>Otp ({})</span>'\
                '<input type="text" name="otp" />'.format(
                    otp
                )
            else:
                extra_challenge = ''
            return extra_challenge

    def unpack_args(self, elems):
        """
        Unpack arguments from request
        """
        return dict([(k, v) for k, v in elems.items()])

    def _raise_error(self, msg, extra=None):
        """
        Raise some error using 'abort' function from Flask

        :param msg: string for error type
        :param extra: optional string for error details
        """

        abort(
            Response(
               render_template(
                    "error.html",
                    **{'msg': msg, 'extra': extra or ""}
                ), 200
            )
        )

    def _store_request(self, authnreq):
        """
        Store authnrequest in a dictionary

        :param authnreq: authentication request string
        """
        self.app.logger.debug('store_request: {}'.format(authnreq))
        # FIXME: improve this
        from lxml.etree import tostring
        key = sha1(tostring(authnreq._xml_doc)).hexdigest()
        # store the AuthnRequest
        self.ticket[key] = authnreq
        return key

    def _handle_errors(self, xmlstr, errors=None):
        rendered_error_response = render_template(
            'spid_error.html',
            **{
                'lines': xmlstr.splitlines(),
                'errors': errors
                }
            )
        return rendered_error_response

    def _parse_message(self, action):
        """
        Parse an AuthnRequest or a LogoutRequest

        :param action: type of request
        """
        method = request.method

        if method == 'GET':
            return self._handle_http_redirect(action)
        elif method == 'POST':
            return self._handle_http_post(action)
        else:
            self._raise_error(
                'I metodi consentiti sono'
                ' GET (Http-Redirect) o POST (Http-Post)'
            )

    def _handle_http_redirect(self, action):
        # FIXME: replace the following code with a call to a function
        # in the parser.py module after metadata refactoring.
        # The IdpServer class should not
        # be responsible of request parsing, or know anything
        # about request parsing *at all*.
        saml_msg = self.unpack_args(request.args)
        request_data = HTTPRedirectRequestParser(saml_msg).parse()
        deserializer = get_http_redirect_request_deserializer(
            request_data, action, self.server.metadata)
        saml_tree = deserializer.deserialize()
        certs = self._get_certificates_by_issuer(saml_tree.issuer.text)
        for cert in certs:
            HTTPRedirectSignatureVerifier(cert, request_data).verify()
        return SPIDRequest(request_data, saml_tree)

    def _handle_http_post(self, action):
        # FIXME: replace the following code with a call to a function
        # in the parser.py module after metadata refactoring.
        # The IdpServer class should not
        # be responsible of request parsing, or know anything
        # about request parsing *at all*.
        saml_msg = self.unpack_args(request.form)
        request_data = HTTPPostRequestParser(saml_msg).parse()
        deserializer = get_http_post_request_deserializer(
            request_data, action, self.server.metadata)
        saml_tree = deserializer.deserialize()
        certs = self._get_certificates_by_issuer(saml_tree.issuer.text)
        for cert in certs:
            HTTPPostSignatureVerifier(cert, request_data).verify()
        return SPIDRequest(request_data, saml_tree)

    def _get_certificates_by_issuer(self, issuer):
        try:
            return self.server.metadata.certs(issuer, 'any', 'signing')
        except KeyError:
            self._raise_error(
                'entity ID {} non registrato, impossibile ricavare'\
                ' un certificato valido.'.format(issuer)
            )

    def single_sign_on_service(self):
        """
        Process Http-Redirect or Http-POST request

        :param request: Flask request object
        """
        try:
            spid_request = self._parse_message(action='login')
            self.app.logger.debug(
                'AuthnRequest: \n{}'.format(spid_request.data.saml_request)
            )
            # Perform login
            key = self._store_request(spid_request.saml_tree)
            session['request_key'] = key
            session['relay_state'] = spid_request.data.relay_state or ''
            return redirect(url_for('login'))
        except RequestParserError as err:
            self._raise_error(err.args[0])
        except SignatureVerificationError as err:
            self._raise_error(err.args[0])
        except UnknownEntityIDError as err:
            self._raise_error(err.args[0])
        except DeserializationError as err:
            return self._handle_errors(err.initial_data, err.details)

    @property
    def _spid_main_fields(self):
        """
        Returns a list of spid main attributes
        """
        return self._spid_attributes['primary'].keys()

    @property
    def _spid_secondary_fields(self):
        """
        Returns a list of spid secondary attributes
        """
        return self._spid_attributes['secondary'].keys()

    @property
    def _all_attributes(self):
        _dct = self._spid_attributes['primary'].copy()
        _dct.update(self._spid_attributes['secondary'])
        return _dct

    def users(self):
        """
        Add user endpoint
        """
        spid_main_fields = self._spid_main_fields
        spid_secondary_fields = self._spid_secondary_fields
        rendered_form = render_template(
            "users.html",
            **{
                'action': '/users',
                'primary_attributes': spid_main_fields,
                'secondary_attributes': spid_secondary_fields,
                'users': self.user_manager.all(),
                'sp_list': self.server.metadata.service_providers()
            }
        )
        if request.method == 'GET':
            return rendered_form, 200
        elif request.method == 'POST':
            username = request.form.get('username')
            password = request.form.get('password')
            sp = request.form.get('service_provider')
            if not sp:
                sp = None
            if not username or not password:
                abort(400)
            extra = {}
            for spid_field in spid_main_fields:
                spid_value = request.form.get(spid_field)
                if spid_value:
                    extra[spid_field] = spid_value
            for spid_field in spid_secondary_fields:
                spid_value = request.form.get(spid_field)
                if spid_value:
                    extra[spid_field] = spid_value
            self.user_manager.add(username, password, sp, extra)
        return redirect(url_for('users'))

    def index(self):
        rendered_form = render_template(
            "home.html",
            **{
                'sp_list': [
                    {
                        "name": sp, "spId": sp
                    } for sp in self.server.metadata.service_providers()
                ],
            }
        )
        return rendered_form, 200

    def get_destination(self, req, sp_id):
        destination = None
        acs_index = getattr(req, 'assertion_consumer_service_index', None)
        protocol_binding = getattr(req, 'protocol_binding', None)
        if acs_index is not None:
            acss = self.server.metadata.assertion_consumer_service(
                sp_id, protocol_binding
            )
            for acs in acss:
                if acs.get('index') == acs_index:
                    destination = acs.get('location')
                    break
            self.app.logger.debug(
                'AssertionConsumerServiceIndex Location: {}'.format(
                    destination
                )
            )
        if destination is None:
            destination = getattr(req, 'assertion_consumer_service_url', None)
            if destination is not None and protocol_binding is not None:
                self.app.logger.debug(
                    'AssertionConsumerServiceURL: {}'.format(
                        destination
                    )
                )
        return destination

    def login(self):
        """
        Login endpoint (verify user credentials)
        """

        key = from_session('request_key')
        relay_state = from_session('relay_state')
        self.app.logger.debug('Request key: {}'.format(key))
        if key and key in self.ticket:
            authn_request = self.ticket[key]
            sp_id = authn_request.issuer.text
            destination = self.get_destination(authn_request, sp_id)
            authn_context = authn_request.requested_authn_context
            spid_level = authn_context.authn_context_class_ref.text
            if request.method == 'GET':
                # inject extra data in form login based on spid level
                extra_challenge = self._verify_spid(level=spid_level, **{'key': key})
                rendered_form = render_template(
                    'login.html',
                    **{
                        'action': url_for('login'),
                        'request_key': key,
                        'relay_state': relay_state,
                        'extra_challenge': extra_challenge
                    }
                )
                return rendered_form, 200

            if 'confirm' in request.form:
                # verify optional challenge based on spid level
                verified = self._verify_spid(
                    level=spid_level,
                    verify=True, **{
                        'key': key, 'data': request.form
                    }
                )
                if verified:
                    # verify user credentials
                    user_id, user = self.user_manager.get(
                        request.form['username'],
                        request.form['password'],
                        sp_id
                    )
                    if user_id is not None:
                        # setup response
                        identity = user['attrs'].copy()
                        self.app.logger.debug(
                            'Unfiltered data: {}'.format(identity)
                        )
                        atcs_idx = getattr(authn_request, 'attribute_consuming_service_index', None)
                        self.app.logger.debug(
                            'AttributeConsumingServiceIndex: {}'.format(
                                atcs_idx
                            )
                        )
                        if atcs_idx:
                            # TODO: Remove this pysaml2 dependency
                            attrs = self.server.wants(sp_id, atcs_idx)
                            required = [el.get('name') for el in attrs.get('required')]
                            optional = [el.get('name') for el in attrs.get('optional')]
                        else:
                            required = []
                            optional = []

                        for k, v in identity.items():
                            if k in self._spid_main_fields:
                                _type = self._spid_attributes['primary'][k]
                            else:
                                _type = self._spid_attributes['secondary'][k]
                            identity[k] = (_type, v)

                        _identity = {}
                        for _key in required:
                            _identity[_key] = identity[_key]
                        for _key in optional:
                            _identity[_key] = identity[_key]

                        self.app.logger.debug(
                            'Filtered data: {}'.format(_identity)
                        )

                        response_xmlstr = create_response(
                            {
                                'response': {
                                    'attrs': {
                                        'in_response_to': authn_request.id,
                                        'destination': destination
                                    }
                                },
                                'issuer': {
                                    'attrs': {
                                        'name_qualifier': self._config.entity_id,
                                    },
                                    'text': self._config.entity_id
                                },
                                'name_id': {
                                    'attrs': {
                                        'name_qualifier': self._config.entity_id,
                                    }
                                },

                                'subject_confirmation_data': {
                                    'attrs': {
                                        'recipient': destination
                                    }
                                },
                                'audience': {
                                    'text': sp_id
                                },
                                'authn_context_class_ref': {
                                    'text': spid_level
                                }
                            },
                            {
                                'status_code': STATUS_SUCCESS
                            },
                            _identity.copy()
                        ).to_xml()
                        response = sign_http_post(
                            response_xmlstr,
                            self._config.idp_key,
                            self._config.idp_certificate,
                        )
                        self.app.logger.debug(
                            'Response: \n{}'.format(response)
                        )
                        rendered_template = render_template(
                            'form_http_post.html',
                            **{
                                'action': destination,
                                'relay_state': relay_state,
                                'message': response,
                                'message_type': 'SAMLResponse'
                            }
                        )
                        self.responses[key] = rendered_template
                        # Setup confirmation page data
                        rendered_response = render_template(
                            'confirm.html',
                            **{
                                'destination_service': sp_id,
                                'lines': escape(
                                    response_xmlstr.decode('ascii')
                                ).splitlines(),
                                'attrs': _identity.keys(),
                                'action': '/continue-response',
                                'request_key': key
                            }
                        )
                        return rendered_response, 200
            elif 'delete' in request.form:
                error_info = get_spid_error(
                    AUTH_NO_CONSENT
                )
                response = create_error_response(
                    {
                        'response': {
                            'attrs': {
                                'in_response_to': authn_request.id,
                                'destination': destination
                            }
                        },
                        'issuer': {
                            'attrs': {
                                'name_qualifier': self._config.entity_id,
                            },
                            'text': self._config.entity_id
                        },
                    },
                    {
                        'status_code': error_info[0],
                        'status_message': error_info[1]
                    }
                ).to_xml()
                self.app.logger.debug(
                    'Error response: \n{}'.format(response)
                )
                response = sign_http_post(
                    response,
                    self._config.idp_key,
                    self._config.idp_certificate,
                )
                del self.ticket[key]
                rendered_template = render_template(
                    'form_http_post.html',
                    **{
                        'action': destination,
                        'relay_state': relay_state,
                        'message': response,
                        'message_type': 'SAMLResponse'
                    }
                )
                return rendered_template, 200
        return render_template('403.html'), 403

    def continue_response(self):
        key = request.form['request_key']
        relay_state = from_session('relay_state')
        if key and key in self.responses:
            _response = self.responses.pop(key)
            auth_req = self.ticket.pop(key)
            if 'confirm' in request.form:
                return _response, 200
            elif 'delete' in request.form:
                destination = self.get_destination(
                    auth_req, auth_req.issuer.text
                )
                error_info = get_spid_error(
                        AUTH_NO_CONSENT
                    )
                response = create_error_response(
                    {
                        'response': {
                            'attrs': {
                                'in_response_to': auth_req.id,
                                'destination': destination
                            }
                        },
                        'issuer': {
                            'attrs': {
                                'name_qualifier': 'something',
                            },
                            'text': self._config.entity_id
                        },
                    },
                    {
                        'status_code': error_info[0],
                        'status_message': error_info[1]
                    }
                ).to_xml()
                self.app.logger.debug(
                    'Error response: \n{}'.format(response)
                )
                response = sign_http_post(
                    response,
                    self._config.idp_key,
                    self._config.idp_certificate,
                )
                rendered_template = render_template(
                    'form_http_post.html',
                    **{
                        'action': destination,
                        'relay_state': relay_state,
                        'message': response,
                        'message_type': 'SAMLResponse'
                    }
                )
                return rendered_template, 200
        return render_template('403.html'), 403

    def _sp_single_logout_service(self, issuer_name):
        _slo = None
        for binding in [BINDING_HTTP_POST, BINDING_HTTP_REDIRECT]:
            try:
                _slo = self.server.metadata.single_logout_service(
                    issuer_name, binding=binding, typ='spsso'
                )
            except UnsupportedBinding:
                pass
        return _slo

    def single_logout_service(self):
        """
        SLO endpoint
        """

        self.app.logger.debug("req: '%s'", request)
        try:
            spid_request = self._parse_message(action='logout')
            issuer_name = spid_request.saml_tree.issuer.text
            # TODO: retrieve the following data from some custom structure
            _slo = self._sp_single_logout_service(issuer_name)
            if _slo is None:
                self._raise_error(
                    'Impossibile trovare un servizio di'\
                    ' Single Logout per il service provider {}'.format(
                        issuer_name
                    )
                )
            response_binding = _slo[0].get('binding')
            self.app.logger.debug(
                'Response binding: \n{}'.format(
                    response_binding
                )
            )
            destination = _slo[0].get('location')
            response = create_logout_response(
                {
                    'logout_response': {
                        'attrs': {
                            'in_response_to': spid_request.saml_tree.id,
                            'destination': destination
                        }
                    },
                    'issuer': {
                        'attrs': {
                            'name_qualifier': 'something',
                        },
                        'text': self._config.entity_id
                    }
                },
                {
                    'status_code': STATUS_SUCCESS
                }
            ).to_xml()
            relay_state = spid_request.data.relay_state or ''
            if response_binding == BINDING_HTTP_POST:
                response = sign_http_post(
                    response,
                    self._config.idp_key,
                    self._config.idp_certificate,
                    message=True, assertion=False
                )
                rendered_template = render_template(
                    'form_http_post.html',
                    **{
                        'action': destination,
                        'relay_state': relay_state,
                        'message': response,
                        'message_type': 'SAMLResponse'
                    }
                )
                return rendered_template, 200
            elif response_binding == BINDING_HTTP_REDIRECT:
                query_string = sign_http_redirect(
                    response,
                    self._config.idp_key,
                    relay_state,
                )
                location = '{}?{}'.format(destination, query_string)
                if location:
                    return redirect(location)
        except RequestParserError as err:
            self._raise_error(err.args[0])
        except SignatureVerificationError as err:
            self._raise_error(err.args[0])
        except UnknownEntityIDError as err:
            self._raise_error(err.args[0])
        except DeserializationError as err:
            return self._handle_errors(err.initial_data, err.details)
        abort(400)

    def metadata(self):
        cert_file = self.server.config.cert_file
        with open(cert_file, 'r') as fp:
            cert = fp.readlines()[1:-1]
            cert = ''.join(cert)
        endpoints = getattr(self.server.config, '_idp_endpoints')
        sso = endpoints.get('single_sign_on_service')
        slo = endpoints.get('single_logout_service')
        sso = [Sso(*_sso) for _sso in sso]
        slo = [Slo(*_slo) for _slo in slo]
        metadata = create_idp_metadata(
            entity_id=self.server.config.entityid,
            want_authn_requests_signed='true',
            keys=[Key(use='signing', value=cert)],
            single_sign_on_services=sso,
            single_logout_services=slo
        ).to_xml()
        return Response(metadata, mimetype='text/xml')

    @property
    def _wsgiconf(self):
        _cnf = {
            'host': self._config.host,
            'port': self._config.port,
            'debug': self._config.debug,
        }
        if self._config.https:
            key = self._config.https_key_file_path
            cert = self._config.https_certificate_file_path
            _cnf['ssl_context'] = (cert, key,)
        return _cnf

    def start(self):
        """
        Start the server instance
        """
        self.app.run(
            **self._wsgiconf
        )
Exemplo n.º 40
0
class TestClient:
    def setup_class(self):
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = Saml2Client(conf)

    def test_create_attribute_query1(self):
        req = self.client.create_attribute_query(
            "id1",
            "E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
            "https://idp.example.com/idp/",
            nameid_format=saml.NAMEID_FORMAT_PERSISTENT)
        reqstr = "%s" % req.to_string()

        assert req.destination == "https://idp.example.com/idp/"
        assert req.id == "id1"
        assert req.version == "2.0"
        subject = req.subject
        name_id = subject.name_id
        assert name_id.format == saml.NAMEID_FORMAT_PERSISTENT
        assert name_id.text == "E8042FB4-4D5B-48C3-8E14-8EDD852790DD"
        issuer = req.issuer
        assert issuer.text == "urn:mace:example.com:saml:roland:sp"

        attrq = samlp.attribute_query_from_string(reqstr)

        print attrq.keyswv()
        assert _leq(attrq.keyswv(), [
            'destination', 'subject', 'issue_instant', 'version', 'id',
            'issuer'
        ])

        assert attrq.destination == req.destination
        assert attrq.id == req.id
        assert attrq.version == req.version
        assert attrq.issuer.text == issuer.text
        assert attrq.issue_instant == req.issue_instant
        assert attrq.subject.name_id.format == name_id.format
        assert attrq.subject.name_id.text == name_id.text

    def test_create_attribute_query2(self):
        req = self.client.create_attribute_query(
            "id1",
            "E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
            "https://idp.example.com/idp/",
            attribute={
                ("urn:oid:2.5.4.42", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "givenName"):
                None,
                ("urn:oid:2.5.4.4", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "surname"):
                None,
                ("urn:oid:1.2.840.113549.1.9.1", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"):
                None,
            },
            nameid_format=saml.NAMEID_FORMAT_PERSISTENT)

        print req.to_string()
        assert req.destination == "https://idp.example.com/idp/"
        assert req.id == "id1"
        assert req.version == "2.0"
        subject = req.subject
        name_id = subject.name_id
        assert name_id.format == saml.NAMEID_FORMAT_PERSISTENT
        assert name_id.text == "E8042FB4-4D5B-48C3-8E14-8EDD852790DD"
        assert len(req.attribute) == 3
        # one is givenName
        seen = []
        for attribute in req.attribute:
            if attribute.name == "urn:oid:2.5.4.42":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                assert attribute.friendly_name == "givenName"
                seen.append("givenName")
            elif attribute.name == "urn:oid:2.5.4.4":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                assert attribute.friendly_name == "surname"
                seen.append("surname")
            elif attribute.name == "urn:oid:1.2.840.113549.1.9.1":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                if getattr(attribute, "friendly_name"):
                    assert False
                seen.append("email")
        assert set(seen) == set(["givenName", "surname", "email"])

    def test_create_attribute_query_3(self):
        req = self.client.create_attribute_query(
            "id1",
            "_e7b68a04488f715cda642fbdd90099f5",
            "https://aai-demo-idp.switch.ch/idp/shibboleth",
            nameid_format=saml.NAMEID_FORMAT_TRANSIENT)

        assert isinstance(req, samlp.AttributeQuery)
        assert req.destination == "https://aai-demo-idp.switch.ch/idp/shibboleth"
        assert req.id == "id1"
        assert req.version == "2.0"
        assert req.issue_instant
        assert req.issuer.text == "urn:mace:example.com:saml:roland:sp"
        nameid = req.subject.name_id
        assert nameid.format == saml.NAMEID_FORMAT_TRANSIENT
        assert nameid.text == "_e7b68a04488f715cda642fbdd90099f5"

    def test_attribute_query(self):
        req = self.client.attribute_query(
            "_e7b68a04488f715cda642fbdd90099f5",
            "https://aai-demo-idp.switch.ch/idp/shibboleth",
            nameid_format=saml.NAMEID_FORMAT_TRANSIENT)

        # since no one is answering on the other end
        assert req is None

    # def test_idp_entry(self):
    #     idp_entry = self.client.idp_entry(name="Umeå Universitet",
    #                         location="https://idp.umu.se/")
    #
    #     assert idp_entry.name == "Umeå Universitet"
    #     assert idp_entry.loc == "https://idp.umu.se/"
    #
    # def test_scope(self):
    #     entity_id = "urn:mace:example.com:saml:roland:idp"
    #     locs = self.client.metadata.single_sign_on_services(entity_id)
    #     scope = self.client.scoping_from_metadata(entity_id, locs)
    #
    #     assert scope.idp_list
    #     assert len(scope.idp_list.idp_entry) == 1
    #     idp_entry = scope.idp_list.idp_entry[0]
    #     assert idp_entry.name == 'Exempel AB'
    #     assert idp_entry.loc == ['http://*****:*****@nyy.mlb.com"]
        }

        resp_str = "\n".join(
            self.server.authn_response(
                identity=ava,
                in_response_to="id1",
                destination="http://lingon.catalogix.se:8087/",
                sp_entity_id="urn:mace:example.com:saml:roland:sp",
                name_id_policy=samlp.NameIDPolicy(
                    format=saml.NAMEID_FORMAT_PERSISTENT),
                userid="*****@*****.**"))

        resp_str = base64.encodestring(resp_str)

        authn_response = self.client.response(
            {"SAMLResponse": resp_str},
            {"id1": "http://foo.example.com/service"})

        assert authn_response is not None
        assert authn_response.issuer() == IDP
        assert authn_response.response.assertion[0].issuer.text == IDP
        session_info = authn_response.session_info()

        print session_info
        assert session_info["ava"] == {
            'mail': ['*****@*****.**'],
            'givenName': ['Derek'],
            'sn': ['Jeter']
        }
        assert session_info["issuer"] == IDP
        assert session_info["came_from"] == "http://foo.example.com/service"
        response = samlp.response_from_string(authn_response.xmlstr)
        assert response.destination == "http://lingon.catalogix.se:8087/"

        # One person in the cache
        assert len(self.client.users.subjects()) == 1
        subject_id = self.client.users.subjects()[0]
        print "||||", self.client.users.get_info_from(subject_id, IDP)
        # The information I have about the subject comes from one source
        assert self.client.users.issuers_of_info(subject_id) == [IDP]

        # --- authenticate another person

        ava = {
            "givenName": ["Alfonson"],
            "surname": ["Soriano"],
            "mail": ["*****@*****.**"]
        }

        resp_str = "\n".join(
            self.server.authn_response(
                identity=ava,
                in_response_to="id2",
                destination="http://lingon.catalogix.se:8087/",
                sp_entity_id="urn:mace:example.com:saml:roland:sp",
                name_id_policy=samlp.NameIDPolicy(
                    format=saml.NAMEID_FORMAT_PERSISTENT),
                userid="*****@*****.**"))

        resp_str = base64.encodestring(resp_str)

        self.client.response({"SAMLResponse": resp_str},
                             {"id2": "http://foo.example.com/service"})

        # Two persons in the cache
        assert len(self.client.users.subjects()) == 2
        issuers = [
            self.client.users.issuers_of_info(s)
            for s in self.client.users.subjects()
        ]
        # The information I have about the subjects comes from the same source
        print issuers
        assert issuers == [[IDP], [IDP]]

    def test_init_values(self):
        entityid = self.client.config.entityid
        print entityid
        assert entityid == "urn:mace:example.com:saml:roland:sp"
        print self.client.config.idp
        print self.client.config.metadata.idps()
        print self.client.config.idps()
        location = self.client._sso_location()
        print location
        assert location == 'http://*****:*****@example.com"
            }
        }
        self.client.users.add_information_about_person(session_info)
        entity_ids = self.client.users.issuers_of_info("123456")
        assert entity_ids == ["urn:mace:example.com:saml:roland:idp"]
        resp = self.client.global_logout("123456", "Tired",
                                         in_a_while(minutes=5))
        print resp
        assert resp
        assert resp[0]  # a session_id
        assert resp[1] == '200 OK'
        assert resp[2] == [('Content-type', 'text/html')]
        assert resp[3][0] == '<head>'
        assert resp[3][1] == '<title>SAML 2.0 POST</title>'
        session_info = self.client.state[resp[0]]
        print session_info
        assert session_info["entity_id"] == entity_ids[0]
        assert session_info["subject_id"] == "123456"
        assert session_info["reason"] == "Tired"
        assert session_info["operation"] == "SLO"
        assert session_info["entity_ids"] == entity_ids
        assert session_info["sign"] == False

    def test_logout_2(self):
        """ one IdP/AA with BINDING_SOAP, can't actually send something"""

        conf = config.SPConfig()
        conf.load_file("server2_conf")
        client = Saml2Client(conf)

        # information about the user from an IdP
        session_info = {
            "name_id": "123456",
            "issuer": "urn:mace:example.com:saml:roland:idp",
            "not_on_or_after": in_a_while(minutes=15),
            "ava": {
                "givenName": "Anders",
                "surName": "Andersson",
                "mail": "*****@*****.**"
            }
        }
        client.users.add_information_about_person(session_info)
        entity_ids = self.client.users.issuers_of_info("123456")
        assert entity_ids == ["urn:mace:example.com:saml:roland:idp"]
        destinations = client.config.single_logout_services(
            entity_ids[0], BINDING_SOAP)
        print destinations
        assert destinations == ['http://*****:*****@example.com"
            }
        }
        client.users.add_information_about_person(session_info_authn)
        session_info_aa = {
            "name_id": "123456",
            "issuer": "urn:mace:example.com:saml:roland:aa",
            "not_on_or_after": in_a_while(minutes=15),
            "ava": {
                "eduPersonEntitlement": "Foobar",
            }
        }
        client.users.add_information_about_person(session_info_aa)
        entity_ids = client.users.issuers_of_info("123456")
        assert _leq(entity_ids, [
            "urn:mace:example.com:saml:roland:idp",
            "urn:mace:example.com:saml:roland:aa"
        ])
        resp = client.global_logout("123456", "Tired", in_a_while(minutes=5))
        print resp
        assert resp
        assert resp[0]  # a session_id
        assert resp[1] == '200 OK'
        # HTTP POST
        assert resp[2] == [('Content-type', 'text/html')]
        assert resp[3][0] == '<head>'
        assert resp[3][1] == '<title>SAML 2.0 POST</title>'

        state_info = client.state[resp[0]]
        print state_info
        assert state_info["entity_id"] == entity_ids[0]
        assert state_info["subject_id"] == "123456"
        assert state_info["reason"] == "Tired"
        assert state_info["operation"] == "SLO"
        assert state_info["entity_ids"] == entity_ids
        assert state_info["sign"] == False

    def test_authz_decision_query(self):
        conf = config.SPConfig()
        conf.load_file("server3_conf")
        client = Saml2Client(conf)

        AVA = {
            'mail': u'*****@*****.**',
            'eduPersonTargetedID': '95e9ae91dbe62d35198fbbd5e1fb0976',
            'displayName': u'Roland Hedberg',
            'uid': 'http://roland.hedberg.myopenid.com/'
        }

        sp_entity_id = "sp_entity_id"
        in_response_to = "1234"
        consumer_url = "http://example.com/consumer"
        name_id = saml.NameID(saml.NAMEID_FORMAT_TRANSIENT, text="name_id")
        policy = Policy()
        ava = Assertion(AVA)
        assertion = ava.construct(sp_entity_id,
                                  in_response_to,
                                  consumer_url,
                                  name_id,
                                  conf.attribute_converters,
                                  policy,
                                  issuer=client._issuer())

        adq = client.authz_decision_query_using_assertion(
            "entity_id", assertion, "read", "http://example.com/text")

        assert adq
        print adq
        assert adq.keyswv() != []
        assert adq.destination == "entity_id"
        assert adq.resource == "http://example.com/text"
        assert adq.action[0].text == "read"

    def test_request_to_discovery_service(self):
        disc_url = "http://example.com/saml2/idp/disc"
        url = self.client.discovery_service_request_url(disc_url)
        print url
        assert url == "http://example.com/saml2/idp/disc?entityID=urn%3Amace%3Aexample.com%3Asaml%3Aroland%3Asp"

        url = self.client.discovery_service_request_url(
            disc_url, return_url="http://example.org/saml2/sp/ds")

        print url
        assert url == "http://example.com/saml2/idp/disc?entityID=urn%3Amace%3Aexample.com%3Asaml%3Aroland%3Asp&return=http%3A%2F%2Fexample.org%2Fsaml2%2Fsp%2Fds"

    def test_get_idp_from_discovery_service(self):
        pdir = {"entityID": "http://example.org/saml2/idp/sso"}
        params = urllib.urlencode(pdir)
        redirect_url = "http://example.com/saml2/sp/disc?%s" % params

        entity_id = self.client.discovery_service_response(url=redirect_url)
        assert entity_id == "http://example.org/saml2/idp/sso"

        pdir = {"idpID": "http://example.org/saml2/idp/sso"}
        params = urllib.urlencode(pdir)
        redirect_url = "http://example.com/saml2/sp/disc?%s" % params

        entity_id = self.client.discovery_service_response(
            url=redirect_url, returnIDParam="idpID")

        assert entity_id == "http://example.org/saml2/idp/sso"

    def test_unsolicited_response(self):
        """

        """
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = Saml2Client(conf)

        for subject in self.client.users.subjects():
            self.client.users.remove_person(subject)

        IDP = "urn:mace:example.com:saml:roland:idp"

        ava = {
            "givenName": ["Derek"],
            "surname": ["Jeter"],
            "mail": ["*****@*****.**"]
        }

        resp_str = "\n".join(
            self.server.authn_response(
                identity=ava,
                in_response_to="id1",
                destination="http://lingon.catalogix.se:8087/",
                sp_entity_id="urn:mace:example.com:saml:roland:sp",
                name_id_policy=samlp.NameIDPolicy(
                    format=saml.NAMEID_FORMAT_PERSISTENT),
                userid="*****@*****.**"))

        resp_str = base64.encodestring(resp_str)

        self.client.allow_unsolicited = True
        authn_response = self.client.response({"SAMLResponse": resp_str}, ())

        assert authn_response is not None
        assert authn_response.issuer() == IDP
        assert authn_response.response.assertion[0].issuer.text == IDP
        session_info = authn_response.session_info()

        print session_info
        assert session_info["ava"] == {
            'mail': ['*****@*****.**'],
            'givenName': ['Derek'],
            'sn': ['Jeter']
        }
        assert session_info["issuer"] == IDP
        assert session_info["came_from"] == ""
        response = samlp.response_from_string(authn_response.xmlstr)
        assert response.destination == "http://lingon.catalogix.se:8087/"

        # One person in the cache
        assert len(self.client.users.subjects()) == 1
Exemplo n.º 41
0
    def test_encrypted_response_8(self):
        try:
            _resp = self.server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=True,
                encrypt_assertion_self_contained=True,
                pefim=True,
                encrypt_cert_advice="whatever",
                encrypt_cert_assertion="whatever")
            assert False, "Must throw an exception"
        except EncryptError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        try:
            _resp = self.server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=False,
                encrypt_assertion_self_contained=True,
                pefim=True,
                encrypt_cert_advice="whatever",
            )
            assert False, "Must throw an exception"
        except EncryptError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        try:
            _resp = self.server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=True,
                encrypt_assertion_self_contained=True,
                encrypted_advice_attributes=False,
                encrypt_cert_assertion="whatever")
            assert False, "Must throw an exception"
        except EncryptError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        _server = Server("idp_conf_verify_cert")

        try:
            _resp = _server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=True,
                encrypt_assertion_self_contained=True,
                pefim=True,
                encrypt_cert_advice="whatever",
                encrypt_cert_assertion="whatever")
            assert False, "Must throw an exception"
        except CertificateError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        try:
            _resp = _server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=False,
                encrypt_assertion_self_contained=True,
                pefim=True,
                encrypt_cert_advice="whatever",
            )
            assert False, "Must throw an exception"
        except CertificateError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"

        try:
            _resp = _server.create_authn_response(
                self.ava,
                "id12",  # in_response_to
                "http://lingon.catalogix.se:8087/",  # consumer_url
                "urn:mace:example.com:saml:roland:sp",  # sp_entity_id
                name_id=self.name_id,
                sign_response=False,
                sign_assertion=False,
                encrypt_assertion=True,
                encrypt_assertion_self_contained=True,
                encrypted_advice_attributes=False,
                encrypt_cert_assertion="whatever")
            assert False, "Must throw an exception"
        except CertificateError as ex:
            pass
        except Exception as ex:
            assert False, "Wrong exception!"
Exemplo n.º 42
0
 def setup_class(self):
     self.sp = make_plugin("rem", saml_conf="server_conf")
     self.server = Server(config_file="idp_conf")
Exemplo n.º 43
0
from saml2.pack import http_redirect_message
from saml2.sigver import verify_redirect_signature
from saml2.sigver import import_rsa_key_from_file
from saml2.sigver import SIG_RSA_SHA1
from saml2.server import Server
from saml2 import BINDING_HTTP_REDIRECT
from saml2.client import Saml2Client
from saml2.config import SPConfig
from urlparse import parse_qs

from pathutils import dotname

__author__ = 'rolandh'

idp = Server(config_file=dotname("idp_all_conf"))

conf = SPConfig()
conf.load_file(dotname("servera_conf"))
sp = Saml2Client(conf)


def test():
    srvs = sp.metadata.single_sign_on_service(idp.config.entityid,
                                              BINDING_HTTP_REDIRECT)

    destination = srvs[0]["location"]
    req_id, req = sp.create_authn_request(destination, id="id1")

    try:
        key = sp.sec.key
    except AttributeError:
Exemplo n.º 44
0
class TestClient:
    def setup_class(self):
        self.server = Server("idp_conf")

        conf = config.SPConfig()
        conf.load_file("server_conf")
        self.client = Saml2Client(conf)

    def test_create_attribute_query1(self):
        req_id, req = self.client.create_attribute_query(
            "https://idp.example.com/idp/",
            "E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
            format=saml.NAMEID_FORMAT_PERSISTENT,
            message_id="id1")
        reqstr = "%s" % req.to_string()

        assert req.destination == "https://idp.example.com/idp/"
        assert req.id == "id1"
        assert req.version == "2.0"
        subject = req.subject
        name_id = subject.name_id
        assert name_id.format == saml.NAMEID_FORMAT_PERSISTENT
        assert name_id.text == "E8042FB4-4D5B-48C3-8E14-8EDD852790DD"
        issuer = req.issuer
        assert issuer.text == "urn:mace:example.com:saml:roland:sp"

        attrq = samlp.attribute_query_from_string(reqstr)

        print attrq.keyswv()
        assert _leq(attrq.keyswv(), [
            'destination', 'subject', 'issue_instant', 'version', 'id',
            'issuer'
        ])

        assert attrq.destination == req.destination
        assert attrq.id == req.id
        assert attrq.version == req.version
        assert attrq.issuer.text == issuer.text
        assert attrq.issue_instant == req.issue_instant
        assert attrq.subject.name_id.format == name_id.format
        assert attrq.subject.name_id.text == name_id.text

    def test_create_attribute_query2(self):
        req_id, req = self.client.create_attribute_query(
            "https://idp.example.com/idp/",
            "E8042FB4-4D5B-48C3-8E14-8EDD852790DD",
            attribute={
                ("urn:oid:2.5.4.42", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "givenName"):
                None,
                ("urn:oid:2.5.4.4", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", "surname"):
                None,
                ("urn:oid:1.2.840.113549.1.9.1", "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"):
                None,
            },
            format=saml.NAMEID_FORMAT_PERSISTENT,
            message_id="id1")

        print req.to_string()
        assert req.destination == "https://idp.example.com/idp/"
        assert req.id == "id1"
        assert req.version == "2.0"
        subject = req.subject
        name_id = subject.name_id
        assert name_id.format == saml.NAMEID_FORMAT_PERSISTENT
        assert name_id.text == "E8042FB4-4D5B-48C3-8E14-8EDD852790DD"
        assert len(req.attribute) == 3
        # one is givenName
        seen = []
        for attribute in req.attribute:
            if attribute.name == "urn:oid:2.5.4.42":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                assert attribute.friendly_name == "givenName"
                seen.append("givenName")
            elif attribute.name == "urn:oid:2.5.4.4":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                assert attribute.friendly_name == "surname"
                seen.append("surname")
            elif attribute.name == "urn:oid:1.2.840.113549.1.9.1":
                assert attribute.name_format == saml.NAME_FORMAT_URI
                if getattr(attribute, "friendly_name"):
                    assert False
                seen.append("email")
        assert _leq(seen, ["givenName", "surname", "email"])

    def test_create_attribute_query_3(self):
        req_id, req = self.client.create_attribute_query(
            "https://aai-demo-idp.switch.ch/idp/shibboleth",
            "_e7b68a04488f715cda642fbdd90099f5",
            format=saml.NAMEID_FORMAT_TRANSIENT,
            message_id="id1")

        assert isinstance(req, samlp.AttributeQuery)
        assert req.destination == "https://aai-demo-idp.switch" \
                                  ".ch/idp/shibboleth"
        assert req.id == "id1"
        assert req.version == "2.0"
        assert req.issue_instant
        assert req.issuer.text == "urn:mace:example.com:saml:roland:sp"
        nameid = req.subject.name_id
        assert nameid.format == saml.NAMEID_FORMAT_TRANSIENT
        assert nameid.text == "_e7b68a04488f715cda642fbdd90099f5"

    def test_create_auth_request_0(self):
        ar_str = "%s" % self.client.create_authn_request(
            "http://www.example.com/sso", message_id="id1")[1]

        ar = samlp.authn_request_from_string(ar_str)
        print ar
        assert ar.assertion_consumer_service_url == ("http://lingon.catalogix"
                                                     ".se:8087/")
        assert ar.destination == "http://www.example.com/sso"
        assert ar.protocol_binding == BINDING_HTTP_POST
        assert ar.version == "2.0"
        assert ar.provider_name == "urn:mace:example.com:saml:roland:sp"
        assert ar.issuer.text == "urn:mace:example.com:saml:roland:sp"
        nid_policy = ar.name_id_policy
        assert nid_policy.allow_create == "false"
        assert nid_policy.format == saml.NAMEID_FORMAT_TRANSIENT

    def test_create_auth_request_vo(self):
        assert self.client.config.vorg.keys() == [
            "urn:mace:example.com:it:tek"
        ]

        ar_str = "%s" % self.client.create_authn_request(
            "http://www.example.com/sso",
            "urn:mace:example.com:it:tek",  # vo
            nameid_format=NAMEID_FORMAT_PERSISTENT,
            message_id="666")[1]

        ar = samlp.authn_request_from_string(ar_str)
        print ar
        assert ar.id == "666"
        assert ar.assertion_consumer_service_url == "http://lingon.catalogix" \
                                                    ".se:8087/"
        assert ar.destination == "http://www.example.com/sso"
        assert ar.protocol_binding == BINDING_HTTP_POST
        assert ar.version == "2.0"
        assert ar.provider_name == "urn:mace:example.com:saml:roland:sp"
        assert ar.issuer.text == "urn:mace:example.com:saml:roland:sp"
        nid_policy = ar.name_id_policy
        assert nid_policy.allow_create == "false"
        assert nid_policy.format == saml.NAMEID_FORMAT_PERSISTENT
        assert nid_policy.sp_name_qualifier == "urn:mace:example.com:it:tek"

    def test_sign_auth_request_0(self):
        #print self.client.config

        req_id, areq = self.client.create_authn_request(
            "http://www.example.com/sso", sign=True, message_id="id1")

        ar_str = "%s" % areq
        ar = samlp.authn_request_from_string(ar_str)

        assert ar
        assert ar.signature
        assert ar.signature.signature_value
        signed_info = ar.signature.signed_info
        #print signed_info
        assert len(signed_info.reference) == 1
        assert signed_info.reference[0].uri == "#id1"
        assert signed_info.reference[0].digest_value
        print "------------------------------------------------"
        try:
            assert self.client.sec.correctly_signed_authn_request(
                ar_str, self.client.config.xmlsec_binary,
                self.client.config.metadata)
        except Exception:  # missing certificate
            self.client.sec.verify_signature(ar_str, node_name=class_name(ar))

    def test_response(self):
        IDP = "urn:mace:example.com:saml:roland:idp"

        ava = {
            "givenName": ["Derek"],
            "surName": ["Jeter"],
            "mail": ["*****@*****.**"],
            "title": ["The man"]
        }

        nameid_policy = samlp.NameIDPolicy(
            allow_create="false", format=saml.NAMEID_FORMAT_PERSISTENT)

        resp = self.server.create_authn_response(
            identity=ava,
            in_response_to="id1",
            destination="http://lingon.catalogix.se:8087/",
            sp_entity_id="urn:mace:example.com:saml:roland:sp",
            name_id_policy=nameid_policy,
            userid="*****@*****.**",
            authn=AUTHN)

        resp_str = "%s" % resp

        resp_str = base64.encodestring(resp_str)

        authn_response = self.client.parse_authn_request_response(
            resp_str, BINDING_HTTP_POST,
            {"id1": "http://foo.example.com/service"})

        assert authn_response is not None
        assert authn_response.issuer() == IDP
        assert authn_response.response.assertion[0].issuer.text == IDP
        session_info = authn_response.session_info()

        print session_info
        assert session_info["ava"] == {
            'mail': ['*****@*****.**'],
            'givenName': ['Derek'],
            'sn': ['Jeter'],
            'title': ["The man"]
        }
        assert session_info["issuer"] == IDP
        assert session_info["came_from"] == "http://foo.example.com/service"
        response = samlp.response_from_string(authn_response.xmlstr)
        assert response.destination == "http://lingon.catalogix.se:8087/"

        # One person in the cache
        assert len(self.client.users.subjects()) == 1
        subject_id = self.client.users.subjects()[0]
        print "||||", self.client.users.get_info_from(subject_id, IDP)
        # The information I have about the subject comes from one source
        assert self.client.users.issuers_of_info(subject_id) == [IDP]

        # --- authenticate another person

        ava = {
            "givenName": ["Alfonson"],
            "surName": ["Soriano"],
            "mail": ["*****@*****.**"],
            "title": ["outfielder"]
        }

        resp_str = "%s" % self.server.create_authn_response(
            identity=ava,
            in_response_to="id2",
            destination="http://lingon.catalogix.se:8087/",
            sp_entity_id="urn:mace:example.com:saml:roland:sp",
            name_id_policy=nameid_policy,
            userid="*****@*****.**",
            authn=AUTHN)

        resp_str = base64.encodestring(resp_str)

        self.client.parse_authn_request_response(
            resp_str, BINDING_HTTP_POST,
            {"id2": "http://foo.example.com/service"})

        # Two persons in the cache
        assert len(self.client.users.subjects()) == 2
        issuers = [
            self.client.users.issuers_of_info(s)
            for s in self.client.users.subjects()
        ]
        # The information I have about the subjects comes from the same source
        print issuers
        assert issuers == [[IDP], [IDP]]

    def test_init_values(self):
        entityid = self.client.config.entityid
        print entityid
        assert entityid == "urn:mace:example.com:saml:roland:sp"
        print self.client.metadata.with_descriptor("idpsso")
        location = self.client._sso_location()
        print location
        assert location == 'http://localhost:8088/sso'
        my_name = self.client._my_name()
        print my_name
        assert my_name == "urn:mace:example.com:saml:roland:sp"

    def test_sign_then_encrypt_assertion(self):
        # Begin with the IdPs side
        _sec = self.server.sec

        assertion = s_utils.assertion_factory(
            subject=factory(saml.Subject,
                            text="_aaa",
                            name_id=factory(
                                saml.NameID,
                                format=saml.NAMEID_FORMAT_TRANSIENT)),
            attribute_statement=do_attribute_statement({
                ("", "", "surName"): ("Jeter", ""),
                ("", "", "givenName"): ("Derek", ""),
            }),
            issuer=self.server._issuer(),
        )

        assertion.signature = sigver.pre_signature_part(
            assertion.id, _sec.my_cert, 1)

        sigass = _sec.sign_statement(assertion,
                                     class_name(assertion),
                                     key_file="pki/mykey.pem",
                                     node_id=assertion.id)
        # Create an Assertion instance from the signed assertion
        _ass = saml.assertion_from_string(sigass)

        response = sigver.response_factory(
            in_response_to="_012345",
            destination="https:#www.example.com",
            status=s_utils.success_status_factory(),
            issuer=self.server._issuer(),
            assertion=_ass)

        enctext = _sec.crypto.encrypt_assertion(response, _sec.cert_file,
                                                pre_encryption_part())

        seresp = samlp.response_from_string(enctext)

        # Now over to the client side
        _csec = self.client.sec
        if seresp.encrypted_assertion:
            decr_text = _csec.decrypt(enctext)
            seresp = samlp.response_from_string(decr_text)
            resp_ass = []

            sign_cert_file = "pki/mycert.pem"
            for enc_ass in seresp.encrypted_assertion:
                assers = extension_elements_to_elements(
                    enc_ass.extension_elements, [saml, samlp])
                for ass in assers:
                    if ass.signature:
                        if not _csec.verify_signature(
                                "%s" % ass,
                                sign_cert_file,
                                node_name=class_name(ass)):
                            continue
                    resp_ass.append(ass)

            seresp.assertion = resp_ass
            seresp.encrypted_assertion = None
            #print _sresp

        assert seresp.assertion

    def test_sign_then_encrypt_assertion2(self):
        # Begin with the IdPs side
        _sec = self.server.sec

        nameid_policy = samlp.NameIDPolicy(
            allow_create="false", format=saml.NAMEID_FORMAT_PERSISTENT)

        asser = Assertion({"givenName": "Derek", "surName": "Jeter"})
        assertion = asser.construct(
            self.client.config.entityid,
            "_012345",
            "http://lingon.catalogix.se:8087/",
            factory(saml.NameID, format=saml.NAMEID_FORMAT_TRANSIENT),
            policy=self.server.config.getattr("policy", "idp"),
            issuer=self.server._issuer(),
            attrconvs=self.server.config.attribute_converters,
            authn_class=INTERNETPROTOCOLPASSWORD,
            authn_auth="http://www.example.com/login")

        assertion.signature = sigver.pre_signature_part(
            assertion.id, _sec.my_cert, 1)

        sigass = _sec.sign_statement(assertion,
                                     class_name(assertion),
                                     key_file=self.client.sec.key_file,
                                     node_id=assertion.id)

        sigass = rm_xmltag(sigass)

        response = sigver.response_factory(
            in_response_to="_012345",
            destination="https://www.example.com",
            status=s_utils.success_status_factory(),
            issuer=self.server._issuer(),
            encrypted_assertion=EncryptedAssertion())

        xmldoc = "%s" % response
        # strangely enough I get different tags if I run this test separately
        # or as part of a bunch of tests.
        xmldoc = add_subelement(xmldoc, "EncryptedAssertion", sigass)

        enctext = _sec.crypto.encrypt_assertion(xmldoc, _sec.cert_file,
                                                pre_encryption_part())

        #seresp = samlp.response_from_string(enctext)

        resp_str = base64.encodestring(enctext)
        # Now over to the client side
        resp = self.client.parse_authn_request_response(
            resp_str, BINDING_HTTP_POST,
            {"_012345": "http://foo.example.com/service"})

        #assert resp.encrypted_assertion == []
        assert resp.assertion
        assert resp.ava == {'givenName': ['Derek'], 'sn': ['Jeter']}
Exemplo n.º 45
0
 def __init__(self, config_file="", config=None, cache=None):
     Server.__init__(self, config_file, config, cache)
Exemplo n.º 46
0
def test_basic_flow():
    sp = Saml2Client(config_file="servera_conf")
    with closing(Server(config_file="idp_all_conf")) as idp:
        # -------- @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("id12", sp.config.entityid)

        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: "/"})

        # == Look for assertion X

        asid = aresp.assertion.id

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

        hinfo = sp.apply_binding(binding, asid, destination)

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

        aid = get_msg(hinfo, binding, response=False)

        # == construct response

        resp = idp.create_assertion_id_request_response(aid)

        hinfo = idp.apply_binding(binding,
                                  "%s" % resp,
                                  None,
                                  "",
                                  response=True)

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

        xmlstr = get_msg(hinfo, binding, response=True)

        final = sp.parse_assertion_id_request_response(xmlstr, binding)

        print final.response
        assert isinstance(final.response, Assertion)
Exemplo n.º 47
0
def test_artifact_flow():
    #SP = 'urn:mace:example.com:saml:roland:sp'
    sp = Saml2Client(config_file="servera_conf")
    idp = Server(config_file="idp_all_conf")

    # original request

    binding, destination = sp.pick_binding("single_sign_on_service",
                                           entity_id=idp.config.entityid)
    relay_state = "RS0"
    req_id, req = sp.create_authn_request(destination, id="id1")

    artifact = sp.use_artifact(req, 1)

    binding, destination = sp.pick_binding("single_sign_on_service",
                                           [BINDING_HTTP_ARTIFACT],
                                           entity_id=idp.config.entityid)

    hinfo = sp.apply_binding(binding, artifact, destination, relay_state)

    # ========== @IDP ============

    artifact2 = get_msg(hinfo, binding)

    assert artifact == artifact2

    # The IDP now wants to replace the artifact with the real request

    destination = idp.artifact2destination(artifact2, "spsso")

    msg_id, msg = idp.create_artifact_resolve(artifact2, destination, sid())

    hinfo = idp.use_soap(msg, destination, None, False)

    # ======== @SP ==========

    msg = get_msg(hinfo, BINDING_SOAP)

    ar = sp.parse_artifact_resolve(msg)

    assert ar.artifact.text == artifact

    # The SP picks the request out of the repository with the artifact as the key
    oreq = sp.artifact[ar.artifact.text]
    # Should be the same as req above

    # Returns the information over the existing SOAP connection so
    # no transport information needed

    msg = sp.create_artifact_response(ar, artifact)
    hinfo = sp.use_soap(msg, destination)

    # ========== @IDP ============

    msg = get_msg(hinfo, BINDING_SOAP)

    # The IDP untangles the request from the artifact resolve response
    spreq = idp.parse_artifact_resolve_response(msg)

    # should be the same as req above

    assert spreq.id == req.id

    # That was one way, the Request from the SP
    # ---------------------------------------------#
    # Now for the other, the response from the IDP

    name_id = idp.ident.transient_nameid(sp.config.entityid, "derek")

    resp_args = idp.response_args(spreq, [BINDING_HTTP_POST])

    response = idp.create_authn_response(
        {
            "eduPersonEntitlement": "Short stop",
            "surName": "Jeter",
            "givenName": "Derek",
            "mail": "*****@*****.**",
            "title": "The man"
        },
        name_id=name_id,
        authn=AUTHN,
        **resp_args)

    print(response)

    # with the response in hand create an artifact

    artifact = idp.use_artifact(response, 1)

    binding, destination = sp.pick_binding("single_sign_on_service",
                                           [BINDING_HTTP_ARTIFACT],
                                           entity_id=idp.config.entityid)

    hinfo = sp.apply_binding(binding,
                             artifact,
                             destination,
                             relay_state,
                             response=True)

    # ========== SP =========

    artifact3 = get_msg(hinfo, binding)

    assert artifact == artifact3

    destination = sp.artifact2destination(artifact, "idpsso")

    # Got an artifact want to replace it with the real message
    msg_id, msg = sp.create_artifact_resolve(artifact, destination, sid())

    print(msg)

    hinfo = sp.use_soap(msg, destination, None, False)

    # ======== IDP ==========

    msg = get_msg(hinfo, BINDING_SOAP)

    ar = idp.parse_artifact_resolve(msg)

    print(ar)

    assert ar.artifact.text == artifact3

    # The IDP retrieves the response from the database using the artifact as the key
    #oreq = idp.artifact[ar.artifact.text]

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

    resp = idp.create_artifact_response(ar, ar.artifact.text)
    hinfo = idp.use_soap(resp, destination)

    # ========== SP ============

    msg = get_msg(hinfo, BINDING_SOAP)
    sp_resp = sp.parse_artifact_resolve_response(msg)

    assert sp_resp.id == response.id
Exemplo n.º 48
0
class IdPHandlerViewMixin:
    """ Contains some methods used by multiple views """

    error_view = import_string(
        getattr(settings, 'SAML_IDP_ERROR_VIEW_CLASS',
                'djangosaml2idp.error_views.SamlIDPErrorView'))

    def handle_error(self, request, **kwargs):
        return self.error_view.as_view()(request, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        """ Construct IDP server with config from settings dict
        """
        conf = IdPConfig()
        try:
            conf.load(copy.deepcopy(settings.SAML_IDP_CONFIG))
            self.IDP = Server(config=conf)
        except Exception as e:
            return self.handle_error(request, exception=e)
        return super().dispatch(request, *args, **kwargs)

    def set_sp(self, sp_entity_id):
        """ Saves SP info to instance variable
            Raises an exception if sp matching the given entity id cannot be found.
        """
        self.sp = {'id': sp_entity_id}
        try:
            self.sp['config'] = settings.SAML_IDP_SPCONFIG[sp_entity_id]
        except KeyError:
            raise ImproperlyConfigured(
                "No config for SP {} defined in SAML_IDP_SPCONFIG".format(
                    sp_entity_id))

    def set_processor(self):
        """ Instantiate user-specified processor or default to an all-access base processor.
            Raises an exception if the configured processor class can not be found or initialized.
        """
        processor_string = self.sp['config'].get('processor', None)
        if processor_string:
            try:
                self.processor = import_string(processor_string)(self.sp['id'])
                return
            except Exception as e:
                logger.error("Failed to instantiate processor: {} - {}".format(
                    processor_string, e),
                             exc_info=True)
                raise e
        self.processor = BaseProcessor(self.sp['id'])

    def get_authn(self, req_info=None):
        req_authn_context = req_info.message.requested_authn_context if req_info else PASSWORD
        broker = AuthnBroker()
        broker.add(authn_context_class_ref(req_authn_context), "")
        return broker.get_authn_by_accr(req_authn_context)

    def build_authn_response(self, user, authn, resp_args):
        name_id_formats = [resp_args.get('name_id_policy').format
                           ] or self.IDP.config.getattr(
                               "name_id_format",
                               "idp") or [NAMEID_FORMAT_UNSPECIFIED]
        authn_resp = self.IDP.create_authn_response(
            authn=authn,
            identity=self.processor.create_identity(user, self.sp['config']),
            userid=self.processor.get_user_id(user, self.sp['config']),
            name_id=NameID(format=name_id_formats[0],
                           sp_name_qualifier=self.sp['id'],
                           text=self.processor.get_user_id(
                               user, self.sp['config'])),
            sign_response=self.sp['config'].get("sign_response")
            or self.IDP.config.getattr("sign_response", "idp") or False,
            sign_assertion=self.sp['config'].get("sign_assertion")
            or self.IDP.config.getattr("sign_assertion", "idp") or False,
            **resp_args)
        return authn_resp

    def create_html_response(self, request, binding, authn_resp, destination,
                             relay_state):
        """ Login form for SSO
        """
        if binding == BINDING_HTTP_POST:
            context = {
                "acs_url": destination,
                "saml_response":
                base64.b64encode(authn_resp.encode()).decode(),
                "relay_state": relay_state,
            }
            html_response = render_to_string("djangosaml2idp/login.html",
                                             context=context,
                                             request=request)
        else:
            http_args = self.IDP.apply_binding(binding=binding,
                                               msg_str=authn_resp,
                                               destination=destination,
                                               relay_state=relay_state,
                                               response=True)

            logger.debug('http args are: %s' % http_args)
            html_response = http_args['data']
        return html_response

    def render_response(self, request, html_response):
        """ Return either as redirect to MultiFactorView or as html with self-submitting form.
        """
        if self.processor.enable_multifactor(request.user):
            # Store http_args in session for after multi factor is complete
            request.session['saml_data'] = html_response
            logger.debug("Redirecting to process_multi_factor")
            return HttpResponseRedirect(reverse('saml_multi_factor'))
        logger.debug("Performing SAML redirect")
        return HttpResponse(html_response)