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_dn = op->o_bd->be_rootdn;
140 o.o_ndn = op->o_bd->be_rootndn;
141 o.o_req_dn = age->age_dn;
142 o.o_req_ndn = age->age_ndn;
143 o.o_permissive_modify = 1;
144 o.o_managedsait = SLAP_CONTROL_CRITICAL;
145 o.o_relax = SLAP_CONTROL_CRITICAL;
147 o.o_bd->bd_info = (BackendInfo *)on->on_info;
148 (void)op->o_bd->be_modify( &o, &sreply );
149 o.o_bd->bd_info = (BackendInfo *)on;
151 slap_mods_free( modlist, 1 );
153 return sreply.sr_err;
157 ** e - the entry where to get the attribute values
158 ** age - the group to which the values will be added
161 autogroup_add_member_values_to_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
163 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
164 Modifications modlist;
165 SlapReply sreply = {REP_RESULT};
166 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
169 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
170 dn->bv_val, age->age_dn.bv_val, 0);
172 modlist.sml_op = LDAP_MOD_ADD;
173 modlist.sml_desc = age->age_def->agd_member_ad;
174 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
175 modlist.sml_values = attr->a_vals;
176 modlist.sml_nvalues = attr->a_nvals;
177 modlist.sml_numvals = attr->a_numvals;
178 modlist.sml_flags = SLAP_MOD_INTERNAL;
179 modlist.sml_next = NULL;
181 o.o_tag = LDAP_REQ_MODIFY;
183 o.orm_modlist = &modlist;
184 o.o_dn = op->o_bd->be_rootdn;
185 o.o_ndn = op->o_bd->be_rootndn;
186 o.o_req_dn = age->age_dn;
187 o.o_req_ndn = age->age_ndn;
188 o.o_permissive_modify = 1;
189 o.o_managedsait = SLAP_CONTROL_CRITICAL;
190 o.o_relax = SLAP_CONTROL_CRITICAL;
192 o.o_bd->bd_info = (BackendInfo *)on->on_info;
193 (void)op->o_bd->be_modify( &o, &sreply );
194 o.o_bd->bd_info = (BackendInfo *)on;
196 return sreply.sr_err;
200 ** dn,ndn - the DN to be deleted
201 ** age - the group from which the DN will be deleted
202 ** If we pass a NULL dn and ndn, all members are deleted from the group.
205 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
207 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
208 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
209 SlapReply sreply = {REP_RESULT};
210 BerValue *vals, *nvals;
211 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
214 if ( dn == NULL || ndn == NULL ) {
215 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
216 age->age_dn.bv_val, 0 ,0);
218 modlist->sml_values = NULL;
219 modlist->sml_nvalues = NULL;
220 modlist->sml_numvals = 0;
222 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
223 dn->bv_val, age->age_dn.bv_val, 0);
225 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
226 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
227 ber_dupbv( vals, dn );
228 BER_BVZERO( &vals[ 1 ] );
229 ber_dupbv( nvals, ndn );
230 BER_BVZERO( &nvals[ 1 ] );
232 modlist->sml_values = vals;
233 modlist->sml_nvalues = nvals;
234 modlist->sml_numvals = 1;
238 modlist->sml_op = LDAP_MOD_DELETE;
239 modlist->sml_desc = age->age_def->agd_member_ad;
240 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
241 modlist->sml_flags = SLAP_MOD_INTERNAL;
242 modlist->sml_next = NULL;
245 o.o_tag = LDAP_REQ_MODIFY;
246 o.orm_modlist = modlist;
247 o.o_dn = op->o_bd->be_rootdn;
248 o.o_ndn = op->o_bd->be_rootndn;
249 o.o_req_dn = age->age_dn;
250 o.o_req_ndn = age->age_ndn;
251 o.o_relax = SLAP_CONTROL_CRITICAL;
252 o.o_managedsait = SLAP_CONTROL_CRITICAL;
253 o.o_permissive_modify = 1;
255 o.o_bd->bd_info = (BackendInfo *)on->on_info;
256 (void)op->o_bd->be_modify( &o, &sreply );
257 o.o_bd->bd_info = (BackendInfo *)on;
259 slap_mods_free( modlist, 1 );
261 return sreply.sr_err;
265 ** e - the entry where to get the attribute values
266 ** age - the group from which the values will be deleted
269 autogroup_delete_member_values_from_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
271 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
272 Modifications modlist;
273 SlapReply sreply = {REP_RESULT};
274 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
277 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
278 dn->bv_val, age->age_dn.bv_val, 0);
280 modlist.sml_op = LDAP_MOD_DELETE;
281 modlist.sml_desc = age->age_def->agd_member_ad;
282 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
283 modlist.sml_values = attr->a_vals;
284 modlist.sml_nvalues = attr->a_nvals;
285 modlist.sml_numvals = attr->a_numvals;
286 modlist.sml_flags = SLAP_MOD_INTERNAL;
287 modlist.sml_next = NULL;
289 o.o_tag = LDAP_REQ_MODIFY;
291 o.orm_modlist = &modlist;
292 o.o_dn = op->o_bd->be_rootdn;
293 o.o_ndn = op->o_bd->be_rootndn;
294 o.o_req_dn = age->age_dn;
295 o.o_req_ndn = age->age_ndn;
296 o.o_permissive_modify = 1;
297 o.o_managedsait = SLAP_CONTROL_CRITICAL;
298 o.o_relax = SLAP_CONTROL_CRITICAL;
300 o.o_bd->bd_info = (BackendInfo *)on->on_info;
301 (void)op->o_bd->be_modify( &o, &sreply );
302 o.o_bd->bd_info = (BackendInfo *)on;
304 return sreply.sr_err;
308 ** Callback used to add entries to a group,
309 ** which are going to be written in the database
310 ** (used in bi_op_add)
311 ** The group is passed in autogroup_ga_t->agg_group
314 autogroup_member_search_cb( Operation *op, SlapReply *rs )
316 assert( op->o_tag == LDAP_REQ_SEARCH );
318 if ( rs->sr_type == REP_SEARCH ) {
319 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
320 autogroup_entry_t *age = agg->agg_group;
321 autogroup_filter_t *agf = agg->agg_filter;
323 const char *text = NULL;
325 struct berval *vals, *nvals;
326 struct berval lvals[ 2 ], lnvals[ 2 ];
329 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
330 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
332 if ( agf->agf_anlist ) {
333 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
336 nvals = attr->a_nvals;
337 numvals = attr->a_numvals;
343 lvals[ 0 ] = rs->sr_entry->e_name;
344 BER_BVZERO( &lvals[ 1 ] );
345 lnvals[ 0 ] = rs->sr_entry->e_nname;
346 BER_BVZERO( &lnvals[ 1 ] );
352 mod.sm_op = LDAP_MOD_ADD;
353 mod.sm_desc = age->age_def->agd_member_ad;
354 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
355 mod.sm_values = vals;
356 mod.sm_nvalues = nvals;
357 mod.sm_numvals = numvals;
359 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
366 ** Callback used to add entries to a group, which is already in the database.
367 ** (used in on_response)
368 ** The group is passed in autogroup_ga_t->agg_group
372 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
374 assert( op->o_tag == LDAP_REQ_SEARCH );
376 if ( rs->sr_type == REP_SEARCH ) {
377 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
378 autogroup_entry_t *age = agg->agg_group;
379 autogroup_filter_t *agf = agg->agg_filter;
380 Modifications *modlist;
381 struct berval *vals, *nvals;
382 struct berval lvals[ 2 ], lnvals[ 2 ];
385 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
386 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
388 if ( agf->agf_anlist ) {
389 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
392 nvals = attr->a_nvals;
393 numvals = attr->a_numvals;
399 lvals[ 0 ] = rs->sr_entry->e_name;
400 BER_BVZERO( &lvals[ 1 ] );
401 lnvals[ 0 ] = rs->sr_entry->e_nname;
402 BER_BVZERO( &lnvals[ 1 ] );
409 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
411 modlist->sml_op = LDAP_MOD_ADD;
412 modlist->sml_desc = age->age_def->agd_member_ad;
413 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
415 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
416 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
417 modlist->sml_numvals = numvals;
419 modlist->sml_flags = SLAP_MOD_INTERNAL;
420 modlist->sml_next = NULL;
422 if ( agg->agg_mod == NULL ) {
423 agg->agg_mod = modlist;
424 agg->agg_mod_last = modlist;
426 agg->agg_mod_last->sml_next = modlist;
427 agg->agg_mod_last = modlist;
438 ** Adds all entries matching the passed filter to the specified group.
439 ** If modify == 1, then we modify the group's entry in the database using be_modify.
440 ** If modify == 0, then, we must supply a rw entry for the group,
441 ** because we only modify the entry, without calling be_modify.
442 ** e - the group entry, to which the members will be added
447 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
449 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
451 SlapReply rs = { REP_SEARCH };
452 slap_callback cb = { 0 };
453 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
456 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
457 age->age_dn.bv_val, 0, 0);
460 o.o_tag = LDAP_REQ_SEARCH;
462 o.o_dn = op->o_bd->be_rootdn;
463 o.o_ndn = op->o_bd->be_rootndn;
464 o.o_req_dn = agf->agf_dn;
465 o.o_req_ndn = agf->agf_ndn;
467 o.ors_filterstr = agf->agf_filterstr;
468 o.ors_filter = agf->agf_filter;
470 o.ors_scope = agf->agf_scope;
471 o.ors_deref = LDAP_DEREF_NEVER;
473 o.ors_tlimit = SLAP_NO_LIMIT;
474 o.ors_slimit = SLAP_NO_LIMIT;
475 o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
478 agg.agg_filter = agf;
480 agg.agg_mod_last = NULL;
482 cb.sc_private = &agg;
485 cb.sc_response = autogroup_member_search_modify_cb;
487 cb.sc_response = autogroup_member_search_cb;
490 cb.sc_cleanup = NULL;
495 o.o_bd->bd_info = (BackendInfo *)on->on_info;
496 op->o_bd->be_search( &o, &rs );
497 o.o_bd->bd_info = (BackendInfo *)on;
499 if ( modify == 1 && agg.agg_mod ) {
500 rs_reinit( &rs, REP_RESULT );
503 o.o_callback = &null_cb;
504 o.o_tag = LDAP_REQ_MODIFY;
505 o.orm_modlist = agg.agg_mod;
506 o.o_dn = op->o_bd->be_rootdn;
507 o.o_ndn = op->o_bd->be_rootndn;
508 o.o_req_dn = age->age_dn;
509 o.o_req_ndn = age->age_ndn;
510 o.o_relax = SLAP_CONTROL_CRITICAL;
511 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
512 o.o_permissive_modify = 1;
514 o.o_bd->bd_info = (BackendInfo *)on->on_info;
515 (void)op->o_bd->be_modify( &o, &rs );
516 o.o_bd->bd_info = (BackendInfo *)on;
518 slap_mods_free(agg.agg_mod, 1);
525 ** Adds a group to the internal list from the passed entry.
526 ** scan specifies whether to add all maching members to the group.
527 ** modify specifies whether to modify the given group entry (when modify == 0),
528 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
529 ** agi - pointer to the groups and the attribute definitions
530 ** agd - the attribute definition of the added group
531 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
532 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
535 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
537 autogroup_entry_t **agep = &agi->agi_entry;
538 autogroup_filter_t *agf, *agf_prev = NULL;
539 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
540 LDAPURLDesc *lud = NULL;
543 int rc = 0, match = 1, null_entry = 0;
546 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
547 LDAP_SUCCESS || e == NULL ) {
548 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
555 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
556 e->e_name.bv_val, 0, 0);
558 if ( agi->agi_entry != NULL ) {
559 for ( ; *agep ; agep = &(*agep)->age_next ) {
560 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
562 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
570 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
571 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
572 (*agep)->age_def = agd;
573 (*agep)->age_filter = NULL;
574 (*agep)->age_mustrefresh = 0;
575 (*agep)->age_modrdn_olddnmodified = 0;
577 ber_dupbv( &(*agep)->age_dn, &e->e_name );
578 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
580 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
582 if ( null_entry == 1 ) {
584 overlay_entry_release_ov( op, e, 0, on );
588 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
590 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
592 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
594 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
595 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
601 agf->agf_scope = lud->lud_scope;
603 if ( lud->lud_dn == NULL ) {
604 BER_BVSTR( &dn, "" );
606 ber_str2bv( lud->lud_dn, 0, 0, &dn );
609 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
610 if ( rc != LDAP_SUCCESS ) {
611 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
616 if ( lud->lud_filter != NULL ) {
617 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
618 agf->agf_filter = str2filter( lud->lud_filter );
620 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: URL filter is missing <%s>\n", bv->bv_val,0,0);
625 if ( lud->lud_attrs != NULL ) {
628 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
633 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
636 filter_free( agf->agf_filter );
637 ch_free( agf->agf_filterstr.bv_val );
638 ch_free( agf->agf_dn.bv_val );
639 ch_free( agf->agf_ndn.bv_val );
640 ldap_free_urldesc( lud );
645 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
647 if ( agf->agf_anlist == NULL ) {
648 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
649 lud->lud_attrs[0], 0, 0 );
651 filter_free( agf->agf_filter );
652 ch_free( agf->agf_filterstr.bv_val );
653 ch_free( agf->agf_dn.bv_val );
654 ch_free( agf->agf_ndn.bv_val );
655 ldap_free_urldesc( lud );
661 agf->agf_next = NULL;
663 if( (*agep)->age_filter == NULL ) {
664 (*agep)->age_filter = agf;
667 if( agf_prev != NULL ) {
668 agf_prev->agf_next = agf;
674 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
677 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
678 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
680 ldap_free_urldesc( lud );
687 ch_free( agf->agf_ndn.bv_val );
688 ch_free( agf->agf_dn.bv_val );
689 ldap_free_urldesc( lud );
694 if ( null_entry == 1 ) {
701 ** Used when opening the database to add all existing
702 ** groups from the database to our internal list.
705 autogroup_group_add_cb( Operation *op, SlapReply *rs )
707 assert( op->o_tag == LDAP_REQ_SEARCH );
709 if ( rs->sr_type == REP_SEARCH ) {
710 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
712 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
713 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
715 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
721 typedef struct ag_addinfo {
724 autogroup_def_t *agd;
728 autogroup_add_entry_cb( Operation *op, SlapReply *rs )
730 slap_callback *sc = op->o_callback;
731 ag_addinfo *aa = sc->sc_private;
732 slap_overinst *on = aa->on;
733 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
734 BackendInfo *bi = op->o_bd->bd_info;
736 if ( rs->sr_err != LDAP_SUCCESS )
739 op->o_bd->bd_info = (BackendInfo *)on;
740 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
742 autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
744 autogroup_entry_t *age;
745 autogroup_filter_t *agf;
747 for ( age = agi->agi_entry; age ; age = age->age_next ) {
748 ldap_pvt_thread_mutex_lock( &age->age_mutex );
750 /* Check if any of the filters are the suffix to the entry DN.
751 If yes, we can test that filter against the entry. */
753 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
754 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
755 rc = test_filter( op, aa->e, agf->agf_filter );
756 if ( rc == LDAP_COMPARE_TRUE ) {
757 if ( agf->agf_anlist ) {
758 Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
760 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
762 autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
768 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
771 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
773 op->o_bd->bd_info = bi;
776 op->o_callback = sc->sc_next;
777 op->o_tmpfree( sc, op->o_tmpmemctx );
779 return SLAP_CB_CONTINUE;
783 ** When adding a group, we first strip any existing members,
784 ** and add all which match the filters ourselfs.
787 autogroup_add_entry( Operation *op, SlapReply *rs)
789 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
790 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
791 autogroup_def_t *agd = agi->agi_def;
792 slap_callback *sc = NULL;
793 ag_addinfo *aa = NULL;
796 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
797 op->ora_e->e_name.bv_val, 0, 0);
799 sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
800 sc->sc_private = (sc+1);
801 sc->sc_response = autogroup_add_entry_cb;
805 sc->sc_next = op->o_callback;
808 /* Check if it's a group. */
809 for ( ; agd ; agd = agd->agd_next ) {
810 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
812 const char *text = NULL;
815 mod.sm_op = LDAP_MOD_DELETE;
816 mod.sm_desc = agd->agd_member_ad;
817 mod.sm_type = agd->agd_member_ad->ad_cname;
818 mod.sm_values = NULL;
819 mod.sm_nvalues = NULL;
821 /* We don't want any member attributes added by the user. */
822 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
830 return SLAP_CB_CONTINUE;
834 ** agi - internal group and attribute definitions list
835 ** e - the group to remove from the internal list
838 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
840 autogroup_entry_t *age = agi->agi_entry,
845 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
846 age->age_dn.bv_val, 0, 0);
848 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
849 age_next = age->age_next;
852 autogroup_filter_t *agf = age->age_filter,
855 if ( age_prev != NULL ) {
856 age_prev->age_next = age_next;
858 agi->agi_entry = NULL;
861 ch_free( age->age_dn.bv_val );
862 ch_free( age->age_ndn.bv_val );
864 for( agf_next = agf ; agf_next ; agf = agf_next ){
865 agf_next = agf->agf_next;
867 filter_free( agf->agf_filter );
868 ch_free( agf->agf_filterstr.bv_val );
869 ch_free( agf->agf_dn.bv_val );
870 ch_free( agf->agf_ndn.bv_val );
871 anlist_free( agf->agf_anlist, 1, NULL );
875 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
876 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
885 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
892 autogroup_delete_entry( Operation *op, SlapReply *rs)
894 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
895 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
896 autogroup_entry_t *age, *age_prev, *age_next;
897 autogroup_filter_t *agf;
899 int matched_group = 0, rc = 0;
901 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
903 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
905 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
906 LDAP_SUCCESS || e == NULL ) {
907 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
908 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
909 return SLAP_CB_CONTINUE;
912 /* Check if the entry to be deleted is one of our groups. */
913 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
915 ldap_pvt_thread_mutex_lock( &age->age_mutex );
916 age_next = age->age_next;
918 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
923 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
926 autogroup_delete_group( agi, age );
931 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
934 if ( matched_group == 1 ) {
935 overlay_entry_release_ov( op, e, 0, on );
936 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
937 return SLAP_CB_CONTINUE;
940 /* Check if the entry matches any of the groups.
941 If yes, we can delete the entry from that group. */
943 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
944 ldap_pvt_thread_mutex_lock( &age->age_mutex );
946 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
947 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
948 rc = test_filter( op, e, agf->agf_filter );
949 if ( rc == LDAP_COMPARE_TRUE ) {
950 /* If the attribute is retrieved from the entry, we don't know what to delete
951 ** So the group must be entirely refreshed
952 ** But the refresh can't be done now because the entry is not deleted
953 ** So the group is marked as mustrefresh
955 if ( agf->agf_anlist ) {
956 age->age_mustrefresh = 1;
958 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
964 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
967 overlay_entry_release_ov( op, e, 0, on );
968 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
970 return SLAP_CB_CONTINUE;
974 autogroup_response( Operation *op, SlapReply *rs )
976 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
977 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
978 autogroup_def_t *agd = agi->agi_def;
979 autogroup_entry_t *age;
980 autogroup_filter_t *agf;
981 BerValue new_dn, new_ndn, pdn;
983 Attribute *a, *ea, *attrs;
984 int is_olddn, is_newdn, is_value_refresh, dn_equal;
986 /* Handle all cases where a refresh of the group is needed */
987 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
988 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
990 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
992 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
993 /* Request detected that the group must be refreshed */
995 ldap_pvt_thread_mutex_lock( &age->age_mutex );
997 if ( age->age_mustrefresh ) {
998 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1000 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1001 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1005 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1008 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1010 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
1011 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
1013 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
1015 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1017 if ( op->oq_modrdn.rs_newSup ) {
1018 pdn = *op->oq_modrdn.rs_newSup;
1020 dnParent( &op->o_req_dn, &pdn );
1022 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
1024 if ( op->oq_modrdn.rs_nnewSup ) {
1025 pdn = *op->oq_modrdn.rs_nnewSup;
1027 dnParent( &op->o_req_ndn, &pdn );
1029 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1031 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
1033 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
1035 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
1036 LDAP_SUCCESS || e == NULL ) {
1037 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
1038 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1039 return SLAP_CB_CONTINUE;
1042 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1046 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
1047 overlay_entry_release_ov( op, e, 0, on );
1048 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1049 return SLAP_CB_CONTINUE;
1053 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1054 for ( ; agd; agd = agd->agd_next ) {
1056 if ( value_find_ex( slap_schema.si_ad_objectClass,
1057 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1058 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1059 a->a_nvals, &agd->agd_oc->soc_cname,
1060 op->o_tmpmemctx ) == 0 )
1062 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1065 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1067 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1068 ber_dupbv( &age->age_dn, &new_dn );
1069 ber_dupbv( &age->age_ndn, &new_ndn );
1071 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1072 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1073 overlay_entry_release_ov( op, e, 0, on );
1074 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1075 return SLAP_CB_CONTINUE;
1083 1. check if the orginal entry's DN is in the group.
1084 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1086 If 1 and 2 are both false, we do nothing.
1087 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1088 If 1 is false, and 2 is true, we check the entry against the group's filters,
1089 and add it's DN to the group.
1090 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1092 attrs = attrs_dup( e->e_attrs );
1093 overlay_entry_release_ov( op, e, 0, on );
1094 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1097 is_value_refresh = 0;
1099 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1101 if ( age->age_filter && age->age_filter->agf_anlist ) {
1102 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1108 if ( age->age_modrdn_olddnmodified ) {
1109 /* Resquest already marked this group to be updated */
1111 is_value_refresh = 1;
1112 age->age_modrdn_olddnmodified = 0;
1115 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1116 LDAP_SUCCESS || group == NULL ) {
1117 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1119 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1120 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1122 attrs_free( attrs );
1123 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1124 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1125 return SLAP_CB_CONTINUE;
1128 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1131 if ( value_find_ex( age->age_def->agd_member_ad,
1132 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1133 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1134 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1141 overlay_entry_release_ov( op, group, 0, on );
1145 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1146 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1147 /* TODO: should retest filter as it could imply conditions on the dn */
1154 if ( is_value_refresh ) {
1155 if ( is_olddn != is_newdn ) {
1157 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1159 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1160 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1163 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1166 if ( is_olddn == 1 && is_newdn == 0 ) {
1168 autogroup_delete_member_values_from_group( op, &new_dn, age, ea );
1170 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1172 if ( is_olddn == 0 && is_newdn == 1 ) {
1173 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1174 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1176 autogroup_add_member_values_to_group( op, &new_dn, age, ea );
1178 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1183 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1186 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1188 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1189 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1193 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1194 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1198 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1201 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1202 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1204 attrs_free( attrs );
1206 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1210 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1211 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1213 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1215 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1217 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1218 LDAP_SUCCESS || e == NULL ) {
1219 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1220 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1221 return SLAP_CB_CONTINUE;
1224 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1228 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1229 overlay_entry_release_ov( op, e, 0, on );
1230 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1231 return SLAP_CB_CONTINUE;
1234 /* If we modify a group's memberURL, we have to delete all of it's members,
1235 and add them anew, because we cannot tell from which memberURL a member was added. */
1236 for ( ; agd; agd = agd->agd_next ) {
1238 if ( value_find_ex( slap_schema.si_ad_objectClass,
1239 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1240 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1241 a->a_nvals, &agd->agd_oc->soc_cname,
1242 op->o_tmpmemctx ) == 0 )
1247 m = op->orm_modlist;
1249 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1250 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1252 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1255 for ( ; m ; m = m->sml_next ) {
1256 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1257 autogroup_def_t *group_agd = age->age_def;
1258 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1259 op->o_req_dn.bv_val, 0, 0);
1261 overlay_entry_release_ov( op, e, 0, on );
1263 autogroup_delete_member_from_group( op, NULL, NULL, age );
1264 autogroup_delete_group( agi, age );
1266 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1268 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1269 return SLAP_CB_CONTINUE;
1273 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1277 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1280 overlay_entry_release_ov( op, e, 0, on );
1281 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1282 return SLAP_CB_CONTINUE;
1286 /* When modifying any of the attributes of an entry, we must
1287 check if the entry is in any of our groups, and if
1288 the modified entry maches any of the filters of that group.
1290 If the entry exists in a group, but the modified attributes do
1291 not match any of the group's filters, we delete the entry from that group.
1292 If the entry doesn't exist in a group, but matches a filter,
1293 we add it to that group.
1295 attrs = attrs_dup( e->e_attrs );
1296 overlay_entry_release_ov( op, e, 0, on );
1297 etmp.e_name = op->o_req_dn;
1298 etmp.e_nname = op->o_req_ndn;
1299 etmp.e_attrs = attrs;
1300 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1305 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1307 if ( age->age_filter && age->age_filter->agf_anlist ) {
1308 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1314 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1315 LDAP_SUCCESS || group == NULL ) {
1316 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1317 age->age_dn.bv_val, 0, 0);
1319 attrs_free( attrs );
1320 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1321 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1322 return SLAP_CB_CONTINUE;
1325 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1328 if ( value_find_ex( age->age_def->agd_member_ad,
1329 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1330 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1331 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1338 overlay_entry_release_ov( op, group, 0, on );
1340 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1341 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1342 if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1349 if ( is_olddn == 1 && is_newdn == 0 ) {
1351 autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
1353 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1355 if ( is_olddn == 0 && is_newdn == 1 ) {
1357 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
1359 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1362 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1365 attrs_free( attrs );
1367 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1371 return SLAP_CB_CONTINUE;
1375 ** Detect if filter contains a memberOf check for dn
1378 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1381 if ( f == NULL ) return 0;
1383 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1384 case LDAP_FILTER_AND:
1385 case LDAP_FILTER_OR:
1386 case LDAP_FILTER_NOT:
1387 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1388 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1391 case LDAP_FILTER_EQUALITY:
1392 result = ( f->f_ava->aa_desc == memberof_ad &&
1393 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1403 ** When modifing a group, we must deny any modifications to the member attribute,
1404 ** because the group would be inconsistent.
1407 autogroup_modify_entry( Operation *op, SlapReply *rs)
1409 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1410 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1411 autogroup_def_t *agd = agi->agi_def;
1412 autogroup_entry_t *age;
1416 if ( get_manageDSAit( op ) ) {
1417 return SLAP_CB_CONTINUE;
1420 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1421 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1423 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1424 LDAP_SUCCESS || e == NULL ) {
1425 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1426 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1427 return SLAP_CB_CONTINUE;
1430 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1431 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1432 autogroup_filter_t *agf;
1433 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1434 if ( agf->agf_anlist ) {
1436 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1437 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1438 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1439 int rc = test_filter( op, e, agf->agf_filter );
1440 if ( rc == LDAP_COMPARE_TRUE ) {
1441 age->age_mustrefresh = 1;
1448 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1449 age->age_mustrefresh = 1;
1454 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1457 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1458 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1459 return SLAP_CB_CONTINUE;
1463 for ( ; agd; agd = agd->agd_next ) {
1465 if ( value_find_ex( slap_schema.si_ad_objectClass,
1466 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1467 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1468 a->a_nvals, &agd->agd_oc->soc_cname,
1469 op->o_tmpmemctx ) == 0 )
1474 m = op->orm_modlist;
1476 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1477 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1480 for ( ; m ; m = m->sml_next ) {
1481 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1482 overlay_entry_release_ov( op, e, 0, on );
1483 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1484 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1485 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1486 return LDAP_CONSTRAINT_VIOLATION;
1493 overlay_entry_release_ov( op, e, 0, on );
1494 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1495 return SLAP_CB_CONTINUE;
1499 overlay_entry_release_ov( op, e, 0, on );
1500 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1501 return SLAP_CB_CONTINUE;
1505 ** Detect if the olddn is part of a group and so if the group should be refreshed
1508 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1510 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1511 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1512 autogroup_entry_t *age;
1515 if ( get_manageDSAit( op ) ) {
1516 return SLAP_CB_CONTINUE;
1519 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1520 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1522 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1523 LDAP_SUCCESS || e == NULL ) {
1524 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1525 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1526 return SLAP_CB_CONTINUE;
1529 /* Must check if a dn is modified */
1530 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1531 autogroup_filter_t *agf;
1532 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1533 if ( agf->agf_anlist ) {
1534 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1535 int rc = test_filter( op, e, agf->agf_filter );
1536 if ( rc == LDAP_COMPARE_TRUE ) {
1537 age->age_modrdn_olddnmodified = 1;
1544 overlay_entry_release_ov( op, e, 0, on );
1545 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1546 return SLAP_CB_CONTINUE;
1550 ** Builds a filter for searching for the
1551 ** group entries, according to the objectClass.
1554 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1558 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1560 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1561 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1562 + agd->agd_oc->soc_cname.bv_len;
1563 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1565 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1567 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1571 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1573 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1584 static ConfigDriver ag_cfgen;
1586 static ConfigTable agcfg[] = {
1587 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1588 4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1589 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1590 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1591 "EQUALITY caseIgnoreMatch "
1592 "SYNTAX OMsDirectoryString "
1593 "X-ORDERED 'VALUES' )",
1596 { "autogroup-memberof-ad", "memberOf attribute",
1597 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1598 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1599 "DESC 'memberOf attribute' "
1600 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1603 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1606 static ConfigOCs agocs[] = {
1607 { "( OLcfgCtOc:2.1 "
1608 "NAME 'olcAutomaticGroups' "
1609 "DESC 'Automatic groups configuration' "
1610 "SUP olcOverlayConfig "
1613 "$ olcAGmemberOfAd "
1616 Cft_Overlay, agcfg, NULL, NULL },
1622 ag_cfgen( ConfigArgs *c )
1624 slap_overinst *on = (slap_overinst *)c->bi;
1625 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1626 autogroup_def_t *agd;
1627 autogroup_entry_t *age;
1631 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1634 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1635 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1636 agi->agi_def = NULL;
1637 agi->agi_entry = NULL;
1638 on->on_bi.bi_private = (void *)agi;
1642 age = agi->agi_entry;
1644 if ( c->op == SLAP_CONFIG_EMIT ) {
1648 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1650 char *ptr = c->cr_msg;
1652 assert(agd->agd_oc != NULL);
1653 assert(agd->agd_member_url_ad != NULL);
1654 assert(agd->agd_member_ad != NULL);
1656 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1657 SLAP_X_ORDERED_FMT "%s %s %s", i,
1658 agd->agd_oc->soc_cname.bv_val,
1659 agd->agd_member_url_ad->ad_cname.bv_val,
1660 agd->agd_member_ad->ad_cname.bv_val );
1662 bv.bv_val = c->cr_msg;
1663 bv.bv_len = ptr - bv.bv_val;
1664 value_add_one ( &c->rvalue_vals, &bv );
1669 case AG_MEMBER_OF_AD:
1670 if ( agi->agi_memberof_ad != NULL ){
1671 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1682 }else if ( c->op == LDAP_MOD_DELETE ) {
1684 autogroup_def_t *agd_next;
1685 autogroup_entry_t *age_next;
1686 autogroup_filter_t *agf = age->age_filter,
1689 for ( agd_next = agd; agd_next; agd = agd_next ) {
1690 agd_next = agd->agd_next;
1695 for ( age_next = age ; age_next ; age = age_next ) {
1696 age_next = age->age_next;
1698 ch_free( age->age_dn.bv_val );
1699 ch_free( age->age_ndn.bv_val );
1701 for( agf_next = agf ; agf_next ; agf = agf_next ){
1702 agf_next = agf->agf_next;
1704 filter_free( agf->agf_filter );
1705 ch_free( agf->agf_filterstr.bv_val );
1706 ch_free( agf->agf_dn.bv_val );
1707 ch_free( agf->agf_ndn.bv_val );
1708 anlist_free( agf->agf_anlist, 1, NULL );
1712 ldap_pvt_thread_mutex_init( &age->age_mutex );
1717 on->on_bi.bi_private = NULL;
1720 autogroup_def_t **agdp;
1721 autogroup_entry_t *age_next, *age_prev;
1722 autogroup_filter_t *agf,
1725 for ( i = 0, agdp = &agi->agi_def;
1728 if ( *agdp == NULL) {
1731 agdp = &(*agdp)->agd_next;
1735 *agdp = agd->agd_next;
1737 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1738 age_next = age->age_next;
1740 if( age->age_def == agd ) {
1741 agf = age->age_filter;
1743 ch_free( age->age_dn.bv_val );
1744 ch_free( age->age_ndn.bv_val );
1746 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1747 agf_next = agf->agf_next;
1748 filter_free( agf->agf_filter );
1749 ch_free( agf->agf_filterstr.bv_val );
1750 ch_free( agf->agf_dn.bv_val );
1751 ch_free( agf->agf_ndn.bv_val );
1752 anlist_free( agf->agf_anlist, 1, NULL );
1756 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1761 if( age_prev != NULL ) {
1762 age_prev->age_next = age_next;
1777 autogroup_def_t **agdp,
1779 ObjectClass *oc = NULL;
1780 AttributeDescription *member_url_ad = NULL,
1785 oc = oc_find( c->argv[ 1 ] );
1787 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1788 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1789 "unable to find ObjectClass \"%s\"",
1791 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1792 c->log, c->cr_msg, 0 );
1797 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1798 if( rc != LDAP_SUCCESS ) {
1799 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1800 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1801 "unable to find AttributeDescription \"%s\"",
1803 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1804 c->log, c->cr_msg, 0 );
1808 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1809 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1810 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1811 "AttributeDescription \"%s\" ",
1812 "must be of a subtype \"labeledURI\"",
1814 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1815 c->log, c->cr_msg, 0 );
1819 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1820 if( rc != LDAP_SUCCESS ) {
1821 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1822 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1823 "unable to find AttributeDescription \"%s\"",
1825 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1826 c->log, c->cr_msg, 0 );
1830 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1831 /* The same URL attribute / member attribute pair
1832 * cannot be repeated */
1834 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1835 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1836 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1837 "URL attributeDescription \"%s\" already mapped",
1838 member_ad->ad_cname.bv_val );
1839 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1840 c->log, c->cr_msg, 0 );
1841 /* return 1; //warning*/
1845 if ( c->valx > 0 ) {
1848 for ( i = 0, agdp = &agi->agi_def ;
1851 if ( *agdp == NULL ) {
1852 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1853 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1854 "invalid index {%d}",
1856 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1857 c->log, c->cr_msg, 0 );
1861 agdp = &(*agdp)->agd_next;
1866 for ( agdp = &agi->agi_def; *agdp;
1867 agdp = &(*agdp)->agd_next )
1871 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1873 (*agdp)->agd_oc = oc;
1874 (*agdp)->agd_member_url_ad = member_url_ad;
1875 (*agdp)->agd_member_ad = member_ad;
1876 (*agdp)->agd_next = agd_next;
1880 case AG_MEMBER_OF_AD: {
1881 AttributeDescription *memberof_ad = NULL;
1884 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1885 if( rc != LDAP_SUCCESS ) {
1886 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1887 "\"autogroup-memberof-ad <memberof-ad>\": "
1888 "unable to find AttributeDescription \"%s\"",
1890 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1891 c->log, c->cr_msg, 0 );
1895 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1896 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1898 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1899 "memberof attribute=\"%s\" must either "
1900 "have DN (%s) or nameUID (%s) syntax",
1901 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1902 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1903 c->log, c->cr_msg, 0 );
1907 agi->agi_memberof_ad = memberof_ad;
1919 extern int slapMode;
1922 ** Do a search for all the groups in the
1923 ** database, and add them to out internal list.
1930 slap_overinst *on = (slap_overinst *) be->bd_info;
1931 autogroup_info_t *agi = on->on_bi.bi_private;
1932 autogroup_def_t *agd;
1935 slap_callback cb = { 0 };
1937 void *thrctx = ldap_pvt_thread_pool_context();
1938 Connection conn = { 0 };
1939 OperationBuffer opbuf;
1941 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1943 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1947 connection_fake_init( &conn, &opbuf, thrctx );
1950 op->ors_attrsonly = 0;
1951 op->o_tag = LDAP_REQ_SEARCH;
1952 op->o_dn = be->be_rootdn;
1953 op->o_ndn = be->be_rootndn;
1955 op->o_req_dn = be->be_suffix[0];
1956 op->o_req_ndn = be->be_nsuffix[0];
1958 op->ors_scope = LDAP_SCOPE_SUBTREE;
1959 op->ors_deref = LDAP_DEREF_NEVER;
1960 op->ors_limit = NULL;
1961 op->ors_tlimit = SLAP_NO_LIMIT;
1962 op->ors_slimit = SLAP_NO_LIMIT;
1963 op->ors_attrs = slap_anlist_no_attrs;
1966 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1969 cb.sc_private = &ags;
1970 cb.sc_response = autogroup_group_add_cb;
1971 cb.sc_cleanup = NULL;
1974 op->o_callback = &cb;
1976 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1977 SlapReply rs = { REP_RESULT };
1979 autogroup_build_def_filter(agd, op);
1983 op->o_bd->be_search( op, &rs );
1985 filter_free_x( op, op->ors_filter, 1 );
1986 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1989 if( ! agi->agi_memberof_ad ){
1991 const char *text = NULL;
1993 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1994 if ( rc != LDAP_SUCCESS ) {
1995 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1996 "unable to find attribute=\"%s\": %s (%d)\n",
1997 SLAPD_MEMBEROF_ATTR, text, rc );
2010 slap_overinst *on = (slap_overinst *) be->bd_info;
2012 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
2014 if ( on->on_bi.bi_private ) {
2015 autogroup_info_t *agi = on->on_bi.bi_private;
2016 autogroup_entry_t *age = agi->agi_entry,
2018 autogroup_filter_t *agf, *agf_next;
2020 for ( age_next = age; age_next; age = age_next ) {
2021 age_next = age->age_next;
2023 ch_free( age->age_dn.bv_val );
2024 ch_free( age->age_ndn.bv_val );
2026 agf = age->age_filter;
2028 for ( agf_next = agf; agf_next; agf = agf_next ) {
2029 agf_next = agf->agf_next;
2031 filter_free( agf->agf_filter );
2032 ch_free( agf->agf_filterstr.bv_val );
2033 ch_free( agf->agf_dn.bv_val );
2034 ch_free( agf->agf_ndn.bv_val );
2035 anlist_free( agf->agf_anlist, 1, NULL );
2039 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2048 autogroup_db_destroy(
2052 slap_overinst *on = (slap_overinst *) be->bd_info;
2054 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2056 if ( on->on_bi.bi_private ) {
2057 autogroup_info_t *agi = on->on_bi.bi_private;
2058 autogroup_def_t *agd = agi->agi_def,
2061 for ( agd_next = agd; agd_next; agd = agd_next ) {
2062 agd_next = agd->agd_next;
2067 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2074 static slap_overinst autogroup = { { NULL } };
2078 autogroup_initialize(void)
2081 autogroup.on_bi.bi_type = "autogroup";
2083 autogroup.on_bi.bi_db_open = autogroup_db_open;
2084 autogroup.on_bi.bi_db_close = autogroup_db_close;
2085 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2087 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2088 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2089 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2090 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2092 autogroup.on_response = autogroup_response;
2094 autogroup.on_bi.bi_cf_ocs = agocs;
2096 rc = config_register_schema( agcfg, agocs );
2101 return overlay_register( &autogroup );
2105 init_module( int argc, char *argv[] )
2107 return autogroup_initialize();