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: to much attributes specified in url <%s>\n",
636 ldap_free_urldesc( lud );
640 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
642 if ( agf->agf_anlist == NULL ) {
643 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
644 lud->lud_attrs[0], 0, 0 );
646 ldap_free_urldesc( lud );
652 agf->agf_next = NULL;
655 if( (*agep)->age_filter == NULL ) {
656 (*agep)->age_filter = agf;
659 if( agf_prev != NULL ) {
660 agf_prev->agf_next = agf;
666 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
669 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
670 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
672 ldap_free_urldesc( lud );
679 ldap_free_urldesc( lud );
684 if ( null_entry == 1 ) {
691 ** Used when opening the database to add all existing
692 ** groups from the database to our internal list.
695 autogroup_group_add_cb( Operation *op, SlapReply *rs )
697 assert( op->o_tag == LDAP_REQ_SEARCH );
699 if ( rs->sr_type == REP_SEARCH ) {
700 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
702 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
703 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
705 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
713 ** When adding a group, we first strip any existing members,
714 ** and add all which match the filters ourselfs.
717 autogroup_add_entry( Operation *op, SlapReply *rs)
719 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
720 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
721 autogroup_def_t *agd = agi->agi_def;
722 autogroup_entry_t *age;
723 autogroup_filter_t *agf;
726 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
727 op->ora_e->e_name.bv_val, 0, 0);
729 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
731 /* Check if it's a group. */
732 for ( ; agd ; agd = agd->agd_next ) {
733 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
735 const char *text = NULL;
738 mod.sm_op = LDAP_MOD_DELETE;
739 mod.sm_desc = agd->agd_member_ad;
740 mod.sm_type = agd->agd_member_ad->ad_cname;
741 mod.sm_values = NULL;
742 mod.sm_nvalues = NULL;
744 /* We don't want any member attributes added by the user. */
745 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
747 autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
748 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
749 return SLAP_CB_CONTINUE;
754 for ( age = agi->agi_entry; age ; age = age->age_next ) {
755 ldap_pvt_thread_mutex_lock( &age->age_mutex );
757 /* Check if any of the filters are the suffix to the entry DN.
758 If yes, we can test that filter against the entry. */
760 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
761 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
762 rc = test_filter( op, op->ora_e, agf->agf_filter );
763 if ( rc == LDAP_COMPARE_TRUE ) {
764 if ( agf->agf_anlist ) {
765 autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
767 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
773 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
776 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
778 return SLAP_CB_CONTINUE;
782 ** agi - internal group and attribute definitions list
783 ** e - the group to remove from the internal list
786 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
788 autogroup_entry_t *age = agi->agi_entry,
793 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
794 age->age_dn.bv_val, 0, 0);
796 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
797 age_next = age->age_next;
800 autogroup_filter_t *agf = age->age_filter,
803 if ( age_prev != NULL ) {
804 age_prev->age_next = age_next;
806 agi->agi_entry = NULL;
809 ch_free( age->age_dn.bv_val );
810 ch_free( age->age_ndn.bv_val );
812 for( agf_next = agf ; agf_next ; agf = agf_next ){
813 agf_next = agf->agf_next;
815 filter_free( agf->agf_filter );
816 ch_free( agf->agf_filterstr.bv_val );
817 ch_free( agf->agf_dn.bv_val );
818 ch_free( agf->agf_ndn.bv_val );
819 anlist_free( agf->agf_anlist, 1, NULL );
823 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
824 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
833 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
840 autogroup_delete_entry( Operation *op, SlapReply *rs)
842 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
843 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
844 autogroup_entry_t *age, *age_prev, *age_next;
845 autogroup_filter_t *agf;
847 int matched_group = 0, rc = 0;
849 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
851 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
853 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
854 LDAP_SUCCESS || e == NULL ) {
855 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
856 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
857 return SLAP_CB_CONTINUE;
860 /* Check if the entry to be deleted is one of our groups. */
861 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
863 ldap_pvt_thread_mutex_lock( &age->age_mutex );
864 age_next = age->age_next;
866 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
871 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
874 autogroup_delete_group( agi, age );
879 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
882 if ( matched_group == 1 ) {
883 overlay_entry_release_ov( op, e, 0, on );
884 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
885 return SLAP_CB_CONTINUE;
888 /* Check if the entry matches any of the groups.
889 If yes, we can delete the entry from that group. */
891 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
892 ldap_pvt_thread_mutex_lock( &age->age_mutex );
894 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
895 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
896 rc = test_filter( op, e, agf->agf_filter );
897 if ( rc == LDAP_COMPARE_TRUE ) {
898 /* If the attribute is retrieved from the entry, we don't know what to delete
899 ** So the group must be entirely refreshed
900 ** But the refresh can't be done now because the entry is not deleted
901 ** So the group is marked as mustrefresh
903 if ( agf->agf_anlist ) {
904 age->age_mustrefresh = 1;
906 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
912 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
915 overlay_entry_release_ov( op, e, 0, on );
916 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
918 return SLAP_CB_CONTINUE;
922 autogroup_response( Operation *op, SlapReply *rs )
924 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
925 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
926 autogroup_def_t *agd = agi->agi_def;
927 autogroup_entry_t *age;
928 autogroup_filter_t *agf;
929 BerValue new_dn, new_ndn, pdn;
932 int is_olddn, is_newdn, is_value_refresh, dn_equal;
934 /* Handle all cases where a refresh of the group is needed */
935 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
936 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
938 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
940 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
941 /* Request detected that the group must be refreshed */
943 ldap_pvt_thread_mutex_lock( &age->age_mutex );
945 if ( age->age_mustrefresh ) {
946 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
948 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
949 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
953 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
956 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
958 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
959 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
961 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
963 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
965 if ( op->oq_modrdn.rs_newSup ) {
966 pdn = *op->oq_modrdn.rs_newSup;
968 dnParent( &op->o_req_dn, &pdn );
970 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
972 if ( op->oq_modrdn.rs_nnewSup ) {
973 pdn = *op->oq_modrdn.rs_nnewSup;
975 dnParent( &op->o_req_ndn, &pdn );
977 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
979 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
981 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
983 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
984 LDAP_SUCCESS || e == NULL ) {
985 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
986 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
987 return SLAP_CB_CONTINUE;
990 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
994 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
995 overlay_entry_release_ov( op, e, 0, on );
996 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
997 return SLAP_CB_CONTINUE;
1001 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1002 for ( ; agd; agd = agd->agd_next ) {
1004 if ( value_find_ex( slap_schema.si_ad_objectClass,
1005 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1006 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1007 a->a_nvals, &agd->agd_oc->soc_cname,
1008 op->o_tmpmemctx ) == 0 )
1010 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1013 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1015 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1016 ber_dupbv( &age->age_dn, &new_dn );
1017 ber_dupbv( &age->age_ndn, &new_ndn );
1019 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1020 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1021 overlay_entry_release_ov( op, e, 0, on );
1022 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1023 return SLAP_CB_CONTINUE;
1031 1. check if the orginal entry's DN is in the group.
1032 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1034 If 1 and 2 are both false, we do nothing.
1035 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1036 If 1 is false, and 2 is true, we check the entry against the group's filters,
1037 and add it's DN to the group.
1038 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1040 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1043 is_value_refresh = 0;
1046 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1048 if ( age->age_filter && age->age_filter->agf_anlist ) {
1049 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1055 if ( age->age_modrdn_olddnmodified ) {
1056 /* Resquest already marked this group to be updated */
1058 is_value_refresh = 1;
1059 age->age_modrdn_olddnmodified = 0;
1062 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1063 LDAP_SUCCESS || group == NULL ) {
1064 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1066 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1067 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1069 overlay_entry_release_ov( op, e, 0, on );
1070 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1071 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1072 return SLAP_CB_CONTINUE;
1075 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1078 if ( value_find_ex( age->age_def->agd_member_ad,
1079 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1080 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1081 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1088 overlay_entry_release_ov( op, group, 0, on );
1092 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1093 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1094 /* TODO: should retest filter as it could imply conditions on the dn */
1101 if ( is_value_refresh ) {
1102 if ( is_olddn != is_newdn ) {
1104 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1106 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1107 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1110 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1113 if ( is_olddn == 1 && is_newdn == 0 ) {
1115 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1117 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1119 if ( is_olddn == 0 && is_newdn == 1 ) {
1120 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1121 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1123 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1125 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1130 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1133 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1135 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1136 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1140 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1141 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1145 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1148 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1149 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1151 overlay_entry_release_ov( op, e, 0, on );
1153 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1157 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1158 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1159 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1161 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1163 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1164 LDAP_SUCCESS || e == NULL ) {
1165 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1166 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1167 return SLAP_CB_CONTINUE;
1170 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1174 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1175 overlay_entry_release_ov( op, e, 0, on );
1176 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1177 return SLAP_CB_CONTINUE;
1180 /* If we modify a group's memberURL, we have to delete all of it's members,
1181 and add them anew, because we cannot tell from which memberURL a member was added. */
1182 for ( ; agd; agd = agd->agd_next ) {
1184 if ( value_find_ex( slap_schema.si_ad_objectClass,
1185 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1186 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1187 a->a_nvals, &agd->agd_oc->soc_cname,
1188 op->o_tmpmemctx ) == 0 )
1193 m = op->orm_modlist;
1195 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1196 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1198 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1201 for ( ; m ; m = m->sml_next ) {
1202 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1203 autogroup_def_t *group_agd = age->age_def;
1204 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1205 op->o_req_dn.bv_val, 0, 0);
1207 overlay_entry_release_ov( op, e, 0, on );
1209 autogroup_delete_member_from_group( op, NULL, NULL, age );
1210 autogroup_delete_group( agi, age );
1212 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1214 overlay_entry_release_ov( op, e, 0, on );
1215 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1216 return SLAP_CB_CONTINUE;
1220 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1224 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1227 overlay_entry_release_ov( op, e, 0, on );
1228 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1229 return SLAP_CB_CONTINUE;
1233 /* When modifing any of the attributes of an entry, we must
1234 check if the entry is in any of our groups, and if
1235 the modified entry maches any of the filters of that group.
1237 If the entry exists in a group, but the modified attributes do
1238 not match any of the group's filters, we delete the entry from that group.
1239 If the entry doesn't exist in a group, but matches a filter,
1240 we add it to that group.
1242 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1247 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1249 if ( age->age_filter && age->age_filter->agf_anlist ) {
1250 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1256 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1257 LDAP_SUCCESS || group == NULL ) {
1258 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1259 age->age_dn.bv_val, 0, 0);
1261 overlay_entry_release_ov( op, e, 0, on );
1262 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1263 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1264 return SLAP_CB_CONTINUE;
1267 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1270 if ( value_find_ex( age->age_def->agd_member_ad,
1271 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1272 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1273 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1280 overlay_entry_release_ov( op, group, 0, on );
1282 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1283 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1284 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1291 if ( is_olddn == 1 && is_newdn == 0 ) {
1293 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1295 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1297 if ( is_olddn == 0 && is_newdn == 1 ) {
1299 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1301 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1304 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1307 overlay_entry_release_ov( op, e, 0, on );
1309 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1313 return SLAP_CB_CONTINUE;
1317 ** Detect if filter contains a memberOf check for dn
1320 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1323 if ( f == NULL ) return 0;
1325 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1326 case LDAP_FILTER_AND:
1327 case LDAP_FILTER_OR:
1328 case LDAP_FILTER_NOT:
1329 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1330 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1333 case LDAP_FILTER_EQUALITY:
1334 result = ( f->f_ava->aa_desc == memberof_ad &&
1335 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1345 ** When modifing a group, we must deny any modifications to the member attribute,
1346 ** because the group would be inconsistent.
1349 autogroup_modify_entry( Operation *op, SlapReply *rs)
1351 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1352 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1353 autogroup_def_t *agd = agi->agi_def;
1354 autogroup_entry_t *age;
1358 if ( get_manageDSAit( op ) ) {
1359 return SLAP_CB_CONTINUE;
1362 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1363 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1365 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1366 LDAP_SUCCESS || e == NULL ) {
1367 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1368 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1369 return SLAP_CB_CONTINUE;
1372 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1373 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1374 autogroup_filter_t *agf;
1375 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1376 if ( agf->agf_anlist ) {
1378 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1379 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1380 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1381 int rc = test_filter( op, e, agf->agf_filter );
1382 if ( rc == LDAP_COMPARE_TRUE ) {
1383 age->age_mustrefresh = 1;
1390 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1391 age->age_mustrefresh = 1;
1396 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1399 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1400 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1401 return SLAP_CB_CONTINUE;
1405 for ( ; agd; agd = agd->agd_next ) {
1407 if ( value_find_ex( slap_schema.si_ad_objectClass,
1408 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1409 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1410 a->a_nvals, &agd->agd_oc->soc_cname,
1411 op->o_tmpmemctx ) == 0 )
1416 m = op->orm_modlist;
1418 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1419 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1422 for ( ; m ; m = m->sml_next ) {
1423 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1424 overlay_entry_release_ov( op, e, 0, on );
1425 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1426 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1427 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1428 return LDAP_CONSTRAINT_VIOLATION;
1435 overlay_entry_release_ov( op, e, 0, on );
1436 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1437 return SLAP_CB_CONTINUE;
1441 overlay_entry_release_ov( op, e, 0, on );
1442 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1443 return SLAP_CB_CONTINUE;
1447 ** Detect if the olddn is part of a group and so if the group should be refreshed
1450 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1452 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1453 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1454 autogroup_entry_t *age;
1457 if ( get_manageDSAit( op ) ) {
1458 return SLAP_CB_CONTINUE;
1461 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1462 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1464 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1465 LDAP_SUCCESS || e == NULL ) {
1466 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1467 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1468 return SLAP_CB_CONTINUE;
1471 /* Must check if a dn is modified */
1472 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1473 autogroup_filter_t *agf;
1474 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1475 if ( agf->agf_anlist ) {
1476 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1477 int rc = test_filter( op, e, agf->agf_filter );
1478 if ( rc == LDAP_COMPARE_TRUE ) {
1479 age->age_modrdn_olddnmodified = 1;
1486 overlay_entry_release_ov( op, e, 0, on );
1487 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1488 return SLAP_CB_CONTINUE;
1492 ** Builds a filter for searching for the
1493 ** group entries, according to the objectClass.
1496 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1500 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1502 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1503 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1504 + agd->agd_oc->soc_cname.bv_len;
1505 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1507 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1509 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1513 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1515 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1526 static ConfigDriver ag_cfgen;
1528 static ConfigTable agcfg[] = {
1529 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1530 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1531 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1532 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1533 "EQUALITY caseIgnoreMatch "
1534 "SYNTAX OMsDirectoryString "
1535 "X-ORDERED 'VALUES' )",
1538 { "autogroup-memberof-ad", "memberOf attribute",
1539 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1540 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1541 "DESC 'memberOf attribute' "
1542 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1545 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1548 static ConfigOCs agocs[] = {
1549 { "( OLcfgCtOc:2.1 "
1550 "NAME 'olcAutomaticGroups' "
1551 "DESC 'Automatic groups configuration' "
1552 "SUP olcOverlayConfig "
1555 "$ olcAGmemberOfAd "
1558 Cft_Overlay, agcfg, NULL, NULL },
1564 ag_cfgen( ConfigArgs *c )
1566 slap_overinst *on = (slap_overinst *)c->bi;
1567 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1568 autogroup_def_t *agd;
1569 autogroup_entry_t *age;
1573 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1576 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1577 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1578 agi->agi_def = NULL;
1579 agi->agi_entry = NULL;
1580 on->on_bi.bi_private = (void *)agi;
1584 age = agi->agi_entry;
1586 if ( c->op == SLAP_CONFIG_EMIT ) {
1590 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1592 char *ptr = c->cr_msg;
1594 assert(agd->agd_oc != NULL);
1595 assert(agd->agd_member_url_ad != NULL);
1596 assert(agd->agd_member_ad != NULL);
1598 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1599 SLAP_X_ORDERED_FMT "%s %s %s", i,
1600 agd->agd_oc->soc_cname.bv_val,
1601 agd->agd_member_url_ad->ad_cname.bv_val,
1602 agd->agd_member_ad->ad_cname.bv_val );
1604 bv.bv_val = c->cr_msg;
1605 bv.bv_len = ptr - bv.bv_val;
1606 value_add_one ( &c->rvalue_vals, &bv );
1611 case AG_MEMBER_OF_AD:
1612 if ( agi->agi_memberof_ad != NULL ){
1613 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1624 }else if ( c->op == LDAP_MOD_DELETE ) {
1626 autogroup_def_t *agd_next;
1627 autogroup_entry_t *age_next;
1628 autogroup_filter_t *agf = age->age_filter,
1631 for ( agd_next = agd; agd_next; agd = agd_next ) {
1632 agd_next = agd->agd_next;
1637 for ( age_next = age ; age_next ; age = age_next ) {
1638 age_next = age->age_next;
1640 ch_free( age->age_dn.bv_val );
1641 ch_free( age->age_ndn.bv_val );
1643 for( agf_next = agf ; agf_next ; agf = agf_next ){
1644 agf_next = agf->agf_next;
1646 filter_free( agf->agf_filter );
1647 ch_free( agf->agf_filterstr.bv_val );
1648 ch_free( agf->agf_dn.bv_val );
1649 ch_free( agf->agf_ndn.bv_val );
1650 anlist_free( agf->agf_anlist, 1, NULL );
1654 ldap_pvt_thread_mutex_init( &age->age_mutex );
1659 on->on_bi.bi_private = NULL;
1662 autogroup_def_t **agdp;
1663 autogroup_entry_t *age_next, *age_prev;
1664 autogroup_filter_t *agf,
1667 for ( i = 0, agdp = &agi->agi_def;
1670 if ( *agdp == NULL) {
1673 agdp = &(*agdp)->agd_next;
1677 *agdp = agd->agd_next;
1679 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1680 age_next = age->age_next;
1682 if( age->age_def == agd ) {
1683 agf = age->age_filter;
1685 ch_free( age->age_dn.bv_val );
1686 ch_free( age->age_ndn.bv_val );
1688 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1689 agf_next = agf->agf_next;
1690 filter_free( agf->agf_filter );
1691 ch_free( agf->agf_filterstr.bv_val );
1692 ch_free( agf->agf_dn.bv_val );
1693 ch_free( agf->agf_ndn.bv_val );
1694 anlist_free( agf->agf_anlist, 1, NULL );
1698 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1703 if( age_prev != NULL ) {
1704 age_prev->age_next = age_next;
1719 autogroup_def_t **agdp,
1721 ObjectClass *oc = NULL;
1722 AttributeDescription *member_url_ad = NULL,
1727 oc = oc_find( c->argv[ 1 ] );
1729 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1730 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1731 "unable to find ObjectClass \"%s\"",
1733 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1734 c->log, c->cr_msg, 0 );
1739 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1740 if( rc != LDAP_SUCCESS ) {
1741 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1742 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1743 "unable to find AttributeDescription \"%s\"",
1745 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1746 c->log, c->cr_msg, 0 );
1750 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1751 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1752 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1753 "AttributeDescription \"%s\" ",
1754 "must be of a subtype \"labeledURI\"",
1756 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1757 c->log, c->cr_msg, 0 );
1761 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1762 if( rc != LDAP_SUCCESS ) {
1763 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1764 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1765 "unable to find AttributeDescription \"%s\"",
1767 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1768 c->log, c->cr_msg, 0 );
1772 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1773 /* The same URL attribute / member attribute pair
1774 * cannot be repeated */
1776 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1777 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1778 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1779 "URL attributeDescription \"%s\" already mapped",
1780 member_ad->ad_cname.bv_val );
1781 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1782 c->log, c->cr_msg, 0 );
1783 /* return 1; //warning*/
1787 if ( c->valx > 0 ) {
1790 for ( i = 0, agdp = &agi->agi_def ;
1793 if ( *agdp == NULL ) {
1794 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1795 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1796 "invalid index {%d}",
1798 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1799 c->log, c->cr_msg, 0 );
1803 agdp = &(*agdp)->agd_next;
1808 for ( agdp = &agi->agi_def; *agdp;
1809 agdp = &(*agdp)->agd_next )
1813 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1815 (*agdp)->agd_oc = oc;
1816 (*agdp)->agd_member_url_ad = member_url_ad;
1817 (*agdp)->agd_member_ad = member_ad;
1818 (*agdp)->agd_next = agd_next;
1822 case AG_MEMBER_OF_AD: {
1823 AttributeDescription *memberof_ad = NULL;
1826 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1827 if( rc != LDAP_SUCCESS ) {
1828 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1829 "\"autogroup-memberof-ad <memberof-ad>\": "
1830 "unable to find AttributeDescription \"%s\"",
1832 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1833 c->log, c->cr_msg, 0 );
1837 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1838 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1840 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1841 "memberof attribute=\"%s\" must either "
1842 "have DN (%s) or nameUID (%s) syntax",
1843 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1844 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1845 c->log, c->cr_msg, 0 );
1849 agi->agi_memberof_ad = memberof_ad;
1861 extern int slapMode;
1864 ** Do a search for all the groups in the
1865 ** database, and add them to out internal list.
1872 slap_overinst *on = (slap_overinst *) be->bd_info;
1873 autogroup_info_t *agi = on->on_bi.bi_private;
1874 autogroup_def_t *agd;
1877 slap_callback cb = { 0 };
1879 void *thrctx = ldap_pvt_thread_pool_context();
1880 Connection conn = { 0 };
1881 OperationBuffer opbuf;
1883 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1885 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1889 connection_fake_init( &conn, &opbuf, thrctx );
1892 op->ors_attrsonly = 0;
1893 op->o_tag = LDAP_REQ_SEARCH;
1894 op->o_dn = be->be_rootdn;
1895 op->o_ndn = be->be_rootndn;
1897 op->o_req_dn = be->be_suffix[0];
1898 op->o_req_ndn = be->be_nsuffix[0];
1900 op->ors_scope = LDAP_SCOPE_SUBTREE;
1901 op->ors_deref = LDAP_DEREF_NEVER;
1902 op->ors_limit = NULL;
1903 op->ors_tlimit = SLAP_NO_LIMIT;
1904 op->ors_slimit = SLAP_NO_LIMIT;
1905 op->ors_attrs = slap_anlist_no_attrs;
1908 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1911 cb.sc_private = &ags;
1912 cb.sc_response = autogroup_group_add_cb;
1913 cb.sc_cleanup = NULL;
1916 op->o_callback = &cb;
1918 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1919 SlapReply rs = { REP_RESULT };
1921 autogroup_build_def_filter(agd, op);
1925 op->o_bd->be_search( op, &rs );
1927 filter_free_x( op, op->ors_filter, 1 );
1928 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1931 if( ! agi->agi_memberof_ad ){
1933 const char *text = NULL;
1935 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1936 if ( rc != LDAP_SUCCESS ) {
1937 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1938 "unable to find attribute=\"%s\": %s (%d)\n",
1939 SLAPD_MEMBEROF_ATTR, text, rc );
1952 slap_overinst *on = (slap_overinst *) be->bd_info;
1954 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1956 if ( on->on_bi.bi_private ) {
1957 autogroup_info_t *agi = on->on_bi.bi_private;
1958 autogroup_entry_t *age = agi->agi_entry,
1960 autogroup_filter_t *agf, *agf_next;
1962 for ( age_next = age; age_next; age = age_next ) {
1963 age_next = age->age_next;
1965 ch_free( age->age_dn.bv_val );
1966 ch_free( age->age_ndn.bv_val );
1968 agf = age->age_filter;
1970 for ( agf_next = agf; agf_next; agf = agf_next ) {
1971 agf_next = agf->agf_next;
1973 filter_free( agf->agf_filter );
1974 ch_free( agf->agf_filterstr.bv_val );
1975 ch_free( agf->agf_dn.bv_val );
1976 ch_free( agf->agf_ndn.bv_val );
1977 anlist_free( agf->agf_anlist, 1, NULL );
1981 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1990 autogroup_db_destroy(
1994 slap_overinst *on = (slap_overinst *) be->bd_info;
1996 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
1998 if ( on->on_bi.bi_private ) {
1999 autogroup_info_t *agi = on->on_bi.bi_private;
2000 autogroup_def_t *agd = agi->agi_def,
2003 for ( agd_next = agd; agd_next; agd = agd_next ) {
2004 agd_next = agd->agd_next;
2009 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2016 static slap_overinst autogroup = { { NULL } };
2020 autogroup_initialize(void)
2023 autogroup.on_bi.bi_type = "autogroup";
2025 autogroup.on_bi.bi_db_open = autogroup_db_open;
2026 autogroup.on_bi.bi_db_close = autogroup_db_close;
2027 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2029 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2030 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2031 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2032 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2034 autogroup.on_response = autogroup_response;
2036 autogroup.on_bi.bi_cf_ocs = agocs;
2038 rc = config_register_schema( agcfg, agocs );
2043 return overlay_register( &autogroup );
2047 init_module( int argc, char *argv[] )
2049 return autogroup_initialize();