From 974eb7b5efd20ad2195b0ad578637ab31f4c1df4 Mon Sep 17 00:00:00 2001 From: Martin Babinsky Date: Jul 01 2016 07:37:25 +0000 Subject: ipalib: introduce Principal parameter This patch introduces a separate Principal parameter that allows the framework to syntactically validate incoming/outcoming principals by using a single shared codebase. https://fedorahosted.org/freeipa/ticket/3864 Reviewed-By: David Kupka Reviewed-By: Jan Cholasta --- diff --git a/ipaclient/remote_plugins/schema.py b/ipaclient/remote_plugins/schema.py index 8ce26e6..8ebc3a9 100644 --- a/ipaclient/remote_plugins/schema.py +++ b/ipaclient/remote_plugins/schema.py @@ -31,6 +31,7 @@ if six.PY3: _TYPES = { 'DN': DN, 'DNSName': DNSName, + 'Principal': unicode, 'NoneType': type(None), 'Sequence': collections.Sequence, 'bool': bool, @@ -45,6 +46,7 @@ _PARAMS = { 'Decimal': parameters.Decimal, 'DN': parameters.DNParam, 'DNSName': parameters.DNSNameParam, + 'Principal': parameters.Principal, 'bool': parameters.Bool, 'bytes': parameters.Bytes, 'datetime': parameters.DateTime, diff --git a/ipalib/parameters.py b/ipalib/parameters.py index a081134..1581b7d 100644 --- a/ipalib/parameters.py +++ b/ipalib/parameters.py @@ -115,9 +115,11 @@ from ipalib.errors import PasswordMismatch, Base64DecodeError from ipalib.constants import TYPE_ERROR, CALLABLE_ERROR, LDAP_GENERALIZED_TIME_FORMAT from ipalib.text import Gettext, FixMe from ipalib.util import json_serialize, validate_idna_domain +from ipapython import kerberos from ipapython.dn import DN from ipapython.dnsutil import DNSName + def _is_null(value): return not value and value != 0 # NOTE: False == 0 @@ -1970,3 +1972,38 @@ class Dict(Param): type = dict type_error = _("must be dictionary") + + +class Principal(Param): + """ + Kerberos principal name + """ + + type = kerberos.Principal + type_error = _('must be Kerberos principal') + kwargs = Param.kwargs + ( + ('require_service', bool, False), + ) + + @property + def allowed_types(self): + return (self.type, unicode) + + def _convert_scalar(self, value, index=None): + if isinstance(value, unicode): + try: + value = kerberos.Principal(value) + except ValueError: + raise ConversionError( + name=self.get_param_name(), + error=_("Malformed principal: '%(value)s'") % dict( + value=value)) + + return super(Principal, self)._convert_scalar(value) + + def _rule_require_service(self, _, value): + if self.require_service and not value.is_service: + raise ValidationError( + name=self.get_param_name(), + error=_("Service principal is required") + ) diff --git a/ipalib/rpc.py b/ipalib/rpc.py index a549f02..1c00289 100644 --- a/ipalib/rpc.py +++ b/ipalib/rpc.py @@ -66,6 +66,7 @@ from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, \ KRB5_REALM_CANT_RESOLVE, KRB5_CC_NOTFOUND, get_principal from ipapython.dn import DN +from ipapython.kerberos import Principal from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES from ipalib import api @@ -194,6 +195,9 @@ def xml_wrap(value, version): else: return unicode(value) + if isinstance(value, Principal): + return unicode(value) + assert type(value) in (unicode, float, bool, type(None)) + six.integer_types return value @@ -316,6 +320,8 @@ def json_encode_binary(val, version): return {'__dns_name__': unicode(val)} else: return unicode(val) + elif isinstance(val, Principal): + return unicode(val) else: return val diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py index 9258faf..704e71a 100644 --- a/ipapython/ipaldap.py +++ b/ipapython/ipaldap.py @@ -42,6 +42,7 @@ from ipapython.ipautil import ( from ipapython.ipa_log_manager import log_mgr from ipapython.dn import DN from ipapython.dnsutil import DNSName +from ipapython.kerberos import Principal if six.PY3: unicode = str @@ -686,6 +687,8 @@ class LDAPClient(object): 'idnssoamname': DNSName, 'idnssoarname': DNSName, 'dnszoneidnsname': DNSName, + 'krbcanonicalname': Principal, + 'krbprincipalname': Principal, 'nsds5replicalastupdatestart': unicode, 'nsds5replicalastupdateend': unicode, 'nsds5replicalastinitstart': unicode, @@ -847,7 +850,8 @@ class LDAPClient(object): return 'TRUE' else: return 'FALSE' - elif isinstance(val, (unicode, six.integer_types, Decimal, DN)): + elif isinstance(val, (unicode, six.integer_types, Decimal, DN, + Principal)): return value_to_utf8(val) elif isinstance(val, DNSName): return val.to_text() diff --git a/makeapi b/makeapi index 594b8db..515dd6e 100755 --- a/makeapi +++ b/makeapi @@ -71,6 +71,7 @@ PARAM_IGNORED_KW_ATTRIBUTES = ( 'pattern_errmsg', 'precision', 'primary_key', + 'require_service', 'query', 'sortorder', ) diff --git a/pylint_plugins.py b/pylint_plugins.py index 26a4efb..bf35773 100644 --- a/pylint_plugins.py +++ b/pylint_plugins.py @@ -186,6 +186,9 @@ ipa_class_members = { 'only_absolute', 'only_relative', ], + 'ipalib.parameters.Principal': [ + 'require_service', + ], 'ipalib.plugable.API': [ fake_api_env, ] + NAMESPACE_ATTRS + LOGGING_ATTRS,