Blob Blame History Raw
From 48f20550f175503cb226bac47c570a2ff3e79be1 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabinsk@redhat.com>
Date: Fri, 12 May 2017 15:15:37 +0200
Subject: [PATCH] Add an attribute reporting client PKINIT-capable servers

A new multi-valued server attribute `pkinit_server` was added which
reports IPA masters that have PKINIT configuration usable by clients.

The existing tests were modified to allow for testing the new attribute.

https://pagure.io/freeipa/issue/6937

Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
---
 ipaserver/servroles.py                      |   7 ++
 ipatests/test_ipaserver/test_serverroles.py | 109 +++++++++++++---------------
 2 files changed, 59 insertions(+), 57 deletions(-)

diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
index 84fed1046b3b46dc9f5f8bbe6e03354725f1136c..f6e79338b9187aa741fe45b9fae42476cc65f724 100644
--- a/ipaserver/servroles.py
+++ b/ipaserver/servroles.py
@@ -625,4 +625,11 @@ attribute_instances = (
         u"DNSSEC",
         u"dnssecKeyMaster",
     ),
+    ServerAttribute(
+        u"pkinit_server_server",
+        u"PKINIT enabled server",
+        u"ipa_master_server",
+        u"KDC",
+        u"pkinitEnabled"
+    )
 )
diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py
index e671272783d8d71c2ee56074459433b98b79dd0a..b373a4d32f60e5ef48bcf07ac29162516113e8a8 100644
--- a/ipatests/test_ipaserver/test_serverroles.py
+++ b/ipatests/test_ipaserver/test_serverroles.py
@@ -58,7 +58,7 @@ _adtrust_agents = DN(
 
 
 master_data = {
-    'ca-dns-dnssec-keymaster': {
+    'ca-dns-dnssec-keymaster-pkinit-server': {
         'services': {
             'CA': {
                 'enabled': True,
@@ -72,14 +72,19 @@ master_data = {
             'DNSSEC': {
                 'enabled': True,
                 'config': ['DNSSecKeyMaster']
+            },
+            'KDC': {
+                'enabled': True,
+                'config': ['pkinitEnabled']
             }
         },
         'expected_roles': {
             'enabled': ['IPA master', 'CA server', 'DNS server']
         },
-        'expected_attributes': {'DNS server': 'dnssec_key_master_server'}
+        'expected_attributes': {'DNS server': 'dnssec_key_master_server',
+                                'IPA master': 'pkinit_server_server'}
     },
-    'ca-kra-renewal-master': {
+    'ca-kra-renewal-master-pkinit-server': {
         'services': {
             'CA': {
                 'enabled': True,
@@ -88,11 +93,16 @@ master_data = {
             'KRA': {
                 'enabled': True,
             },
+            'KDC': {
+                'enabled': True,
+                'config': ['pkinitEnabled']
+            },
         },
         'expected_roles': {
             'enabled': ['IPA master', 'CA server', 'KRA server']
         },
-        'expected_attributes': {'CA server': 'ca_renewal_master_server'}
+        'expected_attributes': {'CA server': 'ca_renewal_master_server',
+                                'IPA master': 'pkinit_server_server'}
     },
     'dns-trust-agent': {
         'services': {
@@ -234,7 +244,7 @@ class MockMasterTopology(object):
                 no_members=True,
                 raw=True)['result']}
 
-        self.existing_attributes = self._check_test_host_attributes()
+        self.original_dns_configs = self._remove_test_host_attrs()
 
     def iter_domain_data(self):
         MasterData = namedtuple('MasterData',
@@ -287,7 +297,6 @@ class MockMasterTopology(object):
             pass
 
     def _add_svc_entries(self, master_dn, svc_desc):
-        self._add_ipamaster_services(master_dn)
         for name in svc_desc:
             svc_dn = self.get_service_dn(name, master_dn)
             svc_mods = svc_desc[name]
@@ -298,6 +307,8 @@ class MockMasterTopology(object):
                     enabled=svc_mods['enabled'],
                     other_config=svc_mods.get('config', None)))
 
+        self._add_ipamaster_services(master_dn)
+
     def _remove_svc_master_entries(self, master_dn):
         try:
             entries = self.ldap.connection.search_s(
@@ -317,7 +328,11 @@ class MockMasterTopology(object):
         """
         for svc_name in self.ipamaster_services:
             svc_dn = self.get_service_dn(svc_name, master_dn)
-            self.ldap.add_entry(str(svc_dn), _make_service_entry_mods())
+            try:
+                self.api.Backend.ldap2.get_entry(svc_dn)
+            except errors.NotFound:
+                self.ldap.add_entry(
+                    str(svc_dn), _make_service_entry_mods())
 
     def _add_members(self, dn, fqdn, member_attrs):
         _entry, attrs = self.ldap.connection.search_s(
@@ -376,57 +391,36 @@ class MockMasterTopology(object):
         except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE):
             pass
 
-    def _check_test_host_attributes(self):
-        existing_attributes = set()
-
-        for service, value, attr_name in (
-                ('CA', 'caRenewalMaster', 'ca renewal master'),
-                ('DNSSEC', 'DNSSecKeyMaster', 'dnssec key master')):
+    def _remove_test_host_attrs(self):
+        original_dns_configs = []
 
-            svc_dn = DN(('cn', service), self.test_master_dn)
+        for attr_name in (
+                'caRenewalMaster', 'dnssecKeyMaster', 'pkinitEnabled'):
             try:
-                svc_entry = self.api.Backend.ldap2.get_entry(svc_dn)
+                svc_entry = self.api.Backend.ldap2.find_entry_by_attr(
+                    'ipaConfigString', attr_name, 'ipaConfigObject',
+                    base_dn=self.test_master_dn)
             except errors.NotFound:
                 continue
             else:
-                config_string_val = svc_entry.get('ipaConfigString', [])
+                original_dns_configs.append(
+                    (svc_entry.dn, list(svc_entry.get('ipaConfigString', [])))
+                )
+                svc_entry[u'ipaConfigString'].remove(attr_name)
+                self.api.Backend.ldap2.update_entry(svc_entry)
 
-                if value in config_string_val:
-                    existing_attributes.add(attr_name)
-
-        return existing_attributes
-
-    def _remove_ca_renewal_master(self):
-        if 'ca renewal master' not in self.existing_attributes:
-            return
+        return original_dns_configs
 
-        ca_dn = DN(('cn', 'CA'), self.test_master_dn)
-        ca_entry = self.api.Backend.ldap2.get_entry(ca_dn)
-
-        config_string_val = ca_entry.get('ipaConfigString', [])
-        try:
-            config_string_val.remove('caRenewalMaster')
-        except KeyError:
-            return
-
-        ca_entry.update({'ipaConfigString': config_string_val})
-        self.api.Backend.ldap2.update_entry(ca_entry)
-
-    def _restore_ca_renewal_master(self):
-        if 'ca renewal master' not in self.existing_attributes:
-            return
-
-        ca_dn = DN(('cn', 'CA'), self.test_master_dn)
-        ca_entry = self.api.Backend.ldap2.get_entry(ca_dn)
-
-        config_string_val = ca_entry.get('ipaConfigString', [])
-        config_string_val.append('caRenewalMaster')
-
-        ca_entry.update({'ipaConfigString': config_string_val})
-        self.api.Backend.ldap2.update_entry(ca_entry)
+    def _restore_test_host_attrs(self):
+        for dn, config in self.original_dns_configs:
+            try:
+                svc_entry = self.api.Backend.ldap2.get_entry(dn)
+                svc_entry['ipaConfigString'] = config
+                self.api.Backend.ldap2.update_entry(svc_entry)
+            except (errors.NotFound, errors.EmptyModlist):
+                continue
 
     def setup_data(self):
-        self._remove_ca_renewal_master()
         for master_data in self.iter_domain_data():
             # create host
             self._add_host_entry(master_data.fqdn)
@@ -449,7 +443,6 @@ class MockMasterTopology(object):
                     )
 
     def teardown_data(self):
-        self._restore_ca_renewal_master()
         for master_data in self.iter_domain_data():
             # first remove the master entries and service containers
             self._remove_svc_master_entries(master_data.dn)
@@ -466,6 +459,8 @@ class MockMasterTopology(object):
             # finally remove host entry
             self._del_host_entry(master_data.fqdn)
 
+        self._restore_test_host_attrs()
+
 
 @pytest.fixture(scope='module')
 def mock_api(request):
@@ -665,14 +660,14 @@ class TestServerRoleStatusRetrieval(object):
 
     def test_unknown_role_status_raises_notfound(self, mock_api, mock_masters):
         unknown_role = 'IAP maestr'
-        fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster')
+        fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster-pkinit-server')
         with pytest.raises(errors.NotFound):
             mock_api.Backend.serverroles.server_role_retrieve(
                 fqdn, unknown_role)
 
     def test_no_servrole_queries_all_roles_on_server(self, mock_api,
                                                      mock_masters):
-        master_name = 'ca-dns-dnssec-keymaster'
+        master_name = 'ca-dns-dnssec-keymaster-pkinit-server'
         enabled_roles = master_data[master_name]['expected_roles']['enabled']
         result = self.find_role(None, mock_api, mock_masters,
                                 master=master_name)
@@ -688,7 +683,7 @@ class TestServerRoleStatusRetrieval(object):
         invalid_substr = 'fwfgbb'
 
         assert (not self.find_role(invalid_substr, mock_api, mock_masters,
-                                   'ca-dns-dnssec-keymaster'))
+                                   'ca-dns-dnssec-keymaster-pkinit-server'))
 
 
 class TestServerAttributes(object):
@@ -706,7 +701,7 @@ class TestServerAttributes(object):
         actual_attr_masters = self.config_retrieve(
             assoc_role, mock_api)[attr_name]
 
-        assert actual_attr_masters == [fqdn]
+        assert fqdn in actual_attr_masters
 
     def test_set_attribute_on_the_same_provider_raises_emptymodlist(
             self, mock_api, mock_masters):
@@ -744,10 +739,10 @@ class TestServerAttributes(object):
         original_renewal_master = self.config_retrieve(
             role_name, mock_api)[attr_name]
 
-        other_ca_server = mock_masters.get_fqdn('trust-controller-ca')
+        other_ca_server = [mock_masters.get_fqdn('trust-controller-ca')]
 
         for host in (other_ca_server, original_renewal_master):
-            self.config_update(mock_api, **{attr_name: [host]})
+            self.config_update(mock_api, **{attr_name: host})
 
             assert (
-                self.config_retrieve(role_name, mock_api)[attr_name] == [host])
+                self.config_retrieve(role_name, mock_api)[attr_name] == host)
-- 
2.9.4