]> git.sur5r.net Git - openldap/blobdiff - contrib/slapd-modules/autogroup/autogroup.c
Merge remote-tracking branch 'origin/mdb.master' into OPENLDAP_REL_ENG_2_4
[openldap] / contrib / slapd-modules / autogroup / autogroup.c
index 61aaf0cef709ce5e63d22fa4294e27427a62ab1c..06e140789f1ac65558c2c91282122fea3153cec3 100644 (file)
@@ -1,7 +1,10 @@
 /* autogroup.c - automatic group overlay */
 /* $OpenLDAP$ */
-/*
- * Copyright 2007 Michał Szulczyński.
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2013 The OpenLDAP Foundation.
+ * Portions Copyright 2007 Michał Szulczyński.
+ * Portions Copyright 2009 Howard Chu.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * top-level directory of the distribution or, alternatively, at
  * <http://www.OpenLDAP.org/license.html>.
  */
+/* ACKNOWLEDGEMENTS:
+ * 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 */
@@ -30,6 +45,7 @@ typedef struct autogroup_filter_t {
        struct berval                   agf_filterstr;
        Filter                          *agf_filter;
        int                             agf_scope;
+       AttributeName                   *agf_anlist;
        struct autogroup_filter_t       *agf_next;
 } autogroup_filter_t;
 
@@ -48,6 +64,8 @@ typedef struct autogroup_entry_t {
        autogroup_filter_t      *age_filter; /* List of filters made from memberURLs */
        autogroup_def_t         *age_def; /* Attribute definition */
        ldap_pvt_thread_mutex_t age_mutex;
+       int                     age_mustrefresh; /* Defined in request to refresh in response */
+       int                     age_modrdn_olddnmodified; /* Defined in request to refresh in response */
        struct autogroup_entry_t        *age_next;
 } autogroup_entry_t;
 
@@ -55,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;
 
@@ -67,6 +86,7 @@ typedef struct autogroup_sc_t {
 /* Used for adding members, found when searching, to a group. */
 typedef struct autogroup_ga_t {
        autogroup_entry_t       *agg_group;     /* The group to which the members will be added. */
+       autogroup_filter_t      *agg_filter;    /* Current filter */
        Entry                   *agg_entry;     /* Used in autogroup_member_search_cb to modify 
                                                this entry with the search results. */
 
@@ -86,29 +106,81 @@ static int
 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
 {
        slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
-       Modifications   modlist;
+       Modifications   *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
        SlapReply       sreply = {REP_RESULT};
-       BerValue        vals[ 2 ], nvals[ 2 ];
+       BerValue        *vals, *nvals;
        slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
        Operation       o = *op;
 
-       Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
-               dn->bv_val, age->age_dn.bv_val, 0);
-
        assert( dn != NULL );
        assert( ndn != NULL );
+       Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
+               dn->bv_val, age->age_dn.bv_val, 0);
 
-       vals[ 0 ] = *dn;
+       vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+       nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+       ber_dupbv( vals, dn );
        BER_BVZERO( &vals[ 1 ] );
-       nvals[ 0 ] = *ndn;
+       ber_dupbv( nvals, ndn );
        BER_BVZERO( &nvals[ 1 ] );
 
+       modlist->sml_op = LDAP_MOD_ADD;
+       modlist->sml_desc = age->age_def->agd_member_ad;
+       modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
+       modlist->sml_values = vals;
+       modlist->sml_nvalues = nvals;
+       modlist->sml_numvals = 1;
+       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;
+
+       slap_mods_free( modlist, 1 );
+
+       return sreply.sr_err;
+}
+
+/* 
+**     e       - the entry where to get the attribute values
+**     age     - the group to which the values will be added
+*/
+static int
+autogroup_add_member_values_to_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;
+
+       assert( e != NULL );
+       Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
+               e->e_name.bv_val, age->age_dn.bv_val, 0);
+
+       attr = attrs_find( e->e_attrs, attrdesc );
+       if (!attr) {
+               // Nothing to add
+               return LDAP_SUCCESS;
+       }
+
        modlist.sml_op = LDAP_MOD_ADD;
        modlist.sml_desc = age->age_def->agd_member_ad;
        modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
-       modlist.sml_values = vals;
-       modlist.sml_nvalues = nvals;
-       modlist.sml_numvals = 1;
+       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;
 
@@ -137,9 +209,9 @@ static int
 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
 {
        slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
-       Modifications   modlist;
+       Modifications   *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
        SlapReply       sreply = {REP_RESULT};
-       BerValue        vals[ 2 ], nvals[ 2 ];
+       BerValue        *vals, *nvals;
        slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
        Operation       o = *op;
 
@@ -147,33 +219,35 @@ autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn,
                Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
                        age->age_dn.bv_val, 0 ,0);
 
-               modlist.sml_values = NULL;
-               modlist.sml_nvalues = NULL;
-               modlist.sml_numvals = 0;
+               modlist->sml_values = NULL;
+               modlist->sml_nvalues = NULL;
+               modlist->sml_numvals = 0;
        } else {
                Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
                        dn->bv_val, age->age_dn.bv_val, 0);
 
-               vals[ 0 ] = *dn;
+               vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+               nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+               ber_dupbv( vals, dn );
                BER_BVZERO( &vals[ 1 ] );
-               nvals[ 0 ] = *ndn;
+               ber_dupbv( nvals, ndn );
                BER_BVZERO( &nvals[ 1 ] );
 
-               modlist.sml_values = vals;
-               modlist.sml_nvalues = nvals;
-               modlist.sml_numvals = 1;
+               modlist->sml_values = vals;
+               modlist->sml_nvalues = nvals;
+               modlist->sml_numvals = 1;
        }
 
 
-       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_flags = SLAP_MOD_INTERNAL;
-       modlist.sml_next = NULL;
+       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_flags = SLAP_MOD_INTERNAL;
+       modlist->sml_next = NULL;
 
        o.o_callback = &cb;
        o.o_tag = LDAP_REQ_MODIFY;
-       o.orm_modlist = &modlist;
+       o.orm_modlist = modlist;
        o.o_req_dn = age->age_dn;
        o.o_req_ndn = age->age_ndn;
        o.o_relax = SLAP_CONTROL_CRITICAL;
@@ -184,9 +258,60 @@ autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn,
        (void)op->o_bd->be_modify( &o, &sreply );
        o.o_bd->bd_info = (BackendInfo *)on;
 
+       slap_mods_free( modlist, 1 );
+
        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;
+
+        assert( e != NULL );
+        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);
+
+        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
@@ -196,32 +321,48 @@ autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn,
 static int
 autogroup_member_search_cb( Operation *op, SlapReply *rs )
 {
-       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
-
        assert( op->o_tag == LDAP_REQ_SEARCH );
 
        if ( rs->sr_type == REP_SEARCH ) {
                autogroup_ga_t          *agg = (autogroup_ga_t *)op->o_callback->sc_private;
                autogroup_entry_t       *age = agg->agg_group;
+               autogroup_filter_t      *agf = agg->agg_filter;
                Modification            mod;
                const char              *text = NULL;
                char                    textbuf[1024];
-               struct berval           vals[ 2 ], nvals[ 2 ];
+               struct berval           *vals, *nvals;
+               int                     numvals;
 
                Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
                        rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
 
-               vals[ 0 ] = rs->sr_entry->e_name;
-               BER_BVZERO( &vals[ 1 ] );
-               nvals[ 0 ] = rs->sr_entry->e_nname;
-               BER_BVZERO( &nvals[ 1 ] );
+               if ( agf->agf_anlist ) {
+                       Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
+                       if (attr) {
+                               vals = attr->a_vals;
+                               nvals = attr->a_nvals;
+                               numvals = attr->a_numvals;
+                       } else {
+                               // Nothing to add
+                               return 0;
+                       }
+               } else {
+                       struct berval           lvals[ 2 ], lnvals[ 2 ];
+                       lvals[ 0 ] = rs->sr_entry->e_name;
+                       BER_BVZERO( &lvals[ 1 ] );
+                       lnvals[ 0 ] = rs->sr_entry->e_nname;
+                       BER_BVZERO( &lnvals[ 1 ] );
+                       vals = lvals;
+                       nvals = lnvals;
+                       numvals = 1;
+               }
 
                mod.sm_op = LDAP_MOD_ADD;
                mod.sm_desc = age->age_def->agd_member_ad;
                mod.sm_type = age->age_def->agd_member_ad->ad_cname;
                mod.sm_values = vals;
                mod.sm_nvalues = nvals;
-               mod.sm_numvals = 1;
+               mod.sm_numvals = numvals;
 
                modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
        }
@@ -238,48 +379,61 @@ autogroup_member_search_cb( Operation *op, SlapReply *rs )
 static int
 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
 {
-       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
-
        assert( op->o_tag == LDAP_REQ_SEARCH );
 
        if ( rs->sr_type == REP_SEARCH ) {
                autogroup_ga_t          *agg = (autogroup_ga_t *)op->o_callback->sc_private;
                autogroup_entry_t       *age = agg->agg_group;
-               Operation               o = *op;
+               autogroup_filter_t      *agf = agg->agg_filter;
                Modifications           *modlist;
-               SlapReply               sreply = {REP_RESULT};
-               const char              *text = NULL;
-               char                    textbuf[1024];
-               struct berval           vals[ 2 ], nvals[ 2 ];
-               slap_callback           cb = { NULL, slap_null_cb, NULL, NULL };
+               struct berval           *vals, *nvals;
+               int                     numvals;
 
                Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
                        rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
 
-               vals[ 0 ] = rs->sr_entry->e_name;
-               BER_BVZERO( &vals[ 1 ] );
-               nvals[ 0 ] = rs->sr_entry->e_nname;
-               BER_BVZERO( &nvals[ 1 ] );
+               if ( agf->agf_anlist ) {
+                       Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
+                       if (attr) {
+                               vals = attr->a_vals;
+                               nvals = attr->a_nvals;
+                               numvals = attr->a_numvals;
+                       } else {
+                               // Nothing to add
+                               return 0;
+                       }
+               } else {
+                       struct berval           lvals[ 2 ], lnvals[ 2 ];
+                       lvals[ 0 ] = rs->sr_entry->e_name;
+                       BER_BVZERO( &lvals[ 1 ] );
+                       lnvals[ 0 ] = rs->sr_entry->e_nname;
+                       BER_BVZERO( &lnvals[ 1 ] );
+                       vals = lvals;
+                       nvals = lnvals;
+                       numvals = 1;
+               }
 
-               modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
+               if ( numvals ) {
+                       modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
 
-               modlist->sml_op = LDAP_MOD_ADD;
-               modlist->sml_desc = age->age_def->agd_member_ad;
-               modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
+                       modlist->sml_op = LDAP_MOD_ADD;
+                       modlist->sml_desc = age->age_def->agd_member_ad;
+                       modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
 
-               ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
-               ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
-               modlist->sml_numvals = 1;
+                       ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
+                       ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
+                       modlist->sml_numvals = numvals;
 
-               modlist->sml_flags = SLAP_MOD_INTERNAL;
-               modlist->sml_next = NULL;
+                       modlist->sml_flags = SLAP_MOD_INTERNAL;
+                       modlist->sml_next = NULL;
 
-               if ( agg->agg_mod == NULL ) {
-                       agg->agg_mod = modlist;
-                       agg->agg_mod_last = modlist;
-               } else {
-                       agg->agg_mod_last->sml_next = modlist;
-                       agg->agg_mod_last = modlist;
+                       if ( agg->agg_mod == NULL ) {
+                               agg->agg_mod = modlist;
+                               agg->agg_mod_last = modlist;
+                       } else {
+                               agg->agg_mod_last->sml_next = modlist;
+                               agg->agg_mod_last = modlist;
+                       }
                }
 
        }
@@ -324,9 +478,10 @@ autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *a
        o.ors_limit = NULL;
        o.ors_tlimit = SLAP_NO_LIMIT;
        o.ors_slimit = SLAP_NO_LIMIT;
-       o.ors_attrs =  slap_anlist_no_attrs;
+       o.ors_attrs =  agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
 
        agg.agg_group = age;
+       agg.agg_filter = agf;
        agg.agg_mod = NULL;
        agg.agg_mod_last = NULL;
        agg.agg_entry = e;
@@ -347,7 +502,9 @@ autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *a
        op->o_bd->be_search( &o, &rs );
        o.o_bd->bd_info = (BackendInfo *)on;    
 
-       if ( modify == 1 ) {
+       if ( modify == 1 && agg.agg_mod ) {
+               rs_reinit( &rs, REP_RESULT );
+
                o = *op;
                o.o_callback = &null_cb;
                o.o_tag = LDAP_REQ_MODIFY;
@@ -418,6 +575,8 @@ autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd,
        ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
        (*agep)->age_def = agd;
        (*agep)->age_filter = NULL;
+       (*agep)->age_mustrefresh = 0;
+       (*agep)->age_modrdn_olddnmodified = 0;
 
        ber_dupbv( &(*agep)->age_dn, &e->e_name );
        ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
@@ -463,6 +622,34 @@ autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd,
                                agf->agf_filter = str2filter( lud->lud_filter );
                        }                       
 
+                       if ( lud->lud_attrs != NULL ) {
+                               int i;
+
+                               for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
+                                       /* Just counting */;
+                               }
+
+                               if ( i > 1 ) {
+                                       Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
+                                               bv->bv_val, 0, 0);
+                                       /* FIXME: error? */
+                                       ldap_free_urldesc( lud );
+                                       ch_free( agf ); 
+                                       continue;
+                               }
+                                       
+                               agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
+
+                               if ( agf->agf_anlist == NULL ) {
+                                       Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
+                                               lud->lud_attrs[0], 0, 0 );              
+                                       /* FIXME: error? */
+                                       ldap_free_urldesc( lud );
+                                       ch_free( agf ); 
+                                       continue;
+                               }
+                       }
+
                        agf->agf_next = NULL;
 
 
@@ -508,11 +695,8 @@ cleanup:;
 static int
 autogroup_group_add_cb( Operation *op, SlapReply *rs )
 {
-       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
-
        assert( op->o_tag == LDAP_REQ_SEARCH );
 
-
        if ( rs->sr_type == REP_SEARCH ) {
                autogroup_sc_t          *ags = (autogroup_sc_t *)op->o_callback->sc_private;
 
@@ -533,12 +717,11 @@ autogroup_group_add_cb( Operation *op, SlapReply *rs )
 static int
 autogroup_add_entry( Operation *op, SlapReply *rs)
 {
-               slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
-       autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
+       autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
        autogroup_def_t         *agd = agi->agi_def;
-       autogroup_entry_t       *age = agi->agi_entry;
+       autogroup_entry_t       *age;
        autogroup_filter_t      *agf;
-       Attribute               *a;
        int                     rc = 0;
 
        Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n", 
@@ -568,7 +751,8 @@ autogroup_add_entry( Operation *op, SlapReply *rs)
                }
        }
 
-       for ( ; age ; age = age->age_next ) {
+       
+       for ( age = agi->agi_entry; age ; age = age->age_next ) {
                ldap_pvt_thread_mutex_lock( &age->age_mutex );          
 
                /* Check if any of the filters are the suffix to the entry DN. 
@@ -578,7 +762,11 @@ autogroup_add_entry( Operation *op, SlapReply *rs)
                        if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
                                rc = test_filter( op, op->ora_e, agf->agf_filter );
                                if ( rc == LDAP_COMPARE_TRUE ) {
-                               autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
+                                       if ( agf->agf_anlist ) {
+                                               autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
+                                       } else {
+                                               autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
+                                       }
                                        break;
                                }
                        }
@@ -629,6 +817,8 @@ autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
                                ch_free( agf->agf_filterstr.bv_val );
                                ch_free( agf->agf_dn.bv_val );
                                ch_free( agf->agf_ndn.bv_val );
+                               anlist_free( agf->agf_anlist, 1, NULL );
+                               ch_free( agf );
                        }
 
                        ldap_pvt_thread_mutex_unlock( &age->age_mutex );                
@@ -651,10 +841,8 @@ static int
 autogroup_delete_entry( Operation *op, SlapReply *rs)
 {
        slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
-       autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
-       autogroup_def_t         *agd = agi->agi_def;
-       autogroup_entry_t       *age = agi->agi_entry,
-                               *age_prev, *age_next;
+       autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
+       autogroup_entry_t       *age, *age_prev, *age_next;
        autogroup_filter_t      *agf;
        Entry                   *e;
        int                     matched_group = 0, rc = 0;
@@ -671,7 +859,8 @@ autogroup_delete_entry( Operation *op, SlapReply *rs)
        }
 
        /* Check if the entry to be deleted is one of our groups. */
-       for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
+       for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
+               age = age_next;
                ldap_pvt_thread_mutex_lock( &age->age_mutex );
                age_next = age->age_next;
 
@@ -683,9 +872,6 @@ autogroup_delete_entry( Operation *op, SlapReply *rs)
                        dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
 
                        if ( match == 0 ) {
-                               autogroup_filter_t      *agf = age->age_filter,
-                                                       *agf_next;
-
                                autogroup_delete_group( agi, age );
                                break;
                        }
@@ -710,7 +896,16 @@ autogroup_delete_entry( Operation *op, SlapReply *rs)
                        if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
                                rc = test_filter( op, e, agf->agf_filter );
                                if ( rc == LDAP_COMPARE_TRUE ) {
-                               autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
+                                       /* If the attribute is retrieved from the entry, we don't know what to delete
+                                       ** So the group must be entirely refreshed
+                                       ** But the refresh can't be done now because the entry is not deleted
+                                       ** So the group is marked as mustrefresh
+                                       */
+                                       if ( agf->agf_anlist ) {
+                                               age->age_mustrefresh = 1;
+                                       } else {
+                                               autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
+                                       }
                                        break;
                                }
                        }
@@ -728,16 +923,40 @@ static int
 autogroup_response( Operation *op, SlapReply *rs )
 {
        slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
-       autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
+       autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
        autogroup_def_t         *agd = agi->agi_def;
-       autogroup_entry_t       *age = agi->agi_entry;
+       autogroup_entry_t       *age;
        autogroup_filter_t      *agf;
        BerValue                new_dn, new_ndn, pdn;
        Entry                   *e, *group;
-       Attribute               *a;
-       int                     is_olddn, is_newdn, dn_equal;
+       Attribute               *a, *ea;
+       int                     is_olddn, is_newdn, is_value_refresh, dn_equal;
+
+       /* Handle all cases where a refresh of the group is needed */
+       if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
+               if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
+
+                       ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+                       for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+                               /* Request detected that the group must be refreshed */
+
+                               ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+                               if ( age->age_mustrefresh ) {
+                                       autogroup_delete_member_from_group( op, NULL, NULL, age) ;
 
-       if ( op->o_tag == LDAP_REQ_MODRDN ) {
+                                       for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+                                               autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
+                                       }
+                               }
+
+                               ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+                       }
+
+                       ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+               }
+       } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
                if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
 
                        Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
@@ -809,8 +1028,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 
@@ -824,59 +1041,106 @@ autogroup_response( Operation *op, SlapReply *rs )
                        for ( age = agi->agi_entry ; age ; age = age->age_next ) {
                                is_olddn = 0;
                                is_newdn = 0;
+                               is_value_refresh = 0;
 
 
                                ldap_pvt_thread_mutex_lock( &age->age_mutex );
 
-                               if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
-                                       LDAP_SUCCESS || group == NULL ) {
-                                       Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
+                               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;
+                               }
 
-                                       op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
-                                       op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+                               if ( age->age_modrdn_olddnmodified ) {
+                                       /* Resquest already marked this group to be updated */
+                                       is_olddn = 1;
+                                       is_value_refresh = 1;
+                                       age->age_modrdn_olddnmodified = 0;
+                               } else {
 
-                                       ldap_pvt_thread_mutex_unlock( &age->age_mutex );
-                                       ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
-                                       return SLAP_CB_CONTINUE;
-                               }
+                                       if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
+                                               LDAP_SUCCESS || group == NULL ) {
+                                               Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
 
-                               a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
+                                               op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+                                               op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
 
-                               if ( a != NULL ) {
-                                       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 ) 
-                                       {
-                                               is_olddn = 1;
+                                               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;
                                        }
 
-                               }
+                                       a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
 
-                               overlay_entry_release_ov( op, group, 0, on );
+                                       if ( a != NULL ) {
+                                               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, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
+                                               {
+                                                       is_olddn = 1;
+                                               }
+
+                                       }
+
+                                       overlay_entry_release_ov( op, group, 0, on );
+
+                               }
 
                                for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
                                        if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
+                                               /* TODO: should retest filter as it could imply conditions on the dn */
                                                is_newdn = 1;
                                                break;
                                        }
                                }
 
 
+                               if ( is_value_refresh ) {
+                                       if ( is_olddn != is_newdn ) {
+                                               /* 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 );
+                                               }
+                                       }
+                                       ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+                                       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 );
@@ -885,6 +1149,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 );                        
                }
        }
@@ -912,7 +1178,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 ) {
@@ -928,7 +1193,7 @@ autogroup_response( Operation *op, SlapReply *rs )
 
                                        m = op->orm_modlist;
 
-                                       for ( ; age ; age = age->age_next ) {
+                                       for ( age = agi->agi_entry ; age ; age = age->age_next ) {
                                                ldap_pvt_thread_mutex_lock( &age->age_mutex );
 
                                                dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
@@ -947,6 +1212,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;
                                                                }
@@ -965,8 +1231,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.
@@ -983,11 +1247,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;
@@ -999,7 +1271,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;
                                        }
@@ -1018,15 +1290,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 );
                }
        }
@@ -1034,6 +1314,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.
@@ -1044,7 +1352,7 @@ autogroup_modify_entry( Operation *op, SlapReply *rs)
        slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
        autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
        autogroup_def_t         *agd = agi->agi_def;
-       autogroup_entry_t       *age = agi->agi_entry;
+       autogroup_entry_t       *age;
        Entry                   *e;
        Attribute               *a;
 
@@ -1062,6 +1370,30 @@ autogroup_modify_entry( Operation *op, SlapReply *rs)
                return SLAP_CB_CONTINUE;
        }
 
+       /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
+       for ( age = agi->agi_entry; age ; age = age->age_next ) {
+               autogroup_filter_t      *agf;
+               for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+                       if ( agf->agf_anlist ) {
+                               Modifications   *m;
+                               for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
+                                       if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
+                                               if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+                                                       int rc = test_filter( op, e, agf->agf_filter );
+                                                       if ( rc == LDAP_COMPARE_TRUE ) {
+                                                               age->age_mustrefresh = 1;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+
+                       if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
+                               age->age_mustrefresh = 1;
+                       }
+               }
+       }
+
        a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
 
        if ( a == NULL ) {
@@ -1084,7 +1416,7 @@ autogroup_modify_entry( Operation *op, SlapReply *rs)
 
                        m = op->orm_modlist;
 
-                       for ( ; age ; age = age->age_next ) {
+                       for ( age = agi->agi_entry ; age ; age = age->age_next ) {
                                dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
 
                                if ( match == 0 ) {
@@ -1112,6 +1444,51 @@ autogroup_modify_entry( Operation *op, SlapReply *rs)
        return SLAP_CB_CONTINUE;
 }
 
+/*
+** Detect if the olddn is part of a group and so if the group should be refreshed
+*/
+static int
+autogroup_modrdn_entry( Operation *op, SlapReply *rs)
+{
+       slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
+       autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
+       autogroup_entry_t       *age;
+       Entry                   *e;
+
+       if ( get_manageDSAit( op ) ) {
+               return SLAP_CB_CONTINUE;
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
+       ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
+
+       if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+               LDAP_SUCCESS || e == NULL ) {
+               Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
+               ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+               return SLAP_CB_CONTINUE;
+       }
+
+       /* Must check if a dn is modified */
+       for ( age = agi->agi_entry; age ; age = age->age_next ) {
+               autogroup_filter_t      *agf;
+               for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+                       if ( agf->agf_anlist ) {
+                               if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+                                       int rc = test_filter( op, e, agf->agf_filter );
+                                       if ( rc == LDAP_COMPARE_TRUE ) {
+                                               age->age_modrdn_olddnmodified = 1;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       overlay_entry_release_ov( op, e, 0, on );
+       ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
+       return SLAP_CB_CONTINUE;
+}
+
 /* 
 ** Builds a filter for searching for the 
 ** group entries, according to the objectClass. 
@@ -1143,6 +1520,7 @@ autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
 
 enum {
        AG_ATTRSET = 1,
+       AG_MEMBER_OF_AD,
        AG_LAST
 };
 
@@ -1151,21 +1529,33 @@ static ConfigDriver     ag_cfgen;
 static ConfigTable agcfg[] = {
        { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
                3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
-               "( OLcfgOvAt:20.1 NAME 'olcAGattrSet' "
+               "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
                        "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
                        "EQUALITY caseIgnoreMatch "
                        "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 }
 };
 
 static ConfigOCs agocs[] = {
-       { "( OLcfgOvOc:20.1 "
+       { "( OLcfgCtOc:2.1 "
                "NAME 'olcAutomaticGroups' "
                "DESC 'Automatic groups configuration' "
                "SUP olcOverlayConfig "
-               "MAY olcAGattrSet )",
+               "MAY ( "
+                       "olcAGattrSet "
+                       "$ olcAGmemberOfAd "
+                   ")"
+         ")",
                Cft_Overlay, agcfg, NULL, NULL },
        { NULL, 0, NULL }
 };
@@ -1196,28 +1586,39 @@ ag_cfgen( ConfigArgs *c )
 
        if ( c->op == SLAP_CONFIG_EMIT ) {
 
-               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);
-
-                       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 );
+               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;
 
-                       bv.bv_val = c->cr_msg;
-                       bv.bv_len = ptr - bv.bv_val;
-                       value_add_one ( &c->rvalue_vals, &bv );
+               case AG_MEMBER_OF_AD:
+                       if ( agi->agi_memberof_ad != NULL ){
+                               value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
+                       }
+                       break;
 
-               }
-               ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+               default:
+                       assert( 0 );
+                       return 1;
+      }
 
                return rc;
 
@@ -1228,8 +1629,6 @@ ag_cfgen( ConfigArgs *c )
                        autogroup_filter_t      *agf = age->age_filter,
                                                *agf_next;
 
-                       ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
-
                        for ( agd_next = agd; agd_next; agd = agd_next ) {
                                agd_next = agd->agd_next;
 
@@ -1249,15 +1648,14 @@ ag_cfgen( ConfigArgs *c )
                                        ch_free( agf->agf_filterstr.bv_val );
                                        ch_free( agf->agf_dn.bv_val );
                                        ch_free( agf->agf_ndn.bv_val );
+                                       anlist_free( agf->agf_anlist, 1, NULL );
+                                       ch_free( agf );
                                }
 
                                ldap_pvt_thread_mutex_init( &age->age_mutex );
                                ch_free( age );
                        }
 
-                       ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
-
-                       ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
                        ch_free( agi );
                        on->on_bi.bi_private = NULL;
 
@@ -1266,9 +1664,6 @@ ag_cfgen( ConfigArgs *c )
                        autogroup_entry_t       *age_next, *age_prev;
                        autogroup_filter_t      *agf,
                                                *agf_next;
-                       struct berval           *bv;
-
-                       ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
 
                        for ( i = 0, agdp = &agi->agi_def;
                                i < c->valx; i++ ) 
@@ -1297,6 +1692,8 @@ ag_cfgen( ConfigArgs *c )
                                                ch_free( agf->agf_filterstr.bv_val );
                                                ch_free( agf->agf_dn.bv_val );
                                                ch_free( agf->agf_ndn.bv_val );
+                                               anlist_free( agf->agf_anlist, 1, NULL );
+                                               ch_free( agf );
                                        }
 
                                        ldap_pvt_thread_mutex_destroy( &age->age_mutex );
@@ -1312,7 +1709,6 @@ ag_cfgen( ConfigArgs *c )
 
                        ch_free( agd );
                        agd = agi->agi_def;
-                       ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
 
                }
 
@@ -1374,8 +1770,6 @@ ag_cfgen( ConfigArgs *c )
                        return 1;
                }
 
-               ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
-
                for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
                        /* The same URL attribute / member attribute pair
                        * cannot be repeated */
@@ -1405,7 +1799,6 @@ ag_cfgen( ConfigArgs *c )
                                        Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
                                                c->log, c->cr_msg, 0 );
 
-                                       ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
                                        return 1;
                                }
                                agdp = &(*agdp)->agd_next;
@@ -1425,7 +1818,36 @@ ag_cfgen( ConfigArgs *c )
                (*agdp)->agd_member_ad = member_ad;
                (*agdp)->agd_next = agd_next;
 
-               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;
+               }
+
+               agi->agi_memberof_ad = memberof_ad;
 
                } break;
 
@@ -1437,6 +1859,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.
@@ -1446,24 +1870,23 @@ autogroup_db_open(
        BackendDB       *be,
        ConfigReply     *cr )
 {
-       slap_overinst                   *on = (slap_overinst *) be->bd_info,
-                               *on_bd;
+       slap_overinst                   *on = (slap_overinst *) be->bd_info;
        autogroup_info_t                *agi = on->on_bi.bi_private;
        autogroup_def_t         *agd;
        autogroup_sc_t          ags;
        Operation               *op;
-       SlapReply               rs = { REP_RESULT };
        slap_callback           cb = { 0 };
 
        void                            *thrctx = ldap_pvt_thread_pool_context();
        Connection                      conn = { 0 };
        OperationBuffer         opbuf;
-       BerValue                bv;
-       char                    *ptr;
-       int                     rc = 0;
 
        Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
 
+       if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
+               return 0;
+       }
+
        connection_fake_init( &conn, &opbuf, thrctx );
        op = &opbuf.ob_op;
 
@@ -1482,31 +1905,42 @@ autogroup_db_open(
        op->ors_slimit = SLAP_NO_LIMIT;
        op->ors_attrs =  slap_anlist_no_attrs;
 
-       op->o_bd = select_backend(&op->o_req_ndn, 0);
+       op->o_bd = be;
+       op->o_bd->bd_info = (BackendInfo *)on->on_info;
+
+       ags.ags_info = agi;
+       cb.sc_private = &ags;
+       cb.sc_response = autogroup_group_add_cb;
+       cb.sc_cleanup = NULL;
+       cb.sc_next = NULL;
+
+       op->o_callback = &cb;
 
-       ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
        for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
+               SlapReply       rs = { REP_RESULT };
 
                autogroup_build_def_filter(agd, op);
 
-
-               ags.ags_info = agi;
                ags.ags_def = agd;
-               cb.sc_private = &ags;
-               cb.sc_response = autogroup_group_add_cb;
-               cb.sc_cleanup = NULL;
-               cb.sc_next = NULL;
 
-               op->o_callback = &cb;
-
-               op->o_bd->bd_info = (BackendInfo *)on->on_info;
                op->o_bd->be_search( op, &rs );
-               op->o_bd->bd_info = (BackendInfo *)on;
 
-               filter_free_x( op, op->ors_filter );
+               filter_free_x( op, op->ors_filter, 1 );
                op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
        }               
-       ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+
+       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;
 }
@@ -1541,6 +1975,7 @@ autogroup_db_close(
                                ch_free( agf->agf_filterstr.bv_val );
                                ch_free( agf->agf_dn.bv_val );
                                ch_free( agf->agf_ndn.bv_val ); 
+                               anlist_free( agf->agf_anlist, 1, NULL );
                                ch_free( agf );
                        }
 
@@ -1595,6 +2030,7 @@ autogroup_initialize(void)
        autogroup.on_bi.bi_op_add = autogroup_add_entry;
        autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
        autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
+       autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
 
        autogroup.on_response = autogroup_response;