Commit 76e1dd7b authored by Quanah Gibson-Mount's avatar Quanah Gibson-Mount
Browse files

ITS#5466

parent 674419ec
......@@ -36,6 +36,7 @@ OpenLDAP 2.4.9 Engineering
Fixed slapd-meta crash on search (ITS#5481)
Fixed slapo-accesslog null callback stack crash (ITS#5490)
Fixed slapo-auditlog unnecessary syscall (ITS#5441)
Added slapo-dynlist mapping to dynamic attrs generation (ITS#5466)
Fixed slapo-refint dnSubtreeMatch (ITS#5427)
Fixed slapo-refint global referential integrity (ITS#5428)
Fixed slapo-syncprov psearch on closed connection (ITS#5401)
......
......@@ -50,7 +50,7 @@ occurrences, and it must appear after the
.B overlay
directive.
.TP
.B dynlist-attrset <group-oc> <URL-ad> [<member-ad>]
.B dynlist-attrset <group-oc> <URL-ad> [[<mapped-ad>:]<member-ad> ...]
The value
.B <group-oc>
is the name of the objectClass that triggers the dynamic expansion of the
......@@ -82,6 +82,15 @@ of the URI were present in the
entry as values of the
.B <member-ad>
attribute.
Alternatively,
.B <mapped-ad>:<member-ad>
can be used to remap attributes obtained through expansion.
.B <member-ad>
attributes are not filled by expanded DN, but are remapped as
.B <mapped-ad>
attributes. Multiple mapping statements can be used.
.LP
The dynlist overlay may be used with any backend, but it is mainly
intended for use with local storage backends.
......@@ -173,3 +182,5 @@ overlay supports dynamic configuration via
.SH ACKNOWLEDGEMENTS
.P
This module was written in 2004 by Pierangelo Masarati for SysNet s.n.c.
.P
Attribute remapping was contributed in 2008 by Emmanuel Dreyfus.
......@@ -4,6 +4,7 @@
*
* Copyright 2003-2008 The OpenLDAP Foundation.
* Portions Copyright 2004-2005 Pierangelo Masarati.
* Portions Copyright 2008 Emmanuel Dreyfus.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
......@@ -60,14 +61,23 @@ static AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
static AttributeDescription *ad_dgIdentity, *ad_dgAuthz;
typedef struct dynlist_map_t {
AttributeDescription *dlm_member_ad;
AttributeDescription *dlm_mapped_ad;
struct dynlist_map_t *dlm_next;
} dynlist_map_t;
typedef struct dynlist_info_t {
ObjectClass *dli_oc;
AttributeDescription *dli_ad;
AttributeDescription *dli_member_ad;
struct dynlist_map_t *dli_dlm;
struct berval dli_default_filter;
struct dynlist_info_t *dli_next;
} dynlist_info_t;
#define DYNLIST_USAGE \
"\"dynlist-attrset <oc> <URL-ad> [[<mapped-ad>:]<member-ad> ...]\": "
static dynlist_info_t *
dynlist_is_dynlist_next( Operation *op, SlapReply *rs, dynlist_info_t *old_dli )
{
......@@ -149,6 +159,7 @@ dynlist_sc_update( Operation *op, SlapReply *rs )
AccessControlState acl_state = ACL_STATE_INIT;
dynlist_sc_t *dlc;
dynlist_map_t *dlm;
if ( rs->sr_type != REP_SEARCH ) {
return 0;
......@@ -167,7 +178,9 @@ dynlist_sc_update( Operation *op, SlapReply *rs )
goto done;
}
if ( dlc->dlc_dli->dli_member_ad ) {
for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
if (dlm->dlm_mapped_ad != NULL)
continue;
/* if access allowed, try to add values, emulating permissive
* control to silently ignore duplicates */
......@@ -185,8 +198,8 @@ dynlist_sc_update( Operation *op, SlapReply *rs )
BER_BVZERO( &nvals[ 1 ] );
mod.sm_op = LDAP_MOD_ADD;
mod.sm_desc = dlc->dlc_dli->dli_member_ad;
mod.sm_type = dlc->dlc_dli->dli_member_ad->ad_cname;
mod.sm_desc = dlm->dlm_member_ad;
mod.sm_type = dlm->dlm_member_ad->ad_cname;
mod.sm_values = vals;
mod.sm_nvalues = nvals;
mod.sm_numvals = 1;
......@@ -282,15 +295,25 @@ dynlist_sc_update( Operation *op, SlapReply *rs )
Modification mod;
const char *text = NULL;
char textbuf[1024];
dynlist_map_t *dlm;
AttributeDescription *ad;
BER_BVZERO( &vals[j] );
if ( nvals ) {
BER_BVZERO( &nvals[j] );
}
ad = a->a_desc;
for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
if ( dlm->dlm_member_ad == a->a_desc ) {
ad = dlm->dlm_mapped_ad;
break;
}
}
mod.sm_op = LDAP_MOD_ADD;
mod.sm_desc = a->a_desc;
mod.sm_type = a->a_desc->ad_cname;
mod.sm_desc = ad;
mod.sm_type = ad->ad_cname;
mod.sm_values = vals;
mod.sm_nvalues = nvals;
mod.sm_numvals = j;
......@@ -328,6 +351,7 @@ dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
int opattrs,
userattrs;
dynlist_sc_t dlc = { 0 };
dynlist_map_t *dlm;
a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
if ( a == NULL ) {
......@@ -344,9 +368,13 @@ dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
#endif /* SLAP_OPATTRS */
/* Don't generate member list if it wasn't requested */
if ( dli->dli_member_ad && !userattrs && !ad_inlist( dli->dli_member_ad, rs->sr_attrs ) ) {
return SLAP_CB_CONTINUE;
for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
if ( userattrs ||
ad_inlist( dlm->dlm_member_ad, rs->sr_attrs ) )
break;
}
if ( dli->dli_dlm && !dlm )
return SLAP_CB_CONTINUE;
if ( ad_dgIdentity && ( id = attrs_find( rs->sr_entry->e_attrs, ad_dgIdentity ))) {
Attribute *authz = NULL;
......@@ -393,6 +421,7 @@ dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
int i, j;
struct berval dn;
int rc;
dynlist_map_t *dlm;
BER_BVZERO( &o.o_req_dn );
BER_BVZERO( &o.o_req_ndn );
......@@ -431,7 +460,13 @@ dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
}
o.ors_scope = lud->lud_scope;
if ( dli->dli_member_ad != NULL ) {
for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
if ( dlm->dlm_mapped_ad != NULL ) {
break;
}
}
if ( dli->dli_dlm && !dlm ) {
/* if ( lud->lud_attrs != NULL ),
* the URL should be ignored */
o.ors_attrs = slap_anlist_no_attrs;
......@@ -564,9 +599,14 @@ dynlist_compare( Operation *op, SlapReply *rs )
dynlist_info_t *dli = (dynlist_info_t *)on->on_bi.bi_private;
Operation o = *op;
Entry *e = NULL;
dynlist_map_t *dlm;
for ( ; dli != NULL; dli = dli->dli_next ) {
if ( op->oq_compare.rs_ava->aa_desc == dli->dli_member_ad ) {
for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next )
if ( op->oq_compare.rs_ava->aa_desc == dlm->dlm_member_ad )
break;
if ( dli->dli_dlm && dlm ) {
/* This compare is for one of the attributes we're
* interested in. We'll use slapd's existing dyngroup
* evaluator to get the answer we want.
......@@ -834,11 +874,11 @@ dynlist_db_config(
ObjectClass *oc;
AttributeDescription *ad = NULL,
*member_ad = NULL;
dynlist_map_t *dlm = NULL;
const char *text;
if ( argc < 3 || argc > 4 ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
if ( argc < 3 ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
"invalid arg number #%d.\n",
fname, lineno, argc );
return 1;
......@@ -846,8 +886,7 @@ dynlist_db_config(
oc = oc_find( argv[1] );
if ( oc == NULL ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
"unable to find ObjectClass \"%s\"\n",
fname, lineno, argv[ 1 ] );
return 1;
......@@ -855,41 +894,90 @@ dynlist_db_config(
rc = slap_str2ad( argv[2], &ad, &text );
if ( rc != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
"unable to find AttributeDescription \"%s\"\n",
fname, lineno, argv[2] );
return 1;
}
if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
"AttributeDescription \"%s\" "
"must be a subtype of \"labeledURI\"\n",
fname, lineno, argv[2] );
return 1;
}
if ( argc == 4 ) {
rc = slap_str2ad( argv[3], &member_ad, &text );
for ( i = 3; i < argc; i++ ) {
char *arg;
char *cp;
AttributeDescription *member_ad = NULL;
AttributeDescription *mapped_ad = NULL;
dynlist_map_t *dlmp;
dynlist_map_t *dlml;
/*
* If no mapped attribute is given, dn is used
* for backward compatibility.
*/
arg = argv[i];
if ( cp = strchr( arg, (int)':' ) != NULL ) {
struct berval bv;
ber_str2bv( arg, cp - arg, 0, &bv );
rc = slap_bv2ad( &bv, &mapped_ad, &text );
if ( rc != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
DYNLIST_USAGE
"unable to find mapped AttributeDescription \"%s\"\n",
fname, lineno, arg );
return 1;
}
arg = cp + 1;
}
rc = slap_str2ad( arg, &member_ad, &text );
if ( rc != LDAP_SUCCESS ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
DYNLIST_USAGE
"unable to find AttributeDescription \"%s\"\n",
fname, lineno, argv[3] );
fname, lineno, arg );
return 1;
}
dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
if ( dlm == NULL ) {
dlm = dlmp;
dlml = NULL;
}
dlmp->dlm_member_ad = member_ad;
dlmp->dlm_mapped_ad = mapped_ad;
dlmp->dlm_next = NULL;
if ( dlml != NULL )
dlml->dlm_next = dlmp;
dlml = dlmp;
}
for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
*dlip; dlip = &(*dlip)->dli_next )
{
/* The same URL attribute / member attribute pair
* cannot be repeated */
if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
/*
* The same URL attribute / member attribute pair
* cannot be repeated, but we enforce this only
* when the member attribute is unique. Performing
* the check for multiple values would require
* sorting and comparing the lists, which is left
* as a future improvement
*/
if ( (*dlip)->dli_ad == ad &&
(*dlip)->dli_dlm->dlm_next == NULL &&
dlm->dlm_next == NULL &&
dlm->dlm_member_ad == (*dlip)->dli_dlm->dlm_member_ad &&
dlm->dlm_mapped_ad == (*dlip)->dli_dlm->dlm_mapped_ad ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
DYNLIST_USAGE
"URL attributeDescription \"%s\" already mapped.\n",
fname, lineno, ad->ad_cname.bv_val );
#if 0
......@@ -902,9 +990,18 @@ dynlist_db_config(
*dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
(*dlip)->dli_oc = oc;
(*dlip)->dli_ad = ad;
(*dlip)->dli_member_ad = member_ad;
(*dlip)->dli_dlm = dlm;
if ( dynlist_build_def_filter( *dlip ) ) {
dynlist_map_t *dlm = (*dlip)->ldi_dlm;
dynlist_map_t *dlm_next;
while ( dlm != NULL ) {
dlm_next = dlm->dlm_next;
ch_free( dlm );
dlm = dlm_next;
}
ch_free( *dlip );
*dlip = NULL;
return 1;
......@@ -965,9 +1062,17 @@ dynlist_db_config(
for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
*dlip; dlip = &(*dlip)->dli_next )
{
/* The same URL attribute / member attribute pair
* cannot be repeated */
if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
/*
* The same URL attribute / member attribute pair
* cannot be repeated, but we enforce this only
* when the member attribute is unique. Performing
* the check for multiple values would require
* sorting and comparing the lists, which is left
* as a future improvement
*/
if ( (*dlip)->dli_ad == ad &&
(*dlip)->dli_dlm->dlm_next == NULL &&
member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
Debug( LDAP_DEBUG_ANY, "%s: line %d: "
"\"dynlist-attrpair <member-ad> <URL-ad>\": "
"URL attributeDescription \"%s\" already mapped.\n",
......@@ -982,9 +1087,12 @@ dynlist_db_config(
*dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
(*dlip)->dli_oc = oc;
(*dlip)->dli_ad = ad;
(*dlip)->dli_member_ad = member_ad;
(*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
(*dlip)->dli_dlm->dlm_member_ad = member_ad;
(*dlip)->dli_dlm->dlm_mapped_ad = NULL;
if ( dynlist_build_def_filter( *dlip ) ) {
ch_free( (*dlip)->dli_dlm );
ch_free( *dlip );
*dlip = NULL;
return 1;
......@@ -1007,9 +1115,10 @@ enum {
static ConfigDriver dl_cfgen;
/* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */
static ConfigTable dlcfg[] = {
{ "dynlist-attrset", "group-oc> <URL-ad> <member-ad",
3, 4, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
3, 255, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
"( OLcfgOvAt:8.1 NAME 'olcDLattrSet' "
"DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
"EQUALITY caseIgnoreMatch "
......@@ -1051,6 +1160,7 @@ dl_cfgen( ConfigArgs *c )
for ( i = 0; dli; i++, dli = dli->dli_next ) {
struct berval bv;
char *ptr = c->cr_msg;
dynlist_map_t *dlm;
assert( dli->dli_oc != NULL );
assert( dli->dli_ad != NULL );
......@@ -1060,10 +1170,16 @@ dl_cfgen( ConfigArgs *c )
dli->dli_oc->soc_cname.bv_val,
dli->dli_ad->ad_cname.bv_val );
if ( dli->dli_member_ad != NULL ) {
for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
ptr[ 0 ] = ' ';
ptr++;
ptr = lutil_strcopy( ptr, dli->dli_member_ad->ad_cname.bv_val );
if ( dlm->dlm_mapped_ad ) {
ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val );
ptr[ 0 ] = ':';
ptr++;
}
ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val );
}
bv.bv_val = c->cr_msg;
......@@ -1091,9 +1207,18 @@ dl_cfgen( ConfigArgs *c )
dynlist_info_t *dli_next;
for ( dli_next = dli; dli_next; dli = dli_next ) {
dynlist_map_t *dlm = dli->dli_dlm;
dynlist_map_t *dlm_next;
dli_next = dli->dli_next;
ch_free( dli->dli_default_filter.bv_val );
while ( dlm != NULL ) {
dlm_next = dlm->dlm_next;
ch_free( dlm );
dlm = dlm_next;
}
ch_free( dli );
}
......@@ -1101,6 +1226,8 @@ dl_cfgen( ConfigArgs *c )
} else {
dynlist_info_t **dlip;
dynlist_map_t *dlm;
dynlist_map_t *dlm_next;
for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
i < c->valx; i++ )
......@@ -1114,6 +1241,13 @@ dl_cfgen( ConfigArgs *c )
dli = *dlip;
*dlip = dli->dli_next;
ch_free( dli->dli_default_filter.bv_val );
dlm = dli->dli_dlm;
while ( dlm != NULL ) {
dlm_next = dlm->dlm_next;
ch_free( dlm );
dlm = dlm_next;
}
ch_free( dli );
dli = (dynlist_info_t *)on->on_bi.bi_private;
......@@ -1138,14 +1272,13 @@ dl_cfgen( ConfigArgs *c )
dynlist_info_t **dlip,
*dli_next = NULL;
ObjectClass *oc = NULL;
AttributeDescription *ad = NULL,
*member_ad = NULL;
AttributeDescription *ad = NULL;
dynlist_map_t *dlm = NULL;
const char *text;
oc = oc_find( c->argv[ 1 ] );
if ( oc == NULL ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
"unable to find ObjectClass \"%s\"",
c->argv[ 1 ] );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
......@@ -1155,8 +1288,7 @@ dl_cfgen( ConfigArgs *c )
rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
if ( rc != LDAP_SUCCESS ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
"unable to find AttributeDescription \"%s\"",
c->argv[ 2 ] );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
......@@ -1165,8 +1297,7 @@ dl_cfgen( ConfigArgs *c )
}
if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
"AttributeDescription \"%s\" "
"must be a subtype of \"labeledURI\"",
c->argv[ 2 ] );
......@@ -1175,36 +1306,59 @@ dl_cfgen( ConfigArgs *c )
return 1;
}
if ( c->argc == 4 ) {
rc = slap_str2ad( c->argv[ 3 ], &member_ad, &text );
for ( i = 3; i < c->argc; i++ ) {
char *arg;
char *cp;
AttributeDescription *member_ad = NULL;
AttributeDescription *mapped_ad = NULL;
dynlist_map_t *dlmp;
dynlist_map_t *dlml;
/*
* If no mapped attribute is given, dn is used
* for backward compatibility.
*/
arg = c->argv[i];
if ( ( cp = strchr( arg, ':' ) ) != NULL ) {
struct berval bv;
ber_str2bv( arg, cp - arg, 0, &bv );
rc = slap_bv2ad( &bv, &mapped_ad, &text );
if ( rc != LDAP_SUCCESS ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
DYNLIST_USAGE
"unable to find mapped AttributeDescription #%d \"%s\"\n",
i - 3, c->argv[ i ] );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
c->log, c->cr_msg, 0 );
return 1;
}
arg = cp + 1;
}
rc = slap_str2ad( arg, &member_ad, &text );
if ( rc != LDAP_SUCCESS ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
"unable to find AttributeDescription \"%s\"\n",
c->argv[ 3 ] );
DYNLIST_USAGE
"unable to find AttributeDescription #%d \"%s\"\n",
i - 3, c->argv[ i ] );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
c->log, c->cr_msg, 0 );
return 1;
}
}
for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
*dlip; dlip = &(*dlip)->dli_next )
{
/* The same URL attribute / member attribute pair
* cannot be repeated */
if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
"URL attributeDescription \"%s\" already mapped.\n",
ad->ad_cname.bv_val );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
c->log, c->cr_msg, 0 );
#if 0
/* make it a warning... */
return 1;
#endif
dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
if ( dlm == NULL ) {
dlm = dlmp;
dlml = NULL;
}
dlmp->dlm_member_ad = member_ad;
dlmp->dlm_mapped_ad = mapped_ad;
dlmp->dlm_next = NULL;
if ( dlml != NULL )
dlml->dlm_next = dlmp;
dlml = dlmp;
}
if ( c->valx > 0 ) {
......@@ -1215,7 +1369,7 @@ dl_cfgen( ConfigArgs *c )
{
if ( *dlip == NULL ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
DYNLIST_USAGE
"invalid index {%d}\n",
c->valx );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
......@@ -1236,7 +1390,7 @@ dl_cfgen( ConfigArgs *c )
(*dlip)->dli_oc = oc;
(*dlip)->dli_ad = ad;
(*dlip)->dli_member_ad = member_ad;
(*dlip)->dli_dlm = dlm;
(*dlip)->dli_next = dli_next;
rc = dynlist_build_def_filter( *dlip );
......@@ -1291,7 +1445,7 @@ dl_cfgen( ConfigArgs *c )
if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
DYNLIST_USAGE
"AttributeDescription \"%s\" "
"must be a subtype of \"labeledURI\"",
c->argv[ 2 ] );
......@@ -1303,9 +1457,17 @@ dl_cfgen( ConfigArgs *c )
for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
*dlip; dlip = &(*dlip)->dli_next )
{
/* The same URL attribute / member attribute pair
* cannot be repeated */
if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
/*
* The same URL attribute / member attribute pair
* cannot be repeated, but we enforce this only
* when the member attribute is unique. Performing
* the check for multiple values would require
* sorting and comparing the lists, which is left
* as a future improvement
*/
if ( (*dlip)->dli_ad == ad &&
(*dlip)->dli_dlm->dlm_next == NULL &&
member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"\"dynlist-attrpair <member-ad> <URL-ad>\": "
"URL attributeDescription \"%s\" already mapped.