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, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
161 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
162 Modifications modlist;
163 SlapReply sreply = {REP_RESULT};
164 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
167 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
168 dn->bv_val, age->age_dn.bv_val, 0);
170 modlist.sml_op = LDAP_MOD_ADD;
171 modlist.sml_desc = age->age_def->agd_member_ad;
172 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
173 modlist.sml_values = attr->a_vals;
174 modlist.sml_nvalues = attr->a_nvals;
175 modlist.sml_numvals = attr->a_numvals;
176 modlist.sml_flags = SLAP_MOD_INTERNAL;
177 modlist.sml_next = NULL;
179 o.o_tag = LDAP_REQ_MODIFY;
181 o.orm_modlist = &modlist;
182 o.o_req_dn = age->age_dn;
183 o.o_req_ndn = age->age_ndn;
184 o.o_permissive_modify = 1;
185 o.o_managedsait = SLAP_CONTROL_CRITICAL;
186 o.o_relax = SLAP_CONTROL_CRITICAL;
188 o.o_bd->bd_info = (BackendInfo *)on->on_info;
189 (void)op->o_bd->be_modify( &o, &sreply );
190 o.o_bd->bd_info = (BackendInfo *)on;
192 return sreply.sr_err;
196 ** dn,ndn - the DN to be deleted
197 ** age - the group from which the DN will be deleted
198 ** If we pass a NULL dn and ndn, all members are deleted from the group.
201 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
203 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
204 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
205 SlapReply sreply = {REP_RESULT};
206 BerValue *vals, *nvals;
207 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
210 if ( dn == NULL || ndn == NULL ) {
211 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
212 age->age_dn.bv_val, 0 ,0);
214 modlist->sml_values = NULL;
215 modlist->sml_nvalues = NULL;
216 modlist->sml_numvals = 0;
218 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
219 dn->bv_val, age->age_dn.bv_val, 0);
221 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
222 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
223 ber_dupbv( vals, dn );
224 BER_BVZERO( &vals[ 1 ] );
225 ber_dupbv( nvals, ndn );
226 BER_BVZERO( &nvals[ 1 ] );
228 modlist->sml_values = vals;
229 modlist->sml_nvalues = nvals;
230 modlist->sml_numvals = 1;
234 modlist->sml_op = LDAP_MOD_DELETE;
235 modlist->sml_desc = age->age_def->agd_member_ad;
236 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
237 modlist->sml_flags = SLAP_MOD_INTERNAL;
238 modlist->sml_next = NULL;
241 o.o_tag = LDAP_REQ_MODIFY;
242 o.orm_modlist = modlist;
243 o.o_req_dn = age->age_dn;
244 o.o_req_ndn = age->age_ndn;
245 o.o_relax = SLAP_CONTROL_CRITICAL;
246 o.o_managedsait = SLAP_CONTROL_CRITICAL;
247 o.o_permissive_modify = 1;
249 o.o_bd->bd_info = (BackendInfo *)on->on_info;
250 (void)op->o_bd->be_modify( &o, &sreply );
251 o.o_bd->bd_info = (BackendInfo *)on;
253 slap_mods_free( modlist, 1 );
255 return sreply.sr_err;
259 ** e - the entry where to get the attribute values
260 ** age - the group from which the values will be deleted
263 autogroup_delete_member_values_from_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
265 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
266 Modifications modlist;
267 SlapReply sreply = {REP_RESULT};
268 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
271 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
272 dn->bv_val, age->age_dn.bv_val, 0);
274 modlist.sml_op = LDAP_MOD_DELETE;
275 modlist.sml_desc = age->age_def->agd_member_ad;
276 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
277 modlist.sml_values = attr->a_vals;
278 modlist.sml_nvalues = attr->a_nvals;
279 modlist.sml_numvals = attr->a_numvals;
280 modlist.sml_flags = SLAP_MOD_INTERNAL;
281 modlist.sml_next = NULL;
283 o.o_tag = LDAP_REQ_MODIFY;
285 o.orm_modlist = &modlist;
286 o.o_req_dn = age->age_dn;
287 o.o_req_ndn = age->age_ndn;
288 o.o_permissive_modify = 1;
289 o.o_managedsait = SLAP_CONTROL_CRITICAL;
290 o.o_relax = SLAP_CONTROL_CRITICAL;
292 o.o_bd->bd_info = (BackendInfo *)on->on_info;
293 (void)op->o_bd->be_modify( &o, &sreply );
294 o.o_bd->bd_info = (BackendInfo *)on;
296 return sreply.sr_err;
300 ** Callback used to add entries to a group,
301 ** which are going to be written in the database
302 ** (used in bi_op_add)
303 ** The group is passed in autogroup_ga_t->agg_group
306 autogroup_member_search_cb( Operation *op, SlapReply *rs )
308 assert( op->o_tag == LDAP_REQ_SEARCH );
310 if ( rs->sr_type == REP_SEARCH ) {
311 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
312 autogroup_entry_t *age = agg->agg_group;
313 autogroup_filter_t *agf = agg->agg_filter;
315 const char *text = NULL;
317 struct berval *vals, *nvals;
318 struct berval lvals[ 2 ], lnvals[ 2 ];
321 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
322 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
324 if ( agf->agf_anlist ) {
325 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
328 nvals = attr->a_nvals;
329 numvals = attr->a_numvals;
335 lvals[ 0 ] = rs->sr_entry->e_name;
336 BER_BVZERO( &lvals[ 1 ] );
337 lnvals[ 0 ] = rs->sr_entry->e_nname;
338 BER_BVZERO( &lnvals[ 1 ] );
344 mod.sm_op = LDAP_MOD_ADD;
345 mod.sm_desc = age->age_def->agd_member_ad;
346 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
347 mod.sm_values = vals;
348 mod.sm_nvalues = nvals;
349 mod.sm_numvals = numvals;
351 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
358 ** Callback used to add entries to a group, which is already in the database.
359 ** (used in on_response)
360 ** The group is passed in autogroup_ga_t->agg_group
364 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
366 assert( op->o_tag == LDAP_REQ_SEARCH );
368 if ( rs->sr_type == REP_SEARCH ) {
369 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
370 autogroup_entry_t *age = agg->agg_group;
371 autogroup_filter_t *agf = agg->agg_filter;
372 Modifications *modlist;
373 struct berval *vals, *nvals;
374 struct berval lvals[ 2 ], lnvals[ 2 ];
377 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
378 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
380 if ( agf->agf_anlist ) {
381 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
384 nvals = attr->a_nvals;
385 numvals = attr->a_numvals;
391 lvals[ 0 ] = rs->sr_entry->e_name;
392 BER_BVZERO( &lvals[ 1 ] );
393 lnvals[ 0 ] = rs->sr_entry->e_nname;
394 BER_BVZERO( &lnvals[ 1 ] );
401 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
403 modlist->sml_op = LDAP_MOD_ADD;
404 modlist->sml_desc = age->age_def->agd_member_ad;
405 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
407 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
408 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
409 modlist->sml_numvals = numvals;
411 modlist->sml_flags = SLAP_MOD_INTERNAL;
412 modlist->sml_next = NULL;
414 if ( agg->agg_mod == NULL ) {
415 agg->agg_mod = modlist;
416 agg->agg_mod_last = modlist;
418 agg->agg_mod_last->sml_next = modlist;
419 agg->agg_mod_last = modlist;
430 ** Adds all entries matching the passed filter to the specified group.
431 ** If modify == 1, then we modify the group's entry in the database using be_modify.
432 ** If modify == 0, then, we must supply a rw entry for the group,
433 ** because we only modify the entry, without calling be_modify.
434 ** e - the group entry, to which the members will be added
439 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
441 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
443 SlapReply rs = { REP_SEARCH };
444 slap_callback cb = { 0 };
445 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
448 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
449 age->age_dn.bv_val, 0, 0);
452 o.o_tag = LDAP_REQ_SEARCH;
454 o.o_req_dn = agf->agf_dn;
455 o.o_req_ndn = agf->agf_ndn;
457 o.ors_filterstr = agf->agf_filterstr;
458 o.ors_filter = agf->agf_filter;
460 o.ors_scope = agf->agf_scope;
461 o.ors_deref = LDAP_DEREF_NEVER;
463 o.ors_tlimit = SLAP_NO_LIMIT;
464 o.ors_slimit = SLAP_NO_LIMIT;
465 o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
468 agg.agg_filter = agf;
470 agg.agg_mod_last = NULL;
472 cb.sc_private = &agg;
475 cb.sc_response = autogroup_member_search_modify_cb;
477 cb.sc_response = autogroup_member_search_cb;
480 cb.sc_cleanup = NULL;
485 o.o_bd->bd_info = (BackendInfo *)on->on_info;
486 op->o_bd->be_search( &o, &rs );
487 o.o_bd->bd_info = (BackendInfo *)on;
489 if ( modify == 1 && agg.agg_mod ) {
490 rs_reinit( &rs, REP_RESULT );
493 o.o_callback = &null_cb;
494 o.o_tag = LDAP_REQ_MODIFY;
495 o.orm_modlist = agg.agg_mod;
496 o.o_req_dn = age->age_dn;
497 o.o_req_ndn = age->age_ndn;
498 o.o_relax = SLAP_CONTROL_CRITICAL;
499 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
500 o.o_permissive_modify = 1;
502 o.o_bd->bd_info = (BackendInfo *)on->on_info;
503 (void)op->o_bd->be_modify( &o, &rs );
504 o.o_bd->bd_info = (BackendInfo *)on;
506 slap_mods_free(agg.agg_mod, 1);
513 ** Adds a group to the internal list from the passed entry.
514 ** scan specifies whether to add all maching members to the group.
515 ** modify specifies whether to modify the given group entry (when modify == 0),
516 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
517 ** agi - pointer to the groups and the attribute definitions
518 ** agd - the attribute definition of the added group
519 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
520 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
523 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
525 autogroup_entry_t **agep = &agi->agi_entry;
526 autogroup_filter_t *agf, *agf_prev = NULL;
527 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
528 LDAPURLDesc *lud = NULL;
531 int rc = 0, match = 1, null_entry = 0;
534 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
535 LDAP_SUCCESS || e == NULL ) {
536 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
543 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
544 e->e_name.bv_val, 0, 0);
546 if ( agi->agi_entry != NULL ) {
547 for ( ; *agep ; agep = &(*agep)->age_next ) {
548 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
550 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
558 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
559 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
560 (*agep)->age_def = agd;
561 (*agep)->age_filter = NULL;
562 (*agep)->age_mustrefresh = 0;
563 (*agep)->age_modrdn_olddnmodified = 0;
565 ber_dupbv( &(*agep)->age_dn, &e->e_name );
566 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
568 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
570 if ( null_entry == 1 ) {
572 overlay_entry_release_ov( op, e, 0, on );
576 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
578 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
580 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
582 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
583 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
589 agf->agf_scope = lud->lud_scope;
591 if ( lud->lud_dn == NULL ) {
592 BER_BVSTR( &dn, "" );
594 ber_str2bv( lud->lud_dn, 0, 0, &dn );
597 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
598 if ( rc != LDAP_SUCCESS ) {
599 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
604 if ( lud->lud_filter != NULL ) {
605 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
606 agf->agf_filter = str2filter( lud->lud_filter );
608 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: URL filter is missing <%s>\n", bv->bv_val,0,0);
613 if ( lud->lud_attrs != NULL ) {
616 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
621 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
624 filter_free( agf->agf_filter );
625 ch_free( agf->agf_filterstr.bv_val );
626 ch_free( agf->agf_dn.bv_val );
627 ch_free( agf->agf_ndn.bv_val );
628 ldap_free_urldesc( lud );
633 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
635 if ( agf->agf_anlist == NULL ) {
636 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
637 lud->lud_attrs[0], 0, 0 );
639 filter_free( agf->agf_filter );
640 ch_free( agf->agf_filterstr.bv_val );
641 ch_free( agf->agf_dn.bv_val );
642 ch_free( agf->agf_ndn.bv_val );
643 ldap_free_urldesc( lud );
649 agf->agf_next = NULL;
651 if( (*agep)->age_filter == NULL ) {
652 (*agep)->age_filter = agf;
655 if( agf_prev != NULL ) {
656 agf_prev->agf_next = agf;
662 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
665 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
666 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
668 ldap_free_urldesc( lud );
675 ch_free( agf->agf_ndn.bv_val );
676 ch_free( agf->agf_dn.bv_val );
677 ldap_free_urldesc( lud );
682 if ( null_entry == 1 ) {
689 ** Used when opening the database to add all existing
690 ** groups from the database to our internal list.
693 autogroup_group_add_cb( Operation *op, SlapReply *rs )
695 assert( op->o_tag == LDAP_REQ_SEARCH );
697 if ( rs->sr_type == REP_SEARCH ) {
698 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
700 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
701 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
703 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
709 typedef struct ag_addinfo {
712 autogroup_def_t *agd;
716 autogroup_add_entry_cb( Operation *op, SlapReply *rs )
718 slap_callback *sc = op->o_callback;
719 ag_addinfo *aa = sc->sc_private;
720 slap_overinst *on = aa->on;
721 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
722 BackendInfo *bi = op->o_bd->bd_info;
724 if ( rs->sr_err != LDAP_SUCCESS )
727 op->o_bd->bd_info = (BackendInfo *)on;
728 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
730 autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
732 autogroup_entry_t *age;
733 autogroup_filter_t *agf;
735 for ( age = agi->agi_entry; age ; age = age->age_next ) {
736 ldap_pvt_thread_mutex_lock( &age->age_mutex );
738 /* Check if any of the filters are the suffix to the entry DN.
739 If yes, we can test that filter against the entry. */
741 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
742 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
743 rc = test_filter( op, aa->e, agf->agf_filter );
744 if ( rc == LDAP_COMPARE_TRUE ) {
745 if ( agf->agf_anlist ) {
746 Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
748 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
750 autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
756 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
759 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
761 op->o_bd->bd_info = bi;
764 op->o_callback = sc->sc_next;
765 op->o_tmpfree( sc, op->o_tmpmemctx );
767 return SLAP_CB_CONTINUE;
771 ** When adding a group, we first strip any existing members,
772 ** and add all which match the filters ourselfs.
775 autogroup_add_entry( Operation *op, SlapReply *rs)
777 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
778 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
779 autogroup_def_t *agd = agi->agi_def;
780 slap_callback *sc = NULL;
781 ag_addinfo *aa = NULL;
784 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
785 op->ora_e->e_name.bv_val, 0, 0);
787 sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
788 sc->sc_private = (sc+1);
789 sc->sc_response = autogroup_add_entry_cb;
793 sc->sc_next = op->o_callback;
796 /* Check if it's a group. */
797 for ( ; agd ; agd = agd->agd_next ) {
798 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
800 const char *text = NULL;
803 mod.sm_op = LDAP_MOD_DELETE;
804 mod.sm_desc = agd->agd_member_ad;
805 mod.sm_type = agd->agd_member_ad->ad_cname;
806 mod.sm_values = NULL;
807 mod.sm_nvalues = NULL;
809 /* We don't want any member attributes added by the user. */
810 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
818 return SLAP_CB_CONTINUE;
822 ** agi - internal group and attribute definitions list
823 ** e - the group to remove from the internal list
826 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
828 autogroup_entry_t *age = agi->agi_entry,
833 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
834 age->age_dn.bv_val, 0, 0);
836 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
837 age_next = age->age_next;
840 autogroup_filter_t *agf = age->age_filter,
843 if ( age_prev != NULL ) {
844 age_prev->age_next = age_next;
846 agi->agi_entry = NULL;
849 ch_free( age->age_dn.bv_val );
850 ch_free( age->age_ndn.bv_val );
852 for( agf_next = agf ; agf_next ; agf = agf_next ){
853 agf_next = agf->agf_next;
855 filter_free( agf->agf_filter );
856 ch_free( agf->agf_filterstr.bv_val );
857 ch_free( agf->agf_dn.bv_val );
858 ch_free( agf->agf_ndn.bv_val );
859 anlist_free( agf->agf_anlist, 1, NULL );
863 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
864 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
873 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
880 autogroup_delete_entry( Operation *op, SlapReply *rs)
882 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
883 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
884 autogroup_entry_t *age, *age_prev, *age_next;
885 autogroup_filter_t *agf;
887 int matched_group = 0, rc = 0;
889 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
891 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
893 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
894 LDAP_SUCCESS || e == NULL ) {
895 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
896 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
897 return SLAP_CB_CONTINUE;
900 /* Check if the entry to be deleted is one of our groups. */
901 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
903 ldap_pvt_thread_mutex_lock( &age->age_mutex );
904 age_next = age->age_next;
906 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
911 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
914 autogroup_delete_group( agi, age );
919 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
922 if ( matched_group == 1 ) {
923 overlay_entry_release_ov( op, e, 0, on );
924 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
925 return SLAP_CB_CONTINUE;
928 /* Check if the entry matches any of the groups.
929 If yes, we can delete the entry from that group. */
931 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
932 ldap_pvt_thread_mutex_lock( &age->age_mutex );
934 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
935 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
936 rc = test_filter( op, e, agf->agf_filter );
937 if ( rc == LDAP_COMPARE_TRUE ) {
938 /* If the attribute is retrieved from the entry, we don't know what to delete
939 ** So the group must be entirely refreshed
940 ** But the refresh can't be done now because the entry is not deleted
941 ** So the group is marked as mustrefresh
943 if ( agf->agf_anlist ) {
944 age->age_mustrefresh = 1;
946 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
952 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
955 overlay_entry_release_ov( op, e, 0, on );
956 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
958 return SLAP_CB_CONTINUE;
962 autogroup_response( Operation *op, SlapReply *rs )
964 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
965 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
966 autogroup_def_t *agd = agi->agi_def;
967 autogroup_entry_t *age;
968 autogroup_filter_t *agf;
969 BerValue new_dn, new_ndn, pdn;
971 Attribute *a, *ea, *attrs;
972 int is_olddn, is_newdn, is_value_refresh, dn_equal;
974 /* Handle all cases where a refresh of the group is needed */
975 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
976 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
978 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
980 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
981 /* Request detected that the group must be refreshed */
983 ldap_pvt_thread_mutex_lock( &age->age_mutex );
985 if ( age->age_mustrefresh ) {
986 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
988 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
989 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
993 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
996 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
998 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
999 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
1001 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
1003 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1005 if ( op->oq_modrdn.rs_newSup ) {
1006 pdn = *op->oq_modrdn.rs_newSup;
1008 dnParent( &op->o_req_dn, &pdn );
1010 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
1012 if ( op->oq_modrdn.rs_nnewSup ) {
1013 pdn = *op->oq_modrdn.rs_nnewSup;
1015 dnParent( &op->o_req_ndn, &pdn );
1017 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1019 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
1021 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
1023 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
1024 LDAP_SUCCESS || e == NULL ) {
1025 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
1026 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1027 return SLAP_CB_CONTINUE;
1030 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1034 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
1035 overlay_entry_release_ov( op, e, 0, on );
1036 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1037 return SLAP_CB_CONTINUE;
1041 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1042 for ( ; agd; agd = agd->agd_next ) {
1044 if ( value_find_ex( slap_schema.si_ad_objectClass,
1045 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1046 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1047 a->a_nvals, &agd->agd_oc->soc_cname,
1048 op->o_tmpmemctx ) == 0 )
1050 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1053 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1055 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1056 ber_dupbv( &age->age_dn, &new_dn );
1057 ber_dupbv( &age->age_ndn, &new_ndn );
1059 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1060 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1061 overlay_entry_release_ov( op, e, 0, on );
1062 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1063 return SLAP_CB_CONTINUE;
1071 1. check if the orginal entry's DN is in the group.
1072 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1074 If 1 and 2 are both false, we do nothing.
1075 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1076 If 1 is false, and 2 is true, we check the entry against the group's filters,
1077 and add it's DN to the group.
1078 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1080 attrs = attrs_dup( e->e_attrs );
1081 overlay_entry_release_ov( op, e, 0, on );
1082 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1085 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( 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 attrs_free( attrs );
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, &new_dn, age, ea );
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, &new_dn, age, ea );
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 attrs_free( attrs );
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 modifying 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 attrs = attrs_dup( e->e_attrs );
1283 overlay_entry_release_ov( op, e, 0, on );
1284 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1289 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1291 if ( age->age_filter && age->age_filter->agf_anlist ) {
1292 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1298 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1299 LDAP_SUCCESS || group == NULL ) {
1300 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1301 age->age_dn.bv_val, 0, 0);
1303 attrs_free( attrs );
1304 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1305 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1306 return SLAP_CB_CONTINUE;
1309 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1312 if ( value_find_ex( age->age_def->agd_member_ad,
1313 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1314 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1315 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1322 overlay_entry_release_ov( op, group, 0, on );
1324 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1325 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1326 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1333 if ( is_olddn == 1 && is_newdn == 0 ) {
1335 autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
1337 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1339 if ( is_olddn == 0 && is_newdn == 1 ) {
1341 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
1343 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1346 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1349 attrs_free( attrs );
1351 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1355 return SLAP_CB_CONTINUE;
1359 ** Detect if filter contains a memberOf check for dn
1362 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1365 if ( f == NULL ) return 0;
1367 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1368 case LDAP_FILTER_AND:
1369 case LDAP_FILTER_OR:
1370 case LDAP_FILTER_NOT:
1371 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1372 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1375 case LDAP_FILTER_EQUALITY:
1376 result = ( f->f_ava->aa_desc == memberof_ad &&
1377 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1387 ** When modifing a group, we must deny any modifications to the member attribute,
1388 ** because the group would be inconsistent.
1391 autogroup_modify_entry( Operation *op, SlapReply *rs)
1393 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1394 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1395 autogroup_def_t *agd = agi->agi_def;
1396 autogroup_entry_t *age;
1400 if ( get_manageDSAit( op ) ) {
1401 return SLAP_CB_CONTINUE;
1404 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1405 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1407 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1408 LDAP_SUCCESS || e == NULL ) {
1409 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1410 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1411 return SLAP_CB_CONTINUE;
1414 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1415 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1416 autogroup_filter_t *agf;
1417 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1418 if ( agf->agf_anlist ) {
1420 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1421 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1422 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1423 int rc = test_filter( op, e, agf->agf_filter );
1424 if ( rc == LDAP_COMPARE_TRUE ) {
1425 age->age_mustrefresh = 1;
1432 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1433 age->age_mustrefresh = 1;
1438 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1441 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1442 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1443 return SLAP_CB_CONTINUE;
1447 for ( ; agd; agd = agd->agd_next ) {
1449 if ( value_find_ex( slap_schema.si_ad_objectClass,
1450 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1451 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1452 a->a_nvals, &agd->agd_oc->soc_cname,
1453 op->o_tmpmemctx ) == 0 )
1458 m = op->orm_modlist;
1460 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1461 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1464 for ( ; m ; m = m->sml_next ) {
1465 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1466 overlay_entry_release_ov( op, e, 0, on );
1467 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1468 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1469 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1470 return LDAP_CONSTRAINT_VIOLATION;
1477 overlay_entry_release_ov( op, e, 0, on );
1478 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1479 return SLAP_CB_CONTINUE;
1483 overlay_entry_release_ov( op, e, 0, on );
1484 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1485 return SLAP_CB_CONTINUE;
1489 ** Detect if the olddn is part of a group and so if the group should be refreshed
1492 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1494 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1495 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1496 autogroup_entry_t *age;
1499 if ( get_manageDSAit( op ) ) {
1500 return SLAP_CB_CONTINUE;
1503 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1504 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1506 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1507 LDAP_SUCCESS || e == NULL ) {
1508 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1509 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1510 return SLAP_CB_CONTINUE;
1513 /* Must check if a dn is modified */
1514 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1515 autogroup_filter_t *agf;
1516 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1517 if ( agf->agf_anlist ) {
1518 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1519 int rc = test_filter( op, e, agf->agf_filter );
1520 if ( rc == LDAP_COMPARE_TRUE ) {
1521 age->age_modrdn_olddnmodified = 1;
1528 overlay_entry_release_ov( op, e, 0, on );
1529 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1530 return SLAP_CB_CONTINUE;
1534 ** Builds a filter for searching for the
1535 ** group entries, according to the objectClass.
1538 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1542 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1544 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1545 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1546 + agd->agd_oc->soc_cname.bv_len;
1547 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1549 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1551 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1555 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1557 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1568 static ConfigDriver ag_cfgen;
1570 static ConfigTable agcfg[] = {
1571 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1572 4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1573 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1574 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1575 "EQUALITY caseIgnoreMatch "
1576 "SYNTAX OMsDirectoryString "
1577 "X-ORDERED 'VALUES' )",
1580 { "autogroup-memberof-ad", "memberOf attribute",
1581 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1582 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1583 "DESC 'memberOf attribute' "
1584 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1587 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1590 static ConfigOCs agocs[] = {
1591 { "( OLcfgCtOc:2.1 "
1592 "NAME 'olcAutomaticGroups' "
1593 "DESC 'Automatic groups configuration' "
1594 "SUP olcOverlayConfig "
1597 "$ olcAGmemberOfAd "
1600 Cft_Overlay, agcfg, NULL, NULL },
1606 ag_cfgen( ConfigArgs *c )
1608 slap_overinst *on = (slap_overinst *)c->bi;
1609 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1610 autogroup_def_t *agd;
1611 autogroup_entry_t *age;
1615 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1618 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1619 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1620 agi->agi_def = NULL;
1621 agi->agi_entry = NULL;
1622 on->on_bi.bi_private = (void *)agi;
1626 age = agi->agi_entry;
1628 if ( c->op == SLAP_CONFIG_EMIT ) {
1632 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1634 char *ptr = c->cr_msg;
1636 assert(agd->agd_oc != NULL);
1637 assert(agd->agd_member_url_ad != NULL);
1638 assert(agd->agd_member_ad != NULL);
1640 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1641 SLAP_X_ORDERED_FMT "%s %s %s", i,
1642 agd->agd_oc->soc_cname.bv_val,
1643 agd->agd_member_url_ad->ad_cname.bv_val,
1644 agd->agd_member_ad->ad_cname.bv_val );
1646 bv.bv_val = c->cr_msg;
1647 bv.bv_len = ptr - bv.bv_val;
1648 value_add_one ( &c->rvalue_vals, &bv );
1653 case AG_MEMBER_OF_AD:
1654 if ( agi->agi_memberof_ad != NULL ){
1655 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1666 }else if ( c->op == LDAP_MOD_DELETE ) {
1668 autogroup_def_t *agd_next;
1669 autogroup_entry_t *age_next;
1670 autogroup_filter_t *agf = age->age_filter,
1673 for ( agd_next = agd; agd_next; agd = agd_next ) {
1674 agd_next = agd->agd_next;
1679 for ( age_next = age ; age_next ; age = age_next ) {
1680 age_next = age->age_next;
1682 ch_free( age->age_dn.bv_val );
1683 ch_free( age->age_ndn.bv_val );
1685 for( agf_next = agf ; agf_next ; agf = agf_next ){
1686 agf_next = agf->agf_next;
1688 filter_free( agf->agf_filter );
1689 ch_free( agf->agf_filterstr.bv_val );
1690 ch_free( agf->agf_dn.bv_val );
1691 ch_free( agf->agf_ndn.bv_val );
1692 anlist_free( agf->agf_anlist, 1, NULL );
1696 ldap_pvt_thread_mutex_init( &age->age_mutex );
1701 on->on_bi.bi_private = NULL;
1704 autogroup_def_t **agdp;
1705 autogroup_entry_t *age_next, *age_prev;
1706 autogroup_filter_t *agf,
1709 for ( i = 0, agdp = &agi->agi_def;
1712 if ( *agdp == NULL) {
1715 agdp = &(*agdp)->agd_next;
1719 *agdp = agd->agd_next;
1721 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1722 age_next = age->age_next;
1724 if( age->age_def == agd ) {
1725 agf = age->age_filter;
1727 ch_free( age->age_dn.bv_val );
1728 ch_free( age->age_ndn.bv_val );
1730 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1731 agf_next = agf->agf_next;
1732 filter_free( agf->agf_filter );
1733 ch_free( agf->agf_filterstr.bv_val );
1734 ch_free( agf->agf_dn.bv_val );
1735 ch_free( agf->agf_ndn.bv_val );
1736 anlist_free( agf->agf_anlist, 1, NULL );
1740 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1745 if( age_prev != NULL ) {
1746 age_prev->age_next = age_next;
1761 autogroup_def_t **agdp,
1763 ObjectClass *oc = NULL;
1764 AttributeDescription *member_url_ad = NULL,
1769 oc = oc_find( c->argv[ 1 ] );
1771 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1772 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1773 "unable to find ObjectClass \"%s\"",
1775 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1776 c->log, c->cr_msg, 0 );
1781 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1782 if( rc != LDAP_SUCCESS ) {
1783 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1784 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1785 "unable to find AttributeDescription \"%s\"",
1787 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1788 c->log, c->cr_msg, 0 );
1792 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1793 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1794 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1795 "AttributeDescription \"%s\" ",
1796 "must be of a subtype \"labeledURI\"",
1798 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1799 c->log, c->cr_msg, 0 );
1803 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1804 if( rc != LDAP_SUCCESS ) {
1805 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1806 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1807 "unable to find AttributeDescription \"%s\"",
1809 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1810 c->log, c->cr_msg, 0 );
1814 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1815 /* The same URL attribute / member attribute pair
1816 * cannot be repeated */
1818 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1819 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1820 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1821 "URL attributeDescription \"%s\" already mapped",
1822 member_ad->ad_cname.bv_val );
1823 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1824 c->log, c->cr_msg, 0 );
1825 /* return 1; //warning*/
1829 if ( c->valx > 0 ) {
1832 for ( i = 0, agdp = &agi->agi_def ;
1835 if ( *agdp == NULL ) {
1836 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1837 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1838 "invalid index {%d}",
1840 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1841 c->log, c->cr_msg, 0 );
1845 agdp = &(*agdp)->agd_next;
1850 for ( agdp = &agi->agi_def; *agdp;
1851 agdp = &(*agdp)->agd_next )
1855 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1857 (*agdp)->agd_oc = oc;
1858 (*agdp)->agd_member_url_ad = member_url_ad;
1859 (*agdp)->agd_member_ad = member_ad;
1860 (*agdp)->agd_next = agd_next;
1864 case AG_MEMBER_OF_AD: {
1865 AttributeDescription *memberof_ad = NULL;
1868 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1869 if( rc != LDAP_SUCCESS ) {
1870 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1871 "\"autogroup-memberof-ad <memberof-ad>\": "
1872 "unable to find AttributeDescription \"%s\"",
1874 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1875 c->log, c->cr_msg, 0 );
1879 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1880 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1882 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1883 "memberof attribute=\"%s\" must either "
1884 "have DN (%s) or nameUID (%s) syntax",
1885 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1886 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1887 c->log, c->cr_msg, 0 );
1891 agi->agi_memberof_ad = memberof_ad;
1903 extern int slapMode;
1906 ** Do a search for all the groups in the
1907 ** database, and add them to out internal list.
1914 slap_overinst *on = (slap_overinst *) be->bd_info;
1915 autogroup_info_t *agi = on->on_bi.bi_private;
1916 autogroup_def_t *agd;
1919 slap_callback cb = { 0 };
1921 void *thrctx = ldap_pvt_thread_pool_context();
1922 Connection conn = { 0 };
1923 OperationBuffer opbuf;
1925 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1927 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1931 connection_fake_init( &conn, &opbuf, thrctx );
1934 op->ors_attrsonly = 0;
1935 op->o_tag = LDAP_REQ_SEARCH;
1936 op->o_dn = be->be_rootdn;
1937 op->o_ndn = be->be_rootndn;
1939 op->o_req_dn = be->be_suffix[0];
1940 op->o_req_ndn = be->be_nsuffix[0];
1942 op->ors_scope = LDAP_SCOPE_SUBTREE;
1943 op->ors_deref = LDAP_DEREF_NEVER;
1944 op->ors_limit = NULL;
1945 op->ors_tlimit = SLAP_NO_LIMIT;
1946 op->ors_slimit = SLAP_NO_LIMIT;
1947 op->ors_attrs = slap_anlist_no_attrs;
1950 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1953 cb.sc_private = &ags;
1954 cb.sc_response = autogroup_group_add_cb;
1955 cb.sc_cleanup = NULL;
1958 op->o_callback = &cb;
1960 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1961 SlapReply rs = { REP_RESULT };
1963 autogroup_build_def_filter(agd, op);
1967 op->o_bd->be_search( op, &rs );
1969 filter_free_x( op, op->ors_filter, 1 );
1970 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1973 if( ! agi->agi_memberof_ad ){
1975 const char *text = NULL;
1977 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1978 if ( rc != LDAP_SUCCESS ) {
1979 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1980 "unable to find attribute=\"%s\": %s (%d)\n",
1981 SLAPD_MEMBEROF_ATTR, text, rc );
1994 slap_overinst *on = (slap_overinst *) be->bd_info;
1996 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1998 if ( on->on_bi.bi_private ) {
1999 autogroup_info_t *agi = on->on_bi.bi_private;
2000 autogroup_entry_t *age = agi->agi_entry,
2002 autogroup_filter_t *agf, *agf_next;
2004 for ( age_next = age; age_next; age = age_next ) {
2005 age_next = age->age_next;
2007 ch_free( age->age_dn.bv_val );
2008 ch_free( age->age_ndn.bv_val );
2010 agf = age->age_filter;
2012 for ( agf_next = agf; agf_next; agf = agf_next ) {
2013 agf_next = agf->agf_next;
2015 filter_free( agf->agf_filter );
2016 ch_free( agf->agf_filterstr.bv_val );
2017 ch_free( agf->agf_dn.bv_val );
2018 ch_free( agf->agf_ndn.bv_val );
2019 anlist_free( agf->agf_anlist, 1, NULL );
2023 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2032 autogroup_db_destroy(
2036 slap_overinst *on = (slap_overinst *) be->bd_info;
2038 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2040 if ( on->on_bi.bi_private ) {
2041 autogroup_info_t *agi = on->on_bi.bi_private;
2042 autogroup_def_t *agd = agi->agi_def,
2045 for ( agd_next = agd; agd_next; agd = agd_next ) {
2046 agd_next = agd->agd_next;
2051 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2058 static slap_overinst autogroup = { { NULL } };
2062 autogroup_initialize(void)
2065 autogroup.on_bi.bi_type = "autogroup";
2067 autogroup.on_bi.bi_db_open = autogroup_db_open;
2068 autogroup.on_bi.bi_db_close = autogroup_db_close;
2069 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2071 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2072 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2073 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2074 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2076 autogroup.on_response = autogroup_response;
2078 autogroup.on_bi.bi_cf_ocs = agocs;
2080 rc = config_register_schema( agcfg, agocs );
2085 return overlay_register( &autogroup );
2089 init_module( int argc, char *argv[] )
2091 return autogroup_initialize();