import saml2 from saml2 import BINDING_HTTP_POST from saml2 import extension_elements_to_elements from saml2 import md from saml2.attribute_converter import ac_factory from saml2.config import IdPConfig from saml2.config import SPConfig from saml2.extension import idpdisc from saml2.extension import mdui from saml2.extension import shibmd from saml2.saml import NAME_FORMAT_URI def _eq(l1, l2): return set(l1) == set(l2) SP = { "name": "Rolands SP", "description": "One of the best SPs in business", "service": { "sp": { "endpoints": { "single_logout_service": ["http://localhost:8087/logout"], "assertion_consumer_service": [ {"location": "http://localhost:8087/", "binding": BINDING_HTTP_POST}, ], }, "required_attributes": ["sn", "givenName", "mail"], "optional_attributes": ["title"], "idp": { "": "https://example.com/saml2/idp/SSOService.php", }, } }, "metadata": { "local": ["foo.xml"], }, "attribute_map_dir": "attributemaps", } IDP = { "name": "Rolands IdP", "service": { "idp": { "endpoints": { "single_sign_on_service": ["http://localhost:8088/sso"], }, "policy": { "default": { "lifetime": {"minutes": 15}, "attribute_restrictions": None, # means all I have "name_form": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", }, "urn:mace:example.com:saml:roland:sp": { "lifetime": {"minutes": 5}, "nameid_format": "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent", }, }, "scope": ["example.org"], "ui_info": { "privacy_statement_url": "http://example.com/saml2/privacyStatement.html", "information_url": "http://example.com/saml2/info.html", "logo": {"height": "40", "width": "30", "text": "http://example.com/logo.jpg"}, "display_name": "Example Co.", "description": {"text": "Exempel bolag", "lang": "se"}, "keywords": {"lang": "en", "text": ["foo", "bar"]}, }, } }, "metadata": { "local": ["bar.xml"], }, } def test_org_1(): desc = { "name": [ ("Example Company", "en"), ("Exempel AB", "se"), "Example", ], "display_name": ["Example AS", ("Voorbeeld AZ", "")], "url": [("http://example.com", "en")], } org = metadata.do_organization_info(desc) print(org) assert isinstance(org, md.Organization) print(org.keyswv()) assert _eq(org.keyswv(), ["organization_name", "organization_display_name", "organization_url"]) assert len(org.organization_name) == 3 assert len(org.organization_display_name) == 2 assert len(org.organization_url) == 1 def test_org_2(): desc = { "name": [ ("Example Company", "en"), ("Exempel AB", "se"), "Example", ], "display_name": "Example AS", "url": ("http://example.com", "en"), } org = metadata.do_organization_info(desc) print(org) assert _eq(org.keyswv(), ["organization_name", "organization_display_name", "organization_url"]) assert len(org.organization_name) == 3 assert len(org.organization_display_name) == 1 assert org.organization_display_name[0].text == "Example AS" assert len(org.organization_url) == 1 assert isinstance(org.organization_url[0], md.OrganizationURL) assert org.organization_url[0].lang == "en" assert org.organization_url[0].text == "http://example.com" def test_org_3(): desc = {"display_name": ["Rolands SAML"]} org = metadata.do_organization_info(desc) assert _eq(org.keyswv(), ["organization_display_name"]) assert len(org.organization_display_name) == 1 def test_contact_0(): conf = [ { "given_name": "Roland", "sur_name": "Hedberg", "telephone_number": "+46 70 100 00 00", "email_address": ["foo@eample.com", "foo@example.org"], "contact_type": "technical", } ] contact_person = metadata.do_contact_person_info(conf) assert _eq( contact_person[0].keyswv(), ["given_name", "sur_name", "contact_type", "telephone_number", "email_address"] ) print(contact_person[0]) person = contact_person[0] assert person.contact_type == "technical" assert isinstance(person.given_name, md.GivenName) assert person.given_name.text == "Roland" assert isinstance(person.sur_name, md.SurName) assert person.sur_name.text == "Hedberg" assert isinstance(person.telephone_number[0], md.TelephoneNumber) assert person.telephone_number[0].text == "+46 70 100 00 00" assert len(person.email_address) == 2 assert isinstance(person.email_address[0], md.EmailAddress) assert person.email_address[0].text == "foo@eample.com" def test_do_endpoints(): eps = metadata.do_endpoints(SP["service"]["sp"]["endpoints"], metadata.ENDPOINTS["sp"]) print(eps) assert _eq(eps.keys(), ["assertion_consumer_service", "single_logout_service"]) assert len(eps["single_logout_service"]) == 1 sls = eps["single_logout_service"][0] assert sls.location == "http://localhost:8087/logout" assert sls.binding == BINDING_HTTP_POST assert len(eps["assertion_consumer_service"]) == 1 acs = eps["assertion_consumer_service"][0] assert acs.location == "http://localhost:8087/" assert acs.binding == BINDING_HTTP_POST assert "artifact_resolution_service" not in eps assert "manage_name_id_service" not in eps def test_required_attributes(): attrconverters = ac_factory("../tests/attributemaps") ras = metadata.do_requested_attribute( SP["service"]["sp"]["required_attributes"], attrconverters, is_required="true" ) assert len(ras) == len(SP["service"]["sp"]["required_attributes"]) print(ras[0]) assert ras[0].name == "urn:oid:2.5.4.4" assert ras[0].name_format == NAME_FORMAT_URI assert ras[0].is_required == "true" def test_optional_attributes(): attrconverters = ac_factory("../tests/attributemaps") ras = metadata.do_requested_attribute(SP["service"]["sp"]["optional_attributes"], attrconverters) assert len(ras) == len(SP["service"]["sp"]["optional_attributes"]) print(ras[0]) assert ras[0].name == "urn:oid:2.5.4.12" assert ras[0].name_format == NAME_FORMAT_URI assert ras[0].is_required == "false" def test_do_sp_sso_descriptor(): conf = SPConfig().load(SP) spsso = metadata.do_spsso_descriptor(conf) assert isinstance(spsso, md.SPSSODescriptor) assert _eq( spsso.keyswv(), [ "authn_requests_signed", "attribute_consuming_service", "single_logout_service", "protocol_support_enumeration", "assertion_consumer_service", "want_assertions_signed", ], ) assert spsso.authn_requests_signed == "false" assert spsso.want_assertions_signed == "true" assert len(spsso.attribute_consuming_service) == 1 acs = spsso.attribute_consuming_service[0] print(acs.keyswv()) assert _eq(acs.keyswv(), ["requested_attribute", "service_name", "service_description", "index"]) assert acs.service_name[0].text == SP["name"] assert acs.service_description[0].text == SP["description"] assert len(acs.requested_attribute) == 4 assert acs.requested_attribute[0].friendly_name == "sn" assert acs.requested_attribute[0].name == "urn:oid:2.5.4.4" assert acs.requested_attribute[0].name_format == NAME_FORMAT_URI assert acs.requested_attribute[0].is_required == "true" def test_do_sp_sso_descriptor_2(): SP["service"]["sp"]["discovery_response"] = "http://example.com/sp/ds" conf = SPConfig().load(SP) spsso = metadata.do_spsso_descriptor(conf) assert isinstance(spsso, md.SPSSODescriptor) print(spsso.keyswv()) assert _eq( spsso.keyswv(), [ "authn_requests_signed", "attribute_consuming_service", "single_logout_service", "protocol_support_enumeration", "assertion_consumer_service", "want_assertions_signed", "extensions", ], ) exts = spsso.extensions.extension_elements assert len(exts) == 1 print(exts) idpd = saml2.extension_element_to_element(exts[0], idpdisc.ELEMENT_FROM_STRING, namespace=idpdisc.NAMESPACE) print(idpd) assert idpd.location == "http://example.com/sp/ds" assert idpd.index == "0" assert idpd.binding == "urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" def test_entity_description(): # confd = eval(open("../tests/server.config").read()) confd = SPConfig().load_file("server_conf") print(confd.attribute_converters) entd = metadata.entity_descriptor(confd) assert entd is not None print(entd.keyswv()) assert _eq(entd.keyswv(), ["valid_until", "entity_id", "contact_person", "spsso_descriptor", "organization"]) print(entd) assert entd.entity_id == "urn:mace:example.com:saml:roland:sp" def test_do_idp_sso_descriptor(): conf = IdPConfig().load(IDP) idpsso = metadata.do_idpsso_descriptor(conf) assert isinstance(idpsso, md.IDPSSODescriptor) assert _eq( idpsso.keyswv(), ["protocol_support_enumeration", "single_sign_on_service", "want_authn_requests_signed", "extensions"], ) exts = idpsso.extensions.extension_elements assert len(exts) == 2 print(exts) inst = saml2.extension_element_to_element(exts[0], shibmd.ELEMENT_FROM_STRING, namespace=shibmd.NAMESPACE) assert isinstance(inst, shibmd.Scope) assert inst.text == "example.org" assert inst.regexp == "false" uiinfo = saml2.extension_element_to_element(exts[1], mdui.ELEMENT_FROM_STRING, namespace=mdui.NAMESPACE) assert uiinfo assert _eq( uiinfo.keyswv(), ["display_name", "description", "information_url", "privacy_statement_url", "keywords", "logo"] ) assert len(uiinfo.privacy_statement_url) == 1 assert uiinfo.privacy_statement_url[0].text == "http://example.com/saml2/privacyStatement.html" assert len(uiinfo.description) == 1 assert uiinfo.description[0].text == "Exempel bolag" assert uiinfo.description[0].lang == "se" res = extension_elements_to_elements(exts, [shibmd, mdui]) assert len(res) == 2 # one is a shibmd.Scope instance and the other a mdui.UIInfo instance if isinstance(res[0], shibmd.Scope): assert isinstance(res[1], mdui.UIInfo) elif isinstance(res[1], shibmd.Scope): assert isinstance(res[0], mdui.UIInfo) found = idpsso.extensions.find_extensions(mdui.UIInfo.c_tag, mdui.NAMESPACE) assert len(found) == 1 elem = idpsso.extensions.extensions_as_elements(mdui.UIInfo.c_tag, mdui) assert len(elem) == 1 assert isinstance(elem[0], mdui.UIInfo)