|
| 1 | +#!/usr/bin/env python |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | +import datetime |
| 4 | +import re |
| 5 | +from urllib import quote_plus |
| 6 | +from saml2.httpbase import HTTPBase |
| 7 | + |
| 8 | +from saml2.mdstore import MetadataStore, MetaDataMDX |
| 9 | +from saml2.mdstore import destinations |
| 10 | +from saml2.mdstore import name |
| 11 | + |
| 12 | +from saml2 import md |
| 13 | +from saml2 import sigver |
| 14 | +from saml2 import BINDING_SOAP |
| 15 | +from saml2 import BINDING_HTTP_REDIRECT |
| 16 | +from saml2 import BINDING_HTTP_POST |
| 17 | +from saml2 import BINDING_HTTP_ARTIFACT |
| 18 | +from saml2 import saml |
| 19 | +from saml2 import config |
| 20 | +from saml2.attribute_converter import ac_factory |
| 21 | +from saml2.attribute_converter import d_to_local_name |
| 22 | + |
| 23 | +from saml2.extension import mdui |
| 24 | +from saml2.extension import idpdisc |
| 25 | +from saml2.extension import dri |
| 26 | +from saml2.extension import mdattr |
| 27 | +from saml2.extension import ui |
| 28 | +from saml2.s_utils import UnknownPrincipal |
| 29 | +import xmldsig |
| 30 | +import xmlenc |
| 31 | + |
| 32 | +from pathutils import full_path |
| 33 | + |
| 34 | +sec_config = config.Config() |
| 35 | +#sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) |
| 36 | + |
| 37 | +ONTS = { |
| 38 | + saml.NAMESPACE: saml, |
| 39 | + mdui.NAMESPACE: mdui, |
| 40 | + mdattr.NAMESPACE: mdattr, |
| 41 | + dri.NAMESPACE: dri, |
| 42 | + ui.NAMESPACE: ui, |
| 43 | + idpdisc.NAMESPACE: idpdisc, |
| 44 | + md.NAMESPACE: md, |
| 45 | + xmldsig.NAMESPACE: xmldsig, |
| 46 | + xmlenc.NAMESPACE: xmlenc |
| 47 | +} |
| 48 | + |
| 49 | +ATTRCONV = ac_factory(full_path("attributemaps")) |
| 50 | + |
| 51 | +METADATACONF = { |
| 52 | + "1": { |
| 53 | + "local": [full_path("swamid-1.0.xml")] |
| 54 | + }, |
| 55 | + "2": { |
| 56 | + "local": [full_path("InCommon-metadata.xml")] |
| 57 | + }, |
| 58 | + "3": { |
| 59 | + "local": [full_path("extended.xml")] |
| 60 | + }, |
| 61 | + "7": { |
| 62 | + "local": [full_path("metadata_sp_1.xml"), |
| 63 | + full_path("InCommon-metadata.xml")], |
| 64 | + "remote": [ |
| 65 | + {"url": "https://kalmar2.org/simplesaml/module.php/aggregator/?id=kalmarcentral2&set=saml2", |
| 66 | + "cert": full_path("kalmar2.pem")}] |
| 67 | + }, |
| 68 | + "4": { |
| 69 | + "local": [full_path("metadata_example.xml")] |
| 70 | + }, |
| 71 | + "5": { |
| 72 | + "local": [full_path("metadata.aaitest.xml")] |
| 73 | + }, |
| 74 | + "8": { |
| 75 | + "mdfile": [full_path("swamid.md")] |
| 76 | + }, |
| 77 | + "9": { |
| 78 | + "local": [full_path("metadata")] |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | + |
| 83 | +def _eq(l1, l2): |
| 84 | + return set(l1) == set(l2) |
| 85 | + |
| 86 | + |
| 87 | +def _fix_valid_until(xmlstring): |
| 88 | + new_date = datetime.datetime.now() + datetime.timedelta(days=1) |
| 89 | + new_date = new_date.strftime("%Y-%m-%dT%H:%M:%SZ") |
| 90 | + return re.sub(r' validUntil=".*?"', ' validUntil="%s"' % new_date, |
| 91 | + xmlstring) |
| 92 | + |
| 93 | + |
| 94 | +def test_swami_1(): |
| 95 | + UMU_IDP = 'https://idp.umu.se/saml2/idp/metadata.php' |
| 96 | + mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, |
| 97 | + disable_ssl_certificate_validation=True) |
| 98 | + |
| 99 | + mds.imp(METADATACONF["1"]) |
| 100 | + assert len(mds) == 1 # One source |
| 101 | + idps = mds.with_descriptor("idpsso") |
| 102 | + assert idps.keys() |
| 103 | + idpsso = mds.single_sign_on_service(UMU_IDP) |
| 104 | + assert len(idpsso) == 1 |
| 105 | + assert destinations(idpsso) == [ |
| 106 | + 'https://idp.umu.se/saml2/idp/SSOService.php'] |
| 107 | + |
| 108 | + _name = name(mds[UMU_IDP]) |
| 109 | + assert _name == u'Umeå University (SAML2)' |
| 110 | + certs = mds.certs(UMU_IDP, "idpsso", "signing") |
| 111 | + assert len(certs) == 1 |
| 112 | + |
| 113 | + sps = mds.with_descriptor("spsso") |
| 114 | + assert len(sps) == 108 |
| 115 | + |
| 116 | + wants = mds.attribute_requirement('https://connect8.sunet.se/shibboleth') |
| 117 | + lnamn = [d_to_local_name(mds.attrc, attr) for attr in wants["optional"]] |
| 118 | + assert _eq(lnamn, ['eduPersonPrincipalName', 'mail', 'givenName', 'sn', |
| 119 | + 'eduPersonScopedAffiliation']) |
| 120 | + |
| 121 | + wants = mds.attribute_requirement('https://beta.lobber.se/shibboleth') |
| 122 | + assert wants["required"] == [] |
| 123 | + lnamn = [d_to_local_name(mds.attrc, attr) for attr in wants["optional"]] |
| 124 | + assert _eq(lnamn, ['eduPersonPrincipalName', 'mail', 'givenName', 'sn', |
| 125 | + 'eduPersonScopedAffiliation', 'eduPersonEntitlement']) |
| 126 | + |
| 127 | + |
| 128 | +def test_incommon_1(): |
| 129 | + mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, |
| 130 | + disable_ssl_certificate_validation=True) |
| 131 | + |
| 132 | + mds.imp(METADATACONF["2"]) |
| 133 | + |
| 134 | + print mds.entities() |
| 135 | + assert mds.entities() > 1700 |
| 136 | + idps = mds.with_descriptor("idpsso") |
| 137 | + print idps.keys() |
| 138 | + assert len(idps) > 300 # ~ 18% |
| 139 | + try: |
| 140 | + _ = mds.single_sign_on_service('urn:mace:incommon:uiuc.edu') |
| 141 | + except UnknownPrincipal: |
| 142 | + pass |
| 143 | + |
| 144 | + idpsso = mds.single_sign_on_service('urn:mace:incommon:alaska.edu') |
| 145 | + assert len(idpsso) == 1 |
| 146 | + print idpsso |
| 147 | + assert destinations(idpsso) == [ |
| 148 | + 'https://idp.alaska.edu/idp/profile/SAML2/Redirect/SSO'] |
| 149 | + |
| 150 | + sps = mds.with_descriptor("spsso") |
| 151 | + |
| 152 | + acs_sp = [] |
| 153 | + for nam, desc in sps.items(): |
| 154 | + if "attribute_consuming_service" in desc: |
| 155 | + acs_sp.append(nam) |
| 156 | + |
| 157 | + assert len(acs_sp) == 0 |
| 158 | + |
| 159 | + # Look for attribute authorities |
| 160 | + aas = mds.with_descriptor("attribute_authority") |
| 161 | + |
| 162 | + print aas.keys() |
| 163 | + assert len(aas) == 180 |
| 164 | + |
| 165 | + |
| 166 | +def test_ext_2(): |
| 167 | + mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, |
| 168 | + disable_ssl_certificate_validation=True) |
| 169 | + |
| 170 | + mds.imp(METADATACONF["3"]) |
| 171 | + # No specific binding defined |
| 172 | + |
| 173 | + ents = mds.with_descriptor("spsso") |
| 174 | + for binding in [BINDING_SOAP, BINDING_HTTP_POST, BINDING_HTTP_ARTIFACT, |
| 175 | + BINDING_HTTP_REDIRECT]: |
| 176 | + assert mds.single_logout_service(ents.keys()[0], binding, "spsso") |
| 177 | + |
| 178 | + |
| 179 | +def test_example(): |
| 180 | + mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, |
| 181 | + disable_ssl_certificate_validation=True) |
| 182 | + |
| 183 | + mds.imp(METADATACONF["4"]) |
| 184 | + assert len(mds.keys()) == 1 |
| 185 | + idps = mds.with_descriptor("idpsso") |
| 186 | + |
| 187 | + assert idps.keys() == [ |
| 188 | + 'http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php'] |
| 189 | + certs = mds.certs( |
| 190 | + 'http://xenosmilus.umdc.umu.se/simplesaml/saml2/idp/metadata.php', |
| 191 | + "idpsso", "signing") |
| 192 | + assert len(certs) == 1 |
| 193 | + |
| 194 | + |
| 195 | +def test_switch_1(): |
| 196 | + mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, |
| 197 | + disable_ssl_certificate_validation=True) |
| 198 | + |
| 199 | + mds.imp(METADATACONF["5"]) |
| 200 | + assert len(mds.keys()) > 160 |
| 201 | + idps = mds.with_descriptor("idpsso") |
| 202 | + print idps.keys() |
| 203 | + idpsso = mds.single_sign_on_service( |
| 204 | + 'https://aai-demo-idp.switch.ch/idp/shibboleth') |
| 205 | + assert len(idpsso) == 1 |
| 206 | + print idpsso |
| 207 | + assert destinations(idpsso) == [ |
| 208 | + 'https://aai-demo-idp.switch.ch/idp/profile/SAML2/Redirect/SSO'] |
| 209 | + assert len(idps) > 30 |
| 210 | + aas = mds.with_descriptor("attribute_authority") |
| 211 | + print aas.keys() |
| 212 | + aad = aas['https://aai-demo-idp.switch.ch/idp/shibboleth'] |
| 213 | + print aad.keys() |
| 214 | + assert len(aad["attribute_authority_descriptor"]) == 1 |
| 215 | + assert len(aad["idpsso_descriptor"]) == 1 |
| 216 | + |
| 217 | + sps = mds.with_descriptor("spsso") |
| 218 | + dual = [eid for eid, ent in idps.items() if eid in sps] |
| 219 | + print len(dual) |
| 220 | + assert len(dual) == 0 |
| 221 | + |
| 222 | + |
| 223 | +def test_metadata_file(): |
| 224 | + sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) |
| 225 | + mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, |
| 226 | + disable_ssl_certificate_validation=True) |
| 227 | + |
| 228 | + mds.imp(METADATACONF["8"]) |
| 229 | + print len(mds.keys()) |
| 230 | + assert len(mds.keys()) == 560 |
| 231 | + |
| 232 | + |
| 233 | +def test_mdx_service(): |
| 234 | + sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) |
| 235 | + http = HTTPBase(verify=False, ca_bundle=None) |
| 236 | + |
| 237 | + mdx = MetaDataMDX(quote_plus, ONTS.values(), ATTRCONV, |
| 238 | + "http://pyff-test.nordu.net", |
| 239 | + sec_config, None, http) |
| 240 | + foo = mdx.service("https://idp.umu.se/saml2/idp/metadata.php", |
| 241 | + "idpsso_descriptor", "single_sign_on_service") |
| 242 | + |
| 243 | + assert len(foo) == 1 |
| 244 | + assert foo.keys()[0] == BINDING_HTTP_REDIRECT |
| 245 | + |
| 246 | + |
| 247 | +def test_mdx_certs(): |
| 248 | + sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) |
| 249 | + http = HTTPBase(verify=False, ca_bundle=None) |
| 250 | + |
| 251 | + mdx = MetaDataMDX(quote_plus, ONTS.values(), ATTRCONV, |
| 252 | + "http://pyff-test.nordu.net", |
| 253 | + sec_config, None, http) |
| 254 | + foo = mdx.certs("https://idp.umu.se/saml2/idp/metadata.php", "idpsso") |
| 255 | + |
| 256 | + assert len(foo) == 1 |
| 257 | + |
| 258 | + |
| 259 | +def test_load_local_dir(): |
| 260 | + sec_config.xmlsec_binary = sigver.get_xmlsec_binary(["/opt/local/bin"]) |
| 261 | + mds = MetadataStore(ONTS.values(), ATTRCONV, sec_config, |
| 262 | + disable_ssl_certificate_validation=True) |
| 263 | + |
| 264 | + mds.imp(METADATACONF["9"]) |
| 265 | + print mds |
| 266 | + assert len(mds) == 3 # Three sources |
| 267 | + assert len(mds.keys()) == 4 # number of idps |
| 268 | + |
| 269 | +if __name__ == "__main__": |
| 270 | + test_mdx_certs() |
0 commit comments