Imported Debian patch 4.0.5-6~numeezy

This commit is contained in:
Alexandre Ellert
2016-02-17 15:07:45 +01:00
committed by Mario Fetka
parent c44de33144
commit 10dfc9587b
1203 changed files with 53869 additions and 241462 deletions

View 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

View File

@@ -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

View File

@@ -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)

View 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__()

Binary file not shown.

View File

@@ -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])

View File

@@ -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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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, ()

View File

@@ -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, ()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View 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)

View File

@@ -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)