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: too many attributes specified in url <%s>\n",
638 ldap_free_urldesc( lud );
643 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
645 if ( agf->agf_anlist == NULL ) {
646 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
647 lud->lud_attrs[0], 0, 0 );
649 ldap_free_urldesc( lud );
655 agf->agf_next = NULL;
658 if( (*agep)->age_filter == NULL ) {
659 (*agep)->age_filter = agf;
662 if( agf_prev != NULL ) {
663 agf_prev->agf_next = agf;
669 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
672 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
673 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
675 ldap_free_urldesc( lud );
682 ldap_free_urldesc( lud );
687 if ( null_entry == 1 ) {
694 ** Used when opening the database to add all existing
695 ** groups from the database to our internal list.
698 autogroup_group_add_cb( Operation *op, SlapReply *rs )
700 assert( op->o_tag == LDAP_REQ_SEARCH );
702 if ( rs->sr_type == REP_SEARCH ) {
703 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
705 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
706 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
708 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
716 ** When adding a group, we first strip any existing members,
717 ** and add all which match the filters ourselfs.
720 autogroup_add_entry( Operation *op, SlapReply *rs)
722 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
723 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
724 autogroup_def_t *agd = agi->agi_def;
725 autogroup_entry_t *age;
726 autogroup_filter_t *agf;
729 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
730 op->ora_e->e_name.bv_val, 0, 0);
732 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
734 /* Check if it's a group. */
735 for ( ; agd ; agd = agd->agd_next ) {
736 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
738 const char *text = NULL;
741 mod.sm_op = LDAP_MOD_DELETE;
742 mod.sm_desc = agd->agd_member_ad;
743 mod.sm_type = agd->agd_member_ad->ad_cname;
744 mod.sm_values = NULL;
745 mod.sm_nvalues = NULL;
747 /* We don't want any member attributes added by the user. */
748 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
750 autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
751 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
752 return SLAP_CB_CONTINUE;
757 for ( age = agi->agi_entry; age ; age = age->age_next ) {
758 ldap_pvt_thread_mutex_lock( &age->age_mutex );
760 /* Check if any of the filters are the suffix to the entry DN.
761 If yes, we can test that filter against the entry. */
763 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
764 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
765 rc = test_filter( op, op->ora_e, agf->agf_filter );
766 if ( rc == LDAP_COMPARE_TRUE ) {
767 if ( agf->agf_anlist ) {
768 autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
770 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
776 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
779 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
781 return SLAP_CB_CONTINUE;
785 ** agi - internal group and attribute definitions list
786 ** e - the group to remove from the internal list
789 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
791 autogroup_entry_t *age = agi->agi_entry,
796 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
797 age->age_dn.bv_val, 0, 0);
799 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
800 age_next = age->age_next;
803 autogroup_filter_t *agf = age->age_filter,
806 if ( age_prev != NULL ) {
807 age_prev->age_next = age_next;
809 agi->agi_entry = NULL;
812 ch_free( age->age_dn.bv_val );
813 ch_free( age->age_ndn.bv_val );
815 for( agf_next = agf ; agf_next ; agf = agf_next ){
816 agf_next = agf->agf_next;
818 filter_free( agf->agf_filter );
819 ch_free( agf->agf_filterstr.bv_val );
820 ch_free( agf->agf_dn.bv_val );
821 ch_free( agf->agf_ndn.bv_val );
822 anlist_free( agf->agf_anlist, 1, NULL );
826 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
827 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
836 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
843 autogroup_delete_entry( Operation *op, SlapReply *rs)
845 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
846 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
847 autogroup_entry_t *age, *age_prev, *age_next;
848 autogroup_filter_t *agf;
850 int matched_group = 0, rc = 0;
852 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
854 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
856 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
857 LDAP_SUCCESS || e == NULL ) {
858 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
859 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
860 return SLAP_CB_CONTINUE;
863 /* Check if the entry to be deleted is one of our groups. */
864 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
866 ldap_pvt_thread_mutex_lock( &age->age_mutex );
867 age_next = age->age_next;
869 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
874 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
877 autogroup_delete_group( agi, age );
882 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
885 if ( matched_group == 1 ) {
886 overlay_entry_release_ov( op, e, 0, on );
887 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
888 return SLAP_CB_CONTINUE;
891 /* Check if the entry matches any of the groups.
892 If yes, we can delete the entry from that group. */
894 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
895 ldap_pvt_thread_mutex_lock( &age->age_mutex );
897 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
898 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
899 rc = test_filter( op, e, agf->agf_filter );
900 if ( rc == LDAP_COMPARE_TRUE ) {
901 /* If the attribute is retrieved from the entry, we don't know what to delete
902 ** So the group must be entirely refreshed
903 ** But the refresh can't be done now because the entry is not deleted
904 ** So the group is marked as mustrefresh
906 if ( agf->agf_anlist ) {
907 age->age_mustrefresh = 1;
909 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
915 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
918 overlay_entry_release_ov( op, e, 0, on );
919 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
921 return SLAP_CB_CONTINUE;
925 autogroup_response( Operation *op, SlapReply *rs )
927 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
928 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
929 autogroup_def_t *agd = agi->agi_def;
930 autogroup_entry_t *age;
931 autogroup_filter_t *agf;
932 BerValue new_dn, new_ndn, pdn;
935 int is_olddn, is_newdn, is_value_refresh, dn_equal;
937 /* Handle all cases where a refresh of the group is needed */
938 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
939 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
941 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
943 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
944 /* Request detected that the group must be refreshed */
946 ldap_pvt_thread_mutex_lock( &age->age_mutex );
948 if ( age->age_mustrefresh ) {
949 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
951 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
952 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
956 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
959 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
961 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
962 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
964 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
966 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
968 if ( op->oq_modrdn.rs_newSup ) {
969 pdn = *op->oq_modrdn.rs_newSup;
971 dnParent( &op->o_req_dn, &pdn );
973 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
975 if ( op->oq_modrdn.rs_nnewSup ) {
976 pdn = *op->oq_modrdn.rs_nnewSup;
978 dnParent( &op->o_req_ndn, &pdn );
980 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
982 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
984 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
986 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
987 LDAP_SUCCESS || e == NULL ) {
988 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
989 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
990 return SLAP_CB_CONTINUE;
993 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
997 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
998 overlay_entry_release_ov( op, e, 0, on );
999 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1000 return SLAP_CB_CONTINUE;
1004 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1005 for ( ; agd; agd = agd->agd_next ) {
1007 if ( value_find_ex( slap_schema.si_ad_objectClass,
1008 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1009 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1010 a->a_nvals, &agd->agd_oc->soc_cname,
1011 op->o_tmpmemctx ) == 0 )
1013 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1016 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1018 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1019 ber_dupbv( &age->age_dn, &new_dn );
1020 ber_dupbv( &age->age_ndn, &new_ndn );
1022 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1023 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1024 overlay_entry_release_ov( op, e, 0, on );
1025 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1026 return SLAP_CB_CONTINUE;
1034 1. check if the orginal entry's DN is in the group.
1035 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1037 If 1 and 2 are both false, we do nothing.
1038 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1039 If 1 is false, and 2 is true, we check the entry against the group's filters,
1040 and add it's DN to the group.
1041 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1043 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1046 is_value_refresh = 0;
1049 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1051 if ( age->age_filter && age->age_filter->agf_anlist ) {
1052 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1058 if ( age->age_modrdn_olddnmodified ) {
1059 /* Resquest already marked this group to be updated */
1061 is_value_refresh = 1;
1062 age->age_modrdn_olddnmodified = 0;
1065 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1066 LDAP_SUCCESS || group == NULL ) {
1067 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1069 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1070 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1072 overlay_entry_release_ov( op, e, 0, on );
1073 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1074 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1075 return SLAP_CB_CONTINUE;
1078 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1081 if ( value_find_ex( age->age_def->agd_member_ad,
1082 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1083 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1084 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1091 overlay_entry_release_ov( op, group, 0, on );
1095 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1096 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1097 /* TODO: should retest filter as it could imply conditions on the dn */
1104 if ( is_value_refresh ) {
1105 if ( is_olddn != is_newdn ) {
1107 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1109 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1110 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1113 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1116 if ( is_olddn == 1 && is_newdn == 0 ) {
1118 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1120 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1122 if ( is_olddn == 0 && is_newdn == 1 ) {
1123 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1124 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1126 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1128 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1133 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1136 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1138 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1139 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1143 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1144 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1148 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1151 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1152 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1154 overlay_entry_release_ov( op, e, 0, on );
1156 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1160 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1161 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1162 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1164 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1166 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1167 LDAP_SUCCESS || e == NULL ) {
1168 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1169 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1170 return SLAP_CB_CONTINUE;
1173 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1177 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1178 overlay_entry_release_ov( op, e, 0, on );
1179 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1180 return SLAP_CB_CONTINUE;
1183 /* If we modify a group's memberURL, we have to delete all of it's members,
1184 and add them anew, because we cannot tell from which memberURL a member was added. */
1185 for ( ; agd; agd = agd->agd_next ) {
1187 if ( value_find_ex( slap_schema.si_ad_objectClass,
1188 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1189 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1190 a->a_nvals, &agd->agd_oc->soc_cname,
1191 op->o_tmpmemctx ) == 0 )
1196 m = op->orm_modlist;
1198 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1199 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1201 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1204 for ( ; m ; m = m->sml_next ) {
1205 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1206 autogroup_def_t *group_agd = age->age_def;
1207 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1208 op->o_req_dn.bv_val, 0, 0);
1210 overlay_entry_release_ov( op, e, 0, on );
1212 autogroup_delete_member_from_group( op, NULL, NULL, age );
1213 autogroup_delete_group( agi, age );
1215 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1217 overlay_entry_release_ov( op, e, 0, on );
1218 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1219 return SLAP_CB_CONTINUE;
1223 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1227 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1230 overlay_entry_release_ov( op, e, 0, on );
1231 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1232 return SLAP_CB_CONTINUE;
1236 /* When modifing any of the attributes of an entry, we must
1237 check if the entry is in any of our groups, and if
1238 the modified entry maches any of the filters of that group.
1240 If the entry exists in a group, but the modified attributes do
1241 not match any of the group's filters, we delete the entry from that group.
1242 If the entry doesn't exist in a group, but matches a filter,
1243 we add it to that group.
1245 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1250 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1252 if ( age->age_filter && age->age_filter->agf_anlist ) {
1253 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1259 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1260 LDAP_SUCCESS || group == NULL ) {
1261 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1262 age->age_dn.bv_val, 0, 0);
1264 overlay_entry_release_ov( op, e, 0, on );
1265 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1266 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1267 return SLAP_CB_CONTINUE;
1270 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1273 if ( value_find_ex( age->age_def->agd_member_ad,
1274 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1275 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1276 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1283 overlay_entry_release_ov( op, group, 0, on );
1285 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1286 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1287 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1294 if ( is_olddn == 1 && is_newdn == 0 ) {
1296 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1298 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1300 if ( is_olddn == 0 && is_newdn == 1 ) {
1302 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1304 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1307 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1310 overlay_entry_release_ov( op, e, 0, on );
1312 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1316 return SLAP_CB_CONTINUE;
1320 ** Detect if filter contains a memberOf check for dn
1323 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1326 if ( f == NULL ) return 0;
1328 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1329 case LDAP_FILTER_AND:
1330 case LDAP_FILTER_OR:
1331 case LDAP_FILTER_NOT:
1332 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1333 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1336 case LDAP_FILTER_EQUALITY:
1337 result = ( f->f_ava->aa_desc == memberof_ad &&
1338 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1348 ** When modifing a group, we must deny any modifications to the member attribute,
1349 ** because the group would be inconsistent.
1352 autogroup_modify_entry( Operation *op, SlapReply *rs)
1354 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1355 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1356 autogroup_def_t *agd = agi->agi_def;
1357 autogroup_entry_t *age;
1361 if ( get_manageDSAit( op ) ) {
1362 return SLAP_CB_CONTINUE;
1365 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1366 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1368 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1369 LDAP_SUCCESS || e == NULL ) {
1370 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1371 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1372 return SLAP_CB_CONTINUE;
1375 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1376 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1377 autogroup_filter_t *agf;
1378 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1379 if ( agf->agf_anlist ) {
1381 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1382 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1383 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1384 int rc = test_filter( op, e, agf->agf_filter );
1385 if ( rc == LDAP_COMPARE_TRUE ) {
1386 age->age_mustrefresh = 1;
1393 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1394 age->age_mustrefresh = 1;
1399 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1402 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1403 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1404 return SLAP_CB_CONTINUE;
1408 for ( ; agd; agd = agd->agd_next ) {
1410 if ( value_find_ex( slap_schema.si_ad_objectClass,
1411 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1412 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1413 a->a_nvals, &agd->agd_oc->soc_cname,
1414 op->o_tmpmemctx ) == 0 )
1419 m = op->orm_modlist;
1421 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1422 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1425 for ( ; m ; m = m->sml_next ) {
1426 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1427 overlay_entry_release_ov( op, e, 0, on );
1428 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1429 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1430 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1431 return LDAP_CONSTRAINT_VIOLATION;
1438 overlay_entry_release_ov( op, e, 0, on );
1439 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1440 return SLAP_CB_CONTINUE;
1444 overlay_entry_release_ov( op, e, 0, on );
1445 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1446 return SLAP_CB_CONTINUE;
1450 ** Detect if the olddn is part of a group and so if the group should be refreshed
1453 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1455 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1456 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1457 autogroup_entry_t *age;
1460 if ( get_manageDSAit( op ) ) {
1461 return SLAP_CB_CONTINUE;
1464 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1465 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1467 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1468 LDAP_SUCCESS || e == NULL ) {
1469 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1470 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1471 return SLAP_CB_CONTINUE;
1474 /* Must check if a dn is modified */
1475 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1476 autogroup_filter_t *agf;
1477 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1478 if ( agf->agf_anlist ) {
1479 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1480 int rc = test_filter( op, e, agf->agf_filter );
1481 if ( rc == LDAP_COMPARE_TRUE ) {
1482 age->age_modrdn_olddnmodified = 1;
1489 overlay_entry_release_ov( op, e, 0, on );
1490 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1491 return SLAP_CB_CONTINUE;
1495 ** Builds a filter for searching for the
1496 ** group entries, according to the objectClass.
1499 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1503 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1505 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1506 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1507 + agd->agd_oc->soc_cname.bv_len;
1508 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1510 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1512 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1516 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1518 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1529 static ConfigDriver ag_cfgen;
1531 static ConfigTable agcfg[] = {
1532 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1533 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1534 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1535 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1536 "EQUALITY caseIgnoreMatch "
1537 "SYNTAX OMsDirectoryString "
1538 "X-ORDERED 'VALUES' )",
1541 { "autogroup-memberof-ad", "memberOf attribute",
1542 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1543 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1544 "DESC 'memberOf attribute' "
1545 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1548 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1551 static ConfigOCs agocs[] = {
1552 { "( OLcfgCtOc:2.1 "
1553 "NAME 'olcAutomaticGroups' "
1554 "DESC 'Automatic groups configuration' "
1555 "SUP olcOverlayConfig "
1558 "$ olcAGmemberOfAd "
1561 Cft_Overlay, agcfg, NULL, NULL },
1567 ag_cfgen( ConfigArgs *c )
1569 slap_overinst *on = (slap_overinst *)c->bi;
1570 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1571 autogroup_def_t *agd;
1572 autogroup_entry_t *age;
1576 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1579 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1580 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1581 agi->agi_def = NULL;
1582 agi->agi_entry = NULL;
1583 on->on_bi.bi_private = (void *)agi;
1587 age = agi->agi_entry;
1589 if ( c->op == SLAP_CONFIG_EMIT ) {
1593 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1595 char *ptr = c->cr_msg;
1597 assert(agd->agd_oc != NULL);
1598 assert(agd->agd_member_url_ad != NULL);
1599 assert(agd->agd_member_ad != NULL);
1601 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1602 SLAP_X_ORDERED_FMT "%s %s %s", i,
1603 agd->agd_oc->soc_cname.bv_val,
1604 agd->agd_member_url_ad->ad_cname.bv_val,
1605 agd->agd_member_ad->ad_cname.bv_val );
1607 bv.bv_val = c->cr_msg;
1608 bv.bv_len = ptr - bv.bv_val;
1609 value_add_one ( &c->rvalue_vals, &bv );
1614 case AG_MEMBER_OF_AD:
1615 if ( agi->agi_memberof_ad != NULL ){
1616 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1627 }else if ( c->op == LDAP_MOD_DELETE ) {
1629 autogroup_def_t *agd_next;
1630 autogroup_entry_t *age_next;
1631 autogroup_filter_t *agf = age->age_filter,
1634 for ( agd_next = agd; agd_next; agd = agd_next ) {
1635 agd_next = agd->agd_next;
1640 for ( age_next = age ; age_next ; age = age_next ) {
1641 age_next = age->age_next;
1643 ch_free( age->age_dn.bv_val );
1644 ch_free( age->age_ndn.bv_val );
1646 for( agf_next = agf ; agf_next ; agf = agf_next ){
1647 agf_next = agf->agf_next;
1649 filter_free( agf->agf_filter );
1650 ch_free( agf->agf_filterstr.bv_val );
1651 ch_free( agf->agf_dn.bv_val );
1652 ch_free( agf->agf_ndn.bv_val );
1653 anlist_free( agf->agf_anlist, 1, NULL );
1657 ldap_pvt_thread_mutex_init( &age->age_mutex );
1662 on->on_bi.bi_private = NULL;
1665 autogroup_def_t **agdp;
1666 autogroup_entry_t *age_next, *age_prev;
1667 autogroup_filter_t *agf,
1670 for ( i = 0, agdp = &agi->agi_def;
1673 if ( *agdp == NULL) {
1676 agdp = &(*agdp)->agd_next;
1680 *agdp = agd->agd_next;
1682 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1683 age_next = age->age_next;
1685 if( age->age_def == agd ) {
1686 agf = age->age_filter;
1688 ch_free( age->age_dn.bv_val );
1689 ch_free( age->age_ndn.bv_val );
1691 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1692 agf_next = agf->agf_next;
1693 filter_free( agf->agf_filter );
1694 ch_free( agf->agf_filterstr.bv_val );
1695 ch_free( agf->agf_dn.bv_val );
1696 ch_free( agf->agf_ndn.bv_val );
1697 anlist_free( agf->agf_anlist, 1, NULL );
1701 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1706 if( age_prev != NULL ) {
1707 age_prev->age_next = age_next;
1722 autogroup_def_t **agdp,
1724 ObjectClass *oc = NULL;
1725 AttributeDescription *member_url_ad = NULL,
1730 oc = oc_find( c->argv[ 1 ] );
1732 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1733 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1734 "unable to find ObjectClass \"%s\"",
1736 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1737 c->log, c->cr_msg, 0 );
1742 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1743 if( rc != LDAP_SUCCESS ) {
1744 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1745 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1746 "unable to find AttributeDescription \"%s\"",
1748 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1749 c->log, c->cr_msg, 0 );
1753 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1754 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1755 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1756 "AttributeDescription \"%s\" ",
1757 "must be of a subtype \"labeledURI\"",
1759 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1760 c->log, c->cr_msg, 0 );
1764 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1765 if( rc != LDAP_SUCCESS ) {
1766 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1767 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1768 "unable to find AttributeDescription \"%s\"",
1770 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1771 c->log, c->cr_msg, 0 );
1775 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1776 /* The same URL attribute / member attribute pair
1777 * cannot be repeated */
1779 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1780 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1781 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1782 "URL attributeDescription \"%s\" already mapped",
1783 member_ad->ad_cname.bv_val );
1784 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1785 c->log, c->cr_msg, 0 );
1786 /* return 1; //warning*/
1790 if ( c->valx > 0 ) {
1793 for ( i = 0, agdp = &agi->agi_def ;
1796 if ( *agdp == NULL ) {
1797 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1798 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1799 "invalid index {%d}",
1801 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1802 c->log, c->cr_msg, 0 );
1806 agdp = &(*agdp)->agd_next;
1811 for ( agdp = &agi->agi_def; *agdp;
1812 agdp = &(*agdp)->agd_next )
1816 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1818 (*agdp)->agd_oc = oc;
1819 (*agdp)->agd_member_url_ad = member_url_ad;
1820 (*agdp)->agd_member_ad = member_ad;
1821 (*agdp)->agd_next = agd_next;
1825 case AG_MEMBER_OF_AD: {
1826 AttributeDescription *memberof_ad = NULL;
1829 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1830 if( rc != LDAP_SUCCESS ) {
1831 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1832 "\"autogroup-memberof-ad <memberof-ad>\": "
1833 "unable to find AttributeDescription \"%s\"",
1835 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1836 c->log, c->cr_msg, 0 );
1840 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1841 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1843 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1844 "memberof attribute=\"%s\" must either "
1845 "have DN (%s) or nameUID (%s) syntax",
1846 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1847 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1848 c->log, c->cr_msg, 0 );
1852 agi->agi_memberof_ad = memberof_ad;
1864 extern int slapMode;
1867 ** Do a search for all the groups in the
1868 ** database, and add them to out internal list.
1875 slap_overinst *on = (slap_overinst *) be->bd_info;
1876 autogroup_info_t *agi = on->on_bi.bi_private;
1877 autogroup_def_t *agd;
1880 slap_callback cb = { 0 };
1882 void *thrctx = ldap_pvt_thread_pool_context();
1883 Connection conn = { 0 };
1884 OperationBuffer opbuf;
1886 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1888 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1892 connection_fake_init( &conn, &opbuf, thrctx );
1895 op->ors_attrsonly = 0;
1896 op->o_tag = LDAP_REQ_SEARCH;
1897 op->o_dn = be->be_rootdn;
1898 op->o_ndn = be->be_rootndn;
1900 op->o_req_dn = be->be_suffix[0];
1901 op->o_req_ndn = be->be_nsuffix[0];
1903 op->ors_scope = LDAP_SCOPE_SUBTREE;
1904 op->ors_deref = LDAP_DEREF_NEVER;
1905 op->ors_limit = NULL;
1906 op->ors_tlimit = SLAP_NO_LIMIT;
1907 op->ors_slimit = SLAP_NO_LIMIT;
1908 op->ors_attrs = slap_anlist_no_attrs;
1911 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1914 cb.sc_private = &ags;
1915 cb.sc_response = autogroup_group_add_cb;
1916 cb.sc_cleanup = NULL;
1919 op->o_callback = &cb;
1921 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1922 SlapReply rs = { REP_RESULT };
1924 autogroup_build_def_filter(agd, op);
1928 op->o_bd->be_search( op, &rs );
1930 filter_free_x( op, op->ors_filter, 1 );
1931 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1934 if( ! agi->agi_memberof_ad ){
1936 const char *text = NULL;
1938 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1939 if ( rc != LDAP_SUCCESS ) {
1940 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1941 "unable to find attribute=\"%s\": %s (%d)\n",
1942 SLAPD_MEMBEROF_ATTR, text, rc );
1955 slap_overinst *on = (slap_overinst *) be->bd_info;
1957 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1959 if ( on->on_bi.bi_private ) {
1960 autogroup_info_t *agi = on->on_bi.bi_private;
1961 autogroup_entry_t *age = agi->agi_entry,
1963 autogroup_filter_t *agf, *agf_next;
1965 for ( age_next = age; age_next; age = age_next ) {
1966 age_next = age->age_next;
1968 ch_free( age->age_dn.bv_val );
1969 ch_free( age->age_ndn.bv_val );
1971 agf = age->age_filter;
1973 for ( agf_next = agf; agf_next; agf = agf_next ) {
1974 agf_next = agf->agf_next;
1976 filter_free( agf->agf_filter );
1977 ch_free( agf->agf_filterstr.bv_val );
1978 ch_free( agf->agf_dn.bv_val );
1979 ch_free( agf->agf_ndn.bv_val );
1980 anlist_free( agf->agf_anlist, 1, NULL );
1984 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1993 autogroup_db_destroy(
1997 slap_overinst *on = (slap_overinst *) be->bd_info;
1999 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2001 if ( on->on_bi.bi_private ) {
2002 autogroup_info_t *agi = on->on_bi.bi_private;
2003 autogroup_def_t *agd = agi->agi_def,
2006 for ( agd_next = agd; agd_next; agd = agd_next ) {
2007 agd_next = agd->agd_next;
2012 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2019 static slap_overinst autogroup = { { NULL } };
2023 autogroup_initialize(void)
2026 autogroup.on_bi.bi_type = "autogroup";
2028 autogroup.on_bi.bi_db_open = autogroup_db_open;
2029 autogroup.on_bi.bi_db_close = autogroup_db_close;
2030 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2032 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2033 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2034 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2035 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2037 autogroup.on_response = autogroup_response;
2039 autogroup.on_bi.bi_cf_ocs = agocs;
2041 rc = config_register_schema( agcfg, agocs );
2046 return overlay_register( &autogroup );
2050 init_module( int argc, char *argv[] )
2052 return autogroup_initialize();