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 );
609 if ( lud->lud_attrs != NULL ) {
612 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
617 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
620 ldap_free_urldesc( lud );
625 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
627 if ( agf->agf_anlist == NULL ) {
628 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
629 lud->lud_attrs[0], 0, 0 );
631 ldap_free_urldesc( lud );
637 agf->agf_next = NULL;
640 if( (*agep)->age_filter == NULL ) {
641 (*agep)->age_filter = agf;
644 if( agf_prev != NULL ) {
645 agf_prev->agf_next = agf;
651 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
654 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
655 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
657 ldap_free_urldesc( lud );
664 ldap_free_urldesc( lud );
669 if ( null_entry == 1 ) {
676 ** Used when opening the database to add all existing
677 ** groups from the database to our internal list.
680 autogroup_group_add_cb( Operation *op, SlapReply *rs )
682 assert( op->o_tag == LDAP_REQ_SEARCH );
684 if ( rs->sr_type == REP_SEARCH ) {
685 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
687 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
688 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
690 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
696 typedef struct ag_addinfo {
699 autogroup_def_t *agd;
703 autogroup_add_entry_cb( Operation *op, SlapReply *rs )
705 slap_callback *sc = op->o_callback;
706 ag_addinfo *aa = sc->sc_private;
707 slap_overinst *on = aa->on;
708 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
709 BackendInfo *bi = op->o_bd->bd_info;
711 if ( rs->sr_err != LDAP_SUCCESS )
714 op->o_bd->bd_info = (BackendInfo *)on;
715 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
717 autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
719 autogroup_entry_t *age;
720 autogroup_filter_t *agf;
722 for ( age = agi->agi_entry; age ; age = age->age_next ) {
723 ldap_pvt_thread_mutex_lock( &age->age_mutex );
725 /* Check if any of the filters are the suffix to the entry DN.
726 If yes, we can test that filter against the entry. */
728 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
729 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
730 rc = test_filter( op, aa->e, agf->agf_filter );
731 if ( rc == LDAP_COMPARE_TRUE ) {
732 if ( agf->agf_anlist ) {
733 Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
735 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
737 autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
743 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
746 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
748 op->o_bd->bd_info = bi;
751 op->o_callback = sc->sc_next;
752 op->o_tmpfree( sc, op->o_tmpmemctx );
754 return SLAP_CB_CONTINUE;
758 ** When adding a group, we first strip any existing members,
759 ** and add all which match the filters ourselfs.
762 autogroup_add_entry( Operation *op, SlapReply *rs)
764 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
765 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
766 autogroup_def_t *agd = agi->agi_def;
767 slap_callback *sc = NULL;
768 ag_addinfo *aa = NULL;
771 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
772 op->ora_e->e_name.bv_val, 0, 0);
774 sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
775 sc->sc_private = (sc+1);
776 sc->sc_response = autogroup_add_entry_cb;
780 sc->sc_next = op->o_callback;
783 /* Check if it's a group. */
784 for ( ; agd ; agd = agd->agd_next ) {
785 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
787 const char *text = NULL;
790 mod.sm_op = LDAP_MOD_DELETE;
791 mod.sm_desc = agd->agd_member_ad;
792 mod.sm_type = agd->agd_member_ad->ad_cname;
793 mod.sm_values = NULL;
794 mod.sm_nvalues = NULL;
796 /* We don't want any member attributes added by the user. */
797 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
805 return SLAP_CB_CONTINUE;
809 ** agi - internal group and attribute definitions list
810 ** e - the group to remove from the internal list
813 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
815 autogroup_entry_t *age = agi->agi_entry,
820 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
821 age->age_dn.bv_val, 0, 0);
823 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
824 age_next = age->age_next;
827 autogroup_filter_t *agf = age->age_filter,
830 if ( age_prev != NULL ) {
831 age_prev->age_next = age_next;
833 agi->agi_entry = NULL;
836 ch_free( age->age_dn.bv_val );
837 ch_free( age->age_ndn.bv_val );
839 for( agf_next = agf ; agf_next ; agf = agf_next ){
840 agf_next = agf->agf_next;
842 filter_free( agf->agf_filter );
843 ch_free( agf->agf_filterstr.bv_val );
844 ch_free( agf->agf_dn.bv_val );
845 ch_free( agf->agf_ndn.bv_val );
846 anlist_free( agf->agf_anlist, 1, NULL );
850 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
851 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
860 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
867 autogroup_delete_entry( Operation *op, SlapReply *rs)
869 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
870 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
871 autogroup_entry_t *age, *age_prev, *age_next;
872 autogroup_filter_t *agf;
874 int matched_group = 0, rc = 0;
876 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
878 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
880 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
881 LDAP_SUCCESS || e == NULL ) {
882 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
883 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
884 return SLAP_CB_CONTINUE;
887 /* Check if the entry to be deleted is one of our groups. */
888 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
890 ldap_pvt_thread_mutex_lock( &age->age_mutex );
891 age_next = age->age_next;
893 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
898 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
901 autogroup_delete_group( agi, age );
906 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
909 if ( matched_group == 1 ) {
910 overlay_entry_release_ov( op, e, 0, on );
911 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
912 return SLAP_CB_CONTINUE;
915 /* Check if the entry matches any of the groups.
916 If yes, we can delete the entry from that group. */
918 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
919 ldap_pvt_thread_mutex_lock( &age->age_mutex );
921 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
922 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
923 rc = test_filter( op, e, agf->agf_filter );
924 if ( rc == LDAP_COMPARE_TRUE ) {
925 /* If the attribute is retrieved from the entry, we don't know what to delete
926 ** So the group must be entirely refreshed
927 ** But the refresh can't be done now because the entry is not deleted
928 ** So the group is marked as mustrefresh
930 if ( agf->agf_anlist ) {
931 age->age_mustrefresh = 1;
933 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
939 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
942 overlay_entry_release_ov( op, e, 0, on );
943 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
945 return SLAP_CB_CONTINUE;
949 autogroup_response( Operation *op, SlapReply *rs )
951 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
952 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
953 autogroup_def_t *agd = agi->agi_def;
954 autogroup_entry_t *age;
955 autogroup_filter_t *agf;
956 BerValue new_dn, new_ndn, pdn;
958 Attribute *a, *ea, *attrs;
959 int is_olddn, is_newdn, is_value_refresh, dn_equal;
961 /* Handle all cases where a refresh of the group is needed */
962 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
963 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
965 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
967 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
968 /* Request detected that the group must be refreshed */
970 ldap_pvt_thread_mutex_lock( &age->age_mutex );
972 if ( age->age_mustrefresh ) {
973 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
975 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
976 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
980 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
983 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
985 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
986 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
988 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
990 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
992 if ( op->oq_modrdn.rs_newSup ) {
993 pdn = *op->oq_modrdn.rs_newSup;
995 dnParent( &op->o_req_dn, &pdn );
997 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
999 if ( op->oq_modrdn.rs_nnewSup ) {
1000 pdn = *op->oq_modrdn.rs_nnewSup;
1002 dnParent( &op->o_req_ndn, &pdn );
1004 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1006 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
1008 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
1010 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
1011 LDAP_SUCCESS || e == NULL ) {
1012 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
1013 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1014 return SLAP_CB_CONTINUE;
1017 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1021 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
1022 overlay_entry_release_ov( op, e, 0, on );
1023 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1024 return SLAP_CB_CONTINUE;
1028 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1029 for ( ; agd; agd = agd->agd_next ) {
1031 if ( value_find_ex( slap_schema.si_ad_objectClass,
1032 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1033 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1034 a->a_nvals, &agd->agd_oc->soc_cname,
1035 op->o_tmpmemctx ) == 0 )
1037 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1040 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1042 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1043 ber_dupbv( &age->age_dn, &new_dn );
1044 ber_dupbv( &age->age_ndn, &new_ndn );
1046 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1047 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1048 overlay_entry_release_ov( op, e, 0, on );
1049 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1050 return SLAP_CB_CONTINUE;
1058 1. check if the orginal entry's DN is in the group.
1059 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1061 If 1 and 2 are both false, we do nothing.
1062 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1063 If 1 is false, and 2 is true, we check the entry against the group's filters,
1064 and add it's DN to the group.
1065 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1067 attrs = attrs_dup( e->e_attrs );
1068 overlay_entry_release_ov( op, e, 0, on );
1069 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1072 is_value_refresh = 0;
1074 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1076 if ( age->age_filter && age->age_filter->agf_anlist ) {
1077 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1083 if ( age->age_modrdn_olddnmodified ) {
1084 /* Resquest already marked this group to be updated */
1086 is_value_refresh = 1;
1087 age->age_modrdn_olddnmodified = 0;
1090 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1091 LDAP_SUCCESS || group == NULL ) {
1092 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1094 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1095 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1097 attrs_free( attrs );
1098 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1099 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1100 return SLAP_CB_CONTINUE;
1103 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1106 if ( value_find_ex( age->age_def->agd_member_ad,
1107 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1108 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1109 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1116 overlay_entry_release_ov( op, group, 0, on );
1120 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1121 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1122 /* TODO: should retest filter as it could imply conditions on the dn */
1129 if ( is_value_refresh ) {
1130 if ( is_olddn != is_newdn ) {
1132 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1134 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1135 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1138 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1141 if ( is_olddn == 1 && is_newdn == 0 ) {
1143 autogroup_delete_member_values_from_group( op, &new_dn, age, ea );
1145 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1147 if ( is_olddn == 0 && is_newdn == 1 ) {
1148 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1149 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1151 autogroup_add_member_values_to_group( op, &new_dn, age, ea );
1153 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1158 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1161 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1163 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1164 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1168 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1169 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1173 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1176 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1177 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1179 attrs_free( attrs );
1181 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1185 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1186 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1187 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1189 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1191 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1192 LDAP_SUCCESS || e == NULL ) {
1193 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1194 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1195 return SLAP_CB_CONTINUE;
1198 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1202 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1203 overlay_entry_release_ov( op, e, 0, on );
1204 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1205 return SLAP_CB_CONTINUE;
1208 /* If we modify a group's memberURL, we have to delete all of it's members,
1209 and add them anew, because we cannot tell from which memberURL a member was added. */
1210 for ( ; agd; agd = agd->agd_next ) {
1212 if ( value_find_ex( slap_schema.si_ad_objectClass,
1213 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1214 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1215 a->a_nvals, &agd->agd_oc->soc_cname,
1216 op->o_tmpmemctx ) == 0 )
1221 m = op->orm_modlist;
1223 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1224 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1226 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1229 for ( ; m ; m = m->sml_next ) {
1230 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1231 autogroup_def_t *group_agd = age->age_def;
1232 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1233 op->o_req_dn.bv_val, 0, 0);
1235 overlay_entry_release_ov( op, e, 0, on );
1237 autogroup_delete_member_from_group( op, NULL, NULL, age );
1238 autogroup_delete_group( agi, age );
1240 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1242 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1243 return SLAP_CB_CONTINUE;
1247 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1251 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1254 overlay_entry_release_ov( op, e, 0, on );
1255 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1256 return SLAP_CB_CONTINUE;
1260 /* When modifying any of the attributes of an entry, we must
1261 check if the entry is in any of our groups, and if
1262 the modified entry maches any of the filters of that group.
1264 If the entry exists in a group, but the modified attributes do
1265 not match any of the group's filters, we delete the entry from that group.
1266 If the entry doesn't exist in a group, but matches a filter,
1267 we add it to that group.
1269 attrs = attrs_dup( e->e_attrs );
1270 overlay_entry_release_ov( op, e, 0, on );
1271 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1276 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1278 if ( age->age_filter && age->age_filter->agf_anlist ) {
1279 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1285 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1286 LDAP_SUCCESS || group == NULL ) {
1287 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1288 age->age_dn.bv_val, 0, 0);
1290 attrs_free( attrs );
1291 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1292 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1293 return SLAP_CB_CONTINUE;
1296 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1299 if ( value_find_ex( age->age_def->agd_member_ad,
1300 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1301 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1302 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1309 overlay_entry_release_ov( op, group, 0, on );
1311 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1312 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1313 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1320 if ( is_olddn == 1 && is_newdn == 0 ) {
1322 autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
1324 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1326 if ( is_olddn == 0 && is_newdn == 1 ) {
1328 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
1330 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1333 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1336 attrs_free( attrs );
1338 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1342 return SLAP_CB_CONTINUE;
1346 ** Detect if filter contains a memberOf check for dn
1349 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1352 if ( f == NULL ) return 0;
1354 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1355 case LDAP_FILTER_AND:
1356 case LDAP_FILTER_OR:
1357 case LDAP_FILTER_NOT:
1358 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1359 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1362 case LDAP_FILTER_EQUALITY:
1363 result = ( f->f_ava->aa_desc == memberof_ad &&
1364 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1374 ** When modifing a group, we must deny any modifications to the member attribute,
1375 ** because the group would be inconsistent.
1378 autogroup_modify_entry( Operation *op, SlapReply *rs)
1380 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1381 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1382 autogroup_def_t *agd = agi->agi_def;
1383 autogroup_entry_t *age;
1387 if ( get_manageDSAit( op ) ) {
1388 return SLAP_CB_CONTINUE;
1391 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1392 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1394 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1395 LDAP_SUCCESS || e == NULL ) {
1396 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1397 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1398 return SLAP_CB_CONTINUE;
1401 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1402 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1403 autogroup_filter_t *agf;
1404 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1405 if ( agf->agf_anlist ) {
1407 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1408 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1409 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1410 int rc = test_filter( op, e, agf->agf_filter );
1411 if ( rc == LDAP_COMPARE_TRUE ) {
1412 age->age_mustrefresh = 1;
1419 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1420 age->age_mustrefresh = 1;
1425 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1428 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1429 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1430 return SLAP_CB_CONTINUE;
1434 for ( ; agd; agd = agd->agd_next ) {
1436 if ( value_find_ex( slap_schema.si_ad_objectClass,
1437 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1438 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1439 a->a_nvals, &agd->agd_oc->soc_cname,
1440 op->o_tmpmemctx ) == 0 )
1445 m = op->orm_modlist;
1447 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1448 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1451 for ( ; m ; m = m->sml_next ) {
1452 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1453 overlay_entry_release_ov( op, e, 0, on );
1454 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1455 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1456 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1457 return LDAP_CONSTRAINT_VIOLATION;
1464 overlay_entry_release_ov( op, e, 0, on );
1465 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1466 return SLAP_CB_CONTINUE;
1470 overlay_entry_release_ov( op, e, 0, on );
1471 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1472 return SLAP_CB_CONTINUE;
1476 ** Detect if the olddn is part of a group and so if the group should be refreshed
1479 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1481 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1482 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1483 autogroup_entry_t *age;
1486 if ( get_manageDSAit( op ) ) {
1487 return SLAP_CB_CONTINUE;
1490 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1491 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1493 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1494 LDAP_SUCCESS || e == NULL ) {
1495 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1496 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1497 return SLAP_CB_CONTINUE;
1500 /* Must check if a dn is modified */
1501 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1502 autogroup_filter_t *agf;
1503 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1504 if ( agf->agf_anlist ) {
1505 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1506 int rc = test_filter( op, e, agf->agf_filter );
1507 if ( rc == LDAP_COMPARE_TRUE ) {
1508 age->age_modrdn_olddnmodified = 1;
1515 overlay_entry_release_ov( op, e, 0, on );
1516 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1517 return SLAP_CB_CONTINUE;
1521 ** Builds a filter for searching for the
1522 ** group entries, according to the objectClass.
1525 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1529 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1531 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1532 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1533 + agd->agd_oc->soc_cname.bv_len;
1534 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1536 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1538 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1542 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1544 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1555 static ConfigDriver ag_cfgen;
1557 static ConfigTable agcfg[] = {
1558 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1559 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1560 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1561 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1562 "EQUALITY caseIgnoreMatch "
1563 "SYNTAX OMsDirectoryString "
1564 "X-ORDERED 'VALUES' )",
1567 { "autogroup-memberof-ad", "memberOf attribute",
1568 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1569 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1570 "DESC 'memberOf attribute' "
1571 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1574 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1577 static ConfigOCs agocs[] = {
1578 { "( OLcfgCtOc:2.1 "
1579 "NAME 'olcAutomaticGroups' "
1580 "DESC 'Automatic groups configuration' "
1581 "SUP olcOverlayConfig "
1584 "$ olcAGmemberOfAd "
1587 Cft_Overlay, agcfg, NULL, NULL },
1593 ag_cfgen( ConfigArgs *c )
1595 slap_overinst *on = (slap_overinst *)c->bi;
1596 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1597 autogroup_def_t *agd;
1598 autogroup_entry_t *age;
1602 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1605 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1606 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1607 agi->agi_def = NULL;
1608 agi->agi_entry = NULL;
1609 on->on_bi.bi_private = (void *)agi;
1613 age = agi->agi_entry;
1615 if ( c->op == SLAP_CONFIG_EMIT ) {
1619 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1621 char *ptr = c->cr_msg;
1623 assert(agd->agd_oc != NULL);
1624 assert(agd->agd_member_url_ad != NULL);
1625 assert(agd->agd_member_ad != NULL);
1627 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1628 SLAP_X_ORDERED_FMT "%s %s %s", i,
1629 agd->agd_oc->soc_cname.bv_val,
1630 agd->agd_member_url_ad->ad_cname.bv_val,
1631 agd->agd_member_ad->ad_cname.bv_val );
1633 bv.bv_val = c->cr_msg;
1634 bv.bv_len = ptr - bv.bv_val;
1635 value_add_one ( &c->rvalue_vals, &bv );
1640 case AG_MEMBER_OF_AD:
1641 if ( agi->agi_memberof_ad != NULL ){
1642 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1653 }else if ( c->op == LDAP_MOD_DELETE ) {
1655 autogroup_def_t *agd_next;
1656 autogroup_entry_t *age_next;
1657 autogroup_filter_t *agf = age->age_filter,
1660 for ( agd_next = agd; agd_next; agd = agd_next ) {
1661 agd_next = agd->agd_next;
1666 for ( age_next = age ; age_next ; age = age_next ) {
1667 age_next = age->age_next;
1669 ch_free( age->age_dn.bv_val );
1670 ch_free( age->age_ndn.bv_val );
1672 for( agf_next = agf ; agf_next ; agf = agf_next ){
1673 agf_next = agf->agf_next;
1675 filter_free( agf->agf_filter );
1676 ch_free( agf->agf_filterstr.bv_val );
1677 ch_free( agf->agf_dn.bv_val );
1678 ch_free( agf->agf_ndn.bv_val );
1679 anlist_free( agf->agf_anlist, 1, NULL );
1683 ldap_pvt_thread_mutex_init( &age->age_mutex );
1688 on->on_bi.bi_private = NULL;
1691 autogroup_def_t **agdp;
1692 autogroup_entry_t *age_next, *age_prev;
1693 autogroup_filter_t *agf,
1696 for ( i = 0, agdp = &agi->agi_def;
1699 if ( *agdp == NULL) {
1702 agdp = &(*agdp)->agd_next;
1706 *agdp = agd->agd_next;
1708 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1709 age_next = age->age_next;
1711 if( age->age_def == agd ) {
1712 agf = age->age_filter;
1714 ch_free( age->age_dn.bv_val );
1715 ch_free( age->age_ndn.bv_val );
1717 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1718 agf_next = agf->agf_next;
1719 filter_free( agf->agf_filter );
1720 ch_free( agf->agf_filterstr.bv_val );
1721 ch_free( agf->agf_dn.bv_val );
1722 ch_free( agf->agf_ndn.bv_val );
1723 anlist_free( agf->agf_anlist, 1, NULL );
1727 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1732 if( age_prev != NULL ) {
1733 age_prev->age_next = age_next;
1748 autogroup_def_t **agdp,
1750 ObjectClass *oc = NULL;
1751 AttributeDescription *member_url_ad = NULL,
1756 oc = oc_find( c->argv[ 1 ] );
1758 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1759 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1760 "unable to find ObjectClass \"%s\"",
1762 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1763 c->log, c->cr_msg, 0 );
1768 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1769 if( rc != LDAP_SUCCESS ) {
1770 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1771 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1772 "unable to find AttributeDescription \"%s\"",
1774 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1775 c->log, c->cr_msg, 0 );
1779 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1780 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1781 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1782 "AttributeDescription \"%s\" ",
1783 "must be of a subtype \"labeledURI\"",
1785 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1786 c->log, c->cr_msg, 0 );
1790 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1791 if( rc != LDAP_SUCCESS ) {
1792 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1793 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1794 "unable to find AttributeDescription \"%s\"",
1796 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1797 c->log, c->cr_msg, 0 );
1801 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1802 /* The same URL attribute / member attribute pair
1803 * cannot be repeated */
1805 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1806 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1807 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1808 "URL attributeDescription \"%s\" already mapped",
1809 member_ad->ad_cname.bv_val );
1810 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1811 c->log, c->cr_msg, 0 );
1812 /* return 1; //warning*/
1816 if ( c->valx > 0 ) {
1819 for ( i = 0, agdp = &agi->agi_def ;
1822 if ( *agdp == NULL ) {
1823 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1824 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1825 "invalid index {%d}",
1827 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1828 c->log, c->cr_msg, 0 );
1832 agdp = &(*agdp)->agd_next;
1837 for ( agdp = &agi->agi_def; *agdp;
1838 agdp = &(*agdp)->agd_next )
1842 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1844 (*agdp)->agd_oc = oc;
1845 (*agdp)->agd_member_url_ad = member_url_ad;
1846 (*agdp)->agd_member_ad = member_ad;
1847 (*agdp)->agd_next = agd_next;
1851 case AG_MEMBER_OF_AD: {
1852 AttributeDescription *memberof_ad = NULL;
1855 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1856 if( rc != LDAP_SUCCESS ) {
1857 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1858 "\"autogroup-memberof-ad <memberof-ad>\": "
1859 "unable to find AttributeDescription \"%s\"",
1861 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1862 c->log, c->cr_msg, 0 );
1866 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1867 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1869 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1870 "memberof attribute=\"%s\" must either "
1871 "have DN (%s) or nameUID (%s) syntax",
1872 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1873 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1874 c->log, c->cr_msg, 0 );
1878 agi->agi_memberof_ad = memberof_ad;
1890 extern int slapMode;
1893 ** Do a search for all the groups in the
1894 ** database, and add them to out internal list.
1901 slap_overinst *on = (slap_overinst *) be->bd_info;
1902 autogroup_info_t *agi = on->on_bi.bi_private;
1903 autogroup_def_t *agd;
1906 slap_callback cb = { 0 };
1908 void *thrctx = ldap_pvt_thread_pool_context();
1909 Connection conn = { 0 };
1910 OperationBuffer opbuf;
1912 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1914 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1918 connection_fake_init( &conn, &opbuf, thrctx );
1921 op->ors_attrsonly = 0;
1922 op->o_tag = LDAP_REQ_SEARCH;
1923 op->o_dn = be->be_rootdn;
1924 op->o_ndn = be->be_rootndn;
1926 op->o_req_dn = be->be_suffix[0];
1927 op->o_req_ndn = be->be_nsuffix[0];
1929 op->ors_scope = LDAP_SCOPE_SUBTREE;
1930 op->ors_deref = LDAP_DEREF_NEVER;
1931 op->ors_limit = NULL;
1932 op->ors_tlimit = SLAP_NO_LIMIT;
1933 op->ors_slimit = SLAP_NO_LIMIT;
1934 op->ors_attrs = slap_anlist_no_attrs;
1937 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1940 cb.sc_private = &ags;
1941 cb.sc_response = autogroup_group_add_cb;
1942 cb.sc_cleanup = NULL;
1945 op->o_callback = &cb;
1947 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1948 SlapReply rs = { REP_RESULT };
1950 autogroup_build_def_filter(agd, op);
1954 op->o_bd->be_search( op, &rs );
1956 filter_free_x( op, op->ors_filter, 1 );
1957 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1960 if( ! agi->agi_memberof_ad ){
1962 const char *text = NULL;
1964 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1965 if ( rc != LDAP_SUCCESS ) {
1966 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1967 "unable to find attribute=\"%s\": %s (%d)\n",
1968 SLAPD_MEMBEROF_ATTR, text, rc );
1981 slap_overinst *on = (slap_overinst *) be->bd_info;
1983 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1985 if ( on->on_bi.bi_private ) {
1986 autogroup_info_t *agi = on->on_bi.bi_private;
1987 autogroup_entry_t *age = agi->agi_entry,
1989 autogroup_filter_t *agf, *agf_next;
1991 for ( age_next = age; age_next; age = age_next ) {
1992 age_next = age->age_next;
1994 ch_free( age->age_dn.bv_val );
1995 ch_free( age->age_ndn.bv_val );
1997 agf = age->age_filter;
1999 for ( agf_next = agf; agf_next; agf = agf_next ) {
2000 agf_next = agf->agf_next;
2002 filter_free( agf->agf_filter );
2003 ch_free( agf->agf_filterstr.bv_val );
2004 ch_free( agf->agf_dn.bv_val );
2005 ch_free( agf->agf_ndn.bv_val );
2006 anlist_free( agf->agf_anlist, 1, NULL );
2010 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2019 autogroup_db_destroy(
2023 slap_overinst *on = (slap_overinst *) be->bd_info;
2025 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2027 if ( on->on_bi.bi_private ) {
2028 autogroup_info_t *agi = on->on_bi.bi_private;
2029 autogroup_def_t *agd = agi->agi_def,
2032 for ( agd_next = agd; agd_next; agd = agd_next ) {
2033 agd_next = agd->agd_next;
2038 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2045 static slap_overinst autogroup = { { NULL } };
2049 autogroup_initialize(void)
2052 autogroup.on_bi.bi_type = "autogroup";
2054 autogroup.on_bi.bi_db_open = autogroup_db_open;
2055 autogroup.on_bi.bi_db_close = autogroup_db_close;
2056 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2058 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2059 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2060 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2061 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2063 autogroup.on_response = autogroup_response;
2065 autogroup.on_bi.bi_cf_ocs = agocs;
2067 rc = config_register_schema( agcfg, agocs );
2072 return overlay_register( &autogroup );
2076 init_module( int argc, char *argv[] )
2078 return autogroup_initialize();