1 /* autogroup.c - automatic group overlay */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2007-2014 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;
334 struct berval lvals[ 2 ], lnvals[ 2 ];
337 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
338 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
340 if ( agf->agf_anlist ) {
341 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
344 nvals = attr->a_nvals;
345 numvals = attr->a_numvals;
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;
390 struct berval lvals[ 2 ], lnvals[ 2 ];
393 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
394 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
396 if ( agf->agf_anlist ) {
397 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
400 nvals = attr->a_nvals;
401 numvals = attr->a_numvals;
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 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();