[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 50077 - Fix compiler warnings in automember rebuild task
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new 094e015 Ticket 50077 - Fix compiler warnings in automember rebuild task
094e015 is described below
commit 094e0151d0f2cbd4180843f86b74e98d96990f59
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Tue Jan 15 13:46:06 2019 -0500
Ticket 50077 - Fix compiler warnings in automember rebuild task
https://pagure.io/389-ds-base/issue/50077
Reviewed by: mreynolds(one line commit rule)
(cherry picked from commit eae696f1e0cf477f96966859feab9eacd9a0be82)
---
ldap/servers/plugins/automember/automember.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ldap/servers/plugins/automember/automember.c b/ldap/servers/plugins/automember/automember.c
index 6641745..abd6df8 100644
--- a/ldap/servers/plugins/automember/automember.c
+++ b/ldap/servers/plugins/automember/automember.c
@@ -2455,8 +2455,8 @@ out:
slapi_task_log_notice(task, "Automember rebuild task aborted. Error (%d)", result);
slapi_task_log_status(task, "Automember rebuild task aborted. Error (%d)", result);
} else {
- slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%d) entries.", i);
- slapi_task_log_status(task, "Automember rebuild task finished. Processed (%d) entries.", i);
+ slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%d) entries.", (int32_t)i);
+ slapi_task_log_status(task, "Automember rebuild task finished. Processed (%d) entries.", (int32_t)i);
}
slapi_task_inc_progress(task);
slapi_task_finish(task, result);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch master updated: Ticket 50077 - Fix compiler warnings in automember rebuild task
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch master
in repository 389-ds-base.
The following commit(s) were added to refs/heads/master by this push:
new eae696f Ticket 50077 - Fix compiler warnings in automember rebuild task
eae696f is described below
commit eae696f1e0cf477f96966859feab9eacd9a0be82
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Tue Jan 15 13:46:06 2019 -0500
Ticket 50077 - Fix compiler warnings in automember rebuild task
https://pagure.io/389-ds-base/issue/50077
Reviewed by: mreynolds(one line commit rule)
---
ldap/servers/plugins/automember/automember.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ldap/servers/plugins/automember/automember.c b/ldap/servers/plugins/automember/automember.c
index 6641745..abd6df8 100644
--- a/ldap/servers/plugins/automember/automember.c
+++ b/ldap/servers/plugins/automember/automember.c
@@ -2455,8 +2455,8 @@ out:
slapi_task_log_notice(task, "Automember rebuild task aborted. Error (%d)", result);
slapi_task_log_status(task, "Automember rebuild task aborted. Error (%d)", result);
} else {
- slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%d) entries.", i);
- slapi_task_log_status(task, "Automember rebuild task finished. Processed (%d) entries.", i);
+ slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%d) entries.", (int32_t)i);
+ slapi_task_log_status(task, "Automember rebuild task finished. Processed (%d) entries.", (int32_t)i);
}
slapi_task_inc_progress(task);
slapi_task_finish(task, result);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 49972 - use-after-free in case of several parallel krb authentication
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
tbordaz pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new bd3a05a Ticket 49972 - use-after-free in case of several parallel krb authentication
bd3a05a is described below
commit bd3a05a8ceedc3a8c6bfc2b3a400684f5bd910b3
Author: Thierry Bordaz <tbordaz(a)redhat.com>
AuthorDate: Thu Oct 11 16:15:46 2018 +0200
Ticket 49972 - use-after-free in case of several parallel krb authentication
Bug Description:
When several threads (RA) authenticates to the same host and at the same time
There is a good chance they will share the same credential cache.
If one authentication fails, the thread will clear the cache (krb5_cc_destroy)
although others threads may still use it.
Fix Description:
The best approach is to drop using krb5 function and use gssapi.
It is a quite intrusive change and a simplest temporary fix will serialize
all krb5 calls.
During initialization of the interaction structure (sasl), if using gssapi mechanism,
the calls to krb5 functions are serialized with a lock.
Then the lock is released for the authentication and cleanup.
Cleanup needs to be serialized as well as it calls krb5_cc_destroy.
The fix consist to acquire the lock over initialization/authentication/cleanup.
So only one RA can authenticate at the same time.
https://pagure.io/389-ds-base/issue/49972
Reviewed by: Robbie Harwood, William Brown (many thanks for your reviews !!)
Platforms tested: F27 & F28
Flag Day: no
Doc impact: no
---
ldap/servers/slapd/ldaputil.c | 78 +++++++++++++++++++++++++------------------
1 file changed, 45 insertions(+), 33 deletions(-)
diff --git a/ldap/servers/slapd/ldaputil.c b/ldap/servers/slapd/ldaputil.c
index fcf22e6..a215e90 100644
--- a/ldap/servers/slapd/ldaputil.c
+++ b/ldap/servers/slapd/ldaputil.c
@@ -1002,6 +1002,22 @@ slapi_ldap_init(char *ldaphost, int ldapport, int secure, int shared)
return slapi_ldap_init_ext(NULL, ldaphost, ldapport, secure, shared, NULL /*, NULL*/);
}
+static PRCallOnceType krb5_callOnce = {0, 0, 0};
+static PRLock *krb5_lock = NULL;
+
+static PRStatus
+internal_krb5_init(void)
+{
+ PR_ASSERT(NULL == krb5_lock);
+ if ((krb5_lock = PR_NewLock()) == NULL) {
+ PRErrorCode errorCode = PR_GetError();
+ slapi_log_err(SLAPI_LOG_ERR, "internal_krb5_init", "PR_NewLock failed %d:%s\n",
+ errorCode, slapd_pr_strerror(errorCode));
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
/*
* Does the correct bind operation simple/sasl/cert depending
* on the arguments passed in. If the user specified to use
@@ -1272,6 +1288,13 @@ slapi_ldap_bind(
}
}
} else {
+ int krb5_serialized = 0;
+
+#ifdef HAVE_KRB5
+ if (mech && !strcmp(mech, "GSSAPI")) {
+ krb5_serialized = 1;
+ }
+#endif
/*
* a SASL mech - set the sasl ssf to 0 if using TLS/SSL.
* openldap supports tls + sasl security
@@ -1282,6 +1305,23 @@ slapi_ldap_bind(
ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX, &max_ssf);
}
#endif
+ /*
+ * we are using static variables and sharing an in-memory credentials cache
+ * so we put a lock around all kerberos interactions
+ */
+ if (PR_SUCCESS != PR_CallOnce(&krb5_callOnce, internal_krb5_init)) {
+ slapi_log_err(SLAPI_LOG_ERR, "slapi_ldap_bind",
+ "Could not perform internal krb5 init\n");
+ rc = LDAP_LOCAL_ERROR;
+ goto done;
+ }
+
+#ifdef HAVE_KRB5
+ if (krb5_serialized) {
+ PR_Lock(krb5_lock);
+ }
+#endif
+
rc = slapd_ldap_sasl_interactive_bind(ld, bindid, creds, mech,
serverctrls, returnedctrls, msgidp);
if (LDAP_SUCCESS != rc) {
@@ -1298,6 +1338,11 @@ slapi_ldap_bind(
}
#endif
}
+#ifdef HAVE_KRB5
+ if (krb5_serialized) {
+ PR_Unlock(krb5_lock);
+ }
+#endif
}
done:
@@ -1853,22 +1898,6 @@ cleanup:
return myrc;
}
-static PRCallOnceType krb5_callOnce = {0, 0, 0};
-static PRLock *krb5_lock = NULL;
-
-static PRStatus
-internal_krb5_init(void)
-{
- PR_ASSERT(NULL == krb5_lock);
- if ((krb5_lock = PR_NewLock()) == NULL) {
- PRErrorCode errorCode = PR_GetError();
- slapi_log_err(SLAPI_LOG_ERR, "internal_krb5_init", "PR_NewLock failed %d:%s\n",
- errorCode, slapd_pr_strerror(errorCode));
- return PR_FAILURE;
- }
-
- return PR_SUCCESS;
-}
/*
* This implementation assumes that we want to use the
@@ -1907,19 +1936,6 @@ set_krb5_creds(
appear to be used
currently */
- /*
- * we are using static variables and sharing an in-memory credentials cache
- * so we put a lock around all kerberos interactions
- */
- if (PR_SUCCESS != PR_CallOnce(&krb5_callOnce, internal_krb5_init)) {
- slapi_log_err(SLAPI_LOG_ERR, logname,
- "Could not perform internal krb5 init\n");
- rc = -1;
- goto cleanup;
- }
-
- PR_Lock(krb5_lock);
-
/* initialize the kerberos context */
if ((rc = krb5_init_context(&ctx))) {
slapi_log_err(SLAPI_LOG_ERR, logname,
@@ -2221,7 +2237,6 @@ cleanup:
if (ctx) { /* cannot pass NULL to free context */
krb5_free_context(ctx);
}
- PR_Unlock(krb5_lock);
return;
}
@@ -2233,8 +2248,6 @@ clear_krb5_ccache(void)
krb5_ccache cc = NULL;
int rc = 0;
- PR_Lock(krb5_lock);
-
/* initialize the kerberos context */
if ((rc = krb5_init_context(&ctx))) {
slapi_log_err(SLAPI_LOG_ERR, "clear_krb5_ccache", "Could not initialize kerberos context: %d (%s)\n",
@@ -2260,7 +2273,6 @@ done:
krb5_free_context(ctx);
}
- PR_Unlock(krb5_lock);
}
#endif /* HAVE_KRB5 */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 50153 - Increase default max logs
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new f38a03d Ticket 50153 - Increase default max logs
f38a03d is described below
commit f38a03d8a0f02847b00805c1eda06e3bf42e2789
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Mon Jan 14 12:19:07 2019 -0500
Ticket 50153 - Increase default max logs
Bug Description: The errors & audit logs default to max number of logs to 1.
This prevents the max log size rotation policy from working.
This is documented in the docs, but the problem is that this
can allow the server to fill up the FS on /var under certain
conditions.
Fix Description: Change the default max number of logs to "2". This is still
a small value, and it allows the rotation policy to be effective.
https://pagure.io/389-ds-base/issue/50153
Reviewed by: spichugi & firstyear (Thanks!!)
(cherry picked from commit ca13f42da4d49fd30200fed960e1ad2d0fd1c763)
---
ldap/servers/slapd/slap.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index a027926..618e245 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -320,8 +320,8 @@ typedef void (*VFPV)(); /* takes undefined arguments */
#define SLAPD_DEFAULT_LOG_ROTATIONTIME_STR "1"
#define SLAPD_DEFAULT_LOG_ACCESS_MAXNUMLOGS 10
#define SLAPD_DEFAULT_LOG_ACCESS_MAXNUMLOGS_STR "10"
-#define SLAPD_DEFAULT_LOG_MAXNUMLOGS 1
-#define SLAPD_DEFAULT_LOG_MAXNUMLOGS_STR "1"
+#define SLAPD_DEFAULT_LOG_MAXNUMLOGS 2
+#define SLAPD_DEFAULT_LOG_MAXNUMLOGS_STR "2"
#define SLAPD_DEFAULT_LOG_EXPTIME 1
#define SLAPD_DEFAULT_LOG_EXPTIME_STR "1"
/* This is in MB */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 50077 - RFE - improve automember plugin to work with modify ops
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new e86c2b2 Ticket 50077 - RFE - improve automember plugin to work with modify ops
e86c2b2 is described below
commit e86c2b2f4d53d9f0db34e95cecf4f3055377c42d
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Tue Dec 11 17:14:54 2018 -0500
Ticket 50077 - RFE - improve automember plugin to work with modify ops
Description:
Previously automember was only invoked for ADD operations. This enhancement
allows it to work with modify operations, and it will also maintain the
correct memberships. So if a modify changes which groups the user would
belong to, it will add the user to the new group, and remove them from the
old group.
https://pagure.io/389-ds-base/issue/50077
Reviewed by: spichugi & firstyear (Thanks!!)
(cherry picked from commit 7096094e0f26ad3f4acbbffeefc0a174a791686d)
---
.../automember_plugin/automember_mod_test.py | 142 +++++++
ldap/ldif/template-dse.ldif.in | 1 +
ldap/servers/plugins/automember/automember.c | 407 +++++++++++++++++----
ldap/servers/plugins/automember/automember.h | 5 +
src/lib389/lib389/plugins.py | 206 ++++++++++-
5 files changed, 680 insertions(+), 81 deletions(-)
diff --git a/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py b/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py
new file mode 100644
index 0000000..4ba3bc8
--- /dev/null
+++ b/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py
@@ -0,0 +1,142 @@
+import logging
+import pytest
+import os
+from lib389.utils import ds_is_older
+from lib389._constants import *
+from lib389.plugins import AutoMembershipPlugin, AutoMembershipDefinitions
+from lib389.idm.user import UserAccounts
+from lib389.idm.group import Groups
+from lib389.topologies import topology_st as topo
+
+# Skip on older versions
+pytestmark = pytest.mark.skipif(ds_is_older('1.4.0'), reason="Not implemented")
+
+DEBUGGING = os.getenv("DEBUGGING", default=False)
+if DEBUGGING:
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
+else:
+ logging.getLogger(__name__).setLevel(logging.INFO)
+log = logging.getLogger(__name__)
+
+
+(a)pytest.fixture(scope="module")
+def automember_fixture(topo, request):
+ # Create group
+ groups = []
+ group_obj = Groups(topo.standalone, DEFAULT_SUFFIX)
+ groups.append(group_obj.create(properties={'cn': 'testgroup'}))
+ groups.append(group_obj.create(properties={'cn': 'testgroup2'}))
+ groups.append(group_obj.create(properties={'cn': 'testgroup3'}))
+
+ # Create test user
+ user_accts = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
+ user = user_accts.create_test_user()
+
+ # Create automember definitions and regex rules
+ automember_prop = {
+ 'cn': 'testgroup_definition',
+ 'autoMemberScope': DEFAULT_SUFFIX,
+ 'autoMemberFilter': 'objectclass=posixaccount',
+ 'autoMemberDefaultGroup': groups[0].dn,
+ 'autoMemberGroupingAttr': 'member:dn',
+ }
+ automembers = AutoMembershipDefinitions(topo.standalone)
+ auto_def = automembers.create(properties=automember_prop)
+ auto_def.add_regex_rule("regex1", groups[1].dn, include_regex=['cn=mark.*'])
+ auto_def.add_regex_rule("regex2", groups[2].dn, include_regex=['cn=simon.*'])
+
+ # Enable plugin
+ automemberplugin = AutoMembershipPlugin(topo.standalone)
+ automemberplugin.enable()
+ topo.standalone.restart()
+
+ return (user, groups)
+
+
+def test_mods(automember_fixture, topo):
+ """Modify the user so that it is added to the various automember groups
+
+ :id: 28a2b070-7f16-4905-8831-c80fa6441693
+ :setup: Standalone Instance
+ :steps:
+ 1. Update user that should add it to group[0]
+ 2. Update user that should add it to group[1]
+ 3. Update user that should add it to group[2]
+ 4. Update user that should add it to group[0]
+ 5. Test rebuild task correctly moves user to group[1]
+ :expectedresults:
+ 1. Success
+ 2. Success
+ 3. Success
+ 4. Success
+ 5. Success
+ """
+ (user, groups) = automember_fixture
+
+ # Update user which should go into group[0]
+ user.replace('cn', 'whatever')
+ groups[0].is_member(user.dn)
+ if groups[1].is_member(user.dn):
+ assert False
+ if groups[2].is_member(user.dn):
+ assert False
+
+ # Update user0 which should go into group[1]
+ user.replace('cn', 'mark')
+ groups[1].is_member(user.dn)
+ if groups[0].is_member(user.dn):
+ assert False
+ if groups[2].is_member(user.dn):
+ assert False
+
+ # Update user which should go into group[2]
+ user.replace('cn', 'simon')
+ groups[2].is_member(user.dn)
+ if groups[0].is_member(user.dn):
+ assert False
+ if groups[1].is_member(user.dn):
+ assert False
+
+ # Update user which should go back into group[0] (full circle)
+ user.replace('cn', 'whatever')
+ groups[0].is_member(user.dn)
+ if groups[1].is_member(user.dn):
+ assert False
+ if groups[2].is_member(user.dn):
+ assert False
+
+ #
+ # Test rebuild task. First disable plugin
+ #
+ automemberplugin = AutoMembershipPlugin(topo.standalone)
+ automemberplugin.disable()
+ topo.standalone.restart()
+
+ # Make change that would move the entry from group[0] to group[1]
+ user.replace('cn', 'mark')
+
+ # Enable plugin
+ automemberplugin.enable()
+ topo.standalone.restart()
+
+ # Run rebuild task
+ task = automemberplugin.fixup(DEFAULT_SUFFIX, "objectclass=posixaccount")
+ task.wait()
+
+ # Test membership
+ groups[1].is_member(user.dn)
+ if groups[0].is_member(user.dn):
+ assert False
+ if groups[2].is_member(user.dn):
+ assert False
+
+ # Success
+ log.info("Test PASSED")
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main(["-s", CURRENT_FILE])
+
diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in
index 3107903..bffdd30 100644
--- a/ldap/ldif/template-dse.ldif.in
+++ b/ldap/ldif/template-dse.ldif.in
@@ -681,6 +681,7 @@ nsslapd-plugininitfunc: automember_init
nsslapd-plugintype: betxnpreoperation
nsslapd-pluginenabled: on
nsslapd-plugin-depends-on-type: database
+autoMemberProcessModifyOps: on
dn: cn=Bitwise Plugin,cn=plugins,cn=config
objectClass: top
diff --git a/ldap/servers/plugins/automember/automember.c b/ldap/servers/plugins/automember/automember.c
index 08d9e49..6641745 100644
--- a/ldap/servers/plugins/automember/automember.c
+++ b/ldap/servers/plugins/automember/automember.c
@@ -73,7 +73,8 @@ static struct automemberRegexRule *automember_parse_regex_rule(char *rule_string
static void automember_free_regex_rule(struct automemberRegexRule *rule);
static int automember_parse_grouping_attr(char *value, char **grouping_attr, char **grouping_value);
static int automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileDesc *ldif_fd);
-static int automember_add_member_value(Slapi_Entry *member_e, const char *group_dn, char *grouping_attr, char *grouping_value, PRFileDesc *ldif_fd);
+static int automember_update_member_value(Slapi_Entry *member_e, const char *group_dn, char *grouping_attr,
+ char *grouping_value, PRFileDesc *ldif_fd, int add);
/*
* task functions
@@ -89,6 +90,8 @@ static void automember_task_export_destructor(Slapi_Task *task);
static void automember_task_map_destructor(Slapi_Task *task);
#define DEFAULT_FILE_MODE PR_IRUSR | PR_IWUSR
+static uint64_t plugin_do_modify = 1;
+static uint64_t plugin_is_betxn = 0;
/*
* Config cache locking functions
@@ -139,8 +142,6 @@ automember_get_plugin_sdn(void)
return _PluginDN;
}
-static int plugin_is_betxn = 0;
-
/*
* Plug-in initialization functions
*/
@@ -158,8 +159,7 @@ automember_init(Slapi_PBlock *pb)
"--> automember_init\n");
/* get args */
- if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) &&
- plugin_entry &&
+ if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) && plugin_entry &&
(plugin_type = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-plugintype")) &&
plugin_type && strstr(plugin_type, "betxn")) {
plugin_is_betxn = 1;
@@ -297,7 +297,9 @@ static int
automember_start(Slapi_PBlock *pb)
{
Slapi_DN *plugindn = NULL;
+ Slapi_Entry *plugin_entry = NULL;
char *config_area = NULL;
+ const char *do_modify = NULL;
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"--> automember_start\n");
@@ -342,8 +344,22 @@ automember_start(Slapi_PBlock *pb)
return -1;
}
+ /* Check and set if we should process modify operations */
+ plugin_do_modify = 1; /* default is "on" */
+ if ((slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &plugin_entry) == 0) && plugin_entry){
+ if ((do_modify = slapi_fetch_attr(plugin_entry, AUTOMEMBER_DO_MODIFY, NULL)) ) {
+ if (strcasecmp(do_modify, "on") && strcasecmp(do_modify, "off")) {
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_start - %s: invalid value \"%s\". Valid values are \"on\" or \"off\". Using default of \"on\"\n",
+ AUTOMEMBER_DO_MODIFY, do_modify);
+ } else if (strcasecmp(do_modify, "off") == 0 ){
+ plugin_do_modify = 0;
+ }
+ }
+ }
+
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "auto membership plug-in: ready for service\n");
+ "automember_start - ready for service\n");
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"<-- automember_start\n");
@@ -1365,37 +1381,49 @@ bail:
}
/*
- * automember_update_membership()
- *
- * Determines which target groups need to be updated according to
- * the rules in config, then performs the updates.
+ * Free the exclusion and inclusion group dn's created by
+ * automember_get_membership_lists()
*/
-static int
-automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileDesc *ldif_fd)
+static void
+automember_free_membership_lists(PRCList *exclusions, PRCList *targets)
+{
+ struct automemberDNListItem *dnitem = NULL;
+
+ /*
+ * Free the exclusions and targets lists. Remember that the DN's
+ * are not ours, so don't free them!
+ */
+ while (!PR_CLIST_IS_EMPTY(exclusions)) {
+ dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(exclusions);
+ PR_REMOVE_LINK((PRCList *)dnitem);
+ slapi_ch_free((void **)&dnitem);
+ }
+
+ while (!PR_CLIST_IS_EMPTY(targets)) {
+ dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(targets);
+ PR_REMOVE_LINK((PRCList *)dnitem);
+ slapi_ch_free((void **)&dnitem);
+ }
+}
+
+/*
+ * Populate the exclusion and inclusion(target) PRCLists based on the
+ * slapi entry and configEntry provided. The PRCLists should be freed
+ * using automember_free_membership_lists()
+ */
+static void
+automember_get_membership_lists(struct configEntry *config, PRCList *exclusions, PRCList *targets, Slapi_Entry *e)
{
PRCList *rule = NULL;
struct automemberRegexRule *curr_rule = NULL;
- PRCList exclusions;
- PRCList targets;
struct automemberDNListItem *dnitem = NULL;
Slapi_DN *last = NULL;
PRCList *curr_exclusion = NULL;
char **vals = NULL;
- int rc = 0;
int i = 0;
- if (!config || !e) {
- return -1;
- }
-
- slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "automember_update_membership - Processing \"%s\" "
- "definition entry for candidate entry \"%s\".\n",
- config->dn, slapi_entry_get_dn(e));
-
- /* Initialize our lists that keep track of targets. */
- PR_INIT_CLIST(&exclusions);
- PR_INIT_CLIST(&targets);
+ PR_INIT_CLIST(exclusions);
+ PR_INIT_CLIST(targets);
/* Go through exclusive rules and build an exclusion list. */
if (config->exclusive_rules) {
@@ -1416,20 +1444,19 @@ automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileD
/* Found a match. Add to end of the exclusion list
* and set last as a hint to ourselves. */
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "automember_update_membership - Adding \"%s\" "
+ "automember_get_membership_lists - Adding \"%s\" "
"to list of excluded groups for \"%s\" "
"(matched: \"%s=%s\").\n",
slapi_sdn_get_dn(curr_rule->target_group_dn),
slapi_entry_get_dn(e), curr_rule->attr,
curr_rule->regex_str);
- dnitem = (struct automemberDNListItem *)slapi_ch_calloc(1,
- sizeof(struct automemberDNListItem));
+ dnitem = (struct automemberDNListItem *)slapi_ch_calloc(1, sizeof(struct automemberDNListItem));
/* We are just referencing the dn from the regex rule. We
* will not free it when we clean up this list. This list
* is more short-lived than the regex rule list, so we can
* get away with this optimization. */
dnitem->dn = curr_rule->target_group_dn;
- PR_APPEND_LINK(&(dnitem->list), &exclusions);
+ PR_APPEND_LINK(&(dnitem->list), exclusions);
last = curr_rule->target_group_dn;
}
}
@@ -1450,8 +1477,8 @@ automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileD
last = NULL;
/* Get the first excluded target for exclusion checking. */
- if (!PR_CLIST_IS_EMPTY(&exclusions)) {
- curr_exclusion = PR_LIST_HEAD(&exclusions);
+ if (!PR_CLIST_IS_EMPTY(exclusions)) {
+ curr_exclusion = PR_LIST_HEAD(exclusions);
}
rule = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
@@ -1468,7 +1495,7 @@ automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileD
* until we find a target that is the same or comes after the
* current rule. */
if (curr_exclusion) {
- while ((curr_exclusion != &exclusions) && (slapi_sdn_compare(
+ while ((curr_exclusion != exclusions) && (slapi_sdn_compare(
((struct automemberDNListItem *)curr_exclusion)->dn,
curr_rule->target_group_dn) < 0)) {
curr_exclusion = PR_NEXT_LINK(curr_exclusion);
@@ -1479,7 +1506,7 @@ automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileD
* we can skip all rules for the last target group DN that we
* added to the targets list. We also skip any rules for
* target groups that have been excluded by an exclusion rule. */
- if (((curr_exclusion == NULL) || (curr_exclusion == &exclusions) ||
+ if (((curr_exclusion == NULL) || (curr_exclusion == exclusions) ||
slapi_sdn_compare(((struct automemberDNListItem *)curr_exclusion)->dn,
curr_rule->target_group_dn) != 0) &&
((last == NULL) ||
@@ -1492,7 +1519,7 @@ automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileD
/* Found a match. Add to the end of the targets list
* and set last as a hint to ourselves. */
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "automember_update_membership - Adding \"%s\" "
+ "automember_get_membership_lists - Adding \"%s\" "
"to list of target groups for \"%s\" "
"(matched: \"%s=%s\").\n",
slapi_sdn_get_dn(curr_rule->target_group_dn),
@@ -1505,7 +1532,7 @@ automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileD
* is more short-lived than the regex rule list, so we can
* get away with this optimization. */
dnitem->dn = curr_rule->target_group_dn;
- PR_APPEND_LINK(&(dnitem->list), &targets);
+ PR_APPEND_LINK(&(dnitem->list), targets);
last = curr_rule->target_group_dn;
}
}
@@ -1518,6 +1545,42 @@ automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileD
}
}
}
+}
+
+/*
+ * automember_update_membership()
+ *
+ * Determines which target groups need to be updated according to
+ * the rules in config, then performs the updates.
+ *
+ * Return SLAPI_PLUGIN_FAILURE for failures, or
+ * SLAPI_PLUGIN_SUCCESS for success (no memberships updated), or
+ * MEMBERSHIP_UPDATED for success (memberships updated)
+ */
+static int
+automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileDesc *ldif_fd)
+{
+ PRCList exclusions;
+ PRCList targets;
+ struct automemberDNListItem *dnitem = NULL;
+ int rc = SLAPI_PLUGIN_SUCCESS;
+ int i = 0;
+
+ if (!config || !e) {
+ return SLAPI_PLUGIN_FAILURE;
+ }
+
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_update_membership - Processing \"%s\" "
+ "definition entry for candidate entry \"%s\".\n",
+ config->dn, slapi_entry_get_dn(e));
+
+ /* Initialize our lists that keep track of targets. */
+ PR_INIT_CLIST(&exclusions);
+ PR_INIT_CLIST(&targets);
+
+ /* get the membership lists */
+ automember_get_membership_lists(config, &exclusions, &targets, e);
/* If no targets, update default groups if set. Otherwise, update
* targets. Use a helper to do the actual updates. We can just pass an
@@ -1526,51 +1589,45 @@ automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileD
if (PR_CLIST_IS_EMPTY(&targets)) {
/* Add to each default group. */
for (i = 0; config->default_groups && config->default_groups[i]; i++) {
- if (automember_add_member_value(e, config->default_groups[i], config->grouping_attr,
- config->grouping_value, ldif_fd)) {
+ if (automember_update_member_value(e, config->default_groups[i], config->grouping_attr,
+ config->grouping_value, ldif_fd, ADD_MEMBER))
+ {
rc = SLAPI_PLUGIN_FAILURE;
goto out;
}
+ rc = MEMBERSHIP_UPDATED;
}
} else {
/* Update the target groups. */
dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(&targets);
while ((PRCList *)dnitem != &targets) {
- if (automember_add_member_value(e, slapi_sdn_get_dn(dnitem->dn), config->grouping_attr,
- config->grouping_value, ldif_fd)) {
+ if (automember_update_member_value(e, slapi_sdn_get_dn(dnitem->dn), config->grouping_attr,
+ config->grouping_value, ldif_fd, ADD_MEMBER))
+ {
rc = SLAPI_PLUGIN_FAILURE;
goto out;
}
dnitem = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dnitem);
+ rc = MEMBERSHIP_UPDATED;
}
}
- /* Free the exclusions and targets lists. Remember that the DN's
- * are not ours, so don't free them! */
- while (!PR_CLIST_IS_EMPTY(&exclusions)) {
- dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(&exclusions);
- PR_REMOVE_LINK((PRCList *)dnitem);
- slapi_ch_free((void **)&dnitem);
- }
-
- while (!PR_CLIST_IS_EMPTY(&targets)) {
- dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(&targets);
- PR_REMOVE_LINK((PRCList *)dnitem);
- slapi_ch_free((void **)&dnitem);
- }
+ /* Free the exclusions and targets lists */
+ automember_free_membership_lists(&exclusions, &targets);
out:
return rc;
}
+
/*
- * automember_add_member_value()
+ * automember_update_member_value()
*
* Adds a member entry to a group.
*/
static int
-automember_add_member_value(Slapi_Entry *member_e, const char *group_dn, char *grouping_attr, char *grouping_value, PRFileDesc *ldif_fd)
+automember_update_member_value(Slapi_Entry *member_e, const char *group_dn, char *grouping_attr, char *grouping_value, PRFileDesc *ldif_fd, int add)
{
Slapi_PBlock *mod_pb = slapi_pblock_new();
int result = LDAP_SUCCESS;
@@ -1606,34 +1663,57 @@ automember_add_member_value(Slapi_Entry *member_e, const char *group_dn, char *g
/* Set up the operation. */
vals[0] = member_value;
vals[1] = 0;
- mod.mod_op = LDAP_MOD_ADD;
+ if (add) {
+ mod.mod_op = LDAP_MOD_ADD;
+ } else {
+ mod.mod_op = LDAP_MOD_DELETE;
+ }
mod.mod_type = grouping_attr;
mod.mod_values = vals;
mods[0] = &mod;
mods[1] = 0;
/* Perform the modify operation. */
- slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "automember_add_member_value - Adding \"%s\" as "
- "a \"%s\" value to group \"%s\".\n",
- member_value, grouping_attr, group_dn);
+ if (add){
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_update_member_value - Adding \"%s\" as "
+ "a \"%s\" value to group \"%s\".\n",
+ member_value, grouping_attr, group_dn);
+ } else {
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_update_member_value - Deleting \"%s\" as "
+ "a \"%s\" value from group \"%s\".\n",
+ member_value, grouping_attr, group_dn);
+ }
slapi_modify_internal_set_pb(mod_pb, group_dn,
mods, 0, 0, automember_get_plugin_id(), 0);
slapi_modify_internal_pb(mod_pb);
slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
- if ((result != LDAP_SUCCESS) && (result != LDAP_TYPE_OR_VALUE_EXISTS)) {
- slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "automember_add_member_value - Unable to add \"%s\" as "
- "a \"%s\" value to group \"%s\" (%s).\n",
- member_value, grouping_attr, group_dn,
- ldap_err2string(result));
- rc = result;
+ if(add){
+ if ((result != LDAP_SUCCESS) && (result != LDAP_TYPE_OR_VALUE_EXISTS)) {
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_update_member_value - Unable to add \"%s\" as "
+ "a \"%s\" value to group \"%s\" (%s).\n",
+ member_value, grouping_attr, group_dn,
+ ldap_err2string(result));
+ rc = result;
+ }
+ } else {
+ /* delete value */
+ if ((result != LDAP_SUCCESS) && (result != LDAP_NO_SUCH_ATTRIBUTE)) {
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_update_member_value - Unable to delete \"%s\" as "
+ "a \"%s\" value from group \"%s\" (%s).\n",
+ member_value, grouping_attr, group_dn,
+ ldap_err2string(result));
+ rc = result;
+ }
}
} else {
slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "automember_add_member_value - Unable to find grouping "
+ "automember_update_member_value - Unable to find grouping "
"value attribute \"%s\" in entry \"%s\".\n",
grouping_value, slapi_entry_get_dn(member_e));
}
@@ -1780,22 +1860,144 @@ automember_mod_pre_op(Slapi_PBlock *pb)
static int
automember_mod_post_op(Slapi_PBlock *pb)
{
+ Slapi_Entry *post_e = NULL;
+ Slapi_Entry *pre_e = NULL;
Slapi_DN *sdn = NULL;
+ struct configEntry *config = NULL;
+ PRCList *list = NULL;
+ int rc = SLAPI_PLUGIN_SUCCESS;
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
"--> automember_mod_post_op\n");
if (automember_oktodo(pb) && (sdn = automember_get_sdn(pb))) {
- /* Check if the config is being modified and reload if so. */
if (automember_dn_is_config(sdn)) {
+ /*
+ * The config is being modified, reload it
+ */
automember_load_config();
+ } else if ( !automember_isrepl(pb) && plugin_do_modify) {
+ /*
+ * We might be applying an update that will invoke automembership changes...
+ */
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e);
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &pre_e);
+
+ if (post_e) {
+ /*
+ * Check if a config entry applies to the entry being modified
+ */
+ automember_config_read_lock();
+
+ if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
+ list = PR_LIST_HEAD(g_automember_config);
+ while (list != g_automember_config) {
+ config = (struct configEntry *)list;
+
+ /* Does the entry meet scope and filter requirements? */
+ if (slapi_dn_issuffix(slapi_sdn_get_dn(sdn), config->scope) &&
+ slapi_filter_test_simple(post_e, config->filter) == 0)
+ {
+ /* Find out what membership changes are needed and make them. */
+ if ((rc = automember_update_membership(config, post_e, NULL)) == SLAPI_PLUGIN_FAILURE) {
+ /* Failed to update our groups, break out */
+ break;
+ } else if (rc == MEMBERSHIP_UPDATED) {
+ /*
+ * We updated one of our groups, but we might need to do some cleanup in other groups
+ */
+ PRCList exclusions_post, targets_post;
+ PRCList exclusions_pre, targets_pre;
+ struct automemberDNListItem *dn_pre = NULL;
+ struct automemberDNListItem *dn_post = NULL;
+ int i;
+
+ /* reset rc */
+ rc = SLAPI_PLUGIN_SUCCESS;
+
+ /* Get the group lists */
+ automember_get_membership_lists(config, &exclusions_post, &targets_post, post_e);
+ automember_get_membership_lists(config, &exclusions_pre, &targets_pre, pre_e);
+
+ /* Process the before and after lists */
+ if (PR_CLIST_IS_EMPTY(&targets_pre) && !PR_CLIST_IS_EMPTY(&targets_post)) {
+ /*
+ * We were in the default groups, but not anymore
+ */
+ for (i = 0; config->default_groups && config->default_groups[i]; i++) {
+ if (automember_update_member_value(post_e, config->default_groups[i], config->grouping_attr,
+ config->grouping_value, NULL, DEL_MEMBER))
+ {
+ rc = SLAPI_PLUGIN_FAILURE;
+ break;
+ }
+ }
+ } else if (!PR_CLIST_IS_EMPTY(&targets_pre) && PR_CLIST_IS_EMPTY(&targets_post)) {
+ /*
+ * We were in non-default groups, but not anymore
+ */
+ dn_pre = (struct automemberDNListItem *)PR_LIST_HEAD(&targets_pre);
+ while ((PRCList *)dn_pre != &targets_pre) {
+ if (automember_update_member_value(post_e, slapi_sdn_get_dn(dn_pre->dn), config->grouping_attr,
+ config->grouping_value, NULL, DEL_MEMBER))
+ {
+ rc = SLAPI_PLUGIN_FAILURE;
+ break;
+ }
+ dn_pre = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dn_pre);
+ }
+ } else {
+ /*
+ * We were previously in non-default groups, and still in non-default groups.
+ * Compare before and after memberships and cleanup the orphaned memberships
+ */
+ dn_pre = (struct automemberDNListItem *)PR_LIST_HEAD(&targets_pre);
+ while ((PRCList *)dn_pre != &targets_pre) {
+ int found = 0;
+ dn_post = (struct automemberDNListItem *)PR_LIST_HEAD(&targets_post);
+ while ((PRCList *)dn_post != &targets_post) {
+ if (slapi_sdn_compare(dn_pre->dn, dn_post->dn) == 0) {
+ /* found */
+ found = 1;
+ break;
+ }
+ /* Next dn */
+ dn_post = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dn_post);
+ }
+ if (!found){
+ /* Remove user from dn_pre->dn */
+ if (automember_update_member_value(post_e, slapi_sdn_get_dn(dn_pre->dn), config->grouping_attr,
+ config->grouping_value, NULL, DEL_MEMBER))
+ {
+ rc = SLAPI_PLUGIN_FAILURE;
+ break;
+ }
+ }
+ /* Next dn */
+ dn_pre = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dn_pre);
+ }
+ }
+
+ /* All done with this config entry, free the lists */
+ automember_free_membership_lists(&exclusions_post, &targets_post);
+ automember_free_membership_lists(&exclusions_pre, &targets_pre);
+ if (rc == SLAPI_PLUGIN_FAILURE) {
+ break;
+ }
+ }
+ }
+ list = PR_NEXT_LINK(list);
+ }
+ }
+ automember_config_unlock();
+ }
}
}
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
- "<-- automember_mod_post_op\n");
+ "<-- automember_mod_post_op (%d)\n", rc);
- return SLAPI_PLUGIN_SUCCESS;
+ return rc;
}
static int
@@ -1854,7 +2056,7 @@ automember_add_post_op(Slapi_PBlock *pb)
if (slapi_dn_issuffix(slapi_sdn_get_dn(sdn), config->scope) &&
(slapi_filter_test_simple(e, config->filter) == 0)) {
/* Find out what membership changes are needed and make them. */
- if (automember_update_membership(config, e, NULL)) {
+ if (automember_update_membership(config, e, NULL) == SLAPI_PLUGIN_FAILURE) {
rc = SLAPI_PLUGIN_FAILURE;
break;
}
@@ -2098,8 +2300,9 @@ automember_rebuild_task_thread(void *arg)
Slapi_Entry **entries = NULL;
task_data *td = NULL;
PRCList *list = NULL;
+ PRCList *include_list = NULL;
int result = 0;
- int i = 0;
+ size_t i = 0, ii = 0;
if (!task) {
return; /* no task */
@@ -2175,9 +2378,55 @@ automember_rebuild_task_thread(void *arg)
config = (struct configEntry *)list;
/* Does the entry meet scope and filter requirements? */
if (slapi_dn_issuffix(slapi_entry_get_dn(entries[i]), config->scope) &&
- (slapi_filter_test_simple(entries[i], config->filter) == 0)) {
+ (slapi_filter_test_simple(entries[i], config->filter) == 0))
+ {
+ /* First clear out all the defaults groups */
+ for (ii = 0; config->default_groups && config->default_groups[ii]; ii++) {
+ if ((result = automember_update_member_value(entries[i], config->default_groups[ii],
+ config->grouping_attr, config->grouping_value, NULL, DEL_MEMBER)))
+ {
+ slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
+ "member from default group (%s) error (%d)\n",
+ config->default_groups[ii], result);
+ slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
+ "member from default group (%s) error (%d)\n",
+ config->default_groups[ii], result);
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
+ config->default_groups[ii], result);
+ automember_config_unlock();
+ goto out;
+ }
+ }
+
+ /* Then clear out the non-default group */
+ if (config->inclusive_rules && !PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
+ include_list = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
+ while (include_list != (PRCList *)config->inclusive_rules) {
+ struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)include_list;
+ if ((result = automember_update_member_value(entries[i], slapi_sdn_get_dn(curr_rule->target_group_dn),
+ config->grouping_attr, config->grouping_value, NULL, DEL_MEMBER)))
+ {
+ slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
+ "member from group (%s) error (%d)\n",
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
+ "member from group (%s) error (%d)\n",
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
+ "automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
+ automember_config_unlock();
+ goto out;
+ }
+ include_list = PR_NEXT_LINK(include_list);
+ }
+ }
+
+ /* Update the memberships for this entries */
if (slapi_is_shutting_down() ||
- automember_update_membership(config, entries[i], NULL)) {
+ automember_update_membership(config, entries[i], NULL) == SLAPI_PLUGIN_FAILURE)
+ {
result = SLAPI_PLUGIN_FAILURE;
automember_config_unlock();
goto out;
@@ -2390,7 +2639,7 @@ automember_export_task_thread(void *arg)
if (slapi_dn_issuffix(slapi_sdn_get_dn(td->base_dn), config->scope) &&
(slapi_filter_test_simple(entries[i], config->filter) == 0)) {
if (slapi_is_shutting_down() ||
- automember_update_membership(config, entries[i], ldif_fd)) {
+ automember_update_membership(config, entries[i], ldif_fd) == SLAPI_PLUGIN_FAILURE) {
result = SLAPI_DSE_CALLBACK_ERROR;
automember_config_unlock();
goto out;
@@ -2594,7 +2843,7 @@ automember_map_task_thread(void *arg)
if (slapi_dn_issuffix(slapi_entry_get_dn_const(e), config->scope) &&
(slapi_filter_test_simple(e, config->filter) == 0)) {
if (slapi_is_shutting_down() ||
- automember_update_membership(config, e, ldif_fd_out)) {
+ automember_update_membership(config, e, ldif_fd_out) == SLAPI_PLUGIN_FAILURE) {
result = SLAPI_DSE_CALLBACK_ERROR;
slapi_entry_free(e);
slapi_ch_free_string(&entrystr);
@@ -2702,7 +2951,7 @@ automember_modrdn_post_op(Slapi_PBlock *pb)
if (slapi_dn_issuffix(slapi_sdn_get_dn(new_sdn), config->scope) &&
(slapi_filter_test_simple(post_e, config->filter) == 0)) {
/* Find out what membership changes are needed and make them. */
- if (automember_update_membership(config, post_e, NULL)) {
+ if (automember_update_membership(config, post_e, NULL) == SLAPI_PLUGIN_FAILURE) {
rc = SLAPI_PLUGIN_FAILURE;
break;
}
diff --git a/ldap/servers/plugins/automember/automember.h b/ldap/servers/plugins/automember/automember.h
index 37b88b0..4bd379a 100644
--- a/ldap/servers/plugins/automember/automember.h
+++ b/ldap/servers/plugins/automember/automember.h
@@ -44,6 +44,7 @@
#define AUTOMEMBER_GROUPING_ATTR_TYPE "autoMemberGroupingAttr"
#define AUTOMEMBER_DISABLED_TYPE "autoMemberDisabled"
#define AUTOMEMBER_TARGET_GROUP_TYPE "autoMemberTargetGroup"
+#define AUTOMEMBER_DO_MODIFY "autoMemberProcessModifyOps"
/*
* Config loading filters
@@ -55,6 +56,10 @@
* Helper defines
*/
#define IS_ATTRDESC_CHAR(c) (isalnum(c) || (c == '.') || (c == ';') || (c == '-'))
+#define MEMBERSHIP_UPDATED 1
+#define ADD_MEMBER 1
+#define DEL_MEMBER 0
+
struct automemberRegexRule
{
diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py
index d683f21..761ef96 100644
--- a/src/lib389/lib389/plugins.py
+++ b/src/lib389/lib389/plugins.py
@@ -387,8 +387,210 @@ class AccountUsabilityPlugin(Plugin):
super(AccountUsabilityPlugin, self).__init__(instance, dn, batch)
class AutoMembershipPlugin(Plugin):
- def __init__(self, instance, dn="cn=Auto Membership Plugin,cn=plugins,cn=config", batch=False):
- super(AutoMembershipPlugin, self).__init__(instance, dn, batch)
+ """An instance of Auto Membership plugin entry
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param dn: Entry DN
+ :type dn: str
+ """
+
+ _plugin_properties = {
+ 'cn' : 'Auto Membership Plugin',
+ 'nsslapd-pluginEnabled' : 'off',
+ 'nsslapd-pluginPath' : 'libautomember-plugin',
+ 'nsslapd-pluginInitfunc' : 'automember_init',
+ 'nsslapd-pluginType' : 'betxnpreoperation',
+ 'nsslapd-plugin-depends-on-type' : 'database',
+ 'nsslapd-pluginId' : 'Auto Membership',
+ 'nsslapd-pluginVendor' : '389 Project',
+ 'nsslapd-pluginVersion' : '1.3.7.0',
+ 'nsslapd-pluginDescription' : 'Auto Membership plugin',
+ }
+
+ def __init__(self, instance, dn="cn=Auto Membership Plugin,cn=plugins,cn=config"):
+ super(AutoMembershipPlugin, self).__init__(instance, dn)
+
+ def fixup(self, basedn, _filter=None):
+ """Create an automember rebuild membership task
+
+ :param basedn: Basedn to fix up
+ :type basedn: str
+ :param _filter: a filter for entries to fix up
+ :type _filter: str
+
+ :returns: an instance of Task(DSLdapObject)
+ """
+
+ task = tasks.AutomemberRebuildMembershipTask(self._instance)
+ task_properties = {'basedn': basedn}
+ if _filter is not None:
+ task_properties['filter'] = _filter
+ task.create(properties=task_properties)
+
+ return task
+
+
+class AutoMembershipRegexRule(DSLdapObject):
+ def __init__(self, instance, dn=None):
+ super(AutoMembershipRegexRule, self).__init__(instance, dn)
+ self._rdn_attribute = 'cn'
+ self._must_attributes = ['cn', 'autoMemberTargetGroup']
+ self._create_objectclasses = ['top', 'autoMemberRegexRule']
+ self._protected = False
+
+
+class AutoMembershipRegexRules(DSLdapObjects):
+ def __init__(self, instance, basedn="cn=Auto Membership Plugin,cn=plugins,cn=config"):
+ super(AutoMembershipRegexRules, self).__init__(instance)
+ self._objectclasses = ['top', 'autoMemberRegexRule']
+ self._filterattrs = ['cn']
+ self._childobject = AutoMembershipRegexRule
+ self._basedn = basedn
+
+
+class AutoMembershipDefinition(DSLdapObject):
+ """A single instance of Auto Membership Plugin config entry
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param dn: Entry DN
+ :type dn: str
+ """
+
+ def __init__(self, instance, dn=None):
+ super(AutoMembershipDefinition, self).__init__(instance, dn)
+ self._rdn_attribute = 'cn'
+ self._must_attributes = ['cn', 'autoMemberScope', 'autoMemberFilter', 'autoMemberGroupingAttr']
+ self._create_objectclasses = ['top', 'autoMemberDefinition']
+ self._protected = False
+
+ def get_groupattr(self):
+ """Get autoMemberGroupingAttr attributes"""
+
+ return self.get_attr_vals_utf8('autoMemberGroupingAttr')
+
+ def set_groupattr(self, attr):
+ """Set autoMemberGroupingAttr attribute"""
+
+ self.set('autoMemberGroupingAttr', attr)
+
+ def get_defaultgroup(self, attr):
+ """Get autoMemberDefaultGroup attributes"""
+
+ return self.get_attr_vals_utf8('autoMemberDefaultGroup')
+
+ def set_defaultgroup(self, attr):
+ """Set autoMemberDefaultGroup attribute"""
+
+ self.set('autoMemberDefaultGroup', attr)
+
+ def get_scope(self, attr):
+ """Get autoMemberScope attributes"""
+
+ return self.get_attr_vals_utf8('autoMemberScope')
+
+ def set_scope(self, attr):
+ """Set autoMemberScope attribute"""
+
+ self.set('autoMemberScope', attr)
+
+ def get_filter(self, attr):
+ """Get autoMemberFilter attributes"""
+
+ return self.get_attr_vals_utf8('autoMemberFilter')
+
+ def set_filter(self, attr):
+ """Set autoMemberFilter attributes"""
+
+ self.set('autoMemberFilter', attr)
+
+ def add_regex_rule(self, rule_name, target, include_regex=None, exclude_regex=None):
+ """Add a regex rule
+ :param rule_name - Name of the rule - used dfor the "cn" value inthe DN of the rule entry
+ :param target - the target group DN
+ :param include_regex - a List of regex rules used for group inclusion
+ :param exclude_regex - a List of regex rules used for group exclusion
+ """
+ props = {'cn': rule_name,
+ 'autoMemberTargetGroup': target}
+
+ if include_regex is not None:
+ props['autoMemberInclusiveRegex'] = include_regex
+ if exclude_regex is not None:
+ props['autoMemberInclusiveRegex'] = exclude_regex
+
+ rules = AutoMembershipRegexRules(self._instance, basedn=self.dn)
+ rules.create(properties=props)
+
+ def del_regex_rule(self, rule_name):
+ """Delete a regex rule from this definition
+ :param rule_name - The "cn" values of the regex rule entry
+ :raises ValueError - If a regex rule entry can not be found using rule_name
+ """
+ rules = AutoMembershipRegexRules(self._instance, basedn=self.dn)
+ regex = rules.get(selector=rule_name)
+ if regex is not None:
+ regex.delete()
+ else:
+ raise ValueError("No regex rule found with the name ({}) under ({})".format(rule_name, self.dn))
+
+ def list_regex_rules(self):
+ """Return a list of regex rule entries for this definition
+ """
+ rules = AutoMembershipRegexRules(self._instance, basedn=self.dn)
+ return rules.list()
+
+
+class AutoMembershipDefinitions(DSLdapObjects):
+ """A DSLdapObjects entity which represents Auto Membership Plugin config entry
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param basedn: Base DN for all account entries below
+ :type basedn: str
+ """
+
+ def __init__(self, instance, basedn="cn=Auto Membership Plugin,cn=plugins,cn=config"):
+ super(AutoMembershipDefinitions, self).__init__(instance)
+ self._objectclasses = ['top', 'autoMemberDefinition']
+ self._filterattrs = ['cn']
+ self._childobject = AutoMembershipDefinition
+ self._basedn = basedn
+
+
+class AutoMembershipRegexRule(DSLdapObject):
+ """A single instance of Auto Membership Plugin Regex Rule config entry
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param dn: Entry DN
+ :type dn: str
+ """
+
+ def __init__(self, instance, dn=None):
+ super(AutoMembershipRegexRule, self).__init__(instance, dn)
+ self._rdn_attribute = 'cn'
+ self._must_attributes = ['cn', 'autoMemberTargetGroup']
+ self._create_objectclasses = ['top', 'autoMemberRegexRule']
+ self._protected = False
+
+
+class AutoMembershipRegexRules(DSLdapObjects):
+ """A DSLdapObjects entity which represents Auto Membership Plugin Regex Rule config entry
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param basedn: Base DN for all account entries below
+ :type basedn: str
+ """
+
+ def __init__(self, instance, basedn):
+ super(AutoMembershipRegexRules, self).__init__(instance)
+ self._objectclasses = ['top', 'autoMemberRegexRule']
+ self._filterattrs = ['cn']
+ self._childobject = AutoMembershipRegexRule
+ self._basedn = basedn
class ContentSynchronizationPlugin(Plugin):
def __init__(self, instance, dn="cn=Content Synchronization,cn=plugins,cn=config", batch=False):
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 50138 - db2bak.pl -P LDAPS does not work when nsslapd-securePort is missing
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mhonek pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new 89a7f26 Ticket 50138 - db2bak.pl -P LDAPS does not work when nsslapd-securePort is missing
89a7f26 is described below
commit 89a7f266822ee7821a0c6208ed3b8e7ee019e7eb
Author: Matúš Honěk <mhonek(a)redhat.com>
AuthorDate: Wed Jan 9 13:49:25 2019 +0100
Ticket 50138 - db2bak.pl -P LDAPS does not work when nsslapd-securePort is missing
Bug Description:
When nsslapd-securePort is not set in dse.ldif, DSUtil (in case of LDAPS)
interpolates ldapmodify (and other) call to a URI ending only with a colon
because obtained securePort is just an empty string. The very same turns out to
happen for the nsslapd-port.
Fix Description:
Hardcode the default ports, which is a short variant of omitting the
colon and port, and which would be chosen by the OpenLDAP utils any way.
https://pagure.io/389-ds-base/issue/50138
Author: mhonek
Review by: mreynolds (thanks!)
---
ldap/admin/src/scripts/DSUtil.pm.in | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ldap/admin/src/scripts/DSUtil.pm.in b/ldap/admin/src/scripts/DSUtil.pm.in
index 791464d..c2a9af0 100644
--- a/ldap/admin/src/scripts/DSUtil.pm.in
+++ b/ldap/admin/src/scripts/DSUtil.pm.in
@@ -1239,7 +1239,7 @@ sub get_info {
$info{host} = $entry->getValues("nsslapd-localhost");
}
if($info{port} eq ""){
- $info{port} = $entry->getValues("nsslapd-port");
+ $info{port} = $entry->getValues("nsslapd-port") || "389";
}
if($info{rootdn} eq ""){
$info{rootdn} = $entry->getValues("nsslapd-rootdn");
@@ -1253,7 +1253,7 @@ sub get_info {
$ENV{LDAPTLS_CACERTDIR}=$info{certdir};
}
$info{security} = $entry->getValues("nsslapd-security");
- $info{secure_port} = $entry->getValues("nsslapd-securePort");
+ $info{secure_port} = $entry->getValues("nsslapd-securePort") || "636";
$info{ldapi} = $entry->getValues("nsslapd-ldapilisten");
$info{autobind} = $entry->getValues("nsslapd-ldapiautobind");
$value = $entry->getValues("nsslapd-ldapifilepath");
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch master updated: Ticket 50056 - dsctl db2ldif throws an exception
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
mreynolds pushed a commit to branch master
in repository 389-ds-base.
The following commit(s) were added to refs/heads/master by this push:
new a4632aa Ticket 50056 - dsctl db2ldif throws an exception
a4632aa is described below
commit a4632aa66f4e3dfdc7ffebbcecc5a347198434ce
Author: Mark Reynolds <mreynolds(a)redhat.com>
AuthorDate: Thu Jan 10 12:49:10 2019 -0500
Ticket 50056 - dsctl db2ldif throws an exception
Description: dsctl db2ldif throws an exception because of a typo in
a parameter name.
https://pagure.io/389-ds-base/issue/50056
Reviewed by: mreynolds(one line commit rule)
---
src/lib389/lib389/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
index 78221df..d76b34b 100644
--- a/src/lib389/lib389/__init__.py
+++ b/src/lib389/lib389/__init__.py
@@ -2847,7 +2847,7 @@ class DirSrv(SimpleLDAPObject, object):
cmd.append('-a')
tnow = datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
if bename:
- ldifname = os.path.join(self.ds_paths.ldif_dir, "%s-%s-%s.ldif" % (self.serverid, be_name, tnow))
+ ldifname = os.path.join(self.ds_paths.ldif_dir, "%s-%s-%s.ldif" % (self.serverid, bename, tnow))
else:
ldifname = os.path.join(self.ds_paths.ldif_dir, "%s-%s.ldif" % (self.serverid, tnow))
cmd.append(ldifname)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 49985 - memberof may silently fails to update a member
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
tbordaz pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new 3f8221c Ticket 49985 - memberof may silently fails to update a member
3f8221c is described below
commit 3f8221c8103d19402e8f26ad74adcedf870d0f99
Author: Thierry Bordaz <tbordaz(a)redhat.com>
AuthorDate: Mon Oct 22 12:33:13 2018 +0200
Ticket 49985 - memberof may silently fails to update a member
Bug Description:
when adding 'memberof' to a member entry, the update may fail
(invalid schema, db errors...).
The error is reported at upper level. But in case of MODRDN
the error is lost in memberof_moddn_attr_list where returned
code of memberof_modop_one_replace_r is not tested
Fix Description:
Report a failure in memberof_moddn_attr_list as soon as
memberof_modop_one_replace_r fails
https://pagure.io/389-ds-base/issue/49985
Reviewed by: Simon Pichugi, William Brown
Platforms tested: F27
Flag Day: no
Doc impact: no
---
.../suites/memberof_plugin/regression_test.py | 858 +++++++++++++++++++++
ldap/servers/plugins/memberof/memberof.c | 4 +-
2 files changed, 860 insertions(+), 2 deletions(-)
diff --git a/dirsrvtests/tests/suites/memberof_plugin/regression_test.py b/dirsrvtests/tests/suites/memberof_plugin/regression_test.py
new file mode 100644
index 0000000..2b0c4ae
--- /dev/null
+++ b/dirsrvtests/tests/suites/memberof_plugin/regression_test.py
@@ -0,0 +1,858 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2017 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
+import logging
+import pytest
+import os
+import time
+import ldap
+import subprocess
+from random import sample
+from lib389.utils import ds_is_older, ensure_list_bytes, ensure_bytes, ensure_str
+from lib389.topologies import topology_m1h1c1 as topo, topology_st, topology_m2 as topo_m2
+from lib389._constants import *
+from lib389.plugins import MemberOfPlugin
+from lib389 import agreement, Entry
+from lib389.idm.user import UserAccount, UserAccounts, TEST_USER_PROPERTIES
+from lib389.idm.group import Groups, Group
+from lib389.replica import ReplicationManager
+from lib389.tasks import *
+from lib389.idm.nscontainer import nsContainers
+
+
+# Skip on older versions
+pytestmark = pytest.mark.skipif(ds_is_older('1.3.7'), reason="Not implemented")
+
+USER_CN = 'user_'
+GROUP_CN = 'group1'
+DEBUGGING = os.getenv('DEBUGGING', False)
+SUBTREE_1 = 'cn=sub1,%s' % SUFFIX
+SUBTREE_2 = 'cn=sub2,%s' % SUFFIX
+
+
+if DEBUGGING:
+ logging.getLogger(__name__).setLevel(logging.DEBUG)
+else:
+ logging.getLogger(__name__).setLevel(logging.INFO)
+log = logging.getLogger(__name__)
+
+
+def add_users(topo_m2, users_num, suffix):
+ """Add users to the default suffix
+ Return the list of added user DNs.
+ """
+ users_list = []
+ users = UserAccounts(topo_m2.ms["master1"], suffix, rdn=None)
+ log.info('Adding %d users' % users_num)
+ for num in sample(range(1000), users_num):
+ num_ran = int(round(num))
+ USER_NAME = 'test%05d' % num_ran
+ user = users.create(properties={
+ 'uid': USER_NAME,
+ 'sn': USER_NAME,
+ 'cn': USER_NAME,
+ 'uidNumber': '%s' % num_ran,
+ 'gidNumber': '%s' % num_ran,
+ 'homeDirectory': '/home/%s' % USER_NAME,
+ 'mail': '%s(a)redhat.com' % USER_NAME,
+ 'userpassword': 'pass%s' % num_ran,
+ })
+ users_list.append(user)
+ return users_list
+
+
+def config_memberof(server):
+ # Configure fractional to prevent total init to send memberof
+ memberof = MemberOfPlugin(server)
+ memberof.enable()
+ memberof.set_autoaddoc('nsMemberOf')
+ server.restart()
+ ents = server.agreement.list(suffix=DEFAULT_SUFFIX)
+ for ent in ents:
+ log.info('update %s to add nsDS5ReplicatedAttributeListTotal' % ent.dn)
+ server.agreement.setProperties(agmnt_dn=ents[0].dn,
+ properties={RA_FRAC_EXCLUDE:'(objectclass=*) $ EXCLUDE memberOf',
+ RA_FRAC_EXCLUDE_TOTAL_UPDATE:'(objectclass=*) $ EXCLUDE '})
+
+
+def send_updates_now(server):
+ ents = server.agreement.list(suffix=DEFAULT_SUFFIX)
+ for ent in ents:
+ server.agreement.pause(ent.dn)
+ server.agreement.resume(ent.dn)
+
+
+def _find_memberof(server, member_dn, group_dn):
+ #To get the specific server's (M1, C1 and H1) user and group
+ user = UserAccount(server, member_dn)
+ assert user.exists()
+ group = Group(server, group_dn)
+ assert group.exists()
+
+ #test that the user entry should have memberof attribute with sepecified group dn value
+ assert group._dn in user.get_attr_vals_utf8('memberOf')
+
+
+(a)pytest.mark.bz1352121
+def test_memberof_with_repl(topo):
+ """Test that we allowed to enable MemberOf plugin in dedicated consumer
+
+ :id: 60c11636-55a1-4704-9e09-2c6bcc828de4
+ :setup: 1 Master - 1 Hub - 1 Consumer
+ :steps:
+ 1. Configure replication to EXCLUDE memberof
+ 2. Enable memberof plugin
+ 3. Create users/groups
+ 4. Make user_0 member of group_0
+ 5. Checks that user_0 is memberof group_0 on M,H,C
+ 6. Make group_0 member of group_1 (nest group)
+ 7. Checks that user_0 is memberof group_0 and group_1 on M,H,C
+ 8. Check group_0 is memberof group_1 on M,H,C
+ 9. Remove group_0 from group_1
+ 10. Check group_0 and user_0 are NOT memberof group_1 on M,H,C
+ 11. Remove user_0 from group_0
+ 12. Check user_0 is not memberof group_0 and group_1 on M,H,C
+ 13. Disable memberof on C
+ 14. make user_0 member of group_1
+ 15. Checks that user_0 is memberof group_0 on M,H but not on C
+ 16. Enable memberof on C
+ 17. Checks that user_0 is memberof group_0 on M,H but not on C
+ 18. Run memberof fixup task
+ 19. Checks that user_0 is memberof group_0 on M,H,C
+ :expectedresults:
+ 1. Configuration should be successful
+ 2. Plugin should be enabled
+ 3. Users and groups should be created
+ 4. user_0 should be member of group_0
+ 5. user_0 should be memberof group_0 on M,H,C
+ 6. group_0 should be member of group_1
+ 7. user_0 should be memberof group_0 and group_1 on M,H,C
+ 8. group_0 should be memberof group_1 on M,H,C
+ 9. group_0 from group_1 removal should be successful
+ 10. group_0 and user_0 should not be memberof group_1 on M,H,C
+ 11. user_0 from group_0 remove should be successful
+ 12. user_0 should not be memberof group_0 and group_1 on M,H,C
+ 13. memberof should be disabled on C
+ 14. user_0 should be member of group_1
+ 15. user_0 should be memberof group_0 on M,H and should not on C
+ 16. Enable memberof on C should be successful
+ 17. user_0 should be memberof group_0 on M,H should not on C
+ 18. memberof fixup task should be successful
+ 19. user_0 should be memberof group_0 on M,H,C
+ """
+
+ M1 = topo.ms["master1"]
+ H1 = topo.hs["hub1"]
+ C1 = topo.cs["consumer1"]
+
+ # Step 1 & 2
+ M1.config.enable_log('audit')
+ config_memberof(M1)
+ M1.restart()
+
+ H1.config.enable_log('audit')
+ config_memberof(H1)
+ H1.restart()
+
+ C1.config.enable_log('audit')
+ config_memberof(C1)
+ C1.restart()
+
+ #Declare lists of users and groups
+ test_users = []
+ test_groups = []
+
+ # Step 3
+ #In for loop create users and add them in the user list
+ #it creates user_0 to user_9 (range is fun)
+ for i in range(10):
+ CN = '%s%d' % (USER_CN, i)
+ users = UserAccounts(M1, SUFFIX)
+ user_props = TEST_USER_PROPERTIES.copy()
+ user_props.update({'uid': CN, 'cn': CN, 'sn': '_%s' % CN})
+ testuser = users.create(properties=user_props)
+ time.sleep(2)
+ test_users.append(testuser)
+
+ #In for loop create groups and add them to the group list
+ #it creates group_0 to group_2 (range is fun)
+ for i in range(3):
+ CN = '%s%d' % (GROUP_CN, i)
+ groups = Groups(M1, SUFFIX)
+ testgroup = groups.create(properties={'cn' : CN})
+ time.sleep(2)
+ test_groups.append(testgroup)
+
+ # Step 4
+ #Now start testing by adding differnt user to differn group
+ if not ds_is_older('1.3.7'):
+ test_groups[0].remove('objectClass', 'nsMemberOf')
+
+ member_dn = test_users[0].dn
+ grp0_dn = test_groups[0].dn
+ grp1_dn = test_groups[1].dn
+
+ test_groups[0].add_member(member_dn)
+ time.sleep(5)
+
+ # Step 5
+ for i in [M1, H1, C1]:
+ _find_memberof(i, member_dn, grp0_dn)
+
+ # Step 6
+ test_groups[1].add_member(test_groups[0].dn)
+ time.sleep(5)
+
+ # Step 7
+ for i in [grp0_dn, grp1_dn]:
+ for inst in [M1, H1, C1]:
+ _find_memberof(inst, member_dn, i)
+
+ # Step 8
+ for i in [M1, H1, C1]:
+ _find_memberof(i, grp0_dn, grp1_dn)
+
+ # Step 9
+ test_groups[1].remove_member(test_groups[0].dn)
+ time.sleep(5)
+
+ # Step 10
+ # For negative testcase, we are using assertionerror
+ for inst in [M1, H1, C1]:
+ for i in [grp0_dn, member_dn]:
+ with pytest.raises(AssertionError):
+ _find_memberof(inst, i, grp1_dn)
+
+ # Step 11
+ test_groups[0].remove_member(member_dn)
+ time.sleep(5)
+
+ # Step 12
+ for inst in [M1, H1, C1]:
+ for grp in [grp0_dn, grp1_dn]:
+ with pytest.raises(AssertionError):
+ _find_memberof(inst, member_dn, grp)
+
+ # Step 13
+ C1.plugins.disable(name=PLUGIN_MEMBER_OF)
+ C1.restart()
+
+ # Step 14
+ test_groups[0].add_member(member_dn)
+ time.sleep(5)
+
+ # Step 15
+ for i in [M1, H1]:
+ _find_memberof(i, member_dn, grp0_dn)
+ with pytest.raises(AssertionError):
+ _find_memberof(C1, member_dn, grp0_dn)
+
+ # Step 16
+ memberof = MemberOfPlugin(C1)
+ memberof.enable()
+ C1.restart()
+
+ # Step 17
+ for i in [M1, H1]:
+ _find_memberof(i, member_dn, grp0_dn)
+ with pytest.raises(AssertionError):
+ _find_memberof(C1, member_dn, grp0_dn)
+
+ # Step 18
+ memberof.fixup(SUFFIX)
+ time.sleep(5)
+
+ # Step 19
+ for i in [M1, H1, C1]:
+ _find_memberof(i, member_dn, grp0_dn)
+
+
+(a)pytest.mark.skipif(ds_is_older('1.3.7'), reason="Not implemented")
+def test_scheme_violation_errors_logged(topo_m2):
+ """Check that ERR messages are verbose enough, if a member entry
+ doesn't have the appropriate objectclass to support 'memberof' attribute
+
+ :id: e2af0aaa-447e-4e85-a5ce-57ae66260d0b
+ :setup: Standalone instance
+ :steps:
+ 1. Enable memberofPlugin and set autoaddoc to nsMemberOf
+ 2. Restart the instance
+ 3. Add a user without nsMemberOf attribute
+ 4. Create a group and add the user to the group
+ 5. Check that user has memberOf attribute
+ 6. Check the error log for ".*oc_check_allowed_sv.*USER_DN.*memberOf.*not allowed.*"
+ and ".*schema violation caught - repair operation.*" patterns
+ :expectedresults:
+ 1. Should be successful
+ 2. Should be successful
+ 3. Should be successful
+ 4. Should be successful
+ 5. User should have the attribute
+ 6. Errors should be logged
+ """
+
+ inst = topo_m2.ms["master1"]
+ memberof = MemberOfPlugin(inst)
+ memberof.enable()
+ memberof.set_autoaddoc('nsMemberOf')
+ inst.restart()
+
+ users = UserAccounts(inst, SUFFIX)
+ user_props = TEST_USER_PROPERTIES.copy()
+ user_props.update({'uid': USER_CN, 'cn': USER_CN, 'sn': USER_CN})
+ testuser = users.create(properties=user_props)
+ testuser.remove('objectclass', 'nsMemberOf')
+
+ groups = Groups(inst, SUFFIX)
+ testgroup = groups.create(properties={'cn': GROUP_CN})
+
+ testgroup.add('member', testuser.dn)
+
+ user_memberof_attr = testuser.get_attr_val_utf8('memberof')
+ assert user_memberof_attr
+ log.info('memberOf attr value - {}'.format(user_memberof_attr))
+
+ pattern = ".*oc_check_allowed_sv.*{}.*memberOf.*not allowed.*".format(testuser.dn)
+ log.info("pattern = %s" % pattern)
+ assert inst.ds_error_log.match(pattern)
+
+ pattern = ".*schema violation caught - repair operation.*"
+ assert inst.ds_error_log.match(pattern)
+
+
+(a)pytest.mark.bz1192099
+def test_memberof_with_changelog_reset(topo_m2):
+ """Test that replication does not break, after DS stop-start, due to changelog reset
+
+ :id: 60c11636-55a1-4704-9e09-2c6bcc828de4
+ :setup: 2 Masters
+ :steps:
+ 1. On M1 and M2, Enable memberof
+ 2. On M1, add 999 entries allowing memberof
+ 3. On M1, add a group with these 999 entries as members
+ 4. Stop M1 in between,
+ when add the group memerof is called and before it is finished the
+ add, so step 4 should be executed after memberof has started and
+ before the add has finished
+ 5. Check that replication is working fine
+ :expectedresults:
+ 1. memberof should be enabled
+ 2. Entries should be added
+ 3. Add operation should start
+ 4. M1 should be stopped
+ 5. Replication should be working fine
+ """
+ m1 = topo_m2.ms["master1"]
+ m2 = topo_m2.ms["master2"]
+
+ log.info("Configure memberof on M1 and M2")
+ memberof = MemberOfPlugin(m1)
+ memberof.enable()
+ memberof.set_autoaddoc('nsMemberOf')
+ m1.restart()
+
+ memberof = MemberOfPlugin(m2)
+ memberof.enable()
+ memberof.set_autoaddoc('nsMemberOf')
+ m2.restart()
+
+ log.info("On M1, add 999 test entries allowing memberof")
+ users_list = add_users(topo_m2, 999, DEFAULT_SUFFIX)
+
+ log.info("On M1, add a group with these 999 entries as members")
+ dic_of_attributes = {'cn': ensure_bytes('testgroup'),
+ 'objectclass': ensure_list_bytes(['top', 'groupOfNames'])}
+
+ for user in users_list:
+ dic_of_attributes.setdefault('member',[])
+ dic_of_attributes['member'].append(user.dn)
+
+ log.info('Adding the test group using async function')
+ groupdn = 'cn=testgroup,%s' % DEFAULT_SUFFIX
+ m1.add(Entry((groupdn, dic_of_attributes)))
+
+ #shutdown the server in-between adding the group
+ m1.stop()
+
+ #start the server
+ m1.start()
+
+ log.info("Check the log messages for error")
+ error_msg = "ERR - NSMMReplicationPlugin - ruv_compare_ruv"
+ assert not m1.ds_error_log.match(error_msg)
+
+ log.info("Check that the replication is working fine both ways, M1 <-> M2")
+ repl = ReplicationManager(DEFAULT_SUFFIX)
+ repl.test_replication_topology(topo_m2)
+
+
+def add_container(inst, dn, name, sleep=False):
+ """Creates container entry"""
+ conts = nsContainers(inst, dn)
+ cont = conts.create(properties={'cn': name})
+ if sleep:
+ time.sleep(1)
+ return cont
+
+
+def add_member(server, cn, subtree):
+ dn = subtree
+ users = UserAccounts(server, dn, rdn=None)
+ users.create(properties={'uid': 'test_%s' % cn,
+ 'cn': "%s" % cn,
+ 'sn': 'SN',
+ 'description': 'member',
+ 'uidNumber': '1000',
+ 'gidNumber': '2000',
+ 'homeDirectory': '/home/testuser'
+ })
+
+
+def add_group(server, cn, subtree):
+ group = Groups(server, subtree, rdn=None)
+ group.create(properties={'cn': "%s" % cn,
+ 'member': ['uid=test_m1,%s' % SUBTREE_1, 'uid=test_m2,%s' % SUBTREE_1],
+ 'description': 'group'})
+
+
+def rename_entry(server, cn, from_subtree, to_subtree):
+ dn = '%s,%s' % (cn, from_subtree)
+ nrdn = '%s-new' % cn
+ log.fatal('Renaming user (%s): new %s' % (dn, nrdn))
+ server.rename_s(dn, nrdn, newsuperior=to_subtree, delold=0)
+
+
+def _find_memberof(server, user_dn=None, group_dn=None, find_result=True):
+ assert (server)
+ assert (user_dn)
+ assert (group_dn)
+ ent = server.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ found = False
+ if ent.hasAttr('memberof'):
+
+ for val in ent.getValues('memberof'):
+ server.log.info("!!!!!!! %s: memberof->%s" % (user_dn, val))
+ if ensure_str(val) == group_dn:
+ found = True
+ break
+
+ if find_result:
+ assert found
+ else:
+ assert (not found)
+
+
+(a)pytest.mark.ds49161
+def test_memberof_group(topology_st):
+ """Test memberof does not fail if group is moved into scope
+
+ :id: 552850aa-agc3-473e-9d39-aae812b46f11
+
+ :setup: Single instance
+
+ :steps:
+ 1. Enable memberof plugin and set memberofentryscope
+ 2. Restart the server
+ 3. Add test sub-suffixes
+ 4. Add test users
+ 5. Add test groups
+ 6. Check for memberof attribute added to the test users
+ 7. Rename the group entry
+ 8. Check the new name is reflected in memberof attribute of user
+
+ :expectedresults:
+ 1. memberof plugin should be enabled and memberofentryscope should be set
+ 2. Server should be restarted
+ 3. Sub-suffixes should be added
+ 4. Test users should be added
+ 5. Test groups should be added
+ 6. memberof attribute should be present in the test users
+ 7. Group entry should be renamed
+ 8. New group name should be present in memberof attribute of user
+ """
+
+ inst = topology_st.standalone
+ log.info('Enable memberof plugin and set the scope as cn=sub1,dc=example,dc=com')
+ memberof = MemberOfPlugin(inst)
+ memberof.enable()
+ memberof.replace('memberOfEntryScope', SUBTREE_1)
+ inst.restart()
+
+ add_container(inst, SUFFIX, 'sub1')
+ add_container(inst, SUFFIX, 'sub2')
+ add_member(inst, 'm1', SUBTREE_1)
+ add_member(inst, 'm2', SUBTREE_1)
+ add_group(inst, 'g1', SUBTREE_1)
+ add_group(inst, 'g2', SUBTREE_2)
+
+ # _check_memberof
+ dn1 = '%s,%s' % ('uid=test_m1', SUBTREE_1)
+ dn2 = '%s,%s' % ('uid=test_m2', SUBTREE_1)
+ g1 = '%s,%s' % ('cn=g1', SUBTREE_1)
+ g2 = '%s,%s' % ('cn=g2', SUBTREE_2)
+ _find_memberof(inst, dn1, g1, True)
+ _find_memberof(inst, dn2, g1, True)
+ _find_memberof(inst, dn1, g2, False)
+ _find_memberof(inst, dn2, g2, False)
+
+ rename_entry(inst, 'cn=g2', SUBTREE_2, SUBTREE_1)
+
+ g2n = '%s,%s' % ('cn=g2-new', SUBTREE_1)
+ _find_memberof(inst, dn1, g1, True)
+ _find_memberof(inst, dn2, g1, True)
+ _find_memberof(inst, dn1, g2n, True)
+ _find_memberof(inst, dn2, g2n, True)
+
+def _config_memberof_entrycache_on_modrdn_failure(server):
+
+ server.plugins.enable(name=PLUGIN_MEMBER_OF)
+ peoplebase = 'ou=people,%s' % SUFFIX
+ MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
+ server.modify_s(MEMBEROF_PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberOfAllBackends', b'on'),
+ (ldap.MOD_REPLACE, 'memberOfEntryScope', peoplebase.encode()),
+ (ldap.MOD_REPLACE, 'memberOfAutoAddOC', b'nsMemberOf')])
+
+def _disable_auto_oc_memberof(server):
+ MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
+ server.modify_s(MEMBEROF_PLUGIN_DN,
+ [(ldap.MOD_REPLACE, 'memberOfAutoAddOC', b'nsContainer')])
+
+(a)pytest.mark.ds49967
+def test_entrycache_on_modrdn_failure(topology_st):
+ """This test checks that when a modrdn fails, the destination entry is not returned by a search
+ This could happen in case the destination entry remains in the entry cache
+
+ :id: a4d8ac0b-2448-406a-9dc2-5a72851e30b6
+ :setup: Standalone Instance
+ :steps:
+ 1. configure memberof to only scope ou=people,SUFFIX
+ 2. Creates 10 users
+ 3. Create groups0 (in peoplebase) that contain user0 and user1
+ 4. Check user0 and user1 have memberof=group0.dn
+ 5. Create group1 (OUT peoplebase) that contain user0 and user1
+ 6. Check user0 and user1 have NOT memberof=group1.dn
+ 7. Move group1 IN peoplebase and check users0 and user1 HAVE memberof=group1.dn
+ 8. Create group2 (OUT peoplebase) that contain user2 and user3. Group2 contains a specific description value
+ 9. Check user2 and user3 have NOT memberof=group2.dn
+ 10. configure memberof so that added objectclass does not allow 'memberof' attribute
+ 11. Move group2 IN peoplebase and check move failed OPERATIONS_ERROR (because memberof failed)
+ 12. Search all groups and check that the group, having the specific description value,
+ has the original DN of group2.dn
+ :expectedresults:
+ 1. should succeed
+ 2. should succeed
+ 3. should succeed
+ 4. should succeed
+ 5. should succeed
+ 6. should succeed
+ 7. should succeed
+ 8. should succeed
+ 9. should succeed
+ 10. should succeed
+ 11. should fail OPERATION_ERROR because memberof plugin fails to add 'memberof' to members.
+ 12. should succeed
+
+ """
+
+ # only scopes peoplebase
+ _config_memberof_entrycache_on_modrdn_failure(topology_st.standalone)
+ topology_st.standalone.restart(timeout=10)
+
+ # create 10 users
+ peoplebase = 'ou=people,%s' % SUFFIX
+ for i in range(10):
+ cn = 'user%d' % i
+ dn = 'cn=%s,%s' % (cn, peoplebase)
+ log.fatal('Adding user (%s): ' % dn)
+ topology_st.standalone.add_s(Entry((dn, {'objectclass': ['top', 'person'],
+ 'sn': 'user_%s' % cn,
+ 'description': 'add on standalone'})))
+
+ # Check that members of group0 (in the scope) have 'memberof
+ group0_dn = 'cn=group_in0,%s' % peoplebase
+ topology_st.standalone.add_s(Entry((group0_dn, {'objectclass': ['top', 'groupofnames'],
+ 'member': [
+ 'cn=user0,%s' % peoplebase,
+ 'cn=user1,%s' % peoplebase,
+ ],
+ 'description': 'mygroup'})))
+
+ # Check the those entries have memberof with group0
+ for i in range(2):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ assert ent.hasAttr('memberof')
+ found = False
+ for val in ent.getValues('memberof'):
+ topology_st.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, group0_dn.encode().lower()))
+ if val.lower() == group0_dn.encode().lower():
+ found = True
+ break
+ assert found
+
+ # Create a group1 out of the scope
+ group1_dn = 'cn=group_out1,%s' % SUFFIX
+ topology_st.standalone.add_s(Entry((group1_dn, {'objectclass': ['top', 'groupofnames'],
+ 'member': [
+ 'cn=user0,%s' % peoplebase,
+ 'cn=user1,%s' % peoplebase,
+ ],
+ 'description': 'mygroup'})))
+
+ # Check the those entries have not memberof with group1
+ for i in range(2):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ assert ent.hasAttr('memberof')
+ found = False
+ for val in ent.getValues('memberof'):
+ topology_st.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, group1_dn.encode().lower()))
+ if val.lower() == group1_dn.encode().lower():
+ found = True
+ break
+ assert not found
+
+ # move group1 into the scope and check user0 and user1 are memberof group1
+ topology_st.standalone.rename_s(group1_dn, 'cn=group_in1', newsuperior=peoplebase, delold=0)
+ new_group1_dn = 'cn=group_in1,%s' % peoplebase
+ for i in range(2):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ assert ent.hasAttr('memberof')
+ found = False
+ for val in ent.getValues('memberof'):
+ topology_st.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, new_group1_dn.encode().lower()))
+ if val.lower() == new_group1_dn.encode().lower():
+ found = True
+ break
+ assert found
+
+ # Create a group2 out of the scope with a SPECIFIC description value
+ entry_description = "this is to check that the entry having this description has the appropriate DN"
+ group2_dn = 'cn=group_out2,%s' % SUFFIX
+ topology_st.standalone.add_s(Entry((group2_dn, {'objectclass': ['top', 'groupofnames'],
+ 'member': [
+ 'cn=user2,%s' % peoplebase,
+ 'cn=user3,%s' % peoplebase,
+ ],
+ 'description': entry_description})))
+
+ # Check the those entries have not memberof with group2
+ for i in (2, 3):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ assert not ent.hasAttr('memberof')
+
+ # memberof will not add the missing objectclass
+ _disable_auto_oc_memberof(topology_st.standalone)
+ topology_st.standalone.restart(timeout=10)
+
+ # move group2 into the scope and check it fails
+ try:
+ topology_st.standalone.rename_s(group2_dn, 'cn=group_in2', newsuperior=peoplebase, delold=0)
+ topology_st.standalone.log.info("This is unexpected, modrdn should fail as the member entry have not the appropriate objectclass")
+ assert False
+ except ldap.OPERATIONS_ERROR:
+ pass
+
+ # retrieve the entry having the specific description value
+ # check that the entry DN is the original group2 DN
+ ents = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, '(cn=gr*)')
+ found = False
+ for ent in ents:
+ topology_st.standalone.log.info("retrieve: %s with desc=%s" % (ent.dn, ent.getValue('description')))
+ if ent.getValue('description') == entry_description.encode():
+ found = True
+ assert ent.dn == group2_dn
+ assert found
+
+def _config_memberof_silent_memberof_failure(server):
+ _config_memberof_entrycache_on_modrdn_failure(server)
+
+def test_silent_memberof_failure(topology_st):
+ """This test checks that if during a MODRDN, the memberof plugin fails
+ then MODRDN also fails
+
+ :id: 095aee01-581c-43dd-a241-71f9631a18bb
+ :setup: Standalone Instance
+ :steps:
+ 1. configure memberof to only scope ou=people,SUFFIX
+ 2. Do some cleanup and Creates 10 users
+ 3. Create groups0 (IN peoplebase) that contain user0 and user1
+ 4. Check user0 and user1 have memberof=group0.dn
+ 5. Create group1 (OUT peoplebase) that contain user0 and user1
+ 6. Check user0 and user1 have NOT memberof=group1.dn
+ 7. Move group1 IN peoplebase and check users0 and user1 HAVE memberof=group1.dn
+ 8. Create group2 (OUT peoplebase) that contain user2 and user3.
+ 9. Check user2 and user3 have NOT memberof=group2.dn
+ 10. configure memberof so that added objectclass does not allow 'memberof' attribute
+ 11. Move group2 IN peoplebase and check move failed OPERATIONS_ERROR (because memberof failed)
+ 12. Check user2 and user3 have NOT memberof=group2.dn
+ 13. ADD group3 (IN peoplebase) with user4 and user5 members and check add failed OPERATIONS_ERROR (because memberof failed)
+ 14. Check user4 and user5 have NOT memberof=group2.dn
+ :expectedresults:
+ 1. should succeed
+ 2. should succeed
+ 3. should succeed
+ 4. should succeed
+ 5. should succeed
+ 6. should succeed
+ 7. should succeed
+ 8. should succeed
+ 9. should succeed
+ 10. should succeed
+ 11. should fail OPERATION_ERROR because memberof plugin fails to add 'memberof' to members.
+ 12. should succeed
+ 14. should fail OPERATION_ERROR because memberof plugin fails to add 'memberof' to members
+ 14. should succeed
+ """
+ # only scopes peoplebase
+ _config_memberof_silent_memberof_failure(topology_st.standalone)
+ topology_st.standalone.restart(timeout=10)
+
+ # first do some cleanup
+ peoplebase = 'ou=people,%s' % SUFFIX
+ for i in range(10):
+ cn = 'user%d' % i
+ dn = 'cn=%s,%s' % (cn, peoplebase)
+ topology_st.standalone.delete_s(dn)
+ topology_st.standalone.delete_s('cn=group_in0,%s' % peoplebase)
+ topology_st.standalone.delete_s('cn=group_in1,%s' % peoplebase)
+ topology_st.standalone.delete_s('cn=group_out2,%s' % SUFFIX)
+
+ # create 10 users
+ for i in range(10):
+ cn = 'user%d' % i
+ dn = 'cn=%s,%s' % (cn, peoplebase)
+ log.fatal('Adding user (%s): ' % dn)
+ topology_st.standalone.add_s(Entry((dn, {'objectclass': ['top', 'person'],
+ 'sn': 'user_%s' % cn,
+ 'description': 'add on standalone'})))
+
+ # Check that members of group0 (in the scope) have 'memberof
+ group0_dn = 'cn=group_in0,%s' % peoplebase
+ topology_st.standalone.add_s(Entry((group0_dn, {'objectclass': ['top', 'groupofnames'],
+ 'member': [
+ 'cn=user0,%s' % peoplebase,
+ 'cn=user1,%s' % peoplebase,
+ ],
+ 'description': 'mygroup'})))
+
+ # Check the those entries have memberof with group0
+ for i in range(2):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ assert ent.hasAttr('memberof')
+ found = False
+ for val in ent.getValues('memberof'):
+ topology_st.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, group0_dn.encode().lower()))
+ if val.lower() == group0_dn.encode().lower():
+ found = True
+ break
+ assert found
+
+ # Create a group1 out of the scope
+ group1_dn = 'cn=group_out1,%s' % SUFFIX
+ topology_st.standalone.add_s(Entry((group1_dn, {'objectclass': ['top', 'groupofnames'],
+ 'member': [
+ 'cn=user0,%s' % peoplebase,
+ 'cn=user1,%s' % peoplebase,
+ ],
+ 'description': 'mygroup'})))
+
+ # Check the those entries have not memberof with group1
+ for i in range(2):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ assert ent.hasAttr('memberof')
+ found = False
+ for val in ent.getValues('memberof'):
+ topology_st.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, group1_dn.encode().lower()))
+ if val.lower() == group1_dn.encode().lower():
+ found = True
+ break
+ assert not found
+
+ # move group1 into the scope and check user0 and user1 are memberof group1
+ topology_st.standalone.rename_s(group1_dn, 'cn=group_in1', newsuperior=peoplebase, delold=0)
+ new_group1_dn = 'cn=group_in1,%s' % peoplebase
+ for i in range(2):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ assert ent.hasAttr('memberof')
+ found = False
+ for val in ent.getValues('memberof'):
+ topology_st.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, new_group1_dn.encode().lower()))
+ if val.lower() == new_group1_dn.encode().lower():
+ found = True
+ break
+ assert found
+
+ # Create a group2 out of the scope
+ group2_dn = 'cn=group_out2,%s' % SUFFIX
+ topology_st.standalone.add_s(Entry((group2_dn, {'objectclass': ['top', 'groupofnames'],
+ 'member': [
+ 'cn=user2,%s' % peoplebase,
+ 'cn=user3,%s' % peoplebase,
+ ],
+ 'description': 'mygroup'})))
+
+ # Check the those entries have not memberof with group2
+ for i in (2, 3):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ assert not ent.hasAttr('memberof')
+
+ # memberof will not add the missing objectclass
+ _disable_auto_oc_memberof(topology_st.standalone)
+ topology_st.standalone.restart(timeout=10)
+
+ # move group2 into the scope and check it fails
+ try:
+ topology_st.standalone.rename_s(group2_dn, 'cn=group_in2', newsuperior=peoplebase, delold=0)
+ topology_st.standalone.log.info("This is unexpected, modrdn should fail as the member entry have not the appropriate objectclass")
+ assert False
+ except ldap.OPERATIONS_ERROR:
+ pass
+
+ # Check the those entries have not memberof
+ for i in (2, 3):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ topology_st.standalone.log.info("Should assert %s has memberof is %s" % (user_dn, ent.hasAttr('memberof')))
+ assert not ent.hasAttr('memberof')
+
+ # Create a group3 in the scope
+ group3_dn = 'cn=group3_in,%s' % peoplebase
+ try:
+ topology_st.standalone.add_s(Entry((group3_dn, {'objectclass': ['top', 'groupofnames'],
+ 'member': [
+ 'cn=user4,%s' % peoplebase,
+ 'cn=user5,%s' % peoplebase,
+ ],
+ 'description': 'mygroup'})))
+ topology_st.standalone.log.info("This is unexpected, ADD should fail as the member entry have not the appropriate objectclass")
+ assert False
+ except ldap.OBJECT_CLASS_VIOLATION:
+ pass
+ except ldap.OPERATIONS_ERROR:
+ pass
+
+ # Check the those entries have not memberof
+ for i in (4, 5):
+ user_dn = 'cn=user%d,%s' % (i, peoplebase)
+ ent = topology_st.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
+ topology_st.standalone.log.info("Should assert %s has memberof is %s" % (user_dn, ent.hasAttr('memberof')))
+ assert not ent.hasAttr('memberof')
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
+
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
index f4d45ed..329a845 100644
--- a/ldap/servers/plugins/memberof/memberof.c
+++ b/ldap/servers/plugins/memberof/memberof.c
@@ -1972,7 +1972,7 @@ memberof_moddn_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_DN *pre
int hint = slapi_attr_first_value(attr, &val);
Slapi_DN *sdn = slapi_sdn_new();
- while (val) {
+ while (val && (rc == 0)) {
char *dn_str = 0;
struct berval *bv = (struct berval *)slapi_value_get_berval(val);
@@ -1995,7 +1995,7 @@ memberof_moddn_attr_list(Slapi_PBlock *pb, MemberOfConfig *config, Slapi_DN *pre
strncpy(dn_str, bv->bv_val, (size_t)bv->bv_len);
slapi_sdn_set_normdn_byref(sdn, dn_str); /* dn_str is normalized */
- memberof_modop_one_replace_r(pb, config, LDAP_MOD_REPLACE,
+ rc = memberof_modop_one_replace_r(pb, config, LDAP_MOD_REPLACE,
post_sdn, pre_sdn, post_sdn, sdn, 0);
hint = slapi_attr_next_value(attr, hint, &val);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch 389-ds-base-1.3.8 updated: Ticket 50078 - cannot add cenotaph in read only consumer
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
lkrispen pushed a commit to branch 389-ds-base-1.3.8
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.8 by this push:
new 94e6f12 Ticket 50078 - cannot add cenotaph in read only consumer
94e6f12 is described below
commit 94e6f128f5e8cf39e318d01ed93f159c127153cc
Author: Ludwig Krispenz <lkrispen(a)redhat.com>
AuthorDate: Tue Dec 11 11:06:44 2018 +0100
Ticket 50078 - cannot add cenotaph in read only consumer
Bug: For modrdn operations a cenotaph entry is created to be used in later conflict
resolution procedures, this is done by an internal add operation and
fails on hubs and consumers
Fix: Add the "bypass referral" flag to the internal add operation to allow it
Reviewed by: Thierry, thanks
---
dirsrvtests/tests/tickets/ticket50078_test.py | 68 +++++++++++++++++++++++++++
ldap/servers/plugins/replication/urp.c | 2 +-
2 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/dirsrvtests/tests/tickets/ticket50078_test.py b/dirsrvtests/tests/tickets/ticket50078_test.py
new file mode 100644
index 0000000..3f6c5ec
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket50078_test.py
@@ -0,0 +1,68 @@
+import pytest
+from lib389.utils import *
+from lib389.topologies import topology_m1h1c1
+from lib389.idm.user import UserAccounts
+
+from lib389._constants import (DEFAULT_SUFFIX, REPLICA_RUV_FILTER, defaultProperties,
+ REPLICATION_BIND_DN, REPLICATION_BIND_PW, REPLICATION_BIND_METHOD,
+ REPLICATION_TRANSPORT, SUFFIX, RA_NAME, RA_BINDDN, RA_BINDPW,
+ RA_METHOD, RA_TRANSPORT_PROT, SUFFIX)
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+TEST_USER = "test_user"
+
+def test_ticket50078(topology_m1h1c1):
+ """
+ Test that for a MODRDN operation the cenotaph entry is created on
+ a hub or consumer.
+ """
+
+ M1 = topology_m1h1c1.ms["master1"]
+ H1 = topology_m1h1c1.hs["hub1"]
+ C1 = topology_m1h1c1.cs["consumer1"]
+ #
+ # Test replication is working
+ #
+ if M1.testReplication(DEFAULT_SUFFIX, topology_m1h1c1.cs["consumer1"]):
+ log.info('Replication is working.')
+ else:
+ log.fatal('Replication is not working.')
+ assert False
+
+ ua = UserAccounts(M1, DEFAULT_SUFFIX)
+ ua.create(properties={
+ 'uid': "%s%d" % (TEST_USER, 1),
+ 'cn' : "%s%d" % (TEST_USER, 1),
+ 'sn' : 'user',
+ 'uidNumber' : '1000',
+ 'gidNumber' : '2000',
+ 'homeDirectory' : '/home/testuser'
+ })
+
+ user = ua.get('%s1' % TEST_USER)
+ log.info(" Rename the test entry %s..." % user)
+ user.rename('uid=test_user_new')
+
+ # wait until replication is in sync
+ if M1.testReplication(DEFAULT_SUFFIX, topology_m1h1c1.cs["consumer1"]):
+ log.info('Replication is working.')
+ else:
+ log.fatal('Replication is not working.')
+ assert False
+
+ # check if cenotaph was created on hub and consumer
+ ents = H1.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, filterstr="(&(objectclass=nstombstone)(cenotaphid=*))")
+ assert len(ents) == 1
+
+ ents = C1.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, filterstr="(&(objectclass=nstombstone)(cenotaphid=*))")
+ assert len(ents) == 1
+
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
diff --git a/ldap/servers/plugins/replication/urp.c b/ldap/servers/plugins/replication/urp.c
index 11c5da7..37fe773 100644
--- a/ldap/servers/plugins/replication/urp.c
+++ b/ldap/servers/plugins/replication/urp.c
@@ -911,7 +911,7 @@ urp_fixup_add_cenotaph (Slapi_PBlock *pb, char *sessionid, CSN *opcsn)
cenotaph,
NULL,
repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
- OP_FLAG_REPL_FIXUP|OP_FLAG_NOOP|OP_FLAG_CENOTAPH_ENTRY);
+ OP_FLAG_REPL_FIXUP|OP_FLAG_NOOP|OP_FLAG_CENOTAPH_ENTRY|SLAPI_OP_FLAG_BYPASS_REFERRALS);
slapi_add_internal_pb(add_pb);
slapi_pblock_get(add_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months
[389-ds-base] branch 389-ds-base-1.3.9 updated: Ticket 50078 - cannot add cenotaph in read only consumer
by pagure@pagure.io
This is an automated email from the git hooks/post-receive script.
lkrispen pushed a commit to branch 389-ds-base-1.3.9
in repository 389-ds-base.
The following commit(s) were added to refs/heads/389-ds-base-1.3.9 by this push:
new 94378b9 Ticket 50078 - cannot add cenotaph in read only consumer
94378b9 is described below
commit 94378b9e05984e26936d3c70d309485368a2e39b
Author: Ludwig Krispenz <lkrispen(a)redhat.com>
AuthorDate: Tue Dec 11 11:06:44 2018 +0100
Ticket 50078 - cannot add cenotaph in read only consumer
Bug: For modrdn operations a cenotaph entry is created to be used in later conflict
resolution procedures, this is done by an internal add operation and
fails on hubs and consumers
Fix: Add the "bypass referral" flag to the internal add operation to allow it
Reviewed by: Thierry, thanks
---
dirsrvtests/tests/tickets/ticket50078_test.py | 68 +++++++++++++++++++++++++++
ldap/servers/plugins/replication/urp.c | 2 +-
2 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/dirsrvtests/tests/tickets/ticket50078_test.py b/dirsrvtests/tests/tickets/ticket50078_test.py
new file mode 100644
index 0000000..3f6c5ec
--- /dev/null
+++ b/dirsrvtests/tests/tickets/ticket50078_test.py
@@ -0,0 +1,68 @@
+import pytest
+from lib389.utils import *
+from lib389.topologies import topology_m1h1c1
+from lib389.idm.user import UserAccounts
+
+from lib389._constants import (DEFAULT_SUFFIX, REPLICA_RUV_FILTER, defaultProperties,
+ REPLICATION_BIND_DN, REPLICATION_BIND_PW, REPLICATION_BIND_METHOD,
+ REPLICATION_TRANSPORT, SUFFIX, RA_NAME, RA_BINDDN, RA_BINDPW,
+ RA_METHOD, RA_TRANSPORT_PROT, SUFFIX)
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+TEST_USER = "test_user"
+
+def test_ticket50078(topology_m1h1c1):
+ """
+ Test that for a MODRDN operation the cenotaph entry is created on
+ a hub or consumer.
+ """
+
+ M1 = topology_m1h1c1.ms["master1"]
+ H1 = topology_m1h1c1.hs["hub1"]
+ C1 = topology_m1h1c1.cs["consumer1"]
+ #
+ # Test replication is working
+ #
+ if M1.testReplication(DEFAULT_SUFFIX, topology_m1h1c1.cs["consumer1"]):
+ log.info('Replication is working.')
+ else:
+ log.fatal('Replication is not working.')
+ assert False
+
+ ua = UserAccounts(M1, DEFAULT_SUFFIX)
+ ua.create(properties={
+ 'uid': "%s%d" % (TEST_USER, 1),
+ 'cn' : "%s%d" % (TEST_USER, 1),
+ 'sn' : 'user',
+ 'uidNumber' : '1000',
+ 'gidNumber' : '2000',
+ 'homeDirectory' : '/home/testuser'
+ })
+
+ user = ua.get('%s1' % TEST_USER)
+ log.info(" Rename the test entry %s..." % user)
+ user.rename('uid=test_user_new')
+
+ # wait until replication is in sync
+ if M1.testReplication(DEFAULT_SUFFIX, topology_m1h1c1.cs["consumer1"]):
+ log.info('Replication is working.')
+ else:
+ log.fatal('Replication is not working.')
+ assert False
+
+ # check if cenotaph was created on hub and consumer
+ ents = H1.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, filterstr="(&(objectclass=nstombstone)(cenotaphid=*))")
+ assert len(ents) == 1
+
+ ents = C1.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, filterstr="(&(objectclass=nstombstone)(cenotaphid=*))")
+ assert len(ents) == 1
+
+
+
+if __name__ == '__main__':
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
diff --git a/ldap/servers/plugins/replication/urp.c b/ldap/servers/plugins/replication/urp.c
index 11c5da7..37fe773 100644
--- a/ldap/servers/plugins/replication/urp.c
+++ b/ldap/servers/plugins/replication/urp.c
@@ -911,7 +911,7 @@ urp_fixup_add_cenotaph (Slapi_PBlock *pb, char *sessionid, CSN *opcsn)
cenotaph,
NULL,
repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
- OP_FLAG_REPL_FIXUP|OP_FLAG_NOOP|OP_FLAG_CENOTAPH_ENTRY);
+ OP_FLAG_REPL_FIXUP|OP_FLAG_NOOP|OP_FLAG_CENOTAPH_ENTRY|SLAPI_OP_FLAG_BYPASS_REFERRALS);
slapi_add_internal_pb(add_pb);
slapi_pblock_get(add_pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
5 years, 3 months