diff --git a/src/pyff/resource.py b/src/pyff/resource.py
index fe5ce6a3..0c8fbd14 100644
--- a/src/pyff/resource.py
+++ b/src/pyff/resource.py
@@ -339,6 +339,10 @@ def _replace(self, r: Resource) -> None:
raise ValueError("Resource {} not present - use add_child".format(r.url))
def add_child(self, url: str, opts: ResourceOpts) -> Resource:
+ """
+ Spent 3 man days. Make sure to make a deep copy of opts.
+
+ """
r = Resource(url, opts)
if r in self.children:
log.debug(f'\n\n{self}:\nURL {url}\nReplacing child {r}')
diff --git a/src/pyff/samlmd.py b/src/pyff/samlmd.py
index 671f32f4..c611a55c 100644
--- a/src/pyff/samlmd.py
+++ b/src/pyff/samlmd.py
@@ -187,9 +187,9 @@ def parse(self, resource: Resource, content: str) -> SAMLParserInfo:
resource.expire_time = expire_time
info.expiration_time = str(expire_time)
- def _extra_md(_t, info, **kwargs):
+ def _extra_md(_t, resource_opts, **kwargs):
entityID = kwargs.get('entityID')
- if info['alias'] != entityID:
+ if resource_opts['alias'] != entityID:
return _t
sp_entities = kwargs.get('sp_entities')
location = kwargs.get('location')
@@ -218,7 +218,8 @@ def _extra_md(_t, info, **kwargs):
if md_source is not None:
location = md_source.attrib.get('src')
if location is not None:
- child_opts = resource.opts.model_copy(update={'alias': entityID})
+ child_opts = resource.opts.model_copy(update={'alias': entityID}, deep=True)
+
r = resource.add_child(location, child_opts)
kwargs = {
'entityID': entityID,
@@ -320,12 +321,12 @@ def parse(self, resource: Resource, content: str) -> EidasMDParserInfo:
info.scheme_territory, location, fp, args.get('country_code')
)
)
- child_opts = resource.opts.model_copy(update={'alias': None})
+ child_opts = resource.opts.model_copy(update={'alias': None}, deep=True)
child_opts.verify = fp
r = resource.add_child(location, child_opts)
# this is specific post-processing for MDSL files
- def _update_entities(_t, **kwargs):
+ def _update_entities(_t, resource_opts, **kwargs):
_country_code = kwargs.get('country_code')
_hide_from_discovery = kwargs.get('hide_from_discovery')
for e in iter_entities(_t):
diff --git a/src/pyff/test/data/eidas/countries/FR.xml b/src/pyff/test/data/eidas/countries/FR.xml
new file mode 100644
index 00000000..4db01c9e
--- /dev/null
+++ b/src/pyff/test/data/eidas/countries/FR.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+ MIIECTCCAnGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJGUjEkMCIGA1UECgwb
+RlItRElOVU0tZUlEQVMtbm9kZS1wcmVwcm9kMB4XDTIyMDkyMjE0NDgwNloXDTMyMDkxOTE0NDgw
+NlowTDELMAkGA1UEBhMCRlIxJDAiBgNVBAoMG0ZSLURJTlVNLWVJREFTLW5vZGUtcHJlcHJvZDEX
+MBUGA1UEAwwOZUlEQVMtbWV0YWRhdGEwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDO
+B8ffvjFTk3rBzcyBiwFFMDlYPxIlj+i1BLdLvvzSJdUHKZhegOhzNuRVCeJfoyvml9vtlwK5tfzD
+iY4znZsFO/qIB1wROxrPVRq8AEw0LiPCfP1Ie1rvK2Ddaw0wUEI6wFn4ViAStP5wI3/yaOqN1cFf
+JceXUbvgVfjRS5ETRVZK7UER5vMeMIPn4ESkf86d+GB0rvZoipNOyymXfgs9RU/dvjd40lrRs5rZ
+Nc/l4dOrFUpxHXq/AfLsWKjmmOGx959qMmYtsK9KcQdEAQs2L/adKM+yLJL0JyHxyNnWuaUJDBwW
+xV5PK/hWnkLkebkgpeB5loF7f7Ra2MnB1uzZlaAa69tSILjycdw0cOcY5+hnH6QFq74JDL4WPDR8
+FzCdcN+TY7/HvG6cTR4lPdW/iY3eZQycQqkEccQiFITAeewwVwZbQddbWhHxdtJE8P73QoZl3iSp
+/TzP33Kr3im+IEX1o2PV6Ur36/cDNmc9WQWsRKTeydoIY+AOmV/odt0CAwEAAaMPMA0wCwYDVR0P
+BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBgQB2FpL/Xen8z318X2To+3nEdRthc/9OwPF9ZNzftcce
+QP4Q+lxuwKFacklt5VoXURy/JMsgPtSHhar5kiFZTm2SMXVSEqVDj4UzAobVnUutiZPsFoyPWr/c
+iEsWu0VSthsI31AUOvSTisy0w81rPKjNkRuCU5V+AS1Z5rBqMlkwOEiUxUMci+pZQ4VhH+mqg1oH
+1rMuJ1gysYf/zMjSWGlraSbcHApIMBAjs24uSTH/O6io+9k9jLhZGZv9LpssoeSPVz3wJY6h1LHT
+D5OHZYDtelO4X5ZCVRFx5kCaUpKtb4mvo72oWN9KlGvrqp6SkUF1ogovTl16sEf2FZHoN4r0+5ZN
+/vS/plTsCGbe46QPGuj+FO5yN6KclM+eonmBp0JSyoABfYN1T28a4dRwE8CPWCOgJmzxuwqSOH+P
+XlUbPftbx5nGsE3mRBAU1Jga2S4S+zJPHirc2lfZGh6ggdYG9kkqp7X7kiTyPyPuWU644+YaCOwv
+x7804cuLUcQ8VSQ=
+
+
+
+
+
+
+
+ MIIECTCCAnGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAzMQswCQYDVQQGEwJGUjEkMCIGA1UECgwb
+RlItRElOVU0tZUlEQVMtbm9kZS1wcmVwcm9kMB4XDTIyMDkyMjE0NDgwNloXDTMyMDkxOTE0NDgw
+NlowTDELMAkGA1UEBhMCRlIxJDAiBgNVBAoMG0ZSLURJTlVNLWVJREFTLW5vZGUtcHJlcHJvZDEX
+MBUGA1UEAwwOZUlEQVMtbWV0YWRhdGEwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDO
+B8ffvjFTk3rBzcyBiwFFMDlYPxIlj+i1BLdLvvzSJdUHKZhegOhzNuRVCeJfoyvml9vtlwK5tfzD
+iY4znZsFO/qIB1wROxrPVRq8AEw0LiPCfP1Ie1rvK2Ddaw0wUEI6wFn4ViAStP5wI3/yaOqN1cFf
+JceXUbvgVfjRS5ETRVZK7UER5vMeMIPn4ESkf86d+GB0rvZoipNOyymXfgs9RU/dvjd40lrRs5rZ
+Nc/l4dOrFUpxHXq/AfLsWKjmmOGx959qMmYtsK9KcQdEAQs2L/adKM+yLJL0JyHxyNnWuaUJDBwW
+xV5PK/hWnkLkebkgpeB5loF7f7Ra2MnB1uzZlaAa69tSILjycdw0cOcY5+hnH6QFq74JDL4WPDR8
+FzCdcN+TY7/HvG6cTR4lPdW/iY3eZQycQqkEccQiFITAeewwVwZbQddbWhHxdtJE8P73QoZl3iSp
+/TzP33Kr3im+IEX1o2PV6Ur36/cDNmc9WQWsRKTeydoIY+AOmV/odt0CAwEAAaMPMA0wCwYDVR0P
+BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBgQB2FpL/Xen8z318X2To+3nEdRthc/9OwPF9ZNzftcce
+QP4Q+lxuwKFacklt5VoXURy/JMsgPtSHhar5kiFZTm2SMXVSEqVDj4UzAobVnUutiZPsFoyPWr/c
+iEsWu0VSthsI31AUOvSTisy0w81rPKjNkRuCU5V+AS1Z5rBqMlkwOEiUxUMci+pZQ4VhH+mqg1oH
+1rMuJ1gysYf/zMjSWGlraSbcHApIMBAjs24uSTH/O6io+9k9jLhZGZv9LpssoeSPVz3wJY6h1LHT
+D5OHZYDtelO4X5ZCVRFx5kCaUpKtb4mvo72oWN9KlGvrqp6SkUF1ogovTl16sEf2FZHoN4r0+5ZN
+/vS/plTsCGbe46QPGuj+FO5yN6KclM+eonmBp0JSyoABfYN1T28a4dRwE8CPWCOgJmzxuwqSOH+P
+XlUbPftbx5nGsE3mRBAU1Jga2S4S+zJPHirc2lfZGh6ggdYG9kkqp7X7kiTyPyPuWU644+YaCOwv
+x7804cuLUcQ8VSQ=
+
+
+
+
diff --git a/src/pyff/test/data/eidas/countries/GR.xml b/src/pyff/test/data/eidas/countries/GR.xml
new file mode 100644
index 00000000..a8e1626e
--- /dev/null
+++ b/src/pyff/test/data/eidas/countries/GR.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+ MIIE5zCCA0+gAwIBAgIULMraEI5oF6p3T3LH0x/21gVFOvcwDQYJKoZIhvcNAQELBQAwgYkxCzAJ
+BgNVBAYTAkdSMScwJQYDVQQKDB5NaW5pc3RyeSBvZiBEaWdpdGFsIEdvdmVybmFuY2UxPTA7BgNV
+BAsMNGVJREFTIE5vZGUgTWV0YWRhdGEgU2lnbmluZyB0ZXN0aW5nIGVudmlyb25tZW50IDIwMjMx
+EjAQBgNVBAMMCUNvbm5lY3RvcjAeFw0yMzA5MjAxODIxMDlaFw0zMzA5MTcxODIxMDlaMIGJMQsw
+CQYDVQQGEwJHUjEnMCUGA1UECgweTWluaXN0cnkgb2YgRGlnaXRhbCBHb3Zlcm5hbmNlMT0wOwYD
+VQQLDDRlSURBUyBOb2RlIE1ldGFkYXRhIFNpZ25pbmcgdGVzdGluZyBlbnZpcm9ubWVudCAyMDIz
+MRIwEAYDVQQDDAlDb25uZWN0b3IwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC0PU5P
+pibubglyAQJDW0Q+iWiKXnfoBV6K5yFvzzE91UT/lE2UvX3haqqj8PwsfsJsPG7M805ky2UPY5nO
+U7VcnaQ3MWzz35kjNI1fvlXkR06YEkhBkr64WJ/7JTxf0wLO83ZRqBcvMqlDCqf8bPHXdjar89sS
+e866Wd2rthi7Tu6Ah6buF96lXpS2d9vwnf1S9mhOmtykQ53Vs5zgqMVOaHfKwCThefoYOCyzuqNP
+S1G0dQaRbXkIDpbniT96aap8Ksf0/Yx5E+o7BnvbnEsCJPHTYudTKqN7ljD8+Q4M2UNpMT05qIR7
+Zd2KfpufEV8o5YJzMCIftZH6sndBYbpDYkqM+Cd6qy84HBy2/UWgZDt2iQ1eML/Szk59wSm21szd
+Q4mshSv4rTwHuTtHg+ZeiCzwJGgvnQen3WR+TGm0YyHijfI9DqIvzbM8yXF8Abp00l3sA3Grm7wo
+E0YW+EbkiiJGUFrs88+P+cpcXeNkLltXSvhVosgF0JLcgoLl62UCAwEAAaNFMEMwEgYDVR0TAQH/
+BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFDu6OnhYjoq3nx9jmT8vwQ5tjFWD
+MA0GCSqGSIb3DQEBCwUAA4IBgQARw/klO118DF5BNunXqsiExeQSQqb+n/qcBXDYLypQJomO/JAs
+VEg4FEn05Z26QxyacwWxVqWhInVLFVhXgBJ5vnRTwK7RUkJCDQ+9u00oHd3JeOvygFjc7DoC5SWJ
+q9hnN+pX1qK9yI+R9hs7cKPKpQ+MaNPWl0yVQCE53GZrWk+Skgl2T5/s0rY+LTYUx5d21kxq1tKE
+cpzy9di+33w05uv7ozh7xcbL91wj7zbdDSibxpXmFdlY6C8BH4DOI0kRoYjWKpbuQnPrEbhQwZjI
+V9S5OcEaPyiYkVB9n4Z0z3AMTD5G4X52gKmdSh/iTuZuYP4Vj1hbgNYpMHI7B5fnXisa26e2DzT4
+OFEots8gDlsf26WBEHQcJnrNzqCPTd4Zyl5Jpzg+Vo/dwknIESuZYn2l+Iu1GJCMIV+RrI/LoPD4
+FRGrw9YbcFOqAgmSdqxRj6fSb2W5WanIvc7OAT0hKQjPu1jYHDGIeXipKf1rBLjpRF/xtzU/xb4m
+JDmr+yA=
+
+
+
+
+
+
+
+ MIIE7TCCA1WgAwIBAgIUTo2pg82nNFjKFzNyHnCbQu1rEVYwDQYJKoZIhvcNAQELBQAwgYwxCzAJ
+BgNVBAYTAkdSMScwJQYDVQQKDB5NaW5pc3RyeSBvZiBEaWdpdGFsIEdvdmVybmFuY2UxPTA7BgNV
+BAsMNGVJREFTIE5vZGUgTWV0YWRhdGEgU2lnbmluZyB0ZXN0aW5nIGVudmlyb25tZW50IDIwMjMx
+FTATBgNVBAMMDFByb3h5U2VydmljZTAeFw0yMzA5MjAxODM5MDZaFw0zMzA5MTcxODM5MDZaMIGM
+MQswCQYDVQQGEwJHUjEnMCUGA1UECgweTWluaXN0cnkgb2YgRGlnaXRhbCBHb3Zlcm5hbmNlMT0w
+OwYDVQQLDDRlSURBUyBOb2RlIE1ldGFkYXRhIFNpZ25pbmcgdGVzdGluZyBlbnZpcm9ubWVudCAy
+MDIzMRUwEwYDVQQDDAxQcm94eVNlcnZpY2UwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIB
+gQCqNGRL80G3XrF5wgtJIOCglAg8361zga2Hup8G+w6eQdP5bUyX9JZskTr9IUNeajUw7sRGN9GC
+iflAILmzHqySnspOZZer4bUaOEKhRE0RZnhWoCyeZgdp1j9bwe2uLgRJtLQWpeGq3kEzCoSqul70
+iLegKd85f8i0S5ZgzdpjSBJcetGwQxV8bw1+3IT4/OXrL467Z2tBvPE1AClf0ETkw9y5vfI1O+Vr
+OHJg8ywIKUgLdfgpCpjHGzljOVlA1ZbCplPvKOOkjRKx7BWAFwFqUbSLYjVDrhIHkCv+EsGeHLK4
+aLmxAVVpwj5qhlnxJ7vweKUEorw7GUGHhAmiM9bem5Wc3jakt06Hd8vA6/kn7Yr17feZtaBfWHAP
+HoH0nvGLHk6+WU3W3/i89KLnYH+JsGfY8vSOQesaavmZy6WTEmXk6AkDrUocdC0IrMhq8duOiN3u
+KrzN9t0vaRb6KMr35x7l9Sq6hxyiIjfZW+qB8434HmxKyZODCcXEncpNK8MCAwEAAaNFMEMwEgYD
+VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFM6pVXDExVEGIVCJbVKV
+/S9m7zFtMA0GCSqGSIb3DQEBCwUAA4IBgQCmADqhrXNb4iOsSOkHBghndhsT/lowKPtCicLrwfmm
+twQtwA/H1JILe11r97LxIuDmFi0wdSIwx+E0ioA3BF4kcddXrYDimQD2LDQZIWFYHaMhAII89Kfv
+VXnn9Ox048cJHsKrteMchCOKw1xxZYsOKr1dFPiQQOX+mP/S1aBnj+7Sr4GuTSkWXC4OiO+BaZQW
+4mWe83DsngdUjHmCDrvnBT3xzUbJx5hEky2lnU1ZFZtusH6v6VElQ1KPgDBov1dTktB+r44v47DO
+WZ7GUkwvoOVS0lpl7nCpD9QBBch0JmAyPI9RvVRD5vaPghD+Jv5JEiQtMJwcE2l1GYLcwo5Q1BOu
+FsLJQEEmD7PUm0ZisV7GTQbRqh2YcLSHMrXOMvbvBlNdEuTDrdMvKFK/FMGh+4DHh/J3+hklGw+e
+X+U++5GGUbOS6nZtDwmW/KBYMSnanWyWJ1cnhos7A2JkL429B6uhZsPIHAQn26LBz8yGx3vxze8D
+wBm+w1VVhk19CHc=
+
+
+
+
diff --git a/src/pyff/test/data/eidas/eidas.xml b/src/pyff/test/data/eidas/eidas.xml
new file mode 100644
index 00000000..f9d28c92
--- /dev/null
+++ b/src/pyff/test/data/eidas/eidas.xml
@@ -0,0 +1,18 @@
+
+
+
+ Swedish E-Identification Board
+ urn:se:elegnamnden:eidas:mdlist:local
+ SE
+
+
+
+
+
+ https://qa.md.eidas.swedenconnect.se/mdservicelist-aggregate.xml
+
+
diff --git a/src/pyff/test/test_mdsl.py b/src/pyff/test/test_mdsl.py
new file mode 100644
index 00000000..3ec44d75
--- /dev/null
+++ b/src/pyff/test/test_mdsl.py
@@ -0,0 +1,124 @@
+import json
+import os
+import shutil
+import sys
+import tempfile
+
+import pytest
+import six
+import yaml
+from mako.lookup import TemplateLookup
+from mock import patch
+
+from pyff import builtins
+from pyff.exceptions import MetadataException
+from pyff.parse import ParserException
+from pyff.pipes import PipeException, Plumbing, plumbing
+from pyff.repo import MDRepository
+from pyff.resource import ResourceException
+from pyff.test import ExitException, SignerTestCase
+from pyff.utils import hash_id, parse_xml, resource_filename, root, dumptree
+from pyff.constants import NS
+
+
+__author__ = 'leifj'
+
+# The 'builtins' import appears unused to static analysers, ensure it isn't removed
+assert builtins is not None
+
+
+class PipeLineTest(SignerTestCase):
+ @pytest.fixture(autouse=True)
+ def _capsys(self, capsys):
+ self._capsys = capsys
+
+ @property
+ def captured_stdout(self) -> str:
+ """ Return anything written to STDOUT during this test """
+ out, _err = self._capsys.readouterr() # type: ignore
+ return out
+
+ @property
+ def captured_stderr(self) -> str:
+ """ Return anything written to STDERR during this test """
+ _out, err = self._capsys.readouterr() # type: ignore
+ return err
+
+ @pytest.fixture(autouse=True)
+ def _caplog(self, caplog):
+ """ Return anything written to the logging system during this test """
+ self._caplog = caplog
+
+ @property
+ def captured_log_text(self) -> str:
+ return self._caplog.text # type: ignore
+
+ def run_pipeline(self, pl_name, ctx=None, md=None):
+ if ctx is None:
+ ctx = dict()
+
+ if md is None:
+ md = MDRepository()
+
+ templates = TemplateLookup(directories=[os.path.join(self.datadir, 'simple-pipeline')])
+ pipeline = tempfile.NamedTemporaryFile('w').name
+ template = templates.get_template(pl_name)
+ with open(pipeline, "w") as fd:
+ fd.write(template.render(ctx=ctx))
+ res = plumbing(pipeline).process(md, state={'batch': True, 'stats': {}})
+ os.unlink(pipeline)
+ return res, md, ctx
+
+ def exec_pipeline(self, pstr):
+ md = MDRepository()
+ p = yaml.safe_load(six.StringIO(pstr))
+ print("\n{}".format(yaml.dump(p)))
+ pl = Plumbing(p, pid="test")
+ res = pl.process(md, state={'batch': True, 'stats': {}})
+ return res, md
+
+ @classmethod
+ def setUpClass(cls):
+ SignerTestCase.setUpClass()
+
+ def setUp(self):
+ SignerTestCase.setUpClass()
+ self.templates = TemplateLookup(directories=[os.path.join(self.datadir, 'simple-pipeline')])
+
+
+class ParseTest(PipeLineTest):
+ def test_eidas_country(self):
+ tmpfile = tempfile.NamedTemporaryFile('w').name
+ try:
+ self.exec_pipeline(f"""
+- when eidas:
+ - xslt:
+ stylesheet: eidas-cleanup.xsl
+ - break
+
+- load:
+ - file://{self.datadir}/eidas/eidas.xml cleanup eidas
+- select
+- publish: {tmpfile}
+"""
+ )
+ xml = parse_xml(tmpfile)
+ assert xml is not None
+ entityID = "https://pre.eidas.gov.gr/EidasNode/ServiceMetadata"
+ with_hide_from_discovery = xml.find("{%s}EntityDescriptor[@entityID='%s']" % (NS['md'], entityID))
+ assert with_hide_from_discovery is not None
+ search = "{%s}Extensions/{%s}EntityAttributes/{%s}Attribute[@Name='%s']" % (NS['md'], NS['mdattr'], NS['saml'],'http://macedir.org/entity-category')
+ ecs = with_hide_from_discovery.find(search)
+ assert ecs is not None
+ entityID2 = "https://eidas.pp.dev-franceconnect.fr/EidasNode/ServiceMetadata"
+ without_hide_from_discovery = xml.find("{%s}EntityDescriptor[@entityID='%s']" % (NS['md'], entityID2))
+ ecs2 = without_hide_from_discovery.find(search)
+ assert ecs2 is None
+ except IOError:
+ pass
+ finally:
+ try:
+ #os.unlink(tmpfile)
+ pass
+ except (IOError, OSError):
+ pass