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:
parent
1039653a1b
commit
3f0eb1417c
@ -27,7 +27,7 @@ from ipalib import Command
|
|||||||
from ipalib import Flag, Bool, Int, List, Str, StrEnum
|
from ipalib import Flag, Bool, Int, List, Str, StrEnum
|
||||||
from ipalib.plugins.baseldap import *
|
from ipalib.plugins.baseldap import *
|
||||||
from ipalib import _, ngettext
|
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 import dnsclient
|
||||||
from ipapython.ipautil import valid_ip
|
from ipapython.ipautil import valid_ip
|
||||||
from ldap import explode_dn
|
from ldap import explode_dn
|
||||||
@ -152,13 +152,6 @@ def _rname_validator(ugettext, zonemgr):
|
|||||||
return unicode(e)
|
return unicode(e)
|
||||||
return None
|
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):
|
def _create_zone_serial(**kwargs):
|
||||||
"""Generate serial number for zones."""
|
"""Generate serial number for zones."""
|
||||||
return int('%s01' % time.strftime('%Y%d%m'))
|
return int('%s01' % time.strftime('%Y%d%m'))
|
||||||
@ -678,7 +671,7 @@ class dnszone(LDAPObject):
|
|||||||
label=_('Administrator e-mail address'),
|
label=_('Administrator e-mail address'),
|
||||||
doc=_('Administrator e-mail address'),
|
doc=_('Administrator e-mail address'),
|
||||||
default_from=lambda idnsname: 'root.%s' % idnsname,
|
default_from=lambda idnsname: 'root.%s' % idnsname,
|
||||||
normalizer=_rname_normalizer,
|
normalizer=normalize_zonemgr,
|
||||||
),
|
),
|
||||||
Int('idnssoaserial?',
|
Int('idnssoaserial?',
|
||||||
cli_name='serial',
|
cli_name='serial',
|
||||||
|
@ -196,33 +196,63 @@ def check_writable_file(filename):
|
|||||||
except (IOError, OSError), e:
|
except (IOError, OSError), e:
|
||||||
raise errors.FileError(reason=str(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):
|
def validate_zonemgr(zonemgr):
|
||||||
""" See RFC 1033, 1035 """
|
""" See RFC 1033, 1035 """
|
||||||
regex_domain = 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_name = re.compile(r'^[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:
|
if len(zonemgr) > 255:
|
||||||
raise ValueError(_('cannot be longer that 255 characters'))
|
raise ValueError(_('cannot be longer that 255 characters'))
|
||||||
|
|
||||||
|
if zonemgr.endswith('.'):
|
||||||
|
zonemgr = zonemgr[:-1]
|
||||||
|
|
||||||
if zonemgr.count('@') == 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:
|
elif zonemgr.count('@') > 1:
|
||||||
raise ValueError(_('too many \'@\' characters'))
|
raise ValueError(_('too many \'@\' characters'))
|
||||||
else:
|
else:
|
||||||
# address in SOA format already (without @)
|
last_fake_sep = zonemgr.rfind('\\.')
|
||||||
name, dot, domain = zonemgr.partition('.')
|
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('.'):
|
if not all(regex_local_part.match(part) for part in local_part.split('\\.')):
|
||||||
domain = domain[:-1]
|
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:
|
if '.' not in domain:
|
||||||
raise ValueError(_('address domain is not fully qualified ' \
|
raise ValueError(_('address domain is not fully qualified ' \
|
||||||
'("example.com" instead of just "example")'))
|
'("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(".")):
|
if not all(regex_domain.match(part) for part in domain.split(".")):
|
||||||
raise ValueError(_('domain name may only include letters, numbers, and -'))
|
raise ValueError(_('domain name may only include letters, numbers, and -'))
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ from ipapython import sysrestore
|
|||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
from ipalib.constants import DNS_ZONE_REFRESH
|
from ipalib.constants import DNS_ZONE_REFRESH
|
||||||
from ipalib.parameters import IA5Str
|
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 *
|
from ipapython.ipa_log_manager import *
|
||||||
|
|
||||||
import ipalib
|
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:
|
if update_policy is None:
|
||||||
update_policy = "grant %(realm)s krb5-self * A; grant %(realm)s krb5-self * AAAA;" % dict(realm=api.env.realm)
|
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:
|
if ns_hostname is None:
|
||||||
# automatically retrieve list of DNS masters
|
# automatically retrieve list of DNS masters
|
||||||
dns_masters = api.Object.dnsrecord.get_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:
|
except ValueError, e:
|
||||||
parser.error("invalid zonemgr: " + unicode(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
|
parser.values.zonemgr = value
|
||||||
|
|
||||||
class DnsBackup(object):
|
class DnsBackup(object):
|
||||||
@ -387,10 +386,10 @@ class BindInstance(service.Service):
|
|||||||
self.zone_refresh = zone_refresh
|
self.zone_refresh = zone_refresh
|
||||||
self.zone_notif = zone_notif
|
self.zone_notif = zone_notif
|
||||||
|
|
||||||
if zonemgr:
|
if not zonemgr:
|
||||||
self.zonemgr = zonemgr.replace('@','.')
|
|
||||||
else:
|
|
||||||
self.zonemgr = 'root.%s.%s' % (self.host, self.domain)
|
self.zonemgr = 'root.%s.%s' % (self.host, self.domain)
|
||||||
|
else:
|
||||||
|
self.zonemgr = normalize_zonemgr(zonemgr)
|
||||||
|
|
||||||
self.__setup_sub_dict()
|
self.__setup_sub_dict()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user