diff options
author | Martin Basti <mbasti@redhat.com> | 2014-03-27 14:36:39 +0100 |
---|---|---|
committer | Martin Kosek <mkosek@redhat.com> | 2014-06-03 15:55:32 +0200 |
commit | 64d8da21c6515315d19c8bc76128d15474f4031e (patch) | |
tree | c27d5f03789ec6af56a25c00fc71233d5f97a613 /ipalib | |
parent | 9c7b0ad156cacbc3afc7d319245090d67ac37e88 (diff) | |
download | freeipa-64d8da21c6515315d19c8bc76128d15474f4031e.tar.gz freeipa-64d8da21c6515315d19c8bc76128d15474f4031e.tar.xz freeipa-64d8da21c6515315d19c8bc76128d15474f4031e.zip |
DNSNameParam parameter
New param type for domain names
Part of ticket:
IPA should allow internationalized domain names
https://fedorahosted.org/freeipa/ticket/3169
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Diffstat (limited to 'ipalib')
-rw-r--r-- | ipalib/__init__.py | 3 | ||||
-rw-r--r-- | ipalib/parameters.py | 69 |
2 files changed, 71 insertions, 1 deletions
diff --git a/ipalib/__init__.py b/ipalib/__init__.py index 2a87103b8..6895b46d4 100644 --- a/ipalib/__init__.py +++ b/ipalib/__init__.py @@ -886,7 +886,8 @@ from frontend import Command, LocalOrRemote, Updater, Advice from frontend import Object, Method from crud import Create, Retrieve, Update, Delete, Search from parameters import DefaultFrom, Bool, Flag, Int, Decimal, Bytes, Str, IA5Str, Password, DNParam, DeprecatedParam -from parameters import BytesEnum, StrEnum, IntEnum, AccessTime, File, DateTime +from parameters import (BytesEnum, StrEnum, IntEnum, AccessTime, File, + DateTime, DNSNameParam) from errors import SkipPluginModule from text import _, ngettext, GettextFactory, NGettextFactory diff --git a/ipalib/parameters.py b/ipalib/parameters.py index 728faee53..1dff13cc1 100644 --- a/ipalib/parameters.py +++ b/ipalib/parameters.py @@ -114,6 +114,9 @@ from constants import TYPE_ERROR, CALLABLE_ERROR, LDAP_GENERALIZED_TIME_FORMAT from text import Gettext, FixMe from util import json_serialize from ipapython.dn import DN +from ipapython.dnsutil import DNSName +import dns.name +import encodings.idna def _is_null(value): return not value and value != 0 # NOTE: False == 0 @@ -1919,3 +1922,69 @@ def create_param(spec): TYPE_ERROR % ('spec', (str, Param), spec, type(spec)) ) return Str(spec) + + +class DNSNameParam(Param): + """ + Domain name parameter type. + + :only_absolute a domain name has to be absolute + (makes it absolute from unicode input) + :only_relative a domain name has to be relative + """ + type = DNSName + type_error = _('must be DNS name') + kwargs = Param.kwargs + ( + ('only_absolute', bool, False), + ('only_relative', bool, False), + ) + + def __init__(self, name, *rules, **kw): + super(DNSNameParam, self).__init__(name, *rules, **kw) + if self.only_absolute and self.only_relative: + raise ValueError('%s: cannot be both absolute and relative' % + self.nice) + + def _convert_scalar(self, value, index=None): + if isinstance(value, unicode): + error = None + + try: + domain_name = DNSName(value) + except dns.name.BadEscape: + error = _('invalid escape code in domain name') + except dns.name.EmptyLabel: + error = _('empty DNS label') + except dns.name.NameTooLong: + error = _('domain name cannot be longer than 255 characters') + except dns.name.LabelTooLong: + error = _('DNS label cannot be longer than 63 characters') + except dns.exception.SyntaxError: + error = _('invalid domain name') + + #compare if IDN normalized and original domain match + #there is N:1 mapping between unicode and IDNA names + #user should use normalized names to avoid mistakes + normalized_domain_name = encodings.idna.nameprep(value) + if value != normalized_domain_name: + error = _("domain name '%(domain)s' and normalized domain name" + " '%(normalized)s' do not match. Please use only" + " normalized domains") % {'domain': value, + 'normalized': normalized_domain_name} + if error: + raise ConversionError(name=self.get_param_name(), index=index, + error=error) + value = domain_name + + if self.only_absolute and not value.is_absolute(): + value = value.make_absolute() + + return super(DNSNameParam, self)._convert_scalar(value, index) + + def _rule_only_absolute(self, _, value): + if self.only_absolute and not value.is_absolute(): + return _('must be absolute') + + def _rule_only_relative(self, _, value): + if self.only_relative and value.is_absolute(): + return _('must be relative') |