1 /* autogroup.c - automatic group overlay */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2007-2014 The OpenLDAP Foundation.
6 * Portions Copyright 2007 Michał Szulczyński.
7 * Portions Copyright 2009 Howard Chu.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
19 * This work was initially developed by Michał Szulczyński for inclusion in
20 * OpenLDAP Software. Additional significant contributors include:
31 #include <ac/string.h>
37 #ifndef SLAPD_MEMBEROF_ATTR
38 #define SLAPD_MEMBEROF_ATTR "memberOf"
41 /* Filter represents the memberURL of a group. */
42 typedef struct autogroup_filter_t {
43 struct berval agf_dn; /* The base DN in memberURL */
44 struct berval agf_ndn;
45 struct berval agf_filterstr;
48 AttributeName *agf_anlist;
49 struct autogroup_filter_t *agf_next;
52 /* Description of group attributes. */
53 typedef struct autogroup_def_t {
55 AttributeDescription *agd_member_url_ad;
56 AttributeDescription *agd_member_ad;
57 struct autogroup_def_t *agd_next;
60 /* Represents the group entry. */
61 typedef struct autogroup_entry_t {
64 autogroup_filter_t *age_filter; /* List of filters made from memberURLs */
65 autogroup_def_t *age_def; /* Attribute definition */
66 ldap_pvt_thread_mutex_t age_mutex;
67 int age_mustrefresh; /* Defined in request to refresh in response */
68 int age_modrdn_olddnmodified; /* Defined in request to refresh in response */
69 struct autogroup_entry_t *age_next;
72 /* Holds pointers to attribute definitions and groups. */
73 typedef struct autogroup_info_t {
74 autogroup_def_t *agi_def; /* Group attributes definitions. */
75 autogroup_entry_t *agi_entry; /* Group entries. */
76 AttributeDescription *agi_memberof_ad; /* memberOf attribute description */
77 ldap_pvt_thread_mutex_t agi_mutex;
80 /* Search callback for adding groups initially. */
81 typedef struct autogroup_sc_t {
82 autogroup_info_t *ags_info; /* Group definitions and entries. */
83 autogroup_def_t *ags_def; /* Attributes definition of the group being added. */
86 /* Used for adding members, found when searching, to a group. */
87 typedef struct autogroup_ga_t {
88 autogroup_entry_t *agg_group; /* The group to which the members will be added. */
89 autogroup_filter_t *agg_filter; /* Current filter */
90 Entry *agg_entry; /* Used in autogroup_member_search_cb to modify
91 this entry with the search results. */
93 Modifications *agg_mod; /* Used in autogroup_member_search_modify_cb to hold the
94 search results which will be added to the group. */
96 Modifications *agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
97 have to search for the last mod added. */
102 ** dn, ndn - the DN of the member to add
103 ** age - the group to which the member DN will be added
106 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
108 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
109 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
110 SlapReply sreply = {REP_RESULT};
111 BerValue *vals, *nvals;
112 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
115 assert( dn != NULL );
116 assert( ndn != NULL );
117 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
118 dn->bv_val, age->age_dn.bv_val, 0);
120 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
121 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
122 ber_dupbv( vals, dn );
123 BER_BVZERO( &vals[ 1 ] );
124 ber_dupbv( nvals, ndn );
125 BER_BVZERO( &nvals[ 1 ] );
127 modlist->sml_op = LDAP_MOD_ADD;
128 modlist->sml_desc = age->age_def->agd_member_ad;
129 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
130 modlist->sml_values = vals;
131 modlist->sml_nvalues = nvals;
132 modlist->sml_numvals = 1;
133 modlist->sml_flags = SLAP_MOD_INTERNAL;
134 modlist->sml_next = NULL;
136 o.o_tag = LDAP_REQ_MODIFY;
138 o.orm_modlist = modlist;
139 o.o_req_dn = age->age_dn;
140 o.o_req_ndn = age->age_ndn;
141 o.o_permissive_modify = 1;
142 o.o_managedsait = SLAP_CONTROL_CRITICAL;
143 o.o_relax = SLAP_CONTROL_CRITICAL;
145 o.o_bd->bd_info = (BackendInfo *)on->on_info;
146 (void)op->o_bd->be_modify( &o, &sreply );
147 o.o_bd->bd_info = (BackendInfo *)on;
149 slap_mods_free( modlist, 1 );
151 return sreply.sr_err;
155 ** e - the entry where to get the attribute values
156 ** age - the group to which the values will be added
159 autogroup_add_member_values_to_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
161 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
162 Modifications modlist;
163 SlapReply sreply = {REP_RESULT};
165 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
169 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
170 e->e_name.bv_val, age->age_dn.bv_val, 0);
172 attr = attrs_find( e->e_attrs, attrdesc );
178 modlist.sml_op = LDAP_MOD_ADD;
179 modlist.sml_desc = age->age_def->agd_member_ad;
180 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
181 modlist.sml_values = attr->a_vals;
182 modlist.sml_nvalues = attr->a_nvals;
183 modlist.sml_numvals = attr->a_numvals;
184 modlist.sml_flags = SLAP_MOD_INTERNAL;
185 modlist.sml_next = NULL;
187 o.o_tag = LDAP_REQ_MODIFY;
189 o.orm_modlist = &modlist;
190 o.o_req_dn = age->age_dn;
191 o.o_req_ndn = age->age_ndn;
192 o.o_permissive_modify = 1;
193 o.o_managedsait = SLAP_CONTROL_CRITICAL;
194 o.o_relax = SLAP_CONTROL_CRITICAL;
196 o.o_bd->bd_info = (BackendInfo *)on->on_info;
197 (void)op->o_bd->be_modify( &o, &sreply );
198 o.o_bd->bd_info = (BackendInfo *)on;
200 return sreply.sr_err;
204 ** dn,ndn - the DN to be deleted
205 ** age - the group from which the DN will be deleted
206 ** If we pass a NULL dn and ndn, all members are deleted from the group.
209 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
211 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
212 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
213 SlapReply sreply = {REP_RESULT};
214 BerValue *vals, *nvals;
215 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
218 if ( dn == NULL || ndn == NULL ) {
219 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
220 age->age_dn.bv_val, 0 ,0);
222 modlist->sml_values = NULL;
223 modlist->sml_nvalues = NULL;
224 modlist->sml_numvals = 0;
226 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
227 dn->bv_val, age->age_dn.bv_val, 0);
229 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
230 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
231 ber_dupbv( vals, dn );
232 BER_BVZERO( &vals[ 1 ] );
233 ber_dupbv( nvals, ndn );
234 BER_BVZERO( &nvals[ 1 ] );
236 modlist->sml_values = vals;
237 modlist->sml_nvalues = nvals;
238 modlist->sml_numvals = 1;
242 modlist->sml_op = LDAP_MOD_DELETE;
243 modlist->sml_desc = age->age_def->agd_member_ad;
244 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
245 modlist->sml_flags = SLAP_MOD_INTERNAL;
246 modlist->sml_next = NULL;
249 o.o_tag = LDAP_REQ_MODIFY;
250 o.orm_modlist = modlist;
251 o.o_req_dn = age->age_dn;
252 o.o_req_ndn = age->age_ndn;
253 o.o_relax = SLAP_CONTROL_CRITICAL;
254 o.o_managedsait = SLAP_CONTROL_CRITICAL;
255 o.o_permissive_modify = 1;
257 o.o_bd->bd_info = (BackendInfo *)on->on_info;
258 (void)op->o_bd->be_modify( &o, &sreply );
259 o.o_bd->bd_info = (BackendInfo *)on;
261 slap_mods_free( modlist, 1 );
263 return sreply.sr_err;
267 ** e - the entry where to get the attribute values
268 ** age - the group from which the values will be deleted
271 autogroup_delete_member_values_from_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
273 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
274 Modifications modlist;
275 SlapReply sreply = {REP_RESULT};
277 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
281 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
282 e->e_name.bv_val, age->age_dn.bv_val, 0);
284 attr = attrs_find( e->e_attrs, attrdesc );
290 modlist.sml_op = LDAP_MOD_DELETE;
291 modlist.sml_desc = age->age_def->agd_member_ad;
292 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
293 modlist.sml_values = attr->a_vals;
294 modlist.sml_nvalues = attr->a_nvals;
295 modlist.sml_numvals = attr->a_numvals;
296 modlist.sml_flags = SLAP_MOD_INTERNAL;
297 modlist.sml_next = NULL;
299 o.o_tag = LDAP_REQ_MODIFY;
301 o.orm_modlist = &modlist;
302 o.o_req_dn = age->age_dn;
303 o.o_req_ndn = age->age_ndn;
304 o.o_permissive_modify = 1;
305 o.o_managedsait = SLAP_CONTROL_CRITICAL;
306 o.o_relax = SLAP_CONTROL_CRITICAL;
308 o.o_bd->bd_info = (BackendInfo *)on->on_info;
309 (void)op->o_bd->be_modify( &o, &sreply );
310 o.o_bd->bd_info = (BackendInfo *)on;
312 return sreply.sr_err;
316 ** Callback used to add entries to a group,
317 ** which are going to be written in the database
318 ** (used in bi_op_add)
319 ** The group is passed in autogroup_ga_t->agg_group
322 autogroup_member_search_cb( Operation *op, SlapReply *rs )
324 assert( op->o_tag == LDAP_REQ_SEARCH );
326 if ( rs->sr_type == REP_SEARCH ) {
327 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
328 autogroup_entry_t *age = agg->agg_group;
329 autogroup_filter_t *agf = agg->agg_filter;
331 const char *text = NULL;
333 struct berval *vals, *nvals;
334 struct berval lvals[ 2 ], lnvals[ 2 ];
337 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
338 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
340 if ( agf->agf_anlist ) {
341 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
344 nvals = attr->a_nvals;
345 numvals = attr->a_numvals;
351 lvals[ 0 ] = rs->sr_entry->e_name;
352 BER_BVZERO( &lvals[ 1 ] );
353 lnvals[ 0 ] = rs->sr_entry->e_nname;
354 BER_BVZERO( &lnvals[ 1 ] );
360 mod.sm_op = LDAP_MOD_ADD;
361 mod.sm_desc = age->age_def->agd_member_ad;
362 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
363 mod.sm_values = vals;
364 mod.sm_nvalues = nvals;
365 mod.sm_numvals = numvals;
367 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
374 ** Callback used to add entries to a group, which is already in the database.
375 ** (used in on_response)
376 ** The group is passed in autogroup_ga_t->agg_group
380 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
382 assert( op->o_tag == LDAP_REQ_SEARCH );
384 if ( rs->sr_type == REP_SEARCH ) {
385 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
386 autogroup_entry_t *age = agg->agg_group;
387 autogroup_filter_t *agf = agg->agg_filter;
388 Modifications *modlist;
389 struct berval *vals, *nvals;
390 struct berval lvals[ 2 ], lnvals[ 2 ];
393 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
394 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
396 if ( agf->agf_anlist ) {
397 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
400 nvals = attr->a_nvals;
401 numvals = attr->a_numvals;
407 lvals[ 0 ] = rs->sr_entry->e_name;
408 BER_BVZERO( &lvals[ 1 ] );
409 lnvals[ 0 ] = rs->sr_entry->e_nname;
410 BER_BVZERO( &lnvals[ 1 ] );
417 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
419 modlist->sml_op = LDAP_MOD_ADD;
420 modlist->sml_desc = age->age_def->agd_member_ad;
421 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
423 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
424 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
425 modlist->sml_numvals = numvals;
427 modlist->sml_flags = SLAP_MOD_INTERNAL;
428 modlist->sml_next = NULL;
430 if ( agg->agg_mod == NULL ) {
431 agg->agg_mod = modlist;
432 agg->agg_mod_last = modlist;
434 agg->agg_mod_last->sml_next = modlist;
435 agg->agg_mod_last = modlist;
446 ** Adds all entries matching the passed filter to the specified group.
447 ** If modify == 1, then we modify the group's entry in the database using be_modify.
448 ** If modify == 0, then, we must supply a rw entry for the group,
449 ** because we only modify the entry, without calling be_modify.
450 ** e - the group entry, to which the members will be added
455 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
457 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
459 SlapReply rs = { REP_SEARCH };
460 slap_callback cb = { 0 };
461 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
464 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
465 age->age_dn.bv_val, 0, 0);
468 o.o_tag = LDAP_REQ_SEARCH;
470 o.o_req_dn = agf->agf_dn;
471 o.o_req_ndn = agf->agf_ndn;
473 o.ors_filterstr = agf->agf_filterstr;
474 o.ors_filter = agf->agf_filter;
476 o.ors_scope = agf->agf_scope;
477 o.ors_deref = LDAP_DEREF_NEVER;
479 o.ors_tlimit = SLAP_NO_LIMIT;
480 o.ors_slimit = SLAP_NO_LIMIT;
481 o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
484 agg.agg_filter = agf;
486 agg.agg_mod_last = NULL;
488 cb.sc_private = &agg;
491 cb.sc_response = autogroup_member_search_modify_cb;
493 cb.sc_response = autogroup_member_search_cb;
496 cb.sc_cleanup = NULL;
501 o.o_bd->bd_info = (BackendInfo *)on->on_info;
502 op->o_bd->be_search( &o, &rs );
503 o.o_bd->bd_info = (BackendInfo *)on;
505 if ( modify == 1 && agg.agg_mod ) {
506 rs_reinit( &rs, REP_RESULT );
509 o.o_callback = &null_cb;
510 o.o_tag = LDAP_REQ_MODIFY;
511 o.orm_modlist = agg.agg_mod;
512 o.o_req_dn = age->age_dn;
513 o.o_req_ndn = age->age_ndn;
514 o.o_relax = SLAP_CONTROL_CRITICAL;
515 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
516 o.o_permissive_modify = 1;
518 o.o_bd->bd_info = (BackendInfo *)on->on_info;
519 (void)op->o_bd->be_modify( &o, &rs );
520 o.o_bd->bd_info = (BackendInfo *)on;
522 slap_mods_free(agg.agg_mod, 1);
529 ** Adds a group to the internal list from the passed entry.
530 ** scan specifies whether to add all maching members to the group.
531 ** modify specifies whether to modify the given group entry (when modify == 0),
532 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
533 ** agi - pointer to the groups and the attribute definitions
534 ** agd - the attribute definition of the added group
535 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
536 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
539 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
541 autogroup_entry_t **agep = &agi->agi_entry;
542 autogroup_filter_t *agf, *agf_prev = NULL;
543 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
544 LDAPURLDesc *lud = NULL;
547 int rc = 0, match = 1, null_entry = 0;
550 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
551 LDAP_SUCCESS || e == NULL ) {
552 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
559 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
560 e->e_name.bv_val, 0, 0);
562 if ( agi->agi_entry != NULL ) {
563 for ( ; *agep ; agep = &(*agep)->age_next ) {
564 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
566 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
574 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
575 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
576 (*agep)->age_def = agd;
577 (*agep)->age_filter = NULL;
578 (*agep)->age_mustrefresh = 0;
579 (*agep)->age_modrdn_olddnmodified = 0;
581 ber_dupbv( &(*agep)->age_dn, &e->e_name );
582 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
584 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
586 if ( null_entry == 1 ) {
588 overlay_entry_release_ov( op, e, 0, on );
592 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
594 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
596 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
598 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
599 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
605 agf->agf_scope = lud->lud_scope;
607 if ( lud->lud_dn == NULL ) {
608 BER_BVSTR( &dn, "" );
610 ber_str2bv( lud->lud_dn, 0, 0, &dn );
613 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
614 if ( rc != LDAP_SUCCESS ) {
615 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
620 if ( lud->lud_filter != NULL ) {
621 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
622 agf->agf_filter = str2filter( lud->lud_filter );
625 if ( lud->lud_attrs != NULL ) {
628 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
633 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
636 ldap_free_urldesc( lud );
641 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
643 if ( agf->agf_anlist == NULL ) {
644 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
645 lud->lud_attrs[0], 0, 0 );
647 ldap_free_urldesc( lud );
653 agf->agf_next = NULL;
656 if( (*agep)->age_filter == NULL ) {
657 (*agep)->age_filter = agf;
660 if( agf_prev != NULL ) {
661 agf_prev->agf_next = agf;
667 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
670 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
671 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
673 ldap_free_urldesc( lud );
680 ldap_free_urldesc( lud );
685 if ( null_entry == 1 ) {
692 ** Used when opening the database to add all existing
693 ** groups from the database to our internal list.
696 autogroup_group_add_cb( Operation *op, SlapReply *rs )
698 assert( op->o_tag == LDAP_REQ_SEARCH );
700 if ( rs->sr_type == REP_SEARCH ) {
701 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
703 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
704 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
706 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
712 typedef struct ag_addinfo {
715 autogroup_def_t *agd;
719 autogroup_add_entry_cb( Operation *op, SlapReply *rs )
721 slap_callback *sc = op->o_callback;
722 ag_addinfo *aa = sc->sc_private;
723 slap_overinst *on = aa->on;
724 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
725 BackendInfo *bi = op->o_bd->bd_info;
727 if ( rs->sr_err != LDAP_SUCCESS )
730 op->o_bd->bd_info = (BackendInfo *)on;
731 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
733 autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
735 autogroup_entry_t *age;
736 autogroup_filter_t *agf;
738 for ( age = agi->agi_entry; age ; age = age->age_next ) {
739 ldap_pvt_thread_mutex_lock( &age->age_mutex );
741 /* Check if any of the filters are the suffix to the entry DN.
742 If yes, we can test that filter against the entry. */
744 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
745 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
746 rc = test_filter( op, aa->e, agf->agf_filter );
747 if ( rc == LDAP_COMPARE_TRUE ) {
748 if ( agf->agf_anlist ) {
749 autogroup_add_member_values_to_group( op, aa->e, age, agf->agf_anlist[0].an_desc );
751 autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
757 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
760 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
762 op->o_bd->bd_info = bi;
765 op->o_callback = sc->sc_next;
766 op->o_tmpfree( sc, op->o_tmpmemctx );
768 return SLAP_CB_CONTINUE;
772 ** When adding a group, we first strip any existing members,
773 ** and add all which match the filters ourselfs.
776 autogroup_add_entry( Operation *op, SlapReply *rs)
778 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
779 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
780 autogroup_def_t *agd = agi->agi_def;
781 slap_callback *sc = NULL;
782 ag_addinfo *aa = NULL;
785 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
786 op->ora_e->e_name.bv_val, 0, 0);
788 sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
789 sc->sc_private = (sc+1);
790 sc->sc_response = autogroup_add_entry_cb;
794 sc->sc_next = op->o_callback;
797 /* Check if it's a group. */
798 for ( ; agd ; agd = agd->agd_next ) {
799 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
801 const char *text = NULL;
804 mod.sm_op = LDAP_MOD_DELETE;
805 mod.sm_desc = agd->agd_member_ad;
806 mod.sm_type = agd->agd_member_ad->ad_cname;
807 mod.sm_values = NULL;
808 mod.sm_nvalues = NULL;
810 /* We don't want any member attributes added by the user. */
811 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
819 return SLAP_CB_CONTINUE;
823 ** agi - internal group and attribute definitions list
824 ** e - the group to remove from the internal list
827 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
829 autogroup_entry_t *age = agi->agi_entry,
834 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
835 age->age_dn.bv_val, 0, 0);
837 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
838 age_next = age->age_next;
841 autogroup_filter_t *agf = age->age_filter,
844 if ( age_prev != NULL ) {
845 age_prev->age_next = age_next;
847 agi->agi_entry = NULL;
850 ch_free( age->age_dn.bv_val );
851 ch_free( age->age_ndn.bv_val );
853 for( agf_next = agf ; agf_next ; agf = agf_next ){
854 agf_next = agf->agf_next;
856 filter_free( agf->agf_filter );
857 ch_free( agf->agf_filterstr.bv_val );
858 ch_free( agf->agf_dn.bv_val );
859 ch_free( agf->agf_ndn.bv_val );
860 anlist_free( agf->agf_anlist, 1, NULL );
864 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
865 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
874 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
881 autogroup_delete_entry( Operation *op, SlapReply *rs)
883 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
884 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
885 autogroup_entry_t *age, *age_prev, *age_next;
886 autogroup_filter_t *agf;
888 int matched_group = 0, rc = 0;
890 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
892 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
894 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
895 LDAP_SUCCESS || e == NULL ) {
896 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
897 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
898 return SLAP_CB_CONTINUE;
901 /* Check if the entry to be deleted is one of our groups. */
902 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
904 ldap_pvt_thread_mutex_lock( &age->age_mutex );
905 age_next = age->age_next;
907 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
912 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
915 autogroup_delete_group( agi, age );
920 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
923 if ( matched_group == 1 ) {
924 overlay_entry_release_ov( op, e, 0, on );
925 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
926 return SLAP_CB_CONTINUE;
929 /* Check if the entry matches any of the groups.
930 If yes, we can delete the entry from that group. */
932 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
933 ldap_pvt_thread_mutex_lock( &age->age_mutex );
935 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
936 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
937 rc = test_filter( op, e, agf->agf_filter );
938 if ( rc == LDAP_COMPARE_TRUE ) {
939 /* If the attribute is retrieved from the entry, we don't know what to delete
940 ** So the group must be entirely refreshed
941 ** But the refresh can't be done now because the entry is not deleted
942 ** So the group is marked as mustrefresh
944 if ( agf->agf_anlist ) {
945 age->age_mustrefresh = 1;
947 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
953 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
956 overlay_entry_release_ov( op, e, 0, on );
957 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
959 return SLAP_CB_CONTINUE;
963 autogroup_response( Operation *op, SlapReply *rs )
965 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
966 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
967 autogroup_def_t *agd = agi->agi_def;
968 autogroup_entry_t *age;
969 autogroup_filter_t *agf;
970 BerValue new_dn, new_ndn, pdn;
973 int is_olddn, is_newdn, is_value_refresh, dn_equal;
975 /* Handle all cases where a refresh of the group is needed */
976 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
977 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
979 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
981 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
982 /* Request detected that the group must be refreshed */
984 ldap_pvt_thread_mutex_lock( &age->age_mutex );
986 if ( age->age_mustrefresh ) {
987 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
989 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
990 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
994 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
997 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
999 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
1000 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
1002 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
1004 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1006 if ( op->oq_modrdn.rs_newSup ) {
1007 pdn = *op->oq_modrdn.rs_newSup;
1009 dnParent( &op->o_req_dn, &pdn );
1011 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
1013 if ( op->oq_modrdn.rs_nnewSup ) {
1014 pdn = *op->oq_modrdn.rs_nnewSup;
1016 dnParent( &op->o_req_ndn, &pdn );
1018 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1020 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
1022 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
1024 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
1025 LDAP_SUCCESS || e == NULL ) {
1026 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
1027 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1028 return SLAP_CB_CONTINUE;
1031 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1035 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
1036 overlay_entry_release_ov( op, e, 0, on );
1037 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1038 return SLAP_CB_CONTINUE;
1042 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1043 for ( ; agd; agd = agd->agd_next ) {
1045 if ( value_find_ex( slap_schema.si_ad_objectClass,
1046 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1047 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1048 a->a_nvals, &agd->agd_oc->soc_cname,
1049 op->o_tmpmemctx ) == 0 )
1051 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1054 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1056 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1057 ber_dupbv( &age->age_dn, &new_dn );
1058 ber_dupbv( &age->age_ndn, &new_ndn );
1060 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1061 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1062 overlay_entry_release_ov( op, e, 0, on );
1063 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1064 return SLAP_CB_CONTINUE;
1072 1. check if the orginal entry's DN is in the group.
1073 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1075 If 1 and 2 are both false, we do nothing.
1076 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1077 If 1 is false, and 2 is true, we check the entry against the group's filters,
1078 and add it's DN to the group.
1079 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1081 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1084 is_value_refresh = 0;
1087 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1089 if ( age->age_filter && age->age_filter->agf_anlist ) {
1090 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1096 if ( age->age_modrdn_olddnmodified ) {
1097 /* Resquest already marked this group to be updated */
1099 is_value_refresh = 1;
1100 age->age_modrdn_olddnmodified = 0;
1103 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1104 LDAP_SUCCESS || group == NULL ) {
1105 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1107 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1108 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1110 overlay_entry_release_ov( op, e, 0, on );
1111 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1112 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1113 return SLAP_CB_CONTINUE;
1116 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1119 if ( value_find_ex( age->age_def->agd_member_ad,
1120 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1121 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1122 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1129 overlay_entry_release_ov( op, group, 0, on );
1133 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1134 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1135 /* TODO: should retest filter as it could imply conditions on the dn */
1142 if ( is_value_refresh ) {
1143 if ( is_olddn != is_newdn ) {
1145 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1147 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1148 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1151 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1154 if ( is_olddn == 1 && is_newdn == 0 ) {
1156 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1158 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1160 if ( is_olddn == 0 && is_newdn == 1 ) {
1161 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1162 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1164 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1166 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1171 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1174 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1176 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1177 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1181 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1182 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1186 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1189 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1190 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1192 overlay_entry_release_ov( op, e, 0, on );
1194 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1198 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1199 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1200 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1202 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1204 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1205 LDAP_SUCCESS || e == NULL ) {
1206 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1207 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1208 return SLAP_CB_CONTINUE;
1211 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1215 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1216 overlay_entry_release_ov( op, e, 0, on );
1217 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1218 return SLAP_CB_CONTINUE;
1221 /* If we modify a group's memberURL, we have to delete all of it's members,
1222 and add them anew, because we cannot tell from which memberURL a member was added. */
1223 for ( ; agd; agd = agd->agd_next ) {
1225 if ( value_find_ex( slap_schema.si_ad_objectClass,
1226 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1227 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1228 a->a_nvals, &agd->agd_oc->soc_cname,
1229 op->o_tmpmemctx ) == 0 )
1234 m = op->orm_modlist;
1236 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1237 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1239 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1242 for ( ; m ; m = m->sml_next ) {
1243 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1244 autogroup_def_t *group_agd = age->age_def;
1245 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1246 op->o_req_dn.bv_val, 0, 0);
1248 overlay_entry_release_ov( op, e, 0, on );
1250 autogroup_delete_member_from_group( op, NULL, NULL, age );
1251 autogroup_delete_group( agi, age );
1253 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1255 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1256 return SLAP_CB_CONTINUE;
1260 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1264 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1267 overlay_entry_release_ov( op, e, 0, on );
1268 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1269 return SLAP_CB_CONTINUE;
1273 /* When modifing any of the attributes of an entry, we must
1274 check if the entry is in any of our groups, and if
1275 the modified entry maches any of the filters of that group.
1277 If the entry exists in a group, but the modified attributes do
1278 not match any of the group's filters, we delete the entry from that group.
1279 If the entry doesn't exist in a group, but matches a filter,
1280 we add it to that group.
1282 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1287 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1289 if ( age->age_filter && age->age_filter->agf_anlist ) {
1290 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1296 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1297 LDAP_SUCCESS || group == NULL ) {
1298 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1299 age->age_dn.bv_val, 0, 0);
1301 overlay_entry_release_ov( op, e, 0, on );
1302 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1303 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1304 return SLAP_CB_CONTINUE;
1307 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1310 if ( value_find_ex( age->age_def->agd_member_ad,
1311 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1312 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1313 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1320 overlay_entry_release_ov( op, group, 0, on );
1322 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1323 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1324 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1331 if ( is_olddn == 1 && is_newdn == 0 ) {
1333 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1335 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1337 if ( is_olddn == 0 && is_newdn == 1 ) {
1339 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1341 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1344 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1347 overlay_entry_release_ov( op, e, 0, on );
1349 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1353 return SLAP_CB_CONTINUE;
1357 ** Detect if filter contains a memberOf check for dn
1360 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1363 if ( f == NULL ) return 0;
1365 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1366 case LDAP_FILTER_AND:
1367 case LDAP_FILTER_OR:
1368 case LDAP_FILTER_NOT:
1369 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1370 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1373 case LDAP_FILTER_EQUALITY:
1374 result = ( f->f_ava->aa_desc == memberof_ad &&
1375 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1385 ** When modifing a group, we must deny any modifications to the member attribute,
1386 ** because the group would be inconsistent.
1389 autogroup_modify_entry( Operation *op, SlapReply *rs)
1391 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1392 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1393 autogroup_def_t *agd = agi->agi_def;
1394 autogroup_entry_t *age;
1398 if ( get_manageDSAit( op ) ) {
1399 return SLAP_CB_CONTINUE;
1402 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1403 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1405 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1406 LDAP_SUCCESS || e == NULL ) {
1407 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1408 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1409 return SLAP_CB_CONTINUE;
1412 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1413 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1414 autogroup_filter_t *agf;
1415 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1416 if ( agf->agf_anlist ) {
1418 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1419 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1420 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1421 int rc = test_filter( op, e, agf->agf_filter );
1422 if ( rc == LDAP_COMPARE_TRUE ) {
1423 age->age_mustrefresh = 1;
1430 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1431 age->age_mustrefresh = 1;
1436 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1439 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1440 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1441 return SLAP_CB_CONTINUE;
1445 for ( ; agd; agd = agd->agd_next ) {
1447 if ( value_find_ex( slap_schema.si_ad_objectClass,
1448 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1449 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1450 a->a_nvals, &agd->agd_oc->soc_cname,
1451 op->o_tmpmemctx ) == 0 )
1456 m = op->orm_modlist;
1458 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1459 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1462 for ( ; m ; m = m->sml_next ) {
1463 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1464 overlay_entry_release_ov( op, e, 0, on );
1465 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1466 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1467 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1468 return LDAP_CONSTRAINT_VIOLATION;
1475 overlay_entry_release_ov( op, e, 0, on );
1476 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1477 return SLAP_CB_CONTINUE;
1481 overlay_entry_release_ov( op, e, 0, on );
1482 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1483 return SLAP_CB_CONTINUE;
1487 ** Detect if the olddn is part of a group and so if the group should be refreshed
1490 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1492 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1493 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1494 autogroup_entry_t *age;
1497 if ( get_manageDSAit( op ) ) {
1498 return SLAP_CB_CONTINUE;
1501 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1502 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1504 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1505 LDAP_SUCCESS || e == NULL ) {
1506 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1507 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1508 return SLAP_CB_CONTINUE;
1511 /* Must check if a dn is modified */
1512 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1513 autogroup_filter_t *agf;
1514 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1515 if ( agf->agf_anlist ) {
1516 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1517 int rc = test_filter( op, e, agf->agf_filter );
1518 if ( rc == LDAP_COMPARE_TRUE ) {
1519 age->age_modrdn_olddnmodified = 1;
1526 overlay_entry_release_ov( op, e, 0, on );
1527 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1528 return SLAP_CB_CONTINUE;
1532 ** Builds a filter for searching for the
1533 ** group entries, according to the objectClass.
1536 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1540 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1542 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1543 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1544 + agd->agd_oc->soc_cname.bv_len;
1545 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1547 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1549 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1553 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1555 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1566 static ConfigDriver ag_cfgen;
1568 static ConfigTable agcfg[] = {
1569 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1570 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1571 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1572 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1573 "EQUALITY caseIgnoreMatch "
1574 "SYNTAX OMsDirectoryString "
1575 "X-ORDERED 'VALUES' )",
1578 { "autogroup-memberof-ad", "memberOf attribute",
1579 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1580 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1581 "DESC 'memberOf attribute' "
1582 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1585 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1588 static ConfigOCs agocs[] = {
1589 { "( OLcfgCtOc:2.1 "
1590 "NAME 'olcAutomaticGroups' "
1591 "DESC 'Automatic groups configuration' "
1592 "SUP olcOverlayConfig "
1595 "$ olcAGmemberOfAd "
1598 Cft_Overlay, agcfg, NULL, NULL },
1604 ag_cfgen( ConfigArgs *c )
1606 slap_overinst *on = (slap_overinst *)c->bi;
1607 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1608 autogroup_def_t *agd;
1609 autogroup_entry_t *age;
1613 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1616 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1617 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1618 agi->agi_def = NULL;
1619 agi->agi_entry = NULL;
1620 on->on_bi.bi_private = (void *)agi;
1624 age = agi->agi_entry;
1626 if ( c->op == SLAP_CONFIG_EMIT ) {
1630 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1632 char *ptr = c->cr_msg;
1634 assert(agd->agd_oc != NULL);
1635 assert(agd->agd_member_url_ad != NULL);
1636 assert(agd->agd_member_ad != NULL);
1638 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1639 SLAP_X_ORDERED_FMT "%s %s %s", i,
1640 agd->agd_oc->soc_cname.bv_val,
1641 agd->agd_member_url_ad->ad_cname.bv_val,
1642 agd->agd_member_ad->ad_cname.bv_val );
1644 bv.bv_val = c->cr_msg;
1645 bv.bv_len = ptr - bv.bv_val;
1646 value_add_one ( &c->rvalue_vals, &bv );
1651 case AG_MEMBER_OF_AD:
1652 if ( agi->agi_memberof_ad != NULL ){
1653 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1664 }else if ( c->op == LDAP_MOD_DELETE ) {
1666 autogroup_def_t *agd_next;
1667 autogroup_entry_t *age_next;
1668 autogroup_filter_t *agf = age->age_filter,
1671 for ( agd_next = agd; agd_next; agd = agd_next ) {
1672 agd_next = agd->agd_next;
1677 for ( age_next = age ; age_next ; age = age_next ) {
1678 age_next = age->age_next;
1680 ch_free( age->age_dn.bv_val );
1681 ch_free( age->age_ndn.bv_val );
1683 for( agf_next = agf ; agf_next ; agf = agf_next ){
1684 agf_next = agf->agf_next;
1686 filter_free( agf->agf_filter );
1687 ch_free( agf->agf_filterstr.bv_val );
1688 ch_free( agf->agf_dn.bv_val );
1689 ch_free( agf->agf_ndn.bv_val );
1690 anlist_free( agf->agf_anlist, 1, NULL );
1694 ldap_pvt_thread_mutex_init( &age->age_mutex );
1699 on->on_bi.bi_private = NULL;
1702 autogroup_def_t **agdp;
1703 autogroup_entry_t *age_next, *age_prev;
1704 autogroup_filter_t *agf,
1707 for ( i = 0, agdp = &agi->agi_def;
1710 if ( *agdp == NULL) {
1713 agdp = &(*agdp)->agd_next;
1717 *agdp = agd->agd_next;
1719 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1720 age_next = age->age_next;
1722 if( age->age_def == agd ) {
1723 agf = age->age_filter;
1725 ch_free( age->age_dn.bv_val );
1726 ch_free( age->age_ndn.bv_val );
1728 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1729 agf_next = agf->agf_next;
1730 filter_free( agf->agf_filter );
1731 ch_free( agf->agf_filterstr.bv_val );
1732 ch_free( agf->agf_dn.bv_val );
1733 ch_free( agf->agf_ndn.bv_val );
1734 anlist_free( agf->agf_anlist, 1, NULL );
1738 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1743 if( age_prev != NULL ) {
1744 age_prev->age_next = age_next;
1759 autogroup_def_t **agdp,
1761 ObjectClass *oc = NULL;
1762 AttributeDescription *member_url_ad = NULL,
1767 oc = oc_find( c->argv[ 1 ] );
1769 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1770 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1771 "unable to find ObjectClass \"%s\"",
1773 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1774 c->log, c->cr_msg, 0 );
1779 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1780 if( rc != LDAP_SUCCESS ) {
1781 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1782 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1783 "unable to find AttributeDescription \"%s\"",
1785 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1786 c->log, c->cr_msg, 0 );
1790 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1791 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1792 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1793 "AttributeDescription \"%s\" ",
1794 "must be of a subtype \"labeledURI\"",
1796 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1797 c->log, c->cr_msg, 0 );
1801 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1802 if( rc != LDAP_SUCCESS ) {
1803 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1804 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1805 "unable to find AttributeDescription \"%s\"",
1807 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1808 c->log, c->cr_msg, 0 );
1812 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1813 /* The same URL attribute / member attribute pair
1814 * cannot be repeated */
1816 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1817 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1818 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1819 "URL attributeDescription \"%s\" already mapped",
1820 member_ad->ad_cname.bv_val );
1821 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1822 c->log, c->cr_msg, 0 );
1823 /* return 1; //warning*/
1827 if ( c->valx > 0 ) {
1830 for ( i = 0, agdp = &agi->agi_def ;
1833 if ( *agdp == NULL ) {
1834 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1835 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1836 "invalid index {%d}",
1838 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1839 c->log, c->cr_msg, 0 );
1843 agdp = &(*agdp)->agd_next;
1848 for ( agdp = &agi->agi_def; *agdp;
1849 agdp = &(*agdp)->agd_next )
1853 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1855 (*agdp)->agd_oc = oc;
1856 (*agdp)->agd_member_url_ad = member_url_ad;
1857 (*agdp)->agd_member_ad = member_ad;
1858 (*agdp)->agd_next = agd_next;
1862 case AG_MEMBER_OF_AD: {
1863 AttributeDescription *memberof_ad = NULL;
1866 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1867 if( rc != LDAP_SUCCESS ) {
1868 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1869 "\"autogroup-memberof-ad <memberof-ad>\": "
1870 "unable to find AttributeDescription \"%s\"",
1872 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1873 c->log, c->cr_msg, 0 );
1877 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1878 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1880 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1881 "memberof attribute=\"%s\" must either "
1882 "have DN (%s) or nameUID (%s) syntax",
1883 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1884 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1885 c->log, c->cr_msg, 0 );
1889 agi->agi_memberof_ad = memberof_ad;
1901 extern int slapMode;
1904 ** Do a search for all the groups in the
1905 ** database, and add them to out internal list.
1912 slap_overinst *on = (slap_overinst *) be->bd_info;
1913 autogroup_info_t *agi = on->on_bi.bi_private;
1914 autogroup_def_t *agd;
1917 slap_callback cb = { 0 };
1919 void *thrctx = ldap_pvt_thread_pool_context();
1920 Connection conn = { 0 };
1921 OperationBuffer opbuf;
1923 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1925 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1929 connection_fake_init( &conn, &opbuf, thrctx );
1932 op->ors_attrsonly = 0;
1933 op->o_tag = LDAP_REQ_SEARCH;
1934 op->o_dn = be->be_rootdn;
1935 op->o_ndn = be->be_rootndn;
1937 op->o_req_dn = be->be_suffix[0];
1938 op->o_req_ndn = be->be_nsuffix[0];
1940 op->ors_scope = LDAP_SCOPE_SUBTREE;
1941 op->ors_deref = LDAP_DEREF_NEVER;
1942 op->ors_limit = NULL;
1943 op->ors_tlimit = SLAP_NO_LIMIT;
1944 op->ors_slimit = SLAP_NO_LIMIT;
1945 op->ors_attrs = slap_anlist_no_attrs;
1948 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1951 cb.sc_private = &ags;
1952 cb.sc_response = autogroup_group_add_cb;
1953 cb.sc_cleanup = NULL;
1956 op->o_callback = &cb;
1958 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1959 SlapReply rs = { REP_RESULT };
1961 autogroup_build_def_filter(agd, op);
1965 op->o_bd->be_search( op, &rs );
1967 filter_free_x( op, op->ors_filter, 1 );
1968 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1971 if( ! agi->agi_memberof_ad ){
1973 const char *text = NULL;
1975 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1976 if ( rc != LDAP_SUCCESS ) {
1977 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1978 "unable to find attribute=\"%s\": %s (%d)\n",
1979 SLAPD_MEMBEROF_ATTR, text, rc );
1992 slap_overinst *on = (slap_overinst *) be->bd_info;
1994 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1996 if ( on->on_bi.bi_private ) {
1997 autogroup_info_t *agi = on->on_bi.bi_private;
1998 autogroup_entry_t *age = agi->agi_entry,
2000 autogroup_filter_t *agf, *agf_next;
2002 for ( age_next = age; age_next; age = age_next ) {
2003 age_next = age->age_next;
2005 ch_free( age->age_dn.bv_val );
2006 ch_free( age->age_ndn.bv_val );
2008 agf = age->age_filter;
2010 for ( agf_next = agf; agf_next; agf = agf_next ) {
2011 agf_next = agf->agf_next;
2013 filter_free( agf->agf_filter );
2014 ch_free( agf->agf_filterstr.bv_val );
2015 ch_free( agf->agf_dn.bv_val );
2016 ch_free( agf->agf_ndn.bv_val );
2017 anlist_free( agf->agf_anlist, 1, NULL );
2021 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2030 autogroup_db_destroy(
2034 slap_overinst *on = (slap_overinst *) be->bd_info;
2036 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2038 if ( on->on_bi.bi_private ) {
2039 autogroup_info_t *agi = on->on_bi.bi_private;
2040 autogroup_def_t *agd = agi->agi_def,
2043 for ( agd_next = agd; agd_next; agd = agd_next ) {
2044 agd_next = agd->agd_next;
2049 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2056 static slap_overinst autogroup = { { NULL } };
2060 autogroup_initialize(void)
2063 autogroup.on_bi.bi_type = "autogroup";
2065 autogroup.on_bi.bi_db_open = autogroup_db_open;
2066 autogroup.on_bi.bi_db_close = autogroup_db_close;
2067 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2069 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2070 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2071 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2072 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2074 autogroup.on_response = autogroup_response;
2076 autogroup.on_bi.bi_cf_ocs = agocs;
2078 rc = config_register_schema( agcfg, agocs );
2083 return overlay_register( &autogroup );
2087 init_module( int argc, char *argv[] )
2089 return autogroup_initialize();