1 /* autogroup.c - automatic group overlay */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2007-2015 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 ) {
1174 etmp.e_name = op->o_req_dn;
1175 etmp.e_nname = op->o_req_ndn;
1176 etmp.e_attrs = attrs;
1177 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1178 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1180 autogroup_add_member_values_to_group( op, &new_dn, age, ea );
1182 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1187 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1190 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1192 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1193 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1197 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1198 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1202 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1205 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1206 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1208 attrs_free( attrs );
1210 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1214 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1215 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1217 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1219 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1221 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1222 LDAP_SUCCESS || e == NULL ) {
1223 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1224 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1225 return SLAP_CB_CONTINUE;
1228 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1232 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1233 overlay_entry_release_ov( op, e, 0, on );
1234 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1235 return SLAP_CB_CONTINUE;
1238 /* If we modify a group's memberURL, we have to delete all of it's members,
1239 and add them anew, because we cannot tell from which memberURL a member was added. */
1240 for ( ; agd; agd = agd->agd_next ) {
1242 if ( value_find_ex( slap_schema.si_ad_objectClass,
1243 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1244 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1245 a->a_nvals, &agd->agd_oc->soc_cname,
1246 op->o_tmpmemctx ) == 0 )
1251 m = op->orm_modlist;
1253 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1254 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1256 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1259 for ( ; m ; m = m->sml_next ) {
1260 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1261 autogroup_def_t *group_agd = age->age_def;
1262 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1263 op->o_req_dn.bv_val, 0, 0);
1265 overlay_entry_release_ov( op, e, 0, on );
1267 autogroup_delete_member_from_group( op, NULL, NULL, age );
1268 autogroup_delete_group( agi, age );
1270 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1272 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1273 return SLAP_CB_CONTINUE;
1277 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1281 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1284 overlay_entry_release_ov( op, e, 0, on );
1285 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1286 return SLAP_CB_CONTINUE;
1290 /* When modifying any of the attributes of an entry, we must
1291 check if the entry is in any of our groups, and if
1292 the modified entry maches any of the filters of that group.
1294 If the entry exists in a group, but the modified attributes do
1295 not match any of the group's filters, we delete the entry from that group.
1296 If the entry doesn't exist in a group, but matches a filter,
1297 we add it to that group.
1299 attrs = attrs_dup( e->e_attrs );
1300 overlay_entry_release_ov( op, e, 0, on );
1301 etmp.e_name = op->o_req_dn;
1302 etmp.e_nname = op->o_req_ndn;
1303 etmp.e_attrs = attrs;
1304 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1309 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1311 if ( age->age_filter && age->age_filter->agf_anlist ) {
1312 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1318 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1319 LDAP_SUCCESS || group == NULL ) {
1320 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1321 age->age_dn.bv_val, 0, 0);
1323 attrs_free( attrs );
1324 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1325 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1326 return SLAP_CB_CONTINUE;
1329 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1332 if ( value_find_ex( age->age_def->agd_member_ad,
1333 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1334 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1335 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1342 overlay_entry_release_ov( op, group, 0, on );
1344 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1345 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1346 if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1353 if ( is_olddn == 1 && is_newdn == 0 ) {
1355 autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
1357 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1359 if ( is_olddn == 0 && is_newdn == 1 ) {
1361 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
1363 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1366 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1369 attrs_free( attrs );
1371 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1375 return SLAP_CB_CONTINUE;
1379 ** Detect if filter contains a memberOf check for dn
1382 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1385 if ( f == NULL ) return 0;
1387 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1388 case LDAP_FILTER_AND:
1389 case LDAP_FILTER_OR:
1390 case LDAP_FILTER_NOT:
1391 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1392 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1395 case LDAP_FILTER_EQUALITY:
1396 result = ( f->f_ava->aa_desc == memberof_ad &&
1397 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1407 ** When modifing a group, we must deny any modifications to the member attribute,
1408 ** because the group would be inconsistent.
1411 autogroup_modify_entry( Operation *op, SlapReply *rs)
1413 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1414 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1415 autogroup_def_t *agd = agi->agi_def;
1416 autogroup_entry_t *age;
1420 if ( get_manageDSAit( op ) ) {
1421 return SLAP_CB_CONTINUE;
1424 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1425 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1427 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1428 LDAP_SUCCESS || e == NULL ) {
1429 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1430 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1431 return SLAP_CB_CONTINUE;
1434 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1435 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1436 autogroup_filter_t *agf;
1437 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1438 if ( agf->agf_anlist ) {
1440 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1441 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1442 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1443 int rc = test_filter( op, e, agf->agf_filter );
1444 if ( rc == LDAP_COMPARE_TRUE ) {
1445 age->age_mustrefresh = 1;
1452 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1453 age->age_mustrefresh = 1;
1458 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1461 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1462 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1463 return SLAP_CB_CONTINUE;
1467 for ( ; agd; agd = agd->agd_next ) {
1469 if ( value_find_ex( slap_schema.si_ad_objectClass,
1470 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1471 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1472 a->a_nvals, &agd->agd_oc->soc_cname,
1473 op->o_tmpmemctx ) == 0 )
1478 m = op->orm_modlist;
1480 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1481 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1484 for ( ; m ; m = m->sml_next ) {
1485 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1486 overlay_entry_release_ov( op, e, 0, on );
1487 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1488 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1489 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1490 return LDAP_CONSTRAINT_VIOLATION;
1497 overlay_entry_release_ov( op, e, 0, on );
1498 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1499 return SLAP_CB_CONTINUE;
1503 overlay_entry_release_ov( op, e, 0, on );
1504 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1505 return SLAP_CB_CONTINUE;
1509 ** Detect if the olddn is part of a group and so if the group should be refreshed
1512 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1514 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1515 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1516 autogroup_entry_t *age;
1519 if ( get_manageDSAit( op ) ) {
1520 return SLAP_CB_CONTINUE;
1523 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1524 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1526 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1527 LDAP_SUCCESS || e == NULL ) {
1528 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1529 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1530 return SLAP_CB_CONTINUE;
1533 /* Must check if a dn is modified */
1534 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1535 autogroup_filter_t *agf;
1536 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1537 if ( agf->agf_anlist ) {
1538 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1539 int rc = test_filter( op, e, agf->agf_filter );
1540 if ( rc == LDAP_COMPARE_TRUE ) {
1541 age->age_modrdn_olddnmodified = 1;
1548 overlay_entry_release_ov( op, e, 0, on );
1549 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1550 return SLAP_CB_CONTINUE;
1554 ** Builds a filter for searching for the
1555 ** group entries, according to the objectClass.
1558 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1562 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1564 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1565 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1566 + agd->agd_oc->soc_cname.bv_len;
1567 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1569 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1571 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1575 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1577 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1588 static ConfigDriver ag_cfgen;
1590 static ConfigTable agcfg[] = {
1591 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1592 4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1593 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1594 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1595 "EQUALITY caseIgnoreMatch "
1596 "SYNTAX OMsDirectoryString "
1597 "X-ORDERED 'VALUES' )",
1600 { "autogroup-memberof-ad", "memberOf attribute",
1601 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1602 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1603 "DESC 'memberOf attribute' "
1604 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1607 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1610 static ConfigOCs agocs[] = {
1611 { "( OLcfgCtOc:2.1 "
1612 "NAME 'olcAutomaticGroups' "
1613 "DESC 'Automatic groups configuration' "
1614 "SUP olcOverlayConfig "
1617 "$ olcAGmemberOfAd "
1620 Cft_Overlay, agcfg, NULL, NULL },
1626 ag_cfgen( ConfigArgs *c )
1628 slap_overinst *on = (slap_overinst *)c->bi;
1629 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1630 autogroup_def_t *agd;
1631 autogroup_entry_t *age;
1635 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1638 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1639 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1640 agi->agi_def = NULL;
1641 agi->agi_entry = NULL;
1642 on->on_bi.bi_private = (void *)agi;
1646 age = agi->agi_entry;
1648 if ( c->op == SLAP_CONFIG_EMIT ) {
1652 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1654 char *ptr = c->cr_msg;
1656 assert(agd->agd_oc != NULL);
1657 assert(agd->agd_member_url_ad != NULL);
1658 assert(agd->agd_member_ad != NULL);
1660 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1661 SLAP_X_ORDERED_FMT "%s %s %s", i,
1662 agd->agd_oc->soc_cname.bv_val,
1663 agd->agd_member_url_ad->ad_cname.bv_val,
1664 agd->agd_member_ad->ad_cname.bv_val );
1666 bv.bv_val = c->cr_msg;
1667 bv.bv_len = ptr - bv.bv_val;
1668 value_add_one ( &c->rvalue_vals, &bv );
1673 case AG_MEMBER_OF_AD:
1674 if ( agi->agi_memberof_ad != NULL ){
1675 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1686 }else if ( c->op == LDAP_MOD_DELETE ) {
1688 autogroup_def_t *agd_next;
1689 autogroup_entry_t *age_next;
1690 autogroup_filter_t *agf = age->age_filter,
1693 for ( agd_next = agd; agd_next; agd = agd_next ) {
1694 agd_next = agd->agd_next;
1699 for ( age_next = age ; age_next ; age = age_next ) {
1700 age_next = age->age_next;
1702 ch_free( age->age_dn.bv_val );
1703 ch_free( age->age_ndn.bv_val );
1705 for( agf_next = agf ; agf_next ; agf = agf_next ){
1706 agf_next = agf->agf_next;
1708 filter_free( agf->agf_filter );
1709 ch_free( agf->agf_filterstr.bv_val );
1710 ch_free( agf->agf_dn.bv_val );
1711 ch_free( agf->agf_ndn.bv_val );
1712 anlist_free( agf->agf_anlist, 1, NULL );
1716 ldap_pvt_thread_mutex_init( &age->age_mutex );
1721 on->on_bi.bi_private = NULL;
1724 autogroup_def_t **agdp;
1725 autogroup_entry_t *age_next, *age_prev;
1726 autogroup_filter_t *agf,
1729 for ( i = 0, agdp = &agi->agi_def;
1732 if ( *agdp == NULL) {
1735 agdp = &(*agdp)->agd_next;
1739 *agdp = agd->agd_next;
1741 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1742 age_next = age->age_next;
1744 if( age->age_def == agd ) {
1745 agf = age->age_filter;
1747 ch_free( age->age_dn.bv_val );
1748 ch_free( age->age_ndn.bv_val );
1750 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1751 agf_next = agf->agf_next;
1752 filter_free( agf->agf_filter );
1753 ch_free( agf->agf_filterstr.bv_val );
1754 ch_free( agf->agf_dn.bv_val );
1755 ch_free( agf->agf_ndn.bv_val );
1756 anlist_free( agf->agf_anlist, 1, NULL );
1760 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1765 if( age_prev != NULL ) {
1766 age_prev->age_next = age_next;
1781 autogroup_def_t **agdp,
1783 ObjectClass *oc = NULL;
1784 AttributeDescription *member_url_ad = NULL,
1789 oc = oc_find( c->argv[ 1 ] );
1791 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1792 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1793 "unable to find ObjectClass \"%s\"",
1795 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1796 c->log, c->cr_msg, 0 );
1801 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1802 if( rc != LDAP_SUCCESS ) {
1803 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1804 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1805 "unable to find AttributeDescription \"%s\"",
1807 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1808 c->log, c->cr_msg, 0 );
1812 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1813 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1814 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1815 "AttributeDescription \"%s\" ",
1816 "must be of a subtype \"labeledURI\"",
1818 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1819 c->log, c->cr_msg, 0 );
1823 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1824 if( rc != LDAP_SUCCESS ) {
1825 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1826 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1827 "unable to find AttributeDescription \"%s\"",
1829 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1830 c->log, c->cr_msg, 0 );
1834 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1835 /* The same URL attribute / member attribute pair
1836 * cannot be repeated */
1838 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1839 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1840 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1841 "URL attributeDescription \"%s\" already mapped",
1842 member_ad->ad_cname.bv_val );
1843 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1844 c->log, c->cr_msg, 0 );
1845 /* return 1; //warning*/
1849 if ( c->valx > 0 ) {
1852 for ( i = 0, agdp = &agi->agi_def ;
1855 if ( *agdp == NULL ) {
1856 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1857 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1858 "invalid index {%d}",
1860 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1861 c->log, c->cr_msg, 0 );
1865 agdp = &(*agdp)->agd_next;
1870 for ( agdp = &agi->agi_def; *agdp;
1871 agdp = &(*agdp)->agd_next )
1875 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1877 (*agdp)->agd_oc = oc;
1878 (*agdp)->agd_member_url_ad = member_url_ad;
1879 (*agdp)->agd_member_ad = member_ad;
1880 (*agdp)->agd_next = agd_next;
1884 case AG_MEMBER_OF_AD: {
1885 AttributeDescription *memberof_ad = NULL;
1888 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1889 if( rc != LDAP_SUCCESS ) {
1890 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1891 "\"autogroup-memberof-ad <memberof-ad>\": "
1892 "unable to find AttributeDescription \"%s\"",
1894 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1895 c->log, c->cr_msg, 0 );
1899 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1900 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1902 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1903 "memberof attribute=\"%s\" must either "
1904 "have DN (%s) or nameUID (%s) syntax",
1905 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1906 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1907 c->log, c->cr_msg, 0 );
1911 agi->agi_memberof_ad = memberof_ad;
1923 extern int slapMode;
1926 ** Do a search for all the groups in the
1927 ** database, and add them to out internal list.
1934 slap_overinst *on = (slap_overinst *) be->bd_info;
1935 autogroup_info_t *agi = on->on_bi.bi_private;
1936 autogroup_def_t *agd;
1939 slap_callback cb = { 0 };
1941 void *thrctx = ldap_pvt_thread_pool_context();
1942 Connection conn = { 0 };
1943 OperationBuffer opbuf;
1945 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1947 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1951 connection_fake_init( &conn, &opbuf, thrctx );
1954 op->ors_attrsonly = 0;
1955 op->o_tag = LDAP_REQ_SEARCH;
1956 op->o_dn = be->be_rootdn;
1957 op->o_ndn = be->be_rootndn;
1959 op->o_req_dn = be->be_suffix[0];
1960 op->o_req_ndn = be->be_nsuffix[0];
1962 op->ors_scope = LDAP_SCOPE_SUBTREE;
1963 op->ors_deref = LDAP_DEREF_NEVER;
1964 op->ors_limit = NULL;
1965 op->ors_tlimit = SLAP_NO_LIMIT;
1966 op->ors_slimit = SLAP_NO_LIMIT;
1967 op->ors_attrs = slap_anlist_no_attrs;
1970 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1973 cb.sc_private = &ags;
1974 cb.sc_response = autogroup_group_add_cb;
1975 cb.sc_cleanup = NULL;
1978 op->o_callback = &cb;
1980 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1981 SlapReply rs = { REP_RESULT };
1983 autogroup_build_def_filter(agd, op);
1987 op->o_bd->be_search( op, &rs );
1989 filter_free_x( op, op->ors_filter, 1 );
1990 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1993 if( ! agi->agi_memberof_ad ){
1995 const char *text = NULL;
1997 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1998 if ( rc != LDAP_SUCCESS ) {
1999 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
2000 "unable to find attribute=\"%s\": %s (%d)\n",
2001 SLAPD_MEMBEROF_ATTR, text, rc );
2014 slap_overinst *on = (slap_overinst *) be->bd_info;
2016 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
2018 if ( on->on_bi.bi_private ) {
2019 autogroup_info_t *agi = on->on_bi.bi_private;
2020 autogroup_entry_t *age = agi->agi_entry,
2022 autogroup_filter_t *agf, *agf_next;
2024 for ( age_next = age; age_next; age = age_next ) {
2025 age_next = age->age_next;
2027 ch_free( age->age_dn.bv_val );
2028 ch_free( age->age_ndn.bv_val );
2030 agf = age->age_filter;
2032 for ( agf_next = agf; agf_next; agf = agf_next ) {
2033 agf_next = agf->agf_next;
2035 filter_free( agf->agf_filter );
2036 ch_free( agf->agf_filterstr.bv_val );
2037 ch_free( agf->agf_dn.bv_val );
2038 ch_free( agf->agf_ndn.bv_val );
2039 anlist_free( agf->agf_anlist, 1, NULL );
2043 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2052 autogroup_db_destroy(
2056 slap_overinst *on = (slap_overinst *) be->bd_info;
2058 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2060 if ( on->on_bi.bi_private ) {
2061 autogroup_info_t *agi = on->on_bi.bi_private;
2062 autogroup_def_t *agd = agi->agi_def,
2065 for ( agd_next = agd; agd_next; agd = agd_next ) {
2066 agd_next = agd->agd_next;
2071 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2078 static slap_overinst autogroup = { { NULL } };
2082 autogroup_initialize(void)
2085 autogroup.on_bi.bi_type = "autogroup";
2087 autogroup.on_bi.bi_db_open = autogroup_db_open;
2088 autogroup.on_bi.bi_db_close = autogroup_db_close;
2089 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2091 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2092 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2093 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2094 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2096 autogroup.on_response = autogroup_response;
2098 autogroup.on_bi.bi_cf_ocs = agocs;
2100 rc = config_register_schema( agcfg, agocs );
2105 return overlay_register( &autogroup );
2109 init_module( int argc, char *argv[] )
2111 return autogroup_initialize();