summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2012-01-30 16:29:32 -0500
committerMartin Kosek <mkosek@redhat.com>2012-02-29 15:28:32 +0100
commitc9e0473c6c6ddf8e09af076934afba6bf43f3fe0 (patch)
treee37037abcf343c94271b262f020d192efcde294b
parent6e6f24747e6ad384c956c952373e4084552a7e8d (diff)
downloadfreeipa.git-c9e0473c6c6ddf8e09af076934afba6bf43f3fe0.tar.gz
freeipa.git-c9e0473c6c6ddf8e09af076934afba6bf43f3fe0.tar.xz
freeipa.git-c9e0473c6c6ddf8e09af076934afba6bf43f3fe0.zip
Add support defaultNamingContext and add --basedn to migrate-ds
There are two sides to this, the server and client side. On the server side we attempt to add a defaultNamingContext on already installed servers. This will fail on older 389-ds instances but the failure is not fatal. New installations on versions of 389-ds that support this attribute will have it already defined. On the client side we need to look for both defaultNamingContext and namingContexts. We still need to check that the defaultNamingContext is an IPA server (info=IPAV2). The migration change also takes advantage of this and adds a new option which allows one to provide a basedn to use instead of trying to detect it. https://fedorahosted.org/freeipa/ticket/1919 https://fedorahosted.org/freeipa/ticket/2314
-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 548fc93d..73d115c0 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 523bde4a..381e3088 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 fe7a4bd0..420e0488 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 57c7bcb2..bc46e938 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 688265fd..884ae4ed 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 20f7578c..416ebf5f 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: