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 assert( op->o_tag == LDAP_REQ_SEARCH );
329 if ( rs->sr_type == REP_SEARCH ) {
330 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
331 autogroup_entry_t *age = agg->agg_group;
332 autogroup_filter_t *agf = agg->agg_filter;
334 const char *text = NULL;
336 struct berval *vals, *nvals;
339 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
340 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
342 if ( agf->agf_anlist ) {
343 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
346 nvals = attr->a_nvals;
347 numvals = attr->a_numvals;
353 struct berval lvals[ 2 ], lnvals[ 2 ];
354 lvals[ 0 ] = rs->sr_entry->e_name;
355 BER_BVZERO( &lvals[ 1 ] );
356 lnvals[ 0 ] = rs->sr_entry->e_nname;
357 BER_BVZERO( &lnvals[ 1 ] );
363 mod.sm_op = LDAP_MOD_ADD;
364 mod.sm_desc = age->age_def->agd_member_ad;
365 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
366 mod.sm_values = vals;
367 mod.sm_nvalues = nvals;
368 mod.sm_numvals = numvals;
370 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
377 ** Callback used to add entries to a group, which is already in the database.
378 ** (used in on_response)
379 ** The group is passed in autogroup_ga_t->agg_group
383 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
385 assert( op->o_tag == LDAP_REQ_SEARCH );
387 if ( rs->sr_type == REP_SEARCH ) {
388 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
389 autogroup_entry_t *age = agg->agg_group;
390 autogroup_filter_t *agf = agg->agg_filter;
391 Modifications *modlist;
392 struct berval *vals, *nvals;
395 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
396 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
398 if ( agf->agf_anlist ) {
399 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
402 nvals = attr->a_nvals;
403 numvals = attr->a_numvals;
409 struct berval lvals[ 2 ], lnvals[ 2 ];
410 lvals[ 0 ] = rs->sr_entry->e_name;
411 BER_BVZERO( &lvals[ 1 ] );
412 lnvals[ 0 ] = rs->sr_entry->e_nname;
413 BER_BVZERO( &lnvals[ 1 ] );
420 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
422 modlist->sml_op = LDAP_MOD_ADD;
423 modlist->sml_desc = age->age_def->agd_member_ad;
424 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
426 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
427 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
428 modlist->sml_numvals = numvals;
430 modlist->sml_flags = SLAP_MOD_INTERNAL;
431 modlist->sml_next = NULL;
433 if ( agg->agg_mod == NULL ) {
434 agg->agg_mod = modlist;
435 agg->agg_mod_last = modlist;
437 agg->agg_mod_last->sml_next = modlist;
438 agg->agg_mod_last = modlist;
449 ** Adds all entries matching the passed filter to the specified group.
450 ** If modify == 1, then we modify the group's entry in the database using be_modify.
451 ** If modify == 0, then, we must supply a rw entry for the group,
452 ** because we only modify the entry, without calling be_modify.
453 ** e - the group entry, to which the members will be added
458 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
460 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
462 SlapReply rs = { REP_SEARCH };
463 slap_callback cb = { 0 };
464 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
467 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
468 age->age_dn.bv_val, 0, 0);
471 o.o_tag = LDAP_REQ_SEARCH;
473 o.o_req_dn = agf->agf_dn;
474 o.o_req_ndn = agf->agf_ndn;
476 o.ors_filterstr = agf->agf_filterstr;
477 o.ors_filter = agf->agf_filter;
479 o.ors_scope = agf->agf_scope;
480 o.ors_deref = LDAP_DEREF_NEVER;
482 o.ors_tlimit = SLAP_NO_LIMIT;
483 o.ors_slimit = SLAP_NO_LIMIT;
484 o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
487 agg.agg_filter = agf;
489 agg.agg_mod_last = NULL;
491 cb.sc_private = &agg;
494 cb.sc_response = autogroup_member_search_modify_cb;
496 cb.sc_response = autogroup_member_search_cb;
499 cb.sc_cleanup = NULL;
504 o.o_bd->bd_info = (BackendInfo *)on->on_info;
505 op->o_bd->be_search( &o, &rs );
506 o.o_bd->bd_info = (BackendInfo *)on;
508 if ( modify == 1 && agg.agg_mod ) {
509 rs_reinit( &rs, REP_RESULT );
512 o.o_callback = &null_cb;
513 o.o_tag = LDAP_REQ_MODIFY;
514 o.orm_modlist = agg.agg_mod;
515 o.o_req_dn = age->age_dn;
516 o.o_req_ndn = age->age_ndn;
517 o.o_relax = SLAP_CONTROL_CRITICAL;
518 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
519 o.o_permissive_modify = 1;
521 o.o_bd->bd_info = (BackendInfo *)on->on_info;
522 (void)op->o_bd->be_modify( &o, &rs );
523 o.o_bd->bd_info = (BackendInfo *)on;
525 slap_mods_free(agg.agg_mod, 1);
532 ** Adds a group to the internal list from the passed entry.
533 ** scan specifies whether to add all maching members to the group.
534 ** modify specifies whether to modify the given group entry (when modify == 0),
535 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
536 ** agi - pointer to the groups and the attribute definitions
537 ** agd - the attribute definition of the added group
538 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
539 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
542 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
544 autogroup_entry_t **agep = &agi->agi_entry;
545 autogroup_filter_t *agf, *agf_prev = NULL;
546 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
547 LDAPURLDesc *lud = NULL;
550 int rc = 0, match = 1, null_entry = 0;
553 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
554 LDAP_SUCCESS || e == NULL ) {
555 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
562 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
563 e->e_name.bv_val, 0, 0);
565 if ( agi->agi_entry != NULL ) {
566 for ( ; *agep ; agep = &(*agep)->age_next ) {
567 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
569 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
577 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
578 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
579 (*agep)->age_def = agd;
580 (*agep)->age_filter = NULL;
581 (*agep)->age_mustrefresh = 0;
582 (*agep)->age_modrdn_olddnmodified = 0;
584 ber_dupbv( &(*agep)->age_dn, &e->e_name );
585 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
587 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
589 if ( null_entry == 1 ) {
591 overlay_entry_release_ov( op, e, 0, on );
595 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
597 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
599 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
601 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
602 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
608 agf->agf_scope = lud->lud_scope;
610 if ( lud->lud_dn == NULL ) {
611 BER_BVSTR( &dn, "" );
613 ber_str2bv( lud->lud_dn, 0, 0, &dn );
616 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
617 if ( rc != LDAP_SUCCESS ) {
618 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
623 if ( lud->lud_filter != NULL ) {
624 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
625 agf->agf_filter = str2filter( lud->lud_filter );
628 if ( lud->lud_attrs != NULL ) {
631 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
636 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: to much attributes specified in url <%s>\n",
639 ldap_free_urldesc( lud );
643 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
645 if ( agf->agf_anlist == NULL ) {
646 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
647 lud->lud_attrs[0], 0, 0 );
649 ldap_free_urldesc( lud );
655 agf->agf_next = NULL;
658 if( (*agep)->age_filter == NULL ) {
659 (*agep)->age_filter = agf;
662 if( agf_prev != NULL ) {
663 agf_prev->agf_next = agf;
669 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
672 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
673 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
675 ldap_free_urldesc( lud );
682 ldap_free_urldesc( lud );
687 if ( null_entry == 1 ) {
694 ** Used when opening the database to add all existing
695 ** groups from the database to our internal list.
698 autogroup_group_add_cb( Operation *op, SlapReply *rs )
700 assert( op->o_tag == LDAP_REQ_SEARCH );
702 if ( rs->sr_type == REP_SEARCH ) {
703 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
705 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
706 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
708 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
716 ** When adding a group, we first strip any existing members,
717 ** and add all which match the filters ourselfs.
720 autogroup_add_entry( Operation *op, SlapReply *rs)
722 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
723 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
724 autogroup_def_t *agd = agi->agi_def;
725 autogroup_entry_t *age = agi->agi_entry;
726 autogroup_filter_t *agf;
729 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
730 op->ora_e->e_name.bv_val, 0, 0);
732 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
734 /* Check if it's a group. */
735 for ( ; agd ; agd = agd->agd_next ) {
736 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
738 const char *text = NULL;
741 mod.sm_op = LDAP_MOD_DELETE;
742 mod.sm_desc = agd->agd_member_ad;
743 mod.sm_type = agd->agd_member_ad->ad_cname;
744 mod.sm_values = NULL;
745 mod.sm_nvalues = NULL;
747 /* We don't want any member attributes added by the user. */
748 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
750 autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
751 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
752 return SLAP_CB_CONTINUE;
756 for ( ; age ; age = age->age_next ) {
757 ldap_pvt_thread_mutex_lock( &age->age_mutex );
759 /* Check if any of the filters are the suffix to the entry DN.
760 If yes, we can test that filter against the entry. */
762 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
763 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
764 rc = test_filter( op, op->ora_e, agf->agf_filter );
765 if ( rc == LDAP_COMPARE_TRUE ) {
766 if ( agf->agf_anlist ) {
767 autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
769 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
775 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
778 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
780 return SLAP_CB_CONTINUE;
784 ** agi - internal group and attribute definitions list
785 ** e - the group to remove from the internal list
788 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
790 autogroup_entry_t *age = agi->agi_entry,
795 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
796 age->age_dn.bv_val, 0, 0);
798 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
799 age_next = age->age_next;
802 autogroup_filter_t *agf = age->age_filter,
805 if ( age_prev != NULL ) {
806 age_prev->age_next = age_next;
808 agi->agi_entry = NULL;
811 ch_free( age->age_dn.bv_val );
812 ch_free( age->age_ndn.bv_val );
814 for( agf_next = agf ; agf_next ; agf = agf_next ){
815 agf_next = agf->agf_next;
817 filter_free( agf->agf_filter );
818 ch_free( agf->agf_filterstr.bv_val );
819 ch_free( agf->agf_dn.bv_val );
820 ch_free( agf->agf_ndn.bv_val );
821 anlist_free( agf->agf_anlist, 1, NULL );
825 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
826 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
835 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
842 autogroup_delete_entry( Operation *op, SlapReply *rs)
844 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
845 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
846 autogroup_entry_t *age = agi->agi_entry,
847 *age_prev, *age_next;
848 autogroup_filter_t *agf;
850 int matched_group = 0, rc = 0;
852 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
854 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
856 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
857 LDAP_SUCCESS || e == NULL ) {
858 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
859 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
860 return SLAP_CB_CONTINUE;
863 /* Check if the entry to be deleted is one of our groups. */
864 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
865 ldap_pvt_thread_mutex_lock( &age->age_mutex );
866 age_next = age->age_next;
868 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
873 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
876 autogroup_delete_group( agi, age );
881 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
884 if ( matched_group == 1 ) {
885 overlay_entry_release_ov( op, e, 0, on );
886 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
887 return SLAP_CB_CONTINUE;
890 /* Check if the entry matches any of the groups.
891 If yes, we can delete the entry from that group. */
893 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
894 ldap_pvt_thread_mutex_lock( &age->age_mutex );
896 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
897 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
898 rc = test_filter( op, e, agf->agf_filter );
899 if ( rc == LDAP_COMPARE_TRUE ) {
900 /* If the attribute is retrieved from the entry, we don't know what to delete
901 ** So the group must be entirely refreshed
902 ** But the refresh can't be done now because the entry is not deleted
903 ** So the group is marked as mustrefresh
905 if ( agf->agf_anlist ) {
906 age->age_mustrefresh = 1;
908 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
914 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
917 overlay_entry_release_ov( op, e, 0, on );
918 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
920 return SLAP_CB_CONTINUE;
924 autogroup_response( Operation *op, SlapReply *rs )
926 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
927 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
928 autogroup_def_t *agd = agi->agi_def;
929 autogroup_entry_t *age = agi->agi_entry;
930 autogroup_filter_t *agf;
931 BerValue new_dn, new_ndn, pdn;
934 int is_olddn, is_newdn, is_value_refresh, dn_equal;
936 /* Handle all cases where a refresh of the group is needed */
937 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
938 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
940 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
942 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
943 /* Request detected that the group must be refreshed */
945 ldap_pvt_thread_mutex_lock( &age->age_mutex );
947 if ( age->age_mustrefresh ) {
948 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
950 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
951 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
955 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
958 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
961 if ( op->o_tag == LDAP_REQ_MODRDN ) {
962 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
964 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
966 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
968 if ( op->oq_modrdn.rs_newSup ) {
969 pdn = *op->oq_modrdn.rs_newSup;
971 dnParent( &op->o_req_dn, &pdn );
973 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
975 if ( op->oq_modrdn.rs_nnewSup ) {
976 pdn = *op->oq_modrdn.rs_nnewSup;
978 dnParent( &op->o_req_ndn, &pdn );
980 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
982 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
984 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
986 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
987 LDAP_SUCCESS || e == NULL ) {
988 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
989 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
990 return SLAP_CB_CONTINUE;
993 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
997 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
998 overlay_entry_release_ov( op, e, 0, on );
999 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1000 return SLAP_CB_CONTINUE;
1004 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1005 for ( ; agd; agd = agd->agd_next ) {
1007 if ( value_find_ex( slap_schema.si_ad_objectClass,
1008 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1009 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1010 a->a_nvals, &agd->agd_oc->soc_cname,
1011 op->o_tmpmemctx ) == 0 )
1013 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1016 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1018 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1019 ber_dupbv( &age->age_dn, &new_dn );
1020 ber_dupbv( &age->age_ndn, &new_ndn );
1022 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1023 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1024 overlay_entry_release_ov( op, e, 0, on );
1025 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1026 return SLAP_CB_CONTINUE;
1034 1. check if the orginal entry's DN is in the group.
1035 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1037 If 1 and 2 are both false, we do nothing.
1038 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1039 If 1 is false, and 2 is true, we check the entry against the group's filters,
1040 and add it's DN to the group.
1041 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1043 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1046 is_value_refresh = 0;
1049 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1051 if ( age->age_filter && age->age_filter->agf_anlist ) {
1052 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1058 if ( age->age_modrdn_olddnmodified ) {
1059 /* Resquest already marked this group to be updated */
1061 is_value_refresh = 1;
1062 age->age_modrdn_olddnmodified = 0;
1065 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1066 LDAP_SUCCESS || group == NULL ) {
1067 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1069 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1070 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1072 overlay_entry_release_ov( op, e, 0, on );
1073 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1074 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1075 return SLAP_CB_CONTINUE;
1078 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1081 if ( value_find_ex( age->age_def->agd_member_ad,
1082 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1083 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1084 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1091 overlay_entry_release_ov( op, group, 0, on );
1095 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1096 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1097 /* TODO: should retest filter as it could imply conditions on the dn */
1104 if ( is_value_refresh ) {
1105 if ( is_olddn != is_newdn ) {
1107 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1109 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1110 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1113 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1116 if ( is_olddn == 1 && is_newdn == 0 ) {
1118 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1120 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1122 if ( is_olddn == 0 && is_newdn == 1 ) {
1123 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1124 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1126 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1128 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1133 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1136 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1138 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1139 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1143 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1144 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1148 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1151 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1152 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1154 overlay_entry_release_ov( op, e, 0, on );
1156 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1160 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1161 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1162 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1164 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1166 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1167 LDAP_SUCCESS || e == NULL ) {
1168 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1169 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1170 return SLAP_CB_CONTINUE;
1173 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1177 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1178 overlay_entry_release_ov( op, e, 0, on );
1179 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1180 return SLAP_CB_CONTINUE;
1183 /* If we modify a group's memberURL, we have to delete all of it's members,
1184 and add them anew, because we cannot tell from which memberURL a member was added. */
1185 for ( ; agd; agd = agd->agd_next ) {
1187 if ( value_find_ex( slap_schema.si_ad_objectClass,
1188 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1189 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1190 a->a_nvals, &agd->agd_oc->soc_cname,
1191 op->o_tmpmemctx ) == 0 )
1196 m = op->orm_modlist;
1198 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1199 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1201 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1204 for ( ; m ; m = m->sml_next ) {
1205 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1206 autogroup_def_t *group_agd = age->age_def;
1207 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1208 op->o_req_dn.bv_val, 0, 0);
1210 overlay_entry_release_ov( op, e, 0, on );
1212 autogroup_delete_member_from_group( op, NULL, NULL, age );
1213 autogroup_delete_group( agi, age );
1215 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1217 overlay_entry_release_ov( op, e, 0, on );
1218 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1219 return SLAP_CB_CONTINUE;
1223 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1227 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1230 overlay_entry_release_ov( op, e, 0, on );
1231 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1232 return SLAP_CB_CONTINUE;
1236 /* When modifing any of the attributes of an entry, we must
1237 check if the entry is in any of our groups, and if
1238 the modified entry maches any of the filters of that group.
1240 If the entry exists in a group, but the modified attributes do
1241 not match any of the group's filters, we delete the entry from that group.
1242 If the entry doesn't exist in a group, but matches a filter,
1243 we add it to that group.
1245 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1250 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1252 if ( age->age_filter && age->age_filter->agf_anlist ) {
1253 ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1259 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1260 LDAP_SUCCESS || group == NULL ) {
1261 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1262 age->age_dn.bv_val, 0, 0);
1264 overlay_entry_release_ov( op, e, 0, on );
1265 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1266 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1267 return SLAP_CB_CONTINUE;
1270 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1273 if ( value_find_ex( age->age_def->agd_member_ad,
1274 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1275 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1276 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1283 overlay_entry_release_ov( op, group, 0, on );
1285 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1286 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1287 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1294 if ( is_olddn == 1 && is_newdn == 0 ) {
1296 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1298 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1300 if ( is_olddn == 0 && is_newdn == 1 ) {
1302 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1304 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1307 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1310 overlay_entry_release_ov( op, e, 0, on );
1312 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1316 return SLAP_CB_CONTINUE;
1320 ** Detect if filter contains a memberOf check for dn
1323 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1326 if ( f == NULL ) return 0;
1328 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1329 case LDAP_FILTER_AND:
1330 case LDAP_FILTER_OR:
1331 case LDAP_FILTER_NOT:
1332 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1333 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1336 case LDAP_FILTER_EQUALITY:
1337 result = ( f->f_ava->aa_desc == memberof_ad &&
1338 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1348 ** When modifing a group, we must deny any modifications to the member attribute,
1349 ** because the group would be inconsistent.
1352 autogroup_modify_entry( Operation *op, SlapReply *rs)
1354 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1355 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1356 autogroup_def_t *agd = agi->agi_def;
1357 autogroup_entry_t *age = agi->agi_entry;
1361 if ( get_manageDSAit( op ) ) {
1362 return SLAP_CB_CONTINUE;
1365 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1366 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1368 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1369 LDAP_SUCCESS || e == NULL ) {
1370 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1371 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1372 return SLAP_CB_CONTINUE;
1375 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1376 for ( ; age ; age = age->age_next ) {
1377 autogroup_filter_t *agf;
1378 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1379 if ( agf->agf_anlist ) {
1381 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1382 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1383 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1384 int rc = test_filter( op, e, agf->agf_filter );
1385 if ( rc == LDAP_COMPARE_TRUE ) {
1386 age->age_mustrefresh = 1;
1393 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1394 age->age_mustrefresh = 1;
1399 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1402 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1403 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1404 return SLAP_CB_CONTINUE;
1408 for ( ; agd; agd = agd->agd_next ) {
1410 if ( value_find_ex( slap_schema.si_ad_objectClass,
1411 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1412 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1413 a->a_nvals, &agd->agd_oc->soc_cname,
1414 op->o_tmpmemctx ) == 0 )
1419 m = op->orm_modlist;
1421 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1422 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1425 for ( ; m ; m = m->sml_next ) {
1426 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1427 overlay_entry_release_ov( op, e, 0, on );
1428 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1429 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1430 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1431 return LDAP_CONSTRAINT_VIOLATION;
1438 overlay_entry_release_ov( op, e, 0, on );
1439 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1440 return SLAP_CB_CONTINUE;
1444 overlay_entry_release_ov( op, e, 0, on );
1445 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1446 return SLAP_CB_CONTINUE;
1450 ** Detect if the olddn is part of a group and so if the group should be refreshed
1453 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1455 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1456 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1457 autogroup_entry_t *age = agi->agi_entry;
1460 if ( get_manageDSAit( op ) ) {
1461 return SLAP_CB_CONTINUE;
1464 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1465 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1467 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1468 LDAP_SUCCESS || e == NULL ) {
1469 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1470 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1471 return SLAP_CB_CONTINUE;
1474 /* Must check if a dn is modified */
1475 for ( ; age ; age = age->age_next ) {
1476 autogroup_filter_t *agf;
1477 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1478 if ( agf->agf_anlist ) {
1479 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1480 int rc = test_filter( op, e, agf->agf_filter );
1481 if ( rc == LDAP_COMPARE_TRUE ) {
1482 age->age_modrdn_olddnmodified = 1;
1489 overlay_entry_release_ov( op, e, 0, on );
1490 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1491 return SLAP_CB_CONTINUE;
1495 ** Builds a filter for searching for the
1496 ** group entries, according to the objectClass.
1499 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1503 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1505 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1506 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1507 + agd->agd_oc->soc_cname.bv_len;
1508 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1510 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1512 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1516 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1518 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1529 static ConfigDriver ag_cfgen;
1531 static ConfigTable agcfg[] = {
1532 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1533 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1534 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1535 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1536 "EQUALITY caseIgnoreMatch "
1537 "SYNTAX OMsDirectoryString "
1538 "X-ORDERED 'VALUES' )",
1541 { "autogroup-memberof-ad", "memberOf attribute",
1542 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1543 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1544 "DESC 'memberOf attribute' "
1545 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1548 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1551 static ConfigOCs agocs[] = {
1552 { "( OLcfgCtOc:2.1 "
1553 "NAME 'olcAutomaticGroups' "
1554 "DESC 'Automatic groups configuration' "
1555 "SUP olcOverlayConfig "
1558 "$ olcAGmemberOfAd "
1561 Cft_Overlay, agcfg, NULL, NULL },
1567 ag_cfgen( ConfigArgs *c )
1569 slap_overinst *on = (slap_overinst *)c->bi;
1570 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1571 autogroup_def_t *agd;
1572 autogroup_entry_t *age;
1576 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1579 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1580 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1581 agi->agi_def = NULL;
1582 agi->agi_entry = NULL;
1583 on->on_bi.bi_private = (void *)agi;
1587 age = agi->agi_entry;
1589 if ( c->op == SLAP_CONFIG_EMIT ) {
1591 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
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 );
1627 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1631 }else if ( c->op == LDAP_MOD_DELETE ) {
1633 autogroup_def_t *agd_next;
1634 autogroup_entry_t *age_next;
1635 autogroup_filter_t *agf = age->age_filter,
1638 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1640 for ( agd_next = agd; agd_next; agd = agd_next ) {
1641 agd_next = agd->agd_next;
1646 for ( age_next = age ; age_next ; age = age_next ) {
1647 age_next = age->age_next;
1649 ch_free( age->age_dn.bv_val );
1650 ch_free( age->age_ndn.bv_val );
1652 for( agf_next = agf ; agf_next ; agf = agf_next ){
1653 agf_next = agf->agf_next;
1655 filter_free( agf->agf_filter );
1656 ch_free( agf->agf_filterstr.bv_val );
1657 ch_free( agf->agf_dn.bv_val );
1658 ch_free( agf->agf_ndn.bv_val );
1659 anlist_free( agf->agf_anlist, 1, NULL );
1663 ldap_pvt_thread_mutex_init( &age->age_mutex );
1667 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1669 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
1671 on->on_bi.bi_private = NULL;
1674 autogroup_def_t **agdp;
1675 autogroup_entry_t *age_next, *age_prev;
1676 autogroup_filter_t *agf,
1679 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1681 for ( i = 0, agdp = &agi->agi_def;
1684 if ( *agdp == NULL) {
1687 agdp = &(*agdp)->agd_next;
1691 *agdp = agd->agd_next;
1693 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1694 age_next = age->age_next;
1696 if( age->age_def == agd ) {
1697 agf = age->age_filter;
1699 ch_free( age->age_dn.bv_val );
1700 ch_free( age->age_ndn.bv_val );
1702 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1703 agf_next = agf->agf_next;
1704 filter_free( agf->agf_filter );
1705 ch_free( agf->agf_filterstr.bv_val );
1706 ch_free( agf->agf_dn.bv_val );
1707 ch_free( agf->agf_ndn.bv_val );
1708 anlist_free( agf->agf_anlist, 1, NULL );
1712 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1717 if( age_prev != NULL ) {
1718 age_prev->age_next = age_next;
1725 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1734 autogroup_def_t **agdp,
1736 ObjectClass *oc = NULL;
1737 AttributeDescription *member_url_ad = NULL,
1742 oc = oc_find( c->argv[ 1 ] );
1744 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1745 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1746 "unable to find ObjectClass \"%s\"",
1748 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1749 c->log, c->cr_msg, 0 );
1754 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1755 if( rc != LDAP_SUCCESS ) {
1756 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1757 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1758 "unable to find AttributeDescription \"%s\"",
1760 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1761 c->log, c->cr_msg, 0 );
1765 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1766 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1767 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1768 "AttributeDescription \"%s\" ",
1769 "must be of a subtype \"labeledURI\"",
1771 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1772 c->log, c->cr_msg, 0 );
1776 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1777 if( rc != LDAP_SUCCESS ) {
1778 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1779 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1780 "unable to find AttributeDescription \"%s\"",
1782 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1783 c->log, c->cr_msg, 0 );
1787 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1789 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1790 /* The same URL attribute / member attribute pair
1791 * cannot be repeated */
1793 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1794 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1795 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1796 "URL attributeDescription \"%s\" already mapped",
1797 member_ad->ad_cname.bv_val );
1798 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1799 c->log, c->cr_msg, 0 );
1800 /* return 1; //warning*/
1804 if ( c->valx > 0 ) {
1807 for ( i = 0, agdp = &agi->agi_def ;
1810 if ( *agdp == NULL ) {
1811 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1812 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1813 "invalid index {%d}",
1815 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1816 c->log, c->cr_msg, 0 );
1818 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1821 agdp = &(*agdp)->agd_next;
1826 for ( agdp = &agi->agi_def; *agdp;
1827 agdp = &(*agdp)->agd_next )
1831 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1833 (*agdp)->agd_oc = oc;
1834 (*agdp)->agd_member_url_ad = member_url_ad;
1835 (*agdp)->agd_member_ad = member_ad;
1836 (*agdp)->agd_next = agd_next;
1838 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1842 case AG_MEMBER_OF_AD: {
1843 AttributeDescription *memberof_ad = NULL;
1846 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1847 if( rc != LDAP_SUCCESS ) {
1848 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1849 "\"autogroup-memberof-ad <memberof-ad>\": "
1850 "unable to find AttributeDescription \"%s\"",
1852 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1853 c->log, c->cr_msg, 0 );
1857 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1858 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1860 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1861 "memberof attribute=\"%s\" must either "
1862 "have DN (%s) or nameUID (%s) syntax",
1863 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1864 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1865 c->log, c->cr_msg, 0 );
1869 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1871 agi->agi_memberof_ad = memberof_ad;
1873 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1885 extern int slapMode;
1888 ** Do a search for all the groups in the
1889 ** database, and add them to out internal list.
1896 slap_overinst *on = (slap_overinst *) be->bd_info;
1897 autogroup_info_t *agi = on->on_bi.bi_private;
1898 autogroup_def_t *agd;
1901 slap_callback cb = { 0 };
1903 void *thrctx = ldap_pvt_thread_pool_context();
1904 Connection conn = { 0 };
1905 OperationBuffer opbuf;
1907 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1909 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1913 connection_fake_init( &conn, &opbuf, thrctx );
1916 op->ors_attrsonly = 0;
1917 op->o_tag = LDAP_REQ_SEARCH;
1918 op->o_dn = be->be_rootdn;
1919 op->o_ndn = be->be_rootndn;
1921 op->o_req_dn = be->be_suffix[0];
1922 op->o_req_ndn = be->be_nsuffix[0];
1924 op->ors_scope = LDAP_SCOPE_SUBTREE;
1925 op->ors_deref = LDAP_DEREF_NEVER;
1926 op->ors_limit = NULL;
1927 op->ors_tlimit = SLAP_NO_LIMIT;
1928 op->ors_slimit = SLAP_NO_LIMIT;
1929 op->ors_attrs = slap_anlist_no_attrs;
1932 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1935 cb.sc_private = &ags;
1936 cb.sc_response = autogroup_group_add_cb;
1937 cb.sc_cleanup = NULL;
1940 op->o_callback = &cb;
1942 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1943 SlapReply rs = { REP_RESULT };
1945 autogroup_build_def_filter(agd, op);
1949 op->o_bd->be_search( op, &rs );
1951 filter_free_x( op, op->ors_filter, 1 );
1952 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1955 if( ! agi->agi_memberof_ad ){
1957 const char *text = NULL;
1959 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1960 if ( rc != LDAP_SUCCESS ) {
1961 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1962 "unable to find attribute=\"%s\": %s (%d)\n",
1963 SLAPD_MEMBEROF_ATTR, text, rc );
1976 slap_overinst *on = (slap_overinst *) be->bd_info;
1978 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1980 if ( on->on_bi.bi_private ) {
1981 autogroup_info_t *agi = on->on_bi.bi_private;
1982 autogroup_entry_t *age = agi->agi_entry,
1984 autogroup_filter_t *agf, *agf_next;
1986 for ( age_next = age; age_next; age = age_next ) {
1987 age_next = age->age_next;
1989 ch_free( age->age_dn.bv_val );
1990 ch_free( age->age_ndn.bv_val );
1992 agf = age->age_filter;
1994 for ( agf_next = agf; agf_next; agf = agf_next ) {
1995 agf_next = agf->agf_next;
1997 filter_free( agf->agf_filter );
1998 ch_free( agf->agf_filterstr.bv_val );
1999 ch_free( agf->agf_dn.bv_val );
2000 ch_free( agf->agf_ndn.bv_val );
2001 anlist_free( agf->agf_anlist, 1, NULL );
2005 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2014 autogroup_db_destroy(
2018 slap_overinst *on = (slap_overinst *) be->bd_info;
2020 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2022 if ( on->on_bi.bi_private ) {
2023 autogroup_info_t *agi = on->on_bi.bi_private;
2024 autogroup_def_t *agd = agi->agi_def,
2027 for ( agd_next = agd; agd_next; agd = agd_next ) {
2028 agd_next = agd->agd_next;
2033 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2040 static slap_overinst autogroup = { { NULL } };
2044 autogroup_initialize(void)
2047 autogroup.on_bi.bi_type = "autogroup";
2049 autogroup.on_bi.bi_db_open = autogroup_db_open;
2050 autogroup.on_bi.bi_db_close = autogroup_db_close;
2051 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2053 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2054 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2055 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2056 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2058 autogroup.on_response = autogroup_response;
2060 autogroup.on_bi.bi_cf_ocs = agocs;
2062 rc = config_register_schema( agcfg, agocs );
2067 return overlay_register( &autogroup );
2071 init_module( int argc, char *argv[] )
2073 return autogroup_initialize();