mirror of
				https://salsa.debian.org/freeipa-team/freeipa.git
				synced 2025-02-25 18:55:28 -06:00 
			
		
		
		
	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
This commit is contained in:
		@@ -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'))
 | 
			
		||||
@@ -678,7 +671,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',
 | 
			
		||||
 
 | 
			
		||||
@@ -196,33 +196,63 @@ def check_writable_file(filename):
 | 
			
		||||
    except (IOError, OSError), e:
 | 
			
		||||
        raise errors.FileError(reason=str(e))
 | 
			
		||||
 | 
			
		||||
def normalize_zonemgr(zonemgr):
 | 
			
		||||
    if not zonemgr:
 | 
			
		||||
        # do not normalize empty or None value
 | 
			
		||||
        return 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 -'))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,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
 | 
			
		||||
from ipapython.ipa_log_manager import *
 | 
			
		||||
 | 
			
		||||
import ipalib
 | 
			
		||||
@@ -187,6 +187,9 @@ def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None, ns_ip_addres
 | 
			
		||||
    if update_policy is None:
 | 
			
		||||
        update_policy = "grant %(realm)s krb5-self * A; grant %(realm)s krb5-self * AAAA;" % dict(realm=api.env.realm)
 | 
			
		||||
 | 
			
		||||
    if zonemgr is None:
 | 
			
		||||
        zonemgr = 'root.%s' % name
 | 
			
		||||
 | 
			
		||||
    if ns_hostname is None:
 | 
			
		||||
        # automatically retrieve list of DNS masters
 | 
			
		||||
        dns_masters = api.Object.dnsrecord.get_dns_masters()
 | 
			
		||||
@@ -298,10 +301,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 +386,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()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user