diff options
-rw-r--r-- | API.txt | 3 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | install/updates/10-config.update | 6 | ||||
-rw-r--r-- | ipa-client/ipa-join.c | 80 | ||||
-rw-r--r-- | ipalib/plugins/migration.py | 41 | ||||
-rw-r--r-- | ipapython/ipautil.py | 8 |
6 files changed, 99 insertions, 41 deletions
@@ -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) @@ -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: |