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 ) ) {
1212 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1214 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1216 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1217 LDAP_SUCCESS || e == NULL ) {
1218 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1219 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1220 return SLAP_CB_CONTINUE;
1223 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1227 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1228 overlay_entry_release_ov( op, e, 0, on );
1229 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1230 return SLAP_CB_CONTINUE;
1233 /* If we modify a group's memberURL, we have to delete all of it's members,
1234 and add them anew, because we cannot tell from which memberURL a member was added. */
1235 for ( ; agd; agd = agd->agd_next ) {
1237 if ( value_find_ex( slap_schema.si_ad_objectClass,
1238 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1239 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1240 a->a_nvals, &agd->agd_oc->soc_cname,
1241 op->o_tmpmemctx ) == 0 )
1246 m = op->orm_modlist;
1248 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1249 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1251 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1254 for ( ; m ; m = m->sml_next ) {
1255 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1256 autogroup_def_t *group_agd = age->age_def;
1257 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1258 op->o_req_dn.bv_val, 0, 0);
1260 overlay_entry_release_ov( op, e, 0, on );
1262 autogroup_delete_member_from_group( op, NULL, NULL, age );
1263 autogroup_delete_group( agi, age );
1265 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1267 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1268 return SLAP_CB_CONTINUE;
1272 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1276 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1279 overlay_entry_release_ov( op, e, 0, on );
1280 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1281 return SLAP_CB_CONTINUE;
1285 /* When modifying any of the attributes of an entry, we must
1286 check if the entry is in any of our groups, and if
1287 the modified entry maches any of the filters of that group.
1289 If the entry exists in a group, but the modified attributes do
1290 not match any of the group's filters, we delete the entry from that group.
1291 If the entry doesn't exist in a group, but matches a filter,
1292 we add it to that group.
1294 attrs = attrs_dup( e->e_attrs );
1295 overlay_entry_release_ov( op, e, 0, on );
1296 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1301 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1303 if ( age->age_filter && age->age_filter->agf_anlist ) {
1304 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1310 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1311 LDAP_SUCCESS || group == NULL ) {
1312 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1313 age->age_dn.bv_val, 0, 0);
1315 attrs_free( attrs );
1316 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1317 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1318 return SLAP_CB_CONTINUE;
1321 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1324 if ( value_find_ex( age->age_def->agd_member_ad,
1325 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1326 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1327 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1334 overlay_entry_release_ov( op, group, 0, on );
1336 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1337 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1338 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1345 if ( is_olddn == 1 && is_newdn == 0 ) {
1347 autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
1349 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1351 if ( is_olddn == 0 && is_newdn == 1 ) {
1353 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
1355 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1358 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1361 attrs_free( attrs );
1363 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1367 return SLAP_CB_CONTINUE;
1371 ** Detect if filter contains a memberOf check for dn
1374 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1377 if ( f == NULL ) return 0;
1379 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1380 case LDAP_FILTER_AND:
1381 case LDAP_FILTER_OR:
1382 case LDAP_FILTER_NOT:
1383 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1384 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1387 case LDAP_FILTER_EQUALITY:
1388 result = ( f->f_ava->aa_desc == memberof_ad &&
1389 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1399 ** When modifing a group, we must deny any modifications to the member attribute,
1400 ** because the group would be inconsistent.
1403 autogroup_modify_entry( Operation *op, SlapReply *rs)
1405 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1406 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1407 autogroup_def_t *agd = agi->agi_def;
1408 autogroup_entry_t *age;
1412 if ( get_manageDSAit( op ) ) {
1413 return SLAP_CB_CONTINUE;
1416 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1417 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1419 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1420 LDAP_SUCCESS || e == NULL ) {
1421 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1422 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1423 return SLAP_CB_CONTINUE;
1426 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1427 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1428 autogroup_filter_t *agf;
1429 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1430 if ( agf->agf_anlist ) {
1432 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1433 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1434 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1435 int rc = test_filter( op, e, agf->agf_filter );
1436 if ( rc == LDAP_COMPARE_TRUE ) {
1437 age->age_mustrefresh = 1;
1444 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1445 age->age_mustrefresh = 1;
1450 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1453 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1454 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1455 return SLAP_CB_CONTINUE;
1459 for ( ; agd; agd = agd->agd_next ) {
1461 if ( value_find_ex( slap_schema.si_ad_objectClass,
1462 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1463 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1464 a->a_nvals, &agd->agd_oc->soc_cname,
1465 op->o_tmpmemctx ) == 0 )
1470 m = op->orm_modlist;
1472 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1473 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1476 for ( ; m ; m = m->sml_next ) {
1477 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1478 overlay_entry_release_ov( op, e, 0, on );
1479 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1480 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1481 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1482 return LDAP_CONSTRAINT_VIOLATION;
1489 overlay_entry_release_ov( op, e, 0, on );
1490 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1491 return SLAP_CB_CONTINUE;
1495 overlay_entry_release_ov( op, e, 0, on );
1496 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1497 return SLAP_CB_CONTINUE;
1501 ** Detect if the olddn is part of a group and so if the group should be refreshed
1504 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1506 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1507 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1508 autogroup_entry_t *age;
1511 if ( get_manageDSAit( op ) ) {
1512 return SLAP_CB_CONTINUE;
1515 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1516 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1518 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1519 LDAP_SUCCESS || e == NULL ) {
1520 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1521 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1522 return SLAP_CB_CONTINUE;
1525 /* Must check if a dn is modified */
1526 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1527 autogroup_filter_t *agf;
1528 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1529 if ( agf->agf_anlist ) {
1530 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1531 int rc = test_filter( op, e, agf->agf_filter );
1532 if ( rc == LDAP_COMPARE_TRUE ) {
1533 age->age_modrdn_olddnmodified = 1;
1540 overlay_entry_release_ov( op, e, 0, on );
1541 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1542 return SLAP_CB_CONTINUE;
1546 ** Builds a filter for searching for the
1547 ** group entries, according to the objectClass.
1550 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1554 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1556 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1557 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1558 + agd->agd_oc->soc_cname.bv_len;
1559 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1561 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1563 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1567 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1569 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1580 static ConfigDriver ag_cfgen;
1582 static ConfigTable agcfg[] = {
1583 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1584 4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1585 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1586 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1587 "EQUALITY caseIgnoreMatch "
1588 "SYNTAX OMsDirectoryString "
1589 "X-ORDERED 'VALUES' )",
1592 { "autogroup-memberof-ad", "memberOf attribute",
1593 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1594 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1595 "DESC 'memberOf attribute' "
1596 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1599 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1602 static ConfigOCs agocs[] = {
1603 { "( OLcfgCtOc:2.1 "
1604 "NAME 'olcAutomaticGroups' "
1605 "DESC 'Automatic groups configuration' "
1606 "SUP olcOverlayConfig "
1609 "$ olcAGmemberOfAd "
1612 Cft_Overlay, agcfg, NULL, NULL },
1618 ag_cfgen( ConfigArgs *c )
1620 slap_overinst *on = (slap_overinst *)c->bi;
1621 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1622 autogroup_def_t *agd;
1623 autogroup_entry_t *age;
1627 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1630 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1631 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1632 agi->agi_def = NULL;
1633 agi->agi_entry = NULL;
1634 on->on_bi.bi_private = (void *)agi;
1638 age = agi->agi_entry;
1640 if ( c->op == SLAP_CONFIG_EMIT ) {
1644 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1646 char *ptr = c->cr_msg;
1648 assert(agd->agd_oc != NULL);
1649 assert(agd->agd_member_url_ad != NULL);
1650 assert(agd->agd_member_ad != NULL);
1652 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1653 SLAP_X_ORDERED_FMT "%s %s %s", i,
1654 agd->agd_oc->soc_cname.bv_val,
1655 agd->agd_member_url_ad->ad_cname.bv_val,
1656 agd->agd_member_ad->ad_cname.bv_val );
1658 bv.bv_val = c->cr_msg;
1659 bv.bv_len = ptr - bv.bv_val;
1660 value_add_one ( &c->rvalue_vals, &bv );
1665 case AG_MEMBER_OF_AD:
1666 if ( agi->agi_memberof_ad != NULL ){
1667 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1678 }else if ( c->op == LDAP_MOD_DELETE ) {
1680 autogroup_def_t *agd_next;
1681 autogroup_entry_t *age_next;
1682 autogroup_filter_t *agf = age->age_filter,
1685 for ( agd_next = agd; agd_next; agd = agd_next ) {
1686 agd_next = agd->agd_next;
1691 for ( age_next = age ; age_next ; age = age_next ) {
1692 age_next = age->age_next;
1694 ch_free( age->age_dn.bv_val );
1695 ch_free( age->age_ndn.bv_val );
1697 for( agf_next = agf ; agf_next ; agf = agf_next ){
1698 agf_next = agf->agf_next;
1700 filter_free( agf->agf_filter );
1701 ch_free( agf->agf_filterstr.bv_val );
1702 ch_free( agf->agf_dn.bv_val );
1703 ch_free( agf->agf_ndn.bv_val );
1704 anlist_free( agf->agf_anlist, 1, NULL );
1708 ldap_pvt_thread_mutex_init( &age->age_mutex );
1713 on->on_bi.bi_private = NULL;
1716 autogroup_def_t **agdp;
1717 autogroup_entry_t *age_next, *age_prev;
1718 autogroup_filter_t *agf,
1721 for ( i = 0, agdp = &agi->agi_def;
1724 if ( *agdp == NULL) {
1727 agdp = &(*agdp)->agd_next;
1731 *agdp = agd->agd_next;
1733 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1734 age_next = age->age_next;
1736 if( age->age_def == agd ) {
1737 agf = age->age_filter;
1739 ch_free( age->age_dn.bv_val );
1740 ch_free( age->age_ndn.bv_val );
1742 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1743 agf_next = agf->agf_next;
1744 filter_free( agf->agf_filter );
1745 ch_free( agf->agf_filterstr.bv_val );
1746 ch_free( agf->agf_dn.bv_val );
1747 ch_free( agf->agf_ndn.bv_val );
1748 anlist_free( agf->agf_anlist, 1, NULL );
1752 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1757 if( age_prev != NULL ) {
1758 age_prev->age_next = age_next;
1773 autogroup_def_t **agdp,
1775 ObjectClass *oc = NULL;
1776 AttributeDescription *member_url_ad = NULL,
1781 oc = oc_find( c->argv[ 1 ] );
1783 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1784 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1785 "unable to find ObjectClass \"%s\"",
1787 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1788 c->log, c->cr_msg, 0 );
1793 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1794 if( rc != LDAP_SUCCESS ) {
1795 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1796 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1797 "unable to find AttributeDescription \"%s\"",
1799 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1800 c->log, c->cr_msg, 0 );
1804 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1805 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1806 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1807 "AttributeDescription \"%s\" ",
1808 "must be of a subtype \"labeledURI\"",
1810 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1811 c->log, c->cr_msg, 0 );
1815 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1816 if( rc != LDAP_SUCCESS ) {
1817 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1818 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1819 "unable to find AttributeDescription \"%s\"",
1821 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1822 c->log, c->cr_msg, 0 );
1826 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1827 /* The same URL attribute / member attribute pair
1828 * cannot be repeated */
1830 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1831 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1832 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1833 "URL attributeDescription \"%s\" already mapped",
1834 member_ad->ad_cname.bv_val );
1835 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1836 c->log, c->cr_msg, 0 );
1837 /* return 1; //warning*/
1841 if ( c->valx > 0 ) {
1844 for ( i = 0, agdp = &agi->agi_def ;
1847 if ( *agdp == NULL ) {
1848 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1849 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1850 "invalid index {%d}",
1852 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1853 c->log, c->cr_msg, 0 );
1857 agdp = &(*agdp)->agd_next;
1862 for ( agdp = &agi->agi_def; *agdp;
1863 agdp = &(*agdp)->agd_next )
1867 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1869 (*agdp)->agd_oc = oc;
1870 (*agdp)->agd_member_url_ad = member_url_ad;
1871 (*agdp)->agd_member_ad = member_ad;
1872 (*agdp)->agd_next = agd_next;
1876 case AG_MEMBER_OF_AD: {
1877 AttributeDescription *memberof_ad = NULL;
1880 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1881 if( rc != LDAP_SUCCESS ) {
1882 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1883 "\"autogroup-memberof-ad <memberof-ad>\": "
1884 "unable to find AttributeDescription \"%s\"",
1886 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1887 c->log, c->cr_msg, 0 );
1891 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1892 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1894 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1895 "memberof attribute=\"%s\" must either "
1896 "have DN (%s) or nameUID (%s) syntax",
1897 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1898 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1899 c->log, c->cr_msg, 0 );
1903 agi->agi_memberof_ad = memberof_ad;
1915 extern int slapMode;
1918 ** Do a search for all the groups in the
1919 ** database, and add them to out internal list.
1926 slap_overinst *on = (slap_overinst *) be->bd_info;
1927 autogroup_info_t *agi = on->on_bi.bi_private;
1928 autogroup_def_t *agd;
1931 slap_callback cb = { 0 };
1933 void *thrctx = ldap_pvt_thread_pool_context();
1934 Connection conn = { 0 };
1935 OperationBuffer opbuf;
1937 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1939 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1943 connection_fake_init( &conn, &opbuf, thrctx );
1946 op->ors_attrsonly = 0;
1947 op->o_tag = LDAP_REQ_SEARCH;
1948 op->o_dn = be->be_rootdn;
1949 op->o_ndn = be->be_rootndn;
1951 op->o_req_dn = be->be_suffix[0];
1952 op->o_req_ndn = be->be_nsuffix[0];
1954 op->ors_scope = LDAP_SCOPE_SUBTREE;
1955 op->ors_deref = LDAP_DEREF_NEVER;
1956 op->ors_limit = NULL;
1957 op->ors_tlimit = SLAP_NO_LIMIT;
1958 op->ors_slimit = SLAP_NO_LIMIT;
1959 op->ors_attrs = slap_anlist_no_attrs;
1962 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1965 cb.sc_private = &ags;
1966 cb.sc_response = autogroup_group_add_cb;
1967 cb.sc_cleanup = NULL;
1970 op->o_callback = &cb;
1972 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1973 SlapReply rs = { REP_RESULT };
1975 autogroup_build_def_filter(agd, op);
1979 op->o_bd->be_search( op, &rs );
1981 filter_free_x( op, op->ors_filter, 1 );
1982 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1985 if( ! agi->agi_memberof_ad ){
1987 const char *text = NULL;
1989 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1990 if ( rc != LDAP_SUCCESS ) {
1991 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1992 "unable to find attribute=\"%s\": %s (%d)\n",
1993 SLAPD_MEMBEROF_ATTR, text, rc );
2006 slap_overinst *on = (slap_overinst *) be->bd_info;
2008 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
2010 if ( on->on_bi.bi_private ) {
2011 autogroup_info_t *agi = on->on_bi.bi_private;
2012 autogroup_entry_t *age = agi->agi_entry,
2014 autogroup_filter_t *agf, *agf_next;
2016 for ( age_next = age; age_next; age = age_next ) {
2017 age_next = age->age_next;
2019 ch_free( age->age_dn.bv_val );
2020 ch_free( age->age_ndn.bv_val );
2022 agf = age->age_filter;
2024 for ( agf_next = agf; agf_next; agf = agf_next ) {
2025 agf_next = agf->agf_next;
2027 filter_free( agf->agf_filter );
2028 ch_free( agf->agf_filterstr.bv_val );
2029 ch_free( agf->agf_dn.bv_val );
2030 ch_free( agf->agf_ndn.bv_val );
2031 anlist_free( agf->agf_anlist, 1, NULL );
2035 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2044 autogroup_db_destroy(
2048 slap_overinst *on = (slap_overinst *) be->bd_info;
2050 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2052 if ( on->on_bi.bi_private ) {
2053 autogroup_info_t *agi = on->on_bi.bi_private;
2054 autogroup_def_t *agd = agi->agi_def,
2057 for ( agd_next = agd; agd_next; agd = agd_next ) {
2058 agd_next = agd->agd_next;
2063 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2070 static slap_overinst autogroup = { { NULL } };
2074 autogroup_initialize(void)
2077 autogroup.on_bi.bi_type = "autogroup";
2079 autogroup.on_bi.bi_db_open = autogroup_db_open;
2080 autogroup.on_bi.bi_db_close = autogroup_db_close;
2081 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2083 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2084 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2085 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2086 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2088 autogroup.on_response = autogroup_response;
2090 autogroup.on_bi.bi_cf_ocs = agocs;
2092 rc = config_register_schema( agcfg, agocs );
2097 return overlay_register( &autogroup );
2101 init_module( int argc, char *argv[] )
2103 return autogroup_initialize();