mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Add DS migration plugin and password migration page.
This commit is contained in:
parent
41a7a8d3d4
commit
c15c1eee72
@ -7,6 +7,7 @@ NULL =
|
||||
SUBDIRS = \
|
||||
conf \
|
||||
html \
|
||||
migration \
|
||||
share \
|
||||
tools \
|
||||
updates \
|
||||
|
@ -100,6 +100,18 @@ Alias /ipa-assets/ "/var/cache/ipa/assets/"
|
||||
ErrorDocument 401 /ipa/errors/unauthorized.html
|
||||
</Directory>
|
||||
|
||||
# migration related pages
|
||||
Alias /ipa/migration "/usr/share/ipa/migration"
|
||||
|
||||
<Directory "/usr/share/ipa/migration">
|
||||
AllowOverride None
|
||||
Satisfy Any
|
||||
Allow from all
|
||||
|
||||
AddHandler mod_python .py
|
||||
PythonHandler mod_python.publisher
|
||||
</Directory>
|
||||
|
||||
#Alias /ipatest "/usr/share/ipa/ipatest"
|
||||
|
||||
#<Directory "/usr/share/ipa/ipatest">
|
||||
|
@ -34,6 +34,7 @@ AC_CONFIG_FILES([
|
||||
Makefile
|
||||
conf/Makefile
|
||||
html/Makefile
|
||||
migration/Makefile
|
||||
share/Makefile
|
||||
tools/Makefile
|
||||
tools/man/Makefile
|
||||
|
18
install/migration/Makefile.am
Normal file
18
install/migration/Makefile.am
Normal file
@ -0,0 +1,18 @@
|
||||
NULL =
|
||||
|
||||
appdir = $(IPA_DATA_DIR)/migration
|
||||
app_DATA = \
|
||||
error.html \
|
||||
index.html \
|
||||
invalid.html \
|
||||
migration.css \
|
||||
migration.py \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(app_DATA) \
|
||||
$(NULL)
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
*~ \
|
||||
Makefile.in
|
21
install/migration/error.html
Normal file
21
install/migration/error.html
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link rel="stylesheet" href="migration.css" type="text/css">
|
||||
<title>IPA Password Migration Page: Error</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
There was a problem with your request. Please, try again later.
|
||||
</p>
|
||||
<p>
|
||||
If the problem persists, contact your administrator.
|
||||
</p>
|
||||
</body>
|
||||
|
47
install/migration/index.html
Normal file
47
install/migration/index.html
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link rel="stylesheet" href="migration.css" type="text/css">
|
||||
<title>IPA Password Migration Page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
If you have been sent here by your administrator, your personal
|
||||
information is being migrated to a new Identity management solution (IPA).
|
||||
</p>
|
||||
<p>
|
||||
Please, enter your credentials in the form below to complete the process.
|
||||
</p>
|
||||
<p>
|
||||
Upon successful login your Kerberos account will be activated.
|
||||
</p>
|
||||
<div class="migration_form">
|
||||
<div class="migration_form_inner">
|
||||
<form action="migration.py/bind" method="post">
|
||||
<div class="migration_form_title">
|
||||
<span>Password Migration</span>
|
||||
</div>
|
||||
<div class="migration_form_input">
|
||||
<label><em>U</em>sername:</label>
|
||||
<input name="username" value="" type="text" accesskey="u" />
|
||||
</div>
|
||||
<div class="migration_form_input">
|
||||
<label><em>P</em>assword:</label>
|
||||
<input name="password" value="" type="password" accesskey="p" />
|
||||
</div>
|
||||
<div class="migration_form_submit">
|
||||
<input name="submit" value="Migrate!" type="submit" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
21
install/migration/invalid.html
Normal file
21
install/migration/invalid.html
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link rel="stylesheet" href="migration.css" type="text/css">
|
||||
<title>IPA Password Migration Page: Invalid credentials</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
Invalid username or password.
|
||||
</p>
|
||||
<p>
|
||||
<a href="index.html">Let me try again!</a>
|
||||
</p>
|
||||
</body>
|
||||
|
69
install/migration/migration.css
Normal file
69
install/migration/migration.css
Normal file
@ -0,0 +1,69 @@
|
||||
/* migration page CSS; author: Pavel Zuna <pzuna@redhat.com> */
|
||||
|
||||
body
|
||||
{
|
||||
font-family: Verdana;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p
|
||||
{
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.migration_form
|
||||
{
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: center;
|
||||
width: 18em;
|
||||
}
|
||||
|
||||
.migration_form_inner
|
||||
{
|
||||
border: solid 1px #284775;
|
||||
font-size: 0.8em;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.migration_form_title
|
||||
{
|
||||
background: #5d7b9d;
|
||||
color: #f7f6f3;
|
||||
font-weight: bold;
|
||||
height: 1.7em;
|
||||
margin-bottom: 0.3em;
|
||||
padding-top: 0.4em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.migration_form_input
|
||||
{
|
||||
color: #5d7b9d;
|
||||
font-size: 1em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.migration_form_input em
|
||||
{
|
||||
font-style: normal;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.migration_form_submit
|
||||
{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.migration_form_submit input
|
||||
{
|
||||
background: #5d7b9d;
|
||||
border: solid 1px #284775;
|
||||
color: #f7f6f3;
|
||||
height: 1.7em;
|
||||
margin-top: 0.3em;
|
||||
}
|
||||
|
||||
/* end of file */
|
||||
|
67
install/migration/migration.py
Normal file
67
install/migration/migration.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Authors:
|
||||
# Pavel Zuna <pzuna@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2009 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; version 2 only
|
||||
#
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
Password migration script
|
||||
"""
|
||||
|
||||
import ldap
|
||||
from mod_python import apache, util
|
||||
|
||||
|
||||
BASE_DN = ''
|
||||
LDAP_URI = 'ldap://localhost:389'
|
||||
|
||||
|
||||
def get_base_dn():
|
||||
"""
|
||||
Retrieve LDAP server base DN.
|
||||
"""
|
||||
if BASE_DN:
|
||||
return BASE_DN
|
||||
try:
|
||||
conn = ldap.initialize(LDAP_URI)
|
||||
conn.simple_bind_s('', '')
|
||||
entries = conn.search_ext_s(
|
||||
'', scope=ldap.SCOPE_BASE, attrlist=['namingcontexts']
|
||||
)
|
||||
except ldap.LDAPError:
|
||||
return ''
|
||||
conn.unbind_s()
|
||||
try:
|
||||
return entries[0][1]['namingcontexts'][0]
|
||||
except (IndexError, KeyError):
|
||||
return ''
|
||||
|
||||
|
||||
def bind(req, username, password):
|
||||
base_dn = get_base_dn()
|
||||
if not base_dn:
|
||||
util.redirect(req, '/ipa/migration/error.html')
|
||||
bind_dn = 'uid=%s,cn=users,cn=accounts,%s' % (username, base_dn)
|
||||
try:
|
||||
conn = ldap.initialize(LDAP_URI)
|
||||
conn.simple_bind_s(bind_dn, password)
|
||||
except (ldap.INVALID_CREDENTIALS, ldap.UNWILLING_TO_PERFORM,
|
||||
ldap.NO_SUCH_OBJECT):
|
||||
util.redirect(req, '/ipa/migration/invalid.html')
|
||||
except ldap.LDAPError:
|
||||
util.redirect(req, '/ipa/migration/error.html')
|
||||
conn.unbind_s()
|
||||
util.redirect(req, '/ipa/ui')
|
||||
|
@ -382,6 +382,12 @@ fi
|
||||
%dir %{_usr}/share/ipa/html
|
||||
%{_usr}/share/ipa/html/ssbrowser.html
|
||||
%{_usr}/share/ipa/html/unauthorized.html
|
||||
%dir %{_usr}/share/ipa/migration
|
||||
%{_usr}/share/ipa/migration/error.html
|
||||
%{_usr}/share/ipa/migration/index.html
|
||||
%{_usr}/share/ipa/migration/invalid.html
|
||||
%{_usr}/share/ipa/migration/migration.css
|
||||
%{_usr}/share/ipa/migration/migration.py*
|
||||
%dir %{_sysconfdir}/ipa
|
||||
%dir %{_sysconfdir}/ipa/html
|
||||
%config(noreplace) %{_sysconfdir}/ipa/html/ssbrowser.html
|
||||
|
374
ipalib/plugins/migration.py
Normal file
374
ipalib/plugins/migration.py
Normal file
@ -0,0 +1,374 @@
|
||||
# Authors:
|
||||
# Pavel Zuna <pzuna@redhat.com>
|
||||
#
|
||||
# Copyright (C) 2009 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; version 2 only
|
||||
#
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""
|
||||
Migration to IPA
|
||||
|
||||
Example: Migrate users and groups from DS to IPA
|
||||
|
||||
ipa migrate-ds ldap://example.com:389
|
||||
"""
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
from ipalib import api, errors, output, uuid
|
||||
from ipalib import Command, List, Password, Str
|
||||
from ipalib.cli import to_cli
|
||||
from ipaserver.plugins.ldap2 import ldap2
|
||||
|
||||
|
||||
# USER MIGRATION CALLBACKS AND VARS
|
||||
|
||||
_krb_err_msg = 'Kerberos principal %s already exists. ' \
|
||||
'Use \'ipa user-mod\' to set it manually.'
|
||||
_grp_err_msg = 'Failed to add user to the default group. ' \
|
||||
'Use \'ipa group-add-member\' to add manually.'
|
||||
|
||||
|
||||
def _pre_migrate_user(ldap, pkey, dn, entry_attrs, failed, config, ctx):
|
||||
# get default primary group for new users
|
||||
if 'def_group_dn' not in ctx:
|
||||
def_group = config.get('ipadefaultprimarygroup')
|
||||
ctx['def_group_dn'] = api.Object.group.get_dn(def_group)
|
||||
try:
|
||||
(g_dn, g_attrs) = ldap.get_entry(ctx['def_group_dn'], ['gidnumber'])
|
||||
except errors.NotFound:
|
||||
error_msg = 'Default group for new users not found.'
|
||||
raise errors.NotFound(reason=error_msg)
|
||||
ctx['def_group_gid'] = g_attrs['gidnumber'][0]
|
||||
|
||||
# fill in required attributes by IPA
|
||||
entry_attrs['ipauniqueid'] = str(uuid.uuid1())
|
||||
if 'homedirectory' not in entry_attrs:
|
||||
homes_root = config.get('ipahomesrootdir', ('/home', ))[0]
|
||||
home_dir = '%s/%s' % (homes_root, pkey)
|
||||
home_dir = home_dir.replace('//', '/').rstrip('/')
|
||||
entry_attrs['homedirectory'] = home_dir
|
||||
entry_attrs.setdefault('gidnumber', ctx['def_group_gid'])
|
||||
|
||||
# generate a principal name and check if it isn't already taken
|
||||
principal = '%s@%s' % (pkey, api.env.realm)
|
||||
try:
|
||||
ldap.find_entry_by_attr(
|
||||
'krbprincipalname', principal, 'krbprincipalaux', ['']
|
||||
)
|
||||
except errors.NotFound:
|
||||
entry_attrs['krbprincipalname'] = principal
|
||||
else:
|
||||
failed[pkey] = _krb_err_msg % principal
|
||||
|
||||
return dn
|
||||
|
||||
|
||||
def _post_migrate_user(ldap, pkey, dn, entry_attrs, failed, config, ctx):
|
||||
# add user to the default group
|
||||
try:
|
||||
ldap.add_entry_to_group(dn, ctx['def_group_dn'])
|
||||
except errors.ExecutionError, e:
|
||||
failed[pkey] = _grp_err_msg
|
||||
|
||||
|
||||
# GROUP MIGRATION CALLBACKS AND VARS
|
||||
|
||||
def _pre_migrate_group(ldap, pkey, dn, entry_attrs, failed, config, ctx):
|
||||
def convert_members(member_attr, overwrite=False):
|
||||
"""
|
||||
Convert DNs in member attributes to work in IPA.
|
||||
"""
|
||||
new_members = []
|
||||
entry_attrs.setdefault(member_attr, [])
|
||||
for m in entry_attrs[member_attr]:
|
||||
col = m.find(',')
|
||||
if col == -1:
|
||||
continue
|
||||
if m.startswith('uid'):
|
||||
m = '%s,%s' % (m[0:col], api.env.container_user)
|
||||
elif m.startswith('cn'):
|
||||
m = '%s,%s' % (m[0:col], api.env.container_group)
|
||||
m = ldap.normalize_dn(m)
|
||||
new_members.append(m)
|
||||
del entry_attrs[member_attr]
|
||||
if overwrite:
|
||||
entry_attrs['member'] = []
|
||||
entry_attrs['member'] += new_members
|
||||
|
||||
entry_attrs['ipauniqueid'] = str(uuid.uuid1())
|
||||
convert_members('member', overwrite=True)
|
||||
convert_members('uniquemember')
|
||||
|
||||
return dn
|
||||
|
||||
|
||||
# DS MIGRATION PLUGIN
|
||||
|
||||
def validate_ldapuri(ugettext, ldapuri):
|
||||
m = re.match('^ldaps?://[-\w\.]+(:\d+)?$', ldapuri)
|
||||
if not m:
|
||||
err_msg = 'Invalid LDAP URI.'
|
||||
raise errors.ValidationError(name='ldap_uri', error=err_msg)
|
||||
|
||||
|
||||
class migrate_ds(Command):
|
||||
"""
|
||||
Migrate users and groups from DS to IPA.
|
||||
"""
|
||||
migrate_objects = {
|
||||
# OBJECT_NAME: (search_filter, pre_callback, post_callback)
|
||||
#
|
||||
# OBJECT_NAME - is the name of an LDAPObject subclass
|
||||
# search_filter - is the filter to retrieve objects from DS
|
||||
# pre_callback - is called for each object just after it was
|
||||
# retrieved from DS and before being added to IPA
|
||||
# post_callback - is called for each object after it was added to IPA
|
||||
#
|
||||
# {pre, post}_callback parameters:
|
||||
# ldap - ldap2 instance connected to IPA
|
||||
# pkey - primary key value of the object (uid for users, etc.)
|
||||
# dn - dn of the object as it (will be/is) stored in IPA
|
||||
# entry_attrs - attributes of the object
|
||||
# failed - a list of so-far failed objects
|
||||
# config - IPA config entry attributes
|
||||
# ctx - object context, used to pass data between callbacks
|
||||
#
|
||||
# If pre_callback return value evaluates to False, migration
|
||||
# of the current object is aborted.
|
||||
'user': (
|
||||
'(&(objectClass=person)(uid=*))',
|
||||
_pre_migrate_user, _post_migrate_user
|
||||
),
|
||||
'group': (
|
||||
'(&(objectClass=groupOfUniqueNames)(cn=*))',
|
||||
_pre_migrate_group, None
|
||||
),
|
||||
}
|
||||
migrate_order = ('user', 'group')
|
||||
|
||||
takes_args = (
|
||||
Str('ldapuri', validate_ldapuri,
|
||||
cli_name='ldap_uri',
|
||||
doc='LDAP URI of DS server to migrate from',
|
||||
),
|
||||
Password('bindpw',
|
||||
cli_name='password',
|
||||
doc='bind password',
|
||||
),
|
||||
)
|
||||
|
||||
takes_options = (
|
||||
Str('binddn?',
|
||||
cli_name='bind_dn',
|
||||
doc='bind DN',
|
||||
default=u'cn=directory manager',
|
||||
autofill=True,
|
||||
),
|
||||
Str('usercontainer?',
|
||||
cli_name='user_container',
|
||||
doc='RDN of container for users in DS',
|
||||
default=u'ou=people',
|
||||
autofill=True,
|
||||
),
|
||||
Str('groupcontainer?',
|
||||
cli_name='group_container',
|
||||
doc='RDN of container for groups in DS',
|
||||
default=u'ou=groups',
|
||||
autofill=True,
|
||||
),
|
||||
)
|
||||
|
||||
has_output = (
|
||||
output.Output('result',
|
||||
type=dict,
|
||||
doc='Lists of objects migrated; categorized by type.',
|
||||
),
|
||||
output.Output('failed',
|
||||
type=dict,
|
||||
doc='Lists of objects that could not be migrated; ' \
|
||||
'categorized by type.',
|
||||
),
|
||||
output.Output('enabled',
|
||||
type=bool,
|
||||
doc='False if migration mode was disabled.',
|
||||
),
|
||||
)
|
||||
|
||||
exclude_doc = 'comma-separated list of %s to exclude from migration'
|
||||
truncated_err_msg = 'search results for objects to be migrated ' \
|
||||
'have been truncated by the server; migration ' \
|
||||
'process might be uncomplete\n'
|
||||
migration_disabled_msg = 'Migration mode is disabled. ' \
|
||||
'Use \'ipa config-mod\' to enable it.'
|
||||
pwd_migration_msg = 'Passwords have been migrated in pre-hashed format. ' \
|
||||
'IPA is unable to generate Kerberos keys unless provided ' \
|
||||
'with clear text passwords. All migrated users need to ' \
|
||||
'login at https://your.domain/ipa/migration/ before they ' \
|
||||
'can use their Kerberos accounts.'
|
||||
|
||||
def get_options(self):
|
||||
"""
|
||||
Call get_options of the baseclass and add "exclude" options
|
||||
for each type of object being migrated.
|
||||
"""
|
||||
for option in super(migrate_ds, self).get_options():
|
||||
yield option
|
||||
for ldap_obj_name in self.migrate_objects:
|
||||
ldap_obj = self.api.Object[ldap_obj_name]
|
||||
name = 'exclude_%ss' % to_cli(ldap_obj_name)
|
||||
doc = self.exclude_doc % ldap_obj.object_name_plural
|
||||
yield List(
|
||||
'%s?' % name, cli_name=name, doc=doc, default=tuple(),
|
||||
autofill=True
|
||||
)
|
||||
|
||||
def normalize_options(self, options):
|
||||
"""
|
||||
Convert all "exclude" option values to lower-case.
|
||||
|
||||
Also, empty List parameters are converted to None, but the migration
|
||||
plugin doesn't like that - convert back to empty lists.
|
||||
"""
|
||||
for p in self.params():
|
||||
if isinstance(p, List):
|
||||
if options[p.name]:
|
||||
options[p.name] = tuple(
|
||||
v.lower() for v in options[p.name]
|
||||
)
|
||||
else:
|
||||
options[p.name] = tuple()
|
||||
|
||||
def migrate(self, ldap, config, ds_ldap, ds_base_dn, options):
|
||||
"""
|
||||
Migrate objects from DS to LDAP.
|
||||
"""
|
||||
migrated = {} # {'OBJ': ['PKEY1', 'PKEY2', ...], ...}
|
||||
failed = {} # {'OBJ': {'PKEY1': 'Failed 'cos blabla', ...}, ...}
|
||||
for ldap_obj_name in self.migrate_order:
|
||||
ldap_obj = self.api.Object[ldap_obj_name]
|
||||
|
||||
search_filter = self.migrate_objects[ldap_obj_name][0]
|
||||
search_base = '%s,%s' % (
|
||||
options['%scontainer' % to_cli(ldap_obj_name)], ds_base_dn
|
||||
)
|
||||
exclude = options['exclude_%ss' % to_cli(ldap_obj_name)]
|
||||
context = {}
|
||||
|
||||
migrated[ldap_obj_name] = []
|
||||
failed[ldap_obj_name] = {}
|
||||
|
||||
# FIXME: with limits set, we get a strange 'Success' exception
|
||||
(entries, truncated) = ds_ldap.find_entries(
|
||||
search_filter, ['*'], search_base, ds_ldap.SCOPE_ONELEVEL#,
|
||||
#time_limit=0, size_limit=0
|
||||
)
|
||||
if truncated:
|
||||
self.log.error(
|
||||
'%s: %s' % (
|
||||
ldap_obj.object_name_plural, self.truncated_err_msg
|
||||
)
|
||||
)
|
||||
|
||||
for (dn, entry_attrs) in entries:
|
||||
pkey = entry_attrs[ldap_obj.primary_key.name][0].lower()
|
||||
if pkey in exclude:
|
||||
continue
|
||||
|
||||
dn = ldap_obj.get_dn(pkey)
|
||||
entry_attrs['objectclass'] = list(
|
||||
set(
|
||||
config.get(
|
||||
ldap_obj.object_class_config, ldap_obj.object_class
|
||||
) + [o.lower() for o in entry_attrs['objectclass']]
|
||||
)
|
||||
)
|
||||
|
||||
callback = self.migrate_objects[ldap_obj_name][1]
|
||||
if callable(callback):
|
||||
dn = callback(
|
||||
ldap, pkey, dn, entry_attrs, failed[ldap_obj_name],
|
||||
config, context
|
||||
)
|
||||
if not dn:
|
||||
continue
|
||||
|
||||
try:
|
||||
ldap.add_entry(dn, entry_attrs)
|
||||
except errors.ExecutionError, e:
|
||||
failed[ldap_obj_name][pkey] = str(e)
|
||||
else:
|
||||
migrated[ldap_obj_name].append(pkey)
|
||||
|
||||
callback = self.migrate_objects[ldap_obj_name][2]
|
||||
if callable(callback):
|
||||
callback(
|
||||
ldap, pkey, dn, entry_attrs, failed[ldap_obj_name],
|
||||
config, context
|
||||
)
|
||||
|
||||
return (migrated, failed)
|
||||
|
||||
def execute(self, ldapuri, bindpw, **options):
|
||||
ldap = self.api.Backend.ldap2
|
||||
self.normalize_options(options)
|
||||
|
||||
config = ldap.get_ipa_config()[1]
|
||||
|
||||
# check if migration mode is enabled
|
||||
if config.get('ipamigrationenabled', ('FALSE', ))[0] == 'FALSE':
|
||||
return dict(result={}, failed={}, enabled=False)
|
||||
|
||||
# connect to DS
|
||||
ds_ldap = ldap2(shared_instance=False, ldap_uri=ldapuri, base_dn='')
|
||||
ds_ldap.connect(bind_dn=options['binddn'], bind_pw=bindpw)
|
||||
|
||||
# retrieve DS base DN
|
||||
(entries, truncated) = ds_ldap.find_entries(
|
||||
'', ['namingcontexts'], '', ds_ldap.SCOPE_BASE
|
||||
)
|
||||
try:
|
||||
ds_base_dn = entries[0][1]['namingcontexts'][0]
|
||||
except (IndexError, KeyError), e:
|
||||
raise StandardError(str(e))
|
||||
|
||||
# migrate!
|
||||
(migrated, failed) = self.migrate(
|
||||
ldap, config, ds_ldap, ds_base_dn, options
|
||||
)
|
||||
|
||||
return dict(result=migrated, failed=failed, enabled=True)
|
||||
|
||||
def output_for_cli(self, textui, result, ldapuri, bindpw, **options):
|
||||
textui.print_name(self.name)
|
||||
if not result['enabled']:
|
||||
textui.print_plain(self.migration_disabled_msg)
|
||||
return 1
|
||||
textui.print_plain('Migrated:')
|
||||
textui.print_entry1(
|
||||
result['result'], attr_order=self.migrate_order,
|
||||
one_value_per_line=False
|
||||
)
|
||||
for ldap_obj_name in self.migrate_order:
|
||||
textui.print_plain('Failed %s:' % ldap_obj_name)
|
||||
textui.print_entry1(
|
||||
result['failed'][ldap_obj_name], attr_order=self.migrate_order,
|
||||
one_value_per_line=True,
|
||||
)
|
||||
textui.print_plain('-' * len(self.name))
|
||||
textui.print_plain(self.pwd_migration_msg)
|
||||
|
||||
api.register(migrate_ds)
|
||||
|
Loading…
Reference in New Issue
Block a user