Imported Debian patch 4.0.5-6~numeezy
This commit is contained in:
committed by
Mario Fetka
parent
c44de33144
commit
10dfc9587b
22
ipaserver/install/plugins/Makefile.am
Normal file
22
ipaserver/install/plugins/Makefile.am
Normal file
@@ -0,0 +1,22 @@
|
||||
NULL =
|
||||
|
||||
appdir = $(pythondir)/ipaserver/install
|
||||
app_PYTHON = \
|
||||
__init__.py \
|
||||
baseupdate.py \
|
||||
fix_replica_agreements.py \
|
||||
rename_managed.py \
|
||||
dns.py \
|
||||
updateclient.py \
|
||||
update_services.py \
|
||||
update_anonymous_aci.py \
|
||||
update_pacs.py \
|
||||
ca_renewal_master.py \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(NULL)
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
*~ \
|
||||
Makefile.in
|
||||
@@ -20,3 +20,10 @@
|
||||
"""
|
||||
Provide a separate api for updates.
|
||||
"""
|
||||
PRE_SCHEMA_UPDATE = 0
|
||||
PRE_UPDATE = 1
|
||||
POST_UPDATE = 2
|
||||
|
||||
FIRST = 1
|
||||
MIDDLE = 2
|
||||
LAST = 4
|
||||
|
||||
Binary file not shown.
@@ -17,26 +17,24 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipaserver.install.plugins import MIDDLE
|
||||
from ipaserver.install.plugins.baseupdate import PostUpdate
|
||||
from ipalib import api, errors
|
||||
from ipalib import Updater
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipa_log_manager import *
|
||||
from ipapython import sysrestore
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install import sysupgrade
|
||||
|
||||
DEFAULT_ID_RANGE_SIZE = 200000
|
||||
|
||||
class update_default_range(Updater):
|
||||
class update_default_range(PostUpdate):
|
||||
"""
|
||||
Create default ID range for upgraded servers.
|
||||
"""
|
||||
order=MIDDLE
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
ldap = self.obj.backend
|
||||
|
||||
dn = DN(self.api.env.container_ranges, self.api.env.basedn)
|
||||
dn = DN(api.env.container_ranges, api.env.basedn)
|
||||
search_filter = "objectclass=ipaDomainIDRange"
|
||||
try:
|
||||
(entries, truncated) = ldap.find_entries(search_filter, [], dn)
|
||||
@@ -44,35 +42,34 @@ class update_default_range(Updater):
|
||||
pass
|
||||
else:
|
||||
root_logger.debug("default_range: ipaDomainIDRange entry found, skip plugin")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
dn = DN(('cn', 'admins'), self.api.env.container_group,
|
||||
self.api.env.basedn)
|
||||
dn = DN(('cn', 'admins'), api.env.container_group, api.env.basedn)
|
||||
try:
|
||||
admins_entry = ldap.get_entry(dn, ['gidnumber'])
|
||||
except errors.NotFound:
|
||||
root_logger.error("default_range: No local ID range and no admins "
|
||||
"group found. Cannot create default ID range")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
id_range_base_id = admins_entry['gidnumber'][0]
|
||||
id_range_name = '%s_id_range' % self.api.env.realm
|
||||
id_range_name = '%s_id_range' % api.env.realm
|
||||
id_range_size = DEFAULT_ID_RANGE_SIZE
|
||||
|
||||
range_entry = [
|
||||
dict(attr='objectclass', value='top'),
|
||||
dict(attr='objectclass', value='ipaIDrange'),
|
||||
dict(attr='objectclass', value='ipaDomainIDRange'),
|
||||
dict(attr='cn', value=id_range_name),
|
||||
dict(attr='ipabaseid', value=id_range_base_id),
|
||||
dict(attr='ipaidrangesize', value=id_range_size),
|
||||
dict(attr='iparangetype', value='ipa-local'),
|
||||
]
|
||||
range_entry = ['objectclass:top',
|
||||
'objectclass:ipaIDrange',
|
||||
'objectclass:ipaDomainIDRange',
|
||||
'cn:%s' % id_range_name,
|
||||
'ipabaseid:%s' % id_range_base_id,
|
||||
'ipaidrangesize:%s' % id_range_size,
|
||||
'iparangetype:ipa-local',
|
||||
]
|
||||
|
||||
dn = DN(('cn', '%s_id_range' % self.api.env.realm),
|
||||
self.api.env.container_ranges, self.api.env.basedn)
|
||||
updates = {}
|
||||
dn = DN(('cn', '%s_id_range' % api.env.realm),
|
||||
api.env.container_ranges, api.env.basedn)
|
||||
|
||||
update = {'dn': dn, 'default': range_entry}
|
||||
updates[dn] = {'dn': dn, 'default': range_entry}
|
||||
|
||||
# Default range entry has a hard-coded range size to 200000 which is
|
||||
# a default range size in ipa-server-install. This could cause issues
|
||||
@@ -80,7 +77,7 @@ class update_default_range(Updater):
|
||||
# bigger range (option --idmax).
|
||||
# We should make our best to check if this is the case and provide
|
||||
# user with an information how to fix it.
|
||||
dn = DN(self.api.env.container_dna_posix_ids, self.api.env.basedn)
|
||||
dn = DN(api.env.container_dna_posix_ids, api.env.basedn)
|
||||
search_filter = "objectclass=dnaSharedConfig"
|
||||
attrs = ['dnaHostname', 'dnaRemainingValues']
|
||||
try:
|
||||
@@ -118,203 +115,6 @@ class update_default_range(Updater):
|
||||
|
||||
root_logger.error("default_range: %s", "\n".join(msg))
|
||||
|
||||
return False, [update]
|
||||
|
||||
|
||||
class update_default_trust_view(Updater):
|
||||
"""
|
||||
Create Default Trust View for upgraded servers.
|
||||
"""
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
default_trust_view_dn = DN(('cn', 'Default Trust View'),
|
||||
self.api.env.container_views,
|
||||
self.api.env.basedn)
|
||||
|
||||
default_trust_view_entry = [
|
||||
dict(attr='objectclass', value='top'),
|
||||
dict(attr='objectclass', value='ipaIDView'),
|
||||
dict(attr='cn', value='Default Trust View'),
|
||||
dict(attr='description', value='Default Trust View for AD users. '
|
||||
'Should not be deleted.'),
|
||||
]
|
||||
|
||||
# First, see if trusts are enabled on the server
|
||||
if not self.api.Command.adtrust_is_enabled()['result']:
|
||||
self.log.debug('AD Trusts are not enabled on this server')
|
||||
return False, []
|
||||
|
||||
# Second, make sure the Default Trust View does not exist yet
|
||||
try:
|
||||
ldap.get_entry(default_trust_view_dn)
|
||||
except errors.NotFound:
|
||||
pass
|
||||
else:
|
||||
self.log.debug('Default Trust View already present on this server')
|
||||
return False, []
|
||||
|
||||
# We have a server with AD trust support without Default Trust View.
|
||||
# Create the Default Trust View entry.
|
||||
|
||||
update = {
|
||||
'dn': default_trust_view_dn,
|
||||
'default': default_trust_view_entry
|
||||
}
|
||||
|
||||
return False, [update]
|
||||
|
||||
|
||||
class update_sigden_extdom_broken_config(Updater):
|
||||
"""Fix configuration of sidgen and extdom plugins
|
||||
|
||||
Upgrade to IPA 4.2+ cause that sidgen and extdom plugins have improperly
|
||||
configured basedn.
|
||||
|
||||
All trusts which have been added when config was broken must to be
|
||||
re-added manually.
|
||||
|
||||
https://fedorahosted.org/freeipa/ticket/5665
|
||||
"""
|
||||
|
||||
sidgen_config_dn = DN("cn=IPA SIDGEN,cn=plugins,cn=config")
|
||||
extdom_config_dn = DN("cn=ipa_extdom_extop,cn=plugins,cn=config")
|
||||
|
||||
def _fix_config(self):
|
||||
"""Due upgrade error configuration of sidgen and extdom plugins may
|
||||
contain literally "$SUFFIX" value instead of real DN in nsslapd-basedn
|
||||
attribute
|
||||
|
||||
:return: True if config was fixed, False if fix is not needed
|
||||
"""
|
||||
ldap = self.api.Backend.ldap2
|
||||
basedn_attr = 'nsslapd-basedn'
|
||||
modified = False
|
||||
|
||||
for dn in (self.sidgen_config_dn, self.extdom_config_dn):
|
||||
try:
|
||||
entry = ldap.get_entry(dn, attrs_list=[basedn_attr])
|
||||
except errors.NotFound:
|
||||
self.log.debug("configuration for %s not found, skipping", dn)
|
||||
else:
|
||||
configured_suffix = entry.single_value.get(basedn_attr)
|
||||
if configured_suffix is None:
|
||||
raise RuntimeError(
|
||||
"Missing attribute {attr} in {dn}".format(
|
||||
attr=basedn_attr, dn=dn
|
||||
)
|
||||
)
|
||||
elif configured_suffix == "$SUFFIX":
|
||||
# configured value is wrong, fix it
|
||||
entry.single_value[basedn_attr] = str(self.api.env.basedn)
|
||||
self.log.debug("updating attribute %s of %s to correct "
|
||||
"value %s", basedn_attr, dn,
|
||||
self.api.env.basedn)
|
||||
ldap.update_entry(entry)
|
||||
modified = True
|
||||
else:
|
||||
self.log.debug("configured basedn for %s is okay", dn)
|
||||
|
||||
return modified
|
||||
|
||||
def execute(self, **options):
|
||||
if sysupgrade.get_upgrade_state('sidgen', 'config_basedn_updated'):
|
||||
self.log.debug("Already done, skipping")
|
||||
return False, ()
|
||||
|
||||
restart = False
|
||||
if self._fix_config():
|
||||
sysupgrade.set_upgrade_state('sidgen', 'update_sids', True)
|
||||
restart = True # DS has to be restarted to apply changes
|
||||
|
||||
sysupgrade.set_upgrade_state('sidgen', 'config_basedn_updated', True)
|
||||
return restart, ()
|
||||
|
||||
|
||||
class update_sids(Updater):
|
||||
"""SIDs may be not created properly if bug with wrong configuration for
|
||||
sidgen and extdom plugins is effective
|
||||
|
||||
This must be run after "update_sigden_extdom_broken_config"
|
||||
https://fedorahosted.org/freeipa/ticket/5665
|
||||
"""
|
||||
sidgen_config_dn = DN("cn=IPA SIDGEN,cn=plugins,cn=config")
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
if sysupgrade.get_upgrade_state('sidgen', 'update_sids') is not True:
|
||||
self.log.debug("SIDs do not need to be generated")
|
||||
return False, ()
|
||||
|
||||
# check if IPA domain for AD trust has been created, and if we need to
|
||||
# regenerate missing SIDs if attribute 'ipaNTSecurityIdentifier'
|
||||
domain_IPA_AD_dn = DN(
|
||||
('cn', self.api.env.domain),
|
||||
self.api.env.container_cifsdomains,
|
||||
self.api.env.basedn)
|
||||
attr_name = 'ipaNTSecurityIdentifier'
|
||||
|
||||
try:
|
||||
entry = ldap.get_entry(domain_IPA_AD_dn, attrs_list=[attr_name])
|
||||
except errors.NotFound:
|
||||
self.log.debug("IPA domain object %s is not configured",
|
||||
domain_IPA_AD_dn)
|
||||
sysupgrade.set_upgrade_state('sidgen', 'update_sids', False)
|
||||
return False, ()
|
||||
else:
|
||||
if not entry.single_value.get(attr_name):
|
||||
# we need to run sidgen task
|
||||
sidgen_task_dn = DN(
|
||||
"cn=generate domain sid,cn=ipa-sidgen-task,cn=tasks,"
|
||||
"cn=config")
|
||||
sidgen_tasks_attr = {
|
||||
"objectclass": ["top", "extensibleObject"],
|
||||
"cn": ["sidgen"],
|
||||
"delay": [0],
|
||||
"nsslapd-basedn": [self.api.env.basedn],
|
||||
}
|
||||
|
||||
task_entry = ldap.make_entry(sidgen_task_dn,
|
||||
**sidgen_tasks_attr)
|
||||
try:
|
||||
ldap.add_entry(task_entry)
|
||||
except errors.DuplicateEntry:
|
||||
self.log.debug("sidgen task already created")
|
||||
else:
|
||||
self.log.debug("sidgen task has been created")
|
||||
|
||||
# we have to check all trusts domains which may been affected by the
|
||||
# bug. Symptom is missing 'ipaNTSecurityIdentifier' attribute
|
||||
|
||||
base_dn = DN(self.api.env.container_adtrusts, self.api.env.basedn)
|
||||
try:
|
||||
trust_domain_entries, truncated = ldap.find_entries(
|
||||
base_dn=base_dn,
|
||||
scope=ldap.SCOPE_ONELEVEL,
|
||||
attrs_list=["cn"],
|
||||
# more types of trusts can be stored under cn=trusts, we need
|
||||
# the type with ipaNTTrustPartner attribute
|
||||
filter="(&(ipaNTTrustPartner=*)(!(%s=*)))" % attr_name
|
||||
)
|
||||
except errors.NotFound:
|
||||
pass
|
||||
else:
|
||||
if truncated:
|
||||
self.log.warning("update_sids: Search results were truncated")
|
||||
|
||||
for entry in trust_domain_entries:
|
||||
domain = entry.single_value["cn"]
|
||||
self.log.error(
|
||||
"Your trust to %s is broken. Please re-create it by "
|
||||
"running 'ipa trust-add' again.", domain)
|
||||
|
||||
sysupgrade.set_upgrade_state('sidgen', 'update_sids', False)
|
||||
return False, ()
|
||||
|
||||
return (False, True, [updates])
|
||||
|
||||
api.register(update_default_range)
|
||||
api.register(update_default_trust_view)
|
||||
api.register(update_sids)
|
||||
api.register(update_sigden_extdom_broken_config)
|
||||
|
||||
89
ipaserver/install/plugins/baseupdate.py
Normal file
89
ipaserver/install/plugins/baseupdate.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# Authors:
|
||||
# Rob Crittenden <rcritten@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2011 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipalib import api
|
||||
from ipalib import Updater, Object
|
||||
from ipaserver.install import service
|
||||
from ipaserver.install.plugins import (PRE_UPDATE, POST_UPDATE,
|
||||
PRE_SCHEMA_UPDATE, MIDDLE)
|
||||
|
||||
class DSRestart(service.Service):
|
||||
"""
|
||||
Restart the 389-ds service.
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
This class is present to provide ldapupdate the means to
|
||||
restart 389-ds.
|
||||
"""
|
||||
service.Service.__init__(self, "dirsrv")
|
||||
|
||||
def start(self, instance_name="", capture_output=True, wait=True):
|
||||
"""
|
||||
During upgrades the server is listening only on the socket so
|
||||
we don't want to wait on ports. The caller is responsible for
|
||||
waiting for the socket to be ready.
|
||||
"""
|
||||
super(DSRestart, self).start(wait=False)
|
||||
|
||||
def create_instance(self):
|
||||
self.step("stopping directory server", self.stop)
|
||||
self.step("starting directory server", self.start)
|
||||
self.start_creation(start_message="Restarting Directory server "
|
||||
"to apply updates", show_service_name=False)
|
||||
|
||||
class update(Object):
|
||||
"""
|
||||
Generic object used to register all updates into a single namespace.
|
||||
"""
|
||||
backend_name = 'ldap2'
|
||||
|
||||
api.register(update)
|
||||
|
||||
|
||||
class PreSchemaUpdate(Updater):
|
||||
"""
|
||||
Base class for updates that run after file processing.
|
||||
"""
|
||||
updatetype = PRE_SCHEMA_UPDATE
|
||||
order = MIDDLE
|
||||
|
||||
def __init__(self):
|
||||
super(PreSchemaUpdate, self).__init__()
|
||||
|
||||
|
||||
class PreUpdate(Updater):
|
||||
"""
|
||||
Base class for updates that run prior to file processing.
|
||||
"""
|
||||
updatetype = PRE_UPDATE
|
||||
order = MIDDLE
|
||||
|
||||
def __init__(self):
|
||||
super(PreUpdate, self).__init__()
|
||||
|
||||
class PostUpdate(Updater):
|
||||
"""
|
||||
Base class for updates that run after file processing.
|
||||
"""
|
||||
updatetype = POST_UPDATE
|
||||
order = MIDDLE
|
||||
|
||||
def __init__(self):
|
||||
super(PostUpdate, self).__init__()
|
||||
BIN
ipaserver/install/plugins/baseupdate.pyc
Normal file
BIN
ipaserver/install/plugins/baseupdate.pyc
Normal file
Binary file not shown.
@@ -17,18 +17,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipaserver.install.plugins.baseupdate import PostUpdate
|
||||
from ipaserver.install import installutils, certs, cainstance
|
||||
from ipalib import errors
|
||||
from ipalib import Updater
|
||||
from ipalib.plugable import Registry
|
||||
from ipapython import certmonger
|
||||
from ipapython import certmonger, dogtag
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
|
||||
register = Registry()
|
||||
|
||||
@register()
|
||||
class update_ca_renewal_master(Updater):
|
||||
class update_ca_renewal_master(PostUpdate):
|
||||
"""
|
||||
Set CA renewal master in LDAP.
|
||||
"""
|
||||
@@ -37,9 +37,9 @@ class update_ca_renewal_master(Updater):
|
||||
ca = cainstance.CAInstance(self.api.env.realm, certs.NSS_DIR)
|
||||
if not ca.is_configured():
|
||||
self.debug("CA is not configured on this host")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
ldap = self.api.Backend.ldap2
|
||||
ldap = self.obj.backend
|
||||
base_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
|
||||
self.api.env.basedn)
|
||||
filter = '(&(cn=CA)(ipaConfigString=caRenewalMaster))'
|
||||
@@ -50,7 +50,7 @@ class update_ca_renewal_master(Updater):
|
||||
pass
|
||||
else:
|
||||
self.debug("found CA renewal master %s", entries[0].dn[1].value)
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
criteria = {
|
||||
'cert-database': paths.HTTPD_ALIAS_DIR,
|
||||
@@ -65,30 +65,31 @@ class update_ca_renewal_master(Updater):
|
||||
self.warning(
|
||||
"certmonger request for ipaCert is missing ca_name, "
|
||||
"assuming local CA is renewal slave")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
ca_name = ca_name.strip()
|
||||
|
||||
if ca_name == 'dogtag-ipa-renew-agent':
|
||||
pass
|
||||
elif ca_name == 'dogtag-ipa-retrieve-agent-submit':
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
elif ca_name == 'dogtag-ipa-ca-renew-agent':
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
else:
|
||||
self.warning(
|
||||
"certmonger request for ipaCert has unknown ca_name '%s', "
|
||||
"assuming local CA is renewal slave", ca_name)
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
else:
|
||||
self.debug("certmonger request for ipaCert not found")
|
||||
|
||||
config = installutils.get_directive(
|
||||
paths.CA_CS_CFG_PATH, 'subsystem.select', '=')
|
||||
dogtag.configured_constants().CS_CFG_PATH,
|
||||
'subsystem.select', '=')
|
||||
|
||||
if config == 'New':
|
||||
pass
|
||||
elif config == 'Clone':
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
else:
|
||||
self.warning(
|
||||
"CS.cfg has unknown subsystem.select value '%s', "
|
||||
@@ -97,11 +98,10 @@ class update_ca_renewal_master(Updater):
|
||||
|
||||
dn = DN(('cn', 'CA'), ('cn', self.api.env.host), base_dn)
|
||||
update = {
|
||||
dn: {
|
||||
'dn': dn,
|
||||
'updates': [
|
||||
dict(action='add', attr='ipaConfigString',
|
||||
value='caRenewalMaster')
|
||||
],
|
||||
'updates': ['add:ipaConfigString: caRenewalMaster'],
|
||||
},
|
||||
}
|
||||
|
||||
return False, [update]
|
||||
return (False, True, [update])
|
||||
|
||||
@@ -18,20 +18,22 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ldap as _ldap
|
||||
import re
|
||||
import traceback
|
||||
import time
|
||||
|
||||
from ldif import LDIFWriter
|
||||
|
||||
from ipaserver.install.plugins import MIDDLE, LAST
|
||||
from ipaserver.install.plugins.baseupdate import (PostUpdate, PreUpdate,
|
||||
PreSchemaUpdate)
|
||||
from ipaserver.install import sysupgrade
|
||||
from ipalib import api, errors, util
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
from ipalib.plugins.dns import dns_container_exists
|
||||
from ipapython.ipa_log_manager import *
|
||||
|
||||
|
||||
class update_dnszones(Updater):
|
||||
class update_dnszones(PostUpdate):
|
||||
"""
|
||||
Update all zones to meet requirements in the new FreeIPA versions
|
||||
|
||||
@@ -55,17 +57,18 @@ class update_dnszones(Updater):
|
||||
|
||||
This module extends the original policy to allow the SSHFP updates.
|
||||
"""
|
||||
order=MIDDLE
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
ldap = self.obj.backend
|
||||
if not dns_container_exists(ldap):
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
try:
|
||||
zones = self.api.Command.dnszone_find(all=True)['result']
|
||||
zones = api.Command.dnszone_find(all=True)['result']
|
||||
except errors.NotFound:
|
||||
self.log.debug('No DNS zone to update found')
|
||||
return False, []
|
||||
self.log.info('No DNS zone to update found')
|
||||
return (False, False, [])
|
||||
|
||||
for zone in zones:
|
||||
update = {}
|
||||
@@ -77,23 +80,20 @@ class update_dnszones(Updater):
|
||||
# do not open zone transfers by default
|
||||
update['idnsallowtransfer'] = u'none;'
|
||||
|
||||
old_policy = util.get_dns_forward_zone_update_policy(
|
||||
self.api.env.realm, ('A', 'AAAA'))
|
||||
old_policy = util.get_dns_forward_zone_update_policy(api.env.realm, ('A', 'AAAA'))
|
||||
if zone.get('idnsupdatepolicy', [''])[0] == old_policy:
|
||||
update['idnsupdatepolicy'] = util.get_dns_forward_zone_update_policy(\
|
||||
self.api.env.realm)
|
||||
api.env.realm)
|
||||
|
||||
if update:
|
||||
# FIXME: https://fedorahosted.org/freeipa/ticket/4722
|
||||
self.api.Command.dnszone_mod(zone[u'idnsname'][0].make_absolute(),
|
||||
**update)
|
||||
api.Command.dnszone_mod(zone[u'idnsname'][0], **update)
|
||||
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
api.register(update_dnszones)
|
||||
|
||||
|
||||
class update_dns_limits(Updater):
|
||||
class update_dns_limits(PostUpdate):
|
||||
"""
|
||||
bind-dyndb-ldap persistent search queries LDAP for all DNS records.
|
||||
The LDAP connection must have no size or time limits to work
|
||||
@@ -104,10 +104,10 @@ class update_dns_limits(Updater):
|
||||
limit_value = '-1'
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
ldap = self.obj.backend
|
||||
|
||||
if not dns_container_exists(ldap):
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
dns_principal = 'DNS/%s@%s' % (self.env.host, self.env.realm)
|
||||
dns_service_dn = DN(('krbprincipalname', dns_principal),
|
||||
@@ -119,29 +119,56 @@ class update_dns_limits(Updater):
|
||||
except errors.NotFound:
|
||||
# this host may not have DNS service set
|
||||
root_logger.debug("DNS: service %s not found, no need to update limits" % dns_service_dn)
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
if all(entry.get(limit.lower(), [None])[0] == self.limit_value for limit in self.limit_attributes):
|
||||
root_logger.debug("DNS: limits for service %s already set" % dns_service_dn)
|
||||
# service is already updated
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
limit_updates = []
|
||||
|
||||
for limit in self.limit_attributes:
|
||||
limit_updates.append(dict(action='only', attr=limit,
|
||||
value=self.limit_value))
|
||||
limit_updates.append('only:%s:%s' % (limit, self.limit_value))
|
||||
|
||||
dnsupdate = {'dn': dns_service_dn, 'updates': limit_updates}
|
||||
dnsupdates = {}
|
||||
dnsupdates[dns_service_dn] = {'dn': dns_service_dn,
|
||||
'updates': limit_updates}
|
||||
root_logger.debug("DNS: limits for service %s will be updated" % dns_service_dn)
|
||||
|
||||
|
||||
return False, [dnsupdate]
|
||||
return (False, True, [dnsupdates])
|
||||
|
||||
api.register(update_dns_limits)
|
||||
|
||||
|
||||
class update_master_to_dnsforwardzones(Updater):
|
||||
class update_check_forwardzones(PreSchemaUpdate):
|
||||
"""
|
||||
Check if the idnsforwardzone objectclass is in LDAP schema.
|
||||
If not update is required (update_to_forward_zones), set sysupgrade state
|
||||
'update_to_forward_zones' to True
|
||||
"""
|
||||
|
||||
def execute(self, **options):
|
||||
state = sysupgrade.get_upgrade_state('dns', 'update_to_forward_zones')
|
||||
if state is False:
|
||||
# no upgrade is needed
|
||||
return (False, False, [])
|
||||
ldap = self.obj.backend
|
||||
if not dns_container_exists(ldap): # No DNS installed
|
||||
return (False, False, [])
|
||||
result = ldap.schema.get_obj(_ldap.schema.models.ObjectClass, 'idnsforwardzone')
|
||||
if result is None:
|
||||
sysupgrade.set_upgrade_state('dns', 'update_to_forward_zones', True)
|
||||
self.log.info('Prepared upgrade to forward zones')
|
||||
else:
|
||||
sysupgrade.set_upgrade_state('dns', 'update_to_forward_zones', False)
|
||||
return (False, False, [])
|
||||
|
||||
api.register(update_check_forwardzones)
|
||||
|
||||
|
||||
class update_master_to_dnsforwardzones(PostUpdate):
|
||||
"""
|
||||
Update all zones to meet requirements in the new FreeIPA versions
|
||||
|
||||
@@ -151,59 +178,26 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
|
||||
This should be applied only once, and only if original version was lower than 4.0
|
||||
"""
|
||||
order = LAST
|
||||
|
||||
backup_dir = u'/var/lib/ipa/backup/'
|
||||
backup_filename = u'dns-forward-zones-backup-%Y-%m-%d-%H-%M-%S.ldif'
|
||||
backup_path = u'%s%s' % (backup_dir, backup_filename)
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
# check LDAP if forwardzones already uses new semantics
|
||||
dns_container_dn = DN(self.api.env.container_dns, self.api.env.basedn)
|
||||
try:
|
||||
container_entry = ldap.get_entry(dns_container_dn)
|
||||
except errors.NotFound:
|
||||
# DNS container not found, nothing to upgrade
|
||||
return False, []
|
||||
ldap = self.obj.backend
|
||||
if not sysupgrade.get_upgrade_state('dns', 'update_to_forward_zones'):
|
||||
# forward zones was tranformed before, nothing to do
|
||||
return (False, False, [])
|
||||
|
||||
for config_option in container_entry.get("ipaConfigString", []):
|
||||
matched = re.match("^DNSVersion\s+(?P<version>\d+)$",
|
||||
config_option, flags=re.I)
|
||||
if matched and int(matched.group("version")) >= 1:
|
||||
# forwardzones already uses new semantics,
|
||||
# no upgrade is required
|
||||
return False, []
|
||||
|
||||
self.log.debug('Updating forward zones')
|
||||
# update the DNSVersion, following upgrade can be executed only once
|
||||
container_entry.setdefault(
|
||||
'ipaConfigString', []).append(u"DNSVersion 1")
|
||||
ldap.update_entry(container_entry)
|
||||
|
||||
# Updater in IPA version from 4.0 to 4.1.2 doesn't work well, this
|
||||
# should detect if update in past has been executed, and set proper
|
||||
# DNSVersion into LDAP
|
||||
try:
|
||||
fwzones = self.api.Command.dnsforwardzone_find()['result']
|
||||
except errors.NotFound:
|
||||
# No forwardzones found, update probably has not been executed yet
|
||||
pass
|
||||
else:
|
||||
if fwzones:
|
||||
# fwzones exist, do not execute upgrade again
|
||||
return False, []
|
||||
|
||||
zones = []
|
||||
try:
|
||||
# raw values are required to store into ldif
|
||||
zones = self.api.Command.dnszone_find(all=True,
|
||||
zones = api.Command.dnszone_find(all=True,
|
||||
raw=True,
|
||||
sizelimit=0)['result']
|
||||
except errors.NotFound:
|
||||
pass
|
||||
|
||||
if not zones:
|
||||
self.log.debug('No DNS zone to update found')
|
||||
return False, []
|
||||
self.log.info('No DNS zone to update found')
|
||||
return (False, False, [])
|
||||
|
||||
zones_to_transform = []
|
||||
|
||||
@@ -251,7 +245,7 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
zone_to_privileges[zone['idnsname'][0]] = entry['member']
|
||||
|
||||
# raw values are required to store into ldif
|
||||
records = self.api.Command['dnsrecord_find'](
|
||||
records = api.Command['dnsrecord_find'](
|
||||
zone['idnsname'][0],
|
||||
all=True,
|
||||
raw=True,
|
||||
@@ -264,34 +258,34 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
del record['dn']
|
||||
writer.unparse(dn, record)
|
||||
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
self.log.error('Unable to backup zone %s' %
|
||||
zone['idnsname'][0])
|
||||
self.log.error(traceback.format_exc())
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
for privilege_dn in privileges_to_ldif:
|
||||
try:
|
||||
entry = ldap.get_entry(privilege_dn)
|
||||
writer.unparse(str(entry.dn), dict(entry.raw))
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
self.log.error('Unable to backup privilege %s' %
|
||||
privilege_dn)
|
||||
self.log.error(traceback.format_exc())
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
f.close()
|
||||
except Exception:
|
||||
self.log.error('Unable to create backup file')
|
||||
self.log.error(traceback.format_exc())
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
# update
|
||||
for zone in zones_to_transform:
|
||||
# delete master zone
|
||||
try:
|
||||
self.api.Command['dnszone_del'](zone['idnsname'])
|
||||
except Exception as e:
|
||||
api.Command['dnszone_del'](zone['idnsname'])
|
||||
except Exception, e:
|
||||
self.log.error('Transform to forwardzone terminated: '
|
||||
'removing zone %s failed (%s)' % (
|
||||
zone['idnsname'][0], e)
|
||||
@@ -305,8 +299,8 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
'idnsforwarders': zone.get('idnsforwarders', []),
|
||||
'idnsforwardpolicy': zone.get('idnsforwardpolicy', [u'first'])[0]
|
||||
}
|
||||
self.api.Command['dnsforwardzone_add'](zone['idnsname'][0], **kw)
|
||||
except Exception as e:
|
||||
api.Command['dnsforwardzone_add'](zone['idnsname'][0], **kw)
|
||||
except Exception, e:
|
||||
self.log.error('Transform to forwardzone terminated: creating '
|
||||
'forwardzone %s failed' %
|
||||
zone['idnsname'][0])
|
||||
@@ -316,9 +310,9 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
# create permission if original zone has one
|
||||
if 'managedBy' in zone:
|
||||
try:
|
||||
perm_name = self.api.Command['dnsforwardzone_add_permission'](
|
||||
perm_name = api.Command['dnsforwardzone_add_permission'](
|
||||
zone['idnsname'][0])['value']
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
self.log.error('Transform to forwardzone terminated: '
|
||||
'Adding managed by permission to forward zone'
|
||||
' %s failed' % zone['idnsname'])
|
||||
@@ -334,9 +328,9 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
dn[0].value for dn in zone_to_privileges[zone['idnsname'][0]]
|
||||
]
|
||||
try:
|
||||
self.api.Command['permission_add_member'](perm_name,
|
||||
api.Command['permission_add_member'](perm_name,
|
||||
privilege=privileges)
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
self.log.error('Unable to restore privileges for '
|
||||
'permission %s, for zone %s'
|
||||
% (perm_name, zone['idnsname']))
|
||||
@@ -346,9 +340,12 @@ class update_master_to_dnsforwardzones(Updater):
|
||||
zone['idnsname'][0])
|
||||
continue
|
||||
|
||||
self.log.debug('Zone %s was sucessfully transformed to forward zone',
|
||||
self.log.info('Zone %s was sucessfully transformed to forward zone',
|
||||
zone['idnsname'][0])
|
||||
|
||||
return False, []
|
||||
|
||||
sysupgrade.set_upgrade_state('dns', 'update_to_forward_zones', False)
|
||||
|
||||
return (False, False, [])
|
||||
|
||||
api.register(update_master_to_dnsforwardzones)
|
||||
|
||||
@@ -20,27 +20,30 @@
|
||||
import os
|
||||
import pwd
|
||||
from ipapython import ipaldap
|
||||
from ipaserver.install.plugins import MIDDLE
|
||||
from ipaserver.install.plugins.baseupdate import PreUpdate
|
||||
from ipaserver.install import replication
|
||||
from ipalib import api
|
||||
from ipalib import Updater
|
||||
|
||||
EXCLUDE_TEMPLATE = '(objectclass=*) $ EXCLUDE %s'
|
||||
|
||||
|
||||
class update_replica_attribute_lists(Updater):
|
||||
class update_replica_attribute_lists(PreUpdate):
|
||||
"""
|
||||
Run through all replication agreements and ensure that EXCLUDE list
|
||||
has all the required attributes so that we don't cause replication
|
||||
storms.
|
||||
"""
|
||||
|
||||
order = MIDDLE
|
||||
|
||||
def execute(self, **options):
|
||||
# We need an IPAdmin connection to the backend
|
||||
self.log.debug("Start replication agreement exclude list update task")
|
||||
conn = self.api.Backend.ldap2
|
||||
conn = ipaldap.IPAdmin(api.env.host, ldapi=True, realm=api.env.realm)
|
||||
conn.do_external_bind(pwd.getpwuid(os.geteuid()).pw_name)
|
||||
|
||||
repl = replication.ReplicationManager(self.api.env.realm,
|
||||
self.api.env.host,
|
||||
repl = replication.ReplicationManager(api.env.realm, api.env.host,
|
||||
None, conn=conn)
|
||||
|
||||
# We need to update only IPA replica agreements, not winsync
|
||||
@@ -49,8 +52,7 @@ class update_replica_attribute_lists(Updater):
|
||||
self.log.debug("Found %d agreement(s)", len(ipa_replicas))
|
||||
|
||||
for replica in ipa_replicas:
|
||||
for desc in replica.get('description', []):
|
||||
self.log.debug(desc)
|
||||
self.log.debug(replica.single_value.get('description'))
|
||||
|
||||
self._update_attr(repl, replica,
|
||||
'nsDS5ReplicatedAttributeList',
|
||||
@@ -63,7 +65,7 @@ class update_replica_attribute_lists(Updater):
|
||||
|
||||
self.log.debug("Done updating agreements")
|
||||
|
||||
return False, [] # No restart, no updates
|
||||
return (False, False, []) # No restart, no apply now, no updates
|
||||
|
||||
def _update_attr(self, repl, replica, attribute, values, template='%s'):
|
||||
"""Add or update an attribute of a replication agreement
|
||||
@@ -89,7 +91,7 @@ class update_replica_attribute_lists(Updater):
|
||||
try:
|
||||
repl.conn.update_entry(replica)
|
||||
self.log.debug("Updated")
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
self.log.error("Error caught updating replica: %s", str(e))
|
||||
|
||||
else:
|
||||
@@ -107,7 +109,7 @@ class update_replica_attribute_lists(Updater):
|
||||
try:
|
||||
repl.conn.update_entry(replica)
|
||||
self.log.debug("Updated %s", attribute)
|
||||
except Exception as e:
|
||||
except Exception, e:
|
||||
self.log.error("Error caught updating %s: %s",
|
||||
attribute, str(e))
|
||||
else:
|
||||
|
||||
@@ -17,16 +17,11 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import six
|
||||
|
||||
from ipaserver.install.plugins import FIRST, LAST
|
||||
from ipaserver.install.plugins.baseupdate import PreUpdate, PostUpdate
|
||||
from ipalib import api, errors
|
||||
from ipalib import Updater
|
||||
from ipapython import ipautil
|
||||
from ipapython.dn import DN
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
from ipapython.dn import DN, EditableDN
|
||||
|
||||
def entry_to_update(entry):
|
||||
"""
|
||||
@@ -39,34 +34,22 @@ def entry_to_update(entry):
|
||||
update = []
|
||||
for attr in entry.keys():
|
||||
if isinstance(entry[attr], list):
|
||||
for item in entry[attr]:
|
||||
update.append(dict(attr=str(attr), value=str(item)))
|
||||
for i in xrange(len(entry[attr])):
|
||||
update.append('%s:%s' % (str(attr), str(entry[attr][i])))
|
||||
else:
|
||||
update.append(dict(attr=str(attr), value=str(entry[attr])))
|
||||
update.append('%s:%s' % (str(attr), str(entry[attr])))
|
||||
|
||||
return update
|
||||
|
||||
|
||||
class GenerateUpdateMixin(object):
|
||||
def _dn_suffix_replace(self, dn, old_suffix, new_suffix):
|
||||
"""Replace all occurences of "old" AVAs in a DN by "new"
|
||||
|
||||
If the input DN doesn't end with old_suffix, log, an raise ValueError.
|
||||
"""
|
||||
if not dn.endswith(old_suffix):
|
||||
self.error("unable to replace suffix '%s' with '%s' in '%s'",
|
||||
old_suffix, new_suffix, dn)
|
||||
raise ValueError('no replacement made')
|
||||
return DN(*dn[:-len(old_suffix)]) + new_suffix
|
||||
|
||||
def generate_update(self, deletes=False):
|
||||
"""
|
||||
We need to separate the deletes that need to happen from the
|
||||
new entries that need to be added.
|
||||
"""
|
||||
ldap = self.api.Backend.ldap2
|
||||
ldap = self.obj.backend
|
||||
|
||||
suffix = ipautil.realm_to_suffix(self.api.env.realm)
|
||||
suffix = ipautil.realm_to_suffix(api.env.realm)
|
||||
searchfilter = '(objectclass=*)'
|
||||
definitions_managed_entries = []
|
||||
|
||||
@@ -85,7 +68,7 @@ class GenerateUpdateMixin(object):
|
||||
definitions_managed_entries, truncated = ldap.find_entries(
|
||||
searchfilter, ['*'], old_definition_container,
|
||||
ldap.SCOPE_ONELEVEL)
|
||||
except errors.NotFound as e:
|
||||
except errors.NotFound, e:
|
||||
return (False, update_list)
|
||||
|
||||
for entry in definitions_managed_entries:
|
||||
@@ -95,18 +78,18 @@ class GenerateUpdateMixin(object):
|
||||
assert isinstance(old_dn, DN)
|
||||
try:
|
||||
entry = ldap.get_entry(old_dn, ['*'])
|
||||
except errors.NotFound as e:
|
||||
except errors.NotFound, e:
|
||||
pass
|
||||
else:
|
||||
# Compute the new dn by replacing the old container with the new container
|
||||
try:
|
||||
new_dn = self._dn_suffix_replace(
|
||||
entry.dn,
|
||||
old_suffix=old_template_container,
|
||||
new_suffix=new_template_container)
|
||||
except ValueError:
|
||||
new_dn = EditableDN(entry.dn)
|
||||
if new_dn.replace(old_template_container, new_template_container) != 1:
|
||||
self.error("unable to replace '%s' with '%s' in '%s'",
|
||||
old_template_container, new_template_container, entry.dn)
|
||||
continue
|
||||
|
||||
new_dn = DN(new_dn)
|
||||
|
||||
# The old attributes become defaults for the new entry
|
||||
new_update = {'dn': new_dn,
|
||||
'default': entry_to_update(entry)}
|
||||
@@ -115,63 +98,64 @@ class GenerateUpdateMixin(object):
|
||||
old_update = {'dn': entry.dn, 'deleteentry': None}
|
||||
|
||||
# Add the delete and replacement updates to the list of all updates
|
||||
update_list.append(old_update)
|
||||
update_list.append(new_update)
|
||||
update_list.append({entry.dn: old_update, new_dn: new_update})
|
||||
|
||||
else:
|
||||
# Update the template dn by replacing the old containter with the new container
|
||||
try:
|
||||
new_dn = self._dn_suffix_replace(
|
||||
entry['managedtemplate'][0],
|
||||
old_suffix=old_template_container,
|
||||
new_suffix=new_template_container)
|
||||
except ValueError:
|
||||
old_dn = entry['managedtemplate'][0]
|
||||
new_dn = EditableDN(old_dn)
|
||||
if new_dn.replace(old_template_container, new_template_container) != 1:
|
||||
self.error("unable to replace '%s' with '%s' in '%s'",
|
||||
old_template_container, new_template_container, old_dn)
|
||||
continue
|
||||
new_dn = DN(new_dn)
|
||||
entry['managedtemplate'] = new_dn
|
||||
|
||||
# Update the entry dn similarly
|
||||
try:
|
||||
new_dn = self._dn_suffix_replace(
|
||||
entry.dn,
|
||||
old_suffix=old_definition_container,
|
||||
new_suffix=new_definition_container)
|
||||
except ValueError:
|
||||
# Edit the dn, then convert it back to an immutable DN
|
||||
old_dn = entry.dn
|
||||
new_dn = EditableDN(old_dn)
|
||||
if new_dn.replace(old_definition_container, new_definition_container) != 1:
|
||||
self.error("unable to replace '%s' with '%s' in '%s'",
|
||||
old_definition_container, new_definition_container, old_dn)
|
||||
continue
|
||||
new_dn = DN(new_dn)
|
||||
|
||||
# The old attributes become defaults for the new entry
|
||||
new_update = {'dn': new_dn,
|
||||
'default': entry_to_update(entry)}
|
||||
|
||||
# Add the replacement update to the collection of all updates
|
||||
update_list.append(new_update)
|
||||
update_list.append({new_dn: new_update})
|
||||
|
||||
if len(update_list) > 0:
|
||||
restart = True
|
||||
update_list.sort(reverse=True, key=lambda x: x['dn'])
|
||||
update_list.sort(reverse=True)
|
||||
|
||||
return (restart, update_list)
|
||||
|
||||
class update_managed_post_first(Updater, GenerateUpdateMixin):
|
||||
class update_managed_post_first(PreUpdate, GenerateUpdateMixin):
|
||||
"""
|
||||
Update managed entries
|
||||
"""
|
||||
order=FIRST
|
||||
|
||||
def execute(self, **options):
|
||||
# Never need to restart with the pre-update changes
|
||||
(ignore, update_list) = self.generate_update(False)
|
||||
|
||||
return False, update_list
|
||||
return (False, True, update_list)
|
||||
|
||||
api.register(update_managed_post_first)
|
||||
|
||||
class update_managed_post(Updater, GenerateUpdateMixin):
|
||||
class update_managed_post(PostUpdate, GenerateUpdateMixin):
|
||||
"""
|
||||
Update managed entries
|
||||
"""
|
||||
order=LAST
|
||||
|
||||
def execute(self, **options):
|
||||
(restart, update_list) = self.generate_update(True)
|
||||
|
||||
return restart, update_list
|
||||
return (restart, True, update_list)
|
||||
|
||||
api.register(update_managed_post)
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from ipalib import api
|
||||
from ipalib import Updater
|
||||
from ipaserver.install import certs, cainstance
|
||||
from ipaserver.install import ldapupdate
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
|
||||
class update_ca_topology(Updater):
|
||||
"""
|
||||
Updates CA topology configuration entries
|
||||
"""
|
||||
|
||||
def execute(self, **options):
|
||||
|
||||
ca = cainstance.CAInstance(self.api.env.realm, certs.NSS_DIR)
|
||||
if not ca.is_configured():
|
||||
self.log.debug("CA is not configured on this host")
|
||||
return False, []
|
||||
|
||||
ld = ldapupdate.LDAPUpdate(ldapi=True, sub_dict={
|
||||
'SUFFIX': self.api.env.basedn,
|
||||
'FQDN': self.api.env.host,
|
||||
})
|
||||
|
||||
ld.update([paths.CA_TOPOLOGY_ULDIF])
|
||||
|
||||
return False, []
|
||||
|
||||
api.register(update_ca_topology)
|
||||
@@ -17,22 +17,25 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipaserver.install.plugins import MIDDLE
|
||||
from ipaserver.install.plugins.baseupdate import PostUpdate
|
||||
from ipalib import api, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipa_log_manager import *
|
||||
|
||||
|
||||
class update_idrange_type(Updater):
|
||||
class update_idrange_type(PostUpdate):
|
||||
"""
|
||||
Update all ID ranges that do not have ipaRangeType attribute filled.
|
||||
This applies to all ID ranges prior to IPA 3.3.
|
||||
"""
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
order = MIDDLE
|
||||
|
||||
base_dn = DN(self.api.env.container_ranges, self.api.env.basedn)
|
||||
def execute(self, **options):
|
||||
ldap = self.obj.backend
|
||||
|
||||
base_dn = DN(api.env.container_ranges, api.env.basedn)
|
||||
search_filter = ("(&(objectClass=ipaIDrange)(!(ipaRangeType=*)))")
|
||||
root_logger.debug("update_idrange_type: search for ID ranges with no "
|
||||
"type set")
|
||||
@@ -48,18 +51,18 @@ class update_idrange_type(Updater):
|
||||
except errors.NotFound:
|
||||
root_logger.debug("update_idrange_type: no ID range without "
|
||||
"type set found")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
except errors.ExecutionError as e:
|
||||
except errors.ExecutionError, e:
|
||||
root_logger.error("update_idrange_type: cannot retrieve list "
|
||||
"of ranges with no type set: %s", e)
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
if not entries:
|
||||
# No entry was returned, rather break than continue cycling
|
||||
root_logger.debug("update_idrange_type: no ID range was "
|
||||
"returned")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
root_logger.debug("update_idrange_type: found %d "
|
||||
"idranges to update, truncated: %s",
|
||||
@@ -89,7 +92,7 @@ class update_idrange_type(Updater):
|
||||
ldap.update_entry(entry)
|
||||
except (errors.EmptyModlist, errors.NotFound):
|
||||
pass
|
||||
except errors.ExecutionError as e:
|
||||
except errors.ExecutionError, e:
|
||||
root_logger.debug("update_idrange_type: cannot "
|
||||
"update idrange type: %s", e)
|
||||
error = True
|
||||
@@ -98,79 +101,14 @@ class update_idrange_type(Updater):
|
||||
# Exit loop to avoid infinite cycles
|
||||
root_logger.error("update_idrange_type: error(s) "
|
||||
"detected during idrange type update")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
elif not truncated:
|
||||
# All affected entries updated, exit the loop
|
||||
root_logger.debug("update_idrange_type: all affected idranges "
|
||||
"were assigned types")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
return False, []
|
||||
|
||||
|
||||
class update_idrange_baserid(Updater):
|
||||
"""
|
||||
Update ipa-ad-trust-posix ranges' base RID to 0. This applies to AD trust
|
||||
posix ranges prior to IPA 4.1.
|
||||
"""
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
base_dn = DN(self.api.env.container_ranges, self.api.env.basedn)
|
||||
search_filter = ("(&(objectClass=ipaTrustedADDomainRange)"
|
||||
"(ipaRangeType=ipa-ad-trust-posix)"
|
||||
"(!(ipaBaseRID=0)))")
|
||||
root_logger.debug(
|
||||
"update_idrange_baserid: search for ipa-ad-trust-posix ID ranges "
|
||||
"with ipaBaseRID != 0"
|
||||
)
|
||||
|
||||
try:
|
||||
(entries, truncated) = ldap.find_entries(
|
||||
search_filter, ['ipabaserid'], base_dn,
|
||||
paged_search=True, time_limit=0, size_limit=0)
|
||||
|
||||
except errors.NotFound:
|
||||
root_logger.debug("update_idrange_baserid: no AD domain "
|
||||
"range with posix attributes found")
|
||||
return False, []
|
||||
|
||||
except errors.ExecutionError as e:
|
||||
root_logger.error("update_idrange_baserid: cannot retrieve "
|
||||
"list of affected ranges: %s", e)
|
||||
return False, []
|
||||
|
||||
root_logger.debug("update_idrange_baserid: found %d "
|
||||
"idranges possible to update",
|
||||
len(entries))
|
||||
|
||||
error = False
|
||||
|
||||
# Set the range type
|
||||
for entry in entries:
|
||||
entry['ipabaserid'] = 0
|
||||
try:
|
||||
root_logger.debug("Updating existing idrange: %s" % (entry.dn))
|
||||
ldap.update_entry(entry)
|
||||
root_logger.info("Done")
|
||||
except (errors.EmptyModlist, errors.NotFound):
|
||||
pass
|
||||
except errors.ExecutionError as e:
|
||||
root_logger.debug("update_idrange_type: cannot "
|
||||
"update idrange: %s", e)
|
||||
error = True
|
||||
|
||||
if error:
|
||||
root_logger.error("update_idrange_baserid: error(s) "
|
||||
"detected during idrange baserid update")
|
||||
else:
|
||||
# All affected entries updated, exit the loop
|
||||
root_logger.debug("update_idrange_baserid: all affected "
|
||||
"idranges updated")
|
||||
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
api.register(update_idrange_type)
|
||||
api.register(update_idrange_baserid)
|
||||
|
||||
@@ -40,13 +40,13 @@ dict of the same format is defined in this module.
|
||||
The permission name must start with the "System:" prefix.
|
||||
|
||||
The template dictionary can have the following keys:
|
||||
* ipapermtarget, ipapermtargetfilter, ipapermlocation, ipapermright, ,ipapermtargetto, ipapermtargetfrom, objectclass
|
||||
* ipapermtarget, ipapermtargetfilter, ipapermlocation, ipapermright, objectclass
|
||||
- Directly used as attributes on the permission.
|
||||
- Replaced when upgrading an existing permission
|
||||
- If not specified, these default to the defaults of a permission of the
|
||||
corresponding --type, or, if non_object is specified, or if not on an
|
||||
object, to general permission defaults .
|
||||
- ipapermlocation, ipatargetto, ipapermtargetfrom, ipapermtarget must be DNs
|
||||
- ipapermlocation and ipapermtarget must be DNs
|
||||
- ipapermtargetfilter and objectclass must be iterables of strings
|
||||
* ipapermbindruletype
|
||||
- Directly used as attribute on the permission.
|
||||
@@ -83,19 +83,17 @@ No other keys are allowed in the template
|
||||
The plugin also deletes permissions specified in OBSOLETE_PERMISSIONS.
|
||||
"""
|
||||
|
||||
import six
|
||||
|
||||
from ipalib import api, errors
|
||||
from ipapython.dn import DN
|
||||
from ipalib.plugable import Registry
|
||||
from ipalib.plugins import aci
|
||||
from ipalib.plugins.permission import permission, permission_del
|
||||
from ipalib.aci import ACI
|
||||
from ipalib import Updater
|
||||
from ipapython import ipautil
|
||||
from ipaserver.plugins.ldap2 import ldap2
|
||||
from ipaserver.install.plugins import LAST
|
||||
from ipaserver.install.plugins.baseupdate import PostUpdate
|
||||
|
||||
if six.PY3:
|
||||
unicode = str
|
||||
|
||||
register = Registry()
|
||||
|
||||
@@ -119,17 +117,6 @@ NONOBJECT_PERMISSIONS = {
|
||||
},
|
||||
'default_privileges': {'IPA Masters Readers'},
|
||||
},
|
||||
'System: Compat Tree ID View targets': {
|
||||
'replaces_global_anonymous_aci': True,
|
||||
'ipapermlocation': api.env.basedn,
|
||||
'ipapermtarget': DN('cn=*,cn=compat', api.env.basedn),
|
||||
'ipapermtargetfilter': {'(objectclass=ipaOverrideTarget)'},
|
||||
'ipapermbindruletype': 'anonymous',
|
||||
'ipapermright': {'read', 'search', 'compare'},
|
||||
'ipapermdefaultattr': {
|
||||
'ipaAnchorUUID',
|
||||
},
|
||||
},
|
||||
'System: Read DNA Configuration': {
|
||||
'replaces_global_anonymous_aci': True,
|
||||
'ipapermlocation': DN('cn=dna,cn=ipa,cn=etc', api.env.basedn),
|
||||
@@ -152,29 +139,6 @@ NONOBJECT_PERMISSIONS = {
|
||||
'cn', 'objectclass', 'usercertificate',
|
||||
},
|
||||
},
|
||||
'System: Add CA Certificate For Renewal': {
|
||||
'ipapermlocation': DN('cn=ca_renewal,cn=ipa,cn=etc', api.env.basedn),
|
||||
'ipapermtarget': DN(
|
||||
'cn=caSigningCert cert-pki-ca,cn=ca_renewal,cn=ipa,cn=etc',
|
||||
api.env.basedn),
|
||||
'ipapermtargetfilter': {'(objectclass=pkiuser)'},
|
||||
'ipapermbindruletype': 'permission',
|
||||
'ipapermright': {'add'},
|
||||
'default_privileges': {'Certificate Administrators'},
|
||||
},
|
||||
'System: Modify CA Certificate For Renewal': {
|
||||
'ipapermlocation': DN('cn=ca_renewal,cn=ipa,cn=etc', api.env.basedn),
|
||||
'ipapermtarget': DN(
|
||||
'cn=caSigningCert cert-pki-ca,cn=ca_renewal,cn=ipa,cn=etc',
|
||||
api.env.basedn),
|
||||
'ipapermtargetfilter': {'(objectclass=pkiuser)'},
|
||||
'ipapermbindruletype': 'permission',
|
||||
'ipapermright': {'write'},
|
||||
'ipapermdefaultattr': {
|
||||
'usercertificate',
|
||||
},
|
||||
'default_privileges': {'Certificate Administrators'},
|
||||
},
|
||||
'System: Read CA Certificate': {
|
||||
'replaces_global_anonymous_aci': True,
|
||||
'ipapermlocation': DN('cn=CAcert,cn=ipa,cn=etc', api.env.basedn),
|
||||
@@ -186,52 +150,6 @@ NONOBJECT_PERMISSIONS = {
|
||||
'authorityrevocationlist', 'crosscertificatepair',
|
||||
},
|
||||
},
|
||||
'System: Modify CA Certificate': {
|
||||
'ipapermlocation': DN('cn=CAcert,cn=ipa,cn=etc', api.env.basedn),
|
||||
'ipapermtargetfilter': {'(objectclass=pkica)'},
|
||||
'ipapermbindruletype': 'permission',
|
||||
'ipapermright': {'write'},
|
||||
'ipapermdefaultattr': {
|
||||
'cacertificate',
|
||||
},
|
||||
'default_privileges': {'Certificate Administrators'},
|
||||
},
|
||||
'System: Read Certificate Store Entries': {
|
||||
'ipapermlocation': DN('cn=certificates,cn=ipa,cn=etc', api.env.basedn),
|
||||
'ipapermtargetfilter': {'(objectclass=ipacertificate)'},
|
||||
'ipapermbindruletype': 'anonymous',
|
||||
'ipapermright': {'read', 'search', 'compare'},
|
||||
'ipapermdefaultattr': {
|
||||
'cn', 'objectclass', 'ipacertsubject', 'ipacertissuerserial',
|
||||
'ipapublickey', 'ipaconfigstring', 'cacertificate', 'ipakeytrust',
|
||||
'ipakeyusage', 'ipakeyextusage',
|
||||
},
|
||||
},
|
||||
'System: Add Certificate Store Entry': {
|
||||
'ipapermlocation': DN('cn=certificates,cn=ipa,cn=etc', api.env.basedn),
|
||||
'ipapermtargetfilter': {'(objectclass=ipacertificate)'},
|
||||
'ipapermbindruletype': 'permission',
|
||||
'ipapermright': {'add'},
|
||||
'default_privileges': {'Certificate Administrators'},
|
||||
},
|
||||
'System: Modify Certificate Store Entry': {
|
||||
'ipapermlocation': DN('cn=certificates,cn=ipa,cn=etc', api.env.basedn),
|
||||
'ipapermtargetfilter': {'(objectclass=ipacertificate)'},
|
||||
'ipapermbindruletype': 'permission',
|
||||
'ipapermright': {'write'},
|
||||
'ipapermdefaultattr': {
|
||||
'ipacertissuerserial', 'ipaconfigstring', 'cacertificate',
|
||||
'ipakeytrust', 'ipakeyusage', 'ipakeyextusage',
|
||||
},
|
||||
'default_privileges': {'Certificate Administrators'},
|
||||
},
|
||||
'System: Remove Certificate Store Entry': {
|
||||
'ipapermlocation': DN('cn=certificates,cn=ipa,cn=etc', api.env.basedn),
|
||||
'ipapermtargetfilter': {'(objectclass=ipacertificate)'},
|
||||
'ipapermbindruletype': 'permission',
|
||||
'ipapermright': {'delete'},
|
||||
'default_privileges': {'Certificate Administrators'},
|
||||
},
|
||||
'System: Read Replication Information': {
|
||||
'replaces_global_anonymous_aci': True,
|
||||
'ipapermlocation': DN('cn=replication,cn=etc', api.env.basedn),
|
||||
@@ -262,35 +180,67 @@ NONOBJECT_PERMISSIONS = {
|
||||
'ipantdomainguid', 'ipantfallbackprimarygroup',
|
||||
},
|
||||
},
|
||||
'System: Read DUA Profile': {
|
||||
'ipapermlocation': DN('ou=profile', api.env.basedn),
|
||||
'System: Read Replication Agreements': {
|
||||
'ipapermlocation': DN('cn=config'),
|
||||
'ipapermtargetfilter': {
|
||||
'(|'
|
||||
'(objectclass=organizationalUnit)'
|
||||
'(objectclass=DUAConfigProfile)'
|
||||
'(objectclass=nsds5Replica)'
|
||||
'(objectclass=nsds5replicationagreement)'
|
||||
'(objectclass=nsDSWindowsReplicationAgreement)'
|
||||
'(objectClass=nsMappingTree)'
|
||||
')'
|
||||
},
|
||||
'ipapermbindruletype': 'anonymous',
|
||||
'ipapermbindruletype': 'permission',
|
||||
'ipapermright': {'read', 'search', 'compare'},
|
||||
'ipapermdefaultattr': {
|
||||
'objectclass', 'ou', 'cn', 'defaultServerList',
|
||||
'preferredServerList', 'defaultSearchBase', 'defaultSearchScope',
|
||||
'searchTimeLimit', 'bindTimeLimit', 'credentialLevel',
|
||||
'authenticationMethod', 'followReferrals', 'dereferenceAliases',
|
||||
'serviceSearchDescriptor', 'serviceCredentialLevel',
|
||||
'serviceAuthenticationMethod', 'objectclassMap', 'attributeMap',
|
||||
'profileTTL'
|
||||
'cn', 'objectclass',
|
||||
# nsds5Replica
|
||||
'nsds5replicaroot', 'nsds5replicaid', 'nsds5replicacleanruv',
|
||||
'nsds5replicaabortcleanruv', 'nsds5replicatype',
|
||||
'nsds5replicabinddn', 'nsstate', 'nsds5replicaname',
|
||||
'nsds5flags', 'nsds5task', 'nsds5replicareferral',
|
||||
'nsds5replicaautoreferral', 'nsds5replicapurgedelay',
|
||||
'nsds5replicatombstonepurgeinterval', 'nsds5replicachangecount',
|
||||
'nsds5replicalegacyconsumer', 'nsds5replicaprotocoltimeout',
|
||||
'nsds5replicabackoffmin', 'nsds5replicabackoffmax',
|
||||
# nsds5replicationagreement
|
||||
'nsds5replicacleanruvnotified', 'nsds5replicahost',
|
||||
'nsds5replicaport', 'nsds5replicatransportinfo',
|
||||
'nsds5replicabinddn', 'nsds5replicacredentials',
|
||||
'nsds5replicabindmethod', 'nsds5replicaroot',
|
||||
'nsds5replicatedattributelist',
|
||||
'nsds5replicatedattributelisttotal', 'nsds5replicaupdateschedule',
|
||||
'nsds5beginreplicarefresh', 'description', 'nsds50ruv',
|
||||
'nsruvreplicalastmodified', 'nsds5replicatimeout',
|
||||
'nsds5replicachangessentsincestartup', 'nsds5replicalastupdateend',
|
||||
'nsds5replicalastupdatestart', 'nsds5replicalastupdatestatus',
|
||||
'nsds5replicaupdateinprogress', 'nsds5replicalastinitend',
|
||||
'nsds5replicaenabled', 'nsds5replicalastinitstart',
|
||||
'nsds5replicalastinitstatus', 'nsds5debugreplicatimeout',
|
||||
'nsds5replicabusywaittime', 'nsds5replicastripattrs',
|
||||
'nsds5replicasessionpausetime', 'nsds5replicaprotocoltimeout',
|
||||
# nsDSWindowsReplicationAgreement
|
||||
'nsds5replicahost', 'nsds5replicaport',
|
||||
'nsds5replicatransportinfo', 'nsds5replicabinddn',
|
||||
'nsds5replicacredentials', 'nsds5replicabindmethod',
|
||||
'nsds5replicaroot', 'nsds5replicatedattributelist',
|
||||
'nsds5replicaupdateschedule', 'nsds5beginreplicarefresh',
|
||||
'description', 'nsds50ruv', 'nsruvreplicalastmodified',
|
||||
'nsds5replicatimeout', 'nsds5replicachangessentsincestartup',
|
||||
'nsds5replicalastupdateend', 'nsds5replicalastupdatestart',
|
||||
'nsds5replicalastupdatestatus', 'nsds5replicaupdateinprogress',
|
||||
'nsds5replicalastinitend', 'nsds5replicalastinitstart',
|
||||
'nsds5replicalastinitstatus', 'nsds5debugreplicatimeout',
|
||||
'nsds5replicabusywaittime', 'nsds5replicasessionpausetime',
|
||||
'nsds7windowsreplicasubtree', 'nsds7directoryreplicasubtree',
|
||||
'nsds7newwinusersyncenabled', 'nsds7newwingroupsyncenabled',
|
||||
'nsds7windowsdomain', 'nsds7dirsynccookie', 'winsyncinterval',
|
||||
'onewaysync', 'winsyncmoveaction', 'nsds5replicaenabled',
|
||||
'winsyncdirectoryfilter', 'winsyncwindowsfilter',
|
||||
'winsyncsubtreepair',
|
||||
},
|
||||
},
|
||||
'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',
|
||||
},
|
||||
},
|
||||
'default_privileges': {'Replication Administrators'},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -299,13 +249,14 @@ class IncompatibleACIModification(Exception):
|
||||
|
||||
|
||||
@register()
|
||||
class update_managed_permissions(Updater):
|
||||
class update_managed_permissions(PostUpdate):
|
||||
"""Update managed permissions after an update.
|
||||
|
||||
Update managed permissions according to templates specified in plugins.
|
||||
For read permissions, puts any attributes specified in the legacy
|
||||
Anonymous access ACI in the exclude list when creating the permission.
|
||||
"""
|
||||
order = LAST
|
||||
|
||||
def get_anonymous_read_aci(self, ldap):
|
||||
aciname = u'Enable Anonymous access'
|
||||
@@ -327,7 +278,7 @@ class update_managed_permissions(Updater):
|
||||
|
||||
for acistr in acistrs:
|
||||
if ACI(acistr).isequal(anonymous_read_aci):
|
||||
self.log.debug('Removing anonymous ACI: %s', acistr)
|
||||
self.log.info('Removing anonymous ACI: %s', acistr)
|
||||
acistrs.remove(acistr)
|
||||
break
|
||||
else:
|
||||
@@ -343,31 +294,31 @@ class update_managed_permissions(Updater):
|
||||
"""
|
||||
for obj in sorted(self.api.Object(), key=lambda o: o.name):
|
||||
managed_permissions = getattr(obj, 'managed_permissions', {})
|
||||
for name, template in sorted(managed_permissions.items()):
|
||||
for name, template in sorted(managed_permissions.iteritems()):
|
||||
yield name, template, obj
|
||||
|
||||
for name, template in sorted(NONOBJECT_PERMISSIONS.items()):
|
||||
for name, template in sorted(NONOBJECT_PERMISSIONS.iteritems()):
|
||||
yield name, template, None
|
||||
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
ldap = self.api.Backend[ldap2]
|
||||
|
||||
anonymous_read_aci = self.get_anonymous_read_aci(ldap)
|
||||
|
||||
if anonymous_read_aci:
|
||||
self.log.debug('Anonymous read ACI: %s', anonymous_read_aci)
|
||||
self.log.info('Anonymous read ACI: %s', anonymous_read_aci)
|
||||
else:
|
||||
self.log.debug('Anonymous ACI not found')
|
||||
self.log.info('Anonymous ACI not found')
|
||||
|
||||
current_obj = () # initially distinct from any obj value, even None
|
||||
for name, template, obj in self.get_templates():
|
||||
if current_obj != obj:
|
||||
if obj:
|
||||
self.log.debug('Updating managed permissions for %s',
|
||||
self.log.info('Updating managed permissions for %s',
|
||||
obj.name)
|
||||
else:
|
||||
self.log.debug('Updating non-object managed permissions')
|
||||
self.log.info('Updating non-object managed permissions')
|
||||
current_obj = obj
|
||||
|
||||
self.update_permission(ldap,
|
||||
@@ -388,9 +339,9 @@ class update_managed_permissions(Updater):
|
||||
except errors.NotFound:
|
||||
self.log.debug('Obsolete permission not found')
|
||||
else:
|
||||
self.log.debug('Obsolete permission deleted: %s', obsolete_name)
|
||||
self.log.info('Obsolete permission deleted: %s', obsolete_name)
|
||||
|
||||
return False, ()
|
||||
return False, False, ()
|
||||
|
||||
def update_permission(self, ldap, obj, name, template, anonymous_read_aci):
|
||||
"""Update the given permission and the corresponding ACI"""
|
||||
@@ -480,7 +431,7 @@ class update_managed_permissions(Updater):
|
||||
permission_plugin.update_aci(entry)
|
||||
|
||||
if remove_legacy:
|
||||
self.log.debug("Removing legacy permission '%s'", legacy_name)
|
||||
self.log.info("Removing legacy permission '%s'", legacy_name)
|
||||
self.api.Command[permission_del](unicode(legacy_name))
|
||||
|
||||
for name in template.get('replaces_system', ()):
|
||||
@@ -489,14 +440,14 @@ class update_managed_permissions(Updater):
|
||||
entry = ldap.get_entry(permission_plugin.get_dn(name),
|
||||
['ipapermissiontype'])
|
||||
except errors.NotFound:
|
||||
self.log.debug("Legacy permission '%s' not found", name)
|
||||
self.log.info("Legacy permission '%s' not found", name)
|
||||
else:
|
||||
flags = entry.get('ipapermissiontype', [])
|
||||
if list(flags) == ['SYSTEM']:
|
||||
self.log.debug("Removing legacy permission '%s'", name)
|
||||
self.log.info("Removing legacy permission '%s'", name)
|
||||
self.api.Command[permission_del](name, force=True)
|
||||
else:
|
||||
self.log.debug("Ignoring V2 permission '%s'", name)
|
||||
self.log.info("Ignoring V2 permission '%s'", name)
|
||||
|
||||
def get_upgrade_attr_lists(self, current_acistring, default_acistrings):
|
||||
"""Compute included and excluded attributes for a new permission
|
||||
@@ -622,14 +573,6 @@ class update_managed_permissions(Updater):
|
||||
if ipapermtarget is not None:
|
||||
entry['ipapermtarget'] = ipapermtarget
|
||||
|
||||
ipapermtargetto = template.pop('ipapermtargetto', None)
|
||||
if ipapermtargetto is not None:
|
||||
entry['ipapermtargetto'] = ipapermtargetto
|
||||
|
||||
ipapermtargetfrom = template.pop('ipapermtargetfrom', None)
|
||||
if ipapermtargetfrom is not None:
|
||||
entry['ipapermtargetfrom'] = ipapermtargetfrom
|
||||
|
||||
# Attributes from template
|
||||
bindruletype = template.pop('ipapermbindruletype', 'permission')
|
||||
if is_new:
|
||||
@@ -659,7 +602,7 @@ class update_managed_permissions(Updater):
|
||||
anonymous_read_aci.target['targetattr']['expression'])
|
||||
read_blacklist &= attributes
|
||||
if read_blacklist:
|
||||
self.log.debug('Excluded attributes for %s: %s',
|
||||
self.log.info('Excluded attributes for %s: %s',
|
||||
name, ', '.join(read_blacklist))
|
||||
entry['ipapermexcludedattr'] = list(read_blacklist)
|
||||
|
||||
@@ -668,75 +611,3 @@ class update_managed_permissions(Updater):
|
||||
raise ValueError(
|
||||
'Unknown key(s) in managed permission template %s: %s' % (
|
||||
name, ', '.join(template.keys())))
|
||||
|
||||
|
||||
@register()
|
||||
class update_read_replication_agreements_permission(Updater):
|
||||
"""'Read replication agreements' permission must not be managed permission
|
||||
|
||||
https://fedorahosted.org/freeipa/ticket/5631
|
||||
|
||||
Existing permission "cn=System: Read Replication Agreements" must be moved
|
||||
to non-managed permission "cn=Read Replication Agreements" using modrdn
|
||||
ldap operation to keep current membership of the permission set by user.
|
||||
|
||||
ACI is updated via update files
|
||||
"""
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
old_perm_dn = DN(
|
||||
('cn', 'System: Read Replication Agreements'),
|
||||
self.api.env.container_permission,
|
||||
self.api.env.basedn
|
||||
)
|
||||
|
||||
new_perm_dn = DN(
|
||||
('cn', 'Read Replication Agreements'),
|
||||
self.api.env.container_permission,
|
||||
self.api.env.basedn
|
||||
)
|
||||
|
||||
try:
|
||||
perm_entry = ldap.get_entry(old_perm_dn)
|
||||
except errors.NotFound:
|
||||
self.log.debug("Old permission not found")
|
||||
return False, ()
|
||||
|
||||
try:
|
||||
ldap.get_entry(new_perm_dn)
|
||||
except errors.NotFound:
|
||||
# we can happily upgrade
|
||||
pass
|
||||
else:
|
||||
self.log.error("Permission '{}' cannot be upgraded. "
|
||||
"Permission with target name '{}' already "
|
||||
"exists".format(old_perm_dn, new_perm_dn))
|
||||
return False, ()
|
||||
|
||||
# values are case insensitive
|
||||
for t in list(perm_entry['ipapermissiontype']):
|
||||
if t.lower() in ['managed', 'v2']:
|
||||
perm_entry['ipapermissiontype'].remove(t)
|
||||
|
||||
for o in list(perm_entry['objectclass']):
|
||||
if o.lower() == 'ipapermissionv2':
|
||||
# remove permission V2 objectclass and related attributes
|
||||
perm_entry['objectclass'].remove(o)
|
||||
perm_entry['ipapermdefaultattr'] = []
|
||||
perm_entry['ipapermright'] = []
|
||||
perm_entry['ipapermbindruletype'] = []
|
||||
perm_entry['ipapermlocation'] = []
|
||||
perm_entry['ipapermtargetfilter'] = []
|
||||
|
||||
self.log.debug("Removing MANAGED attributes from permission %s",
|
||||
old_perm_dn)
|
||||
try:
|
||||
ldap.update_entry(perm_entry)
|
||||
except errors.EmptyModlist:
|
||||
pass
|
||||
|
||||
# do modrdn on permission
|
||||
self.log.debug("modrdn: %s -> %s", old_perm_dn, new_perm_dn)
|
||||
ldap.move_entry(old_perm_dn, new_perm_dn)
|
||||
return False, ()
|
||||
|
||||
Binary file not shown.
@@ -1,86 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from ipalib.plugable import Registry
|
||||
from ipalib import errors
|
||||
from ipalib import Updater
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
from ipaserver.install import sysupgrade
|
||||
from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
|
||||
register = Registry()
|
||||
|
||||
|
||||
@register()
|
||||
class update_nis_configuration(Updater):
|
||||
"""Update NIS configuration
|
||||
|
||||
NIS configuration can be updated only if NIS Server was configured via
|
||||
ipa-nis-manage command.
|
||||
"""
|
||||
|
||||
def __recover_from_missing_maps(self, ldap):
|
||||
# https://fedorahosted.org/freeipa/ticket/5507
|
||||
# if all following DNs are missing, but 'NIS Server' container exists
|
||||
# we are experiencig bug and maps should be fixed
|
||||
|
||||
if sysupgrade.get_upgrade_state('nis',
|
||||
'done_recover_from_missing_maps'):
|
||||
# this recover must be done only once, a user may deleted some
|
||||
# maps, we do not want to restore them again
|
||||
return
|
||||
|
||||
self.log.debug("Recovering from missing NIS maps bug")
|
||||
|
||||
suffix = "cn=NIS Server,cn=plugins,cn=config"
|
||||
domain = self.api.env.domain
|
||||
missing_dn_list = [
|
||||
DN(nis_map.format(domain=domain, suffix=suffix)) for nis_map in [
|
||||
"nis-domain={domain}+nis-map=passwd.byname,{suffix}",
|
||||
"nis-domain={domain}+nis-map=passwd.byuid,{suffix}",
|
||||
"nis-domain={domain}+nis-map=group.byname,{suffix}",
|
||||
"nis-domain={domain}+nis-map=group.bygid,{suffix}",
|
||||
"nis-domain={domain}+nis-map=netid.byname,{suffix}",
|
||||
"nis-domain={domain}+nis-map=netgroup,{suffix}",
|
||||
]
|
||||
]
|
||||
|
||||
for dn in missing_dn_list:
|
||||
try:
|
||||
ldap.get_entry(dn, attrs_list=['cn'])
|
||||
except errors.NotFound:
|
||||
pass
|
||||
else:
|
||||
# bug is not effective, at least one of 'possible missing'
|
||||
# maps was detected
|
||||
return
|
||||
|
||||
sysupgrade.set_upgrade_state('nis', 'done_recover_from_missing_maps',
|
||||
True)
|
||||
|
||||
# bug is effective run update to recreate missing maps
|
||||
ld = LDAPUpdate(sub_dict={}, ldapi=True)
|
||||
ld.update([paths.NIS_ULDIF])
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
try:
|
||||
ldap.get_entry(dn, attrs_list=['cn'])
|
||||
except errors.NotFound:
|
||||
# NIS is not configured on system, do not execute update
|
||||
self.log.debug("Skipping NIS update, NIS Server is not configured")
|
||||
|
||||
# container does not exist, bug #5507 is not effective
|
||||
sysupgrade.set_upgrade_state(
|
||||
'nis', 'done_recover_from_missing_maps', True)
|
||||
else:
|
||||
self.__recover_from_missing_maps(ldap)
|
||||
|
||||
self.log.debug("Executing NIS Server update")
|
||||
ld = LDAPUpdate(sub_dict={}, ldapi=True)
|
||||
ld.update([paths.NIS_UPDATE_ULDIF])
|
||||
|
||||
return False, ()
|
||||
@@ -17,26 +17,29 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipaserver.install.plugins import MIDDLE
|
||||
from ipaserver.install.plugins.baseupdate import PostUpdate
|
||||
from ipalib import api, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
|
||||
|
||||
class update_pacs(Updater):
|
||||
class update_pacs(PostUpdate):
|
||||
"""
|
||||
Includes default nfs:None only if no nfs: PAC present in ipakrbauthzdata.
|
||||
"""
|
||||
|
||||
order = MIDDLE
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
ldap = self.obj.backend
|
||||
|
||||
try:
|
||||
dn = DN('cn=ipaConfig', 'cn=etc', self.api.env.basedn)
|
||||
dn = DN('cn=ipaConfig', 'cn=etc', api.env.basedn)
|
||||
entry = ldap.get_entry(dn, ['ipakrbauthzdata'])
|
||||
pacs = entry.get('ipakrbauthzdata', [])
|
||||
except errors.NotFound:
|
||||
self.log.warning('Error retrieving: %s' % str(dn))
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
nfs_pac_set = any(pac.startswith('nfs:') for pac in pacs)
|
||||
|
||||
@@ -49,6 +52,6 @@ class update_pacs(Updater):
|
||||
else:
|
||||
self.log.debug('PAC for nfs is already set, not adding nfs:NONE.')
|
||||
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
|
||||
api.register(update_pacs)
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from ipalib import api, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
from ipaserver.install import sysupgrade
|
||||
|
||||
class update_passync_privilege_check(Updater):
|
||||
|
||||
def execute(self, **options):
|
||||
update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
|
||||
if update_done:
|
||||
root_logger.debug("PassSync privilege update pre-check not needed")
|
||||
return False, []
|
||||
|
||||
root_logger.debug("Check if there is existing PassSync privilege")
|
||||
|
||||
passsync_privilege_dn = DN(('cn','PassSync Service'),
|
||||
self.api.env.container_privilege,
|
||||
self.api.env.basedn)
|
||||
|
||||
ldap = self.api.Backend.ldap2
|
||||
try:
|
||||
ldap.get_entry(passsync_privilege_dn, [''])
|
||||
except errors.NotFound:
|
||||
root_logger.debug("PassSync privilege not found, this is a new update")
|
||||
sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', False)
|
||||
else:
|
||||
root_logger.debug("PassSync privilege found, skip updating PassSync")
|
||||
sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
|
||||
|
||||
return False, []
|
||||
|
||||
api.register(update_passync_privilege_check)
|
||||
|
||||
class update_passync_privilege_update(Updater):
|
||||
"""
|
||||
Add PassSync user as a member of PassSync privilege, if it exists
|
||||
"""
|
||||
|
||||
def execute(self, **options):
|
||||
update_done = sysupgrade.get_upgrade_state('winsync', 'passsync_privilege_updated')
|
||||
if update_done:
|
||||
root_logger.debug("PassSync privilege update not needed")
|
||||
return False, []
|
||||
|
||||
root_logger.debug("Add PassSync user as a member of PassSync privilege")
|
||||
ldap = self.api.Backend.ldap2
|
||||
passsync_dn = DN(('uid','passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'),
|
||||
self.api.env.basedn)
|
||||
passsync_privilege_dn = DN(('cn','PassSync Service'),
|
||||
self.api.env.container_privilege,
|
||||
self.api.env.basedn)
|
||||
|
||||
try:
|
||||
entry = ldap.get_entry(passsync_dn, [''])
|
||||
except errors.NotFound:
|
||||
root_logger.debug("PassSync user not found, no update needed")
|
||||
sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
|
||||
return False, []
|
||||
else:
|
||||
root_logger.debug("PassSync user found, do update")
|
||||
|
||||
update = {'dn': passsync_privilege_dn,
|
||||
'updates': [
|
||||
dict(action='add', attr='member', value=passsync_dn),
|
||||
]
|
||||
}
|
||||
|
||||
sysupgrade.set_upgrade_state('winsync', 'passsync_privilege_updated', True)
|
||||
return False, [update]
|
||||
|
||||
api.register(update_passync_privilege_update)
|
||||
@@ -1,87 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2014 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
|
||||
from ipalib import api, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipa_log_manager import root_logger
|
||||
|
||||
class update_referint(Updater):
|
||||
"""
|
||||
Update referential integrity configuration to new style
|
||||
http://directory.fedoraproject.org/docs/389ds/design/ri-plugin-configuration.html
|
||||
|
||||
old attr -> new attr
|
||||
nsslapd-pluginArg0 -> referint-update-delay
|
||||
nsslapd-pluginArg1 -> referint-logfile
|
||||
nsslapd-pluginArg2 -> referint-logchanges
|
||||
nsslapd-pluginArg3..N -> referint-membership-attr [3..N]
|
||||
|
||||
Old and new style cannot be mixed, all nslapd-pluginArg* attrs have to be removed
|
||||
"""
|
||||
|
||||
referint_dn = DN(('cn', 'referential integrity postoperation'),
|
||||
('cn', 'plugins'), ('cn', 'config'))
|
||||
|
||||
def execute(self, **options):
|
||||
|
||||
root_logger.debug("Upgrading referential integrity plugin configuration")
|
||||
ldap = self.api.Backend.ldap2
|
||||
try:
|
||||
entry = ldap.get_entry(self.referint_dn)
|
||||
except errors.NotFound:
|
||||
root_logger.error("Referential integrity configuration not found")
|
||||
return False, []
|
||||
|
||||
referint_membership_attrs = []
|
||||
|
||||
root_logger.debug("Initial value: %s", repr(entry))
|
||||
|
||||
# nsslapd-pluginArg0 -> referint-update-delay
|
||||
update_delay = entry.get('nsslapd-pluginArg0')
|
||||
if update_delay:
|
||||
root_logger.debug("add: referint-update-delay: %s", update_delay)
|
||||
entry['referint-update-delay'] = update_delay
|
||||
entry['nsslapd-pluginArg0'] = None
|
||||
else:
|
||||
root_logger.debug("Plugin already uses new style, skipping")
|
||||
return False, []
|
||||
|
||||
# nsslapd-pluginArg1 -> referint-logfile
|
||||
logfile = entry.get('nsslapd-pluginArg1')
|
||||
if logfile:
|
||||
root_logger.debug("add: referint-logfile: %s", logfile)
|
||||
entry['referint-logfile'] = logfile
|
||||
entry['nsslapd-pluginArg1'] = None
|
||||
|
||||
# nsslapd-pluginArg2 -> referint-logchanges
|
||||
logchanges = entry.get('nsslapd-pluginArg2')
|
||||
if logchanges:
|
||||
root_logger.debug("add: referint-logchanges: %s", logchanges)
|
||||
entry['referint-logchanges'] = logchanges
|
||||
entry['nsslapd-pluginArg2'] = None
|
||||
|
||||
# nsslapd-pluginArg3..N -> referint-membership-attr [3..N]
|
||||
for key in list(entry):
|
||||
if key.lower().startswith('nsslapd-pluginarg'):
|
||||
arg_val = entry.single_value[key]
|
||||
if arg_val:
|
||||
referint_membership_attrs.append(arg_val)
|
||||
entry[key] = None
|
||||
|
||||
if referint_membership_attrs:
|
||||
# entry['referint-membership-attr'] is None, plugin doesn't allow
|
||||
# mixing old and new style
|
||||
entry['referint-membership-attr'] = referint_membership_attrs
|
||||
|
||||
root_logger.debug("Final value: %s", repr(entry))
|
||||
try:
|
||||
ldap.update_entry(entry)
|
||||
except errors.EmptyModlist:
|
||||
root_logger.debug("No modifications required")
|
||||
return False, []
|
||||
|
||||
return False, []
|
||||
|
||||
api.register(update_referint)
|
||||
@@ -17,23 +17,25 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipaserver.install.plugins import MIDDLE
|
||||
from ipaserver.install.plugins.baseupdate import PostUpdate
|
||||
from ipalib import api, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipa_log_manager import *
|
||||
|
||||
|
||||
class update_service_principalalias(Updater):
|
||||
class update_service_principalalias(PostUpdate):
|
||||
"""
|
||||
Update all services which do not have ipakrbprincipalalias attribute
|
||||
used for case-insensitive principal searches filled. This applies for
|
||||
all services created prior IPA 3.0.
|
||||
"""
|
||||
order = MIDDLE
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
ldap = self.obj.backend
|
||||
|
||||
base_dn = DN(self.api.env.container_service, self.api.env.basedn)
|
||||
base_dn = DN(api.env.container_service, api.env.basedn)
|
||||
search_filter = ("(&(objectclass=krbprincipal)(objectclass=ipaservice)"
|
||||
"(!(objectclass=ipakrbprincipal)))")
|
||||
root_logger.debug("update_service_principalalias: search for affected "
|
||||
@@ -49,16 +51,16 @@ class update_service_principalalias(Updater):
|
||||
except errors.NotFound:
|
||||
root_logger.debug("update_service_principalalias: no service "
|
||||
"to update found")
|
||||
return False, []
|
||||
except errors.ExecutionError as e:
|
||||
return (False, False, [])
|
||||
except errors.ExecutionError, e:
|
||||
root_logger.error("update_service_principalalias: cannot "
|
||||
"retrieve list of affected services: %s", e)
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
if not entries:
|
||||
# no entry was returned, rather break than continue cycling
|
||||
root_logger.debug("update_service_principalalias: no service "
|
||||
"was returned")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
root_logger.debug("update_service_principalalias: found %d "
|
||||
"services to update, truncated: %s",
|
||||
len(entries), truncated)
|
||||
@@ -72,7 +74,7 @@ class update_service_principalalias(Updater):
|
||||
ldap.update_entry(entry)
|
||||
except (errors.EmptyModlist, errors.NotFound):
|
||||
pass
|
||||
except errors.ExecutionError as e:
|
||||
except errors.ExecutionError, e:
|
||||
root_logger.debug("update_service_principalalias: cannot "
|
||||
"update service: %s", e)
|
||||
error = True
|
||||
@@ -81,12 +83,12 @@ class update_service_principalalias(Updater):
|
||||
# exit loop to avoid infinite cycles
|
||||
root_logger.error("update_service_principalalias: error(s)"
|
||||
"detected during service update")
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
elif not truncated:
|
||||
# all affected entries updated, exit the loop
|
||||
root_logger.debug("update_service_principalalias: all affected"
|
||||
" services updated")
|
||||
return False, []
|
||||
return False, []
|
||||
return (False, False, [])
|
||||
return (False, False, [])
|
||||
|
||||
api.register(update_service_principalalias)
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
# Authors:
|
||||
# Alexander Bokovoy <abokovoy@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2014 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipalib import api, errors
|
||||
from ipalib import Updater
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipa_log_manager import *
|
||||
|
||||
|
||||
class update_uniqueness_plugins_to_new_syntax(Updater):
|
||||
"""
|
||||
Migrate uniqueness plugins to new style syntax
|
||||
|
||||
* OLD: *
|
||||
nsslapd-pluginarg0: uid
|
||||
nsslapd-pluginarg1: dc=people,dc=example,dc=com
|
||||
nsslapd-pluginarg2: dc=sales, dc=example,dc=com
|
||||
|
||||
or
|
||||
|
||||
nsslapd-pluginarg0: attribute=uid
|
||||
nsslapd-pluginarg1: markerobjectclass=organizationalUnit
|
||||
nsslapd-pluginarg2: requiredobjectclass=person
|
||||
|
||||
* NEW: *
|
||||
uniqueness-attribute-name: uid
|
||||
uniqueness-subtrees: dc=people,dc=example,dc=com
|
||||
uniqueness-subtrees: dc=sales, dc=example,dc=com
|
||||
uniqueness-across-all-subtrees: on
|
||||
|
||||
or
|
||||
|
||||
uniqueness-attribute-name: uid
|
||||
uniqueness-top-entry-oc: organizationalUnit
|
||||
uniqueness-subtree-entries-oc: person
|
||||
"""
|
||||
|
||||
plugins_dn = DN(('cn', 'plugins'), ('cn', 'config'))
|
||||
|
||||
def __remove_update(self, update, key, value):
|
||||
statement = dict(action='remove', attr=key, value=value)
|
||||
update.setdefault('updates', []).append(statement)
|
||||
|
||||
def __add_update(self, update, key, value):
|
||||
statement = dict(action='add', attr=key, value=value)
|
||||
update.setdefault('updates', []).append(statement)
|
||||
|
||||
def __subtree_style(self, entry):
|
||||
"""
|
||||
old attr -> new attr
|
||||
nsslapd-pluginArg0 -> uniqueness-attribute-name
|
||||
nsslapd-pluginArg1..N -> uniqueness-subtrees[1..N]
|
||||
"""
|
||||
update = {
|
||||
'dn': entry.dn,
|
||||
'updates': [],
|
||||
}
|
||||
|
||||
# nsslapd-pluginArg0 -> referint-update-delay
|
||||
attribute = entry.single_value['nsslapd-pluginArg0']
|
||||
if not attribute:
|
||||
raise ValueError("'nsslapd-pluginArg0' not found")
|
||||
self.__remove_update(update, 'nsslapd-pluginArg0', attribute)
|
||||
self.__add_update(update, 'uniqueness-attribute-name', attribute)
|
||||
entry['nsslapd-pluginArg0'] = None
|
||||
|
||||
# nsslapd-pluginArg1..N -> uniqueness-subtrees[1..N]
|
||||
for key in entry.keys():
|
||||
if key.lower().startswith('nsslapd-pluginarg'):
|
||||
subtree_dn = entry.single_value[key]
|
||||
if subtree_dn:
|
||||
self.__remove_update(update, key, subtree_dn)
|
||||
self.__add_update(update, 'uniqueness-subtrees', subtree_dn)
|
||||
|
||||
return update
|
||||
|
||||
def __objectclass_style(self, entry):
|
||||
"""
|
||||
old attr -> new attr
|
||||
nsslapd-pluginArg?[attribute] -> uniqueness-attribute-name
|
||||
nsslapd-pluginArg?[markerobjectclass] -> uniqueness-top-entry-oc
|
||||
nsslapd-pluginArg?[requiredobjectclass](optional)
|
||||
-> uniqueness-subtree-entries-oc
|
||||
nsslapd-pluginArg?[others] -> ERROR: unexpected args
|
||||
|
||||
Single value attributes.
|
||||
"""
|
||||
|
||||
update = {
|
||||
'dn': entry.dn,
|
||||
'updates': [],
|
||||
}
|
||||
|
||||
attribute = None
|
||||
markerobjectclass = None
|
||||
requiredobjectclass = None
|
||||
|
||||
for key in entry.keys():
|
||||
if key.lower().startswith('nsslapd-pluginarg'):
|
||||
try:
|
||||
# split argument name and value
|
||||
value = entry.single_value[key]
|
||||
arg_name, arg_val = value.split('=', 1)
|
||||
except ValueError:
|
||||
# unable to split
|
||||
raise ValueError("unexpected argument %s: %s" %
|
||||
(key, value))
|
||||
arg_name = arg_name.lower()
|
||||
if arg_name == 'attribute':
|
||||
if attribute:
|
||||
raise ValueError("single value argument 'attribute' "
|
||||
"is specified mutliple times")
|
||||
attribute = arg_val
|
||||
self.__remove_update(update, key, value)
|
||||
elif arg_name == 'markerobjectclass':
|
||||
if markerobjectclass:
|
||||
raise ValueError("single value argument "
|
||||
"'markerobjectclass' "
|
||||
"is specified mutliple times")
|
||||
markerobjectclass = arg_val
|
||||
self.__remove_update(update, key, value)
|
||||
elif arg_name == 'requiredobjectclass':
|
||||
if requiredobjectclass:
|
||||
raise ValueError("single value argument "
|
||||
"'requiredobjectclass' "
|
||||
"is specified mutliple times")
|
||||
requiredobjectclass = arg_val
|
||||
self.__remove_update(update, key, value)
|
||||
else:
|
||||
raise ValueError("unexpected argument '%s: %s'" %
|
||||
(key, value))
|
||||
|
||||
if not attribute:
|
||||
raise ValueError("missing required argument 'attribute'")
|
||||
if not markerobjectclass:
|
||||
raise ValueError("missing required argument 'markerobjectclass'")
|
||||
|
||||
self.__add_update(update, 'uniqueness-attribute-name', attribute)
|
||||
self.__add_update(update, 'uniqueness-top-entry-oc', markerobjectclass)
|
||||
|
||||
if requiredobjectclass:
|
||||
# optional argument
|
||||
self.__add_update(update, 'uniqueness-subtree-entries-oc',
|
||||
requiredobjectclass)
|
||||
|
||||
return update
|
||||
|
||||
def execute(self, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
|
||||
old_style_plugin_search_filter = (
|
||||
"(&"
|
||||
"(objectclass=nsSlapdPlugin)"
|
||||
"(nsslapd-pluginId=NSUniqueAttr)"
|
||||
"(nsslapd-pluginPath=libattr-unique-plugin)"
|
||||
"(nsslapd-pluginarg0=*)" # only entries with old configuration
|
||||
")"
|
||||
)
|
||||
|
||||
try:
|
||||
entries, truncated = ldap.find_entries(
|
||||
filter=old_style_plugin_search_filter,
|
||||
base_dn=self.plugins_dn,
|
||||
)
|
||||
except errors.NotFound:
|
||||
root_logger.debug("No uniqueness plugin entries with old style "
|
||||
"configuration found")
|
||||
return False, []
|
||||
|
||||
update_list = []
|
||||
new_attributes = [
|
||||
'uniqueness-subtree-entries-oc',
|
||||
'uniqueness-top-entry-oc',
|
||||
'uniqueness-attribute-name',
|
||||
'uniqueness-subtrees',
|
||||
'uniqueness-across-all-subtrees',
|
||||
]
|
||||
|
||||
for entry in entries:
|
||||
# test for mixed configuration
|
||||
if any(attr in entry for attr in new_attributes):
|
||||
root_logger.critical("Mixed old and new style configuration "
|
||||
"for plugin %s. Plugin will not work. "
|
||||
"Skipping plugin migration, please fix it "
|
||||
"manually",
|
||||
entry.dn)
|
||||
continue
|
||||
root_logger.debug("Configuration of plugin %s will be migrated "
|
||||
"to new style", entry.dn)
|
||||
try:
|
||||
# detect which configuration was used
|
||||
arg0 = entry.get('nsslapd-pluginarg0')
|
||||
if '=' in arg0:
|
||||
update = self.__objectclass_style(entry)
|
||||
else:
|
||||
update = self.__subtree_style(entry)
|
||||
except ValueError as e:
|
||||
root_logger.error("Unable to migrate configuration of "
|
||||
"plugin %s (%s)",
|
||||
entry.dn, e)
|
||||
|
||||
update_list.append(update)
|
||||
|
||||
return False, update_list
|
||||
|
||||
api.register(update_uniqueness_plugins_to_new_syntax)
|
||||
160
ipaserver/install/plugins/updateclient.py
Normal file
160
ipaserver/install/plugins/updateclient.py
Normal file
@@ -0,0 +1,160 @@
|
||||
# Authors: Rob Crittenden <rcritten@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2011 Red Hat
|
||||
# see file 'COPYING' for use and warranty information
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from ipaserver.install.plugins.baseupdate import DSRestart
|
||||
from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
from ipapython.ipautil import wait_for_open_socket
|
||||
from ipalib import api
|
||||
from ipalib import backend
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
|
||||
class updateclient(backend.Executioner):
|
||||
"""
|
||||
Backend used for applying LDAP updates via plugins
|
||||
|
||||
An update plugin can be executed before the file-based plugins or
|
||||
afterward. Each plugin returns three values:
|
||||
|
||||
1. restart: dirsrv needs to be restarted BEFORE this update is
|
||||
applied.
|
||||
2. apply_now: when True the update is applied when the plugin
|
||||
returns. Otherwise the update is cached until all
|
||||
plugins of that update type are complete, then they
|
||||
are applied together.
|
||||
3. updates: A dictionary of updates to be applied.
|
||||
|
||||
updates is a dictionary keyed on dn. The value of an update is a
|
||||
dictionary with the following possible values:
|
||||
- dn: DN, equal to the dn attribute
|
||||
- updates: list of updates against the dn
|
||||
- default: list of the default entry to be added if it doesn't
|
||||
exist
|
||||
- deleteentry: list of dn's to be deleted (typically single dn)
|
||||
|
||||
For example, this update file:
|
||||
|
||||
dn: cn=global_policy,cn=$REALM,cn=kerberos,$SUFFIX
|
||||
replace:krbPwdLockoutDuration:10::600
|
||||
replace: krbPwdMaxFailure:3::6
|
||||
|
||||
Generates this update dictionary:
|
||||
|
||||
dict('cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com':
|
||||
dict(
|
||||
'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
|
||||
'updates': ['replace:krbPwdLockoutDuration:10::600',
|
||||
'replace:krbPwdMaxFailure:3::6']
|
||||
)
|
||||
)
|
||||
|
||||
Here is another example showing how a default entry is configured:
|
||||
|
||||
dn: cn=Managed Entries,cn=etc,$SUFFIX
|
||||
default: objectClass: nsContainer
|
||||
default: objectClass: top
|
||||
default: cn: Managed Entries
|
||||
|
||||
This generates:
|
||||
|
||||
dict('cn=Managed Entries,cn=etc,dc=example,dc=com',
|
||||
dict(
|
||||
'dn': 'cn=Managed Entries,cn=etc,dc=example,dc=com',
|
||||
'default': ['objectClass:nsContainer',
|
||||
'objectClass:top',
|
||||
'cn:Managed Entries'
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
Note that the variable substitution in both examples has been completed.
|
||||
|
||||
A PRE_UPDATE plugin is executed before file-based updates.
|
||||
|
||||
A POST_UPDATE plugin is executed after file-based updates.
|
||||
|
||||
Plugins are executed automatically when ipa-ldap-updater is run
|
||||
in upgrade mode (--upgrade). They are not executed normally otherwise.
|
||||
To execute plugins as well use the --plugins flag.
|
||||
|
||||
Either may make changes directly in LDAP or can return updates in
|
||||
update format.
|
||||
"""
|
||||
def create_context(self, dm_password):
|
||||
if dm_password:
|
||||
autobind = False
|
||||
else:
|
||||
autobind = True
|
||||
self.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=dm_password, autobind=autobind)
|
||||
|
||||
def order(self, updatetype):
|
||||
"""Return plugins of the given updatetype in sorted order.
|
||||
"""
|
||||
ordered = [plugin for plugin in api.Updater()
|
||||
if plugin.updatetype == updatetype]
|
||||
ordered.sort(key=lambda p: p.order)
|
||||
return ordered
|
||||
|
||||
def update(self, updatetype, dm_password, ldapi, live_run):
|
||||
"""
|
||||
Execute all update plugins of type updatetype.
|
||||
"""
|
||||
self.create_context(dm_password)
|
||||
kw = dict(live_run=live_run)
|
||||
result = []
|
||||
ld = LDAPUpdate(dm_password=dm_password, sub_dict={}, live_run=live_run, ldapi=ldapi)
|
||||
for update in self.order(updatetype):
|
||||
(restart, apply_now, res) = self.run(update.name, **kw)
|
||||
if restart:
|
||||
# connection has to be closed before restart, otherwise
|
||||
# ld instance will try to reuse old non-valid connection
|
||||
ld.close_connection()
|
||||
self.restart(dm_password, live_run)
|
||||
|
||||
if apply_now:
|
||||
updates = {}
|
||||
for entry in res:
|
||||
updates.update(entry)
|
||||
ld.update_from_dict(updates)
|
||||
elif res:
|
||||
result.extend(res)
|
||||
|
||||
self.destroy_context()
|
||||
|
||||
return result
|
||||
|
||||
def run(self, method, **kw):
|
||||
"""
|
||||
Execute the update plugin.
|
||||
"""
|
||||
return self.Updater[method](**kw)
|
||||
|
||||
def restart(self, dm_password, live_run):
|
||||
dsrestart = DSRestart()
|
||||
socket_name = paths.SLAPD_INSTANCE_SOCKET_TEMPLATE % \
|
||||
api.env.realm.replace('.','-')
|
||||
if live_run:
|
||||
self.destroy_context()
|
||||
dsrestart.create_instance()
|
||||
wait_for_open_socket(socket_name)
|
||||
self.create_context(dm_password)
|
||||
else:
|
||||
self.log.warn("Test mode, skipping restart")
|
||||
|
||||
api.register(updateclient)
|
||||
@@ -17,80 +17,39 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from ipaserver.install.plugins import MIDDLE
|
||||
from ipaserver.install.plugins.baseupdate import PostUpdate
|
||||
from ipaserver.install.dsinstance import realm_to_serverid, config_dirname
|
||||
from ipaserver.install import certs
|
||||
from ipalib import api, errors, certstore
|
||||
from ipalib import Updater
|
||||
from ipapython import certdb
|
||||
from ipalib import api
|
||||
from ipapython.dn import DN
|
||||
import base64
|
||||
|
||||
class update_upload_cacrt(Updater):
|
||||
class update_upload_cacrt(PostUpdate):
|
||||
"""
|
||||
Upload public CA certificate to LDAP
|
||||
"""
|
||||
order=MIDDLE
|
||||
|
||||
def execute(self, **options):
|
||||
db = certs.CertDB(self.api.env.realm)
|
||||
ca_cert = None
|
||||
ldap = self.obj.backend
|
||||
ipa_config = ldap.get_ipa_config()
|
||||
subject_base = ipa_config.get('ipacertificatesubjectbase', [None])[0]
|
||||
dirname = config_dirname(realm_to_serverid(api.env.realm))
|
||||
certdb = certs.CertDB(api.env.realm, nssdir=dirname, subject_base=subject_base)
|
||||
|
||||
ca_enabled = self.api.Command.ca_is_enabled()['result']
|
||||
if ca_enabled:
|
||||
ca_nickname = certdb.get_ca_nickname(self.api.env.realm)
|
||||
else:
|
||||
ca_nickname = None
|
||||
server_certs = db.find_server_certs()
|
||||
if server_certs:
|
||||
ca_chain = db.find_root_cert(server_certs[0][0])[:-1]
|
||||
if ca_chain:
|
||||
ca_nickname = ca_chain[-1]
|
||||
dercert = certdb.get_cert_from_db(certdb.cacert_name, pem=False)
|
||||
|
||||
ldap = self.api.Backend.ldap2
|
||||
updates = {}
|
||||
dn = DN(('cn', 'CACert'), ('cn', 'ipa'), ('cn','etc'), api.env.basedn)
|
||||
|
||||
for nickname, trust_flags in db.list_certs():
|
||||
if 'u' in trust_flags:
|
||||
continue
|
||||
if nickname == ca_nickname and ca_enabled:
|
||||
trust_flags = 'CT,C,C'
|
||||
cert = db.get_cert_from_db(nickname, pem=False)
|
||||
trust, ca, eku = certstore.trust_flags_to_key_policy(trust_flags)
|
||||
cacrt_entry = ['objectclass:nsContainer',
|
||||
'objectclass:pkiCA',
|
||||
'cn:CAcert',
|
||||
'cACertificate;binary:%s' % dercert,
|
||||
]
|
||||
updates[dn] = {'dn': dn, 'default': cacrt_entry}
|
||||
|
||||
dn = DN(('cn', nickname), ('cn', 'certificates'), ('cn', 'ipa'),
|
||||
('cn','etc'), self.api.env.basedn)
|
||||
entry = ldap.make_entry(dn)
|
||||
|
||||
try:
|
||||
certstore.init_ca_entry(entry, cert, nickname, trust, eku)
|
||||
except Exception as e:
|
||||
self.log.warning("Failed to create entry for %s: %s",
|
||||
nickname, e)
|
||||
continue
|
||||
if nickname == ca_nickname:
|
||||
ca_cert = cert
|
||||
config = entry.setdefault('ipaConfigString', [])
|
||||
if ca_enabled:
|
||||
config.append('ipaCa')
|
||||
config.append('ipaCa')
|
||||
|
||||
try:
|
||||
ldap.add_entry(entry)
|
||||
except errors.DuplicateEntry:
|
||||
pass
|
||||
|
||||
if ca_cert:
|
||||
dn = DN(('cn', 'CACert'), ('cn', 'ipa'), ('cn','etc'),
|
||||
self.api.env.basedn)
|
||||
try:
|
||||
entry = ldap.get_entry(dn)
|
||||
except errors.NotFound:
|
||||
entry = ldap.make_entry(dn)
|
||||
entry['objectclass'] = ['nsContainer', 'pkiCA']
|
||||
entry.single_value['cn'] = 'CAcert'
|
||||
entry.single_value['cACertificate;binary'] = ca_cert
|
||||
ldap.add_entry(entry)
|
||||
else:
|
||||
if b'' in entry['cACertificate;binary']:
|
||||
entry.single_value['cACertificate;binary'] = ca_cert
|
||||
ldap.update_entry(entry)
|
||||
|
||||
return False, []
|
||||
return (False, True, [updates])
|
||||
|
||||
api.register(update_upload_cacrt)
|
||||
|
||||
Reference in New Issue
Block a user