ac7d03
From 48f20550f175503cb226bac47c570a2ff3e79be1 Mon Sep 17 00:00:00 2001
ac7d03
From: Martin Babinsky <mbabinsk@redhat.com>
ac7d03
Date: Fri, 12 May 2017 15:15:37 +0200
ac7d03
Subject: [PATCH] Add an attribute reporting client PKINIT-capable servers
ac7d03
ac7d03
A new multi-valued server attribute `pkinit_server` was added which
ac7d03
reports IPA masters that have PKINIT configuration usable by clients.
ac7d03
ac7d03
The existing tests were modified to allow for testing the new attribute.
ac7d03
ac7d03
https://pagure.io/freeipa/issue/6937
ac7d03
ac7d03
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
ac7d03
Reviewed-By: Stanislav Laznicka <slaznick@redhat.com>
ac7d03
---
ac7d03
 ipaserver/servroles.py                      |   7 ++
ac7d03
 ipatests/test_ipaserver/test_serverroles.py | 109 +++++++++++++---------------
ac7d03
 2 files changed, 59 insertions(+), 57 deletions(-)
ac7d03
ac7d03
diff --git a/ipaserver/servroles.py b/ipaserver/servroles.py
ac7d03
index 84fed1046b3b46dc9f5f8bbe6e03354725f1136c..f6e79338b9187aa741fe45b9fae42476cc65f724 100644
ac7d03
--- a/ipaserver/servroles.py
ac7d03
+++ b/ipaserver/servroles.py
ac7d03
@@ -625,4 +625,11 @@ attribute_instances = (
ac7d03
         u"DNSSEC",
ac7d03
         u"dnssecKeyMaster",
ac7d03
     ),
ac7d03
+    ServerAttribute(
ac7d03
+        u"pkinit_server_server",
ac7d03
+        u"PKINIT enabled server",
ac7d03
+        u"ipa_master_server",
ac7d03
+        u"KDC",
ac7d03
+        u"pkinitEnabled"
ac7d03
+    )
ac7d03
 )
ac7d03
diff --git a/ipatests/test_ipaserver/test_serverroles.py b/ipatests/test_ipaserver/test_serverroles.py
ac7d03
index e671272783d8d71c2ee56074459433b98b79dd0a..b373a4d32f60e5ef48bcf07ac29162516113e8a8 100644
ac7d03
--- a/ipatests/test_ipaserver/test_serverroles.py
ac7d03
+++ b/ipatests/test_ipaserver/test_serverroles.py
ac7d03
@@ -58,7 +58,7 @@ _adtrust_agents = DN(
ac7d03
 
ac7d03
 
ac7d03
 master_data = {
ac7d03
-    'ca-dns-dnssec-keymaster': {
ac7d03
+    'ca-dns-dnssec-keymaster-pkinit-server': {
ac7d03
         'services': {
ac7d03
             'CA': {
ac7d03
                 'enabled': True,
ac7d03
@@ -72,14 +72,19 @@ master_data = {
ac7d03
             'DNSSEC': {
ac7d03
                 'enabled': True,
ac7d03
                 'config': ['DNSSecKeyMaster']
ac7d03
+            },
ac7d03
+            'KDC': {
ac7d03
+                'enabled': True,
ac7d03
+                'config': ['pkinitEnabled']
ac7d03
             }
ac7d03
         },
ac7d03
         'expected_roles': {
ac7d03
             'enabled': ['IPA master', 'CA server', 'DNS server']
ac7d03
         },
ac7d03
-        'expected_attributes': {'DNS server': 'dnssec_key_master_server'}
ac7d03
+        'expected_attributes': {'DNS server': 'dnssec_key_master_server',
ac7d03
+                                'IPA master': 'pkinit_server_server'}
ac7d03
     },
ac7d03
-    'ca-kra-renewal-master': {
ac7d03
+    'ca-kra-renewal-master-pkinit-server': {
ac7d03
         'services': {
ac7d03
             'CA': {
ac7d03
                 'enabled': True,
ac7d03
@@ -88,11 +93,16 @@ master_data = {
ac7d03
             'KRA': {
ac7d03
                 'enabled': True,
ac7d03
             },
ac7d03
+            'KDC': {
ac7d03
+                'enabled': True,
ac7d03
+                'config': ['pkinitEnabled']
ac7d03
+            },
ac7d03
         },
ac7d03
         'expected_roles': {
ac7d03
             'enabled': ['IPA master', 'CA server', 'KRA server']
ac7d03
         },
ac7d03
-        'expected_attributes': {'CA server': 'ca_renewal_master_server'}
ac7d03
+        'expected_attributes': {'CA server': 'ca_renewal_master_server',
ac7d03
+                                'IPA master': 'pkinit_server_server'}
ac7d03
     },
ac7d03
     'dns-trust-agent': {
ac7d03
         'services': {
ac7d03
@@ -234,7 +244,7 @@ class MockMasterTopology(object):
ac7d03
                 no_members=True,
ac7d03
                 raw=True)['result']}
ac7d03
 
ac7d03
-        self.existing_attributes = self._check_test_host_attributes()
ac7d03
+        self.original_dns_configs = self._remove_test_host_attrs()
ac7d03
 
ac7d03
     def iter_domain_data(self):
ac7d03
         MasterData = namedtuple('MasterData',
ac7d03
@@ -287,7 +297,6 @@ class MockMasterTopology(object):
ac7d03
             pass
ac7d03
 
ac7d03
     def _add_svc_entries(self, master_dn, svc_desc):
ac7d03
-        self._add_ipamaster_services(master_dn)
ac7d03
         for name in svc_desc:
ac7d03
             svc_dn = self.get_service_dn(name, master_dn)
ac7d03
             svc_mods = svc_desc[name]
ac7d03
@@ -298,6 +307,8 @@ class MockMasterTopology(object):
ac7d03
                     enabled=svc_mods['enabled'],
ac7d03
                     other_config=svc_mods.get('config', None)))
ac7d03
 
ac7d03
+        self._add_ipamaster_services(master_dn)
ac7d03
+
ac7d03
     def _remove_svc_master_entries(self, master_dn):
ac7d03
         try:
ac7d03
             entries = self.ldap.connection.search_s(
ac7d03
@@ -317,7 +328,11 @@ class MockMasterTopology(object):
ac7d03
         """
ac7d03
         for svc_name in self.ipamaster_services:
ac7d03
             svc_dn = self.get_service_dn(svc_name, master_dn)
ac7d03
-            self.ldap.add_entry(str(svc_dn), _make_service_entry_mods())
ac7d03
+            try:
ac7d03
+                self.api.Backend.ldap2.get_entry(svc_dn)
ac7d03
+            except errors.NotFound:
ac7d03
+                self.ldap.add_entry(
ac7d03
+                    str(svc_dn), _make_service_entry_mods())
ac7d03
 
ac7d03
     def _add_members(self, dn, fqdn, member_attrs):
ac7d03
         _entry, attrs = self.ldap.connection.search_s(
ac7d03
@@ -376,57 +391,36 @@ class MockMasterTopology(object):
ac7d03
         except (ldap.NO_SUCH_OBJECT, ldap.NO_SUCH_ATTRIBUTE):
ac7d03
             pass
ac7d03
 
ac7d03
-    def _check_test_host_attributes(self):
ac7d03
-        existing_attributes = set()
ac7d03
-
ac7d03
-        for service, value, attr_name in (
ac7d03
-                ('CA', 'caRenewalMaster', 'ca renewal master'),
ac7d03
-                ('DNSSEC', 'DNSSecKeyMaster', 'dnssec key master')):
ac7d03
+    def _remove_test_host_attrs(self):
ac7d03
+        original_dns_configs = []
ac7d03
 
ac7d03
-            svc_dn = DN(('cn', service), self.test_master_dn)
ac7d03
+        for attr_name in (
ac7d03
+                'caRenewalMaster', 'dnssecKeyMaster', 'pkinitEnabled'):
ac7d03
             try:
ac7d03
-                svc_entry = self.api.Backend.ldap2.get_entry(svc_dn)
ac7d03
+                svc_entry = self.api.Backend.ldap2.find_entry_by_attr(
ac7d03
+                    'ipaConfigString', attr_name, 'ipaConfigObject',
ac7d03
+                    base_dn=self.test_master_dn)
ac7d03
             except errors.NotFound:
ac7d03
                 continue
ac7d03
             else:
ac7d03
-                config_string_val = svc_entry.get('ipaConfigString', [])
ac7d03
+                original_dns_configs.append(
ac7d03
+                    (svc_entry.dn, list(svc_entry.get('ipaConfigString', [])))
ac7d03
+                )
ac7d03
+                svc_entry[u'ipaConfigString'].remove(attr_name)
ac7d03
+                self.api.Backend.ldap2.update_entry(svc_entry)
ac7d03
 
ac7d03
-                if value in config_string_val:
ac7d03
-                    existing_attributes.add(attr_name)
ac7d03
-
ac7d03
-        return existing_attributes
ac7d03
-
ac7d03
-    def _remove_ca_renewal_master(self):
ac7d03
-        if 'ca renewal master' not in self.existing_attributes:
ac7d03
-            return
ac7d03
+        return original_dns_configs
ac7d03
 
ac7d03
-        ca_dn = DN(('cn', 'CA'), self.test_master_dn)
ac7d03
-        ca_entry = self.api.Backend.ldap2.get_entry(ca_dn)
ac7d03
-
ac7d03
-        config_string_val = ca_entry.get('ipaConfigString', [])
ac7d03
-        try:
ac7d03
-            config_string_val.remove('caRenewalMaster')
ac7d03
-        except KeyError:
ac7d03
-            return
ac7d03
-
ac7d03
-        ca_entry.update({'ipaConfigString': config_string_val})
ac7d03
-        self.api.Backend.ldap2.update_entry(ca_entry)
ac7d03
-
ac7d03
-    def _restore_ca_renewal_master(self):
ac7d03
-        if 'ca renewal master' not in self.existing_attributes:
ac7d03
-            return
ac7d03
-
ac7d03
-        ca_dn = DN(('cn', 'CA'), self.test_master_dn)
ac7d03
-        ca_entry = self.api.Backend.ldap2.get_entry(ca_dn)
ac7d03
-
ac7d03
-        config_string_val = ca_entry.get('ipaConfigString', [])
ac7d03
-        config_string_val.append('caRenewalMaster')
ac7d03
-
ac7d03
-        ca_entry.update({'ipaConfigString': config_string_val})
ac7d03
-        self.api.Backend.ldap2.update_entry(ca_entry)
ac7d03
+    def _restore_test_host_attrs(self):
ac7d03
+        for dn, config in self.original_dns_configs:
ac7d03
+            try:
ac7d03
+                svc_entry = self.api.Backend.ldap2.get_entry(dn)
ac7d03
+                svc_entry['ipaConfigString'] = config
ac7d03
+                self.api.Backend.ldap2.update_entry(svc_entry)
ac7d03
+            except (errors.NotFound, errors.EmptyModlist):
ac7d03
+                continue
ac7d03
 
ac7d03
     def setup_data(self):
ac7d03
-        self._remove_ca_renewal_master()
ac7d03
         for master_data in self.iter_domain_data():
ac7d03
             # create host
ac7d03
             self._add_host_entry(master_data.fqdn)
ac7d03
@@ -449,7 +443,6 @@ class MockMasterTopology(object):
ac7d03
                     )
ac7d03
 
ac7d03
     def teardown_data(self):
ac7d03
-        self._restore_ca_renewal_master()
ac7d03
         for master_data in self.iter_domain_data():
ac7d03
             # first remove the master entries and service containers
ac7d03
             self._remove_svc_master_entries(master_data.dn)
ac7d03
@@ -466,6 +459,8 @@ class MockMasterTopology(object):
ac7d03
             # finally remove host entry
ac7d03
             self._del_host_entry(master_data.fqdn)
ac7d03
 
ac7d03
+        self._restore_test_host_attrs()
ac7d03
+
ac7d03
 
ac7d03
 @pytest.fixture(scope='module')
ac7d03
 def mock_api(request):
ac7d03
@@ -665,14 +660,14 @@ class TestServerRoleStatusRetrieval(object):
ac7d03
 
ac7d03
     def test_unknown_role_status_raises_notfound(self, mock_api, mock_masters):
ac7d03
         unknown_role = 'IAP maestr'
ac7d03
-        fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster')
ac7d03
+        fqdn = mock_masters.get_fqdn('ca-dns-dnssec-keymaster-pkinit-server')
ac7d03
         with pytest.raises(errors.NotFound):
ac7d03
             mock_api.Backend.serverroles.server_role_retrieve(
ac7d03
                 fqdn, unknown_role)
ac7d03
 
ac7d03
     def test_no_servrole_queries_all_roles_on_server(self, mock_api,
ac7d03
                                                      mock_masters):
ac7d03
-        master_name = 'ca-dns-dnssec-keymaster'
ac7d03
+        master_name = 'ca-dns-dnssec-keymaster-pkinit-server'
ac7d03
         enabled_roles = master_data[master_name]['expected_roles']['enabled']
ac7d03
         result = self.find_role(None, mock_api, mock_masters,
ac7d03
                                 master=master_name)
ac7d03
@@ -688,7 +683,7 @@ class TestServerRoleStatusRetrieval(object):
ac7d03
         invalid_substr = 'fwfgbb'
ac7d03
 
ac7d03
         assert (not self.find_role(invalid_substr, mock_api, mock_masters,
ac7d03
-                                   'ca-dns-dnssec-keymaster'))
ac7d03
+                                   'ca-dns-dnssec-keymaster-pkinit-server'))
ac7d03
 
ac7d03
 
ac7d03
 class TestServerAttributes(object):
ac7d03
@@ -706,7 +701,7 @@ class TestServerAttributes(object):
ac7d03
         actual_attr_masters = self.config_retrieve(
ac7d03
             assoc_role, mock_api)[attr_name]
ac7d03
 
ac7d03
-        assert actual_attr_masters == [fqdn]
ac7d03
+        assert fqdn in actual_attr_masters
ac7d03
 
ac7d03
     def test_set_attribute_on_the_same_provider_raises_emptymodlist(
ac7d03
             self, mock_api, mock_masters):
ac7d03
@@ -744,10 +739,10 @@ class TestServerAttributes(object):
ac7d03
         original_renewal_master = self.config_retrieve(
ac7d03
             role_name, mock_api)[attr_name]
ac7d03
 
ac7d03
-        other_ca_server = mock_masters.get_fqdn('trust-controller-ca')
ac7d03
+        other_ca_server = [mock_masters.get_fqdn('trust-controller-ca')]
ac7d03
 
ac7d03
         for host in (other_ca_server, original_renewal_master):
ac7d03
-            self.config_update(mock_api, **{attr_name: [host]})
ac7d03
+            self.config_update(mock_api, **{attr_name: host})
ac7d03
 
ac7d03
             assert (
ac7d03
-                self.config_retrieve(role_name, mock_api)[attr_name] == [host])
ac7d03
+                self.config_retrieve(role_name, mock_api)[attr_name] == host)
ac7d03
-- 
ac7d03
2.9.4
ac7d03