]> git.sur5r.net Git - openldap/commitdiff
ITS#6684 preliminary from Norbert Pueschel and Christian Manal
authorHoward Chu <hyc@openldap.org>
Sat, 29 Jan 2011 21:12:51 +0000 (21:12 +0000)
committerHoward Chu <hyc@openldap.org>
Sat, 29 Jan 2011 21:12:51 +0000 (21:12 +0000)
contrib/slapd-modules/autogroup/README
contrib/slapd-modules/autogroup/autogroup.c

index e6667272359c8953a531e7b7861e1646132c23d5..3141536354a596eb971c13dfd28bb11b37c4da52 100644 (file)
@@ -51,6 +51,14 @@ CONFIGURATION
         specifies the member attribute. User modification of this attribute 
         is disabled for consistency.
 
+    autogroup-memberof-ad <memberof-ad>
+        This configuration option is defined for the autogroup overlay.
+
+        It defines the attribute that is used by the memberOf overlay
+        to store the names of groups that an entry is member of; it must be
+        DN-valued. It should be set to the same value as
+        memberof-memberof-ad. It defaults to 'memberOf'.
+
 
 EXAMPLE
     ### slapd.conf
@@ -66,6 +74,24 @@ EXAMPLE
     autogroup-attrset groupOfURLs memberURL member
     ### end slapd.conf
 
+    ### slapd.conf
+    include /path/to/dyngroup.schema
+    # ...
+    moduleload /path/to/autogroup.so
+    moduleload /path/to/memberof.so
+    # ...
+
+    database <database>
+    #...
+
+    overlay memberof
+    memberof memberof-memberof-ad foo
+
+    overlay autogroup
+    autogroup-attrset groupOfURLs memberURL member
+    autogroup-memberof-ad foo
+    ### end slapd.conf
+
 CAVEATS
     As with static groups, update operations on groups with a large number
     of members may be slow.
index b354116f0569f2e8b21c30168a2840b32a7a69f3..f9414be3b8e6e3d17844ac1622ec303272f40caf 100644 (file)
@@ -19,6 +19,9 @@
  * This work was initially developed by Michał Szulczyński for inclusion in
  * OpenLDAP Software.  Additional significant contributors include:
  *   Howard Chu
+ *   Raphael Ouazana
+ *   Norbert Pueschel
+ *   Christian Manal
  */
 
 #include "portable.h"
 #include "config.h"
 #include "lutil.h"
 
+#ifndef SLAPD_MEMBEROF_ATTR
+#define        SLAPD_MEMBEROF_ATTR     "memberOf"
+#endif
+
 /* Filter represents the memberURL of a group. */
 typedef struct autogroup_filter_t {
        struct berval                   agf_dn; /* The base DN in memberURL */
@@ -66,6 +73,7 @@ typedef struct autogroup_entry_t {
 typedef struct autogroup_info_t {
        autogroup_def_t         *agi_def;       /* Group attributes definitions. */
        autogroup_entry_t       *agi_entry;     /* Group entries.  */
+       AttributeDescription    *agi_memberof_ad;       /* memberOf attribute description  */
        ldap_pvt_thread_mutex_t agi_mutex;
 } autogroup_info_t;
 
@@ -257,6 +265,56 @@ autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn,
        return sreply.sr_err;
 }
 
+/*
+**      e       - the entry where to get the attribute values
+**      age     - the group from which the values will be deleted
+*/
+static int
+autogroup_delete_member_values_from_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
+{
+        slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
+        Modifications   modlist;
+        SlapReply       sreply = {REP_RESULT};
+        Attribute       *attr;
+        slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
+        Operation       o = *op;
+
+        Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
+                e->e_name.bv_val, age->age_dn.bv_val, 0);
+
+        assert( e != NULL );
+
+        attr = attrs_find( e->e_attrs, attrdesc );
+        if (!attr) {
+                // Nothing to add
+                return LDAP_SUCCESS;
+        }
+
+        modlist.sml_op = LDAP_MOD_DELETE;
+        modlist.sml_desc = age->age_def->agd_member_ad;
+        modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
+        modlist.sml_values = attr->a_vals;
+        modlist.sml_nvalues = attr->a_nvals;
+        modlist.sml_numvals = attr->a_numvals;
+        modlist.sml_flags = SLAP_MOD_INTERNAL;
+        modlist.sml_next = NULL;
+
+        o.o_tag = LDAP_REQ_MODIFY;
+        o.o_callback = &cb;
+        o.orm_modlist = &modlist;
+        o.o_req_dn = age->age_dn;
+        o.o_req_ndn = age->age_ndn;
+        o.o_permissive_modify = 1;
+        o.o_managedsait = SLAP_CONTROL_CRITICAL;
+        o.o_relax = SLAP_CONTROL_CRITICAL;
+
+        o.o_bd->bd_info = (BackendInfo *)on->on_info;
+        (void)op->o_bd->be_modify( &o, &sreply );
+        o.o_bd->bd_info = (BackendInfo *)on;
+
+        return sreply.sr_err;
+}
+
 /* 
 ** Callback used to add entries to a group, 
 ** which are going to be written in the database
@@ -872,7 +930,7 @@ autogroup_response( Operation *op, SlapReply *rs )
        autogroup_filter_t      *agf;
        BerValue                new_dn, new_ndn, pdn;
        Entry                   *e, *group;
-       Attribute               *a;
+       Attribute               *a, *ea;
        int                     is_olddn, is_newdn, is_value_refresh, dn_equal;
 
        /* Handle all cases where a refresh of the group is needed */
@@ -972,8 +1030,6 @@ autogroup_response( Operation *op, SlapReply *rs )
                                }
                        }
 
-                       overlay_entry_release_ov( op, e, 0, on );
-
                        /* For each group: 
                           1. check if the orginal entry's DN is in the group.
                           2. chceck if the any of the group filter's base DN is a suffix of the new DN 
@@ -992,6 +1048,13 @@ autogroup_response( Operation *op, SlapReply *rs )
 
                                ldap_pvt_thread_mutex_lock( &age->age_mutex );
 
+                               if ( age->age_filter && age->age_filter->agf_anlist ) {
+                                       ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
+                               }
+                               else {
+                                       ea = NULL;
+                               }
+
                                if ( age->age_modrdn_olddnmodified ) {
                                        /* Resquest already marked this group to be updated */
                                        is_olddn = 1;
@@ -1006,6 +1069,7 @@ autogroup_response( Operation *op, SlapReply *rs )
                                                op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
                                                op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
 
+                                               overlay_entry_release_ov( op, e, 0, on );
                                                ldap_pvt_thread_mutex_unlock( &age->age_mutex );
                                                ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
                                                return SLAP_CB_CONTINUE;
@@ -1017,7 +1081,7 @@ autogroup_response( Operation *op, SlapReply *rs )
                                                if ( value_find_ex( age->age_def->agd_member_ad,
                                                                SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
                                                                SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
-                                                               a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
+                                                               a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
                                                {
                                                        is_olddn = 1;
                                                }
@@ -1050,19 +1114,35 @@ autogroup_response( Operation *op, SlapReply *rs )
                                        continue;
                                }
                                if ( is_olddn == 1 && is_newdn == 0 ) {
-                                       autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+                                       if ( ea )
+                                               autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
+                                       else
+                                               autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
                                } else
                                if ( is_olddn == 0 && is_newdn == 1 ) {
                                        for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
                                                if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
-                                                       autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
+                                                       if ( ea )
+                                                               autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
+                                                       else
+                                                               autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
                                                        break;
                                                }
                                        }
                                } else
                                if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
-                                       autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
-                                       autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
+                                       if ( ea ) {
+                                               /* group refresh */
+                                               autogroup_delete_member_from_group( op, NULL, NULL, age) ;
+
+                                               for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+                                                       autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
+                                               }
+                                       }
+                                       else {
+                                               autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+                                               autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
+                                       }
                                }
 
                                ldap_pvt_thread_mutex_unlock( &age->age_mutex );
@@ -1071,6 +1151,8 @@ autogroup_response( Operation *op, SlapReply *rs )
                        op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
                        op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
 
+                       overlay_entry_release_ov( op, e, 0, on );
+
                        ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
                }
        }
@@ -1098,7 +1180,6 @@ autogroup_response( Operation *op, SlapReply *rs )
                                return SLAP_CB_CONTINUE;
                        }
 
-
                        /* If we modify a group's memberURL, we have to delete all of it's members,
                           and add them anew, because we cannot tell from which memberURL a member was added. */
                        for ( ; agd; agd = agd->agd_next ) {
@@ -1133,6 +1214,7 @@ autogroup_response( Operation *op, SlapReply *rs )
 
                                                                        autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
 
+                                                                       overlay_entry_release_ov( op, e, 0, on );
                                                                        ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
                                                                        return SLAP_CB_CONTINUE;
                                                                }
@@ -1151,8 +1233,6 @@ autogroup_response( Operation *op, SlapReply *rs )
                                }
                        }
 
-                       overlay_entry_release_ov( op, e, 0, on );
-
                        /* When modifing any of the attributes of an entry, we must
                           check if the entry is in any of our groups, and if
                           the modified entry maches any of the filters of that group.
@@ -1169,11 +1249,19 @@ autogroup_response( Operation *op, SlapReply *rs )
 
                                ldap_pvt_thread_mutex_lock( &age->age_mutex );
 
+                               if ( age->age_filter && age->age_filter->agf_anlist ) {
+                                       ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
+                               }
+                               else {
+                                       ea = NULL;
+                               }
+
                                if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
                                        LDAP_SUCCESS || group == NULL ) {
                                        Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", 
                                                age->age_dn.bv_val, 0, 0);
 
+                                       overlay_entry_release_ov( op, e, 0, on );
                                        ldap_pvt_thread_mutex_unlock( &age->age_mutex );
                                        ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
                                        return SLAP_CB_CONTINUE;
@@ -1185,7 +1273,7 @@ autogroup_response( Operation *op, SlapReply *rs )
                                        if ( value_find_ex( age->age_def->agd_member_ad,
                                                        SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
                                                        SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
-                                                       a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
+                                                       a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
                                        {
                                                is_olddn = 1;
                                        }
@@ -1195,7 +1283,7 @@ autogroup_response( Operation *op, SlapReply *rs )
                                overlay_entry_release_ov( op, group, 0, on );
 
                                for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
-                                       if ( !agf->agf_anlist && dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+                                       if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
                                                if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
                                                        is_newdn = 1;
                                                        break;
@@ -1204,15 +1292,23 @@ autogroup_response( Operation *op, SlapReply *rs )
                                }
 
                                if ( is_olddn == 1 && is_newdn == 0 ) {
-                                       autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+                                       if(ea)
+                                               autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
+                                       else
+                                               autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
                                } else
                                if ( is_olddn == 0 && is_newdn == 1 ) {
-                                       autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+                                       if(ea)
+                                               autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
+                                       else
+                                               autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
                                } 
 
                                ldap_pvt_thread_mutex_unlock( &age->age_mutex );
                        }
 
+                       overlay_entry_release_ov( op, e, 0, on );
+
                        ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
                }
        }
@@ -1220,6 +1316,34 @@ autogroup_response( Operation *op, SlapReply *rs )
        return SLAP_CB_CONTINUE;
 }
 
+/*
+** Detect if filter contains a memberOf check for dn
+*/
+static int
+autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
+{
+       int result = 0;
+       if ( f == NULL ) return 0;
+
+       switch ( f->f_choice & SLAPD_FILTER_MASK ) {
+               case LDAP_FILTER_AND:
+               case LDAP_FILTER_OR:
+               case LDAP_FILTER_NOT:
+                       for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
+                               result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
+                       }
+                       break;
+               case LDAP_FILTER_EQUALITY:
+                       result = ( f->f_ava->aa_desc == memberof_ad &&
+                                  ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
+                       break;
+               default:
+                       break;
+       }
+
+       return result;
+}
+
 /*
 ** When modifing a group, we must deny any modifications to the member attribute,
 ** because the group would be inconsistent.
@@ -1248,7 +1372,7 @@ autogroup_modify_entry( Operation *op, SlapReply *rs)
                return SLAP_CB_CONTINUE;
        }
 
-       /* Must refresh groups if a matching member value is modified */
+       /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
        for ( ; age ; age = age->age_next ) {
                autogroup_filter_t      *agf;
                for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
@@ -1265,6 +1389,10 @@ autogroup_modify_entry( Operation *op, SlapReply *rs)
                                        }
                                }
                        }
+
+                       if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
+                               age->age_mustrefresh = 1;
+                       }
                }
        }
 
@@ -1394,6 +1522,7 @@ autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
 
 enum {
        AG_ATTRSET = 1,
+       AG_MEMBER_OF_AD,
        AG_LAST
 };
 
@@ -1408,6 +1537,14 @@ static ConfigTable agcfg[] = {
                        "SYNTAX OMsDirectoryString "
                        "X-ORDERED 'VALUES' )",
                        NULL, NULL },
+       
+       { "autogroup-memberof-ad", "memberOf attribute",
+               2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
+               "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
+                       "DESC 'memberOf attribute' "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+                       NULL, NULL },
+
        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
 };
 
@@ -1416,7 +1553,11 @@ static ConfigOCs agocs[] = {
                "NAME 'olcAutomaticGroups' "
                "DESC 'Automatic groups configuration' "
                "SUP olcOverlayConfig "
-               "MAY olcAGattrSet )",
+               "MAY ( "
+                       "olcAGattrSet "
+                       "$ olcAGmemberOfAd "
+                   ")"
+         ")",
                Cft_Overlay, agcfg, NULL, NULL },
        { NULL, 0, NULL }
 };
@@ -1449,25 +1590,40 @@ ag_cfgen( ConfigArgs *c )
 
                ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
 
-               for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
-                       struct berval   bv;
-                       char            *ptr = c->cr_msg;
-
-                       assert(agd->agd_oc != NULL);
-                       assert(agd->agd_member_url_ad != NULL);
-                       assert(agd->agd_member_ad != NULL);
+               switch( c->type ){
+               case AG_ATTRSET:
+                       for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
+                               struct berval   bv;
+                               char            *ptr = c->cr_msg;
+       
+                               assert(agd->agd_oc != NULL);
+                               assert(agd->agd_member_url_ad != NULL);
+                               assert(agd->agd_member_ad != NULL);
+       
+                               ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       SLAP_X_ORDERED_FMT "%s %s %s", i,
+                                       agd->agd_oc->soc_cname.bv_val,
+                                       agd->agd_member_url_ad->ad_cname.bv_val,
+                                       agd->agd_member_ad->ad_cname.bv_val );
+       
+                               bv.bv_val = c->cr_msg;
+                               bv.bv_len = ptr - bv.bv_val;
+                               value_add_one ( &c->rvalue_vals, &bv );
+       
+                       }
+                       break;
 
-                       ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                               SLAP_X_ORDERED_FMT "%s %s %s", i,
-                               agd->agd_oc->soc_cname.bv_val,
-                               agd->agd_member_url_ad->ad_cname.bv_val,
-                               agd->agd_member_ad->ad_cname.bv_val );
+               case AG_MEMBER_OF_AD:
+                       if ( agi->agi_memberof_ad != NULL ){
+                               value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
+                       }
+                       break;
 
-                       bv.bv_val = c->cr_msg;
-                       bv.bv_len = ptr - bv.bv_val;
-                       value_add_one ( &c->rvalue_vals, &bv );
+               default:
+                       assert( 0 );
+                       return 1;
+      }
 
-               }
                ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
 
                return rc;
@@ -1682,6 +1838,41 @@ ag_cfgen( ConfigArgs *c )
                ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
 
                } break;
+       
+       case AG_MEMBER_OF_AD: {
+               AttributeDescription *memberof_ad = NULL;
+               const char     *text;
+
+               rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
+               if( rc != LDAP_SUCCESS ) {
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                               "\"autogroup-memberof-ad <memberof-ad>\": "
+                               "unable to find AttributeDescription \"%s\"",
+                               c->argv[ 1 ] );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+                               c->log, c->cr_msg, 0 );
+                       return 1;
+               }
+
+               if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX )    /* e.g. "member" */
+                    && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )  /* e.g. "uniqueMember" */
+               {
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                               "memberof attribute=\"%s\" must either "
+                               "have DN (%s) or nameUID (%s) syntax",
+                               c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+                               c->log, c->cr_msg, 0 );
+                       return 1;
+               }
+
+               ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+               agi->agi_memberof_ad = memberof_ad;
+
+               ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+
+               } break;
 
        default:
                rc = 1;
@@ -1691,6 +1882,8 @@ ag_cfgen( ConfigArgs *c )
        return rc;
 }
 
+extern int slapMode;
+
 /* 
 ** Do a search for all the groups in the
 ** database, and add them to out internal list.
@@ -1759,6 +1952,19 @@ autogroup_db_open(
                op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
        }               
 
+       if( ! agi->agi_memberof_ad ){
+               int                     rc;
+               const char              *text = NULL;
+               
+               rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
+               if ( rc != LDAP_SUCCESS ) {
+                       Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
+                       "unable to find attribute=\"%s\": %s (%d)\n",
+                       SLAPD_MEMBEROF_ATTR, text, rc );
+                       return rc;
+               }
+       }
+
        return 0;
 }