From d14bf3e6fee13e8873160b1ea1418449e5b02df7 Mon Sep 17 00:00:00 2001
From: root <root@localhost.localdomain>
Date: Tue, 24 Apr 2012 17:40:35 -0400
Subject: [PATCH] Ticket #326 - MemberOf plugin should work on all backends

Bug Description:  By design the memberOf plugin would only look at the backend from
                  which the group belonged to.  This makes it impossible to maintain
                  users and groups from different suffixes.

Fix Description:  First I added a new config setting in the plugin to turn this on
                  and off.  Then we just look at all the backends when searching.

https://fedorahosted.org/389/ticket/326

Reviewed by: ?
---
 ldap/servers/plugins/memberof/memberof.c        |  233 ++++++++++++-----------
 ldap/servers/plugins/memberof/memberof.h        |    3 +
 ldap/servers/plugins/memberof/memberof_config.c |   33 +++-
 3 files changed, 160 insertions(+), 109 deletions(-)

diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
index 655054c..229b962 100644
--- a/ldap/servers/plugins/memberof/memberof.c
+++ b/ldap/servers/plugins/memberof/memberof.c
@@ -67,10 +67,8 @@
 #endif
 
 #include "slapi-plugin.h"
-
 #include "string.h"
 #include "nspr.h"
-
 #include "memberof.h"
 
 static Slapi_PluginDesc pdesc = { "memberof", VENDOR,
@@ -507,94 +505,101 @@ int memberof_del_dn_type_callback(Slapi_Entry *e, void *callback_data)
 }
 
 /*
- * Does a callback search of "type=dn" under the db suffix that "dn" is in.
- * If "dn" is a user, you'd want "type" to be "member".  If "dn" is a group,
- * you could want type to be either "member" or "memberOf" depending on the
- * case.
+ * Does a callback search of "type=dn" under the db suffix that "dn" is in,
+ * unless all_backends is set, then we look at all the backends.  If "dn"
+ * is a user, you'd want "type" to be "member".  If "dn" is a group, you
+ * could want type to be either "member" or "memberOf" depending on the case.
  */
 int memberof_call_foreach_dn(Slapi_PBlock *pb, char *dn,
 	char **types, plugin_search_entry_callback callback, void *callback_data)
 {
-	int rc = 0;
 	Slapi_PBlock *search_pb = slapi_pblock_new();
-	Slapi_Backend *be = 0;
-	Slapi_DN *sdn = 0;
-	Slapi_DN *base_sdn = 0;
-	char *filter_str = 0;
-	int num_types = 0;
+	Slapi_DN *base_sdn = NULL;
+	Slapi_Backend *be = NULL;
+	Slapi_DN *sdn = NULL;
+	char *filter_str = NULL;
+	char *cookie = NULL;
+	int all_backends = memberof_config_get_all_backends();
 	int types_name_len = 0;
-	int dn_len = 0;
+	int num_types = 0;
+	int dn_len = strlen(dn);
+	int rc = 0;
 	int i = 0;
 
-	/* get the base dn for the backend we are in
-	   (we don't support having members and groups in
-           different backends - issues with offline / read only backends)
-	*/
-	sdn = slapi_sdn_new_normdn_byref(dn);
-	be = slapi_be_select(sdn);
-	if(be)
+	/* Count the number of types. */
+	for (num_types = 0; types && types[num_types]; num_types++)
 	{
-		base_sdn = (Slapi_DN*)slapi_be_getsuffix(be,0);
+		/* Add up the total length of all attribute names.
+		 * We need to know this for building the filter. */
+		types_name_len += strlen(types[num_types]);
 	}
 
-	if(base_sdn)
+	/* Build the search filter. */
+	if (num_types > 1)
 	{
-		/* Find the length of the dn */
-		dn_len = strlen(dn);
+		int bytes_out = 0;
+		int filter_str_len = types_name_len + (num_types * (3 + dn_len)) + 4;
 
-		/* Count the number of types. */
-		for (num_types = 0; types && types[num_types]; num_types++)
-		{
-			/* Add up the total length of all attribute names.
-			 * We need to know this for building the filter. */
-			types_name_len += strlen(types[num_types]);
-		}
+		/* Allocate enough space for the filter */
+		filter_str = slapi_ch_malloc(filter_str_len);
 
-		/* Build the search filter. */
-		if (num_types > 1)
+		/* Add beginning of filter. */
+		bytes_out = snprintf(filter_str, filter_str_len - bytes_out, "(|");
+
+		/* Add filter section for each type. */
+		for (i = 0; types[i]; i++)
 		{
-			int bytes_out = 0;
-			int filter_str_len = types_name_len + (num_types * (3 + dn_len)) + 4;
+			bytes_out += snprintf(filter_str + bytes_out, filter_str_len - bytes_out,
+					"(%s=%s)", types[i], dn);
+		}
 
-			/* Allocate enough space for the filter */
-			filter_str = slapi_ch_malloc(filter_str_len);
+		/* Add end of filter. */
+		snprintf(filter_str + bytes_out, filter_str_len - bytes_out, ")");
+	}
+	else if (num_types == 1)
+	{
+		filter_str = slapi_ch_smprintf("(%s=%s)", types[0], dn);
+	}
 
-			/* Add beginning of filter. */
-			bytes_out = snprintf(filter_str, filter_str_len - bytes_out, "(|");
+	if(filter_str == NULL){
+		return rc;
+	}
 
-			/* Add filter section for each type. */
-			for (i = 0; types[i]; i++)
-			{
-				bytes_out += snprintf(filter_str + bytes_out, filter_str_len - bytes_out,
-						"(%s=%s)", types[i], dn);
+	be = slapi_get_first_backend(&cookie);
+	while(be){
+		if(!all_backends){
+			sdn = slapi_sdn_new_normdn_byref(dn);
+			be = slapi_be_select(sdn);
+			if(be == NULL){
+				break;
 			}
-
-			/* Add end of filter. */
-			snprintf(filter_str + bytes_out, filter_str_len - bytes_out, ")");
 		}
-		else if (num_types == 1)
-		{
-			filter_str = slapi_ch_smprintf("(%s=%s)", types[0], dn);
+		if((base_sdn = (Slapi_DN *)slapi_be_getsuffix(be,0)) == NULL){
+			if(!all_backends){
+				break;
+			} else {
+				/* its ok, goto the next backend */
+				be = slapi_get_next_backend(cookie);
+				continue;
+			}
 		}
-	}
 
-	if(filter_str)
-	{
 		slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn),
-			LDAP_SCOPE_SUBTREE, filter_str, 0, 0,
-			0, 0,
-			memberof_get_plugin_id(),
-			0);	
-
-		slapi_search_internal_callback_pb(search_pb,
-			callback_data,
-			0, callback,
-			0);
+			LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0, memberof_get_plugin_id(), 0);
+		slapi_search_internal_callback_pb(search_pb, callback_data, 0, callback, 0);
+
+		if(!all_backends){
+			break;
+		}
+		slapi_pblock_init(search_pb);
+		be = slapi_get_next_backend(cookie);
 	}
 
 	slapi_sdn_free(&sdn);
 	slapi_pblock_destroy(search_pb);
+	slapi_ch_free((void **)&cookie);
 	slapi_ch_free_string(&filter_str);
+
 	return rc;
 }
 
@@ -1123,60 +1128,72 @@ int memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig *config,
 			Slapi_DN *base_sdn = 0;
 			Slapi_Backend *be = 0;
 			char *filter_str = 0;
+			char *cookie = NULL;
 			int n_entries = 0;
-
-			/* We can't tell for sure if the op_to entry is a
-			 * user or a group since the entry doesn't exist
-			 * anymore.  We can safely ignore the missing entry
-			 * if no other entries have a memberOf attribute that
-			 * points to the missing entry. */
-			be = slapi_be_select(op_to_sdn);
-			if(be)
-			{
-				base_sdn = (Slapi_DN*)slapi_be_getsuffix(be,0);
-			}
-
-			if(base_sdn)
-			{
-				filter_str = slapi_ch_smprintf("(%s=%s)",
-				                               config->memberof_attr, op_to);
-			}
-
-			if(filter_str)
-			{
-				slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn),
-					LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0,
-					memberof_get_plugin_id(), 0);
-
-				if (slapi_search_internal_pb(search_pb))
+			int all_backends = config->allBackends;
+
+			filter_str = slapi_ch_smprintf("(%s=%s)", config->memberof_attr, op_to);
+			be = slapi_get_first_backend(&cookie);
+			while(be){
+				/*
+				 * We can't tell for sure if the op_to entry is a
+				 * user or a group since the entry doesn't exist
+				 * anymore.  We can safely ignore the missing entry
+				 * if no other entries have a memberOf attribute that
+				 * points to the missing entry.
+				 */
+				if(!all_backends){
+					be = slapi_be_select(op_to_sdn);
+					if(be == NULL){
+						break;
+					}
+				}
+				if((base_sdn = (Slapi_DN*)slapi_be_getsuffix(be,0)) == NULL){
+					if(!all_backends){
+						break;
+					} else {
+						be = slapi_get_next_backend (cookie);
+						continue;
+					}
+				}
+				if(filter_str)
 				{
-					/* get result and log an error */
-					int res = 0;
-					slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
-					slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
-					"memberof_modop_one_replace_r: error searching for members: "
-					"%d", res);
-				} else {
-					slapi_pblock_get(search_pb, SLAPI_NENTRIES, &n_entries);
-
-					if(n_entries > 0)
+					slapi_search_internal_set_pb(search_pb, slapi_sdn_get_dn(base_sdn),
+						LDAP_SCOPE_SUBTREE, filter_str, 0, 0, 0, 0,
+						memberof_get_plugin_id(), 0);
+
+					if (slapi_search_internal_pb(search_pb))
 					{
-						/* We want to fixup the membership for the
-						 * entries that referred to the missing group
-						 * entry.  This will fix the references to
-						 * the missing group as well as the group
-						 * represented by op_this. */
-						memberof_test_membership(pb, config, op_to);
+						/* get result and log an error */
+						int res = 0;
+						slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+						slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
+						"memberof_modop_one_replace_r: error searching for members: "
+						"%d", res);
+					} else {
+						slapi_pblock_get(search_pb, SLAPI_NENTRIES, &n_entries);
+						if(n_entries > 0)
+						{
+							/* We want to fixup the membership for the
+							 * entries that referred to the missing group
+							 * entry.  This will fix the references to
+							 * the missing group as well as the group
+							 * represented by op_this. */
+							memberof_test_membership(pb, config, op_to);
+						}
 					}
+					slapi_free_search_results_internal(search_pb);
 				}
-
-				slapi_free_search_results_internal(search_pb);
-				slapi_ch_free_string(&filter_str);
+				slapi_pblock_init(search_pb);
+				if(!all_backends){
+					break;
+				}
+				be = slapi_get_next_backend (cookie);
 			}
-
 			slapi_pblock_destroy(search_pb);
+			slapi_ch_free_string(&filter_str);
+			slapi_ch_free((void **)&cookie);
 		}
-
 		goto bail;
 	}
 
diff --git a/ldap/servers/plugins/memberof/memberof.h b/ldap/servers/plugins/memberof/memberof.h
index fdf1253..36901e3 100644
--- a/ldap/servers/plugins/memberof/memberof.h
+++ b/ldap/servers/plugins/memberof/memberof.h
@@ -65,6 +65,7 @@
 #define MEMBEROF_INT_PREOP_DESC "memberOf internal postop plugin"
 #define MEMBEROF_GROUP_ATTR "memberOfGroupAttr"
 #define MEMBEROF_ATTR "memberOfAttr"
+#define MEMBEROF_BACKEND_ATTR "memberOfAllBackends"
 #define DN_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.12"
 #define NAME_OPT_UID_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.34"
 
@@ -75,6 +76,7 @@
 typedef struct memberofconfig {
 	char **groupattrs;
 	char *memberof_attr;
+	int allBackends;
 	Slapi_Filter *group_filter;
 	Slapi_Attr **group_slapiattrs;
 } MemberOfConfig;
@@ -92,6 +94,7 @@ void memberof_unlock();
 void memberof_rlock_config();
 void memberof_wlock_config();
 void memberof_unlock_config();
+int memberof_config_get_all_backends();
 
 int g_get_shutdown();		/* declared in proto-slap.h */
 
diff --git a/ldap/servers/plugins/memberof/memberof_config.c b/ldap/servers/plugins/memberof/memberof_config.c
index a30d448..b4d557a 100644
--- a/ldap/servers/plugins/memberof/memberof_config.c
+++ b/ldap/servers/plugins/memberof/memberof_config.c
@@ -270,11 +270,13 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
 	char *filter_str = NULL;
 	int num_groupattrs = 0;
 	int groupattr_name_len = 0;
+	char *allBackends = NULL;
 
 	*returncode = LDAP_SUCCESS;
 
 	groupattrs = slapi_entry_attr_get_charray(e, MEMBEROF_GROUP_ATTR);
-        memberof_attr = slapi_entry_attr_get_charptr(e, MEMBEROF_ATTR);
+	memberof_attr = slapi_entry_attr_get_charptr(e, MEMBEROF_ATTR);
+	allBackends = slapi_entry_attr_get_charptr(e, MEMBEROF_BACKEND_ATTR);
 
 	/* We want to be sure we don't change the config in the middle of
 	 * a memberOf operation, so we obtain an exclusive lock here */
@@ -373,11 +375,23 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry*
 		memberof_attr = NULL; /* config now owns memory */
 	}
 
+	if (allBackends)
+	{
+		if(strcasecmp(allBackends,"on")==0){
+			theConfig.allBackends = 1;
+		} else {
+			theConfig.allBackends = 0;
+		}
+	} else {
+		theConfig.allBackends = 0;
+	}
+
 	/* release the lock */
 	memberof_unlock_config();
 
 	slapi_ch_array_free(groupattrs);
 	slapi_ch_free_string(&memberof_attr);
+	slapi_ch_free_string(&allBackends);
 
 	if (*returncode != LDAP_SUCCESS)
 	{
@@ -449,6 +463,11 @@ memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src)
 			slapi_ch_free_string(&dest->memberof_attr);
 			dest->memberof_attr = slapi_ch_strdup(src->memberof_attr);
 		}
+
+		if(src->allBackends)
+		{
+			dest->allBackends = src->allBackends;
+		}
 	}
 }
 
@@ -526,3 +545,15 @@ memberof_unlock_config()
 {
 	slapi_rwlock_unlock(memberof_config_lock);
 }
+
+int
+memberof_config_get_all_backends()
+{
+	int all_backends;
+
+	slapi_rwlock_rdlock(memberof_config_lock);
+	all_backends = theConfig.allBackends;
+	slapi_rwlock_unlock(memberof_config_lock);
+
+	return all_backends;
+}
-- 
1.7.1

