summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--API.txt3
-rw-r--r--VERSION2
-rw-r--r--install/updates/10-config.update6
-rw-r--r--ipa-client/ipa-join.c80
-rw-r--r--ipalib/plugins/migration.py41
-rw-r--r--ipapython/ipautil.py8
6 files changed, 99 insertions, 41 deletions
diff --git a/API.txt b/API.txt
index 548fc93d4..73d115c05 100644
--- a/API.txt
+++ b/API.txt
@@ -1893,7 +1893,7 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('value', <type 'unicode'>, None)
command: migrate_ds
-args: 2,14,3
+args: 2,15,3
arg: Str('ldapuri', cli_name='ldap_uri')
arg: Password('bindpw', cli_name='password', confirm=False)
option: Str('binddn?', autofill=True, cli_name='bind_dn', default=u'cn=directory manager')
@@ -1908,6 +1908,7 @@ option: Str('groupignoreattribute*', autofill=True, cli_name='group_ignore_attri
option: Flag('groupoverwritegid', autofill=True, cli_name='group_overwrite_gid', default=False)
option: StrEnum('schema?', autofill=True, cli_name='schema', default=u'RFC2307bis', values=(u'RFC2307bis', u'RFC2307'))
option: Flag('continue?', autofill=True, default=False)
+option: Str('basedn?', cli_name='base_dn')
option: Str('exclude_groups*', autofill=True, cli_name='exclude_groups', csv=True, default=())
option: Str('exclude_users*', autofill=True, cli_name='exclude_users', csv=True, default=())
output: Output('result', <type 'dict'>, None)
diff --git a/VERSION b/VERSION
index 172a4940e..2a6b13f5d 100644
--- a/VERSION
+++ b/VERSION
@@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=27
+IPA_API_VERSION_MINOR=28
diff --git a/install/updates/10-config.update b/install/updates/10-config.update
index fe7a4bd06..420e04880 100644
--- a/install/updates/10-config.update
+++ b/install/updates/10-config.update
@@ -32,3 +32,9 @@ default:nsLookThroughLimit: 5000
dn: cn=config
add:nsslapd-anonlimitsdn:cn=anonymous-limits,cn=etc,$SUFFIX
+
+# Add a defaultNamingContext if one hasn't already been set. This was
+# introduced in 389-ds-base-1.2.10-0.9.a8. Adding this to a server that
+# doesn't support it generates a non-fatal error.
+dn: cn=config
+add:nsslapd-defaultNamingContext:'$SUFFIX'
diff --git a/ipa-client/ipa-join.c b/ipa-client/ipa-join.c
index 57c7bcb28..bc46e9387 100644
--- a/ipa-client/ipa-join.c
+++ b/ipa-client/ipa-join.c
@@ -276,16 +276,52 @@ fail:
return NULL;
}
+/*
+ * Given a list of naming contexts check each one to see if it has
+ * an IPA v2 server in it. The first one we find wins.
+ */
+static int
+check_ipa_server(LDAP *ld, char **ldap_base, struct berval **vals)
+{
+ struct berval **infovals;
+ LDAPMessage *entry, *res = NULL;
+ char *info_attrs[] = {"info", NULL};
+ int i, ret = 0;
+
+ for (i = 0; !*ldap_base && vals[i]; i++) {
+ ret = ldap_search_ext_s(ld, vals[i]->bv_val,
+ LDAP_SCOPE_BASE, "(info=IPA*)", info_attrs,
+ 0, NULL, NULL, NULL, 0, &res);
+
+ if (ret != LDAP_SUCCESS) {
+ break;
+ }
+
+ entry = ldap_first_entry(ld, res);
+ infovals = ldap_get_values_len(ld, entry, info_attrs[0]);
+ if (!strcmp(infovals[0]->bv_val, "IPA V2.0"))
+ *ldap_base = strdup(vals[i]->bv_val);
+ ldap_msgfree(res);
+ res = NULL;
+ }
+
+ return ret;
+}
+
+/*
+ * Determine the baseDN of the remote server. Look first for a
+ * defaultNamingContext, otherwise fall back to reviewing each
+ * namingContext.
+ */
static int
get_root_dn(const char *ipaserver, char **ldap_base)
{
LDAP *ld = NULL;
- char *root_attrs[] = {"namingContexts", NULL};
- char *info_attrs[] = {"info", NULL};
+ char *root_attrs[] = {"namingContexts", "defaultNamingContext", NULL};
LDAPMessage *entry, *res = NULL;
struct berval **ncvals;
- struct berval **infovals;
- int i, ret, rval = 0;
+ struct berval **defvals;
+ int ret, rval = 0;
ld = connect_ldap(ipaserver, NULL, NULL);
if (!ld) {
@@ -306,33 +342,27 @@ get_root_dn(const char *ipaserver, char **ldap_base)
*ldap_base = NULL;
- /* loop through to find the IPA context */
entry = ldap_first_entry(ld, res);
- ncvals = ldap_get_values_len(ld, entry, root_attrs[0]);
- if (!ncvals) {
- fprintf(stderr, _("No values for %s"), root_attrs[0]);
- rval = 14;
- goto done;
+
+ defvals = ldap_get_values_len(ld, entry, root_attrs[1]);
+ if (defvals) {
+ ret = check_ipa_server(ld, ldap_base, defvals);
}
- for (i = 0; !*ldap_base && ncvals[i]; i++) {
- ret = ldap_search_ext_s(ld, ncvals[i]->bv_val,
- LDAP_SCOPE_BASE, "(info=IPA*)", info_attrs,
- 0, NULL, NULL, NULL, 0, &res);
+ ldap_value_free_len(defvals);
- if (ret != LDAP_SUCCESS) {
- break;
+ /* loop through to find the IPA context */
+ if (ret == LDAP_SUCCESS && !*ldap_base) {
+ ncvals = ldap_get_values_len(ld, entry, root_attrs[0]);
+ if (!ncvals) {
+ fprintf(stderr, _("No values for %s"), root_attrs[0]);
+ rval = 14;
+ ldap_value_free_len(ncvals);
+ goto done;
}
-
- entry = ldap_first_entry(ld, res);
- infovals = ldap_get_values_len(ld, entry, info_attrs[0]);
- if (!strcmp(infovals[0]->bv_val, "IPA V2.0"))
- *ldap_base = strdup(ncvals[i]->bv_val);
- ldap_msgfree(res);
- res = NULL;
+ ret = check_ipa_server(ld, ldap_base, ncvals);
+ ldap_value_free_len(ncvals);
}
- ldap_value_free_len(ncvals);
-
if (ret != LDAP_SUCCESS) {
fprintf(stderr, _("Search for IPA namingContext failed with error %d\n"), ret);
rval = 14;
diff --git a/ipalib/plugins/migration.py b/ipalib/plugins/migration.py
index 688265fd3..884ae4ed6 100644
--- a/ipalib/plugins/migration.py
+++ b/ipalib/plugins/migration.py
@@ -62,6 +62,10 @@ enable it:
ipa config-mod --enable-migration=TRUE
+If a base DN is not provided with --basedn then IPA will use either
+the value of defaultNamingContext if it is set or the first value
+in namingContexts set in the root of the remote LDAP server.
+
EXAMPLES:
The simplest migration, accepting all defaults:
@@ -353,14 +357,14 @@ class migrate_ds(Command):
Str('usercontainer?',
cli_name='user_container',
label=_('User container'),
- doc=_('RDN of container for users in DS'),
+ doc=_('RDN of container for users in DS relative to base DN'),
default=u'ou=people',
autofill=True,
),
Str('groupcontainer?',
cli_name='group_container',
label=_('Group container'),
- doc=_('RDN of container for groups in DS'),
+ doc=_('RDN of container for groups in DS relative to base DN'),
default=u'ou=groups',
autofill=True,
),
@@ -431,6 +435,11 @@ class migrate_ds(Command):
doc=_('Continuous operation mode. Errors are reported but the process continues'),
default=False,
),
+ Str('basedn?',
+ cli_name='base_dn',
+ label=_('Base DN'),
+ doc=_('Base DN on remote LDAP server'),
+ ),
)
has_output = (
@@ -526,14 +535,14 @@ can use their Kerberos accounts.''')
try:
(entries, truncated) = ds_ldap.find_entries(
search_filter, ['*'], search_bases[ldap_obj_name],
- ds_ldap.SCOPE_ONELEVEL,
+ _ldap.SCOPE_ONELEVEL,
time_limit=0, size_limit=-1,
search_refs=True # migrated DS may contain search references
)
except errors.NotFound:
if not options.get('continue',False):
raise errors.NotFound(
- reason=_('Container for %(container)s not found') % {'container': ldap_obj_name}
+ reason=_('Container for %(container)s not found at %(search_base)s') % {'container': ldap_obj_name, 'search_base': search_bases[ldap_obj_name]}
)
else:
truncated = False
@@ -627,6 +636,8 @@ can use their Kerberos accounts.''')
config = ldap.get_ipa_config()[1]
+ ds_base_dn = options.get('basedn')
+
# check if migration mode is enabled
if config.get('ipamigrationenabled', ('FALSE', ))[0] == 'FALSE':
return dict(result={}, failed={}, enabled=False)
@@ -635,15 +646,19 @@ can use their Kerberos accounts.''')
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,
- size_limit=-1, time_limit=0,
- )
- try:
- ds_base_dn = entries[0][1]['namingcontexts'][0]
- except (IndexError, KeyError), e:
- raise StandardError(str(e))
+ if not ds_base_dn:
+ # retrieve base DN from remote LDAP server
+ (entries, truncated) = ds_ldap.find_entries(
+ '', ['namingcontexts', 'defaultnamingcontext'], '',
+ _ldap.SCOPE_BASE, size_limit=-1, time_limit=0,
+ )
+ if 'defaultnamingcontext' in entries[0][1]:
+ ds_base_dn = entries[0][1]['defaultnamingcontext'][0]
+ else:
+ try:
+ ds_base_dn = entries[0][1]['namingcontexts'][0]
+ except (IndexError, KeyError), e:
+ raise StandardError(str(e))
# migrate!
(migrated, failed) = self.migrate(
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 20f7578ce..416ebf5f3 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -1191,10 +1191,16 @@ def get_ipa_basedn(conn):
:param conn: Bound LDAP connection that will be used for searching
"""
entries = conn.search_ext_s(
- '', scope=ldap.SCOPE_BASE, attrlist=['namingcontexts']
+ '', scope=ldap.SCOPE_BASE, attrlist=['defaultnamingcontext', 'namingcontexts']
)
contexts = entries[0][1]['namingcontexts']
+ if entries[0][1].get('defaultnamingcontext'):
+ # If there is a defaultNamingContext examine that one first
+ default = entries[0][1]['defaultnamingcontext'][0]
+ if default in contexts:
+ contexts.remove(default)
+ contexts.insert(0, entries[0][1]['defaultnamingcontext'][0])
for context in contexts:
root_logger.debug("Check if naming context '%s' is for IPA" % context)
try: