1 /* autogroup.c - automatic group overlay */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2007-2011 The OpenLDAP Foundation.
6 * Portions Copyright 2007 Michał Szulczyński.
7 * Portions Copyright 2009 Howard Chu.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
19 * This work was initially developed by Michał Szulczyński for inclusion in
20 * OpenLDAP Software. Additional significant contributors include:
31 #include <ac/string.h>
37 #ifndef SLAPD_MEMBEROF_ATTR
38 #define SLAPD_MEMBEROF_ATTR "memberOf"
41 /* Filter represents the memberURL of a group. */
42 typedef struct autogroup_filter_t {
43 struct berval agf_dn; /* The base DN in memberURL */
44 struct berval agf_ndn;
45 struct berval agf_filterstr;
48 AttributeName *agf_anlist;
49 struct autogroup_filter_t *agf_next;
52 /* Description of group attributes. */
53 typedef struct autogroup_def_t {
55 AttributeDescription *agd_member_url_ad;
56 AttributeDescription *agd_member_ad;
57 struct autogroup_def_t *agd_next;
60 /* Represents the group entry. */
61 typedef struct autogroup_entry_t {
64 autogroup_filter_t *age_filter; /* List of filters made from memberURLs */
65 autogroup_def_t *age_def; /* Attribute definition */
66 ldap_pvt_thread_mutex_t age_mutex;
67 int age_mustrefresh; /* Defined in request to refresh in response */
68 int age_modrdn_olddnmodified; /* Defined in request to refresh in response */
69 struct autogroup_entry_t *age_next;
72 /* Holds pointers to attribute definitions and groups. */
73 typedef struct autogroup_info_t {
74 autogroup_def_t *agi_def; /* Group attributes definitions. */
75 autogroup_entry_t *agi_entry; /* Group entries. */
76 AttributeDescription *agi_memberof_ad; /* memberOf attribute description */
77 ldap_pvt_thread_mutex_t agi_mutex;
80 /* Search callback for adding groups initially. */
81 typedef struct autogroup_sc_t {
82 autogroup_info_t *ags_info; /* Group definitions and entries. */
83 autogroup_def_t *ags_def; /* Attributes definition of the group being added. */
86 /* Used for adding members, found when searching, to a group. */
87 typedef struct autogroup_ga_t {
88 autogroup_entry_t *agg_group; /* The group to which the members will be added. */
89 autogroup_filter_t *agg_filter; /* Current filter */
90 Entry *agg_entry; /* Used in autogroup_member_search_cb to modify
91 this entry with the search results. */
93 Modifications *agg_mod; /* Used in autogroup_member_search_modify_cb to hold the
94 search results which will be added to the group. */
96 Modifications *agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
97 have to search for the last mod added. */
102 ** dn, ndn - the DN of the member to add
103 ** age - the group to which the member DN will be added
106 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
108 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
109 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
110 SlapReply sreply = {REP_RESULT};
111 BerValue *vals, *nvals;
112 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
115 assert( dn != NULL );
116 assert( ndn != NULL );
117 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
118 dn->bv_val, age->age_dn.bv_val, 0);
120 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
121 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
122 ber_dupbv( vals, dn );
123 BER_BVZERO( &vals[ 1 ] );
124 ber_dupbv( nvals, ndn );
125 BER_BVZERO( &nvals[ 1 ] );
127 modlist->sml_op = LDAP_MOD_ADD;
128 modlist->sml_desc = age->age_def->agd_member_ad;
129 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
130 modlist->sml_values = vals;
131 modlist->sml_nvalues = nvals;
132 modlist->sml_numvals = 1;
133 modlist->sml_flags = SLAP_MOD_INTERNAL;
134 modlist->sml_next = NULL;
136 o.o_tag = LDAP_REQ_MODIFY;
138 o.orm_modlist = modlist;
139 o.o_req_dn = age->age_dn;
140 o.o_req_ndn = age->age_ndn;
141 o.o_permissive_modify = 1;
142 o.o_managedsait = SLAP_CONTROL_CRITICAL;
143 o.o_relax = SLAP_CONTROL_CRITICAL;
145 o.o_bd->bd_info = (BackendInfo *)on->on_info;
146 (void)op->o_bd->be_modify( &o, &sreply );
147 o.o_bd->bd_info = (BackendInfo *)on;
149 slap_mods_free( modlist, 1 );
151 return sreply.sr_err;
155 ** e - the entry where to get the attribute values
156 ** age - the group to which the values will be added
159 autogroup_add_member_values_to_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
161 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
162 Modifications modlist;
163 SlapReply sreply = {REP_RESULT};
165 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 e->e_name.bv_val, age->age_dn.bv_val, 0);
172 attr = attrs_find( e->e_attrs, attrdesc );
178 modlist.sml_op = LDAP_MOD_ADD;
179 modlist.sml_desc = age->age_def->agd_member_ad;
180 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
181 modlist.sml_values = attr->a_vals;
182 modlist.sml_nvalues = attr->a_nvals;
183 modlist.sml_numvals = attr->a_numvals;
184 modlist.sml_flags = SLAP_MOD_INTERNAL;
185 modlist.sml_next = NULL;
187 o.o_tag = LDAP_REQ_MODIFY;
189 o.orm_modlist = &modlist;
190 o.o_req_dn = age->age_dn;
191 o.o_req_ndn = age->age_ndn;
192 o.o_permissive_modify = 1;
193 o.o_managedsait = SLAP_CONTROL_CRITICAL;
194 o.o_relax = SLAP_CONTROL_CRITICAL;
196 o.o_bd->bd_info = (BackendInfo *)on->on_info;
197 (void)op->o_bd->be_modify( &o, &sreply );
198 o.o_bd->bd_info = (BackendInfo *)on;
200 return sreply.sr_err;
204 ** dn,ndn - the DN to be deleted
205 ** age - the group from which the DN will be deleted
206 ** If we pass a NULL dn and ndn, all members are deleted from the group.
209 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
211 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
212 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
213 SlapReply sreply = {REP_RESULT};
214 BerValue *vals, *nvals;
215 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
218 if ( dn == NULL || ndn == NULL ) {
219 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
220 age->age_dn.bv_val, 0 ,0);
222 modlist->sml_values = NULL;
223 modlist->sml_nvalues = NULL;
224 modlist->sml_numvals = 0;
226 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
227 dn->bv_val, age->age_dn.bv_val, 0);
229 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
230 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
231 ber_dupbv( vals, dn );
232 BER_BVZERO( &vals[ 1 ] );
233 ber_dupbv( nvals, ndn );
234 BER_BVZERO( &nvals[ 1 ] );
236 modlist->sml_values = vals;
237 modlist->sml_nvalues = nvals;
238 modlist->sml_numvals = 1;
242 modlist->sml_op = LDAP_MOD_DELETE;
243 modlist->sml_desc = age->age_def->agd_member_ad;
244 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
245 modlist->sml_flags = SLAP_MOD_INTERNAL;
246 modlist->sml_next = NULL;
249 o.o_tag = LDAP_REQ_MODIFY;
250 o.orm_modlist = modlist;
251 o.o_req_dn = age->age_dn;
252 o.o_req_ndn = age->age_ndn;
253 o.o_relax = SLAP_CONTROL_CRITICAL;
254 o.o_managedsait = SLAP_CONTROL_CRITICAL;
255 o.o_permissive_modify = 1;
257 o.o_bd->bd_info = (BackendInfo *)on->on_info;
258 (void)op->o_bd->be_modify( &o, &sreply );
259 o.o_bd->bd_info = (BackendInfo *)on;
261 slap_mods_free( modlist, 1 );
263 return sreply.sr_err;
267 ** e - the entry where to get the attribute values
268 ** age - the group from which the values will be deleted
271 autogroup_delete_member_values_from_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
273 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
274 Modifications modlist;
275 SlapReply sreply = {REP_RESULT};
277 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
281 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
282 e->e_name.bv_val, age->age_dn.bv_val, 0);
284 attr = attrs_find( e->e_attrs, attrdesc );
290 modlist.sml_op = LDAP_MOD_DELETE;
291 modlist.sml_desc = age->age_def->agd_member_ad;
292 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
293 modlist.sml_values = attr->a_vals;
294 modlist.sml_nvalues = attr->a_nvals;
295 modlist.sml_numvals = attr->a_numvals;
296 modlist.sml_flags = SLAP_MOD_INTERNAL;
297 modlist.sml_next = NULL;
299 o.o_tag = LDAP_REQ_MODIFY;
301 o.orm_modlist = &modlist;
302 o.o_req_dn = age->age_dn;
303 o.o_req_ndn = age->age_ndn;
304 o.o_permissive_modify = 1;
305 o.o_managedsait = SLAP_CONTROL_CRITICAL;
306 o.o_relax = SLAP_CONTROL_CRITICAL;
308 o.o_bd->bd_info = (BackendInfo *)on->on_info;
309 (void)op->o_bd->be_modify( &o, &sreply );
310 o.o_bd->bd_info = (BackendInfo *)on;
312 return sreply.sr_err;
316 ** Callback used to add entries to a group,
317 ** which are going to be written in the database
318 ** (used in bi_op_add)
319 ** The group is passed in autogroup_ga_t->agg_group
322 autogroup_member_search_cb( Operation *op, SlapReply *rs )
324 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
326 assert( op->o_tag == LDAP_REQ_SEARCH );
328 if ( rs->sr_type == REP_SEARCH ) {
329 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
330 autogroup_entry_t *age = agg->agg_group;
331 autogroup_filter_t *agf = agg->agg_filter;
333 const char *text = NULL;
335 struct berval *vals, *nvals;
338 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
339 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
341 if ( agf->agf_anlist ) {
342 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
345 nvals = attr->a_nvals;
346 numvals = attr->a_numvals;
352 struct berval lvals[ 2 ], lnvals[ 2 ];
353 lvals[ 0 ] = rs->sr_entry->e_name;
354 BER_BVZERO( &lvals[ 1 ] );
355 lnvals[ 0 ] = rs->sr_entry->e_nname;
356 BER_BVZERO( &lnvals[ 1 ] );
362 mod.sm_op = LDAP_MOD_ADD;
363 mod.sm_desc = age->age_def->agd_member_ad;
364 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
365 mod.sm_values = vals;
366 mod.sm_nvalues = nvals;
367 mod.sm_numvals = numvals;
369 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
376 ** Callback used to add entries to a group, which is already in the database.
377 ** (used in on_response)
378 ** The group is passed in autogroup_ga_t->agg_group
382 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
384 assert( op->o_tag == LDAP_REQ_SEARCH );
386 if ( rs->sr_type == REP_SEARCH ) {
387 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
388 autogroup_entry_t *age = agg->agg_group;
389 autogroup_filter_t *agf = agg->agg_filter;
390 Modifications *modlist;
391 struct berval *vals, *nvals;
394 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
395 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
397 if ( agf->agf_anlist ) {
398 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
401 nvals = attr->a_nvals;
402 numvals = attr->a_numvals;
408 struct berval lvals[ 2 ], lnvals[ 2 ];
409 lvals[ 0 ] = rs->sr_entry->e_name;
410 BER_BVZERO( &lvals[ 1 ] );
411 lnvals[ 0 ] = rs->sr_entry->e_nname;
412 BER_BVZERO( &lnvals[ 1 ] );
419 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
421 modlist->sml_op = LDAP_MOD_ADD;
422 modlist->sml_desc = age->age_def->agd_member_ad;
423 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
425 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
426 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
427 modlist->sml_numvals = numvals;
429 modlist->sml_flags = SLAP_MOD_INTERNAL;
430 modlist->sml_next = NULL;
432 if ( agg->agg_mod == NULL ) {
433 agg->agg_mod = modlist;
434 agg->agg_mod_last = modlist;
436 agg->agg_mod_last->sml_next = modlist;
437 agg->agg_mod_last = modlist;
448 ** Adds all entries matching the passed filter to the specified group.
449 ** If modify == 1, then we modify the group's entry in the database using be_modify.
450 ** If modify == 0, then, we must supply a rw entry for the group,
451 ** because we only modify the entry, without calling be_modify.
452 ** e - the group entry, to which the members will be added
457 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
459 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
461 SlapReply rs = { REP_SEARCH };
462 slap_callback cb = { 0 };
463 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
466 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
467 age->age_dn.bv_val, 0, 0);
470 o.o_tag = LDAP_REQ_SEARCH;
472 o.o_req_dn = agf->agf_dn;
473 o.o_req_ndn = agf->agf_ndn;
475 o.ors_filterstr = agf->agf_filterstr;
476 o.ors_filter = agf->agf_filter;
478 o.ors_scope = agf->agf_scope;
479 o.ors_deref = LDAP_DEREF_NEVER;
481 o.ors_tlimit = SLAP_NO_LIMIT;
482 o.ors_slimit = SLAP_NO_LIMIT;
483 o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
486 agg.agg_filter = agf;
488 agg.agg_mod_last = NULL;
490 cb.sc_private = &agg;
493 cb.sc_response = autogroup_member_search_modify_cb;
495 cb.sc_response = autogroup_member_search_cb;
498 cb.sc_cleanup = NULL;
503 o.o_bd->bd_info = (BackendInfo *)on->on_info;
504 op->o_bd->be_search( &o, &rs );
505 o.o_bd->bd_info = (BackendInfo *)on;
507 if ( modify == 1 && agg.agg_mod ) {
508 rs_reinit( &rs, REP_RESULT );
511 o.o_callback = &null_cb;
512 o.o_tag = LDAP_REQ_MODIFY;
513 o.orm_modlist = agg.agg_mod;
514 o.o_req_dn = age->age_dn;
515 o.o_req_ndn = age->age_ndn;
516 o.o_relax = SLAP_CONTROL_CRITICAL;
517 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
518 o.o_permissive_modify = 1;
520 o.o_bd->bd_info = (BackendInfo *)on->on_info;
521 (void)op->o_bd->be_modify( &o, &rs );
522 o.o_bd->bd_info = (BackendInfo *)on;
524 slap_mods_free(agg.agg_mod, 1);
531 ** Adds a group to the internal list from the passed entry.
532 ** scan specifies whether to add all maching members to the group.
533 ** modify specifies whether to modify the given group entry (when modify == 0),
534 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
535 ** agi - pointer to the groups and the attribute definitions
536 ** agd - the attribute definition of the added group
537 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
538 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
541 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
543 autogroup_entry_t **agep = &agi->agi_entry;
544 autogroup_filter_t *agf, *agf_prev = NULL;
545 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
546 LDAPURLDesc *lud = NULL;
549 int rc = 0, match = 1, null_entry = 0;
552 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
553 LDAP_SUCCESS || e == NULL ) {
554 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
561 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
562 e->e_name.bv_val, 0, 0);
564 if ( agi->agi_entry != NULL ) {
565 for ( ; *agep ; agep = &(*agep)->age_next ) {
566 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
568 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
576 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
577 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
578 (*agep)->age_def = agd;
579 (*agep)->age_filter = NULL;
580 (*agep)->age_mustrefresh = 0;
581 (*agep)->age_modrdn_olddnmodified = 0;
583 ber_dupbv( &(*agep)->age_dn, &e->e_name );
584 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
586 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
588 if ( null_entry == 1 ) {
590 overlay_entry_release_ov( op, e, 0, on );
594 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
596 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
598 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
600 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
601 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
607 agf->agf_scope = lud->lud_scope;
609 if ( lud->lud_dn == NULL ) {
610 BER_BVSTR( &dn, "" );
612 ber_str2bv( lud->lud_dn, 0, 0, &dn );
615 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
616 if ( rc != LDAP_SUCCESS ) {
617 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
622 if ( lud->lud_filter != NULL ) {
623 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
624 agf->agf_filter = str2filter( lud->lud_filter );
627 if ( lud->lud_attrs != NULL ) {
630 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
635 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: to much attributes specified in url <%s>\n",
638 ldap_free_urldesc( lud );
642 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
644 if ( agf->agf_anlist == NULL ) {
645 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
646 lud->lud_attrs[0], 0, 0 );
648 ldap_free_urldesc( lud );
654 agf->agf_next = NULL;
657 if( (*agep)->age_filter == NULL ) {
658 (*agep)->age_filter = agf;
661 if( agf_prev != NULL ) {
662 agf_prev->agf_next = agf;
668 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
671 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
672 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
674 ldap_free_urldesc( lud );
681 ldap_free_urldesc( lud );
686 if ( null_entry == 1 ) {
693 ** Used when opening the database to add all existing
694 ** groups from the database to our internal list.
697 autogroup_group_add_cb( Operation *op, SlapReply *rs )
699 assert( op->o_tag == LDAP_REQ_SEARCH );
701 if ( rs->sr_type == REP_SEARCH ) {
702 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
704 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
705 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
707 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
715 ** When adding a group, we first strip any existing members,
716 ** and add all which match the filters ourselfs.
719 autogroup_add_entry( Operation *op, SlapReply *rs)
721 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
722 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
723 autogroup_def_t *agd = agi->agi_def;
724 autogroup_entry_t *age;
725 autogroup_filter_t *agf;
728 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
729 op->ora_e->e_name.bv_val, 0, 0);
731 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
733 /* Check if it's a group. */
734 for ( ; agd ; agd = agd->agd_next ) {
735 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
737 const char *text = NULL;
740 mod.sm_op = LDAP_MOD_DELETE;
741 mod.sm_desc = agd->agd_member_ad;
742 mod.sm_type = agd->agd_member_ad->ad_cname;
743 mod.sm_values = NULL;
744 mod.sm_nvalues = NULL;
746 /* We don't want any member attributes added by the user. */
747 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
749 autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
750 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
751 return SLAP_CB_CONTINUE;
756 for ( age = agi->agi_entry; age ; age = age->age_next ) {
757 ldap_pvt_thread_mutex_lock( &age->age_mutex );
759 /* Check if any of the filters are the suffix to the entry DN.
760 If yes, we can test that filter against the entry. */
762 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
763 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
764 rc = test_filter( op, op->ora_e, agf->agf_filter );
765 if ( rc == LDAP_COMPARE_TRUE ) {
766 if ( agf->agf_anlist ) {
767 autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
769 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
775 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
778 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
780 return SLAP_CB_CONTINUE;
784 ** agi - internal group and attribute definitions list
785 ** e - the group to remove from the internal list
788 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
790 autogroup_entry_t *age = agi->agi_entry,
795 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
796 age->age_dn.bv_val, 0, 0);
798 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
799 age_next = age->age_next;
802 autogroup_filter_t *agf = age->age_filter,
805 if ( age_prev != NULL ) {
806 age_prev->age_next = age_next;
808 agi->agi_entry = NULL;
811 ch_free( age->age_dn.bv_val );
812 ch_free( age->age_ndn.bv_val );
814 for( agf_next = agf ; agf_next ; agf = agf_next ){
815 agf_next = agf->agf_next;
817 filter_free( agf->agf_filter );
818 ch_free( agf->agf_filterstr.bv_val );
819 ch_free( agf->agf_dn.bv_val );
820 ch_free( agf->agf_ndn.bv_val );
821 anlist_free( agf->agf_anlist, 1, NULL );
825 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
826 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
835 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
842 autogroup_delete_entry( Operation *op, SlapReply *rs)
844 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
845 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
846 autogroup_entry_t *age, *age_prev, *age_next;
847 autogroup_filter_t *agf;
849 int matched_group = 0, rc = 0;
851 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
853 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
855 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
856 LDAP_SUCCESS || e == NULL ) {
857 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
858 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
859 return SLAP_CB_CONTINUE;
862 /* Check if the entry to be deleted is one of our groups. */
863 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
865 ldap_pvt_thread_mutex_lock( &age->age_mutex );
866 age_next = age->age_next;
868 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
873 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
876 autogroup_delete_group( agi, age );
881 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
884 if ( matched_group == 1 ) {
885 overlay_entry_release_ov( op, e, 0, on );
886 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
887 return SLAP_CB_CONTINUE;
890 /* Check if the entry matches any of the groups.
891 If yes, we can delete the entry from that group. */
893 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
894 ldap_pvt_thread_mutex_lock( &age->age_mutex );
896 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
897 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
898 rc = test_filter( op, e, agf->agf_filter );
899 if ( rc == LDAP_COMPARE_TRUE ) {
900 /* If the attribute is retrieved from the entry, we don't know what to delete
901 ** So the group must be entirely refreshed
902 ** But the refresh can't be done now because the entry is not deleted
903 ** So the group is marked as mustrefresh
905 if ( agf->agf_anlist ) {
906 age->age_mustrefresh = 1;
908 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
914 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
917 overlay_entry_release_ov( op, e, 0, on );
918 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
920 return SLAP_CB_CONTINUE;
924 autogroup_response( Operation *op, SlapReply *rs )
926 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
927 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
928 autogroup_def_t *agd = agi->agi_def;
929 autogroup_entry_t *age;
930 autogroup_filter_t *agf;
931 BerValue new_dn, new_ndn, pdn;
934 int is_olddn, is_newdn, is_value_refresh, dn_equal;
936 /* Handle all cases where a refresh of the group is needed */
937 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
938 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
940 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
942 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
943 /* Request detected that the group must be refreshed */
945 ldap_pvt_thread_mutex_lock( &age->age_mutex );
947 if ( age->age_mustrefresh ) {
948 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
950 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
951 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
955 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
958 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
960 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
961 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
963 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
965 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
967 if ( op->oq_modrdn.rs_newSup ) {
968 pdn = *op->oq_modrdn.rs_newSup;
970 dnParent( &op->o_req_dn, &pdn );
972 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
974 if ( op->oq_modrdn.rs_nnewSup ) {
975 pdn = *op->oq_modrdn.rs_nnewSup;
977 dnParent( &op->o_req_ndn, &pdn );
979 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
981 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
983 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
985 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
986 LDAP_SUCCESS || e == NULL ) {
987 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
988 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
989 return SLAP_CB_CONTINUE;
992 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
996 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
997 overlay_entry_release_ov( op, e, 0, on );
998 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
999 return SLAP_CB_CONTINUE;
1003 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1004 for ( ; agd; agd = agd->agd_next ) {
1006 if ( value_find_ex( slap_schema.si_ad_objectClass,
1007 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1008 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1009 a->a_nvals, &agd->agd_oc->soc_cname,
1010 op->o_tmpmemctx ) == 0 )
1012 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1015 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1017 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1018 ber_dupbv( &age->age_dn, &new_dn );
1019 ber_dupbv( &age->age_ndn, &new_ndn );
1021 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1022 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1023 overlay_entry_release_ov( op, e, 0, on );
1024 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1025 return SLAP_CB_CONTINUE;
1033 1. check if the orginal entry's DN is in the group.
1034 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1036 If 1 and 2 are both false, we do nothing.
1037 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1038 If 1 is false, and 2 is true, we check the entry against the group's filters,
1039 and add it's DN to the group.
1040 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1042 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1045 is_value_refresh = 0;
1048 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1050 if ( age->age_filter && age->age_filter->agf_anlist ) {
1051 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1057 if ( age->age_modrdn_olddnmodified ) {
1058 /* Resquest already marked this group to be updated */
1060 is_value_refresh = 1;
1061 age->age_modrdn_olddnmodified = 0;
1064 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1065 LDAP_SUCCESS || group == NULL ) {
1066 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1068 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1069 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1071 overlay_entry_release_ov( op, e, 0, on );
1072 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1073 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1074 return SLAP_CB_CONTINUE;
1077 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1080 if ( value_find_ex( age->age_def->agd_member_ad,
1081 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1082 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1083 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1090 overlay_entry_release_ov( op, group, 0, on );
1094 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1095 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1096 /* TODO: should retest filter as it could imply conditions on the dn */
1103 if ( is_value_refresh ) {
1104 if ( is_olddn != is_newdn ) {
1106 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1108 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1109 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1112 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1115 if ( is_olddn == 1 && is_newdn == 0 ) {
1117 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1119 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1121 if ( is_olddn == 0 && is_newdn == 1 ) {
1122 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1123 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1125 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1127 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1132 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1135 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1137 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1138 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1142 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1143 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1147 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1150 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1151 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1153 overlay_entry_release_ov( op, e, 0, on );
1155 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1159 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1160 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1161 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1163 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1165 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1166 LDAP_SUCCESS || e == NULL ) {
1167 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1168 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1169 return SLAP_CB_CONTINUE;
1172 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1176 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1177 overlay_entry_release_ov( op, e, 0, on );
1178 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1179 return SLAP_CB_CONTINUE;
1182 /* If we modify a group's memberURL, we have to delete all of it's members,
1183 and add them anew, because we cannot tell from which memberURL a member was added. */
1184 for ( ; agd; agd = agd->agd_next ) {
1186 if ( value_find_ex( slap_schema.si_ad_objectClass,
1187 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1188 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1189 a->a_nvals, &agd->agd_oc->soc_cname,
1190 op->o_tmpmemctx ) == 0 )
1195 m = op->orm_modlist;
1197 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1198 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1200 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1203 for ( ; m ; m = m->sml_next ) {
1204 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1205 autogroup_def_t *group_agd = age->age_def;
1206 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1207 op->o_req_dn.bv_val, 0, 0);
1209 overlay_entry_release_ov( op, e, 0, on );
1211 autogroup_delete_member_from_group( op, NULL, NULL, age );
1212 autogroup_delete_group( agi, age );
1214 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1216 overlay_entry_release_ov( op, e, 0, on );
1217 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1218 return SLAP_CB_CONTINUE;
1222 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1226 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1229 overlay_entry_release_ov( op, e, 0, on );
1230 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1231 return SLAP_CB_CONTINUE;
1235 /* When modifing any of the attributes of an entry, we must
1236 check if the entry is in any of our groups, and if
1237 the modified entry maches any of the filters of that group.
1239 If the entry exists in a group, but the modified attributes do
1240 not match any of the group's filters, we delete the entry from that group.
1241 If the entry doesn't exist in a group, but matches a filter,
1242 we add it to that group.
1244 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1249 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1251 if ( age->age_filter && age->age_filter->agf_anlist ) {
1252 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1258 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1259 LDAP_SUCCESS || group == NULL ) {
1260 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1261 age->age_dn.bv_val, 0, 0);
1263 overlay_entry_release_ov( op, e, 0, on );
1264 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1265 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1266 return SLAP_CB_CONTINUE;
1269 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1272 if ( value_find_ex( age->age_def->agd_member_ad,
1273 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1274 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1275 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1282 overlay_entry_release_ov( op, group, 0, on );
1284 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1285 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1286 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1293 if ( is_olddn == 1 && is_newdn == 0 ) {
1295 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1297 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1299 if ( is_olddn == 0 && is_newdn == 1 ) {
1301 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1303 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1306 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1309 overlay_entry_release_ov( op, e, 0, on );
1311 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1315 return SLAP_CB_CONTINUE;
1319 ** Detect if filter contains a memberOf check for dn
1322 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1325 if ( f == NULL ) return 0;
1327 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1328 case LDAP_FILTER_AND:
1329 case LDAP_FILTER_OR:
1330 case LDAP_FILTER_NOT:
1331 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1332 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1335 case LDAP_FILTER_EQUALITY:
1336 result = ( f->f_ava->aa_desc == memberof_ad &&
1337 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1347 ** When modifing a group, we must deny any modifications to the member attribute,
1348 ** because the group would be inconsistent.
1351 autogroup_modify_entry( Operation *op, SlapReply *rs)
1353 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1354 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1355 autogroup_def_t *agd = agi->agi_def;
1356 autogroup_entry_t *age;
1360 if ( get_manageDSAit( op ) ) {
1361 return SLAP_CB_CONTINUE;
1364 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1365 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1367 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1368 LDAP_SUCCESS || e == NULL ) {
1369 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1370 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1371 return SLAP_CB_CONTINUE;
1374 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1375 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1376 autogroup_filter_t *agf;
1377 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1378 if ( agf->agf_anlist ) {
1380 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1381 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1382 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1383 int rc = test_filter( op, e, agf->agf_filter );
1384 if ( rc == LDAP_COMPARE_TRUE ) {
1385 age->age_mustrefresh = 1;
1392 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1393 age->age_mustrefresh = 1;
1398 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1401 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1402 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1403 return SLAP_CB_CONTINUE;
1407 for ( ; agd; agd = agd->agd_next ) {
1409 if ( value_find_ex( slap_schema.si_ad_objectClass,
1410 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1411 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1412 a->a_nvals, &agd->agd_oc->soc_cname,
1413 op->o_tmpmemctx ) == 0 )
1418 m = op->orm_modlist;
1420 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1421 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1424 for ( ; m ; m = m->sml_next ) {
1425 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1426 overlay_entry_release_ov( op, e, 0, on );
1427 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1428 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1429 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1430 return LDAP_CONSTRAINT_VIOLATION;
1437 overlay_entry_release_ov( op, e, 0, on );
1438 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1439 return SLAP_CB_CONTINUE;
1443 overlay_entry_release_ov( op, e, 0, on );
1444 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1445 return SLAP_CB_CONTINUE;
1449 ** Detect if the olddn is part of a group and so if the group should be refreshed
1452 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1454 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1455 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1456 autogroup_entry_t *age;
1459 if ( get_manageDSAit( op ) ) {
1460 return SLAP_CB_CONTINUE;
1463 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1464 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1466 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1467 LDAP_SUCCESS || e == NULL ) {
1468 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1469 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1470 return SLAP_CB_CONTINUE;
1473 /* Must check if a dn is modified */
1474 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1475 autogroup_filter_t *agf;
1476 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1477 if ( agf->agf_anlist ) {
1478 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1479 int rc = test_filter( op, e, agf->agf_filter );
1480 if ( rc == LDAP_COMPARE_TRUE ) {
1481 age->age_modrdn_olddnmodified = 1;
1488 overlay_entry_release_ov( op, e, 0, on );
1489 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1490 return SLAP_CB_CONTINUE;
1494 ** Builds a filter for searching for the
1495 ** group entries, according to the objectClass.
1498 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1502 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1504 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1505 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1506 + agd->agd_oc->soc_cname.bv_len;
1507 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1509 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1511 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1515 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1517 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1528 static ConfigDriver ag_cfgen;
1530 static ConfigTable agcfg[] = {
1531 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1532 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1533 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1534 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1535 "EQUALITY caseIgnoreMatch "
1536 "SYNTAX OMsDirectoryString "
1537 "X-ORDERED 'VALUES' )",
1540 { "autogroup-memberof-ad", "memberOf attribute",
1541 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1542 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1543 "DESC 'memberOf attribute' "
1544 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1547 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1550 static ConfigOCs agocs[] = {
1551 { "( OLcfgCtOc:2.1 "
1552 "NAME 'olcAutomaticGroups' "
1553 "DESC 'Automatic groups configuration' "
1554 "SUP olcOverlayConfig "
1557 "$ olcAGmemberOfAd "
1560 Cft_Overlay, agcfg, NULL, NULL },
1566 ag_cfgen( ConfigArgs *c )
1568 slap_overinst *on = (slap_overinst *)c->bi;
1569 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1570 autogroup_def_t *agd;
1571 autogroup_entry_t *age;
1575 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1578 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1579 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1580 agi->agi_def = NULL;
1581 agi->agi_entry = NULL;
1582 on->on_bi.bi_private = (void *)agi;
1586 age = agi->agi_entry;
1588 if ( c->op == SLAP_CONFIG_EMIT ) {
1592 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1594 char *ptr = c->cr_msg;
1596 assert(agd->agd_oc != NULL);
1597 assert(agd->agd_member_url_ad != NULL);
1598 assert(agd->agd_member_ad != NULL);
1600 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1601 SLAP_X_ORDERED_FMT "%s %s %s", i,
1602 agd->agd_oc->soc_cname.bv_val,
1603 agd->agd_member_url_ad->ad_cname.bv_val,
1604 agd->agd_member_ad->ad_cname.bv_val );
1606 bv.bv_val = c->cr_msg;
1607 bv.bv_len = ptr - bv.bv_val;
1608 value_add_one ( &c->rvalue_vals, &bv );
1613 case AG_MEMBER_OF_AD:
1614 if ( agi->agi_memberof_ad != NULL ){
1615 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1626 }else if ( c->op == LDAP_MOD_DELETE ) {
1628 autogroup_def_t *agd_next;
1629 autogroup_entry_t *age_next;
1630 autogroup_filter_t *agf = age->age_filter,
1633 for ( agd_next = agd; agd_next; agd = agd_next ) {
1634 agd_next = agd->agd_next;
1639 for ( age_next = age ; age_next ; age = age_next ) {
1640 age_next = age->age_next;
1642 ch_free( age->age_dn.bv_val );
1643 ch_free( age->age_ndn.bv_val );
1645 for( agf_next = agf ; agf_next ; agf = agf_next ){
1646 agf_next = agf->agf_next;
1648 filter_free( agf->agf_filter );
1649 ch_free( agf->agf_filterstr.bv_val );
1650 ch_free( agf->agf_dn.bv_val );
1651 ch_free( agf->agf_ndn.bv_val );
1652 anlist_free( agf->agf_anlist, 1, NULL );
1656 ldap_pvt_thread_mutex_init( &age->age_mutex );
1661 on->on_bi.bi_private = NULL;
1664 autogroup_def_t **agdp;
1665 autogroup_entry_t *age_next, *age_prev;
1666 autogroup_filter_t *agf,
1669 for ( i = 0, agdp = &agi->agi_def;
1672 if ( *agdp == NULL) {
1675 agdp = &(*agdp)->agd_next;
1679 *agdp = agd->agd_next;
1681 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1682 age_next = age->age_next;
1684 if( age->age_def == agd ) {
1685 agf = age->age_filter;
1687 ch_free( age->age_dn.bv_val );
1688 ch_free( age->age_ndn.bv_val );
1690 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1691 agf_next = agf->agf_next;
1692 filter_free( agf->agf_filter );
1693 ch_free( agf->agf_filterstr.bv_val );
1694 ch_free( agf->agf_dn.bv_val );
1695 ch_free( agf->agf_ndn.bv_val );
1696 anlist_free( agf->agf_anlist, 1, NULL );
1700 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1705 if( age_prev != NULL ) {
1706 age_prev->age_next = age_next;
1721 autogroup_def_t **agdp,
1723 ObjectClass *oc = NULL;
1724 AttributeDescription *member_url_ad = NULL,
1729 oc = oc_find( c->argv[ 1 ] );
1731 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1732 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1733 "unable to find ObjectClass \"%s\"",
1735 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1736 c->log, c->cr_msg, 0 );
1741 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1742 if( rc != LDAP_SUCCESS ) {
1743 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1744 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1745 "unable to find AttributeDescription \"%s\"",
1747 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1748 c->log, c->cr_msg, 0 );
1752 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1753 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1754 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1755 "AttributeDescription \"%s\" ",
1756 "must be of a subtype \"labeledURI\"",
1758 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1759 c->log, c->cr_msg, 0 );
1763 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1764 if( rc != LDAP_SUCCESS ) {
1765 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1766 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1767 "unable to find AttributeDescription \"%s\"",
1769 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1770 c->log, c->cr_msg, 0 );
1774 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1775 /* The same URL attribute / member attribute pair
1776 * cannot be repeated */
1778 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1779 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1780 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1781 "URL attributeDescription \"%s\" already mapped",
1782 member_ad->ad_cname.bv_val );
1783 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1784 c->log, c->cr_msg, 0 );
1785 /* return 1; //warning*/
1789 if ( c->valx > 0 ) {
1792 for ( i = 0, agdp = &agi->agi_def ;
1795 if ( *agdp == NULL ) {
1796 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1797 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1798 "invalid index {%d}",
1800 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1801 c->log, c->cr_msg, 0 );
1805 agdp = &(*agdp)->agd_next;
1810 for ( agdp = &agi->agi_def; *agdp;
1811 agdp = &(*agdp)->agd_next )
1815 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1817 (*agdp)->agd_oc = oc;
1818 (*agdp)->agd_member_url_ad = member_url_ad;
1819 (*agdp)->agd_member_ad = member_ad;
1820 (*agdp)->agd_next = agd_next;
1824 case AG_MEMBER_OF_AD: {
1825 AttributeDescription *memberof_ad = NULL;
1828 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1829 if( rc != LDAP_SUCCESS ) {
1830 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1831 "\"autogroup-memberof-ad <memberof-ad>\": "
1832 "unable to find AttributeDescription \"%s\"",
1834 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1835 c->log, c->cr_msg, 0 );
1839 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1840 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1842 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1843 "memberof attribute=\"%s\" must either "
1844 "have DN (%s) or nameUID (%s) syntax",
1845 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1846 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1847 c->log, c->cr_msg, 0 );
1851 agi->agi_memberof_ad = memberof_ad;
1863 extern int slapMode;
1866 ** Do a search for all the groups in the
1867 ** database, and add them to out internal list.
1874 slap_overinst *on = (slap_overinst *) be->bd_info;
1875 autogroup_info_t *agi = on->on_bi.bi_private;
1876 autogroup_def_t *agd;
1879 slap_callback cb = { 0 };
1881 void *thrctx = ldap_pvt_thread_pool_context();
1882 Connection conn = { 0 };
1883 OperationBuffer opbuf;
1885 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1887 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1891 connection_fake_init( &conn, &opbuf, thrctx );
1894 op->ors_attrsonly = 0;
1895 op->o_tag = LDAP_REQ_SEARCH;
1896 op->o_dn = be->be_rootdn;
1897 op->o_ndn = be->be_rootndn;
1899 op->o_req_dn = be->be_suffix[0];
1900 op->o_req_ndn = be->be_nsuffix[0];
1902 op->ors_scope = LDAP_SCOPE_SUBTREE;
1903 op->ors_deref = LDAP_DEREF_NEVER;
1904 op->ors_limit = NULL;
1905 op->ors_tlimit = SLAP_NO_LIMIT;
1906 op->ors_slimit = SLAP_NO_LIMIT;
1907 op->ors_attrs = slap_anlist_no_attrs;
1910 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1913 cb.sc_private = &ags;
1914 cb.sc_response = autogroup_group_add_cb;
1915 cb.sc_cleanup = NULL;
1918 op->o_callback = &cb;
1920 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1921 SlapReply rs = { REP_RESULT };
1923 autogroup_build_def_filter(agd, op);
1927 op->o_bd->be_search( op, &rs );
1929 filter_free_x( op, op->ors_filter, 1 );
1930 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1933 if( ! agi->agi_memberof_ad ){
1935 const char *text = NULL;
1937 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1938 if ( rc != LDAP_SUCCESS ) {
1939 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1940 "unable to find attribute=\"%s\": %s (%d)\n",
1941 SLAPD_MEMBEROF_ATTR, text, rc );
1954 slap_overinst *on = (slap_overinst *) be->bd_info;
1956 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1958 if ( on->on_bi.bi_private ) {
1959 autogroup_info_t *agi = on->on_bi.bi_private;
1960 autogroup_entry_t *age = agi->agi_entry,
1962 autogroup_filter_t *agf, *agf_next;
1964 for ( age_next = age; age_next; age = age_next ) {
1965 age_next = age->age_next;
1967 ch_free( age->age_dn.bv_val );
1968 ch_free( age->age_ndn.bv_val );
1970 agf = age->age_filter;
1972 for ( agf_next = agf; agf_next; agf = agf_next ) {
1973 agf_next = agf->agf_next;
1975 filter_free( agf->agf_filter );
1976 ch_free( agf->agf_filterstr.bv_val );
1977 ch_free( agf->agf_dn.bv_val );
1978 ch_free( agf->agf_ndn.bv_val );
1979 anlist_free( agf->agf_anlist, 1, NULL );
1983 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1992 autogroup_db_destroy(
1996 slap_overinst *on = (slap_overinst *) be->bd_info;
1998 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2000 if ( on->on_bi.bi_private ) {
2001 autogroup_info_t *agi = on->on_bi.bi_private;
2002 autogroup_def_t *agd = agi->agi_def,
2005 for ( agd_next = agd; agd_next; agd = agd_next ) {
2006 agd_next = agd->agd_next;
2011 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2018 static slap_overinst autogroup = { { NULL } };
2022 autogroup_initialize(void)
2025 autogroup.on_bi.bi_type = "autogroup";
2027 autogroup.on_bi.bi_db_open = autogroup_db_open;
2028 autogroup.on_bi.bi_db_close = autogroup_db_close;
2029 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2031 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2032 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2033 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2034 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2036 autogroup.on_response = autogroup_response;
2038 autogroup.on_bi.bi_cf_ocs = agocs;
2040 rc = config_register_schema( agcfg, agocs );
2045 return overlay_register( &autogroup );
2049 init_module( int argc, char *argv[] )
2051 return autogroup_initialize();