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 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
116 dn->bv_val, age->age_dn.bv_val, 0);
118 assert( dn != NULL );
119 assert( ndn != NULL );
121 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
122 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
123 ber_dupbv( vals, dn );
124 BER_BVZERO( &vals[ 1 ] );
125 ber_dupbv( nvals, ndn );
126 BER_BVZERO( &nvals[ 1 ] );
128 modlist->sml_op = LDAP_MOD_ADD;
129 modlist->sml_desc = age->age_def->agd_member_ad;
130 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
131 modlist->sml_values = vals;
132 modlist->sml_nvalues = nvals;
133 modlist->sml_numvals = 1;
134 modlist->sml_flags = SLAP_MOD_INTERNAL;
135 modlist->sml_next = NULL;
137 o.o_tag = LDAP_REQ_MODIFY;
139 o.orm_modlist = modlist;
140 o.o_req_dn = age->age_dn;
141 o.o_req_ndn = age->age_ndn;
142 o.o_permissive_modify = 1;
143 o.o_managedsait = SLAP_CONTROL_CRITICAL;
144 o.o_relax = SLAP_CONTROL_CRITICAL;
146 o.o_bd->bd_info = (BackendInfo *)on->on_info;
147 (void)op->o_bd->be_modify( &o, &sreply );
148 o.o_bd->bd_info = (BackendInfo *)on;
150 slap_mods_free( modlist, 1 );
152 return sreply.sr_err;
156 ** e - the entry where to get the attribute values
157 ** age - the group to which the values will be added
160 autogroup_add_member_values_to_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
162 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
163 Modifications modlist;
164 SlapReply sreply = {REP_RESULT};
166 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);
174 attr = attrs_find( e->e_attrs, attrdesc );
180 modlist.sml_op = LDAP_MOD_ADD;
181 modlist.sml_desc = age->age_def->agd_member_ad;
182 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
183 modlist.sml_values = attr->a_vals;
184 modlist.sml_nvalues = attr->a_nvals;
185 modlist.sml_numvals = attr->a_numvals;
186 modlist.sml_flags = SLAP_MOD_INTERNAL;
187 modlist.sml_next = NULL;
189 o.o_tag = LDAP_REQ_MODIFY;
191 o.orm_modlist = &modlist;
192 o.o_req_dn = age->age_dn;
193 o.o_req_ndn = age->age_ndn;
194 o.o_permissive_modify = 1;
195 o.o_managedsait = SLAP_CONTROL_CRITICAL;
196 o.o_relax = SLAP_CONTROL_CRITICAL;
198 o.o_bd->bd_info = (BackendInfo *)on->on_info;
199 (void)op->o_bd->be_modify( &o, &sreply );
200 o.o_bd->bd_info = (BackendInfo *)on;
202 return sreply.sr_err;
206 ** dn,ndn - the DN to be deleted
207 ** age - the group from which the DN will be deleted
208 ** If we pass a NULL dn and ndn, all members are deleted from the group.
211 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
213 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
214 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
215 SlapReply sreply = {REP_RESULT};
216 BerValue *vals, *nvals;
217 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
220 if ( dn == NULL || ndn == NULL ) {
221 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
222 age->age_dn.bv_val, 0 ,0);
224 modlist->sml_values = NULL;
225 modlist->sml_nvalues = NULL;
226 modlist->sml_numvals = 0;
228 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
229 dn->bv_val, age->age_dn.bv_val, 0);
231 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
232 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
233 ber_dupbv( vals, dn );
234 BER_BVZERO( &vals[ 1 ] );
235 ber_dupbv( nvals, ndn );
236 BER_BVZERO( &nvals[ 1 ] );
238 modlist->sml_values = vals;
239 modlist->sml_nvalues = nvals;
240 modlist->sml_numvals = 1;
244 modlist->sml_op = LDAP_MOD_DELETE;
245 modlist->sml_desc = age->age_def->agd_member_ad;
246 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
247 modlist->sml_flags = SLAP_MOD_INTERNAL;
248 modlist->sml_next = NULL;
251 o.o_tag = LDAP_REQ_MODIFY;
252 o.orm_modlist = modlist;
253 o.o_req_dn = age->age_dn;
254 o.o_req_ndn = age->age_ndn;
255 o.o_relax = SLAP_CONTROL_CRITICAL;
256 o.o_managedsait = SLAP_CONTROL_CRITICAL;
257 o.o_permissive_modify = 1;
259 o.o_bd->bd_info = (BackendInfo *)on->on_info;
260 (void)op->o_bd->be_modify( &o, &sreply );
261 o.o_bd->bd_info = (BackendInfo *)on;
263 slap_mods_free( modlist, 1 );
265 return sreply.sr_err;
269 ** e - the entry where to get the attribute values
270 ** age - the group from which the values will be deleted
273 autogroup_delete_member_values_from_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
275 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
276 Modifications modlist;
277 SlapReply sreply = {REP_RESULT};
279 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
282 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
283 e->e_name.bv_val, age->age_dn.bv_val, 0);
287 attr = attrs_find( e->e_attrs, attrdesc );
293 modlist.sml_op = LDAP_MOD_DELETE;
294 modlist.sml_desc = age->age_def->agd_member_ad;
295 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
296 modlist.sml_values = attr->a_vals;
297 modlist.sml_nvalues = attr->a_nvals;
298 modlist.sml_numvals = attr->a_numvals;
299 modlist.sml_flags = SLAP_MOD_INTERNAL;
300 modlist.sml_next = NULL;
302 o.o_tag = LDAP_REQ_MODIFY;
304 o.orm_modlist = &modlist;
305 o.o_req_dn = age->age_dn;
306 o.o_req_ndn = age->age_ndn;
307 o.o_permissive_modify = 1;
308 o.o_managedsait = SLAP_CONTROL_CRITICAL;
309 o.o_relax = SLAP_CONTROL_CRITICAL;
311 o.o_bd->bd_info = (BackendInfo *)on->on_info;
312 (void)op->o_bd->be_modify( &o, &sreply );
313 o.o_bd->bd_info = (BackendInfo *)on;
315 return sreply.sr_err;
319 ** Callback used to add entries to a group,
320 ** which are going to be written in the database
321 ** (used in bi_op_add)
322 ** The group is passed in autogroup_ga_t->agg_group
325 autogroup_member_search_cb( Operation *op, SlapReply *rs )
327 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
329 assert( op->o_tag == LDAP_REQ_SEARCH );
331 if ( rs->sr_type == REP_SEARCH ) {
332 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
333 autogroup_entry_t *age = agg->agg_group;
334 autogroup_filter_t *agf = agg->agg_filter;
336 const char *text = NULL;
338 struct berval *vals, *nvals;
341 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
342 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
344 if ( agf->agf_anlist ) {
345 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
348 nvals = attr->a_nvals;
349 numvals = attr->a_numvals;
355 struct berval lvals[ 2 ], lnvals[ 2 ];
356 lvals[ 0 ] = rs->sr_entry->e_name;
357 BER_BVZERO( &lvals[ 1 ] );
358 lnvals[ 0 ] = rs->sr_entry->e_nname;
359 BER_BVZERO( &lnvals[ 1 ] );
365 mod.sm_op = LDAP_MOD_ADD;
366 mod.sm_desc = age->age_def->agd_member_ad;
367 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
368 mod.sm_values = vals;
369 mod.sm_nvalues = nvals;
370 mod.sm_numvals = numvals;
372 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
379 ** Callback used to add entries to a group, which is already in the database.
380 ** (used in on_response)
381 ** The group is passed in autogroup_ga_t->agg_group
385 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
387 assert( op->o_tag == LDAP_REQ_SEARCH );
389 if ( rs->sr_type == REP_SEARCH ) {
390 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
391 autogroup_entry_t *age = agg->agg_group;
392 autogroup_filter_t *agf = agg->agg_filter;
393 Modifications *modlist;
394 struct berval *vals, *nvals;
397 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
398 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
400 if ( agf->agf_anlist ) {
401 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
404 nvals = attr->a_nvals;
405 numvals = attr->a_numvals;
411 struct berval lvals[ 2 ], lnvals[ 2 ];
412 lvals[ 0 ] = rs->sr_entry->e_name;
413 BER_BVZERO( &lvals[ 1 ] );
414 lnvals[ 0 ] = rs->sr_entry->e_nname;
415 BER_BVZERO( &lnvals[ 1 ] );
422 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
424 modlist->sml_op = LDAP_MOD_ADD;
425 modlist->sml_desc = age->age_def->agd_member_ad;
426 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
428 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
429 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
430 modlist->sml_numvals = numvals;
432 modlist->sml_flags = SLAP_MOD_INTERNAL;
433 modlist->sml_next = NULL;
435 if ( agg->agg_mod == NULL ) {
436 agg->agg_mod = modlist;
437 agg->agg_mod_last = modlist;
439 agg->agg_mod_last->sml_next = modlist;
440 agg->agg_mod_last = modlist;
451 ** Adds all entries matching the passed filter to the specified group.
452 ** If modify == 1, then we modify the group's entry in the database using be_modify.
453 ** If modify == 0, then, we must supply a rw entry for the group,
454 ** because we only modify the entry, without calling be_modify.
455 ** e - the group entry, to which the members will be added
460 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
462 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
464 SlapReply rs = { REP_SEARCH };
465 slap_callback cb = { 0 };
466 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
469 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
470 age->age_dn.bv_val, 0, 0);
473 o.o_tag = LDAP_REQ_SEARCH;
475 o.o_req_dn = agf->agf_dn;
476 o.o_req_ndn = agf->agf_ndn;
478 o.ors_filterstr = agf->agf_filterstr;
479 o.ors_filter = agf->agf_filter;
481 o.ors_scope = agf->agf_scope;
482 o.ors_deref = LDAP_DEREF_NEVER;
484 o.ors_tlimit = SLAP_NO_LIMIT;
485 o.ors_slimit = SLAP_NO_LIMIT;
486 o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
489 agg.agg_filter = agf;
491 agg.agg_mod_last = NULL;
493 cb.sc_private = &agg;
496 cb.sc_response = autogroup_member_search_modify_cb;
498 cb.sc_response = autogroup_member_search_cb;
501 cb.sc_cleanup = NULL;
506 o.o_bd->bd_info = (BackendInfo *)on->on_info;
507 op->o_bd->be_search( &o, &rs );
508 o.o_bd->bd_info = (BackendInfo *)on;
510 if ( modify == 1 && agg.agg_mod ) {
511 rs_reinit( &rs, REP_RESULT );
514 o.o_callback = &null_cb;
515 o.o_tag = LDAP_REQ_MODIFY;
516 o.orm_modlist = agg.agg_mod;
517 o.o_req_dn = age->age_dn;
518 o.o_req_ndn = age->age_ndn;
519 o.o_relax = SLAP_CONTROL_CRITICAL;
520 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
521 o.o_permissive_modify = 1;
523 o.o_bd->bd_info = (BackendInfo *)on->on_info;
524 (void)op->o_bd->be_modify( &o, &rs );
525 o.o_bd->bd_info = (BackendInfo *)on;
527 slap_mods_free(agg.agg_mod, 1);
534 ** Adds a group to the internal list from the passed entry.
535 ** scan specifies whether to add all maching members to the group.
536 ** modify specifies whether to modify the given group entry (when modify == 0),
537 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
538 ** agi - pointer to the groups and the attribute definitions
539 ** agd - the attribute definition of the added group
540 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
541 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
544 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
546 autogroup_entry_t **agep = &agi->agi_entry;
547 autogroup_filter_t *agf, *agf_prev = NULL;
548 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
549 LDAPURLDesc *lud = NULL;
552 int rc = 0, match = 1, null_entry = 0;
555 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
556 LDAP_SUCCESS || e == NULL ) {
557 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
564 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
565 e->e_name.bv_val, 0, 0);
567 if ( agi->agi_entry != NULL ) {
568 for ( ; *agep ; agep = &(*agep)->age_next ) {
569 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
571 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
579 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
580 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
581 (*agep)->age_def = agd;
582 (*agep)->age_filter = NULL;
583 (*agep)->age_mustrefresh = 0;
584 (*agep)->age_modrdn_olddnmodified = 0;
586 ber_dupbv( &(*agep)->age_dn, &e->e_name );
587 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
589 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
591 if ( null_entry == 1 ) {
593 overlay_entry_release_ov( op, e, 0, on );
597 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
599 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
601 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
603 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
604 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
610 agf->agf_scope = lud->lud_scope;
612 if ( lud->lud_dn == NULL ) {
613 BER_BVSTR( &dn, "" );
615 ber_str2bv( lud->lud_dn, 0, 0, &dn );
618 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
619 if ( rc != LDAP_SUCCESS ) {
620 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
625 if ( lud->lud_filter != NULL ) {
626 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
627 agf->agf_filter = str2filter( lud->lud_filter );
630 if ( lud->lud_attrs != NULL ) {
633 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
638 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: to much attributes specified in url <%s>\n",
641 ldap_free_urldesc( lud );
645 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
647 if ( agf->agf_anlist == NULL ) {
648 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
649 lud->lud_attrs[0], 0, 0 );
651 ldap_free_urldesc( lud );
657 agf->agf_next = NULL;
660 if( (*agep)->age_filter == NULL ) {
661 (*agep)->age_filter = agf;
664 if( agf_prev != NULL ) {
665 agf_prev->agf_next = agf;
671 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
674 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
675 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
677 ldap_free_urldesc( lud );
684 ldap_free_urldesc( lud );
689 if ( null_entry == 1 ) {
696 ** Used when opening the database to add all existing
697 ** groups from the database to our internal list.
700 autogroup_group_add_cb( Operation *op, SlapReply *rs )
702 assert( op->o_tag == LDAP_REQ_SEARCH );
704 if ( rs->sr_type == REP_SEARCH ) {
705 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
707 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
708 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
710 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
718 ** When adding a group, we first strip any existing members,
719 ** and add all which match the filters ourselfs.
722 autogroup_add_entry( Operation *op, SlapReply *rs)
724 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
725 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
726 autogroup_def_t *agd = agi->agi_def;
727 autogroup_entry_t *age;
728 autogroup_filter_t *agf;
731 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
732 op->ora_e->e_name.bv_val, 0, 0);
734 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
736 /* Check if it's a group. */
737 for ( ; agd ; agd = agd->agd_next ) {
738 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
740 const char *text = NULL;
743 mod.sm_op = LDAP_MOD_DELETE;
744 mod.sm_desc = agd->agd_member_ad;
745 mod.sm_type = agd->agd_member_ad->ad_cname;
746 mod.sm_values = NULL;
747 mod.sm_nvalues = NULL;
749 /* We don't want any member attributes added by the user. */
750 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
752 autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
753 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
754 return SLAP_CB_CONTINUE;
759 for ( age = agi->agi_entry; age ; age = age->age_next ) {
760 ldap_pvt_thread_mutex_lock( &age->age_mutex );
762 /* Check if any of the filters are the suffix to the entry DN.
763 If yes, we can test that filter against the entry. */
765 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
766 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
767 rc = test_filter( op, op->ora_e, agf->agf_filter );
768 if ( rc == LDAP_COMPARE_TRUE ) {
769 if ( agf->agf_anlist ) {
770 autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
772 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
778 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
781 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
783 return SLAP_CB_CONTINUE;
787 ** agi - internal group and attribute definitions list
788 ** e - the group to remove from the internal list
791 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
793 autogroup_entry_t *age = agi->agi_entry,
798 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
799 age->age_dn.bv_val, 0, 0);
801 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
802 age_next = age->age_next;
805 autogroup_filter_t *agf = age->age_filter,
808 if ( age_prev != NULL ) {
809 age_prev->age_next = age_next;
811 agi->agi_entry = NULL;
814 ch_free( age->age_dn.bv_val );
815 ch_free( age->age_ndn.bv_val );
817 for( agf_next = agf ; agf_next ; agf = agf_next ){
818 agf_next = agf->agf_next;
820 filter_free( agf->agf_filter );
821 ch_free( agf->agf_filterstr.bv_val );
822 ch_free( agf->agf_dn.bv_val );
823 ch_free( agf->agf_ndn.bv_val );
824 anlist_free( agf->agf_anlist, 1, NULL );
828 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
829 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
838 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
845 autogroup_delete_entry( Operation *op, SlapReply *rs)
847 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
848 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
849 autogroup_entry_t *age, *age_prev, *age_next;
850 autogroup_filter_t *agf;
852 int matched_group = 0, rc = 0;
854 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
856 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
858 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
859 LDAP_SUCCESS || e == NULL ) {
860 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
861 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
862 return SLAP_CB_CONTINUE;
865 /* Check if the entry to be deleted is one of our groups. */
866 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
868 ldap_pvt_thread_mutex_lock( &age->age_mutex );
869 age_next = age->age_next;
871 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
876 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
879 autogroup_delete_group( agi, age );
884 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
887 if ( matched_group == 1 ) {
888 overlay_entry_release_ov( op, e, 0, on );
889 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
890 return SLAP_CB_CONTINUE;
893 /* Check if the entry matches any of the groups.
894 If yes, we can delete the entry from that group. */
896 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
897 ldap_pvt_thread_mutex_lock( &age->age_mutex );
899 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
900 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
901 rc = test_filter( op, e, agf->agf_filter );
902 if ( rc == LDAP_COMPARE_TRUE ) {
903 /* If the attribute is retrieved from the entry, we don't know what to delete
904 ** So the group must be entirely refreshed
905 ** But the refresh can't be done now because the entry is not deleted
906 ** So the group is marked as mustrefresh
908 if ( agf->agf_anlist ) {
909 age->age_mustrefresh = 1;
911 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
917 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
920 overlay_entry_release_ov( op, e, 0, on );
921 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
923 return SLAP_CB_CONTINUE;
927 autogroup_response( Operation *op, SlapReply *rs )
929 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
930 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
931 autogroup_def_t *agd = agi->agi_def;
932 autogroup_entry_t *age;
933 autogroup_filter_t *agf;
934 BerValue new_dn, new_ndn, pdn;
937 int is_olddn, is_newdn, is_value_refresh, dn_equal;
939 /* Handle all cases where a refresh of the group is needed */
940 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
941 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
943 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
945 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
946 /* Request detected that the group must be refreshed */
948 ldap_pvt_thread_mutex_lock( &age->age_mutex );
950 if ( age->age_mustrefresh ) {
951 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
953 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
954 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
958 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
961 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
963 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
964 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
966 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
968 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
970 if ( op->oq_modrdn.rs_newSup ) {
971 pdn = *op->oq_modrdn.rs_newSup;
973 dnParent( &op->o_req_dn, &pdn );
975 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
977 if ( op->oq_modrdn.rs_nnewSup ) {
978 pdn = *op->oq_modrdn.rs_nnewSup;
980 dnParent( &op->o_req_ndn, &pdn );
982 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
984 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
986 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
988 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
989 LDAP_SUCCESS || e == NULL ) {
990 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
991 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
992 return SLAP_CB_CONTINUE;
995 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
999 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
1000 overlay_entry_release_ov( op, e, 0, on );
1001 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1002 return SLAP_CB_CONTINUE;
1006 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1007 for ( ; agd; agd = agd->agd_next ) {
1009 if ( value_find_ex( slap_schema.si_ad_objectClass,
1010 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1011 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1012 a->a_nvals, &agd->agd_oc->soc_cname,
1013 op->o_tmpmemctx ) == 0 )
1015 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1018 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1020 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1021 ber_dupbv( &age->age_dn, &new_dn );
1022 ber_dupbv( &age->age_ndn, &new_ndn );
1024 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1025 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1026 overlay_entry_release_ov( op, e, 0, on );
1027 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1028 return SLAP_CB_CONTINUE;
1036 1. check if the orginal entry's DN is in the group.
1037 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1039 If 1 and 2 are both false, we do nothing.
1040 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1041 If 1 is false, and 2 is true, we check the entry against the group's filters,
1042 and add it's DN to the group.
1043 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1045 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1048 is_value_refresh = 0;
1051 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1053 if ( age->age_filter && age->age_filter->agf_anlist ) {
1054 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1060 if ( age->age_modrdn_olddnmodified ) {
1061 /* Resquest already marked this group to be updated */
1063 is_value_refresh = 1;
1064 age->age_modrdn_olddnmodified = 0;
1067 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1068 LDAP_SUCCESS || group == NULL ) {
1069 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1071 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1072 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1074 overlay_entry_release_ov( op, e, 0, on );
1075 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1076 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1077 return SLAP_CB_CONTINUE;
1080 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1083 if ( value_find_ex( age->age_def->agd_member_ad,
1084 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1085 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1086 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1093 overlay_entry_release_ov( op, group, 0, on );
1097 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1098 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1099 /* TODO: should retest filter as it could imply conditions on the dn */
1106 if ( is_value_refresh ) {
1107 if ( is_olddn != is_newdn ) {
1109 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1111 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1112 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1115 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1118 if ( is_olddn == 1 && is_newdn == 0 ) {
1120 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1122 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1124 if ( is_olddn == 0 && is_newdn == 1 ) {
1125 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1126 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1128 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1130 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1135 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1138 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1140 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1141 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1145 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1146 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1150 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1153 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1154 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1156 overlay_entry_release_ov( op, e, 0, on );
1158 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1162 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1163 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1164 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1166 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1168 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1169 LDAP_SUCCESS || e == NULL ) {
1170 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1171 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1172 return SLAP_CB_CONTINUE;
1175 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1179 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1180 overlay_entry_release_ov( op, e, 0, on );
1181 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1182 return SLAP_CB_CONTINUE;
1185 /* If we modify a group's memberURL, we have to delete all of it's members,
1186 and add them anew, because we cannot tell from which memberURL a member was added. */
1187 for ( ; agd; agd = agd->agd_next ) {
1189 if ( value_find_ex( slap_schema.si_ad_objectClass,
1190 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1191 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1192 a->a_nvals, &agd->agd_oc->soc_cname,
1193 op->o_tmpmemctx ) == 0 )
1198 m = op->orm_modlist;
1200 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1201 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1203 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1206 for ( ; m ; m = m->sml_next ) {
1207 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1208 autogroup_def_t *group_agd = age->age_def;
1209 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1210 op->o_req_dn.bv_val, 0, 0);
1212 overlay_entry_release_ov( op, e, 0, on );
1214 autogroup_delete_member_from_group( op, NULL, NULL, age );
1215 autogroup_delete_group( agi, age );
1217 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1219 overlay_entry_release_ov( op, e, 0, on );
1220 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1221 return SLAP_CB_CONTINUE;
1225 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1229 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1232 overlay_entry_release_ov( op, e, 0, on );
1233 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1234 return SLAP_CB_CONTINUE;
1238 /* When modifing any of the attributes of an entry, we must
1239 check if the entry is in any of our groups, and if
1240 the modified entry maches any of the filters of that group.
1242 If the entry exists in a group, but the modified attributes do
1243 not match any of the group's filters, we delete the entry from that group.
1244 If the entry doesn't exist in a group, but matches a filter,
1245 we add it to that group.
1247 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1252 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1254 if ( age->age_filter && age->age_filter->agf_anlist ) {
1255 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1261 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1262 LDAP_SUCCESS || group == NULL ) {
1263 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1264 age->age_dn.bv_val, 0, 0);
1266 overlay_entry_release_ov( op, e, 0, on );
1267 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1268 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1269 return SLAP_CB_CONTINUE;
1272 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1275 if ( value_find_ex( age->age_def->agd_member_ad,
1276 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1277 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1278 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1285 overlay_entry_release_ov( op, group, 0, on );
1287 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1288 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1289 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1296 if ( is_olddn == 1 && is_newdn == 0 ) {
1298 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1300 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1302 if ( is_olddn == 0 && is_newdn == 1 ) {
1304 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1306 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1309 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1312 overlay_entry_release_ov( op, e, 0, on );
1314 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1318 return SLAP_CB_CONTINUE;
1322 ** Detect if filter contains a memberOf check for dn
1325 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1328 if ( f == NULL ) return 0;
1330 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1331 case LDAP_FILTER_AND:
1332 case LDAP_FILTER_OR:
1333 case LDAP_FILTER_NOT:
1334 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1335 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1338 case LDAP_FILTER_EQUALITY:
1339 result = ( f->f_ava->aa_desc == memberof_ad &&
1340 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1350 ** When modifing a group, we must deny any modifications to the member attribute,
1351 ** because the group would be inconsistent.
1354 autogroup_modify_entry( Operation *op, SlapReply *rs)
1356 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1357 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1358 autogroup_def_t *agd = agi->agi_def;
1359 autogroup_entry_t *age;
1363 if ( get_manageDSAit( op ) ) {
1364 return SLAP_CB_CONTINUE;
1367 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1368 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1370 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1371 LDAP_SUCCESS || e == NULL ) {
1372 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1373 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1374 return SLAP_CB_CONTINUE;
1377 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1378 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1379 autogroup_filter_t *agf;
1380 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1381 if ( agf->agf_anlist ) {
1383 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1384 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1385 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1386 int rc = test_filter( op, e, agf->agf_filter );
1387 if ( rc == LDAP_COMPARE_TRUE ) {
1388 age->age_mustrefresh = 1;
1395 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1396 age->age_mustrefresh = 1;
1401 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1404 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1405 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1406 return SLAP_CB_CONTINUE;
1410 for ( ; agd; agd = agd->agd_next ) {
1412 if ( value_find_ex( slap_schema.si_ad_objectClass,
1413 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1414 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1415 a->a_nvals, &agd->agd_oc->soc_cname,
1416 op->o_tmpmemctx ) == 0 )
1421 m = op->orm_modlist;
1423 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1424 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1427 for ( ; m ; m = m->sml_next ) {
1428 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1429 overlay_entry_release_ov( op, e, 0, on );
1430 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1431 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1432 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1433 return LDAP_CONSTRAINT_VIOLATION;
1440 overlay_entry_release_ov( op, e, 0, on );
1441 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1442 return SLAP_CB_CONTINUE;
1446 overlay_entry_release_ov( op, e, 0, on );
1447 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1448 return SLAP_CB_CONTINUE;
1452 ** Detect if the olddn is part of a group and so if the group should be refreshed
1455 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1457 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1458 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1459 autogroup_entry_t *age;
1462 if ( get_manageDSAit( op ) ) {
1463 return SLAP_CB_CONTINUE;
1466 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1467 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1469 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1470 LDAP_SUCCESS || e == NULL ) {
1471 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1472 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1473 return SLAP_CB_CONTINUE;
1476 /* Must check if a dn is modified */
1477 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1478 autogroup_filter_t *agf;
1479 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1480 if ( agf->agf_anlist ) {
1481 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1482 int rc = test_filter( op, e, agf->agf_filter );
1483 if ( rc == LDAP_COMPARE_TRUE ) {
1484 age->age_modrdn_olddnmodified = 1;
1491 overlay_entry_release_ov( op, e, 0, on );
1492 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1493 return SLAP_CB_CONTINUE;
1497 ** Builds a filter for searching for the
1498 ** group entries, according to the objectClass.
1501 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1505 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1507 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1508 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1509 + agd->agd_oc->soc_cname.bv_len;
1510 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1512 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1514 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1518 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1520 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1531 static ConfigDriver ag_cfgen;
1533 static ConfigTable agcfg[] = {
1534 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1535 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1536 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1537 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1538 "EQUALITY caseIgnoreMatch "
1539 "SYNTAX OMsDirectoryString "
1540 "X-ORDERED 'VALUES' )",
1543 { "autogroup-memberof-ad", "memberOf attribute",
1544 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1545 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1546 "DESC 'memberOf attribute' "
1547 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1550 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1553 static ConfigOCs agocs[] = {
1554 { "( OLcfgCtOc:2.1 "
1555 "NAME 'olcAutomaticGroups' "
1556 "DESC 'Automatic groups configuration' "
1557 "SUP olcOverlayConfig "
1560 "$ olcAGmemberOfAd "
1563 Cft_Overlay, agcfg, NULL, NULL },
1569 ag_cfgen( ConfigArgs *c )
1571 slap_overinst *on = (slap_overinst *)c->bi;
1572 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1573 autogroup_def_t *agd;
1574 autogroup_entry_t *age;
1578 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1581 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1582 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1583 agi->agi_def = NULL;
1584 agi->agi_entry = NULL;
1585 on->on_bi.bi_private = (void *)agi;
1589 age = agi->agi_entry;
1591 if ( c->op == SLAP_CONFIG_EMIT ) {
1595 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1597 char *ptr = c->cr_msg;
1599 assert(agd->agd_oc != NULL);
1600 assert(agd->agd_member_url_ad != NULL);
1601 assert(agd->agd_member_ad != NULL);
1603 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1604 SLAP_X_ORDERED_FMT "%s %s %s", i,
1605 agd->agd_oc->soc_cname.bv_val,
1606 agd->agd_member_url_ad->ad_cname.bv_val,
1607 agd->agd_member_ad->ad_cname.bv_val );
1609 bv.bv_val = c->cr_msg;
1610 bv.bv_len = ptr - bv.bv_val;
1611 value_add_one ( &c->rvalue_vals, &bv );
1616 case AG_MEMBER_OF_AD:
1617 if ( agi->agi_memberof_ad != NULL ){
1618 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1629 }else if ( c->op == LDAP_MOD_DELETE ) {
1631 autogroup_def_t *agd_next;
1632 autogroup_entry_t *age_next;
1633 autogroup_filter_t *agf = age->age_filter,
1636 for ( agd_next = agd; agd_next; agd = agd_next ) {
1637 agd_next = agd->agd_next;
1642 for ( age_next = age ; age_next ; age = age_next ) {
1643 age_next = age->age_next;
1645 ch_free( age->age_dn.bv_val );
1646 ch_free( age->age_ndn.bv_val );
1648 for( agf_next = agf ; agf_next ; agf = agf_next ){
1649 agf_next = agf->agf_next;
1651 filter_free( agf->agf_filter );
1652 ch_free( agf->agf_filterstr.bv_val );
1653 ch_free( agf->agf_dn.bv_val );
1654 ch_free( agf->agf_ndn.bv_val );
1655 anlist_free( agf->agf_anlist, 1, NULL );
1659 ldap_pvt_thread_mutex_init( &age->age_mutex );
1664 on->on_bi.bi_private = NULL;
1667 autogroup_def_t **agdp;
1668 autogroup_entry_t *age_next, *age_prev;
1669 autogroup_filter_t *agf,
1672 for ( i = 0, agdp = &agi->agi_def;
1675 if ( *agdp == NULL) {
1678 agdp = &(*agdp)->agd_next;
1682 *agdp = agd->agd_next;
1684 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1685 age_next = age->age_next;
1687 if( age->age_def == agd ) {
1688 agf = age->age_filter;
1690 ch_free( age->age_dn.bv_val );
1691 ch_free( age->age_ndn.bv_val );
1693 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1694 agf_next = agf->agf_next;
1695 filter_free( agf->agf_filter );
1696 ch_free( agf->agf_filterstr.bv_val );
1697 ch_free( agf->agf_dn.bv_val );
1698 ch_free( agf->agf_ndn.bv_val );
1699 anlist_free( agf->agf_anlist, 1, NULL );
1703 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1708 if( age_prev != NULL ) {
1709 age_prev->age_next = age_next;
1724 autogroup_def_t **agdp,
1726 ObjectClass *oc = NULL;
1727 AttributeDescription *member_url_ad = NULL,
1732 oc = oc_find( c->argv[ 1 ] );
1734 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1735 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1736 "unable to find ObjectClass \"%s\"",
1738 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1739 c->log, c->cr_msg, 0 );
1744 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1745 if( rc != LDAP_SUCCESS ) {
1746 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1747 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1748 "unable to find AttributeDescription \"%s\"",
1750 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1751 c->log, c->cr_msg, 0 );
1755 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1756 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1757 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1758 "AttributeDescription \"%s\" ",
1759 "must be of a subtype \"labeledURI\"",
1761 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1762 c->log, c->cr_msg, 0 );
1766 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1767 if( rc != LDAP_SUCCESS ) {
1768 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1769 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1770 "unable to find AttributeDescription \"%s\"",
1772 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1773 c->log, c->cr_msg, 0 );
1777 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1778 /* The same URL attribute / member attribute pair
1779 * cannot be repeated */
1781 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1782 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1783 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1784 "URL attributeDescription \"%s\" already mapped",
1785 member_ad->ad_cname.bv_val );
1786 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1787 c->log, c->cr_msg, 0 );
1788 /* return 1; //warning*/
1792 if ( c->valx > 0 ) {
1795 for ( i = 0, agdp = &agi->agi_def ;
1798 if ( *agdp == NULL ) {
1799 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1800 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1801 "invalid index {%d}",
1803 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1804 c->log, c->cr_msg, 0 );
1808 agdp = &(*agdp)->agd_next;
1813 for ( agdp = &agi->agi_def; *agdp;
1814 agdp = &(*agdp)->agd_next )
1818 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1820 (*agdp)->agd_oc = oc;
1821 (*agdp)->agd_member_url_ad = member_url_ad;
1822 (*agdp)->agd_member_ad = member_ad;
1823 (*agdp)->agd_next = agd_next;
1827 case AG_MEMBER_OF_AD: {
1828 AttributeDescription *memberof_ad = NULL;
1831 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1832 if( rc != LDAP_SUCCESS ) {
1833 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1834 "\"autogroup-memberof-ad <memberof-ad>\": "
1835 "unable to find AttributeDescription \"%s\"",
1837 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1838 c->log, c->cr_msg, 0 );
1842 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1843 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1845 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1846 "memberof attribute=\"%s\" must either "
1847 "have DN (%s) or nameUID (%s) syntax",
1848 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1849 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1850 c->log, c->cr_msg, 0 );
1854 agi->agi_memberof_ad = memberof_ad;
1866 extern int slapMode;
1869 ** Do a search for all the groups in the
1870 ** database, and add them to out internal list.
1877 slap_overinst *on = (slap_overinst *) be->bd_info;
1878 autogroup_info_t *agi = on->on_bi.bi_private;
1879 autogroup_def_t *agd;
1882 slap_callback cb = { 0 };
1884 void *thrctx = ldap_pvt_thread_pool_context();
1885 Connection conn = { 0 };
1886 OperationBuffer opbuf;
1888 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1890 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1894 connection_fake_init( &conn, &opbuf, thrctx );
1897 op->ors_attrsonly = 0;
1898 op->o_tag = LDAP_REQ_SEARCH;
1899 op->o_dn = be->be_rootdn;
1900 op->o_ndn = be->be_rootndn;
1902 op->o_req_dn = be->be_suffix[0];
1903 op->o_req_ndn = be->be_nsuffix[0];
1905 op->ors_scope = LDAP_SCOPE_SUBTREE;
1906 op->ors_deref = LDAP_DEREF_NEVER;
1907 op->ors_limit = NULL;
1908 op->ors_tlimit = SLAP_NO_LIMIT;
1909 op->ors_slimit = SLAP_NO_LIMIT;
1910 op->ors_attrs = slap_anlist_no_attrs;
1913 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1916 cb.sc_private = &ags;
1917 cb.sc_response = autogroup_group_add_cb;
1918 cb.sc_cleanup = NULL;
1921 op->o_callback = &cb;
1923 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1924 SlapReply rs = { REP_RESULT };
1926 autogroup_build_def_filter(agd, op);
1930 op->o_bd->be_search( op, &rs );
1932 filter_free_x( op, op->ors_filter, 1 );
1933 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1936 if( ! agi->agi_memberof_ad ){
1938 const char *text = NULL;
1940 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1941 if ( rc != LDAP_SUCCESS ) {
1942 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1943 "unable to find attribute=\"%s\": %s (%d)\n",
1944 SLAPD_MEMBEROF_ATTR, text, rc );
1957 slap_overinst *on = (slap_overinst *) be->bd_info;
1959 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1961 if ( on->on_bi.bi_private ) {
1962 autogroup_info_t *agi = on->on_bi.bi_private;
1963 autogroup_entry_t *age = agi->agi_entry,
1965 autogroup_filter_t *agf, *agf_next;
1967 for ( age_next = age; age_next; age = age_next ) {
1968 age_next = age->age_next;
1970 ch_free( age->age_dn.bv_val );
1971 ch_free( age->age_ndn.bv_val );
1973 agf = age->age_filter;
1975 for ( agf_next = agf; agf_next; agf = agf_next ) {
1976 agf_next = agf->agf_next;
1978 filter_free( agf->agf_filter );
1979 ch_free( agf->agf_filterstr.bv_val );
1980 ch_free( agf->agf_dn.bv_val );
1981 ch_free( agf->agf_ndn.bv_val );
1982 anlist_free( agf->agf_anlist, 1, NULL );
1986 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1995 autogroup_db_destroy(
1999 slap_overinst *on = (slap_overinst *) be->bd_info;
2001 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2003 if ( on->on_bi.bi_private ) {
2004 autogroup_info_t *agi = on->on_bi.bi_private;
2005 autogroup_def_t *agd = agi->agi_def,
2008 for ( agd_next = agd; agd_next; agd = agd_next ) {
2009 agd_next = agd->agd_next;
2014 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2021 static slap_overinst autogroup = { { NULL } };
2025 autogroup_initialize(void)
2028 autogroup.on_bi.bi_type = "autogroup";
2030 autogroup.on_bi.bi_db_open = autogroup_db_open;
2031 autogroup.on_bi.bi_db_close = autogroup_db_close;
2032 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2034 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2035 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2036 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2037 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2039 autogroup.on_response = autogroup_response;
2041 autogroup.on_bi.bi_cf_ocs = agocs;
2043 rc = config_register_schema( agcfg, agocs );
2048 return overlay_register( &autogroup );
2052 init_module( int argc, char *argv[] )
2054 return autogroup_initialize();