Skip to content

Commit e14740f

Browse files
committed
dereference TrustProfileRefs
1 parent 99e731c commit e14740f

File tree

4 files changed

+51
-16
lines changed

4 files changed

+51
-16
lines changed

src/pyff/builtins.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,7 @@ def _discojson_sp(req, *opts):
10391039
if req.t is None:
10401040
raise PipeException("Your pipeline is missing a select statement.")
10411041

1042-
res = discojson_sp_t(req.t)
1042+
res = discojson_sp_t(req)
10431043

10441044
return json.dumps(res)
10451045

src/pyff/resource.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ def __init__(self, url: Optional[str], opts: ResourceOpts):
239239
self.last_parser: Optional['PyffParser'] = None # importing PyffParser in this module causes a loop
240240
self._infos: Deque[ResourceInfo] = deque(maxlen=config.info_buffer_size)
241241
self.children: Deque[Resource] = deque()
242+
self.trust_info: Optional[dict] = None
242243
self.md_sources: Optional[dict] = None
243244
self._setup()
244245

@@ -493,6 +494,13 @@ def parse(self, getter: Callable[[str], Response]) -> Deque[Resource]:
493494

494495
return self.children
495496

497+
def global_trust_info(self):
498+
trust_info = {}
499+
for r in self.walk():
500+
if r.url and r.trust_info is not None:
501+
trust_info[r.url] = r.trust_info['profiles']
502+
return trust_info
503+
496504
def global_md_sources(self):
497505
from pyff.samlmd import SAMLParserInfo
498506

src/pyff/samlmd.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def parse_saml_metadata(
9393
:param base_url: use this base url to resolve relative URLs for XInclude processing
9494
:param validation_errors: A dict that will be used to return validation errors to the caller
9595
96-
:return: Tuple with t (ElementTree), expire_time_offset, exception
96+
:return: Tuple with t (ElementTree), trust_info, expire_time_offset, exception
9797
"""
9898

9999
if validation_errors is None:
@@ -108,6 +108,9 @@ def parse_saml_metadata(
108108

109109
t = check_signature(t, opts.verify)
110110

111+
trust_info = None
112+
extensions = t.find('{%s}Extensions' % NS['md'])
113+
111114
if opts.cleanup is not None:
112115
for cb in opts.cleanup:
113116
t = cb(t)
@@ -132,18 +135,20 @@ def parse_saml_metadata(
132135
t = entitiesdescriptor(
133136
[t], base_url, copy=False, validate=True, filter_invalid=filter_invalid, nsmap=t.nsmap
134137
)
138+
elif t.tag == "{%s}EntitiesDescriptor" % NS['md'] and extensions is not None:
139+
trust_info = discojson_sp(extensions)
135140

136141
except Exception as ex:
137142
log.debug(traceback.format_exc())
138143
log.error("Error parsing {}: {}".format(base_url, ex))
139144
if opts.fail_on_error:
140145
raise ex
141146

142-
return None, None, ex
147+
return None, None, None, ex
143148

144149
log.debug("returning %d valid entities" % len(list(iter_entities(t))))
145150

146-
return t, expire_time_offset, None
151+
return t, trust_info, expire_time_offset, None
147152

148153

149154
class SAMLParserInfo(ParserInfo):
@@ -162,7 +167,7 @@ def magic(self, content: str) -> bool:
162167

163168
def parse(self, resource: Resource, content: str) -> SAMLParserInfo:
164169
info = SAMLParserInfo(description='SAML Metadata', expiration_time='')
165-
t, expire_time_offset, exception = parse_saml_metadata(
170+
t, trust_info, expire_time_offset, exception = parse_saml_metadata(
166171
unicode_stream(content),
167172
base_url=resource.url,
168173
opts=resource.opts,
@@ -184,6 +189,9 @@ def parse(self, resource: Resource, content: str) -> SAMLParserInfo:
184189
for e in iter_entities(t):
185190
info.entities.append(e.get('entityID'))
186191

192+
if trust_info is not None:
193+
resource.trust_info = trust_info
194+
187195
if exception is not None:
188196
resource.info.exception = exception
189197

@@ -890,7 +898,8 @@ def fetch_mdjson(urls):
890898

891899
entities = []
892900
for child in resource.children:
893-
entities.extend(child.t.findall(".//{%s}EntityDescriptor" % NS['md']))
901+
if child.t is not None:
902+
entities.extend(child.t.findall(".//{%s}EntityDescriptor" % NS['md']))
894903

895904
ot = entitiesdescriptor(entities, 'extra-sources')
896905

@@ -903,7 +912,7 @@ def fetch_mdjson(urls):
903912
return entities_json
904913

905914

906-
def discojson_sp(e):
915+
def discojson_sp(e, global_trust_info=None, global_md_sources=None):
907916
sp = {}
908917

909918
tinfo_el = e.find('.//{%s}TrustInfo' % NS['ti'])
@@ -945,14 +954,26 @@ def discojson_sp(e):
945954
include = include if type(include) is bool else include in ('t', 'T', 'true', 'True')
946955
sp['profiles'][name]['entities'].append({'select': select, 'match': match, 'include': include})
947956

957+
if global_trust_info is not None and global_md_sources is not None:
958+
for profileref_el in tinfo_el.findall('.//{%s}TrustProfileRef' % NS['ti']):
959+
refname = profileref_el.text
960+
sources = global_md_sources[sp['entityID']]
961+
for source in sources:
962+
if refname in global_trust_info[source]:
963+
sp['profiles'][refname] = global_trust_info[source][refname]
964+
break
965+
948966
return sp
949967

950968

951-
def discojson_sp_t(t):
969+
def discojson_sp_t(req):
952970
d = []
971+
t = req.t
972+
global_tinfo = req.md.rm.global_trust_info()
973+
global_sources = req.md.rm.global_md_sources()
953974

954975
for e in iter_entities(t):
955-
sp = discojson_sp(e)
976+
sp = discojson_sp(e, global_trust_info=global_tinfo, global_md_sources=global_sources)
956977
if sp is not None:
957978
d.append(sp)
958979

src/pyff/test/data/metadata/edugain-trustinfo-2.0.xml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
<?xml version='1.0' encoding='UTF-8'?>
2-
<md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xrd="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:pyff="http://pyff.io/NS" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ser="http://eidas.europa.eu/metadata/servicelist" xmlns:eidas="http://eidas.europa.eu/saml-extensions" xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport" xmlns:samla="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:init="urn:oasis:names:tc:SAML:profiles:SSO:request-init" xmlns:idpdisc="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" xmlns:wayf="http://sdss.ac.uk/2006/06/WAYF" xmlns:req-attr="urn:oasis:names:tc:SAML:protcol:ext:req-attr" Name="test"><md:EntityDescriptor entityID="https://cpauth.icos-cp.eu/saml/cpauth">
2+
<md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xrd="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:pyff="http://pyff.io/NS" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ser="http://eidas.europa.eu/metadata/servicelist" xmlns:eidas="http://eidas.europa.eu/saml-extensions" xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport" xmlns:samla="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:init="urn:oasis:names:tc:SAML:profiles:SSO:request-init" xmlns:idpdisc="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" xmlns:wayf="http://sdss.ac.uk/2006/06/WAYF" xmlns:req-attr="urn:oasis:names:tc:SAML:protcol:ext:req-attr" Name="test">
3+
<md:Extensions>
4+
<ti:TrustInfo xmlns:ti="https://seamlessaccess.org/NS/trustinfo">
5+
<ti:TrustProfile name="customer2" strict="true">
6+
<ti:DisplayName xml:lang="en">Customer Access</ti:DisplayName>
7+
<ti:DisplayName xml:lang="sv">Kundinloggning</ti:DisplayName>
8+
<ti:FallbackHandler profile="href">https://www.example.org/about</ti:FallbackHandler>
9+
<ti:TrustedEntities match="registrationAuthority">https://www.carsi.edu.cn</ti:TrustedEntities>
10+
</ti:TrustProfile>
11+
</ti:TrustInfo>
12+
</md:Extensions>
13+
<md:EntityDescriptor entityID="https://cpauth.icos-cp.eu/saml/cpauth">
314
<md:Extensions>
415
<mdrpi:RegistrationInfo registrationAuthority="http://www.swamid.se/" registrationInstant="2015-02-11T11:09:51Z">
516
<mdrpi:RegistrationPolicy xml:lang="en">http://swamid.se/policy/mdrps</mdrpi:RegistrationPolicy>
@@ -29,12 +40,7 @@
2940
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
3041
<md:Extensions>
3142
<ti:TrustInfo xmlns:ti="https://seamlessaccess.org/NS/trustinfo">
32-
<ti:TrustProfile name="customer" strict="true">
33-
<ti:DisplayName xml:lang="en">Customer Access</ti:DisplayName>
34-
<ti:DisplayName xml:lang="sv">Kundinloggning</ti:DisplayName>
35-
<ti:FallbackHandler profile="href">https://www.example.org/about</ti:FallbackHandler>
36-
<ti:TrustedEntities match="registrationAuthority">http://www.swamid.se/</ti:TrustedEntities>
37-
</ti:TrustProfile>
43+
<ti:TrustProfileRef>customer2</ti:TrustProfileRef>
3844
</ti:TrustInfo>
3945
<init:RequestInitiator Binding="urn:oasis:names:tc:SAML:profiles:SSO:request-init" Location="https://cpauth.icos-cp.eu/saml/login"/>
4046
<mdui:UIInfo>

0 commit comments

Comments
 (0)