summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Kanakarakis <ivan.kanak@gmail.com>2021-05-18 13:51:43 +0300
committerIvan Kanakarakis <ivan.kanak@gmail.com>2021-05-18 13:51:43 +0300
commit745cda2e0222dafe7eee3053ca133ccd012de0a7 (patch)
tree594af0ed66d4687d1d34e66c2ab6ccba29578fa1
parentc63f1082a022c00ed1cab47119b053e5c38ff2b2 (diff)
downloadpysaml2-745cda2e0222dafe7eee3053ca133ccd012de0a7.tar.gz
Add shibmd_scopes metadata extractor
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
-rw-r--r--src/saml2/mdstore.py39
-rw-r--r--tests/idp_uiinfo.xml34
-rw-r--r--tests/test_30_mdstore.py50
3 files changed, 107 insertions, 16 deletions
diff --git a/src/saml2/mdstore.py b/src/saml2/mdstore.py
index 44930773..af142e79 100644
--- a/src/saml2/mdstore.py
+++ b/src/saml2/mdstore.py
@@ -10,6 +10,7 @@ from warnings import warn as _warn
from hashlib import sha1
from os.path import isfile
from os.path import join
+from re import compile as regex_compile
import requests
@@ -58,6 +59,8 @@ from saml2.extension.mdui import Logo
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__)
@@ -83,6 +86,7 @@ classnames = {
"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"
@@ -1479,6 +1483,41 @@ class MetadataStore(MetaData):
)
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]
diff --git a/tests/idp_uiinfo.xml b/tests/idp_uiinfo.xml
index fa37d703..7d531fd1 100644
--- a/tests/idp_uiinfo.xml
+++ b/tests/idp_uiinfo.xml
@@ -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>
diff --git a/tests/test_30_mdstore.py b/tests/test_30_mdstore.py
index bfe261dd..bf54594e 100644
--- a/tests/test_30_mdstore.py
+++ b/tests/test_30_mdstore.py
@@ -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
@@ -163,6 +164,10 @@ METADATACONF = {
"class": "saml2.mdstore.MetaDataFile",
"metadata": [(full_path("invalid_metadata_file.xml"),)],
}],
+ "15": [{
+ "class": "saml2.mdstore.MetaDataFile",
+ "metadata": [(full_path("idp_uiinfo.xml"),)],
+ }],
}
@@ -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()