diff --git a/src/pyff/builtins.py b/src/pyff/builtins.py
index 8bdd3a2d..e2f42591 100644
--- a/src/pyff/builtins.py
+++ b/src/pyff/builtins.py
@@ -832,7 +832,10 @@ def select(req: Plumbing.Request, *opts):
if opts[0] == 'as' and len(opts) == 2:
name = opts[1]
- entities = resolve_entities(args, lookup_fn=req.md.store.select, dedup=dedup)
+ if dedup:
+ entities = resolve_entities(args, lookup_fn=req.md.store.select, dedup=dedup)
+ else:
+ entities = resolve_entities(args, lookup_fn=req.md.store.select_with_dups, dedup=dedup)
if req.state.get('match', None): # TODO - allow this to be passed in via normal arguments
diff --git a/src/pyff/samlmd.py b/src/pyff/samlmd.py
index 9be47176..6593ee52 100644
--- a/src/pyff/samlmd.py
+++ b/src/pyff/samlmd.py
@@ -1047,14 +1047,34 @@ def discojson_sp_attr(e):
if b64_trustinfos is None:
return None
+ entityID = e.get('entityID', None)
sp = {}
- sp['entityID'] = e.get('entityID', None)
+ sp['entityID'] = entityID
sp['profiles'] = {}
+ sp['extra_md'] = {}
for b64_trustinfo in b64_trustinfos:
- str_trustinfo = b64decode(b64_trustinfo.encode('ascii'))
- trustinfo = json.loads(str_trustinfo.decode('utf8'))
- sp['profiles'].update(trustinfo['profiles'])
+ try:
+ str_trustinfo = b64decode(b64_trustinfo.encode('ascii'))
+ trustinfo = json.loads(str_trustinfo.decode('utf8'))
+ for profile in trustinfo['profiles']:
+ if profile in sp['profiles']:
+ log.warning(f"SP Entity {entityID} has a duplicate trust profile {profile}")
+ else:
+ sp['profiles'][profile] = trustinfo['profiles'][profile]
+
+ if 'extra_md' in trustinfo:
+ for extra_id in trustinfo['extra_md']:
+ if extra_id in sp['extra_md']:
+ log.warning(f"SP Entity {entityID} has a duplicate extra IdP metadata {extra_id}")
+ else:
+ sp['extra_md'][extra_id] = trustinfo['extra_md'][extra_id]
+
+ except Exception as e:
+ log.warning(f"Invalid entity-selection-profile attribute for {entityID}: {e}")
+
+ if not sp['profiles']:
+ return None
return sp
diff --git a/src/pyff/store.py b/src/pyff/store.py
index c4c31b33..9f748d17 100644
--- a/src/pyff/store.py
+++ b/src/pyff/store.py
@@ -4,6 +4,7 @@
import re
import shutil
import time
+from collections import defaultdict
from datetime import datetime, timedelta
from io import BytesIO
from threading import ThreadError
@@ -807,6 +808,7 @@ def __init__(self, *args, **kwargs):
self.md = dict()
self.index = dict()
self.entities = dict()
+ self.md_entities = defaultdict(dict)
for hn in DINDEX:
self.index.setdefault(hn, {})
@@ -885,13 +887,15 @@ def update(self, t, tid=None, etag=None, lazy=True):
self.entities[relt.get('entityID')] = relt # TODO: merge?
if tid is not None:
self.md[tid] = [relt.get('entityID')]
+ self.md_entities[tid][relt.get('entityID')] = relt
elif relt.tag == "{%s}EntitiesDescriptor" % NS['md']:
if tid is None:
tid = relt.get('Name')
lst = []
for e in iter_entities(t):
- self.update(e)
+ self.update(e, tid)
lst.append(e.get('entityID'))
+ self.md_entities[tid][e.get('entityID')] = e
self.md[tid] = lst
def lookup(self, key):
@@ -946,3 +950,9 @@ def _lookup(self, key):
return lst
return []
+
+ def select_with_dups(self, member):
+ if member in self.md_entities:
+ return self.md_entities[member].values()
+
+ return []
diff --git a/src/pyff/test/data/metadata/test-sp-trustinfo-in-attr.xml b/src/pyff/test/data/metadata/test-sp-trustinfo-in-attr.xml
index b1532fda..12e0d268 100644
--- a/src/pyff/test/data/metadata/test-sp-trustinfo-in-attr.xml
+++ b/src/pyff/test/data/metadata/test-sp-trustinfo-in-attr.xml
@@ -71,6 +71,118 @@ fMou5aW0mZ+QgJNKOrxY5vFxUq6pn3OiYbBu3m1C9ajbU/nx2evzt4+qUwTfHFb+
ZgXpOtmxRekFzVvGZ18BSPJKwAAqqZ11X7skT/NwEAhbgplVPv9WkDmDzqNvHqQJ
nyRgD2ZqUPU9nEOjGy0gI07dciVcYZQ+CiZeSECIWgQwjDEBDuwMCVAZA6gfdz6C
KJuN+RUSKPEcxPxle1MiB4MU0ei5X4xUbvLWKn9Ok7TOXg2BpnMAv6eON1wVo0Aa
+D265cqy6Le/toVg=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ICOS Carbon Portal SAML service
+ ICOS Kolportalens SAML tjänst
+
+
+
+
+
+
+ ICOS Carbon Portal
+ ICOS Kolportalen
+ Carbon Portal
+ Kolportalen
+ https://www.icos-cp.eu/
+ https://www.icos-cp.eu/
+
+
+ Oleg
+ Mirzov
+ mailto:oleg.mirzov@nateko.lu.se
+
+
+ Alex
+ Vermeulen
+ mailto:alex.vermeulen@nateko.lu.se
+
+
+
+
+
+ http://swamid.se/policy/mdrps
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ http://www.geant.net/uri/dataprotection-code-of-conduct/v1
+
+
+ invalidValueForAttribute
+
+
+
+
+
+
+
+ Carbon Portal authentication service
+ Kolportalens autentiseringstjänst
+ Single Sign On for services of ICOS Carbon Portal. Maintained by the Carbon Portal team at Physical Geography department (nateko.lu.se).
+ Single Sign On tjänst för ICOS Kolportalen. Hanteras av Carbon Portal teamet på INES (nateko.lu.se).
+ https://cpauth.icos-cp.eu/saml/privacyStatement
+ https://www.icos-cp.eu/
+ https://www.icos-cp.eu/
+ https://cpauth.icos-cp.eu/saml/privacyStatement
+
+
+
+
+ cpauth.icos-cp.eu
+
+ CN=cpauth.icos-cp.eu
+ MIIEJzCCAw+gAwIBAgIJANC3VWNs7fbTMA0GCSqGSIb3DQEBCwUAMIGpMQswCQYD
+VQQGEwJTRTERMA8GA1UECAwIU2vDg8KlbmUxDTALBgNVBAcMBEx1bmQxGzAZBgNV
+BAoMEklDT1MgQ2FyYm9uIFBvcnRhbDEfMB0GA1UECwwWQXV0aGVudGljYXRpb24g
+U2VydmljZTEaMBgGA1UEAwwRY3BhdXRoLmljb3MtY3AuZXUxHjAcBgkqhkiG9w0B
+CQEWD2luZm9AaWNvcy1jcC5ldTAeFw0xNTAyMDUxMjI0MzZaFw0yNTAyMDIxMjI0
+MzZaMIGpMQswCQYDVQQGEwJTRTERMA8GA1UECAwIU2vDg8KlbmUxDTALBgNVBAcM
+BEx1bmQxGzAZBgNVBAoMEklDT1MgQ2FyYm9uIFBvcnRhbDEfMB0GA1UECwwWQXV0
+aGVudGljYXRpb24gU2VydmljZTEaMBgGA1UEAwwRY3BhdXRoLmljb3MtY3AuZXUx
+HjAcBgkqhkiG9w0BCQEWD2luZm9AaWNvcy1jcC5ldTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAM2QN1jaZJeuPAH+4sVMZKk7vg4JIbUuTMKk0+KIAg5M
+XiVsRiEUjY+LtIncrvA/kf2CIySI0WkbwZMjcDd03hNj4kLWhuyxfOCwDO6DsUbG
+MbyI6HIYWXJp5ljfEEFgtMqT3dDtD5vwq8h4Zy20ukxOoIokKczrAvn4JjkMsj6Z
+0CEAFBC29o4E8PWQbUBgvt6Z+2ao+RHMLD7nZVBx98Occ9KfnYnDDd9Oi1XFe009
+zaSbcqY2RpN8I9hcW/KQf3KnGW5xZ5dr4rhGklCkYr+h0W3xKu+hin8bk91t1Dkr
+gaKl/N7M3Oof3k+7ZBlwaV97es5InWCeNgDxCGkBRNsCAwEAAaNQME4wHQYDVR0O
+BBYEFDcD7MVudooGaNRYqXBYqQi3VzGxMB8GA1UdIwQYMBaAFDcD7MVudooGaNRY
+qXBYqQi3VzGxMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABS02eZS
+weXGMJ2fEIy2JH0VhCbjuX/rz+8Hfh9LjzNb3QwKHuwP83yvPqRulV9FYmvOoK8T
+fMou5aW0mZ+QgJNKOrxY5vFxUq6pn3OiYbBu3m1C9ajbU/nx2evzt4+qUwTfHFb+
+ZgXpOtmxRekFzVvGZ18BSPJKwAAqqZ11X7skT/NwEAhbgplVPv9WkDmDzqNvHqQJ
+nyRgD2ZqUPU9nEOjGy0gI07dciVcYZQ+CiZeSECIWgQwjDEBDuwMCVAZA6gfdz6C
+KJuN+RUSKPEcxPxle1MiB4MU0ei5X4xUbvLWKn9Ok7TOXg2BpnMAv6eON1wVo0Aa
D265cqy6Le/toVg=