Skip to content

Commit

Permalink
Add shibmd_scopes metadata extractor
Browse files Browse the repository at this point in the history
Signed-off-by: Ivan Kanakarakis <[email protected]>
  • Loading branch information
c00kiemon5ter committed May 18, 2021
1 parent c63f108 commit 745cda2
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 16 deletions.
39 changes: 39 additions & 0 deletions src/saml2/mdstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from hashlib import sha1
from os.path import isfile
from os.path import join
from re import compile as regex_compile

import requests

Expand Down Expand Up @@ -58,6 +59,8 @@
from saml2.extension.mdrpi import NAMESPACE as NS_MDRPI
from saml2.extension.mdrpi import RegistrationInfo
from saml2.extension.mdrpi import RegistrationPolicy
from saml2.extension.shibmd import NAMESPACE as NS_SHIBMD
from saml2.extension.shibmd import Scope


logger = logging.getLogger(__name__)
Expand All @@ -83,6 +86,7 @@
"service_nameid_mapping": "{ns}&{tag}".format(ns=NS_MD, tag=NameIDMappingService.c_tag),
"mdrpi_registration_info": "{ns}&{tag}".format(ns=NS_MDRPI, tag=RegistrationInfo.c_tag),
"mdrpi_registration_policy": "{ns}&{tag}".format(ns=NS_MDRPI, tag=RegistrationPolicy.c_tag),
"shibmd_scope": "{ns}&{tag}".format(ns=NS_SHIBMD, tag=Scope.c_tag)
}

ENTITY_CATEGORY = "http://macedir.org/entity-category"
Expand Down Expand Up @@ -1479,6 +1483,41 @@ def _lookup_elements_by_key(self, root, key):
)
return elements

def sbibmd_scopes(self, entity_id, typ=None):
try:
md = self[entity_id]
except KeyError:
md = {}

descriptor_scopes = (
{
"regexp": is_regexp,
"text": regex_compile(text) if is_regexp else text,
}
for elem in md.get("extensions", {}).get("extension_elements", [])
if elem.get("__class__") == classnames["shibmd_scope"]
for is_regexp, text in [
(elem.get("regexp", "").lower() == "true", elem.get("text", "")),
]
)

services_of_type = md.get(typ) or []
services_of_type_scopes = (
{
"regexp": is_regexp,
"text": regex_compile(text) if is_regexp else text,
}
for srv in services_of_type
for elem in srv.get("extensions", {}).get("extension_elements", [])
if elem.get("__class__") == classnames["shibmd_scope"]
for is_regexp, text in [
(elem.get("regexp", "").lower() == "true", elem.get("text", "")),
]
)

scopes = chain(descriptor_scopes, services_of_type_scopes)
return scopes

def mdui_uiinfo(self, entity_id):
try:
data = self[entity_id]
Expand Down
34 changes: 18 additions & 16 deletions tests/idp_uiinfo.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
<?xml version='1.0' encoding='UTF-8'?>
<ns0:EntitiesDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns1="urn:mace:shibboleth:metadata:1.0" xmlns:ns2="urn:oasis:names:tc:SAML:metadata:ui" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#"><ns0:EntityDescriptor entityID="http://example.com/saml2/idp.xml"><ns0:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><ns0:Extensions><ns1:Scope regexp="false">example.org</ns1:Scope><ns2:UIInfo><ns2:Keywords xml:lang="en">foo bar</ns2:Keywords><ns2:Logo height="40" width="30">http://example.com/logo.jpg</ns2:Logo><ns2:InformationURL>http://example.com/saml2/info.html</ns2:InformationURL><ns2:DisplayName>Example Co.</ns2:DisplayName><ns2:Description xml:lang="se">Exempel bolag</ns2:Description><ns2:PrivacyStatementURL>http://example.com/saml2/privacyStatement.html</ns2:PrivacyStatementURL></ns2:UIInfo></ns0:Extensions><ns0:KeyDescriptor><ns3:KeyInfo><ns3:X509Data><ns3:X509Certificate>MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy
3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaN
efiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0G
A1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJs
iojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUt
U3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSw
mDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6
h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5
U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6
mrPzGzk3ECbupFnqyREH3+ZPSdk=
</ns3:X509Certificate></ns3:X509Data></ns3:KeyInfo></ns0:KeyDescriptor><ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://example.com/saml2/" /></ns0:IDPSSODescriptor></ns0:EntityDescriptor></ns0:EntitiesDescriptor>
<ns0:EntitiesDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns1="urn:mace:shibboleth:metadata:1.0" xmlns:ns2="urn:oasis:names:tc:SAML:metadata:ui" xmlns:ns3="http://www.w3.org/2000/09/xmldsig#">
<ns0:EntityDescriptor entityID="http://example.com/saml2/idp.xml">
<ns0:Extensions>
<ns1:Scope regexp="false">descriptor-example.org</ns1:Scope>
<ns1:Scope regexp="true">descriptor-example[^0-9]*\.org</ns1:Scope>
</ns0:Extensions>
<ns0:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<ns0:Extensions>
<ns1:Scope regexp="false">idpssodescriptor-example.org</ns1:Scope>
<ns2:UIInfo><ns2:Keywords xml:lang="en">foo bar</ns2:Keywords><ns2:Logo height="40" width="30">http://example.com/logo.jpg</ns2:Logo><ns2:InformationURL>http://example.com/saml2/info.html</ns2:InformationURL><ns2:DisplayName>Example Co.</ns2:DisplayName><ns2:Description xml:lang="se">Exempel bolag</ns2:Description><ns2:PrivacyStatementURL>http://example.com/saml2/privacyStatement.html</ns2:PrivacyStatementURL></ns2:UIInfo>
</ns0:Extensions>
<ns0:KeyDescriptor>
<ns3:KeyInfo><ns3:X509Data><ns3:X509Certificate>MIICsDCCAhmgAwIBAgIJAJrzqSSwmDY9MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMDkxMDA2MTk0OTQxWhcNMDkxMTA1MTk0OTQxWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJg2cms7MqjniT8Fi/XkNHZNPbNVQyMUMXE9tXOdqwYCA1cc8vQdzkihscQMXy3iPw2cMggBu6gjMTOSOxECkuvX5ZCclKr8pXAJM5cY6gVOaVO2PdTZcvDBKGbiaNefiEw5hnoZomqZGp8wHNLAUkwtH9vjqqvxyS/vclc6k2ewIDAQABo4GnMIGkMB0GA1UdDgQWBBRePsKHKYJsiojE78ZWXccK9K4aJTB1BgNVHSMEbjBsgBRePsKHKYJsiojE78ZWXccK9K4aJaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAJrzqSSwmDY9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAJSrKOEzHO7TL5cy6h3qh+3+JAk8HbGBW+cbX6KBCAw/mzU8flK25vnWwXS3dv2FF3Aod0/S7AWNfKib5U/SA9nJaz/mWeF9S0farz9AQFc8/NSzAzaVq7YbM4F6f6N2FRl7GikdXRCed45j6mrPzGzk3ECbupFnqyREH3+ZPSdk=</ns3:X509Certificate></ns3:X509Data></ns3:KeyInfo>
</ns0:KeyDescriptor>
<ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://example.com/saml2/" />
</ns0:IDPSSODescriptor>
</ns0:EntityDescriptor>
</ns0:EntitiesDescriptor>
50 changes: 50 additions & 0 deletions tests/test_30_mdstore.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import datetime
import os
import re
from re import compile as regex_compile
from collections import OrderedDict
from unittest.mock import Mock
from unittest.mock import patch
Expand Down Expand Up @@ -163,6 +164,10 @@
"class": "saml2.mdstore.MetaDataFile",
"metadata": [(full_path("invalid_metadata_file.xml"),)],
}],
"15": [{
"class": "saml2.mdstore.MetaDataFile",
"metadata": [(full_path("idp_uiinfo.xml"),)],
}],
}


Expand Down Expand Up @@ -608,5 +613,50 @@ def test_extension():
assert mds.extension("entity2", "idpsso_descriptor", "test")


def test_shibmd_scope_no_regex_no_descriptor_type():
mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True)
mds.imp(METADATACONF["15"])

scopes = mds.sbibmd_scopes(entity_id='http://example.com/saml2/idp.xml')
all_scopes = list(scopes)

expected = [
{
"regexp": False,
"text": "descriptor-example.org",
},
{
"regexp": True,
"text": regex_compile("descriptor-example[^0-9]*\.org"),
},
]
assert len(all_scopes) == 2
assert all_scopes == expected


def test_shibmd_scope_no_regex_all_descriptors():
mds = MetadataStore(ATTRCONV, sec_config, disable_ssl_certificate_validation=True)
mds.imp(METADATACONF["15"])

scopes = mds.sbibmd_scopes(entity_id='http://example.com/saml2/idp.xml', typ="idpsso_descriptor")
all_scopes = list(scopes)
expected = [
{
"regexp": False,
"text": "descriptor-example.org",
},
{
"regexp": True,
"text": regex_compile("descriptor-example[^0-9]*\.org"),
},
{
"regexp": False,
"text": "idpssodescriptor-example.org",
},
]
assert len(all_scopes) == 3
assert all_scopes == expected


if __name__ == "__main__":
test_metadata_extension_algsupport()

0 comments on commit 745cda2

Please sign in to comment.