mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Add Domain Level feature
https://fedorahosted.org/freeipa/ticket/5018 Reviewed-By: Jan Cholasta <jcholast@redhat.com> Reviewed-By: Petr Vobornik <pvoborni@redhat.com>
This commit is contained in:
committed by
Jan Cholasta
parent
9eedffdfa6
commit
f3010498af
2
ACI.txt
2
ACI.txt
@@ -322,6 +322,8 @@ dn: cn=dna,cn=ipa,cn=etc,dc=ipa,dc=example
|
|||||||
aci: (targetattr = "cn || createtimestamp || dnahostname || dnaportnum || dnaremainingvalues || dnaremotebindmethod || dnaremoteconnprotocol || dnasecureportnum || entryusn || modifytimestamp || objectclass")(targetfilter = "(objectclass=dnasharedconfig)")(version 3.0;acl "permission:System: Read DNA Configuration";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cn || createtimestamp || dnahostname || dnaportnum || dnaremainingvalues || dnaremotebindmethod || dnaremoteconnprotocol || dnasecureportnum || entryusn || modifytimestamp || objectclass")(targetfilter = "(objectclass=dnasharedconfig)")(version 3.0;acl "permission:System: Read DNA Configuration";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: ou=profile,dc=ipa,dc=example
|
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";)
|
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=Domain Level,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
|
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";)
|
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
|
dn: cn=config
|
||||||
|
|||||||
9
API.txt
9
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: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
|
||||||
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
|
||||||
output: PrimaryKey('value', None, None)
|
output: PrimaryKey('value', None, None)
|
||||||
|
command: domainlevel_get
|
||||||
|
args: 0,1,1
|
||||||
|
option: Str('version?', exclude='webui')
|
||||||
|
output: Output('result', <type 'int'>, 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: env
|
command: env
|
||||||
args: 1,3,4
|
args: 1,3,4
|
||||||
arg: Str('variables*')
|
arg: Str('variables*')
|
||||||
|
|||||||
4
VERSION
4
VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
|
|||||||
# #
|
# #
|
||||||
########################################################
|
########################################################
|
||||||
IPA_API_VERSION_MAJOR=2
|
IPA_API_VERSION_MAJOR=2
|
||||||
IPA_API_VERSION_MINOR=119
|
IPA_API_VERSION_MINOR=120
|
||||||
# Last change: edewata - Added vault plugin
|
# Last change: tbabej - Add Domain Level feature
|
||||||
|
|||||||
6
install/share/72domainlevels.ldif
Normal file
6
install/share/72domainlevels.ldif
Normal file
@@ -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')
|
||||||
@@ -22,6 +22,7 @@ app_DATA = \
|
|||||||
70ipaotp.ldif \
|
70ipaotp.ldif \
|
||||||
70topology.ldif \
|
70topology.ldif \
|
||||||
71idviews.ldif \
|
71idviews.ldif \
|
||||||
|
72domainlevels.ldif \
|
||||||
anonymous-vlv.ldif \
|
anonymous-vlv.ldif \
|
||||||
bootstrap-template.ldif \
|
bootstrap-template.ldif \
|
||||||
caJarSigningCert.cfg.template \
|
caJarSigningCert.cfg.template \
|
||||||
@@ -34,6 +35,7 @@ app_DATA = \
|
|||||||
ds-nfiles.ldif \
|
ds-nfiles.ldif \
|
||||||
dns.ldif \
|
dns.ldif \
|
||||||
dnssec.ldif \
|
dnssec.ldif \
|
||||||
|
domainlevel.ldif \
|
||||||
kerberos.ldif \
|
kerberos.ldif \
|
||||||
indices.ldif \
|
indices.ldif \
|
||||||
bind.named.conf.template \
|
bind.named.conf.template \
|
||||||
|
|||||||
7
install/share/domainlevel.ldif
Normal file
7
install/share/domainlevel.ldif
Normal file
@@ -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: $DOMAIN_LEVEL
|
||||||
@@ -3,5 +3,9 @@ changetype: add
|
|||||||
objectclass: top
|
objectclass: top
|
||||||
objectclass: nsContainer
|
objectclass: nsContainer
|
||||||
objectclass: ipaReplTopoManagedServer
|
objectclass: ipaReplTopoManagedServer
|
||||||
ipaReplTopoManagedSuffix: $SUFFIX
|
objectClass: ipaConfigObject
|
||||||
|
objectClass: ipaSupportedDomainLevelConfig
|
||||||
cn: $FQDN
|
cn: $FQDN
|
||||||
|
ipaReplTopoManagedSuffix: $SUFFIX
|
||||||
|
ipaMinDomainLevel: $MIN_DOMAIN_LEVEL
|
||||||
|
ipaMaxDomainLevel: $MAX_DOMAIN_LEVEL
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ from ipaserver.install import cainstance
|
|||||||
from ipaserver.install import kra
|
from ipaserver.install import kra
|
||||||
from ipaserver.install import dns as dns_installer
|
from ipaserver.install import dns as dns_installer
|
||||||
from ipalib import api, create_api, errors, util, certstore, x509
|
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 import version
|
||||||
from ipapython.config import IPAOptionParser
|
from ipapython.config import IPAOptionParser
|
||||||
from ipapython import sysrestore
|
from ipapython import sysrestore
|
||||||
@@ -224,12 +224,12 @@ def install_ca_cert(ldap, base_dn, realm, cafile):
|
|||||||
try:
|
try:
|
||||||
certs = certstore.get_ca_certs(ldap, base_dn, realm, False)
|
certs = certstore.get_ca_certs(ldap, base_dn, realm, False)
|
||||||
except errors.NotFound:
|
except errors.NotFound:
|
||||||
shutil.copy(cafile, CACERT)
|
shutil.copy(cafile, constants.CACERT)
|
||||||
else:
|
else:
|
||||||
certs = [c[0] for c in certs if c[2] is not False]
|
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:
|
except Exception, e:
|
||||||
print "error copying files: " + str(e)
|
print "error copying files: " + str(e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -569,6 +569,30 @@ def main():
|
|||||||
print " %% ipa-replica-manage del %s --force" % config.host_name
|
print " %% ipa-replica-manage del %s --force" % config.host_name
|
||||||
exit(3)
|
exit(3)
|
||||||
|
|
||||||
|
# Detect the current domain level
|
||||||
|
try:
|
||||||
|
current = remote_api.Command['domainlevel_get']()['result']
|
||||||
|
except errors.NotFound:
|
||||||
|
# If we're joining an older master, domain entry 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
|
# Check pre-existing host entry
|
||||||
try:
|
try:
|
||||||
entry = conn.find_entries(u'fqdn=%s' % config.host_name, ['fqdn'], DN(api.env.container_host, api.env.basedn))
|
entry = conn.find_entries(u'fqdn=%s' % config.host_name, ['fqdn'], DN(api.env.container_host, api.env.basedn))
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ from ipapython import sysrestore
|
|||||||
from ipapython.ipautil import *
|
from ipapython.ipautil import *
|
||||||
from ipapython import ipautil
|
from ipapython import ipautil
|
||||||
from ipapython import dogtag
|
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 ipapython.config import IPAOptionParser
|
||||||
from ipalib.util import validate_domain_name
|
from ipalib.util import validate_domain_name
|
||||||
from ipalib.constants import CACERT
|
from ipalib.constants import CACERT
|
||||||
@@ -176,6 +176,8 @@ def parse_options():
|
|||||||
help="create home directories for users "
|
help="create home directories for users "
|
||||||
"on their first login")
|
"on their first login")
|
||||||
basic_group.add_option("--hostname", dest="host_name", help="fully qualified name of server")
|
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",
|
basic_group.add_option("--ip-address", dest="ip_addresses",
|
||||||
type="ip", ip_local=True, action="append", default=[],
|
type="ip", ip_local=True, action="append", default=[],
|
||||||
help="Master Server IP Address. This option can be used multiple times",
|
help="Master Server IP Address. This option can be used multiple times",
|
||||||
@@ -327,6 +329,15 @@ def parse_options():
|
|||||||
except ValueError, e:
|
except ValueError, e:
|
||||||
parser.error("invalid domain: " + unicode(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 not options.setup_dns:
|
||||||
if options.forwarders:
|
if options.forwarders:
|
||||||
parser.error("You cannot specify a --forwarder option without the --setup-dns option")
|
parser.error("You cannot specify a --forwarder option without the --setup-dns option")
|
||||||
@@ -1143,21 +1154,24 @@ def main():
|
|||||||
ntp.create_instance()
|
ntp.create_instance()
|
||||||
|
|
||||||
if options.dirsrv_cert_files:
|
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,
|
ds.create_instance(realm_name, host_name, domain_name,
|
||||||
dm_password, dirsrv_pkcs12_info,
|
dm_password, dirsrv_pkcs12_info,
|
||||||
idstart=options.idstart, idmax=options.idmax,
|
idstart=options.idstart, idmax=options.idmax,
|
||||||
subject_base=options.subject,
|
subject_base=options.subject,
|
||||||
hbac_allow=not options.hbac_allow)
|
hbac_allow=not options.hbac_allow)
|
||||||
else:
|
else:
|
||||||
ds = dsinstance.DsInstance(fstore=fstore)
|
ds = dsinstance.DsInstance(fstore=fstore,
|
||||||
|
domainlevel=options.domainlevel)
|
||||||
ds.create_instance(realm_name, host_name, domain_name,
|
ds.create_instance(realm_name, host_name, domain_name,
|
||||||
dm_password,
|
dm_password,
|
||||||
idstart=options.idstart, idmax=options.idmax,
|
idstart=options.idstart, idmax=options.idmax,
|
||||||
subject_base=options.subject,
|
subject_base=options.subject,
|
||||||
hbac_allow=not options.hbac_allow)
|
hbac_allow=not options.hbac_allow)
|
||||||
else:
|
else:
|
||||||
ds = dsinstance.DsInstance(fstore=fstore)
|
ds = dsinstance.DsInstance(fstore=fstore,
|
||||||
|
domainlevel=options.domainlevel)
|
||||||
ds.init_info(
|
ds.init_info(
|
||||||
realm_name, host_name, domain_name, dm_password,
|
realm_name, host_name, domain_name, dm_password,
|
||||||
options.subject, 1101, 1100, None)
|
options.subject, 1101, 1100, None)
|
||||||
|
|||||||
14
install/updates/72-domainlevels.update
Normal file
14
install/updates/72-domainlevels.update
Normal file
@@ -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
|
||||||
@@ -49,6 +49,7 @@ app_DATA = \
|
|||||||
61-trusts-s4u2proxy.update \
|
61-trusts-s4u2proxy.update \
|
||||||
62-ranges.update \
|
62-ranges.update \
|
||||||
71-idviews.update \
|
71-idviews.update \
|
||||||
|
72-domainlevels.update \
|
||||||
90-post_upgrade_plugins.update \
|
90-post_upgrade_plugins.update \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|||||||
@@ -224,3 +224,6 @@ LDAP_GENERALIZED_TIME_FORMAT = "%Y%m%d%H%M%SZ"
|
|||||||
|
|
||||||
IPA_ANCHOR_PREFIX = ':IPA:'
|
IPA_ANCHOR_PREFIX = ':IPA:'
|
||||||
SID_ANCHOR_PREFIX = ':SID:'
|
SID_ANCHOR_PREFIX = ':SID:'
|
||||||
|
|
||||||
|
MIN_DOMAIN_LEVEL = 0
|
||||||
|
MAX_DOMAIN_LEVEL = 1
|
||||||
|
|||||||
@@ -1344,6 +1344,22 @@ class EmptyResult(NotFound):
|
|||||||
|
|
||||||
errno = 4031
|
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):
|
class BuiltinError(ExecutionError):
|
||||||
"""
|
"""
|
||||||
**4100** Base class for builtin execution errors (*4100 - 4199*).
|
**4100** Base class for builtin execution errors (*4100 - 4199*).
|
||||||
|
|||||||
138
ipalib/plugins/domainlevel.py
Normal file
138
ipalib/plugins/domainlevel.py
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from ipalib import _
|
||||||
|
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, _('Current domain level:')),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_domainlevel_dn(api):
|
||||||
|
domainlevel_dn = DN(
|
||||||
|
('cn', 'Domain Level'),
|
||||||
|
('cn', 'ipa'),
|
||||||
|
('cn', 'etc'),
|
||||||
|
api.env.basedn
|
||||||
|
)
|
||||||
|
|
||||||
|
return domainlevel_dn
|
||||||
|
|
||||||
|
|
||||||
|
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, api):
|
||||||
|
"""
|
||||||
|
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_get(Command):
|
||||||
|
__doc__ = _('Query current Domain Level.')
|
||||||
|
|
||||||
|
has_output = domainlevel_output
|
||||||
|
|
||||||
|
def execute(self, *args, **options):
|
||||||
|
ldap = self.api.Backend.ldap2
|
||||||
|
entry = ldap.get_entry(
|
||||||
|
get_domainlevel_dn(self.api),
|
||||||
|
['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(get_domainlevel_dn(self.api))
|
||||||
|
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, self.api):
|
||||||
|
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'])}
|
||||||
@@ -40,6 +40,7 @@ from ipaserver.install import upgradeinstance
|
|||||||
from ipalib import api
|
from ipalib import api
|
||||||
from ipalib import certstore
|
from ipalib import certstore
|
||||||
from ipalib import errors
|
from ipalib import errors
|
||||||
|
from ipalib import constants
|
||||||
from ipaplatform.tasks import tasks
|
from ipaplatform.tasks import tasks
|
||||||
from ipalib.constants import CACERT
|
from ipalib.constants import CACERT
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
@@ -62,6 +63,7 @@ IPA_SCHEMA_FILES = ("60kerberos.ldif",
|
|||||||
"70ipaotp.ldif",
|
"70ipaotp.ldif",
|
||||||
"70topology.ldif",
|
"70topology.ldif",
|
||||||
"71idviews.ldif",
|
"71idviews.ldif",
|
||||||
|
"72domainlevels.ldif",
|
||||||
"15rfc2307bis.ldif",
|
"15rfc2307bis.ldif",
|
||||||
"15rfc4876.ldif")
|
"15rfc4876.ldif")
|
||||||
|
|
||||||
@@ -186,7 +188,7 @@ info: IPA V2.0
|
|||||||
|
|
||||||
class DsInstance(service.Service):
|
class DsInstance(service.Service):
|
||||||
def __init__(self, realm_name=None, domain_name=None, dm_password=None,
|
def __init__(self, realm_name=None, domain_name=None, dm_password=None,
|
||||||
fstore=None):
|
fstore=None, domainlevel=None):
|
||||||
service.Service.__init__(self, "dirsrv",
|
service.Service.__init__(self, "dirsrv",
|
||||||
service_desc="directory server",
|
service_desc="directory server",
|
||||||
dm_password=dm_password,
|
dm_password=dm_password,
|
||||||
@@ -209,6 +211,7 @@ class DsInstance(service.Service):
|
|||||||
self.subject_base = None
|
self.subject_base = None
|
||||||
self.open_ports = []
|
self.open_ports = []
|
||||||
self.run_init_memberof = True
|
self.run_init_memberof = True
|
||||||
|
self.domainlevel = domainlevel
|
||||||
if realm_name:
|
if realm_name:
|
||||||
self.suffix = ipautil.realm_to_suffix(self.realm)
|
self.suffix = ipautil.realm_to_suffix(self.realm)
|
||||||
self.__setup_sub_dict()
|
self.__setup_sub_dict()
|
||||||
@@ -254,6 +257,7 @@ class DsInstance(service.Service):
|
|||||||
def __common_post_setup(self):
|
def __common_post_setup(self):
|
||||||
self.step("initializing group membership", self.init_memberof)
|
self.step("initializing group membership", self.init_memberof)
|
||||||
self.step("adding master entry", self.__add_master_entry)
|
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.step("configuring Posix uid/gid generation",
|
||||||
self.__config_uidgid_gen)
|
self.__config_uidgid_gen)
|
||||||
self.step("adding replication acis", self.__add_replication_acis)
|
self.step("adding replication acis", self.__add_replication_acis)
|
||||||
@@ -395,7 +399,10 @@ class DsInstance(service.Service):
|
|||||||
IDMAX=self.idmax, HOST=self.fqdn,
|
IDMAX=self.idmax, HOST=self.fqdn,
|
||||||
ESCAPED_SUFFIX=str(self.suffix),
|
ESCAPED_SUFFIX=str(self.suffix),
|
||||||
GROUP=DS_GROUP,
|
GROUP=DS_GROUP,
|
||||||
IDRANGE_SIZE=idrange_size
|
IDRANGE_SIZE=idrange_size,
|
||||||
|
DOMAIN_LEVEL=self.domainlevel,
|
||||||
|
MAX_DOMAIN_LEVEL=constants.MAX_DOMAIN_LEVEL,
|
||||||
|
MIN_DOMAIN_LEVEL=constants.MIN_DOMAIN_LEVEL,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __create_instance(self):
|
def __create_instance(self):
|
||||||
@@ -1011,3 +1018,8 @@ class DsInstance(service.Service):
|
|||||||
root_logger.debug('Unable to find certificate subject base in '
|
root_logger.debug('Unable to find certificate subject base in '
|
||||||
'certmap.conf')
|
'certmap.conf')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def __set_domain_level(self):
|
||||||
|
# Create global domain level entry and set the domain level
|
||||||
|
if self.domainlevel is not None:
|
||||||
|
self._ldap_mod("domainlevel.ldif", self.sub_dict)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ from ipaserver.install import installutils
|
|||||||
from ipapython import ipautil, ipaldap
|
from ipapython import ipautil, ipaldap
|
||||||
from ipalib import errors
|
from ipalib import errors
|
||||||
from ipalib import api, create_api
|
from ipalib import api, create_api
|
||||||
|
from ipalib import constants
|
||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipaplatform import services
|
from ipaplatform import services
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
@@ -305,6 +306,10 @@ class LDAPUpdate:
|
|||||||
self.sub_dict["TIME"] = int(time.time())
|
self.sub_dict["TIME"] = int(time.time())
|
||||||
if not self.sub_dict.get("DOMAIN") and domain is not None:
|
if not self.sub_dict.get("DOMAIN") and domain is not None:
|
||||||
self.sub_dict["DOMAIN"] = domain
|
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 = create_api(mode=None)
|
||||||
self.api.bootstrap(in_server=True, context='updates')
|
self.api.bootstrap(in_server=True, context='updates')
|
||||||
self.api.finalize()
|
self.api.finalize()
|
||||||
|
|||||||
@@ -338,7 +338,16 @@ NONOBJECT_PERMISSIONS = {
|
|||||||
'serviceAuthenticationMethod', 'objectclassMap', 'attributeMap',
|
'serviceAuthenticationMethod', 'objectclassMap', 'attributeMap',
|
||||||
'profileTTL'
|
'profileTTL'
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
'System: Read Domain Level': {
|
||||||
|
'ipapermlocation': DN('cn=Domain Level,cn=ipa,cn=etc', api.env.basedn),
|
||||||
|
'ipapermtargetfilter': {'(objectclass=ipadomainlevelconfig)'},
|
||||||
|
'ipapermbindruletype': 'all',
|
||||||
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
|
'ipapermdefaultattr': {
|
||||||
|
'ipadomainlevel', 'objectclass',
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user