On 05/25/2015 12:42 PM, Tomas Babej wrote: > > > On 05/25/2015 07:30 AM, Jan Cholasta wrote: >> Dne 22.5.2015 v 12:36 Petr Vobornik napsal(a): >>> On 05/22/2015 07:08 AM, Jan Cholasta wrote: >>>> Dne 21.5.2015 v 18:18 Tomas Babej napsal(a): >>>>> >>>>> >>>>> On 05/19/2015 04:07 PM, Tomas Babej wrote: >>>>>> >>>>>> >>>>>> On 05/19/2015 03:59 PM, Martin Kosek wrote: >>>>>>> On 05/19/2015 03:56 PM, Tomas Babej wrote: >>>>>>>> >>>>>>>> On 05/19/2015 03:51 PM, Martin Kosek wrote: >>>>>>>>> On 05/19/2015 03:49 PM, Ludwig Krispenz wrote: >>>>>>>>>> On 05/19/2015 03:36 PM, Martin Kosek wrote: >>>>>>>>>>> On 05/19/2015 03:22 PM, Tomas Babej wrote: >>>>>>>>>>> ... >>>>>>>>>>>>> 3) Domain level is just a single integer and it should be >>>>>>>>>>>>> treated as such, >>>>>>>>>>>>> there's no need for an LDAPObject plugin and other unnecessary >>>>>>>>>>>>> complexities. >>>>>>>>>>>>> The implemetation could be as simple as (from top of my head, >>>>>>>>>>>>> untested): >>>>>>>>>>>> That's right, I also considered this approach, but as far as I >>>>>>>>>>>> know you do >>>>>>>>>>>> not >>>>>>>>>>>> get the permission handling for the global DomainLevel entry >>>>>>>>>>>> otherwise. >>>>>>>>>>>> >>>>>>>>>>>> Ludwig, I changed the path for the global entry to >>>>>>>>>>>> cn=DomainLevel. >>>>>>>>>>> I know this particular DN was added to the design by Simo, but >>>>>>>>>>> why do we want >>>>>>>>>>> to use CamelCase with LDAP object? >>>>>>>>>>> >>>>>>>>>>> Wouldn't "cn=Domain Level,cn=ipa,cn=etc,SUFFIX" be a better place >>>>>>>>>>> for it? This >>>>>>>>>>> is the last time we can change it, so I am asking now. Then, we >>>>>>>>>>> will be stuck >>>>>>>>>>> with this DN forever. >>>>>>>>>> I don't mind using ""cn=Domain Level" , >>>>>>>>>> >>>>>>>>>> but where does the entry live, here you say >>>>>>>>>> >>>>>>>>>> cn=Domain Level,cn=ipa,cn=etc,SUFFIX" >>>>>>>>>> >>>>>>>>>> and in the design page it is: >>>>>>>>>> >>>>>>>>>> cn=DomainLevel,cn=etc,SUFFIX >>>>>>>>>> >>>>>>>>>> The current version of the topology plugin is looking for >>>>>>>>>> >>>>>>>>>> cn=DomainLevel,cn=ipa,cn=etc,SUFFIX" >>>>>>>>>> but I want to change it to do a search on >>>>>>>>>> objectclass=ipaDomainLevelConfig >>>>>>>>> I see - we all need to unify the location apparently. I updated the >>>>>>>>> design page >>>>>>>>> to use "cn=Domain Level,cn=ipa,cn=etc,SUFFIX". Tomas, please send >>>>>>>>> the updated >>>>>>>>> patch set, it should be an extremely simple change :-) >>>>>>>> I prefer the ipa parent and the space in the name, so I'm glad we >>>>>>>> could agree >>>>>>>> on this without much bikeshedding. >>>>>>>> >>>>>>>> Updated patch attaced. >>>>>>>> >>>>>>>> Tomas >>>>>>>> >>>>>>>> >>>>>>> I still see >>>>>>> >>>>>>> +# Create default Domain Level entry if it does not exist >>>>>>> +dn: cn=DomainLevel,cn=ipa,cn=etc,$SUFFIX >>>>>>> +default: objectClass: top >>>>>>> +default: objectClass: nsContainer >>>>>>> +default: objectClass: ipaDomainLevelConfig >>>>>>> +default: ipaDomainLevel: 0 >>>>>>> >>>>>>> ... >>>>>> >>>>>> Right, the space eluded me there, thanks for the catch. >>>>>> >>>>>> Tomas >>>>> >>>>> A new iteration of the patch, including the server-side checks for the >>>>> installers. >>>>> >>>>> Tomas >>>> >>>> 1) https://www.redhat.com/archives/freeipa-devel/2015-May/msg00228.html >>>> - I still don't agree that the plugin should be based on LDAPObject. >>> >>> On the other hand, with LDAPObject base, Web UI for this feature is much >>> more simpler because it can rely on existing conventions. >> >> Following this logic, should *everything* be based on LDAPObject, >> because it would satisfy the convetion? I don't think so. The convetion >> should not apply here, because domain level is conceptually *not* an >> object, it is a property. IMHO having a clean API should be preferred >> over implementation convenience. >> > > I do not have strong opinions over this. Attached version implements > a lightweight approach to the domainlevel related commands. > > Tomas > > >
Fixes a slight schema glitch.
From 7930e267f4c513a6b2059a6c99d69155c1452c49 Mon Sep 17 00:00:00 2001 From: Tomas Babej <tba...@redhat.com> Date: Thu, 14 May 2015 10:49:55 +0200 Subject: [PATCH] Add Domain Level feature https://fedorahosted.org/freeipa/ticket/5018 --- ACI.txt | 2 + API.txt | 9 ++ install/share/72domainlevels.ldif | 6 + install/share/Makefile.am | 2 + install/share/domainlevel.ldif | 7 ++ install/tools/ipa-replica-install | 32 ++++- install/tools/ipa-server-install | 22 +++- install/updates/72-domainlevels.update | 14 +++ install/updates/Makefile.am | 1 + ipalib/constants.py | 3 + ipalib/errors.py | 16 +++ ipalib/plugins/domainlevel.py | 132 +++++++++++++++++++++ ipaserver/install/dsinstance.py | 12 +- ipaserver/install/ldapupdate.py | 5 + .../install/plugins/update_managed_permissions.py | 11 +- 15 files changed, 263 insertions(+), 11 deletions(-) create mode 100644 install/share/72domainlevels.ldif create mode 100644 install/share/domainlevel.ldif create mode 100644 install/updates/72-domainlevels.update create mode 100644 ipalib/plugins/domainlevel.py diff --git a/ACI.txt b/ACI.txt index bf539892910f14ebc3fbee88a72d2b57c0d1327b..1e4eb0f5a75e44a3c56dffe7df1655a9f0a1db87 100644 --- a/ACI.txt +++ b/ACI.txt @@ -323,6 +323,8 @@ aci: (targetattr = "cn || createtimestamp || dnahostname || dnaportnum || dnarem dn: ou=profile,dc=ipa,dc=example aci: (targetattr = "attributemap || authenticationmethod || bindtimelimit || cn || createtimestamp || credentiallevel || defaultsearchbase || defaultsearchscope || defaultserverlist || dereferencealiases || entryusn || followreferrals || modifytimestamp || objectclass || objectclassmap || ou || preferredserverlist || profilettl || searchtimelimit || serviceauthenticationmethod || servicecredentiallevel || servicesearchdescriptor")(targetfilter = "(|(objectclass=organizationalUnit)(objectclass=DUAConfigProfile))")(version 3.0;acl "permission:System: Read DUA Profile";allow (compare,read,search) userdn = "ldap:///anyone";) dn: cn=masters,cn=ipa,cn=etc,dc=ipa,dc=example +aci: (targetattr = "createtimestamp || entryusn || ipadomainlevel || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipadomainlevelconfig)")(version 3.0;acl "permission:System: Read Domain Level";allow (compare,read,search) userdn = "ldap:///all";) +dn: cn=masters,cn=ipa,cn=etc,dc=ipa,dc=example aci: (targetattr = "cn || createtimestamp || entryusn || ipaconfigstring || modifytimestamp || objectclass")(targetfilter = "(objectclass=nscontainer)")(version 3.0;acl "permission:System: Read IPA Masters";allow (compare,read,search) groupdn = "ldap:///cn=System: Read IPA Masters,cn=permissions,cn=pbac,dc=ipa,dc=example";) dn: cn=config aci: (targetattr = "cn || createtimestamp || description || entryusn || modifytimestamp || nsds50ruv || nsds5beginreplicarefresh || nsds5debugreplicatimeout || nsds5flags || nsds5replicaabortcleanruv || nsds5replicaautoreferral || nsds5replicabackoffmax || nsds5replicabackoffmin || nsds5replicabinddn || nsds5replicabindmethod || nsds5replicabusywaittime || nsds5replicachangecount || nsds5replicachangessentsincestartup || nsds5replicacleanruv || nsds5replicacleanruvnotified || nsds5replicacredentials || nsds5replicaenabled || nsds5replicahost || nsds5replicaid || nsds5replicalastinitend || nsds5replicalastinitstart || nsds5replicalastinitstatus || nsds5replicalastupdateend || nsds5replicalastupdatestart || nsds5replicalastupdatestatus || nsds5replicalegacyconsumer || nsds5replicaname || nsds5replicaport || nsds5replicaprotocoltimeout || nsds5replicapurgedelay || nsds5replicareferral || nsds5replicaroot || nsds5replicasessionpausetime || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal || nsds5replicatimeout || nsds5replicatombstonepurgeinterval || nsds5replicatransportinfo || nsds5replicatype || nsds5replicaupdateinprogress || nsds5replicaupdateschedule || nsds5task || nsds7directoryreplicasubtree || nsds7dirsynccookie || nsds7newwingroupsyncenabled || nsds7newwinusersyncenabled || nsds7windowsdomain || nsds7windowsreplicasubtree || nsruvreplicalastmodified || nsstate || objectclass || onewaysync || winsyncdirectoryfilter || winsyncinterval || winsyncmoveaction || winsyncsubtreepair || winsyncwindowsfilter")(targetfilter = "(|(objectclass=nsds5Replica)(objectclass=nsds5replicationagreement)(objectclass=nsDSWindowsReplicationAgreement)(objectClass=nsMappingTree))")(version 3.0;acl "permission:System: Read Replication Agreements";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Replication Agreements,cn=permissions,cn=pbac,dc=ipa,dc=example";) diff --git a/API.txt b/API.txt index 0808f3c64595495c8a9e60da5cbd689d5cdc6224..d707aef6b44955e65ee189acc373a9a8e44003f2 100644 --- a/API.txt +++ b/API.txt @@ -1283,6 +1283,15 @@ option: Str('version?', exclude='webui') output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: PrimaryKey('value', None, None) +command: domainlevel_set +args: 1,1,1 +arg: Int('ipadomainlevel', cli_name='level', minvalue=0) +option: Str('version?', exclude='webui') +output: Output('result', <type 'int'>, None) +command: domainlevel_show +args: 0,1,1 +option: Str('version?', exclude='webui') +output: Output('result', <type 'int'>, None) command: env args: 1,3,4 arg: Str('variables*') diff --git a/install/share/72domainlevels.ldif b/install/share/72domainlevels.ldif new file mode 100644 index 0000000000000000000000000000000000000000..184e1cb220e80395bbe6ea063df9957ebde752ce --- /dev/null +++ b/install/share/72domainlevels.ldif @@ -0,0 +1,6 @@ +dn: cn=schema +attributeTypes: (2.16.840.1.113730.3.8.19.2.1 NAME 'ipaDomainLevel' DESC 'Domain Level value' EQUALITY numericStringMatch ORDERING numericStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 SINGLE-VALUE X-ORIGIN 'IPA v4') +attributeTypes: (2.16.840.1.113730.3.8.19.2.2 NAME 'ipaMinDomainLevel' DESC 'Minimal supported Domain Level value' EQUALITY numericStringMatch ORDERING numericStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 SINGLE-VALUE X-ORIGIN 'IPA v4') +attributeTypes: (2.16.840.1.113730.3.8.19.2.3 NAME 'ipaMaxDomainLevel' DESC 'Maximal supported Domain Level value' EQUALITY numericStringMatch ORDERING numericStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 SINGLE-VALUE X-ORIGIN 'IPA v4') +objectClasses: (2.16.840.1.113730.3.8.19.1.1 NAME 'ipaDomainLevelConfig' SUP ipaConfigObject AUXILIARY DESC 'Domain Level Configuration' MUST (ipaDomainLevel) X-ORIGIN 'IPA v4') +objectClasses: (2.16.840.1.113730.3.8.19.1.2 NAME 'ipaSupportedDomainLevelConfig' SUP ipaConfigObject AUXILIARY DESC 'Supported Domain Level Configuration' MUST (ipaMinDomainLevel $ ipaMaxDomainLevel) X-ORIGIN 'IPA v4') diff --git a/install/share/Makefile.am b/install/share/Makefile.am index ca6128e2911ab5c0a773dd553f8e67eab944f120..52f7e80d4c4268b965d11c2b64d8e52bb1c533a9 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -21,6 +21,7 @@ app_DATA = \ 65ipasudo.ldif \ 70ipaotp.ldif \ 71idviews.ldif \ + 72domainlevels.ldif \ anonymous-vlv.ldif \ bootstrap-template.ldif \ caJarSigningCert.cfg.template \ @@ -33,6 +34,7 @@ app_DATA = \ ds-nfiles.ldif \ dns.ldif \ dnssec.ldif \ + domainlevel.ldif \ kerberos.ldif \ indices.ldif \ bind.named.conf.template \ diff --git a/install/share/domainlevel.ldif b/install/share/domainlevel.ldif new file mode 100644 index 0000000000000000000000000000000000000000..cb90a6563b29abaf9a74e6d504af26666266d407 --- /dev/null +++ b/install/share/domainlevel.ldif @@ -0,0 +1,7 @@ +# Create default Domain Level for new masters +dn: cn=Domain Level,cn=ipa,cn=etc,$SUFFIX +changetype: add +objectClass: top +objectClass: nsContainer +objectClass: ipaDomainLevelConfig +ipaDomainLevel: $DOMAINLEVEL diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install index f68cc8cf4722264ecea2f1f50de3aa245be24ef9..2c5f934845e4cb6609f74e945b3a73de04db0a42 100755 --- a/install/tools/ipa-replica-install +++ b/install/tools/ipa-replica-install @@ -43,7 +43,7 @@ from ipaserver.install import cainstance from ipaserver.install import krainstance from ipaserver.install import dns as dns_installer from ipalib import api, create_api, errors, util, certstore, x509 -from ipalib.constants import CACERT +from ipalib import constants from ipapython import version from ipapython.config import IPAOptionParser from ipapython import sysrestore @@ -224,12 +224,12 @@ def install_ca_cert(ldap, base_dn, realm, cafile): try: certs = certstore.get_ca_certs(ldap, base_dn, realm, False) except errors.NotFound: - shutil.copy(cafile, CACERT) + shutil.copy(cafile, constants.CACERT) else: certs = [c[0] for c in certs if c[2] is not False] - x509.write_certificate_list(certs, CACERT) + x509.write_certificate_list(certs, constants.CACERT) - os.chmod(CACERT, 0444) + os.chmod(constants.CACERT, 0444) except Exception, e: print "error copying files: " + str(e) sys.exit(1) @@ -569,6 +569,30 @@ def main(): print " %% ipa-replica-manage del %s --force" % config.host_name exit(3) + # Detect the current domain level + try: + current = remote_api.Command['domainlevel_show']['result'] + except KeyError: + # If we're joining an older master, domainlevel_show is not + # available + current = 0 + + # Detect if current level is out of supported range + # for this IPA version + under_lower_bound = current < constants.MIN_DOMAIN_LEVEL + above_upper_bound = current > constants.MAX_DOMAIN_LEVEL + + if under_lower_bound or above_upper_bound: + message = ("This version of FreeIPA does not support " + "the Domain Level which is currently set for " + "this domain. The Domain Level needs to be " + "raised before installing a replica with " + "this version is allowed to be installed " + "within this domain.") + root_logger.error(message) + print(message) + exit(3) + # Check pre-existing host entry try: entry = conn.find_entries(u'fqdn=%s' % config.host_name, ['fqdn'], DN(api.env.container_host, api.env.basedn)) diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install index cb6e1abe2016c0f8cefc35b1d685373f05b3ef89..a62642e78eed19ccf6c678a074475ad3c02c62d6 100755 --- a/install/tools/ipa-server-install +++ b/install/tools/ipa-server-install @@ -70,7 +70,7 @@ from ipapython import sysrestore from ipapython.ipautil import * from ipapython import ipautil from ipapython import dogtag -from ipalib import api, errors, util, x509 +from ipalib import api, errors, util, x509, constants from ipapython.config import IPAOptionParser from ipalib.util import validate_domain_name from ipalib.constants import CACERT @@ -176,6 +176,8 @@ def parse_options(): help="create home directories for users " "on their first login") basic_group.add_option("--hostname", dest="host_name", help="fully qualified name of server") + basic_group.add_option("--domain-level", dest="domainlevel", help="IPA domain level", + default=constants.MAX_DOMAIN_LEVEL, type=int) basic_group.add_option("--ip-address", dest="ip_addresses", type="ip", ip_local=True, action="append", default=[], help="Master Server IP Address. This option can be used multiple times", @@ -327,6 +329,15 @@ def parse_options(): except ValueError, e: parser.error("invalid domain: " + unicode(e)) + # Check that Domain Level is within the allowed range + if not options.uninstall: + if options.domainlevel < constants.MIN_DOMAIN_LEVEL: + parser.error("Domain Level cannot be lower than {0}" + .format(constants.MIN_DOMAIN_LEVEL)) + elif options.domainlevel > constants.MAX_DOMAIN_LEVEL: + parser.error("Domain Level cannot be higher than {0}" + .format(constants.MAX_DOMAIN_LEVEL)) + if not options.setup_dns: if options.forwarders: parser.error("You cannot specify a --forwarder option without the --setup-dns option") @@ -1139,21 +1150,24 @@ def main(): ntp.create_instance() if options.dirsrv_cert_files: - ds = dsinstance.DsInstance(fstore=fstore) + ds = dsinstance.DsInstance(fstore=fstore, + domainlevel=options.domainlevel) ds.create_instance(realm_name, host_name, domain_name, dm_password, dirsrv_pkcs12_info, idstart=options.idstart, idmax=options.idmax, subject_base=options.subject, hbac_allow=not options.hbac_allow) else: - ds = dsinstance.DsInstance(fstore=fstore) + ds = dsinstance.DsInstance(fstore=fstore, + domainlevel=options.domainlevel) ds.create_instance(realm_name, host_name, domain_name, dm_password, idstart=options.idstart, idmax=options.idmax, subject_base=options.subject, hbac_allow=not options.hbac_allow) else: - ds = dsinstance.DsInstance(fstore=fstore) + ds = dsinstance.DsInstance(fstore=fstore, + domainlevel=options.domainlevel) ds.init_info( realm_name, host_name, domain_name, dm_password, options.subject, 1101, 1100, None) diff --git a/install/updates/72-domainlevels.update b/install/updates/72-domainlevels.update new file mode 100644 index 0000000000000000000000000000000000000000..2e83c7be9b200121081470a80a3a9303d685a789 --- /dev/null +++ b/install/updates/72-domainlevels.update @@ -0,0 +1,14 @@ +# Create default Domain Level entry if it does not exist +dn: cn=Domain Level,cn=ipa,cn=etc,$SUFFIX +default: objectClass: top +default: objectClass: nsContainer +default: objectClass: ipaDomainLevelConfig +default: ipaDomainLevel: 0 + +# Create entry proclaiming Domain Level support of this master +# This will update the supported Domain Levels during upgrade +dn: cn=$FQDN,cn=masters,cn=ipa,cn=etc,$SUFFIX +add: objectClass: ipaConfigObject +add: objectClass: ipaSupportedDomainLevelConfig +only: ipaMinDomainLevel: $MIN_DOMAIN_LEVEL +only: ipaMaxDomainLevel: $MAX_DOMAIN_LEVEL diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am index 0d63d9ea8d85f1add5f036e7a39f89543586d33b..b52dc2570040336031b7fe01c1cec50156bd90c3 100644 --- a/install/updates/Makefile.am +++ b/install/updates/Makefile.am @@ -48,6 +48,7 @@ app_DATA = \ 61-trusts-s4u2proxy.update \ 62-ranges.update \ 71-idviews.update \ + 72-domainlevels.update \ 90-post_upgrade_plugins.update \ $(NULL) diff --git a/ipalib/constants.py b/ipalib/constants.py index f1e14702ffdf5a3bd23a62b1fdd2ee3cd95d84f8..04e29d25b1b41dbb67c23d02b4a534ec1735e44c 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -223,3 +223,6 @@ LDAP_GENERALIZED_TIME_FORMAT = "%Y%m%d%H%M%SZ" IPA_ANCHOR_PREFIX = ':IPA:' SID_ANCHOR_PREFIX = ':SID:' + +MIN_DOMAIN_LEVEL = 0 +MAX_DOMAIN_LEVEL = 1 diff --git a/ipalib/errors.py b/ipalib/errors.py index 89b1ef2e0dc1d7346a69fb813bd71990746c620c..63ec22269467b769d276c443f6b3dbed38cd766e 100644 --- a/ipalib/errors.py +++ b/ipalib/errors.py @@ -1344,6 +1344,22 @@ class EmptyResult(NotFound): errno = 4031 +class InvalidDomainLevelError(ExecutionError): + """ + **4032** Raised when a operation could not be completed due to a invalid + domain level. + + For example: + + >>> raise InvalidDomainLevelError(reason='feature requires domain level 4') + Traceback (most recent call last): + ... + InvalidDomainLevelError: feature requires domain level 4 + + """ + + errno = 4032 + class BuiltinError(ExecutionError): """ **4100** Base class for builtin execution errors (*4100 - 4199*). diff --git a/ipalib/plugins/domainlevel.py b/ipalib/plugins/domainlevel.py new file mode 100644 index 0000000000000000000000000000000000000000..3ace2aa06ab7ba586bab322458c1c85fb811ef8f --- /dev/null +++ b/ipalib/plugins/domainlevel.py @@ -0,0 +1,132 @@ +# +# Copyright (C) 2015 FreeIPA Contributors see COPYING for license +# + +from collections import namedtuple + +from ipalib import _ +from ipalib import api +from ipalib import Command +from ipalib import errors +from ipalib import output +from ipalib.parameters import Int +from ipalib.plugable import Registry +from ipalib.plugins.baseldap import LDAPObject, LDAPUpdate, LDAPRetrieve + +from ipapython.dn import DN + + +__doc__ = _(""" +Raise the IPA Domain Level. +""") + +register = Registry() + +DomainLevelRange = namedtuple('DomainLevelRange', ['min', 'max']) + +domainlevel_output = ( + output.Output('result', int), +) + +domainlevel_dn = DN( + ('cn', 'Domain Level'), + ('cn', 'ipa'), + ('cn', 'etc'), + api.env.basedn +) + + +def get_domainlevel_range(master_entry): + try: + return DomainLevelRange( + int(master_entry['ipaMinDomainLevel'][0]), + int(master_entry['ipaMaxDomainLevel'][0]) + ) + except KeyError: + return DomainLevelRange(0, 0) + + +def get_master_entries(ldap): + """ + Returns list of LDAPEntries representing IPA masters. + """ + + container_masters = DN( + ('cn', 'masters'), + ('cn', 'ipa'), + ('cn', 'etc'), + api.env.basedn + ) + + masters, _ = ldap.find_entries( + filter="(cn=*)", + base_dn=container_masters, + scope=ldap.SCOPE_ONELEVEL, + paged_search=True, # we need to make sure to get all of them + ) + + return masters + + +@register() +class domainlevel_show(Command): + __doc__ = _('Query current Domain Level.') + + has_output = domainlevel_output + + def execute(self, *args, **options): + ldap = self.api.Backend.ldap2 + entry = ldap.get_entry(domainlevel_dn, ['ipaDomainLevel']) + + return {'result': int(entry.single_value['ipaDomainLevel'])} + + +@register() +class domainlevel_set(Command): + __doc__ = _('Change current Domain Level.') + + has_output = domainlevel_output + + takes_args = ( + Int('ipadomainlevel', + cli_name='level', + label=_('Domain Level'), + minvalue=0, + ), + ) + + def execute(self, *args, **options): + """ + Checks all the IPA masters for supported domain level ranges. + + If the desired domain level is within the supported range of all + masters, it will be raised. + + Domain level cannot be lowered. + """ + + ldap = self.api.Backend.ldap2 + + current_entry = ldap.get_entry(domainlevel_dn) + current_value = int(current_entry.single_value['ipadomainlevel']) + desired_value = int(args[0]) + + # Domain level cannot be lowered + if int(desired_value) < int(current_value): + message = _("Domain Level cannot be lowered.") + raise errors.InvalidDomainLevelError(message) + + # Check if every master supports the desired level + for master in get_master_entries(ldap): + supported = get_domainlevel_range(master) + + if supported.min > desired_value or supported.max < desired_value: + message = _("Domain Level cannot be raised to {0}, server {1} " + "does not support it." + .format(desired_value, master['cn'][0])) + raise errors.InvalidDomainLevelError(message) + + current_entry.single_value['ipaDomainLevel'] = desired_value + ldap.update_entry(current_entry) + + return {'result': int(current_entry.single_value['ipaDomainLevel'])} diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index f1d24e49d1b184efde1c8d18ff37d0e329037ccc..7005513729628caef784d5fc2138768e8ac817a3 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -61,6 +61,7 @@ IPA_SCHEMA_FILES = ("60kerberos.ldif", "65ipasudo.ldif", "70ipaotp.ldif", "71idviews.ldif", + "72domainlevels.ldif", "15rfc2307bis.ldif", "15rfc4876.ldif") @@ -185,7 +186,7 @@ info: IPA V2.0 class DsInstance(service.Service): def __init__(self, realm_name=None, domain_name=None, dm_password=None, - fstore=None): + fstore=None, domainlevel=None): service.Service.__init__(self, "dirsrv", service_desc="directory server", dm_password=dm_password, @@ -208,6 +209,7 @@ class DsInstance(service.Service): self.subject_base = None self.open_ports = [] self.run_init_memberof = True + self.domainlevel = domainlevel if realm_name: self.suffix = ipautil.realm_to_suffix(self.realm) self.__setup_sub_dict() @@ -252,6 +254,7 @@ class DsInstance(service.Service): def __common_post_setup(self): self.step("initializing group membership", self.init_memberof) self.step("adding master entry", self.__add_master_entry) + self.step("initializing domain level", self.__set_domain_level) self.step("configuring Posix uid/gid generation", self.__config_uidgid_gen) self.step("adding replication acis", self.__add_replication_acis) @@ -392,7 +395,8 @@ class DsInstance(service.Service): IDMAX=self.idmax, HOST=self.fqdn, ESCAPED_SUFFIX=str(self.suffix), GROUP=DS_GROUP, - IDRANGE_SIZE=idrange_size + IDRANGE_SIZE=idrange_size, + DOMAINLEVEL=self.domainlevel, ) def __create_instance(self): @@ -1002,3 +1006,7 @@ class DsInstance(service.Service): root_logger.debug('Unable to find certificate subject base in ' 'certmap.conf') return None + + def __set_domain_level(self): + # Create global domain level antry and set the domain level + self._ldap_mod("domainlevel.ldif", self.sub_dict) diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py index 2f5bcc748eb546b4dad7e1aeeb7a2882a40d8d35..4aa463152b9ec05fb5e1de9e1a5e386f6fc46e6f 100644 --- a/ipaserver/install/ldapupdate.py +++ b/ipaserver/install/ldapupdate.py @@ -39,6 +39,7 @@ from ipaserver.install import installutils from ipapython import ipautil, ipaldap from ipalib import errors from ipalib import api, create_api +from ipalib import constants from ipaplatform.paths import paths from ipaplatform import services from ipapython.dn import DN @@ -305,6 +306,10 @@ class LDAPUpdate: self.sub_dict["TIME"] = int(time.time()) if not self.sub_dict.get("DOMAIN") and domain is not None: self.sub_dict["DOMAIN"] = domain + if not self.sub_dict.get("MIN_DOMAIN_LEVEL"): + self.sub_dict["MIN_DOMAIN_LEVEL"] = str(constants.MIN_DOMAIN_LEVEL) + if not self.sub_dict.get("MAX_DOMAIN_LEVEL"): + self.sub_dict["MAX_DOMAIN_LEVEL"] = str(constants.MAX_DOMAIN_LEVEL) self.api = create_api(mode=None) self.api.bootstrap(in_server=True, context='updates') self.api.finalize() diff --git a/ipaserver/install/plugins/update_managed_permissions.py b/ipaserver/install/plugins/update_managed_permissions.py index 1fbfd9993fa2c871690b58cdce7000cd3deba0d5..cc2fc2f99db122a64234a66b31f3b68b81fcc8a1 100644 --- a/ipaserver/install/plugins/update_managed_permissions.py +++ b/ipaserver/install/plugins/update_managed_permissions.py @@ -338,7 +338,16 @@ NONOBJECT_PERMISSIONS = { 'serviceAuthenticationMethod', 'objectclassMap', 'attributeMap', 'profileTTL' }, - } + }, + 'System: Read Domain Level': { + 'ipapermlocation': DN('cn=masters,cn=ipa,cn=etc', api.env.basedn), + 'ipapermtargetfilter': {'(objectclass=ipadomainlevelconfig)'}, + 'ipapermbindruletype': 'all', + 'ipapermright': {'read', 'search', 'compare'}, + 'ipapermdefaultattr': { + 'ipadomainlevel', 'objectclass', + }, + }, } -- 2.1.0
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code