On Fri, 2011-11-11 at 10:22 -0500, Rob Crittenden wrote:
> Martin Kosek wrote:
> > There are 2 patches improving our zone zonemgr:
> >
> > 1) ipa-server-install --zonemgr option validation and normalization +
> > the same thing in ipa dnszone-add/mod --admin-email. They now allow and
> > correctly process '.' in a local-part of the zonemgr e-mail (it is
> > encoded as '\.'.
> >
> > How to test:
> >
> > ipa-server-install -p secret123 -a secret123 --setup-dns 
> > [email protected]
> > OR if e-mail is passed in SOA format:
> > ipa-server-install -p secret123 -a secret123 --setup-dns 
> > --zonemgr='foo\.bar.example.com'
> >
> > In both cases, the zonemgr e-mail will be set to correct format: 'foo
> > \.bar.example.com'.
> >
> > 2) Our default zonemgr is changed to follow RFC 2142 recommendation -
> > hostmaster@<domain>
> >
> > hostmaster is an alias to root anyway (see /etc/aliases).
> >
> > Martin
> 
> NACK 166, this illegal address is allowed: foo\.bar\.baz\.com
> 
> ACK 167 when 166 is ready.
> 
> rob

Are you sure that you quoted the string in shell properly? It likes to
eat backslashes when one is not cautious. The zonemgr value, including
backslashes, should then show up in `ipa dnszone-show ZONE'.

This is my output:

# ipa dnszone-mod example.com --admin-email='foo\.bar\.baz\.com'
ipa: ERROR: invalid 'admin_email': address domain is not fully qualified
("example.com" instead of just "example")

Anyway, attaching a rebased patch (it collided with my patch 120).

Martin
>From 54ae50e61970d930f5bc10ca9afccda34dfbe5cb Mon Sep 17 00:00:00 2001
From: Martin Kosek <[email protected]>
Date: Mon, 14 Nov 2011 10:15:56 +0100
Subject: [PATCH] Improve zonemgr validator and normalizer

The validator has been improved to support better both SOA format
(e-mail address in a domain name format, without '@') and standard
e-mail format. Allow '\.' character in a SOA format encoding the
standard '.' in the local-part of an e-mail. Normalization code
has been moved to one common function.

https://fedorahosted.org/freeipa/ticket/2053
---
 API.txt                           |    6 ++--
 ipalib/plugins/dns.py             |   11 +-------
 ipalib/util.py                    |   47 +++++++++++++++++++++++++++++--------
 ipaserver/install/bindinstance.py |   12 +++------
 4 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/API.txt b/API.txt
index bafff6c0308985a5ebf87667220a52b82f168870..3760e7f5c279bc55c706c48e7883ad550f2db10f 100644
--- a/API.txt
+++ b/API.txt
@@ -858,7 +858,7 @@ args: 1,19,3
 arg: Str('idnsname', attribute=True, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, required=True)
 option: Str('name_from_ip', _validate_ipnet, attribute=True, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('idnssoamname', attribute=True, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, required=True)
-option: Str('idnssoarname', _rname_validator, attribute=True, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, required=True)
+option: Str('idnssoarname', _rname_validator, attribute=True, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=normalize_zonemgr, required=True)
 option: Int('idnssoaserial', attribute=True, autofill=True, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False)
 option: Int('idnssoarefresh', attribute=True, autofill=True, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('idnssoaretry', attribute=True, autofill=True, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
@@ -903,7 +903,7 @@ arg: Str('criteria?', noextrawhitespace=False)
 option: Str('idnsname', attribute=True, autofill=False, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, query=True, required=False)
 option: Str('name_from_ip', _validate_ipnet, attribute=True, autofill=False, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
 option: Str('idnssoamname', attribute=True, autofill=False, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, query=True, required=False)
-option: Str('idnssoarname', _rname_validator, attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, query=True, required=False)
+option: Str('idnssoarname', _rname_validator, attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=normalize_zonemgr, query=True, required=False)
 option: Int('idnssoaserial', attribute=True, autofill=False, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, query=True, required=False)
 option: Int('idnssoarefresh', attribute=True, autofill=False, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
 option: Int('idnssoaretry', attribute=True, autofill=False, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, query=True, required=False)
@@ -930,7 +930,7 @@ args: 1,18,3
 arg: Str('idnsname', attribute=True, cli_name='name', default_from=DefaultFrom(<lambda>, 'name_from_ip'), label=Gettext('Zone name', domain='ipa', localedir=None), multivalue=False, normalizer=<lambda>, primary_key=True, query=True, required=True)
 option: Str('name_from_ip', _validate_ipnet, attribute=True, autofill=False, cli_name='name_from_ip', label=Gettext('Reverse zone IP network', domain='ipa', localedir=None), multivalue=False, required=False)
 option: Str('idnssoamname', attribute=True, autofill=False, cli_name='name_server', label=Gettext('Authoritative nameserver', domain='ipa', localedir=None), multivalue=False, required=False)
-option: Str('idnssoarname', _rname_validator, attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=_rname_normalizer, required=False)
+option: Str('idnssoarname', _rname_validator, attribute=True, autofill=False, cli_name='admin_email', default_from=DefaultFrom(<lambda>, 'idnsname'), label=Gettext('Administrator e-mail address', domain='ipa', localedir=None), multivalue=False, normalizer=normalize_zonemgr, required=False)
 option: Int('idnssoaserial', attribute=True, autofill=False, cli_name='serial', create_default=_create_zone_serial, label=Gettext('SOA serial', domain='ipa', localedir=None), minvalue=1, multivalue=False, required=False)
 option: Int('idnssoarefresh', attribute=True, autofill=False, cli_name='refresh', default=3600, label=Gettext('SOA refresh', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
 option: Int('idnssoaretry', attribute=True, autofill=False, cli_name='retry', default=900, label=Gettext('SOA retry', domain='ipa', localedir=None), minvalue=0, multivalue=False, required=False)
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 0a0bcb79c8bfdc055e7e470fbdc7ba7a090aecbd..618692aa2cab7e6c0bc04bfb26599a3947d044e8 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -27,7 +27,7 @@ from ipalib import Command
 from ipalib import Flag, Bool, Int, List, Str, StrEnum
 from ipalib.plugins.baseldap import *
 from ipalib import _, ngettext
-from ipalib.util import validate_zonemgr, validate_hostname
+from ipalib.util import validate_zonemgr, normalize_zonemgr, validate_hostname
 from ipapython import dnsclient
 from ipapython.ipautil import valid_ip
 from ldap import explode_dn
@@ -152,13 +152,6 @@ def _rname_validator(ugettext, zonemgr):
         return unicode(e)
     return None
 
-# normalizer for admin email
-def _rname_normalizer(value):
-    value = value.replace('@', '.')
-    if not value.endswith('.'):
-        value += '.'
-    return value
-
 def _create_zone_serial(**kwargs):
     """Generate serial number for zones."""
     return int('%s01' % time.strftime('%Y%d%m'))
@@ -677,7 +670,7 @@ class dnszone(LDAPObject):
             label=_('Administrator e-mail address'),
             doc=_('Administrator e-mail address'),
             default_from=lambda idnsname: 'root.%s' % idnsname,
-            normalizer=_rname_normalizer,
+            normalizer=normalize_zonemgr,
         ),
         Int('idnssoaserial?',
             cli_name='serial',
diff --git a/ipalib/util.py b/ipalib/util.py
index 7a4d256d72561b5cbfb637c1154a116b89899365..697461a7911a66c4548a0ce1d26a4d56a2e3c2e8 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -204,33 +204,60 @@ def check_writable_file(filename):
     except (IOError, OSError), e:
         raise errors.FileError(reason=str(e))
 
+def normalize_zonemgr(zonemgr):
+    if '@' in zonemgr:
+        # local-part needs to be normalized
+        name, at, domain = zonemgr.partition('@')
+        name = name.replace('.', '\\.')
+        zonemgr = u''.join((name, u'.', domain))
+
+    if not zonemgr.endswith('.'):
+        zonemgr = zonemgr + u'.'
+
+    return zonemgr
 
 def validate_zonemgr(zonemgr):
     """ See RFC 1033, 1035 """
-    regex_domain = re.compile(r'^[a-z0-9][a-z0-9-]*$', re.IGNORECASE)
-    regex_name = re.compile(r'^[a-z0-9][a-z0-9-_]*$', re.IGNORECASE)
+    regex_domain = re.compile(r'^[a-z0-9]([a-z0-9-]?[a-z0-9])*$', re.IGNORECASE)
+    regex_local_part = re.compile(r'^[a-z0-9]([a-z0-9-_\.]?[a-z0-9])*$',
+                                    re.IGNORECASE)
+
+    local_part_errmsg = _('mail account may only include letters, numbers, -, _ and a dot. There may not be consecutive -, _ and . characters')
 
     if len(zonemgr) > 255:
         raise ValueError(_('cannot be longer that 255 characters'))
 
+    if zonemgr.endswith('.'):
+        zonemgr = zonemgr[:-1]
+
     if zonemgr.count('@') == 1:
-        name, dot, domain = zonemgr.partition('@')
+        local_part, dot, domain = zonemgr.partition('@')
+        if not regex_local_part.match(local_part):
+            raise ValueError(local_part_errmsg)
     elif zonemgr.count('@') > 1:
         raise ValueError(_('too many \'@\' characters'))
     else:
-        # address in SOA format already (without @)
-        name, dot, domain = zonemgr.partition('.')
+        last_fake_sep = zonemgr.rfind('\\.')
+        if last_fake_sep != -1: # there is a 'fake' local-part/domain separator
+            sep = zonemgr.find('.', last_fake_sep+2)
+            if sep == -1:
+                raise ValueError(_('address domain is not fully qualified ' \
+                          '("example.com" instead of just "example")'))
+            local_part = zonemgr[:sep]
+            domain = zonemgr[sep+1:]
 
-    if domain.endswith('.'):
-        domain = domain[:-1]
+            if not all(regex_local_part.match(part) for part in local_part.split('\\.')):
+                raise ValueError(local_part_errmsg)
+        else:
+            local_part, dot, domain = zonemgr.partition('.')
+
+            if not regex_local_part.match(local_part):
+                raise ValueError(local_part_errmsg)
 
     if '.' not in domain:
         raise ValueError(_('address domain is not fully qualified ' \
                           '("example.com" instead of just "example")'))
 
-    if not regex_name.match(name):
-        raise ValueError(_('mail account may only include letters, numbers, -, and _'))
-
     if not all(regex_domain.match(part) for part in domain.split(".")):
         raise ValueError(_('domain name may only include letters, numbers, and -'))
 
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index 7330264fe69f0bd5aaa63ebf58a8391f85384986..95d62f490a632f2949bd6d08f5f7ac402ce3cf79 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -33,7 +33,7 @@ from ipapython import sysrestore
 from ipapython import ipautil
 from ipalib.constants import DNS_ZONE_REFRESH
 from ipalib.parameters import IA5Str
-from ipalib.util import validate_zonemgr
+from ipalib.util import validate_zonemgr, normalize_zonemgr
 
 import ipalib
 from ipalib import api, util, errors
@@ -298,10 +298,6 @@ def zonemgr_callback(option, opt_str, value, parser):
     except ValueError, e:
         parser.error("invalid zonemgr: " + unicode(e))
 
-    name = opt_str.replace('--','')
-    v = unicode(value, 'utf-8')
-    ia = IA5Str(name)
-    ia._convert_scalar(v)
     parser.values.zonemgr = value
 
 class DnsBackup(object):
@@ -387,10 +383,10 @@ class BindInstance(service.Service):
         self.zone_refresh = zone_refresh
         self.zone_notif = zone_notif
 
-        if zonemgr:
-            self.zonemgr = zonemgr.replace('@','.')
-        else:
+        if not zonemgr:
             self.zonemgr = 'root.%s.%s' % (self.host, self.domain)
+        else:
+            self.zonemgr = normalize_zonemgr(zonemgr)
 
         self.__setup_sub_dict()
 
-- 
1.7.7

_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to