dirsrvtests/suites/memberof_plugin/memberof_test.py | 149 +++++++++++++++-----
ldap/servers/plugins/memberof/memberof.c | 145 +++++++++++++------
ldap/servers/plugins/memberof/memberof.h | 2
ldap/servers/plugins/memberof/memberof_config.c | 27 +++
4 files changed, 245 insertions(+), 78 deletions(-)
New commits:
commit ad64ba7adfaaa8b6bbb0836113a59ff766849cd9
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Wed Sep 2 11:51:35 2015 -0400
Ticket 48267 - Add config setting to MO plugin to add objectclass
Description: Add setting to plugin to auto add a predefined
objectclass that allows "memberOf" attribute
https://fedorahosted.org/389/ticket/48267
Reviewed by: nhosoi(Thanks!)
(cherry picked from commit b2fe2aab7548a91beb0907071ae66d4e46f2095f)
diff --git a/dirsrvtests/suites/memberof_plugin/memberof_test.py
b/dirsrvtests/suites/memberof_plugin/memberof_test.py
index b3c98f7..e97c09a 100644
--- a/dirsrvtests/suites/memberof_plugin/memberof_test.py
+++ b/dirsrvtests/suites/memberof_plugin/memberof_test.py
@@ -3,9 +3,10 @@
# All rights reserved.
#
# License: GPL (version 3 or any later version).
-# See LICENSE for details.
+# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
#
+
import os
import sys
import time
@@ -21,9 +22,13 @@ from lib389.utils import *
logging.getLogger(__name__).setLevel(logging.DEBUG)
log = logging.getLogger(__name__)
-
installation1_prefix = None
+MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF +
',cn=plugins,cn=config')
+USER1_DN = 'uid=user1,' + DEFAULT_SUFFIX
+USER2_DN = 'uid=user2,' + DEFAULT_SUFFIX
+GROUP_DN = 'cn=group,' + DEFAULT_SUFFIX
+
class TopologyStandalone(object):
def __init__(self, standalone):
@@ -51,43 +56,121 @@ def topology(request):
standalone.create()
standalone.open()
+ # Delete each instance in the end
+ def fin():
+ standalone.delete()
+ #pass
+ request.addfinalizer(fin)
+
# Clear out the tmp dir
standalone.clearTmpDir(__file__)
return TopologyStandalone(standalone)
-def test_memberof_init(topology):
- '''
- Write any test suite initialization here(if needed)
- '''
-
- return
-
-
-def test_memberof_(topology):
- '''
- Write a single test here...
- '''
-
- return
-
-
-def test_memberof_final(topology):
- topology.standalone.delete()
- log.info('memberof test suite PASSED')
-
-
-def run_isolated():
- global installation1_prefix
- installation1_prefix = None
-
- topo = topology(True)
- test_memberof_init(topo)
- test_memberof_(topo)
- test_memberof_final(topo)
+def test_memberof_auto_add_oc(topology):
+ """
+ Test the auto add objectclass feature. The plugin should add a predefined
+ objectclass that will allow memberOf to be added to an entry.
+ """
+
+ # enable dynamic plugins
+ try:
+ topology.standalone.modify_s(DN_CONFIG,
+ [(ldap.MOD_REPLACE,
+ 'nsslapd-dynamic-plugins',
+ 'on')])
+ except ldap.LDAPError as e:
+ ldap.error('Failed to enable dynamic plugins! ' +
e.message['desc'])
+ assert False
+
+ # Enable the plugin
+ topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
+
+ # First test invalid value (config validation)
+ topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
+ try:
+ topology.standalone.modify_s(MEMBEROF_PLUGIN_DN,
+ [(ldap.MOD_REPLACE,
+ 'memberofAutoAddOC',
+ 'invalid123')])
+ log.fatal('Incorrectly added invalid objectclass!')
+ assert False
+ except ldap.UNWILLING_TO_PERFORM:
+ log.info('Correctly rejected invalid objectclass')
+ except ldap.LDAPError as e:
+ ldap.error('Unexpected error adding invalid objectclass - error: ' +
e.message['desc'])
+ assert False
+
+ # Add valid objectclass
+ topology.standalone.plugins.enable(name=PLUGIN_MEMBER_OF)
+ try:
+ topology.standalone.modify_s(MEMBEROF_PLUGIN_DN,
+ [(ldap.MOD_REPLACE,
+ 'memberofAutoAddOC',
+ 'inetuser')])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to configure memberOf plugin: error ' +
e.message['desc'])
+ assert False
+
+ # Add two users
+ try:
+ topology.standalone.add_s(Entry((USER1_DN,
+ {'objectclass': 'top',
+ 'objectclass': 'person',
+ 'objectclass':
'organizationalPerson',
+ 'objectclass':
'inetorgperson',
+ 'sn': 'last',
+ 'cn': 'full',
+ 'givenname': 'user1',
+ 'uid': 'user1'
+ })))
+ except ldap.LDAPError as e:
+ log.fatal('Failed to add user1 entry, error: ' +
e.message['desc'])
+ assert False
+
+ try:
+ topology.standalone.add_s(Entry((USER2_DN,
+ {'objectclass': 'top',
+ 'objectclass': 'person',
+ 'objectclass':
'organizationalPerson',
+ 'objectclass':
'inetorgperson',
+ 'sn': 'last',
+ 'cn': 'full',
+ 'givenname': 'user2',
+ 'uid': 'user2'
+ })))
+ except ldap.LDAPError as e:
+ log.fatal('Failed to add user2 entry, error: ' +
e.message['desc'])
+ assert False
+
+ # Add a group(that already includes one user
+ try:
+ topology.standalone.add_s(Entry((GROUP_DN,
+ {'objectclass': 'top',
+ 'objectclass': 'groupOfNames',
+ 'cn': 'group',
+ 'member': USER1_DN
+ })))
+ except ldap.LDAPError as e:
+ log.fatal('Failed to add group entry, error: ' +
e.message['desc'])
+ assert False
+
+ # Add a user to the group
+ try:
+ topology.standalone.modify_s(GROUP_DN,
+ [(ldap.MOD_ADD,
+ 'member',
+ USER2_DN)])
+ except ldap.LDAPError as e:
+ log.fatal('Failed to add user2 to group: error ' +
e.message['desc'])
+ assert False
+
+ log.info('Test complete.')
if __name__ == '__main__':
- run_isolated()
-
+ # Run isolated
+ # -s for DEBUG mode
+ CURRENT_FILE = os.path.realpath(__file__)
+ pytest.main("-s %s" % CURRENT_FILE)
\ No newline at end of file
diff --git a/ldap/servers/plugins/memberof/memberof.c
b/ldap/servers/plugins/memberof/memberof.c
index 9b577b9..aad300f 100644
--- a/ldap/servers/plugins/memberof/memberof.c
+++ b/ldap/servers/plugins/memberof/memberof.c
@@ -145,6 +145,8 @@ static void memberof_fixup_task_thread(void *arg);
static int memberof_fix_memberof(MemberOfConfig *config, char *dn, char *filter_str);
static int memberof_fix_memberof_callback(Slapi_Entry *e, void *callback_data);
static int memberof_entry_in_scope(MemberOfConfig *config, Slapi_DN *sdn);
+static int memberof_add_objectclass(char *auto_add_oc, const char *dn);
+static int memberof_add_memberof_attr(LDAPMod **mods, const char *dn, char *add_oc);
/*** implementation ***/
@@ -490,7 +492,7 @@ int memberof_postop_del(Slapi_PBlock *pb)
{
int ret = SLAPI_PLUGIN_SUCCESS;
MemberOfConfig *mainConfig = NULL;
- MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Slapi_DN *sdn;
void *caller_id = NULL;
@@ -818,7 +820,7 @@ int memberof_postop_modrdn(Slapi_PBlock *pb)
if(memberof_oktodo(pb))
{
MemberOfConfig *mainConfig = 0;
- MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct slapi_entry *pre_e = NULL;
struct slapi_entry *post_e = NULL;
Slapi_DN *pre_sdn = 0;
@@ -937,6 +939,7 @@ typedef struct _replace_dn_data
char *pre_dn;
char *post_dn;
char *type;
+ char *add_oc;
} replace_dn_data;
@@ -957,7 +960,9 @@ memberof_replace_dn_from_groups(Slapi_PBlock *pb, MemberOfConfig
*config,
{
replace_dn_data data = {(char *)slapi_sdn_get_dn(pre_sdn),
(char *)slapi_sdn_get_dn(post_sdn),
- config->groupattrs[i]};
+ config->groupattrs[i],
+ config->auto_add_oc
+ };
groupattrs[0] = config->groupattrs[i];
@@ -981,9 +986,9 @@ int memberof_replace_dn_type_callback(Slapi_Entry *e, void
*callback_data)
LDAPMod *mods[3];
char *delval[2];
char *addval[2];
- Slapi_PBlock *mod_pb = 0;
+ char *dn = NULL;
- mod_pb = slapi_pblock_new();
+ dn = slapi_entry_get_dn(e);
mods[0] = &delmod;
mods[1] = &addmod;
@@ -1003,18 +1008,8 @@ int memberof_replace_dn_type_callback(Slapi_Entry *e, void
*callback_data)
addmod.mod_type = ((replace_dn_data *)callback_data)->type;
addmod.mod_values = addval;
- slapi_modify_internal_set_pb_ext(
- mod_pb, slapi_entry_get_sdn(e),
- mods, 0, 0,
- memberof_get_plugin_id(), 0);
-
- slapi_modify_internal_pb(mod_pb);
-
- slapi_pblock_get(mod_pb,
- SLAPI_PLUGIN_INTOP_RESULT,
- &rc);
-
- slapi_pblock_destroy(mod_pb);
+ rc = memberof_add_memberof_attr(mods, dn,
+ ((replace_dn_data *)callback_data)->add_oc);
return rc;
}
@@ -1083,7 +1078,7 @@ int memberof_postop_modify(Slapi_PBlock *pb)
{
int config_copied = 0;
MemberOfConfig *mainConfig = 0;
- MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* get the mod set */
slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
@@ -1261,7 +1256,7 @@ int memberof_postop_add(Slapi_PBlock *pb)
if(memberof_oktodo(pb) && (sdn = memberof_getsdn(pb)))
{
struct slapi_entry *e = NULL;
- MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ MemberOfConfig configCopy = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
MemberOfConfig *mainConfig;
slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e );
@@ -1455,7 +1450,6 @@ memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig
*config,
LDAPMod *mods[3];
char *val[2];
char *replace_val[2];
- Slapi_PBlock *mod_pb = 0;
Slapi_Entry *e = 0;
memberofstringll *ll = 0;
char *op_str = 0;
@@ -1696,8 +1690,6 @@ memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig
*config,
rc = memberof_fix_memberof_callback(e, config);
} else {
/* single entry - do mod */
- mod_pb = slapi_pblock_new();
-
mods[0] = &mod;
if(LDAP_MOD_REPLACE == mod_op)
{
@@ -1724,19 +1716,7 @@ memberof_modop_one_replace_r(Slapi_PBlock *pb, MemberOfConfig
*config,
replace_mod.mod_type = config->memberof_attr;
replace_mod.mod_values = replace_val;
}
-
- slapi_modify_internal_set_pb(
- mod_pb, op_to,
- mods, 0, 0,
- memberof_get_plugin_id(), 0);
-
- slapi_modify_internal_pb(mod_pb);
-
- slapi_pblock_get(mod_pb,
- SLAPI_PLUGIN_INTOP_RESULT,
- &rc);
-
- slapi_pblock_destroy(mod_pb);
+ rc = memberof_add_memberof_attr(mods, op_to, config->auto_add_oc);
}
}
@@ -2899,7 +2879,6 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void
*callback_data)
* with the found values. */
if (groups && slapi_valueset_count(groups))
{
- Slapi_PBlock *mod_pb = slapi_pblock_new();
Slapi_Value *val = 0;
Slapi_Mod *smod;
LDAPMod **mods = (LDAPMod **) slapi_ch_malloc(2 * sizeof(LDAPMod *));
@@ -2922,17 +2901,10 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void
*callback_data)
mods[0] = slapi_mod_get_ldapmod_passout(smod);
mods[1] = 0;
- slapi_modify_internal_set_pb_ext(
- mod_pb, sdn, mods, 0, 0,
- memberof_get_plugin_id(), 0);
-
- slapi_modify_internal_pb(mod_pb);
-
- slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ rc = memberof_add_memberof_attr(mods, slapi_sdn_get_dn(sdn), config->auto_add_oc);
ldap_mods_free(mods, 1);
slapi_mod_free(&smod);
- slapi_pblock_destroy(mod_pb);
} else {
/* No groups were found, so remove the memberOf attribute
* from this entry. */
@@ -2943,3 +2915,88 @@ int memberof_fix_memberof_callback(Slapi_Entry *e, void
*callback_data)
bail:
return rc;
}
+
+/*
+ * Add the "memberof" attribute to the entry. If we get an objectclass
violation,
+ * check if we are auto adding an objectclass. IF so, add the oc, and try the
+ * operation one more time.
+ */
+static int
+memberof_add_memberof_attr(LDAPMod **mods, const char *dn, char *add_oc)
+{
+ Slapi_PBlock *mod_pb = NULL;
+ int added_oc = 0;
+ int rc = 0;
+
+ while(1){
+ mod_pb = slapi_pblock_new();
+ slapi_modify_internal_set_pb(
+ mod_pb, dn, mods, 0, 0,
+ memberof_get_plugin_id(), 0);
+ slapi_modify_internal_pb(mod_pb);
+
+ slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc == LDAP_OBJECT_CLASS_VIOLATION){
+ if (!add_oc || added_oc){
+ /*
+ * We aren't auto adding an objectclass, or we already
+ * added the objectclass, and we are still failing.
+ */
+ break;
+ }
+ if(memberof_add_objectclass(add_oc, dn)){
+ /* Failed to add objectclass */
+ break;
+ }
+ added_oc = 1;
+ slapi_pblock_destroy(mod_pb);
+ } else if (rc){
+ /* Some other fatal error */
+ break;
+ } else {
+ /* success */
+ break;
+ }
+ }
+ slapi_pblock_destroy(mod_pb);
+
+ return rc;
+}
+
+/*
+ * Add the "auto add" objectclass to an entry
+ */
+static int
+memberof_add_objectclass(char *auto_add_oc, const char *dn)
+{
+ Slapi_PBlock *mod_pb = NULL;
+ LDAPMod mod;
+ LDAPMod *mods[2];
+ char *val[2];
+ int rc = 0;
+
+ mod_pb = slapi_pblock_new();
+ mods[0] = &mod;
+ mods[1] = 0;
+ val[0] = auto_add_oc;
+ val[1] = 0;
+
+ mod.mod_op = LDAP_MOD_ADD;
+ mod.mod_type = "objectclass";
+ mod.mod_values = val;
+
+ slapi_modify_internal_set_pb(
+ mod_pb, dn, mods, 0, 0,
+ memberof_get_plugin_id(), 0);
+ slapi_modify_internal_pb(mod_pb);
+
+ slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc){
+ slapi_log_error( SLAPI_LOG_FATAL, MEMBEROF_PLUGIN_SUBSYSTEM,
+ "Failed to add objectclass (%s) to entry (%s)\n",
+ auto_add_oc, dn);
+ }
+ slapi_pblock_destroy(mod_pb);
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/memberof/memberof.h
b/ldap/servers/plugins/memberof/memberof.h
index 9d9d158..ece18e4 100644
--- a/ldap/servers/plugins/memberof/memberof.h
+++ b/ldap/servers/plugins/memberof/memberof.h
@@ -41,6 +41,7 @@
#define MEMBEROF_ENTRY_SCOPE_ATTR "memberOfEntryScope"
#define MEMBEROF_ENTRY_SCOPE_EXCLUDE_SUBTREE
"memberOfEntryScopeExcludeSubtree"
#define MEMBEROF_SKIP_NESTED_ATTR "memberOfSkipNested"
+#define MEMBEROF_AUTO_ADD_OC "memberOfAutoAddOC"
#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"
@@ -60,6 +61,7 @@ typedef struct memberofconfig {
Slapi_Attr **group_slapiattrs;
int skip_nested;
int fixup_task;
+ char *auto_add_oc;
} MemberOfConfig;
diff --git a/ldap/servers/plugins/memberof/memberof_config.c
b/ldap/servers/plugins/memberof/memberof_config.c
index 10cbd7a..99132d8 100644
--- a/ldap/servers/plugins/memberof/memberof_config.c
+++ b/ldap/servers/plugins/memberof/memberof_config.c
@@ -180,6 +180,7 @@ memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore,
Slapi_Entr
char *syntaxoid = NULL;
char *config_dn = NULL;
char *skip_nested = NULL;
+ char *auto_add_oc = NULL;
char **entry_scopes = NULL;
char **entry_exclude_scopes = NULL;
int not_dn_syntax = 0;
@@ -272,6 +273,22 @@ memberof_validate_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore,
Slapi_Entr
}
}
+ if ((auto_add_oc = slapi_entry_attr_get_charptr(e, MEMBEROF_AUTO_ADD_OC))){
+ char *sup = NULL;
+
+ /* Check if the objectclass exists by looking for its superior oc */
+ if((sup = slapi_schema_get_superior_name(auto_add_oc)) == NULL){
+ PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "The %s configuration attribute must be set to "
+ "to an existing objectclass (unknown: %s)",
+ MEMBEROF_AUTO_ADD_OC, auto_add_oc);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ } else {
+ slapi_ch_free_string(&sup);
+ }
+ }
+
if ((config_dn = slapi_entry_attr_get_charptr(e, SLAPI_PLUGIN_SHARED_CONFIG_AREA))){
/* Now check the shared config attribute, validate it now */
@@ -412,6 +429,7 @@ done:
slapi_sdn_free(&config_sdn);
slapi_ch_free_string(&config_dn);
slapi_ch_free_string(&skip_nested);
+ slapi_ch_free_string(&auto_add_oc);
if (*returncode != LDAP_SUCCESS)
{
@@ -445,6 +463,7 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore,
Slapi_Entry*
char **entryScopeExcludeSubtrees = NULL;
char *sharedcfg = NULL;
char *skip_nested = NULL;
+ char *auto_add_oc = NULL;
int num_vals = 0;
*returncode = LDAP_SUCCESS;
@@ -478,6 +497,7 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore,
Slapi_Entry*
memberof_attr = slapi_entry_attr_get_charptr(e, MEMBEROF_ATTR);
allBackends = slapi_entry_attr_get_charptr(e, MEMBEROF_BACKEND_ATTR);
skip_nested = slapi_entry_attr_get_charptr(e, MEMBEROF_SKIP_NESTED_ATTR);
+ auto_add_oc = slapi_entry_attr_get_charptr(e, MEMBEROF_AUTO_ADD_OC);
/*
* We want to be sure we don't change the config in the middle of
@@ -602,6 +622,8 @@ memberof_apply_config (Slapi_PBlock *pb, Slapi_Entry* entryBefore,
Slapi_Entry*
theConfig.allBackends = 0;
}
+ theConfig.auto_add_oc = auto_add_oc;
+
/*
* Check and process the entry scopes
*/
@@ -731,6 +753,9 @@ memberof_copy_config(MemberOfConfig *dest, MemberOfConfig *src)
dest->allBackends = src->allBackends;
}
+ slapi_ch_free_string(&dest->auto_add_oc);
+ dest->auto_add_oc = slapi_ch_strdup(src->auto_add_oc);
+
if(src->entryScopes){
int num_vals = 0;
@@ -770,7 +795,7 @@ memberof_free_config(MemberOfConfig *config)
slapi_attr_free(&config->group_slapiattrs[i]);
}
slapi_ch_free((void **)&config->group_slapiattrs);
-
+ slapi_ch_free_string(&config->auto_add_oc);
slapi_ch_free_string(&config->memberof_attr);
memberof_free_scope(config->entryScopes, &config->entryScopeCount);
memberof_free_scope(config->entryScopeExcludeSubtrees,
&config->entryExcludeScopeCount);
--
389 commits mailing list
389-commits@%(host_name)s
http://lists.fedoraproject.org/postorius/389-commits@lists.fedoraproject.org