summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ACI.txt2
-rw-r--r--API.txt3
-rw-r--r--VERSION.m44
-rw-r--r--ipaserver/plugins/config.py115
4 files changed, 118 insertions, 6 deletions
diff --git a/ACI.txt b/ACI.txt
index 5e84d05e8..a4c346f2c 100644
--- a/ACI.txt
+++ b/ACI.txt
@@ -61,7 +61,7 @@ aci: (targetattr = "cn || description || ipacertprofilestoreissued")(targetfilte
dn: cn=certprofiles,cn=ca,dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || description || entryusn || ipacertprofilestoreissued || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipacertprofile)")(version 3.0;acl "permission:System: Read Certificate Profiles";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=ipaconfig,cn=etc,dc=ipa,dc=example
-aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";)
+aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipadomainresolutionorder || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
aci: (targetfilter = "(objectclass=costemplate)")(version 3.0;acl "permission:System: Add Group Password Policy costemplate";allow (add) groupdn = "ldap:///cn=System: Add Group Password Policy costemplate,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
diff --git a/API.txt b/API.txt
index 2d6b401be..f876afc76 100644
--- a/API.txt
+++ b/API.txt
@@ -1061,7 +1061,7 @@ args: 0,1,1
option: Str('version?')
output: Output('result')
command: config_mod/1
-args: 0,26,3
+args: 0,27,3
option: Str('addattr*', cli_name='addattr')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('ca_renewal_master_server?', autofill=False)
@@ -1070,6 +1070,7 @@ option: StrEnum('ipaconfigstring*', autofill=False, cli_name='ipaconfigstring',
option: Str('ipadefaultemaildomain?', autofill=False, cli_name='emaildomain')
option: Str('ipadefaultloginshell?', autofill=False, cli_name='defaultshell')
option: Str('ipadefaultprimarygroup?', autofill=False, cli_name='defaultgroup')
+option: Str('ipadomainresolutionorder?', autofill=False, cli_name='domain_resolution_order')
option: Str('ipagroupobjectclasses*', autofill=False, cli_name='groupobjectclasses')
option: IA5Str('ipagroupsearchfields?', autofill=False, cli_name='groupsearch')
option: IA5Str('ipahomesrootdir?', autofill=False, cli_name='homedirectory')
diff --git a/VERSION.m4 b/VERSION.m4
index 246d6bb59..9766c749b 100644
--- a/VERSION.m4
+++ b/VERSION.m4
@@ -73,8 +73,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
-define(IPA_API_VERSION_MINOR, 221)
-# Last change: cert: include certificate chain in cert command output
+define(IPA_API_VERSION_MINOR, 222)
+>>>>>>> ipaconfig: add the ability to manipulate domain resolution order
########################################################
diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py
index 5d574657e..232c88121 100644
--- a/ipaserver/plugins/config.py
+++ b/ipaserver/plugins/config.py
@@ -22,6 +22,7 @@ from ipalib import api
from ipalib import Bool, Int, Str, IA5Str, StrEnum, DNParam
from ipalib import errors
from ipalib.plugable import Registry
+from ipalib.util import validate_domain_name
from .baseldap import (
LDAPObject,
LDAPUpdate,
@@ -34,6 +35,8 @@ from ipapython.dn import DN
OPERATIONAL_ATTRIBUTES = ('nsaccountlock', 'member', 'memberof',
'memberindirect', 'memberofindirect',)
+DOMAIN_RESOLUTION_ORDER_SEPARATOR = u':'
+
__doc__ = _("""
Server configuration
@@ -95,7 +98,7 @@ class config(LDAPObject):
'ipamigrationenabled', 'ipacertificatesubjectbase',
'ipapwdexpadvnotify', 'ipaselinuxusermaporder',
'ipaselinuxusermapdefault', 'ipaconfigstring', 'ipakrbauthzdata',
- 'ipauserauthtype'
+ 'ipauserauthtype', 'ipadomainresolutionorder'
]
container_dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'))
permission_filter_objectclasses = ['ipaguiconfig']
@@ -108,7 +111,8 @@ class config(LDAPObject):
'cn', 'objectclass',
'ipacertificatesubjectbase', 'ipaconfigstring',
'ipadefaultemaildomain', 'ipadefaultloginshell',
- 'ipadefaultprimarygroup', 'ipagroupobjectclasses',
+ 'ipadefaultprimarygroup', 'ipadomainresolutionorder',
+ 'ipagroupobjectclasses',
'ipagroupsearchfields', 'ipahomesrootdir',
'ipakrbauthzdata', 'ipamaxusernamelength',
'ipamigrationenabled', 'ipapwdexpadvnotify',
@@ -250,6 +254,13 @@ class config(LDAPObject):
label=_('IPA CA renewal master'),
doc=_('Renewal master for IPA certificate authority'),
flags={'virtual_attribute', 'no_create'}
+ ),
+ Str(
+ 'ipadomainresolutionorder?',
+ cli_name='domain_resolution_order',
+ label=_('Domain resolution order'),
+ doc=_('colon-separated list of domains used for short name'
+ ' qualification')
)
)
@@ -266,6 +277,104 @@ class config(LDAPObject):
config = backend.config_retrieve(role)
entry_attrs.update(config)
+ def gather_trusted_domains(self):
+ """
+ Aggregate all trusted domains into a dict keyed by domain names with
+ values corresponding to domain status (enabled/disabled)
+ """
+ command = self.api.Command
+ try:
+ ad_forests = command.trust_find(sizelimit=0)['result']
+ except errors.NotFound:
+ return {}
+
+ trusted_domains = {}
+ for forest_name in [a['cn'][0] for a in ad_forests]:
+ forest_domains = command.trustdomain_find(
+ forest_name, sizelimit=0)['result']
+
+ trusted_domains.update(
+ {
+ dom['cn'][0]: dom['domain_enabled'][0]
+ for dom in forest_domains if 'domain_enabled' in dom
+ }
+ )
+
+ return trusted_domains
+
+ def _validate_single_domain(self, attr_name, domain, known_domains):
+ """
+ Validate a single domain from domain resolution order
+
+ :param attr_name: name of attribute that holds domain resolution order
+ :param domain: domain name
+ :param known_domains: dict of domains known to IPA keyed by domain name
+ and valued by boolean value corresponding to domain status
+ (enabled/disabled)
+
+ :raises: ValidationError if the domain name is empty, syntactically
+ invalid or corresponds to a disable domain
+ NotFound if a syntactically correct domain name unknown to IPA
+ is supplied (not IPA domain and not any of trusted domains)
+ """
+ if not domain:
+ raise errors.ValidationError(
+ name=attr_name,
+ error=_("Empty domain is not allowed")
+ )
+
+ try:
+ validate_domain_name(domain)
+ except ValueError as e:
+ raise errors.ValidationError(
+ name=attr_name,
+ error=_("Invalid domain name '%(domain)s': %(e)s")
+ % dict(domain=domain, e=e))
+
+ if domain not in known_domains:
+ raise errors.NotFound(
+ reason=_("Server has no information about domain '%(domain)s'")
+ % dict(domain=domain)
+ )
+
+ if not known_domains[domain]:
+ raise errors.ValidationError(
+ name=attr_name,
+ error=_("Disabled domain '%(domain)s' is not allowed")
+ % dict(domain=domain)
+ )
+
+ def validate_domain_resolution_order(self, entry_attrs):
+ """
+ Validate domain resolution order, e.g. split by the delimiter (colon)
+ and check each domain name for non-emptiness, syntactic correctness,
+ and status (enabled/disabled).
+
+ supplying empty order (':') bypasses validations and allows to specify
+ empty attribute value.
+ """
+ attr_name = 'ipadomainresolutionorder'
+ if attr_name not in entry_attrs:
+ return
+
+ domain_resolution_order = entry_attrs[attr_name]
+
+ # empty resolution order is signalized by single separator, do nothing
+ # and let it pass
+ if domain_resolution_order == DOMAIN_RESOLUTION_ORDER_SEPARATOR:
+ return
+
+ submitted_domains = domain_resolution_order.split(
+ DOMAIN_RESOLUTION_ORDER_SEPARATOR)
+
+ known_domains = self.gather_trusted_domains()
+
+ # add FreeIPA domain to the list of domains. This one is always enabled
+ known_domains.update({self.api.env.domain: True})
+
+ for domain in submitted_domains:
+ self._validate_single_domain(attr_name, domain, known_domains)
+
@register()
class config_mod(LDAPUpdate):
@@ -396,6 +505,8 @@ class config_mod(LDAPUpdate):
backend = self.api.Backend.serverroles
backend.config_update(ca_renewal_master_server=new_master)
+ self.obj.validate_domain_resolution_order(entry_attrs)
+
return dn
def exc_callback(self, keys, options, exc, call_func,