From aa721f2b8e59b47d8258cf030fdd91222be02017 Mon Sep 17 00:00:00 2001
From: Emmanuel Cazenave <ecazenave@entrouvert.com>
Date: Wed, 3 Nov 2021 17:49:16 +0100
Subject: [PATCH] check_hobo: delete orphan idp from configuration (#54380)

---
 tests/idp2_metadata.xml | 18 ++++++++++++++++++
 tests/test_hobo.py      | 36 +++++++++++++++++++++++++++++++++++-
 tests/utilities.py      |  4 ++++
 wcs/ctl/check_hobos.py  | 11 +++++++++++
 4 files changed, 68 insertions(+), 1 deletion(-)
 create mode 100644 tests/idp2_metadata.xml

diff --git a/tests/idp2_metadata.xml b/tests/idp2_metadata.xml
new file mode 100644
index 00000000..c4f941df
--- /dev/null
+++ b/tests/idp2_metadata.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<ns0:EntityDescriptor xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ns1="http://www.w3.org/2000/09/xmldsig#" entityID="http://authentic2.example.net/idp/saml2/metadata">
+  <ns0:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+    <ns0:KeyDescriptor>
+      <ns1:KeyInfo>
+        <ns1:X509Data>
+          <ns1:X509Certificate>MIIDKTCCAhGgAwIBAgIUAZvHckWYsjUA9g5NoWeVThoHiPcwDQYJKoZIhvcNAQEL BQAwJDEiMCAGA1UEAwwZYXV0aGVudGljLmRldi5wdWJsaWsubG92ZTAeFw0yMTEx MDIxMzEwMTRaFw0zMTExMDIxMzEwMTRaMCQxIjAgBgNVBAMMGWF1dGhlbnRpYy5k ZXYucHVibGlrLmxvdmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+ vkmsB8DpBfDiwWOmRPD8I5e+Lhi6sb70T1y23ZvZ7PDBmPO0KQ96qp1BANOEWOVV OkCjwXgJg1NqdbnmqEEZyVYFvPw67nzPRaFVSCoBqIheTfY6yfUlFyFHNDXlhhXE FqL2WFUa7ANmPIVQMDo8vXOh8L33Ks5UJXKNpEIlNYJfOpxxo5xrJ+lcmrLqfdzk 7lBRuO1qm9a4jcI5ehwTU76PdMj6PjhH6NO5DfV3Fhe0/ovIXI0cjCUM1jMn4zhb G7hY4uWCYoGtI9czKUoP05++BtEX0hlJm3auHVD6a0iXsa5AXm9QWMfG5OCdRxNx SPsbJrZgSaH3QbRSkXvlAgMBAAGjUzBRMB0GA1UdDgQWBBR1TZp46wgtXoQyEwkX 8gyokc6GtzAfBgNVHSMEGDAWgBR1TZp46wgtXoQyEwkX8gyokc6GtzAPBgNVHRMB Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCZ9EDUHQBZsCk0keM31UO7IvwU dRvcaJwBABfjPl1RbolW1F997qUYjVaZXLRIduGBy9pIdEu9PYdpg4WT/lEa4JCV k7C1QJ6bio1GI0nTzVhbmd2Z1yr87ymEya95irlmdHiLA30CvyhDe6y5IlWiuUKG ol4u40DgzA9jS+qR9RHg4wwxDIixKV3XLQxiChM4sF2SlJdqpPgzlPFH7nqgHP4Q LUtSr0wmKf9DdwiI6QsgN2GLG9n15oU9kmAgezOW0N8p+VBAP+eK4sbVIDfUcvx4 8Nj/JyI1gCNZRTRCLHGs1KnDDQ0EtMCPtWlGO0kDypg4vgwm1lxdW2+xB7ym</ns1:X509Certificate>
+        </ns1:X509Data>
+      </ns1:KeyInfo>
+    </ns0:KeyDescriptor>
+    <ns0:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://authentic2.example.net/idp/saml2/artifact" index="0"/>
+    <ns0:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://authentic2.example.net/idp/saml2/slo" ResponseLocation="http://authentic2.example.net/idp/saml2/slo_return"/>
+    <ns0:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://authentic2.example.net/idp/saml2/slo" ResponseLocation="http://authentic2.example.net/idp/saml2/slo_return"/>
+    <ns0:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://authentic2.example.net/idp/saml2/slo/soap"/>
+    <ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://authentic2.example.net/idp/saml2/sso"/>
+    <ns0:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://authentic2.example.net/idp/saml2/sso"/>
+  </ns0:IDPSSODescriptor>
+</ns0:EntityDescriptor>
diff --git a/tests/test_hobo.py b/tests/test_hobo.py
index d7dc6bb5..a55ccf35 100644
--- a/tests/test_hobo.py
+++ b/tests/test_hobo.py
@@ -363,13 +363,47 @@ def test_configure_authentication_methods(http_requests):
     # with real metadata
     hobo_cmd.configure_authentication_methods(service, pub)
 
-    assert len(pub.cfg['idp'].keys()) == 1
+    idp_keys = list(pub.cfg['idp'].keys())
+    assert len(idp_keys) == 1
+    assert pub.cfg['idp'][idp_keys[0]]['metadata_url'] == 'http://authentic.example.net/idp/saml2/metadata'
     assert pub.cfg['saml_identities']['registration-url']
     assert pub.cfg['sp']['idp-manage-user-attributes']
     assert pub.cfg['sp']['idp-manage-roles']
     assert pub.get_site_option('idp_account_url', 'variables').endswith('/accounts/')
     assert pub.get_site_option('idp_session_cookie_name') == 'a2-opened-session-5aef2f'
 
+    # change idp
+    new_hobo_json = copy.deepcopy(HOBO_JSON)
+    new_authentic_service = {
+        'service-id': 'authentic',
+        'saml-idp-metadata-url': 'http://authentic2.example.net/idp/saml2/metadata',
+        'template_name': '',
+        'variables': {},
+        'title': 'Authentic 2',
+        'base_url': 'http://authentic2.example.net/',
+        'id': 3,
+        'slug': 'authentic-2',
+        'secret_key': '6789',
+    }
+    index = None
+    for i, service in enumerate(new_hobo_json['services']):
+        if service['service-id'] == 'authentic':
+            index = i
+            break
+    new_hobo_json['services'][index] = new_authentic_service
+    try:
+        hobo_cmd.all_services = new_hobo_json
+
+        hobo_cmd.configure_authentication_methods(service, pub)
+        idp_keys = list(pub.cfg['idp'].keys())
+        assert len(idp_keys) == 1
+        # idp changed
+        assert (
+            pub.cfg['idp'][idp_keys[0]]['metadata_url'] == 'http://authentic2.example.net/idp/saml2/metadata'
+        )
+    finally:
+        hobo_cmd.all_services = HOBO_JSON
+
 
 def test_deploy():
     cleanup()
diff --git a/tests/utilities.py b/tests/utilities.py
index bb069efe..f35e86b3 100644
--- a/tests/utilities.py
+++ b/tests/utilities.py
@@ -363,6 +363,9 @@ class HttpRequestsMocking:
 
         with open(os.path.join(os.path.dirname(__file__), 'idp_metadata.xml')) as fd:
             metadata = fd.read()
+        with open(os.path.join(os.path.dirname(__file__), 'idp2_metadata.xml')) as fd:
+            metadata2 = fd.read()
+
         geojson = {
             'features': [
                 {
@@ -418,6 +421,7 @@ class HttpRequestsMocking:
             ),
             'http://remote.example.net/connection-error': (None, None, None),
             'http://authentic.example.net/idp/saml2/metadata': (200, metadata, None),
+            'http://authentic2.example.net/idp/saml2/metadata': (200, metadata2, None),
         }.get(base_url, (200, '', {}))
 
         if url.startswith('file://'):
diff --git a/wcs/ctl/check_hobos.py b/wcs/ctl/check_hobos.py
index 37c0de34..3458fb17 100644
--- a/wcs/ctl/check_hobos.py
+++ b/wcs/ctl/check_hobos.py
@@ -346,6 +346,17 @@ class CmdCheckHobos(Command):
         # automatically and we don't want to lose our changes.
         pub.write_cfg()
 
+        if 'idp' in pub.cfg:
+            idp_urls = [idp['saml-idp-metadata-url'] for idp in idps]
+            # clean up configuration
+            to_delete = []
+            for idp_key, idp in pub.cfg['idp'].items():
+                if idp['metadata_url'] not in idp_urls:
+                    to_delete.append(idp_key)
+            for idp_key in to_delete:
+                del pub.cfg['idp'][idp_key]
+            pub.write_cfg()
+
         for idp in idps:
             if not idp['base_url'].endswith('/'):
                 idp['base_url'] = idp['base_url'] + '/'
-- 
2.30.2

