autogroup.c 61.6 KB
Newer Older
1
2
/* autogroup.c - automatic group overlay */
/* $OpenLDAP$ */
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 2007-2020 The OpenLDAP Foundation.
6
 * Portions Copyright 2007 Michał Szulczyński.
7
 * Portions Copyright 2009 Howard Chu.
8
9
10
11
12
13
14
15
16
17
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
18
19
20
21
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Michał Szulczyński for inclusion in
 * OpenLDAP Software.  Additional significant contributors include:
 *   Howard Chu
22
23
24
 *   Raphael Ouazana
 *   Norbert Pueschel
 *   Christian Manal
25
 */
26
27
28
29
30
31
32
33
34
35
36

#include "portable.h"

#include <stdio.h>

#include <ac/string.h>

#include "slap.h"
#include "config.h"
#include "lutil.h"

37
38
39
40
#ifndef SLAPD_MEMBEROF_ATTR
#define	SLAPD_MEMBEROF_ATTR	"memberOf"
#endif

Howard Chu's avatar
Howard Chu committed
41
42
static slap_overinst	autogroup;

43
44
45
46
47
48
49
/* Filter represents the memberURL of a group. */
typedef struct autogroup_filter_t {
	struct berval			agf_dn;	/* The base DN in memberURL */
	struct berval			agf_ndn;
	struct berval			agf_filterstr;
	Filter				*agf_filter;
	int				agf_scope;
50
	AttributeName			*agf_anlist;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
	struct autogroup_filter_t	*agf_next;
} autogroup_filter_t;

/* Description of group attributes. */
typedef struct autogroup_def_t {
	ObjectClass		*agd_oc;
	AttributeDescription	*agd_member_url_ad;
	AttributeDescription	*agd_member_ad;
	struct autogroup_def_t	*agd_next;
} autogroup_def_t;

/* Represents the group entry. */
typedef struct autogroup_entry_t {
	BerValue		age_dn;
	BerValue		age_ndn;
	autogroup_filter_t	*age_filter; /* List of filters made from memberURLs */
	autogroup_def_t		*age_def; /* Attribute definition */
	ldap_pvt_thread_mutex_t age_mutex;
69
70
	int			age_mustrefresh; /* Defined in request to refresh in response */
	int			age_modrdn_olddnmodified; /* Defined in request to refresh in response */
71
72
73
74
75
76
77
	struct autogroup_entry_t	*age_next;
} autogroup_entry_t;

/* Holds pointers to attribute definitions and groups. */
typedef struct autogroup_info_t {
	autogroup_def_t		*agi_def;	/* Group attributes definitions. */
	autogroup_entry_t	*agi_entry;	/* Group entries.  */
78
	AttributeDescription	*agi_memberof_ad;	/* memberOf attribute description  */
79
	ldap_pvt_thread_mutex_t agi_mutex;
80
81
82
83
84
85
86
87
88
89
90
} autogroup_info_t;

/* Search callback for adding groups initially. */
typedef struct autogroup_sc_t {
	autogroup_info_t		*ags_info;	/* Group definitions and entries.  */
	autogroup_def_t		*ags_def;	/* Attributes definition of the group being added. */
} autogroup_sc_t;

/* Used for adding members, found when searching, to a group. */
typedef struct autogroup_ga_t {
	autogroup_entry_t	*agg_group;	/* The group to which the members will be added. */
91
	autogroup_filter_t	*agg_filter;	/* Current filter */
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
	Entry			*agg_entry;	/* Used in autogroup_member_search_cb to modify 
						this entry with the search results. */

	Modifications		*agg_mod;	/* Used in autogroup_member_search_modify_cb to hold the 
						search results which will be added to the group. */

	Modifications		*agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't 
						have to search for the last mod added. */
} autogroup_ga_t;


/* 
**	dn, ndn	- the DN of the member to add
**	age	- the group to which the member DN will be added
*/
static int
autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
111
	Modifications	*modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
112
	SlapReply	sreply = {REP_RESULT};
113
	BerValue	*vals, *nvals;
114
115
	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
	Operation	o = *op;
116
	unsigned long opid = op->o_opid;
Howard Chu's avatar
Howard Chu committed
117
	OpExtra oex;
118
119
120

	assert( dn != NULL );
	assert( ndn != NULL );
121
	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
122
		dn->bv_val, age->age_dn.bv_val );
123

124
125
126
	vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
	nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
	ber_dupbv( vals, dn );
127
	BER_BVZERO( &vals[ 1 ] );
128
	ber_dupbv( nvals, ndn );
129
130
	BER_BVZERO( &nvals[ 1 ] );

131
132
133
134
135
136
137
138
139
	modlist->sml_op = LDAP_MOD_ADD;
	modlist->sml_desc = age->age_def->agd_member_ad;
	modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
	modlist->sml_values = vals;
	modlist->sml_nvalues = nvals;
	modlist->sml_numvals = 1;
	modlist->sml_flags = SLAP_MOD_INTERNAL;
	modlist->sml_next = NULL;

140
	o.o_opid = 0;	/* shared with op, saved above */
141
142
143
	o.o_tag = LDAP_REQ_MODIFY;
	o.o_callback = &cb;
	o.orm_modlist = modlist;
144
145
	o.o_dn = op->o_bd->be_rootdn;
	o.o_ndn = op->o_bd->be_rootndn;
146
147
148
	o.o_req_dn = age->age_dn;
	o.o_req_ndn = age->age_ndn;
	o.o_permissive_modify = 1;
149
150
	o.o_dont_replicate = 1;
	o.orm_no_opattrs = 1;
151
152
153
	o.o_managedsait = SLAP_CONTROL_CRITICAL;
	o.o_relax = SLAP_CONTROL_CRITICAL;

Howard Chu's avatar
Howard Chu committed
154
155
156
	oex.oe_key = (void *)&autogroup;
	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );

157
158
159
160
	o.o_bd->bd_info = (BackendInfo *)on->on_info;
	(void)op->o_bd->be_modify( &o, &sreply );
	o.o_bd->bd_info = (BackendInfo *)on;

Howard Chu's avatar
Howard Chu committed
161
162
	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );

163
	slap_mods_free( modlist, 1 );
164
	op->o_opid = opid;
165
166
167
168
169
170
171
172
173

	return sreply.sr_err;
}

/* 
**	e	- the entry where to get the attribute values
**	age	- the group to which the values will be added
*/
static int
Howard Chu's avatar
Howard Chu committed
174
autogroup_add_member_values_to_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
175
176
177
178
179
180
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
	Modifications	modlist;
	SlapReply	sreply = {REP_RESULT};
	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
	Operation	o = *op;
181
	unsigned long opid = op->o_opid;
Howard Chu's avatar
Howard Chu committed
182
	OpExtra oex;
183
184

	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
185
		dn->bv_val, age->age_dn.bv_val );
186

187
188
189
	modlist.sml_op = LDAP_MOD_ADD;
	modlist.sml_desc = age->age_def->agd_member_ad;
	modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
190
191
192
	modlist.sml_values = attr->a_vals;
	modlist.sml_nvalues = attr->a_nvals;
	modlist.sml_numvals = attr->a_numvals;
193
194
195
	modlist.sml_flags = SLAP_MOD_INTERNAL;
	modlist.sml_next = NULL;

196
	o.o_opid = 0;
197
198
199
	o.o_tag = LDAP_REQ_MODIFY;
	o.o_callback = &cb;
	o.orm_modlist = &modlist;
200
201
	o.o_dn = op->o_bd->be_rootdn;
	o.o_ndn = op->o_bd->be_rootndn;
202
203
204
	o.o_req_dn = age->age_dn;
	o.o_req_ndn = age->age_ndn;
	o.o_permissive_modify = 1;
205
206
	o.o_dont_replicate = 1;
	o.orm_no_opattrs = 1;
207
208
209
	o.o_managedsait = SLAP_CONTROL_CRITICAL;
	o.o_relax = SLAP_CONTROL_CRITICAL;

Howard Chu's avatar
Howard Chu committed
210
211
212
	oex.oe_key = (void *)&autogroup;
	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );

213
214
215
	o.o_bd->bd_info = (BackendInfo *)on->on_info;
	(void)op->o_bd->be_modify( &o, &sreply );
	o.o_bd->bd_info = (BackendInfo *)on;
216
	op->o_opid = opid;
Howard Chu's avatar
Howard Chu committed
217
	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
218
219
220
221
222
223
224
225
226
227
228
229
230

	return sreply.sr_err;
}

/*
** dn,ndn	- the DN to be deleted
** age		- the group from which the DN will be deleted
** If we pass a NULL dn and ndn, all members are deleted from the group. 
*/
static int
autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
231
	Modifications	*modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
232
	SlapReply	sreply = {REP_RESULT};
233
	BerValue	*vals, *nvals;
234
235
	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
	Operation	o = *op;
236
	unsigned long opid = op->o_opid;
Howard Chu's avatar
Howard Chu committed
237
	OpExtra oex;
238
239
240

	if ( dn == NULL || ndn == NULL ) {
		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
241
			age->age_dn.bv_val );
242

243
244
245
		modlist->sml_values = NULL;
		modlist->sml_nvalues = NULL;
		modlist->sml_numvals = 0;
246
247
	} else {
		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
248
			dn->bv_val, age->age_dn.bv_val );
249

250
251
252
		vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
		nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
		ber_dupbv( vals, dn );
253
		BER_BVZERO( &vals[ 1 ] );
254
		ber_dupbv( nvals, ndn );
255
256
		BER_BVZERO( &nvals[ 1 ] );

257
258
259
		modlist->sml_values = vals;
		modlist->sml_nvalues = nvals;
		modlist->sml_numvals = 1;
260
261
262
	}


263
264
265
266
267
	modlist->sml_op = LDAP_MOD_DELETE;
	modlist->sml_desc = age->age_def->agd_member_ad;
	modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
	modlist->sml_flags = SLAP_MOD_INTERNAL;
	modlist->sml_next = NULL;
268

269
	o.o_opid = 0;
270
271
	o.o_callback = &cb;
	o.o_tag = LDAP_REQ_MODIFY;
272
	o.orm_modlist = modlist;
273
274
	o.o_dn = op->o_bd->be_rootdn;
	o.o_ndn = op->o_bd->be_rootndn;
275
276
277
278
279
	o.o_req_dn = age->age_dn;
	o.o_req_ndn = age->age_ndn;
	o.o_relax = SLAP_CONTROL_CRITICAL;
	o.o_managedsait = SLAP_CONTROL_CRITICAL;
	o.o_permissive_modify = 1;
280
281
	o.o_dont_replicate = 1;
	o.orm_no_opattrs = 1;
282

Howard Chu's avatar
Howard Chu committed
283
284
285
	oex.oe_key = (void *)&autogroup;
	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );

286
287
288
289
	o.o_bd->bd_info = (BackendInfo *)on->on_info;
	(void)op->o_bd->be_modify( &o, &sreply );
	o.o_bd->bd_info = (BackendInfo *)on;

Howard Chu's avatar
Howard Chu committed
290
291
	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );

292
293
	slap_mods_free( modlist, 1 );

294
	op->o_opid = opid;
295
296
297
	return sreply.sr_err;
}

298
299
300
301
302
/*
**      e       - the entry where to get the attribute values
**      age     - the group from which the values will be deleted
*/
static int
Howard Chu's avatar
Howard Chu committed
303
autogroup_delete_member_values_from_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
304
305
306
307
308
309
{
        slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
        Modifications   modlist;
        SlapReply       sreply = {REP_RESULT};
        slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
        Operation       o = *op;
310
	unsigned long opid = op->o_opid;
Howard Chu's avatar
Howard Chu committed
311
	OpExtra oex;
312
313

        Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
314
			dn->bv_val, age->age_dn.bv_val );
315
316
317
318
319
320
321
322
323
324

        modlist.sml_op = LDAP_MOD_DELETE;
        modlist.sml_desc = age->age_def->agd_member_ad;
        modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
        modlist.sml_values = attr->a_vals;
        modlist.sml_nvalues = attr->a_nvals;
        modlist.sml_numvals = attr->a_numvals;
        modlist.sml_flags = SLAP_MOD_INTERNAL;
        modlist.sml_next = NULL;

325
	o.o_opid = 0;
326
327
328
        o.o_tag = LDAP_REQ_MODIFY;
        o.o_callback = &cb;
        o.orm_modlist = &modlist;
329
330
		o.o_dn = op->o_bd->be_rootdn;
		o.o_ndn = op->o_bd->be_rootndn;
331
332
333
        o.o_req_dn = age->age_dn;
        o.o_req_ndn = age->age_ndn;
        o.o_permissive_modify = 1;
334
335
	o.o_dont_replicate = 1;
	o.orm_no_opattrs = 1;
336
337
338
        o.o_managedsait = SLAP_CONTROL_CRITICAL;
        o.o_relax = SLAP_CONTROL_CRITICAL;

Howard Chu's avatar
Howard Chu committed
339
340
341
	oex.oe_key = (void *)&autogroup;
	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );

342
343
344
        o.o_bd->bd_info = (BackendInfo *)on->on_info;
        (void)op->o_bd->be_modify( &o, &sreply );
        o.o_bd->bd_info = (BackendInfo *)on;
345
	op->o_opid = opid;
346

Howard Chu's avatar
Howard Chu committed
347
348
	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );

349
350
351
        return sreply.sr_err;
}

352
353
354
355
356
357
358
359
360
361
362
363
364
365
/* 
** Callback used to add entries to a group, 
** which are going to be written in the database
** (used in bi_op_add)
** The group is passed in autogroup_ga_t->agg_group
*/
static int
autogroup_member_search_cb( Operation *op, SlapReply *rs )
{
	assert( op->o_tag == LDAP_REQ_SEARCH );

	if ( rs->sr_type == REP_SEARCH ) {
		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
		autogroup_entry_t	*age = agg->agg_group;
366
		autogroup_filter_t	*agf = agg->agg_filter;
367
368
369
		Modification		mod;
		const char		*text = NULL;
		char			textbuf[1024];
370
		struct berval		*vals, *nvals;
371
		struct berval		lvals[ 2 ], lnvals[ 2 ];
372
		int			numvals;
373
374

		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
375
			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
376

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
		if ( agf->agf_anlist ) {
			Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
			if (attr) {
				vals = attr->a_vals;
				nvals = attr->a_nvals;
				numvals = attr->a_numvals;
			} else {
				// Nothing to add
				return 0;
			}
		} else {
			lvals[ 0 ] = rs->sr_entry->e_name;
			BER_BVZERO( &lvals[ 1 ] );
			lnvals[ 0 ] = rs->sr_entry->e_nname;
			BER_BVZERO( &lnvals[ 1 ] );
			vals = lvals;
			nvals = lnvals;
			numvals = 1;
		}
396
397
398
399
400
401

		mod.sm_op = LDAP_MOD_ADD;
		mod.sm_desc = age->age_def->agd_member_ad;
		mod.sm_type = age->age_def->agd_member_ad->ad_cname;
		mod.sm_values = vals;
		mod.sm_nvalues = nvals;
402
		mod.sm_numvals = numvals;
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423

		modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
	}

	return 0;
}

/* 
** Callback used to add entries to a group, which is already in the database.
** (used in on_response)
** The group is passed in autogroup_ga_t->agg_group
** NOTE: Very slow.
*/
static int
autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
{
	assert( op->o_tag == LDAP_REQ_SEARCH );

	if ( rs->sr_type == REP_SEARCH ) {
		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
		autogroup_entry_t	*age = agg->agg_group;
424
		autogroup_filter_t	*agf = agg->agg_filter;
425
		Modifications		*modlist;
426
		struct berval		*vals, *nvals;
427
		struct berval		lvals[ 2 ], lnvals[ 2 ];
428
		int			numvals;
429
430

		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
431
			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
432

433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
		if ( agf->agf_anlist ) {
			Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
			if (attr) {
				vals = attr->a_vals;
				nvals = attr->a_nvals;
				numvals = attr->a_numvals;
			} else {
				// Nothing to add
				return 0;
			}
		} else {
			lvals[ 0 ] = rs->sr_entry->e_name;
			BER_BVZERO( &lvals[ 1 ] );
			lnvals[ 0 ] = rs->sr_entry->e_nname;
			BER_BVZERO( &lnvals[ 1 ] );
			vals = lvals;
			nvals = lnvals;
			numvals = 1;
		}
452

453
454
		if ( numvals ) {
			modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
455

456
457
458
			modlist->sml_op = LDAP_MOD_ADD;
			modlist->sml_desc = age->age_def->agd_member_ad;
			modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
459

460
461
462
			ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
			ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
			modlist->sml_numvals = numvals;
463

464
465
			modlist->sml_flags = SLAP_MOD_INTERNAL;
			modlist->sml_next = NULL;
466

467
468
469
470
471
472
473
			if ( agg->agg_mod == NULL ) {
				agg->agg_mod = modlist;
				agg->agg_mod_last = modlist;
			} else {
				agg->agg_mod_last->sml_next = modlist;
				agg->agg_mod_last = modlist;
			}
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
		}

	}

	return 0;
}


/*
** Adds all entries matching the passed filter to the specified group.
** If modify == 1, then we modify the group's entry in the database using be_modify.
** If modify == 0, then, we must supply a rw entry for the group, 
**	because we only modify the entry, without calling be_modify.
** e	- the group entry, to which the members will be added
** age	- the group
** agf	- the filter
*/
static int
autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	Operation		o = *op;
	SlapReply		rs = { REP_SEARCH };
	slap_callback		cb = { 0 };
	slap_callback		null_cb = { NULL, slap_null_cb, NULL, NULL };
	autogroup_ga_t		agg;
Howard Chu's avatar
Howard Chu committed
500
	OpExtra oex;
501
502

	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
503
		age->age_dn.bv_val );
504
505
506
507

	o.ors_attrsonly = 0;
	o.o_tag = LDAP_REQ_SEARCH;

508
509
	o.o_dn = op->o_bd->be_rootdn;
	o.o_ndn = op->o_bd->be_rootndn;
510
511
512
513
514
515
516
517
518
519
520
	o.o_req_dn = agf->agf_dn;
	o.o_req_ndn = agf->agf_ndn;

	o.ors_filterstr = agf->agf_filterstr;
	o.ors_filter = agf->agf_filter;

	o.ors_scope = agf->agf_scope;
	o.ors_deref = LDAP_DEREF_NEVER;
	o.ors_limit = NULL;
	o.ors_tlimit = SLAP_NO_LIMIT;
	o.ors_slimit = SLAP_NO_LIMIT;
521
	o.ors_attrs =  agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
522
	o.o_do_not_cache = 1;
523
524

	agg.agg_group = age;
525
	agg.agg_filter = agf;
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
	agg.agg_mod = NULL;
	agg.agg_mod_last = NULL;
	agg.agg_entry = e;
	cb.sc_private = &agg;

	if ( modify == 1 ) {
		cb.sc_response = autogroup_member_search_modify_cb;
	} else {
		cb.sc_response = autogroup_member_search_cb;
	}

	cb.sc_cleanup = NULL;
	cb.sc_next = NULL;

	o.o_callback = &cb;

	o.o_bd->bd_info = (BackendInfo *)on->on_info;
	op->o_bd->be_search( &o, &rs );
	o.o_bd->bd_info = (BackendInfo *)on;	

546
	if ( modify == 1 && agg.agg_mod ) {
547
548
		unsigned long opid = op->o_opid;

549
550
		rs_reinit( &rs, REP_RESULT );

551
		o = *op;
552
		o.o_opid = 0;
553
554
555
		o.o_callback = &null_cb;
		o.o_tag = LDAP_REQ_MODIFY;
		o.orm_modlist = agg.agg_mod;
556
557
		o.o_dn = op->o_bd->be_rootdn;
		o.o_ndn = op->o_bd->be_rootndn;
558
559
560
561
562
		o.o_req_dn = age->age_dn;
		o.o_req_ndn = age->age_ndn;
		o.o_relax = SLAP_CONTROL_CRITICAL;
		o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
		o.o_permissive_modify = 1;
563
564
		o.o_dont_replicate = 1;
		o.orm_no_opattrs = 1;
565

Howard Chu's avatar
Howard Chu committed
566
567
568
	oex.oe_key = (void *)&autogroup;
	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );

569
570
571
572
		o.o_bd->bd_info = (BackendInfo *)on->on_info;
		(void)op->o_bd->be_modify( &o, &rs );
		o.o_bd->bd_info = (BackendInfo *)on;	

Howard Chu's avatar
Howard Chu committed
573
574
	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );

575
		slap_mods_free(agg.agg_mod, 1);
576
		op->o_opid = opid;
577
578
579
580
581
582
583
	}

	return 0;
}

/* 
** Adds a group to the internal list from the passed entry.
Josh Soref's avatar
Josh Soref committed
584
** scan specifies whether to add all matching members to the group.
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
** modify specifies whether to modify the given group entry (when modify == 0),
**	or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
** agi	- pointer to the groups and the attribute definitions
** agd - the attribute definition of the added group
** e	- the entry representing the group, can be NULL if the ndn is specified, and modify == 1
** ndn	- the DN of the group, can be NULL if we give a non-NULL e
*/
static int
autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
{
	autogroup_entry_t	**agep = &agi->agi_entry;
	autogroup_filter_t	*agf, *agf_prev = NULL;
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	LDAPURLDesc		*lud = NULL;
	Attribute		*a;
	BerValue		*bv, dn;
	int			rc = 0, match = 1, null_entry = 0;

	if ( e == NULL ) {
		if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
			LDAP_SUCCESS || e == NULL ) {
606
			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val );
607
608
609
610
611
612
613
			return 1;
		}

		null_entry = 1;
	}

	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
614
		e->e_name.bv_val );
615
616
617
618
619

	if ( agi->agi_entry != NULL ) {
		for ( ; *agep ; agep = &(*agep)->age_next ) {
			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
			if ( match == 0 ) {
620
				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val );
621
622
623
624
625
626
627
628
629
630
631
				return 1;
			}
			/* goto last */;
		}
	}


	*agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
	ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
	(*agep)->age_def = agd;
	(*agep)->age_filter = NULL;
632
633
	(*agep)->age_mustrefresh = 0;
	(*agep)->age_modrdn_olddnmodified = 0;
634
635
636
637
638
639
640
641
642
643
644
645

	ber_dupbv( &(*agep)->age_dn, &e->e_name );
	ber_dupbv( &(*agep)->age_ndn, &e->e_nname );

	a = attrs_find( e->e_attrs, agd->agd_member_url_ad );

	if ( null_entry == 1 ) {
		a = attrs_dup( a );
		overlay_entry_release_ov( op, e, 0, on );
	}

	if( a == NULL ) {
646
		Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n" );
647
648
649
650
651
652
	} else {
		for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {

			agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );

			if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
653
				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val );
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
				/* FIXME: error? */
				ch_free( agf ); 
				continue;
			}

			agf->agf_scope = lud->lud_scope;

			if ( lud->lud_dn == NULL ) {
				BER_BVSTR( &dn, "" );
			} else {
				ber_str2bv( lud->lud_dn, 0, 0, &dn );
			}

			rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
			if ( rc != LDAP_SUCCESS ) {
669
				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val );
670
671
672
673
674
675
676
				/* FIXME: error? */
				goto cleanup;
			}

			if ( lud->lud_filter != NULL ) {
				ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
				agf->agf_filter = str2filter( lud->lud_filter );
677
			} else {
678
				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: URL filter is missing <%s>\n", bv->bv_val );
679
680
681
				/* FIXME: error? */
				goto cleanup;
			}
682

683
684
685
686
687
688
689
690
			if ( lud->lud_attrs != NULL ) {
				int i;

				for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
					/* Just counting */;
				}

				if ( i > 1 ) {
691
					Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
692
						bv->bv_val );
693
					/* FIXME: error? */
694
695
696
697
					filter_free( agf->agf_filter );
					ch_free( agf->agf_filterstr.bv_val );
					ch_free( agf->agf_dn.bv_val );
					ch_free( agf->agf_ndn.bv_val );
698
					ldap_free_urldesc( lud );
699
					ch_free( agf );
700
					continue;
701
				}
702

703
704
705
706
				agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );

				if ( agf->agf_anlist == NULL ) {
					Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
707
						lud->lud_attrs[0] );
708
					/* FIXME: error? */
709
710
711
712
					filter_free( agf->agf_filter );
					ch_free( agf->agf_filterstr.bv_val );
					ch_free( agf->agf_dn.bv_val );
					ch_free( agf->agf_ndn.bv_val );
713
					ldap_free_urldesc( lud );
714
					ch_free( agf );
715
716
717
718
					continue;
				}
			}

719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
			agf->agf_next = NULL;

			if( (*agep)->age_filter == NULL ) {
				(*agep)->age_filter = agf;
			}

			if( agf_prev != NULL ) {
				agf_prev->agf_next = agf;
			}

			agf_prev = agf;

			if ( scan == 1 ){
				autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
			}

			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
736
				agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val );
737
738
739
740
741
742
743
744

			ldap_free_urldesc( lud );

			continue;


cleanup:;

745
746
			ch_free( agf->agf_ndn.bv_val );
			ch_free( agf->agf_dn.bv_val );
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
			ldap_free_urldesc( lud );				
			ch_free( agf ); 
		}
	}

	if ( null_entry == 1 ) {
		attrs_free( a );
	}
	return rc;
}

/* 
** Used when opening the database to add all existing 
** groups from the database to our internal list.
*/
static int
autogroup_group_add_cb( Operation *op, SlapReply *rs )
{
	assert( op->o_tag == LDAP_REQ_SEARCH );

	if ( rs->sr_type == REP_SEARCH ) {
		autogroup_sc_t		*ags = (autogroup_sc_t *)op->o_callback->sc_private;

		Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
771
			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
772
773
774
775
776
777
778

		autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
	}

	return 0;
}

779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
typedef struct ag_addinfo {
	slap_overinst *on;
	Entry *e;
	autogroup_def_t		*agd;
} ag_addinfo;

static int
autogroup_add_entry_cb( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;
	ag_addinfo *aa = sc->sc_private;
	slap_overinst *on = aa->on;
	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
	BackendInfo *bi = op->o_bd->bd_info;

	if ( rs->sr_err != LDAP_SUCCESS )
		goto done;

	op->o_bd->bd_info = (BackendInfo *)on;
	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
	if ( aa->agd ) {
		autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
	} else {
		autogroup_entry_t	*age;
		autogroup_filter_t	*agf;
Howard Chu's avatar
Howard Chu committed
804
		struct berval odn, ondn;
805
		int rc;
Howard Chu's avatar
Howard Chu committed
806
807
808
809
810
811
812

		/* must use rootdn when calling test_filter */
		odn = op->o_dn;
		ondn = op->o_ndn;
		op->o_dn = op->o_bd->be_rootdn;
		op->o_ndn = op->o_bd->be_rootndn;

813
814
815
816
817
818
819
820
821
822
823
		for ( age = agi->agi_entry; age ; age = age->age_next ) {
			ldap_pvt_thread_mutex_lock( &age->age_mutex );

			/* Check if any of the filters are the suffix to the entry DN.
			   If yes, we can test that filter against the entry. */

			for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
				if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
					rc = test_filter( op, aa->e, agf->agf_filter );
					if ( rc == LDAP_COMPARE_TRUE ) {
						if ( agf->agf_anlist ) {
Howard Chu's avatar
Howard Chu committed
824
825
826
							Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
							if ( a )
								autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
827
828
829
830
831
832
833
834
835
						} else {
							autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
						}
						break;
					}
				}
			}
			ldap_pvt_thread_mutex_unlock( &age->age_mutex );
		}
Howard Chu's avatar
Howard Chu committed
836
837
		op->o_dn = odn;
		op->o_ndn = ondn;
838
839
840
841
842
843
844
845
846
847
848
	}
	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );

	op->o_bd->bd_info = bi;

done:
	op->o_callback = sc->sc_next;
	op->o_tmpfree( sc, op->o_tmpmemctx );

	return SLAP_CB_CONTINUE;
}
849
850
851
852
853
854
855
856

/*
** When adding a group, we first strip any existing members,
** and add all which match the filters ourselfs.
*/
static int
autogroup_add_entry( Operation *op, SlapReply *rs)
{
Howard Chu's avatar
Howard Chu committed
857
858
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
859
	autogroup_def_t		*agd = agi->agi_def;
860
861
	slap_callback	*sc = NULL;
	ag_addinfo	*aa = NULL;
862
863

	Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n", 
864
		op->ora_e->e_name.bv_val );
865

866
867
868
869
870
871
872
873
	sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
	sc->sc_private = (sc+1);
	sc->sc_response = autogroup_add_entry_cb;
	aa = sc->sc_private;
	aa->on = on;
	aa->e = op->ora_e;
	sc->sc_next = op->o_callback;
	op->o_callback = sc;
874

875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
	/* Check if it's a group. */
	for ( ; agd ; agd = agd->agd_next ) {
		if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
			Modification		mod;
			const char		*text = NULL;
			char			textbuf[1024];

			mod.sm_op = LDAP_MOD_DELETE;
			mod.sm_desc = agd->agd_member_ad;
			mod.sm_type = agd->agd_member_ad->ad_cname;
			mod.sm_values = NULL;
			mod.sm_nvalues = NULL;

			/* We don't want any member attributes added by the user. */
			modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );

891
			aa->agd = agd;
892

893
			break;
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
		}
	}

	return SLAP_CB_CONTINUE;
}

/*
** agi	- internal group and attribute definitions list
** e	- the group to remove from the internal list
*/
static int
autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
{
	autogroup_entry_t	*age = agi->agi_entry,
				*age_prev = NULL,
				*age_next;
	int			rc = 1;

	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n", 
913
		age->age_dn.bv_val );
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937

	for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
		age_next = age->age_next;

		if ( age == e ) {
			autogroup_filter_t	*agf = age->age_filter,
							*agf_next;

			if ( age_prev != NULL ) {
				age_prev->age_next = age_next;
			} else {
				agi->agi_entry = NULL;
			}

			ch_free( age->age_dn.bv_val );
			ch_free( age->age_ndn.bv_val );

			for( agf_next = agf ; agf_next ; agf = agf_next ){
				agf_next = agf->agf_next;

				filter_free( agf->agf_filter );
				ch_free( agf->agf_filterstr.bv_val );
				ch_free( agf->agf_dn.bv_val );
				ch_free( agf->agf_ndn.bv_val );
938
939
				anlist_free( agf->agf_anlist, 1, NULL );
				ch_free( agf );
940
941
942
943
944
945
946
947
948
949
950
951
			}

			ldap_pvt_thread_mutex_unlock( &age->age_mutex );		
			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
			ch_free( age );

			rc = 0;	
			return rc;

		}
	}

952
	Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val );
953
954
955
956
957
958
959
960
961

	return rc;

}

static int
autogroup_delete_entry( Operation *op, SlapReply *rs)
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
962
963
	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
	autogroup_entry_t	*age, *age_prev, *age_next;
964
965
966
	autogroup_filter_t	*agf;
	Entry			*e;
	int			matched_group = 0, rc = 0;
Howard Chu's avatar
Howard Chu committed
967
	struct berval odn, ondn;
Howard Chu's avatar
Howard Chu committed
968
969
970
971
972
973
	OpExtra *oex;

	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
		if ( oex->oe_key == (void *)&autogroup )
			return SLAP_CB_CONTINUE;
	}
974

975
	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val );
976

977
978
	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );

979
980
	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
		LDAP_SUCCESS || e == NULL ) {
981
		Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val );
982
		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );			
983
984
985
986
		return SLAP_CB_CONTINUE;
	}

	/* Check if the entry to be deleted is one of our groups. */
Howard Chu's avatar
Howard Chu committed
987
988
	for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
		age = age_next;
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
		ldap_pvt_thread_mutex_lock( &age->age_mutex );
		age_next = age->age_next;

		if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
			int match = 1;

			matched_group = 1;

			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );

			if ( match == 0 ) {
				autogroup_delete_group( agi, age );
				break;
			}
		}

		ldap_pvt_thread_mutex_unlock( &age->age_mutex );			
	}

	if ( matched_group == 1 ) {
		overlay_entry_release_ov( op, e, 0, on );
1010
		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );		
1011
1012
1013
1014
1015
1016
		return SLAP_CB_CONTINUE;
	}

	/* Check if the entry matches any of the groups.
	   If yes, we can delete the entry from that group. */

Howard Chu's avatar
Howard Chu committed
1017
1018
1019
1020
1021
	odn = op->o_dn;
	ondn = op->o_ndn;
	op->o_dn = op->o_bd->be_rootdn;
	op->o_ndn = op->o_bd->be_rootndn;

1022
1023
1024
1025
1026
1027
1028
	for ( age = agi->agi_entry ; age ; age = age->age_next ) {
		ldap_pvt_thread_mutex_lock( &age->age_mutex );		

		for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
			if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
				rc = test_filter( op, e, agf->agf_filter );
				if ( rc == LDAP_COMPARE_TRUE ) {
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
					/* If the attribute is retrieved from the entry, we don't know what to delete
					** So the group must be entirely refreshed
					** But the refresh can't be done now because the entry is not deleted
					** So the group is marked as mustrefresh
					*/
					if ( agf->agf_anlist ) {
						age->age_mustrefresh = 1;
					} else {
						autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
					}
1039
1040
1041
1042
1043
1044
					break;
				}
			}
		}
		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
	}
Howard Chu's avatar
Howard Chu committed
1045
1046
	op->o_dn = odn;
	op->o_ndn = ondn;
1047
1048

	overlay_entry_release_ov( op, e, 0, on );
1049
	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );		
1050
1051
1052
1053
1054
1055
1056
1057

	return SLAP_CB_CONTINUE;
}

static int
autogroup_response( Operation *op, SlapReply *rs )
{
	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
Howard Chu's avatar
Howard Chu committed
1058
	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
1059
	autogroup_def_t		*agd = agi->agi_def;
Howard Chu's avatar
Howard Chu committed
1060
	autogroup_entry_t	*age;
1061
1062
1063
	autogroup_filter_t	*agf;
	BerValue		new_dn, new_ndn, pdn;
	Entry			*e, *group;
Howard Chu's avatar
Howard Chu committed
1064
	Attribute		*a, *ea, *attrs;
1065
	int			is_olddn, is_newdn, is_value_refresh, dn_equal;
Howard Chu's avatar
Howard Chu committed
1066
1067
1068
1069
1070
1071
	OpExtra *oex;

	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
		if ( oex->oe_key == (void *)&autogroup )
			break;
	}
1072
1073
1074

	/* Handle all cases where a refresh of the group is needed */
	if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
Howard Chu's avatar
Howard Chu committed
1075
		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090

			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );

			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
				/* Request detected that the group must be refreshed */

				ldap_pvt_thread_mutex_lock( &age->age_mutex );

				if ( age->age_mustrefresh ) {
					autogroup_delete_member_from_group( op, NULL, NULL, age) ;

					for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
						autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
					}
				}
1091

1092
1093
1094
1095
1096
				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
			}

			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
		}
Howard Chu's avatar
Howard Chu committed
1097
	} else if ( op->o_tag == LDAP_REQ_MODRDN ) {
Howard Chu's avatar
Howard Chu committed
1098
		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
1099

1100
			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val );
1101

1102
1103
			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );			

1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
			if ( op->oq_modrdn.rs_newSup ) {
				pdn = *op->oq_modrdn.rs_newSup;
			} else {
				dnParent( &op->o_req_dn, &pdn );
			}
			build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );

			if ( op->oq_modrdn.rs_nnewSup ) {
				pdn = *op->oq_modrdn.rs_nnewSup;
			} else {
				dnParent( &op->o_req_ndn, &pdn );
			}
			build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );

1118
			Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val );
1119
1120
1121
1122
1123

			dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );

			if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
				LDAP_SUCCESS || e == NULL ) {
1124
				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val );
1125
				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1126
1127
1128
1129
1130
1131
1132
				return SLAP_CB_CONTINUE;
			}

			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );


			if ( a == NULL ) {
1133
				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val );
1134
				overlay_entry_release_ov( op, e, 0, on );
1135
				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );		
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
				return SLAP_CB_CONTINUE;
			}


			/* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
			for ( ; agd; agd = agd->agd_next ) {

				if ( value_find_ex( slap_schema.si_ad_objectClass,
						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
						a->a_nvals, &agd->agd_oc->soc_cname,
						op->o_tmpmemctx ) == 0 )
				{		
					for ( age = agi->agi_entry ; age ; age = age->age_next ) {
						int match = 1;

						dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
						if ( match == 0 ) {
1154
							Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val );
1155
1156
1157
1158
1159
1160
							ber_dupbv( &age->age_dn, &new_dn );
							ber_dupbv( &age->age_ndn, &new_ndn );

							op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
							op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
							overlay_entry_release_ov( op, e, 0, on );
1161
							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );		
1162
1163
1164
1165
1166
1167
1168
1169
							return SLAP_CB_CONTINUE;
						}
					}

				}
			}

			/* For each group: 
Josh Soref's avatar
Josh Soref committed
1170
1171
			   1. check if the original entry's DN is in the group.
			   2. check if the any of the group filter's base DN is a suffix of the new DN
1172
1173
1174
1175
1176
1177
1178

			   If 1 and 2 are both false, we do nothing.
			   If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
			   If 1 is false, and 2 is true, we check the entry against the group's filters,
				and add it's DN to the group.
			   If 1 is true, and 2 is false, we delete the entry's DN from the group.
			*/
Howard Chu's avatar
Howard Chu committed
1179
1180
			attrs = attrs_dup( e->e_attrs );
			overlay_entry_release_ov( op, e, 0, on );
1181
1182
1183
			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
				is_olddn = 0;
				is_newdn = 0;
1184
				is_value_refresh = 0;
1185
1186
1187

				ldap_pvt_thread_mutex_lock( &age->age_mutex );

1188
				if ( age->age_filter && age->age_filter->agf_anlist ) {
Howard Chu's avatar
Howard Chu committed
1189
					ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1190
1191
1192
1193
1194
				}
				else {
					ea = NULL;
				}

1195
				if ( age->age_modrdn_olddnmodified ) {
Josh Soref's avatar
Josh Soref committed
1196
					/* Request already marked this group to be updated */
1197
1198
1199
1200
					is_olddn = 1;
					is_value_refresh = 1;
					age->age_modrdn_olddnmodified = 0;
				} else {
1201

1202
1203
					if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
						LDAP_SUCCESS || group == NULL ) {
1204
						Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val );
1205

1206
1207
						op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
						op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1208

Howard Chu's avatar
Howard Chu committed
1209
						attrs_free( attrs );
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
						ldap_pvt_thread_mutex_unlock( &age->age_mutex );
						ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
						return SLAP_CB_CONTINUE;
					}

					a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );

					if ( a != NULL ) {
						if ( value_find_ex( age->age_def->agd_member_ad,
								SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
								SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1221
								a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
1222
1223
1224
						{
							is_olddn = 1;
						}
1225
1226
1227

					}

1228
					overlay_entry_release_ov( op, group, 0, on );
1229

1230
				}
1231
1232
1233

				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
					if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1234
						/* TODO: should retest filter as it could imply conditions on the dn */
1235
1236
1237
1238
1239
1240
						is_newdn = 1;
						break;
					}
				}


1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
				if ( is_value_refresh ) {
					if ( is_olddn != is_newdn ) {
						/* group refresh */
						autogroup_delete_member_from_group( op, NULL, NULL, age) ;

						for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
							autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
						}
					}
					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
					continue;
				}
1253
				if ( is_olddn == 1 && is_newdn == 0 ) {
1254
					if ( ea )
Howard Chu's avatar
Howard Chu committed
1255
						autogroup_delete_member_values_from_group( op, &new_dn, age, ea );
1256
1257
					else
						autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1258
1259
				} else
				if ( is_olddn == 0 && is_newdn == 1 ) {
Howard Chu's avatar
Howard Chu committed
1260
					Entry etmp;
Howard Chu's avatar
Howard Chu committed
1261
					struct berval odn, ondn;
Howard Chu's avatar
Howard Chu committed
1262
1263
1264
					etmp.e_name = op->o_req_dn;
					etmp.e_nname = op->o_req_ndn;
					etmp.e_attrs = attrs;
Howard Chu's avatar
Howard Chu committed
1265
1266
1267
1268
1269
					odn = op->o_dn;
					ondn = op->o_ndn;
					op->o_dn = op->o_bd->be_rootdn;
					op->o_ndn = op->o_bd->be_rootndn;

1270
					for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
Howard Chu's avatar
Howard Chu committed
1271
						if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
Howard Chu's avatar
Howard Chu committed
1272
1273
1274
							if ( ea ) {
								autogroup_add_member_values_to_group( op, &new_dn, age, ea );
							} else
1275
								autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1276
1277
1278
							break;
						}
					}
Howard Chu's avatar
Howard Chu committed
1279
1280
					op->o_dn = odn;
					op->o_ndn = ondn;
1281
1282
				} else
				if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
					if ( ea ) {
						/* group refresh */
						autogroup_delete_member_from_group( op, NULL, NULL, age) ;

						for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
							autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
						}
					}
					else {
						autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
						autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
					}
1295
1296
1297
1298
1299
1300
1301
				}

				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
			}

			op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
			op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1302

Howard Chu's avatar
Howard Chu committed