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 assert( op->o_tag == LDAP_REQ_SEARCH );
326 if ( rs->sr_type == REP_SEARCH ) {
327 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
328 autogroup_entry_t *age = agg->agg_group;
329 autogroup_filter_t *agf = agg->agg_filter;
331 const char *text = NULL;
333 struct berval *vals, *nvals;
336 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
337 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
339 if ( agf->agf_anlist ) {
340 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
343 nvals = attr->a_nvals;
344 numvals = attr->a_numvals;
350 struct berval lvals[ 2 ], lnvals[ 2 ];
351 lvals[ 0 ] = rs->sr_entry->e_name;
352 BER_BVZERO( &lvals[ 1 ] );
353 lnvals[ 0 ] = rs->sr_entry->e_nname;
354 BER_BVZERO( &lnvals[ 1 ] );
360 mod.sm_op = LDAP_MOD_ADD;
361 mod.sm_desc = age->age_def->agd_member_ad;
362 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
363 mod.sm_values = vals;
364 mod.sm_nvalues = nvals;
365 mod.sm_numvals = numvals;
367 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
374 ** Callback used to add entries to a group, which is already in the database.
375 ** (used in on_response)
376 ** The group is passed in autogroup_ga_t->agg_group
380 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
382 assert( op->o_tag == LDAP_REQ_SEARCH );
384 if ( rs->sr_type == REP_SEARCH ) {
385 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
386 autogroup_entry_t *age = agg->agg_group;
387 autogroup_filter_t *agf = agg->agg_filter;
388 Modifications *modlist;
389 struct berval *vals, *nvals;
392 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
393 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
395 if ( agf->agf_anlist ) {
396 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
399 nvals = attr->a_nvals;
400 numvals = attr->a_numvals;
406 struct berval lvals[ 2 ], lnvals[ 2 ];
407 lvals[ 0 ] = rs->sr_entry->e_name;
408 BER_BVZERO( &lvals[ 1 ] );
409 lnvals[ 0 ] = rs->sr_entry->e_nname;
410 BER_BVZERO( &lnvals[ 1 ] );
417 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
419 modlist->sml_op = LDAP_MOD_ADD;
420 modlist->sml_desc = age->age_def->agd_member_ad;
421 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
423 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
424 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
425 modlist->sml_numvals = numvals;
427 modlist->sml_flags = SLAP_MOD_INTERNAL;
428 modlist->sml_next = NULL;
430 if ( agg->agg_mod == NULL ) {
431 agg->agg_mod = modlist;
432 agg->agg_mod_last = modlist;
434 agg->agg_mod_last->sml_next = modlist;
435 agg->agg_mod_last = modlist;
446 ** Adds all entries matching the passed filter to the specified group.
447 ** If modify == 1, then we modify the group's entry in the database using be_modify.
448 ** If modify == 0, then, we must supply a rw entry for the group,
449 ** because we only modify the entry, without calling be_modify.
450 ** e - the group entry, to which the members will be added
455 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
457 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
459 SlapReply rs = { REP_SEARCH };
460 slap_callback cb = { 0 };
461 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
464 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
465 age->age_dn.bv_val, 0, 0);
468 o.o_tag = LDAP_REQ_SEARCH;
470 o.o_req_dn = agf->agf_dn;
471 o.o_req_ndn = agf->agf_ndn;
473 o.ors_filterstr = agf->agf_filterstr;
474 o.ors_filter = agf->agf_filter;
476 o.ors_scope = agf->agf_scope;
477 o.ors_deref = LDAP_DEREF_NEVER;
479 o.ors_tlimit = SLAP_NO_LIMIT;
480 o.ors_slimit = SLAP_NO_LIMIT;
481 o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
484 agg.agg_filter = agf;
486 agg.agg_mod_last = NULL;
488 cb.sc_private = &agg;
491 cb.sc_response = autogroup_member_search_modify_cb;
493 cb.sc_response = autogroup_member_search_cb;
496 cb.sc_cleanup = NULL;
501 o.o_bd->bd_info = (BackendInfo *)on->on_info;
502 op->o_bd->be_search( &o, &rs );
503 o.o_bd->bd_info = (BackendInfo *)on;
505 if ( modify == 1 && agg.agg_mod ) {
506 rs_reinit( &rs, REP_RESULT );
509 o.o_callback = &null_cb;
510 o.o_tag = LDAP_REQ_MODIFY;
511 o.orm_modlist = agg.agg_mod;
512 o.o_req_dn = age->age_dn;
513 o.o_req_ndn = age->age_ndn;
514 o.o_relax = SLAP_CONTROL_CRITICAL;
515 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
516 o.o_permissive_modify = 1;
518 o.o_bd->bd_info = (BackendInfo *)on->on_info;
519 (void)op->o_bd->be_modify( &o, &rs );
520 o.o_bd->bd_info = (BackendInfo *)on;
522 slap_mods_free(agg.agg_mod, 1);
529 ** Adds a group to the internal list from the passed entry.
530 ** scan specifies whether to add all maching members to the group.
531 ** modify specifies whether to modify the given group entry (when modify == 0),
532 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
533 ** agi - pointer to the groups and the attribute definitions
534 ** agd - the attribute definition of the added group
535 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
536 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
539 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
541 autogroup_entry_t **agep = &agi->agi_entry;
542 autogroup_filter_t *agf, *agf_prev = NULL;
543 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
544 LDAPURLDesc *lud = NULL;
547 int rc = 0, match = 1, null_entry = 0;
550 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
551 LDAP_SUCCESS || e == NULL ) {
552 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
559 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
560 e->e_name.bv_val, 0, 0);
562 if ( agi->agi_entry != NULL ) {
563 for ( ; *agep ; agep = &(*agep)->age_next ) {
564 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
566 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
574 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
575 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
576 (*agep)->age_def = agd;
577 (*agep)->age_filter = NULL;
578 (*agep)->age_mustrefresh = 0;
579 (*agep)->age_modrdn_olddnmodified = 0;
581 ber_dupbv( &(*agep)->age_dn, &e->e_name );
582 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
584 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
586 if ( null_entry == 1 ) {
588 overlay_entry_release_ov( op, e, 0, on );
592 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
594 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
596 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
598 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
599 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
605 agf->agf_scope = lud->lud_scope;
607 if ( lud->lud_dn == NULL ) {
608 BER_BVSTR( &dn, "" );
610 ber_str2bv( lud->lud_dn, 0, 0, &dn );
613 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
614 if ( rc != LDAP_SUCCESS ) {
615 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
620 if ( lud->lud_filter != NULL ) {
621 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
622 agf->agf_filter = str2filter( lud->lud_filter );
625 if ( lud->lud_attrs != NULL ) {
628 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
633 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
636 ldap_free_urldesc( lud );
641 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
643 if ( agf->agf_anlist == NULL ) {
644 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
645 lud->lud_attrs[0], 0, 0 );
647 ldap_free_urldesc( lud );
653 agf->agf_next = NULL;
656 if( (*agep)->age_filter == NULL ) {
657 (*agep)->age_filter = agf;
660 if( agf_prev != NULL ) {
661 agf_prev->agf_next = agf;
667 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
670 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
671 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
673 ldap_free_urldesc( lud );
680 ldap_free_urldesc( lud );
685 if ( null_entry == 1 ) {
692 ** Used when opening the database to add all existing
693 ** groups from the database to our internal list.
696 autogroup_group_add_cb( Operation *op, SlapReply *rs )
698 assert( op->o_tag == LDAP_REQ_SEARCH );
700 if ( rs->sr_type == REP_SEARCH ) {
701 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
703 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
704 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
706 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
714 ** When adding a group, we first strip any existing members,
715 ** and add all which match the filters ourselfs.
718 autogroup_add_entry( Operation *op, SlapReply *rs)
720 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
721 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
722 autogroup_def_t *agd = agi->agi_def;
723 autogroup_entry_t *age;
724 autogroup_filter_t *agf;
727 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
728 op->ora_e->e_name.bv_val, 0, 0);
730 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
732 /* Check if it's a group. */
733 for ( ; agd ; agd = agd->agd_next ) {
734 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
736 const char *text = NULL;
739 mod.sm_op = LDAP_MOD_DELETE;
740 mod.sm_desc = agd->agd_member_ad;
741 mod.sm_type = agd->agd_member_ad->ad_cname;
742 mod.sm_values = NULL;
743 mod.sm_nvalues = NULL;
745 /* We don't want any member attributes added by the user. */
746 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
748 autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
749 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
750 return SLAP_CB_CONTINUE;
755 for ( age = agi->agi_entry; age ; age = age->age_next ) {
756 ldap_pvt_thread_mutex_lock( &age->age_mutex );
758 /* Check if any of the filters are the suffix to the entry DN.
759 If yes, we can test that filter against the entry. */
761 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
762 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
763 rc = test_filter( op, op->ora_e, agf->agf_filter );
764 if ( rc == LDAP_COMPARE_TRUE ) {
765 if ( agf->agf_anlist ) {
766 autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
768 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
774 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
777 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
779 return SLAP_CB_CONTINUE;
783 ** agi - internal group and attribute definitions list
784 ** e - the group to remove from the internal list
787 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
789 autogroup_entry_t *age = agi->agi_entry,
794 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
795 age->age_dn.bv_val, 0, 0);
797 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
798 age_next = age->age_next;
801 autogroup_filter_t *agf = age->age_filter,
804 if ( age_prev != NULL ) {
805 age_prev->age_next = age_next;
807 agi->agi_entry = NULL;
810 ch_free( age->age_dn.bv_val );
811 ch_free( age->age_ndn.bv_val );
813 for( agf_next = agf ; agf_next ; agf = agf_next ){
814 agf_next = agf->agf_next;
816 filter_free( agf->agf_filter );
817 ch_free( agf->agf_filterstr.bv_val );
818 ch_free( agf->agf_dn.bv_val );
819 ch_free( agf->agf_ndn.bv_val );
820 anlist_free( agf->agf_anlist, 1, NULL );
824 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
825 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
834 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
841 autogroup_delete_entry( Operation *op, SlapReply *rs)
843 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
844 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
845 autogroup_entry_t *age, *age_prev, *age_next;
846 autogroup_filter_t *agf;
848 int matched_group = 0, rc = 0;
850 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
852 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
854 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
855 LDAP_SUCCESS || e == NULL ) {
856 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
857 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
858 return SLAP_CB_CONTINUE;
861 /* Check if the entry to be deleted is one of our groups. */
862 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
864 ldap_pvt_thread_mutex_lock( &age->age_mutex );
865 age_next = age->age_next;
867 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
872 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
875 autogroup_delete_group( agi, age );
880 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
883 if ( matched_group == 1 ) {
884 overlay_entry_release_ov( op, e, 0, on );
885 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
886 return SLAP_CB_CONTINUE;
889 /* Check if the entry matches any of the groups.
890 If yes, we can delete the entry from that group. */
892 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
893 ldap_pvt_thread_mutex_lock( &age->age_mutex );
895 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
896 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
897 rc = test_filter( op, e, agf->agf_filter );
898 if ( rc == LDAP_COMPARE_TRUE ) {
899 /* If the attribute is retrieved from the entry, we don't know what to delete
900 ** So the group must be entirely refreshed
901 ** But the refresh can't be done now because the entry is not deleted
902 ** So the group is marked as mustrefresh
904 if ( agf->agf_anlist ) {
905 age->age_mustrefresh = 1;
907 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
913 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
916 overlay_entry_release_ov( op, e, 0, on );
917 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
919 return SLAP_CB_CONTINUE;
923 autogroup_response( Operation *op, SlapReply *rs )
925 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
926 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
927 autogroup_def_t *agd = agi->agi_def;
928 autogroup_entry_t *age;
929 autogroup_filter_t *agf;
930 BerValue new_dn, new_ndn, pdn;
933 int is_olddn, is_newdn, is_value_refresh, dn_equal;
935 /* Handle all cases where a refresh of the group is needed */
936 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
937 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
939 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
941 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
942 /* Request detected that the group must be refreshed */
944 ldap_pvt_thread_mutex_lock( &age->age_mutex );
946 if ( age->age_mustrefresh ) {
947 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
949 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
950 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
954 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
957 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
959 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
960 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
962 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
964 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
966 if ( op->oq_modrdn.rs_newSup ) {
967 pdn = *op->oq_modrdn.rs_newSup;
969 dnParent( &op->o_req_dn, &pdn );
971 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
973 if ( op->oq_modrdn.rs_nnewSup ) {
974 pdn = *op->oq_modrdn.rs_nnewSup;
976 dnParent( &op->o_req_ndn, &pdn );
978 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
980 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
982 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
984 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
985 LDAP_SUCCESS || e == NULL ) {
986 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
987 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
988 return SLAP_CB_CONTINUE;
991 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
995 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
996 overlay_entry_release_ov( op, e, 0, on );
997 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
998 return SLAP_CB_CONTINUE;
1002 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1003 for ( ; agd; agd = agd->agd_next ) {
1005 if ( value_find_ex( slap_schema.si_ad_objectClass,
1006 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1007 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1008 a->a_nvals, &agd->agd_oc->soc_cname,
1009 op->o_tmpmemctx ) == 0 )
1011 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1014 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1016 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1017 ber_dupbv( &age->age_dn, &new_dn );
1018 ber_dupbv( &age->age_ndn, &new_ndn );
1020 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1021 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1022 overlay_entry_release_ov( op, e, 0, on );
1023 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1024 return SLAP_CB_CONTINUE;
1032 1. check if the orginal entry's DN is in the group.
1033 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1035 If 1 and 2 are both false, we do nothing.
1036 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1037 If 1 is false, and 2 is true, we check the entry against the group's filters,
1038 and add it's DN to the group.
1039 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1041 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1044 is_value_refresh = 0;
1047 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1049 if ( age->age_filter && age->age_filter->agf_anlist ) {
1050 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1056 if ( age->age_modrdn_olddnmodified ) {
1057 /* Resquest already marked this group to be updated */
1059 is_value_refresh = 1;
1060 age->age_modrdn_olddnmodified = 0;
1063 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1064 LDAP_SUCCESS || group == NULL ) {
1065 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1067 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1068 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1070 overlay_entry_release_ov( op, e, 0, on );
1071 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1072 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1073 return SLAP_CB_CONTINUE;
1076 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1079 if ( value_find_ex( age->age_def->agd_member_ad,
1080 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1081 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1082 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1089 overlay_entry_release_ov( op, group, 0, on );
1093 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1094 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1095 /* TODO: should retest filter as it could imply conditions on the dn */
1102 if ( is_value_refresh ) {
1103 if ( is_olddn != is_newdn ) {
1105 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1107 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1108 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1111 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1114 if ( is_olddn == 1 && is_newdn == 0 ) {
1116 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1118 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1120 if ( is_olddn == 0 && is_newdn == 1 ) {
1121 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1122 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1124 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1126 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1131 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1134 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1136 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1137 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1141 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1142 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1146 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1149 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1150 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1152 overlay_entry_release_ov( op, e, 0, on );
1154 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1158 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1159 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1160 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1162 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1164 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1165 LDAP_SUCCESS || e == NULL ) {
1166 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1167 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1168 return SLAP_CB_CONTINUE;
1171 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1175 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1176 overlay_entry_release_ov( op, e, 0, on );
1177 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1178 return SLAP_CB_CONTINUE;
1181 /* If we modify a group's memberURL, we have to delete all of it's members,
1182 and add them anew, because we cannot tell from which memberURL a member was added. */
1183 for ( ; agd; agd = agd->agd_next ) {
1185 if ( value_find_ex( slap_schema.si_ad_objectClass,
1186 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1187 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1188 a->a_nvals, &agd->agd_oc->soc_cname,
1189 op->o_tmpmemctx ) == 0 )
1194 m = op->orm_modlist;
1196 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1197 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1199 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1202 for ( ; m ; m = m->sml_next ) {
1203 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1204 autogroup_def_t *group_agd = age->age_def;
1205 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1206 op->o_req_dn.bv_val, 0, 0);
1208 overlay_entry_release_ov( op, e, 0, on );
1210 autogroup_delete_member_from_group( op, NULL, NULL, age );
1211 autogroup_delete_group( agi, age );
1213 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1215 overlay_entry_release_ov( op, e, 0, on );
1216 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1217 return SLAP_CB_CONTINUE;
1221 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1225 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1228 overlay_entry_release_ov( op, e, 0, on );
1229 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1230 return SLAP_CB_CONTINUE;
1234 /* When modifing any of the attributes of an entry, we must
1235 check if the entry is in any of our groups, and if
1236 the modified entry maches any of the filters of that group.
1238 If the entry exists in a group, but the modified attributes do
1239 not match any of the group's filters, we delete the entry from that group.
1240 If the entry doesn't exist in a group, but matches a filter,
1241 we add it to that group.
1243 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1248 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1250 if ( age->age_filter && age->age_filter->agf_anlist ) {
1251 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1257 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1258 LDAP_SUCCESS || group == NULL ) {
1259 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1260 age->age_dn.bv_val, 0, 0);
1262 overlay_entry_release_ov( op, e, 0, on );
1263 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1264 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1265 return SLAP_CB_CONTINUE;
1268 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1271 if ( value_find_ex( age->age_def->agd_member_ad,
1272 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1273 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1274 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1281 overlay_entry_release_ov( op, group, 0, on );
1283 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1284 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1285 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1292 if ( is_olddn == 1 && is_newdn == 0 ) {
1294 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1296 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1298 if ( is_olddn == 0 && is_newdn == 1 ) {
1300 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1302 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1305 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1308 overlay_entry_release_ov( op, e, 0, on );
1310 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1314 return SLAP_CB_CONTINUE;
1318 ** Detect if filter contains a memberOf check for dn
1321 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1324 if ( f == NULL ) return 0;
1326 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1327 case LDAP_FILTER_AND:
1328 case LDAP_FILTER_OR:
1329 case LDAP_FILTER_NOT:
1330 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1331 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1334 case LDAP_FILTER_EQUALITY:
1335 result = ( f->f_ava->aa_desc == memberof_ad &&
1336 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1346 ** When modifing a group, we must deny any modifications to the member attribute,
1347 ** because the group would be inconsistent.
1350 autogroup_modify_entry( Operation *op, SlapReply *rs)
1352 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1353 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1354 autogroup_def_t *agd = agi->agi_def;
1355 autogroup_entry_t *age;
1359 if ( get_manageDSAit( op ) ) {
1360 return SLAP_CB_CONTINUE;
1363 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1364 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1366 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1367 LDAP_SUCCESS || e == NULL ) {
1368 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1369 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1370 return SLAP_CB_CONTINUE;
1373 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1374 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1375 autogroup_filter_t *agf;
1376 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1377 if ( agf->agf_anlist ) {
1379 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1380 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1381 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1382 int rc = test_filter( op, e, agf->agf_filter );
1383 if ( rc == LDAP_COMPARE_TRUE ) {
1384 age->age_mustrefresh = 1;
1391 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1392 age->age_mustrefresh = 1;
1397 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1400 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1401 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1402 return SLAP_CB_CONTINUE;
1406 for ( ; agd; agd = agd->agd_next ) {
1408 if ( value_find_ex( slap_schema.si_ad_objectClass,
1409 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1410 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1411 a->a_nvals, &agd->agd_oc->soc_cname,
1412 op->o_tmpmemctx ) == 0 )
1417 m = op->orm_modlist;
1419 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1420 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1423 for ( ; m ; m = m->sml_next ) {
1424 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1425 overlay_entry_release_ov( op, e, 0, on );
1426 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1427 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1428 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1429 return LDAP_CONSTRAINT_VIOLATION;
1436 overlay_entry_release_ov( op, e, 0, on );
1437 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1438 return SLAP_CB_CONTINUE;
1442 overlay_entry_release_ov( op, e, 0, on );
1443 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1444 return SLAP_CB_CONTINUE;
1448 ** Detect if the olddn is part of a group and so if the group should be refreshed
1451 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1453 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1454 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1455 autogroup_entry_t *age;
1458 if ( get_manageDSAit( op ) ) {
1459 return SLAP_CB_CONTINUE;
1462 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1463 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1465 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1466 LDAP_SUCCESS || e == NULL ) {
1467 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1468 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1469 return SLAP_CB_CONTINUE;
1472 /* Must check if a dn is modified */
1473 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1474 autogroup_filter_t *agf;
1475 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1476 if ( agf->agf_anlist ) {
1477 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1478 int rc = test_filter( op, e, agf->agf_filter );
1479 if ( rc == LDAP_COMPARE_TRUE ) {
1480 age->age_modrdn_olddnmodified = 1;
1487 overlay_entry_release_ov( op, e, 0, on );
1488 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1489 return SLAP_CB_CONTINUE;
1493 ** Builds a filter for searching for the
1494 ** group entries, according to the objectClass.
1497 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1501 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1503 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1504 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1505 + agd->agd_oc->soc_cname.bv_len;
1506 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1508 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1510 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1514 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1516 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1527 static ConfigDriver ag_cfgen;
1529 static ConfigTable agcfg[] = {
1530 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1531 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1532 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1533 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1534 "EQUALITY caseIgnoreMatch "
1535 "SYNTAX OMsDirectoryString "
1536 "X-ORDERED 'VALUES' )",
1539 { "autogroup-memberof-ad", "memberOf attribute",
1540 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1541 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1542 "DESC 'memberOf attribute' "
1543 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1546 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1549 static ConfigOCs agocs[] = {
1550 { "( OLcfgCtOc:2.1 "
1551 "NAME 'olcAutomaticGroups' "
1552 "DESC 'Automatic groups configuration' "
1553 "SUP olcOverlayConfig "
1556 "$ olcAGmemberOfAd "
1559 Cft_Overlay, agcfg, NULL, NULL },
1565 ag_cfgen( ConfigArgs *c )
1567 slap_overinst *on = (slap_overinst *)c->bi;
1568 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1569 autogroup_def_t *agd;
1570 autogroup_entry_t *age;
1574 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1577 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1578 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1579 agi->agi_def = NULL;
1580 agi->agi_entry = NULL;
1581 on->on_bi.bi_private = (void *)agi;
1585 age = agi->agi_entry;
1587 if ( c->op == SLAP_CONFIG_EMIT ) {
1591 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1593 char *ptr = c->cr_msg;
1595 assert(agd->agd_oc != NULL);
1596 assert(agd->agd_member_url_ad != NULL);
1597 assert(agd->agd_member_ad != NULL);
1599 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1600 SLAP_X_ORDERED_FMT "%s %s %s", i,
1601 agd->agd_oc->soc_cname.bv_val,
1602 agd->agd_member_url_ad->ad_cname.bv_val,
1603 agd->agd_member_ad->ad_cname.bv_val );
1605 bv.bv_val = c->cr_msg;
1606 bv.bv_len = ptr - bv.bv_val;
1607 value_add_one ( &c->rvalue_vals, &bv );
1612 case AG_MEMBER_OF_AD:
1613 if ( agi->agi_memberof_ad != NULL ){
1614 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1625 }else if ( c->op == LDAP_MOD_DELETE ) {
1627 autogroup_def_t *agd_next;
1628 autogroup_entry_t *age_next;
1629 autogroup_filter_t *agf = age->age_filter,
1632 for ( agd_next = agd; agd_next; agd = agd_next ) {
1633 agd_next = agd->agd_next;
1638 for ( age_next = age ; age_next ; age = age_next ) {
1639 age_next = age->age_next;
1641 ch_free( age->age_dn.bv_val );
1642 ch_free( age->age_ndn.bv_val );
1644 for( agf_next = agf ; agf_next ; agf = agf_next ){
1645 agf_next = agf->agf_next;
1647 filter_free( agf->agf_filter );
1648 ch_free( agf->agf_filterstr.bv_val );
1649 ch_free( agf->agf_dn.bv_val );
1650 ch_free( agf->agf_ndn.bv_val );
1651 anlist_free( agf->agf_anlist, 1, NULL );
1655 ldap_pvt_thread_mutex_init( &age->age_mutex );
1660 on->on_bi.bi_private = NULL;
1663 autogroup_def_t **agdp;
1664 autogroup_entry_t *age_next, *age_prev;
1665 autogroup_filter_t *agf,
1668 for ( i = 0, agdp = &agi->agi_def;
1671 if ( *agdp == NULL) {
1674 agdp = &(*agdp)->agd_next;
1678 *agdp = agd->agd_next;
1680 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1681 age_next = age->age_next;
1683 if( age->age_def == agd ) {
1684 agf = age->age_filter;
1686 ch_free( age->age_dn.bv_val );
1687 ch_free( age->age_ndn.bv_val );
1689 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1690 agf_next = agf->agf_next;
1691 filter_free( agf->agf_filter );
1692 ch_free( agf->agf_filterstr.bv_val );
1693 ch_free( agf->agf_dn.bv_val );
1694 ch_free( agf->agf_ndn.bv_val );
1695 anlist_free( agf->agf_anlist, 1, NULL );
1699 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1704 if( age_prev != NULL ) {
1705 age_prev->age_next = age_next;
1720 autogroup_def_t **agdp,
1722 ObjectClass *oc = NULL;
1723 AttributeDescription *member_url_ad = NULL,
1728 oc = oc_find( c->argv[ 1 ] );
1730 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1731 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1732 "unable to find ObjectClass \"%s\"",
1734 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1735 c->log, c->cr_msg, 0 );
1740 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1741 if( rc != LDAP_SUCCESS ) {
1742 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1743 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1744 "unable to find AttributeDescription \"%s\"",
1746 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1747 c->log, c->cr_msg, 0 );
1751 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1752 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1753 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1754 "AttributeDescription \"%s\" ",
1755 "must be of a subtype \"labeledURI\"",
1757 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1758 c->log, c->cr_msg, 0 );
1762 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1763 if( rc != LDAP_SUCCESS ) {
1764 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1765 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1766 "unable to find AttributeDescription \"%s\"",
1768 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1769 c->log, c->cr_msg, 0 );
1773 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1774 /* The same URL attribute / member attribute pair
1775 * cannot be repeated */
1777 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1778 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1779 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1780 "URL attributeDescription \"%s\" already mapped",
1781 member_ad->ad_cname.bv_val );
1782 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1783 c->log, c->cr_msg, 0 );
1784 /* return 1; //warning*/
1788 if ( c->valx > 0 ) {
1791 for ( i = 0, agdp = &agi->agi_def ;
1794 if ( *agdp == NULL ) {
1795 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1796 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1797 "invalid index {%d}",
1799 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1800 c->log, c->cr_msg, 0 );
1804 agdp = &(*agdp)->agd_next;
1809 for ( agdp = &agi->agi_def; *agdp;
1810 agdp = &(*agdp)->agd_next )
1814 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1816 (*agdp)->agd_oc = oc;
1817 (*agdp)->agd_member_url_ad = member_url_ad;
1818 (*agdp)->agd_member_ad = member_ad;
1819 (*agdp)->agd_next = agd_next;
1823 case AG_MEMBER_OF_AD: {
1824 AttributeDescription *memberof_ad = NULL;
1827 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1828 if( rc != LDAP_SUCCESS ) {
1829 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1830 "\"autogroup-memberof-ad <memberof-ad>\": "
1831 "unable to find AttributeDescription \"%s\"",
1833 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1834 c->log, c->cr_msg, 0 );
1838 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1839 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1841 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1842 "memberof attribute=\"%s\" must either "
1843 "have DN (%s) or nameUID (%s) syntax",
1844 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1845 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1846 c->log, c->cr_msg, 0 );
1850 agi->agi_memberof_ad = memberof_ad;
1862 extern int slapMode;
1865 ** Do a search for all the groups in the
1866 ** database, and add them to out internal list.
1873 slap_overinst *on = (slap_overinst *) be->bd_info;
1874 autogroup_info_t *agi = on->on_bi.bi_private;
1875 autogroup_def_t *agd;
1878 slap_callback cb = { 0 };
1880 void *thrctx = ldap_pvt_thread_pool_context();
1881 Connection conn = { 0 };
1882 OperationBuffer opbuf;
1884 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1886 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1890 connection_fake_init( &conn, &opbuf, thrctx );
1893 op->ors_attrsonly = 0;
1894 op->o_tag = LDAP_REQ_SEARCH;
1895 op->o_dn = be->be_rootdn;
1896 op->o_ndn = be->be_rootndn;
1898 op->o_req_dn = be->be_suffix[0];
1899 op->o_req_ndn = be->be_nsuffix[0];
1901 op->ors_scope = LDAP_SCOPE_SUBTREE;
1902 op->ors_deref = LDAP_DEREF_NEVER;
1903 op->ors_limit = NULL;
1904 op->ors_tlimit = SLAP_NO_LIMIT;
1905 op->ors_slimit = SLAP_NO_LIMIT;
1906 op->ors_attrs = slap_anlist_no_attrs;
1909 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1912 cb.sc_private = &ags;
1913 cb.sc_response = autogroup_group_add_cb;
1914 cb.sc_cleanup = NULL;
1917 op->o_callback = &cb;
1919 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1920 SlapReply rs = { REP_RESULT };
1922 autogroup_build_def_filter(agd, op);
1926 op->o_bd->be_search( op, &rs );
1928 filter_free_x( op, op->ors_filter, 1 );
1929 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1932 if( ! agi->agi_memberof_ad ){
1934 const char *text = NULL;
1936 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1937 if ( rc != LDAP_SUCCESS ) {
1938 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1939 "unable to find attribute=\"%s\": %s (%d)\n",
1940 SLAPD_MEMBEROF_ATTR, text, rc );
1953 slap_overinst *on = (slap_overinst *) be->bd_info;
1955 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1957 if ( on->on_bi.bi_private ) {
1958 autogroup_info_t *agi = on->on_bi.bi_private;
1959 autogroup_entry_t *age = agi->agi_entry,
1961 autogroup_filter_t *agf, *agf_next;
1963 for ( age_next = age; age_next; age = age_next ) {
1964 age_next = age->age_next;
1966 ch_free( age->age_dn.bv_val );
1967 ch_free( age->age_ndn.bv_val );
1969 agf = age->age_filter;
1971 for ( agf_next = agf; agf_next; agf = agf_next ) {
1972 agf_next = agf->agf_next;
1974 filter_free( agf->agf_filter );
1975 ch_free( agf->agf_filterstr.bv_val );
1976 ch_free( agf->agf_dn.bv_val );
1977 ch_free( agf->agf_ndn.bv_val );
1978 anlist_free( agf->agf_anlist, 1, NULL );
1982 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1991 autogroup_db_destroy(
1995 slap_overinst *on = (slap_overinst *) be->bd_info;
1997 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
1999 if ( on->on_bi.bi_private ) {
2000 autogroup_info_t *agi = on->on_bi.bi_private;
2001 autogroup_def_t *agd = agi->agi_def,
2004 for ( agd_next = agd; agd_next; agd = agd_next ) {
2005 agd_next = agd->agd_next;
2010 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2017 static slap_overinst autogroup = { { NULL } };
2021 autogroup_initialize(void)
2024 autogroup.on_bi.bi_type = "autogroup";
2026 autogroup.on_bi.bi_db_open = autogroup_db_open;
2027 autogroup.on_bi.bi_db_close = autogroup_db_close;
2028 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2030 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2031 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2032 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2033 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2035 autogroup.on_response = autogroup_response;
2037 autogroup.on_bi.bi_cf_ocs = agocs;
2039 rc = config_register_schema( agcfg, agocs );
2044 return overlay_register( &autogroup );
2048 init_module( int argc, char *argv[] )
2050 return autogroup_initialize();