86baa9
From ec58278fa7fc6d0b5ae8340c4054005a7864086f Mon Sep 17 00:00:00 2001
86baa9
From: Christian Heimes <cheimes@redhat.com>
86baa9
Date: Tue, 26 Mar 2019 13:10:23 +0100
86baa9
Subject: [PATCH] Don't allow to hide last server for a role
86baa9
86baa9
DNSSec key master and CA renewal master can't be hidden. There must be
86baa9
at least one enabled server available for each role, too.
86baa9
86baa9
Fixes: https://pagure.io/freeipa/issue/7892
86baa9
Signed-off-by: Christian Heimes <cheimes@redhat.com>
86baa9
Reviewed-By: Thomas Woerner <twoerner@redhat.com>
86baa9
Reviewed-By: Francois Cami <fcami@redhat.com>
86baa9
---
86baa9
 ipaserver/plugins/server.py                   | 30 ++++++++++++++++++
86baa9
 .../test_replica_promotion.py                 | 31 +++++++++++++++++++
86baa9
 2 files changed, 61 insertions(+)
86baa9
86baa9
diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py
86baa9
index 0d144d13bca66b65de64328139fd7126eea24c89..bfd1406ff826aea97195aa08ca35018e35cac18c 100644
86baa9
--- a/ipaserver/plugins/server.py
86baa9
+++ b/ipaserver/plugins/server.py
86baa9
@@ -970,6 +970,35 @@ class server_state(crud.PKQuery):
86baa9
 
86baa9
     has_output = output.standard_boolean
86baa9
 
86baa9
+    def _check_hide_server(self, fqdn):
86baa9
+        result = self.api.Command.config_show()['result']
86baa9
+        err = []
86baa9
+        # single value entries
86baa9
+        if result.get("ca_renewal_master_server") == fqdn:
86baa9
+            err.append(_("Cannot hide CA renewal master."))
86baa9
+        if result.get("dnssec_key_master_server") == fqdn:
86baa9
+            err.append(_("Cannot hide DNSSec key master."))
86baa9
+        # multi value entries, only fail if we are the last one
86baa9
+        checks = [
86baa9
+            ("ca_server_server", "CA"),
86baa9
+            ("dns_server_server", "DNS"),
86baa9
+            ("ipa_master_server", "IPA"),
86baa9
+            ("kra_server_server", "KRA"),
86baa9
+        ]
86baa9
+        for key, name in checks:
86baa9
+            values = result.get(key, [])
86baa9
+            if values == [fqdn]:  # fqdn is the only entry
86baa9
+                err.append(
86baa9
+                    _("Cannot hide last enabled %(name)s server.") % {
86baa9
+                        'name': name
86baa9
+                    }
86baa9
+                )
86baa9
+        if err:
86baa9
+            raise errors.ValidationError(
86baa9
+                name=fqdn,
86baa9
+                error=' '.join(str(e) for e in err)
86baa9
+            )
86baa9
+
86baa9
     def execute(self, *keys, **options):
86baa9
         fqdn = keys[0]
86baa9
         if options['state'] == u'enabled':
86baa9
@@ -992,6 +1021,7 @@ class server_state(crud.PKQuery):
86baa9
         if to_status == ENABLED:
86baa9
             enable_services(fqdn)
86baa9
         else:
86baa9
+            self._check_hide_server(fqdn)
86baa9
             hide_services(fqdn)
86baa9
 
86baa9
         # update system roles
86baa9
diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
86baa9
index 9ce0074ea0df276b4800b306530d04c8274ece69..bf028bf7dc58abb6455ba1659f2d19bede69daa2 100644
86baa9
--- a/ipatests/test_integration/test_replica_promotion.py
86baa9
+++ b/ipatests/test_integration/test_replica_promotion.py
86baa9
@@ -812,7 +812,15 @@ class TestHiddenReplicaPromotion(IntegrationTest):
86baa9
 
86baa9
     @classmethod
86baa9
     def install(cls, mh):
86baa9
+        # master with DNSSEC master
86baa9
         tasks.install_master(cls.master, setup_dns=True, setup_kra=True)
86baa9
+        cls.master.run_command([
86baa9
+            "ipa-dns-install",
86baa9
+            "--dnssec-master",
86baa9
+            "--forwarder", cls.master.config.dns_forwarder,
86baa9
+            "-U",
86baa9
+        ])
86baa9
+        # hidden replica with CA and DNS
86baa9
         tasks.install_replica(
86baa9
             cls.master, cls.replicas[0],
86baa9
             setup_dns=True, setup_kra=True,
86baa9
@@ -879,6 +887,29 @@ class TestHiddenReplicaPromotion(IntegrationTest):
86baa9
         self._check_dnsrecords([self.master], [self.replicas[0]])
86baa9
         self._check_config([self.master], [self.replicas[0]])
86baa9
 
86baa9
+    def test_hide_master_fails(self):
86baa9
+        # verify state
86baa9
+        self._check_config([self.master], [self.replicas[0]])
86baa9
+        # nothing to do
86baa9
+        result = self.master.run_command([
86baa9
+            'ipa', 'server-state',
86baa9
+            self.master.hostname, '--state=enabled'
86baa9
+        ], raiseonerr=False)
86baa9
+        assert result.returncode == 1
86baa9
+        assert "no modifications to be performed" in result.stderr_text
86baa9
+        # hiding the last master fails
86baa9
+        result = self.master.run_command([
86baa9
+            'ipa', 'server-state',
86baa9
+            self.master.hostname, '--state=hidden'
86baa9
+        ], raiseonerr=False)
86baa9
+        assert result.returncode == 1
86baa9
+        keys = [
86baa9
+            "CA renewal master", "DNSSec key master", "CA server",
86baa9
+            "KRA server", "DNS server", "IPA server"
86baa9
+        ]
86baa9
+        for key in keys:
86baa9
+            assert key in result.stderr_text
86baa9
+
86baa9
     def test_hidden_replica_promote(self):
86baa9
         self.replicas[0].run_command([
86baa9
             'ipa', 'server-state',
86baa9
-- 
86baa9
2.20.1
86baa9