diff options
-rw-r--r-- | ipalib/plugins/dns.py | 23 | ||||
-rw-r--r-- | ipalib/plugins/realmdomains.py | 50 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_dns_plugin.py | 8 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_dns_realmdomains_integration.py | 168 |
4 files changed, 248 insertions, 1 deletions
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py index becec1423..d59df59a2 100644 --- a/ipalib/plugins/dns.py +++ b/ipalib/plugins/dns.py @@ -1841,6 +1841,18 @@ class dnszone_add(LDAPCreate): dns_record, nameserver_ip_address) + # Add entry to realmdomains + # except for our own domain, forwarded zones and reverse zones + zone = keys[0] + + if (zone != api.env.domain + and not options.get('idnsforwarders') + and not zone_is_reverse(zone)): + try: + api.Command['realmdomains_mod'](add_domain=zone, force=True) + except errors.EmptyModlist: + pass + return dn api.register(dnszone_add) @@ -1857,6 +1869,17 @@ class dnszone_del(LDAPDelete): force=True) except errors.NotFound: pass + + # Delete entry from realmdomains + # except for our own domain + zone = keys[0] + + if zone != api.env.domain: + try: + api.Command['realmdomains_mod'](del_domain=zone, force=True) + except errors.AttrValueNotFound: + pass + return True api.register(dnszone_del) diff --git a/ipalib/plugins/realmdomains.py b/ipalib/plugins/realmdomains.py index f3dbf8dae..cff193f20 100644 --- a/ipalib/plugins/realmdomains.py +++ b/ipalib/plugins/realmdomains.py @@ -49,6 +49,10 @@ EXAMPLES: """) +def _domain_name_normalizer(d): + return d.lower().rstrip('.') + + class realmdomains(LDAPObject): """ List of domains associated with IPA realm. @@ -64,16 +68,19 @@ class realmdomains(LDAPObject): takes_params = ( Str('associateddomain+', _domain_name_validator, + normalizer=_domain_name_normalizer, cli_name='domain', label=_('Domain'), ), Str('add_domain?', _domain_name_validator, + normalizer=_domain_name_normalizer, cli_name='add_domain', label=_('Add domain'), ), Str('del_domain?', _domain_name_validator, + normalizer=_domain_name_normalizer, cli_name='del_domain', label=_('Delete domain'), ), @@ -133,6 +140,49 @@ class realmdomains_mod(LDAPUpdate): entry_attrs['associateddomain'] = domains return dn + def execute(self, *keys, **options): + dn = self.obj.get_dn(*keys, **options) + ldap = self.obj.backend + + domains_old = set(ldap.get_entry(dn)[1]['associateddomain']) + result = super(realmdomains_mod, self).execute(*keys, **options) + domains_new = set(ldap.get_entry(dn)[1]['associateddomain']) + + domains_added = domains_new - domains_old + domains_deleted = domains_old - domains_new + + # Add a _kerberos TXT record for zones that correspond with + # domains which were added + for d in domains_added: + # Skip our own domain + if d == api.env.domain: + continue + try: + api.Command['dnsrecord_add']( + unicode(d), + u'_kerberos', + txtrecord=api.env.realm + ) + except (errors.EmptyModlist, errors.NotFound): + pass + + # Delete _kerberos TXT record from zones that correspond with + # domains which were deleted + for d in domains_deleted: + # Skip our own domain + if d == api.env.domain: + continue + try: + api.Command['dnsrecord_del']( + unicode(d), + u'_kerberos', + txtrecord=api.env.realm + ) + except (errors.AttrValueNotFound, errors.NotFound): + pass + + return result + api.register(realmdomains_mod) diff --git a/tests/test_xmlrpc/test_dns_plugin.py b/tests/test_xmlrpc/test_dns_plugin.py index 7b14fc670..24fc7ecc4 100644 --- a/tests/test_xmlrpc/test_dns_plugin.py +++ b/tests/test_xmlrpc/test_dns_plugin.py @@ -34,6 +34,7 @@ dnszone1_rname = u'root.%s.' % dnszone1 dnszone1_permission = u'Manage DNS zone %s' % dnszone1 dnszone1_permission_dn = DN(('cn',dnszone1_permission), api.env.container_permission,api.env.basedn) +dnszone1_txtrec_dn = DN(('idnsname', '_kerberos'), dnszone1_dn) dnszone2 = u'dnszone2.test' dnszone2_dn = DN(('idnsname', dnszone2), api.env.container_dns, api.env.basedn) dnszone2_mname = u'ns1.%s.' % dnszone2 @@ -526,7 +527,7 @@ class test_dns(Declarative): command=('dnsrecord_find', [dnszone1], {}), expected={ 'summary': None, - 'count': 3, + 'count': 4, 'truncated': False, 'result': [ { @@ -535,6 +536,11 @@ class test_dns(Declarative): 'idnsname': [u'@'], }, { + 'dn': dnszone1_txtrec_dn, + 'txtrecord': [api.env.realm], + 'idnsname': [u'_kerberos'], + }, + { 'dn': dnszone1_mname_dn, 'idnsname': [u'ns1'], 'arecord': [u'1.2.3.4'], diff --git a/tests/test_xmlrpc/test_dns_realmdomains_integration.py b/tests/test_xmlrpc/test_dns_realmdomains_integration.py new file mode 100644 index 000000000..c4660a577 --- /dev/null +++ b/tests/test_xmlrpc/test_dns_realmdomains_integration.py @@ -0,0 +1,168 @@ +# Authors: +# Ana Krivokapic <akrivoka@redhat.com> +# +# Copyright (C) 2013 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +""" +Test integration of DNS and realmdomains. +1. dnszone_{add,del} should create/delete appropriate entry in realmdomains. +2. realmdomains_mod should add a _kerberos TXT record in the DNS zone. +""" + +from ipalib import api, errors +from ipapython.dn import DN +from tests.test_xmlrpc import objectclasses +from xmlrpc_test import Declarative, fuzzy_digits + + +cn = u'Realm Domains' +dn = DN(('cn', cn), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn) +our_domain = api.env.domain +dnszone_1 = u'dnszone.test' +dnszone_1_dn = DN(('idnsname', dnszone_1), api.env.container_dns, + api.env.basedn) +idnssoamname = u'ns1.%s.' % dnszone_1 +idnssoarname = u'root.%s.' % dnszone_1 +dnszone_2 = u'dnszone2.test' +dnszone_2_dn = DN(('idnsname', dnszone_2), api.env.container_dns, + api.env.basedn) + + +def assert_realmdomain_and_txt_record_present(response): + zone = response['value'] + + r = api.Command['realmdomains_show']() + assert zone in r['result']['associateddomain'] + + r = api.Command['dnsrecord_show'](zone, u'_kerberos') + assert api.env.realm in r['result']['txtrecord'] + + return True + + +def assert_realmdomain_and_txt_record_not_present(response): + zone = response['value'] + + r = api.Command['realmdomains_show']() + assert zone not in r['result']['associateddomain'] + + try: + api.Command['dnsrecord_show'](zone, u'_kerberos') + except errors.NotFound: + return True + + +class test_dns_realmdomains_integration(Declarative): + cleanup_commands = [ + ('realmdomains_mod', [], {'associateddomain': [our_domain]}), + ('dnszone_del', [dnszone_1, dnszone_2], {'continue': True}), + ] + + tests = [ + dict( + desc='Check realmdomain and TXT record get created ' + 'during dnszone_add', + command=( + 'dnszone_add', [dnszone_1], { + 'idnssoamname': idnssoamname, + 'idnssoarname': idnssoarname, + 'ip_address': u'1.2.3.4', + } + ), + expected={ + 'value': dnszone_1, + 'summary': None, + 'result': { + 'dn': dnszone_1_dn, + 'idnsname': [dnszone_1], + 'idnszoneactive': [u'TRUE'], + 'idnssoamname': [idnssoamname], + 'nsrecord': [idnssoamname], + 'idnssoarname': [idnssoarname], + 'idnssoaserial': [fuzzy_digits], + 'idnssoarefresh': [fuzzy_digits], + 'idnssoaretry': [fuzzy_digits], + 'idnssoaexpire': [fuzzy_digits], + 'idnssoaminimum': [fuzzy_digits], + 'idnsallowdynupdate': [u'FALSE'], + 'idnsupdatepolicy': [u'grant %(realm)s krb5-self * A; ' + u'grant %(realm)s krb5-self * AAAA; ' + u'grant %(realm)s krb5-self * SSHFP;' + % dict(realm=api.env.realm)], + 'idnsallowtransfer': [u'none;'], + 'idnsallowquery': [u'any;'], + 'objectclass': objectclasses.dnszone, + + }, + }, + extra_check=assert_realmdomain_and_txt_record_present, + ), + + dict( + desc='Check realmdomain and TXT record do not get created ' + 'during dnszone_add for forwarded zone', + command=( + 'dnszone_add', [dnszone_2], { + 'idnssoamname': idnssoamname, + 'idnssoarname': idnssoarname, + 'idnsforwarders': u'1.2.3.4', + 'idnsforwardpolicy': u'only', + 'force': True, + } + ), + expected={ + 'value': dnszone_2, + 'summary': None, + 'result': { + 'dn': dnszone_2_dn, + 'idnsname': [dnszone_2], + 'idnszoneactive': [u'TRUE'], + 'idnssoamname': [idnssoamname], + 'idnsforwarders': [u'1.2.3.4'], + 'idnsforwardpolicy': [u'only'], + 'nsrecord': [idnssoamname], + 'idnssoarname': [idnssoarname], + 'idnssoaserial': [fuzzy_digits], + 'idnssoarefresh': [fuzzy_digits], + 'idnssoaretry': [fuzzy_digits], + 'idnssoaexpire': [fuzzy_digits], + 'idnssoaminimum': [fuzzy_digits], + 'idnsallowdynupdate': [u'FALSE'], + 'idnsupdatepolicy': [u'grant %(realm)s krb5-self * A; ' + u'grant %(realm)s krb5-self * AAAA; ' + u'grant %(realm)s krb5-self * SSHFP;' + % dict(realm=api.env.realm)], + 'idnsallowtransfer': [u'none;'], + 'idnsallowquery': [u'any;'], + 'objectclass': objectclasses.dnszone, + + }, + }, + extra_check=assert_realmdomain_and_txt_record_not_present, + ), + + dict( + desc='Check realmdomain and TXT record get deleted ' + 'during dnszone_del', + command=('dnszone_del', [dnszone_1], {}), + expected={ + 'value': dnszone_1, + 'summary': u'Deleted DNS zone "%s"' % dnszone_1, + 'result': {'failed': u''}, + }, + extra_check=assert_realmdomain_and_txt_record_not_present, + ), + ] |