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@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
389-commits@lists.fedoraproject.org