summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/alias
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 05:34:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 05:34:59 +0000
commit36a5f1403a91d93db689e989ead3d9cf140c3cde (patch)
tree278cc6e7860205b0d40a895b977a46ea09018eb2 /contrib/slapd-modules/alias
parentReleasing progress-linux version 2.6.7+dfsg-1~exp1~progress7.99u1. (diff)
downloadopenldap-36a5f1403a91d93db689e989ead3d9cf140c3cde.tar.xz
openldap-36a5f1403a91d93db689e989ead3d9cf140c3cde.zip
Merging upstream version 2.6.8+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/slapd-modules/alias')
-rw-r--r--contrib/slapd-modules/alias/Makefile82
-rw-r--r--contrib/slapd-modules/alias/alias.c673
-rw-r--r--contrib/slapd-modules/alias/slapo-alias.5121
-rw-r--r--contrib/slapd-modules/alias/tests/Rules.mk23
-rw-r--r--contrib/slapd-modules/alias/tests/data/alias.conf4
-rw-r--r--contrib/slapd-modules/alias/tests/data/config.ldif5
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-00a-invalid.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-00b-invalid.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-01a-same-alias.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-01b-same-attr.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-01c-chained.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-01d-chained.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-02a-operational.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-02b-single.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-02c-syntax.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-02d-matching.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test001-02e-no-ordering.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test002-add-rdn.ldif5
-rw-r--r--contrib/slapd-modules/alias/tests/data/test002-add.ldif18
-rw-r--r--contrib/slapd-modules/alias/tests/data/test002-delete.ldif3
-rw-r--r--contrib/slapd-modules/alias/tests/data/test002-modify.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test002-modrdn.ldif5
-rw-r--r--contrib/slapd-modules/alias/tests/data/test003-config.ldif4
-rw-r--r--contrib/slapd-modules/alias/tests/data/test003-out.ldif66
-rwxr-xr-xcontrib/slapd-modules/alias/tests/run17
-rwxr-xr-xcontrib/slapd-modules/alias/tests/scripts/all93
-rwxr-xr-xcontrib/slapd-modules/alias/tests/scripts/common.sh105
-rwxr-xr-xcontrib/slapd-modules/alias/tests/scripts/test001-config248
-rwxr-xr-xcontrib/slapd-modules/alias/tests/scripts/test002-add-delete76
-rwxr-xr-xcontrib/slapd-modules/alias/tests/scripts/test003-search151
30 files changed, 1747 insertions, 0 deletions
diff --git a/contrib/slapd-modules/alias/Makefile b/contrib/slapd-modules/alias/Makefile
new file mode 100644
index 0000000..c1be15b
--- /dev/null
+++ b/contrib/slapd-modules/alias/Makefile
@@ -0,0 +1,82 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2023 The OpenLDAP Foundation.
+# Copyright 2023 Ondřej Kuzník, Symas Corp. 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>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+SRCDIR = ./
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+PLAT = UNIX
+NT_LIB = -L$(LDAP_BUILD)/servers/slapd -lslapd
+NT_LDFLAGS = -no-undefined -avoid-version
+UNIX_LDFLAGS = -version-info $(LTVER)
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_ALIAS=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $($(PLAT)_LIB) $(LDAP_LIB)
+LD_FLAGS = $(LDFLAGS) $($(PLAT)_LDFLAGS) -rpath $(moduledir) -module
+
+PROGRAMS = alias.la
+MANPAGES = slapo-alias.5
+CLEAN = *.o *.lo *.la .libs
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: $(PROGRAMS)
+
+d :=
+sp :=
+dir := tests
+include $(dir)/Rules.mk
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+alias.la: alias.lo
+ $(LIBTOOL) --mode=link $(CC) $(LD_FLAGS) -o $@ $? $(LIBS)
+
+clean:
+ rm -rf $(CLEAN)
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/alias/alias.c b/contrib/slapd-modules/alias/alias.c
new file mode 100644
index 0000000..c5707ff
--- /dev/null
+++ b/contrib/slapd-modules/alias/alias.c
@@ -0,0 +1,673 @@
+/* alias.c - expose an attribute under a different name */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2023 The OpenLDAP Foundation.
+ * 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed in 2023 by Ondřej Kuzník for Symas Corp.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_ALIAS
+
+#include <inttypes.h>
+#include <ac/stdlib.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ldap_queue.h"
+
+typedef struct alias_mapping_t {
+ AttributeDescription *source;
+ AttributeDescription *alias;
+} alias_mapping;
+
+typedef struct alias_info_t {
+ alias_mapping *mappings;
+} alias_info;
+
+typedef struct alias_sc_private_t {
+ slap_overinst *on;
+ AttributeName *attrs_orig, *attrs_new;
+} alias_sc_private;
+
+static alias_mapping *
+attribute_mapped( alias_info *ov, AttributeDescription *ad )
+{
+ alias_mapping *m;
+
+ for ( m = ov->mappings; m && m->source; m++ ) {
+ if ( ad == m->alias ) return m;
+ }
+
+ return NULL;
+}
+
+static int
+alias_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ alias_info *ov = on->on_bi.bi_private;
+ Entry *e = op->ora_e;
+ Attribute *a;
+ int rc = LDAP_SUCCESS;
+
+ if ( !BER_BVISEMPTY( &e->e_nname ) ) {
+ LDAPRDN rDN;
+ const char *p;
+ int i;
+
+ rc = ldap_bv2rdn_x( &e->e_nname, &rDN, (char **)&p, LDAP_DN_FORMAT_LDAP,
+ op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "alias_op_add: "
+ "can't parse rdn: dn=%s\n",
+ op->o_req_ndn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; rDN[i]; i++ ) {
+ AttributeDescription *ad = NULL;
+
+ /* If we can't resolve the attribute, ignore it */
+ if ( slap_bv2ad( &rDN[i]->la_attr, &ad, &p ) ) {
+ continue;
+ }
+
+ if ( attribute_mapped( ov, ad ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ break;
+ }
+ }
+
+ ldap_rdnfree_x( rDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, rc,
+ "trying to add a virtual attribute in RDN" );
+ return rc;
+ }
+ }
+
+ for ( a = e->e_attrs; a; a = a->a_next ) {
+ if ( attribute_mapped( ov, a->a_desc ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ send_ldap_error( op, rs, rc,
+ "trying to add a virtual attribute" );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+alias_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ alias_info *ov = on->on_bi.bi_private;
+ alias_mapping *alias = attribute_mapped( ov, op->orc_ava->aa_desc );
+
+ if ( alias )
+ op->orc_ava->aa_desc = alias->source;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+alias_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ alias_info *ov = on->on_bi.bi_private;
+ Modifications *mod;
+ int rc = LDAP_CONSTRAINT_VIOLATION;
+
+ for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
+ if ( attribute_mapped( ov, mod->sml_desc ) ) {
+ send_ldap_error( op, rs, rc,
+ "trying to modify a virtual attribute" );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+alias_op_modrdn( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ alias_info *ov = on->on_bi.bi_private;
+ LDAPRDN rDN;
+ const char *p;
+ int i, rc = SLAP_CB_CONTINUE;
+
+ rc = ldap_bv2rdn_x( &op->orr_nnewrdn, &rDN, (char **)&p,
+ LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "alias_op_modrdn: "
+ "can't parse rdn for dn=%s\n",
+ op->o_req_ndn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; rDN[i]; i++ ) {
+ AttributeDescription *ad = NULL;
+
+ /* If we can't resolve the attribute, ignore it */
+ if ( slap_bv2ad( &rDN[i]->la_attr, &ad, &p ) ) {
+ continue;
+ }
+
+ if ( attribute_mapped( ov, ad ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ break;
+ }
+ }
+
+ ldap_rdnfree_x( rDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, rc,
+ "trying to add a virtual attribute in RDN" );
+ return rc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+alias_response_cleanup( Operation *op, SlapReply *rs )
+{
+ alias_sc_private *data = op->o_callback->sc_private;
+
+ if ( rs->sr_type == REP_RESULT || op->o_abandon ||
+ rs->sr_err == SLAPD_ABANDON )
+ {
+ if ( op->ors_attrs == data->attrs_new )
+ op->ors_attrs = data->attrs_orig;
+
+ ch_free( data->attrs_new );
+ ch_free( op->o_callback );
+ op->o_callback = NULL;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+alias_response( Operation *op, SlapReply *rs )
+{
+ alias_sc_private *data = op->o_callback->sc_private;
+ slap_overinst *on = data->on;
+ alias_info *ov = on->on_bi.bi_private;
+ Entry *e = NULL, *e_orig = rs->sr_entry;
+ alias_mapping *mapping;
+ int rc = SLAP_CB_CONTINUE;
+
+ if ( rs->sr_type != REP_SEARCH || !e_orig ) {
+ return rc;
+ }
+
+ for ( mapping = ov->mappings; mapping && mapping->source; mapping++ ) {
+ Attribute *source, *a;
+ int operational = is_at_operational( mapping->source->ad_type ),
+ keep_source = 0;
+ slap_mask_t requested = operational ?
+ SLAP_OPATTRS_YES : SLAP_USERATTRS_YES;
+
+ if ( !(requested & rs->sr_attr_flags) &&
+ !ad_inlist( mapping->alias, rs->sr_attrs ) )
+ continue;
+
+ /* TODO: deal with multiple aliases from the same source */
+ if ( (requested & rs->sr_attr_flags) ||
+ ad_inlist( mapping->source, data->attrs_orig ) ) {
+ keep_source = 1;
+ }
+
+ if ( operational ) {
+ source = attr_find( rs->sr_operational_attrs, mapping->source );
+ }
+ if ( !source ) {
+ operational = 0;
+ source = attr_find( e_orig->e_attrs, mapping->source );
+ }
+ if ( !source )
+ continue;
+
+ if ( operational ) {
+ if ( !keep_source ) {
+ source->a_desc = mapping->alias;
+ } else {
+ Attribute **ap;
+
+ a = attr_dup( source );
+ a->a_desc = mapping->alias;
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap=&(*ap)->a_next );
+ *ap = a;
+ }
+ continue;
+ }
+
+ if ( !e ) {
+ if ( rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
+ e = e_orig;
+ } else {
+ e = entry_dup( e_orig );
+ }
+ }
+
+ a = attr_find( e->e_attrs, mapping->source );
+ if ( !keep_source ) {
+ a->a_desc = mapping->alias;
+ } else {
+ attr_merge( e, mapping->alias, a->a_vals, a->a_nvals );
+ }
+ }
+
+ if ( e && e != e_orig ) {
+ rs_replace_entry( op, rs, on, e );
+ rs->sr_flags &= ~REP_ENTRY_MASK;
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+
+ return rc;
+}
+
+static int
+alias_filter( alias_info *ov, Filter *f )
+{
+ int changed = 0;
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR: {
+ for ( f = f->f_and; f; f = f->f_next ) {
+ int result = alias_filter( ov, f );
+ if ( result < 0 ) {
+ return result;
+ }
+ changed += result;
+ }
+ } break;
+
+ case LDAP_FILTER_NOT:
+ return alias_filter( ov, f->f_not );
+
+ case LDAP_FILTER_PRESENT: {
+ alias_mapping *alias = attribute_mapped( ov, f->f_desc );
+ if ( alias ) {
+ f->f_desc = alias->source;
+ changed = 1;
+ }
+ } break;
+
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE: {
+ alias_mapping *alias = attribute_mapped( ov, f->f_av_desc );
+ if ( alias ) {
+ f->f_av_desc = alias->source;
+ changed = 1;
+ }
+ } break;
+
+ case LDAP_FILTER_SUBSTRINGS: {
+ alias_mapping *alias = attribute_mapped( ov, f->f_sub_desc );
+ if ( alias ) {
+ f->f_sub_desc = alias->source;
+ changed = 1;
+ }
+ } break;
+
+ case LDAP_FILTER_EXT: {
+ alias_mapping *alias = attribute_mapped( ov, f->f_mr_desc );
+ if ( alias ) {
+ f->f_mr_desc = alias->source;
+ changed = 1;
+ }
+ } break;
+
+ default:
+ return -1;
+ }
+ return changed;
+}
+
+static int
+alias_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ alias_info *ov = on->on_bi.bi_private;
+ alias_mapping *mapping;
+ AttributeName *an_orig = NULL, *an_new = NULL;
+ int mapped, an_length = 0;
+
+ if ( get_manageDSAit( op ) )
+ return SLAP_CB_CONTINUE;
+
+ /*
+ * 1. check filter: traverse, map aliased attributes
+ * 2. unparse filter
+ * 3. check all requested attributes -> register callback if one matches
+ */
+ if ( (mapped = alias_filter( ov, op->ors_filter )) < 0 ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "alias_op_search: failed to process filter" );
+ return LDAP_OTHER;
+ }
+
+ if ( mapped ) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
+ }
+
+ mapped = 0;
+ for ( mapping = ov->mappings; mapping && mapping->source; mapping++ ) {
+ int operational = is_at_operational( mapping->source->ad_type );
+ slap_mask_t requested = operational ?
+ SLAP_OPATTRS_YES : SLAP_USERATTRS_YES;
+
+ if ( requested & slap_attr_flags( op->ors_attrs ) ) {
+ mapped = 1;
+ } else if ( ad_inlist( mapping->alias, op->ors_attrs ) ) {
+ mapped = 1;
+ if ( !an_length ) {
+ for ( ; !BER_BVISNULL( &op->ors_attrs[an_length].an_name ); an_length++ )
+ /* Count */;
+ }
+
+ an_new = ch_realloc( an_new, (an_length+2)*sizeof(AttributeName) );
+ if ( !an_orig ) {
+ int i;
+ an_orig = op->ors_attrs;
+ for ( i=0; i < an_length; i++ ) {
+ an_new[i] = an_orig[i];
+ }
+ }
+
+ an_new[an_length].an_name = mapping->source->ad_cname;
+ an_new[an_length].an_desc = mapping->source;
+ an_length++;
+
+ BER_BVZERO( &an_new[an_length].an_name );
+ }
+ }
+
+ if ( mapped ) {
+ /* We have something to map back */
+ slap_callback *cb = op->o_tmpcalloc( 1,
+ sizeof(slap_callback)+sizeof(alias_sc_private),
+ op->o_tmpmemctx );
+ alias_sc_private *data = (alias_sc_private *)(cb+1);
+
+ data->on = on;
+
+ cb->sc_response = alias_response;
+ cb->sc_private = data;
+ cb->sc_next = op->o_callback;
+ cb->sc_cleanup = alias_response_cleanup;
+
+ if ( an_new ) {
+ data->attrs_orig = an_orig;
+ data->attrs_new = an_new;
+ op->ors_attrs = an_new;
+ }
+
+ op->o_callback = cb;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/* Configuration */
+
+static ConfigDriver alias_config_mapping;
+
+static ConfigTable alias_cfg[] = {
+ { "alias_attribute", "attr> <attr", 3, 3, 0,
+ ARG_MAGIC,
+ alias_config_mapping,
+ "( OLcfgCtAt:10.1 NAME 'olcAliasMapping' "
+ "DESC 'Alias definition' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+/*
+ * FIXME: There is no reason to keep olcAliasMapping MAY (making this overlay
+ * a noop) except we can't enforce a MUST with slaptest+slapd.conf.
+ */
+static ConfigOCs alias_ocs[] = {
+ { "( OLcfgCtOc:10.1 "
+ "NAME 'olcAliasConfig' "
+ "DESC 'Alias overlay configuration' "
+ "MAY ( olcAliasMapping ) "
+ "SUP olcOverlayConfig )",
+ Cft_Overlay, alias_cfg },
+
+ { NULL, 0, NULL }
+};
+
+static int
+alias_config_mapping( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ alias_info *ov = on->on_bi.bi_private;
+ AttributeDescription *source = NULL, *alias = NULL;
+ AttributeType *sat, *aat;
+ const char *text;
+ int i, rc = LDAP_CONSTRAINT_VIOLATION;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ alias_mapping *mapping;
+
+ for ( mapping = ov->mappings; mapping && mapping->source; mapping++ ) {
+ char buf[SLAP_TEXT_BUFLEN];
+ struct berval bv = { .bv_val = buf, .bv_len = SLAP_TEXT_BUFLEN };
+ bv.bv_len = snprintf( buf, bv.bv_len, "%s %s",
+ mapping->source->ad_cname.bv_val,
+ mapping->alias->ad_cname.bv_val );
+ value_add_one( &ca->rvalue_vals, &bv );
+ }
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ if ( ca->valx < 0 ) {
+ ch_free( ov->mappings );
+ ov->mappings = NULL;
+ } else {
+ i = ca->valx;
+ do {
+ ov->mappings[i] = ov->mappings[i+1];
+ i++;
+ } while ( ov->mappings[i].source );
+ }
+ return LDAP_SUCCESS;
+ }
+
+ rc = slap_str2ad( ca->argv[1], &source, &text );
+ if ( rc ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "cannot resolve attribute '%s': \"%s\"",
+ ca->argv[1], text );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ goto done;
+ }
+
+ rc = slap_str2ad( ca->argv[2], &alias, &text );
+ if ( rc ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "cannot resolve attribute '%s': \"%s\"",
+ ca->argv[2], text );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ goto done;
+ }
+
+ sat = source->ad_type;
+ aat = alias->ad_type;
+ if ( sat == aat ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "cannot map attribute %s to itself",
+ source->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ /* The types have to match */
+ if ( is_at_operational( sat ) != is_at_operational( aat ) ||
+ is_at_single_value( sat ) != is_at_single_value( aat ) ||
+ sat->sat_syntax != aat->sat_syntax ||
+ sat->sat_equality != aat->sat_equality ||
+ sat->sat_approx != aat->sat_approx ||
+ sat->sat_ordering != aat->sat_ordering ||
+ sat->sat_substr != aat->sat_substr ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "attributes %s and %s syntax and/or "
+ "default matching rules don't match",
+ source->ad_cname.bv_val,
+ alias->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ if ( !ov->mappings ) {
+ ov->mappings = ch_calloc( 2, sizeof(alias_mapping) );
+ ov->mappings[0].source = source;
+ ov->mappings[0].alias = alias;
+ } else {
+ int i;
+
+ for ( i = 0; ov->mappings[i].source; i++ ) {
+ if ( alias == ov->mappings[i].alias ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "attribute %s already mapped from %s",
+ alias->ad_cname.bv_val,
+ ov->mappings[i].source->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ if ( alias == ov->mappings[i].source ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "cannot use %s as alias source, already mapped from %s",
+ source->ad_cname.bv_val,
+ ov->mappings[i].source->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ if ( source == ov->mappings[i].alias ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "cannot use %s as alias, it is aliased to %s",
+ alias->ad_cname.bv_val,
+ ov->mappings[i].alias->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ }
+
+ if ( ca->valx < 0 || ca->valx > i )
+ ca->valx = i;
+
+ i++;
+ ov->mappings = ch_realloc( ov->mappings, (i + 1) * sizeof(alias_mapping) );
+ do {
+ ov->mappings[i] = ov->mappings[i-1];
+ } while ( --i > ca->valx );
+ ov->mappings[i].source = source;
+ ov->mappings[i].alias = alias;
+ }
+
+ rc = LDAP_SUCCESS;
+done:
+ ca->reply.err = rc;
+ return rc;
+}
+
+static slap_overinst alias;
+
+static int
+alias_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ alias_info *ov;
+
+ /* TODO: can this be global? */
+ if ( SLAP_ISGLOBALOVERLAY(be) ) {
+ Debug( LDAP_DEBUG_ANY, "alias overlay must be instantiated "
+ "within a database.\n" );
+ return 1;
+ }
+
+ ov = ch_calloc( 1, sizeof(alias_info) );
+ on->on_bi.bi_private = ov;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+alias_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ alias_info *ov = on->on_bi.bi_private;
+
+ if ( ov && ov->mappings ) {
+ ch_free( ov->mappings );
+ }
+ ch_free( ov );
+
+ return LDAP_SUCCESS;
+}
+
+int
+alias_initialize()
+{
+ int rc;
+
+ alias.on_bi.bi_type = "alias";
+ alias.on_bi.bi_db_init = alias_db_init;
+ alias.on_bi.bi_db_destroy = alias_db_destroy;
+
+ alias.on_bi.bi_op_add = alias_op_add;
+ alias.on_bi.bi_op_compare = alias_op_compare;
+ alias.on_bi.bi_op_modify = alias_op_mod;
+ alias.on_bi.bi_op_modrdn = alias_op_modrdn;
+ alias.on_bi.bi_op_search = alias_op_search;
+
+ alias.on_bi.bi_cf_ocs = alias_ocs;
+
+ rc = config_register_schema( alias_cfg, alias_ocs );
+ if ( rc ) return rc;
+
+ return overlay_register( &alias );
+}
+
+#if SLAPD_OVER_ALIAS == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return alias_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_ALIAS */
diff --git a/contrib/slapd-modules/alias/slapo-alias.5 b/contrib/slapd-modules/alias/slapo-alias.5
new file mode 100644
index 0000000..4f5fb29
--- /dev/null
+++ b/contrib/slapd-modules/alias/slapo-alias.5
@@ -0,0 +1,121 @@
+.TH SLAPO-ALIAS 5 "RELEASEDATE" "OpenLDAP"
+.\" Copyright 2023 Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See LICENSE.
+.SH NAME
+slapo\-alias \- expose an attribute under a different name
+.SH SYNOPSIS
+olcOverlay=alias
+.SH DESCRIPTION
+The
+.B alias
+overlay to
+.BR slapd (8)
+allows migrations for existing attributes exposed through a name that is
+now deprecated where using
+.BR slapo-rwm (5)
+is not applicable. For this reason, the aliased attributes are not writable
+in any way. In particular:
+
+.RS
+.TP
+.B Search
+
+Instances of the aliased attribute in the
+.B Search
+request filter are replaced by the source attribute.
+
+If the attribute is requested, the values are copied from the source
+attribute, however unlike with
+.BR slapo-rwm (5),
+if the source attribute is also requested, both will be returned.
+.TP
+.B Compare
+The request is mapped to the source attribute before processing.
+.TP
+.B Add, Modify, ModRDN
+Requests affecting aliased attributes are rejected with a
+.B Constraint
+.BR Violation .
+.RE
+
+
+.SH CONFIGURATION LAYOUT
+
+The overlay has to be instantiated under a database adding an entry of
+.B olcOverlay=alias
+with objectClass of
+.BR olcAliasConfig.
+
+These are the available options:
+
+.RS
+.TP
+.B olcAliasMapping: <source-attribute> <aliased-attribute>
+Any time
+.B aliased-attribute
+is requested (explicitly or through
+.B * +
+shorthands), the values of
+.B source-attribute
+are returned. The attributes need to be compatible i.e. both have to be
+operational or neither should, same with the
+.B SINGLE-VALUE
+option, syntax or matching rules. The
+.BR slapd.conf (5)
+equivalent is
+.BR alias_attribute .
+It can be provided multiple times.
+.RE
+
+.SH EXAMPLE
+
+The following is an example of a configured overlay, substitute
+.B $DATABASE
+for the DN of the database it is attached to and
+.B {x}
+with the desired position of the overlay in the overlay stack.
+
+.nf
+dn: olcOverlay={x}alias,$DATABASE
+objectClass: olcAliasConfig
+olcOverlay: alias
+olcAliasMapping: source-attribute aliased-attribute
+.fi
+
+The
+.BR slapd.conf (5)
+equivalent of the above follows:
+
+.nf
+overlay alias
+
+alias_attribute source-attribute aliased-attribute
+.fi
+
+.SH NOTES
+When mapping an operational attribute, you might need to use
+.BR slapo-dsaschema (5)
+contrib module to provide its definition into the schema.
+
+.SH BUGS AND LIMITATIONS
+Setting ACLs that differ between the aliased and its source attribute is not
+supported, they have to match or risk information disclosure.
+
+It is also expected that the aliased attributes are never physically present in
+the database.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR slapd-config (5),
+.BR slapd.conf (5),
+.BR slapd.overlays (5),
+.BR slapo-dsaschema (5),
+.BR slapd (8)
+.SH ACKNOWLEDGEMENTS
+This module was developed in 2023 by Ondřej Kuzník for Symas Corp.
diff --git a/contrib/slapd-modules/alias/tests/Rules.mk b/contrib/slapd-modules/alias/tests/Rules.mk
new file mode 100644
index 0000000..c25c1d2
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/Rules.mk
@@ -0,0 +1,23 @@
+sp := $(sp).x
+dirstack_$(sp) := $(d)
+d := $(dir)
+
+.PHONY: test
+
+CLEAN += clients servers tests/progs tests/schema tests/testdata tests/testrun
+
+test: all clients servers tests/progs
+
+test:
+ cd tests; \
+ SRCDIR=$(abspath $(LDAP_SRC)) \
+ LDAP_BUILD=$(abspath $(LDAP_BUILD)) \
+ TOPDIR=$(abspath $(SRCDIR)) \
+ LIBTOOL=$(abspath $(LIBTOOL)) \
+ $(abspath $(SRCDIR))/tests/run all
+
+servers clients tests/progs:
+ ln -s $(abspath $(LDAP_BUILD))/$@ $@
+
+d := $(dirstack_$(sp))
+sp := $(basename $(sp))
diff --git a/contrib/slapd-modules/alias/tests/data/alias.conf b/contrib/slapd-modules/alias/tests/data/alias.conf
new file mode 100644
index 0000000..5997666
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/alias.conf
@@ -0,0 +1,4 @@
+overlay alias
+
+alias_attribute pager mobile
+
diff --git a/contrib/slapd-modules/alias/tests/data/config.ldif b/contrib/slapd-modules/alias/tests/data/config.ldif
new file mode 100644
index 0000000..9c676a9
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/config.ldif
@@ -0,0 +1,5 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectclass: olcAliasConfig
+olcAliasMapping: pager mobile
diff --git a/contrib/slapd-modules/alias/tests/data/test001-00a-invalid.ldif b/contrib/slapd-modules/alias/tests/data/test001-00a-invalid.ldif
new file mode 100644
index 0000000..f0eff6b
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-00a-invalid.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: description invalidAttr
diff --git a/contrib/slapd-modules/alias/tests/data/test001-00b-invalid.ldif b/contrib/slapd-modules/alias/tests/data/test001-00b-invalid.ldif
new file mode 100644
index 0000000..f351ced
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-00b-invalid.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: invalidAttr description
diff --git a/contrib/slapd-modules/alias/tests/data/test001-01a-same-alias.ldif b/contrib/slapd-modules/alias/tests/data/test001-01a-same-alias.ldif
new file mode 100644
index 0000000..db851ff
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-01a-same-alias.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: fax mobile
diff --git a/contrib/slapd-modules/alias/tests/data/test001-01b-same-attr.ldif b/contrib/slapd-modules/alias/tests/data/test001-01b-same-attr.ldif
new file mode 100644
index 0000000..07275be
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-01b-same-attr.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: c countryname
diff --git a/contrib/slapd-modules/alias/tests/data/test001-01c-chained.ldif b/contrib/slapd-modules/alias/tests/data/test001-01c-chained.ldif
new file mode 100644
index 0000000..92d466d
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-01c-chained.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: mobile fax
diff --git a/contrib/slapd-modules/alias/tests/data/test001-01d-chained.ldif b/contrib/slapd-modules/alias/tests/data/test001-01d-chained.ldif
new file mode 100644
index 0000000..efeaac0
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-01d-chained.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: fax pager
diff --git a/contrib/slapd-modules/alias/tests/data/test001-02a-operational.ldif b/contrib/slapd-modules/alias/tests/data/test001-02a-operational.ldif
new file mode 100644
index 0000000..1c10aa6
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-02a-operational.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: seeAlso entryDN
diff --git a/contrib/slapd-modules/alias/tests/data/test001-02b-single.ldif b/contrib/slapd-modules/alias/tests/data/test001-02b-single.ldif
new file mode 100644
index 0000000..fe464e7
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-02b-single.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: displayName employeeType
diff --git a/contrib/slapd-modules/alias/tests/data/test001-02c-syntax.ldif b/contrib/slapd-modules/alias/tests/data/test001-02c-syntax.ldif
new file mode 100644
index 0000000..8c24f5d
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-02c-syntax.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: dc description
diff --git a/contrib/slapd-modules/alias/tests/data/test001-02d-matching.ldif b/contrib/slapd-modules/alias/tests/data/test001-02d-matching.ldif
new file mode 100644
index 0000000..7f80402
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-02d-matching.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: memberUid mail
diff --git a/contrib/slapd-modules/alias/tests/data/test001-02e-no-ordering.ldif b/contrib/slapd-modules/alias/tests/data/test001-02e-no-ordering.ldif
new file mode 100644
index 0000000..ce2a7ae
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test001-02e-no-ordering.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: gidNumber ipServicePort
diff --git a/contrib/slapd-modules/alias/tests/data/test002-add-rdn.ldif b/contrib/slapd-modules/alias/tests/data/test002-add-rdn.ldif
new file mode 100644
index 0000000..23e17c0
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test002-add-rdn.ldif
@@ -0,0 +1,5 @@
+dn: mobile=\+1 313 555 4474,dc=example,dc=com
+changetype: add
+objectClass: OpenLDAPperson
+cn: Just a phone
+sn: Mobile
diff --git a/contrib/slapd-modules/alias/tests/data/test002-add.ldif b/contrib/slapd-modules/alias/tests/data/test002-add.ldif
new file mode 100644
index 0000000..330bd9a
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test002-add.ldif
@@ -0,0 +1,18 @@
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: add
+objectclass: testPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Anytown, MI 48103
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+testTime: 20050304001801.234Z
+mobile: +1 313 555 8866
diff --git a/contrib/slapd-modules/alias/tests/data/test002-delete.ldif b/contrib/slapd-modules/alias/tests/data/test002-delete.ldif
new file mode 100644
index 0000000..e6932e4
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test002-delete.ldif
@@ -0,0 +1,3 @@
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+delete: mobile
diff --git a/contrib/slapd-modules/alias/tests/data/test002-modify.ldif b/contrib/slapd-modules/alias/tests/data/test002-modify.ldif
new file mode 100644
index 0000000..730dcbb
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test002-modify.ldif
@@ -0,0 +1,4 @@
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+add: mobile
+mobile: +1 313 555 3665
diff --git a/contrib/slapd-modules/alias/tests/data/test002-modrdn.ldif b/contrib/slapd-modules/alias/tests/data/test002-modrdn.ldif
new file mode 100644
index 0000000..1ad729b
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test002-modrdn.ldif
@@ -0,0 +1,5 @@
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+changetype: modrdn
+newrdn: mobile=\+1 313 555 4474
+deleteoldrdn: 0
diff --git a/contrib/slapd-modules/alias/tests/data/test003-config.ldif b/contrib/slapd-modules/alias/tests/data/test003-config.ldif
new file mode 100644
index 0000000..322fcd5
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test003-config.ldif
@@ -0,0 +1,4 @@
+dn: olcOverlay={0}alias,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+add: olcAliasMapping
+olcAliasMapping: title employeeType
diff --git a/contrib/slapd-modules/alias/tests/data/test003-out.ldif b/contrib/slapd-modules/alias/tests/data/test003-out.ldif
new file mode 100644
index 0000000..0aa02e6
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/data/test003-out.ldif
@@ -0,0 +1,66 @@
+# Listing aliased attribute...
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+mobile: +1 313 555 3233
+
+
+# A search when aliased attribute is not requested...
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+pager: +1 313 555 3233
+
+
+# A search when both are requested (explicitly)...
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+pager: +1 313 555 3233
+mobile: +1 313 555 3233
+
+
+# A search when both are requested (implicitly)...
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+mobile: +1 313 555 3233
+
+
+# Testing searches filtering on aliased attributes...
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+mobile: +1 313 555 3233
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+mobile: +1 313 555 4474
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+mobile: +1 313 555 1220
+
+
+# Testing search with new attributes...
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+employeeType: Director, Embedded Systems
+mobile: +1 313 555 4474
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+employeeType: Director, UM Alumni Association
+mobile: +1 313 555 7671
+
diff --git a/contrib/slapd-modules/alias/tests/run b/contrib/slapd-modules/alias/tests/run
new file mode 100755
index 0000000..239bff7
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/run
@@ -0,0 +1,17 @@
+#!/bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## 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>.
+
+TOPSRCDIR="$SRCDIR" OBJDIR="${LDAP_BUILD}" SRCDIR="${SRCDIR}/tests" DEFSDIR="${SRCDIR}/scripts" SCRIPTDIR="${TOPDIR}/tests/scripts" "${LDAP_BUILD}/tests/run" $*
+
diff --git a/contrib/slapd-modules/alias/tests/scripts/all b/contrib/slapd-modules/alias/tests/scripts/all
new file mode 100755
index 0000000..5af7083
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/scripts/all
@@ -0,0 +1,93 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## 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>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in ${SCRIPTDIR}/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *.py) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for $BACKEND were ${TB}skipped${TN}."
diff --git a/contrib/slapd-modules/alias/tests/scripts/common.sh b/contrib/slapd-modules/alias/tests/scripts/common.sh
new file mode 100755
index 0000000..a2e2922
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/scripts/common.sh
@@ -0,0 +1,105 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2023 The OpenLDAP Foundation.
+## 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2022 by Ondřej Kuzník for Symas Corp.
+
+OVERLAY_CONFIG=${OVERLAY_CONFIG-data/config.ldif}
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIF
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+mkdir $TESTDIR/confdir
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $CONF1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+echo "Starting slapd on TCP/IP port $PORT1 for configuration..."
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -s base -b 'cn=module{0},cn=config' 1.1 >$TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: `pwd`/../alias.la
+EOMOD
+ ;;
+32)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../alias.la
+EOMOD
+ ;;
+*)
+ echo "Failed testing for module load entry"
+ exit $RC;
+ ;;
+esac
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Loading test alias configuration..."
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
diff --git a/contrib/slapd-modules/alias/tests/scripts/test001-config b/contrib/slapd-modules/alias/tests/scripts/test001-config
new file mode 100755
index 0000000..fa68e67
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/scripts/test001-config
@@ -0,0 +1,248 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2023 The OpenLDAP Foundation.
+## 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2023 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Applying invalid changes to config (should fail)..."
+for CHANGE in data/test001-*.ldif; do
+ echo "... $CHANGE"
+ . $CONFFILTER $BACKEND $MONITORDB < $CHANGE | \
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 17|19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+# We run this search after the changes above and before restart so we can also
+# check the reconfiguration attempts actually had no side effects
+echo "Saving search output before server restart..."
+echo "# search output from dynamically configured server..." >> $SERVER6OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER6OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $KILLPIDS
+KILLPIDS=""
+sleep $SLEEP0
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -F $TESTDIR/confdir -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Testing slapd.conf support..."
+mkdir $TESTDIR/conftest $DBDIR2
+. $CONFFILTER $BACKEND $MONITORDB < $CONFTWO \
+ | sed -e '/^argsfile.*/a\
+moduleload ../alias.la' \
+ -e '/database.*monitor/i\
+include data/alias.conf' \
+ > $CONF2
+echo "database config" >>$CONF2
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF2
+
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h $URI2 -d $LVL >> $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "# search output from server running from slapd.conf..." >> $SERVER2OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ >> $SERVER2OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT2..."
+kill -HUP $PID
+
+$SLAPD -Tt -f $CONF2 -F $TESTDIR/conftest -d $LVL >> $LOG3 2>&1
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+$SLAPD -F $TESTDIR/conftest -h $URI2 -d $LVL >> $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Gathering overlay configuration from both servers..."
+echo "# overlay configuration from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -b "olcOverlay={0}alias,olcDatabase={1}$BACKEND,cn=config" \
+ | sed -e "s/ {[0-9]*}/ /" -e "s/={[0-9]*}/=/g" \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# overlay configuration from server configured from slapd.conf..." >> $SERVER3OUT
+$LDAPSEARCH -D cn=config -H $URI2 -y $CONFIGPWF \
+ -b "olcOverlay={0}alias,olcDatabase={1}$BACKEND,cn=config" \
+ | sed -e "s/ {[0-9]*}/ /" -e "s/={[0-9]*}/=/g" \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# We've already filtered out the ordering markers, now sort the entries
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SERVER3OUT > $SERVER3FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $SERVER1OUT > $SERVER1FLT
+echo "Comparing filter output..."
+$CMP $SERVER3FLT $SERVER1FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SERVER1OUT $SERVER3OUT
+
+echo "Comparing search output on both servers..."
+echo "# search output from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# search output from server configured from slapd.conf..." >> $SERVER3OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SERVER1OUT > $SERVER1FLT
+$LDIFFILTER -s e < $SERVER2OUT > $SERVER2FLT
+$LDIFFILTER -s e < $SERVER3OUT > $SERVER3FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $SERVER6OUT > $SERVER6FLT
+echo "Comparing filter output..."
+$CMP $SERVER6FLT $SERVER1FLT > $CMPOUT && \
+$CMP $SERVER6FLT $SERVER2FLT > $CMPOUT && \
+$CMP $SERVER6FLT $SERVER3FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/alias/tests/scripts/test002-add-delete b/contrib/slapd-modules/alias/tests/scripts/test002-add-delete
new file mode 100755
index 0000000..c080859
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/scripts/test002-add-delete
@@ -0,0 +1,76 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2023 The OpenLDAP Foundation.
+## 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2023 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Applying changes affecting aliased attribute (should fail)..."
+for CHANGE in data/test002-*.ldif; do
+ echo "... $CHANGE"
+ $LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f $CHANGE >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+echo "Saving search output..."
+# We're just making sure no modifications made it to the DB, bypass
+# the overlay to be able to compare with ldif used to populate it.
+$LDAPSEARCH -M -b "$BASEDN" -H $URI1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/alias/tests/scripts/test003-search b/contrib/slapd-modules/alias/tests/scripts/test003-search
new file mode 100755
index 0000000..467ec9f
--- /dev/null
+++ b/contrib/slapd-modules/alias/tests/scripts/test003-search
@@ -0,0 +1,151 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## 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>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Comparing aliased attribute..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ "mobile:+1 313 555 7671" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPCOMPARE -H $URI1 \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ "mobile:+1 313 555 4177" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 5 ; then
+ echo "ldapcompare should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Listing alias attribute specifically..."
+echo "# Listing aliased attribute..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 "uid=bjensen" mobile \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Not asking for alias attribute..."
+echo >> $SEARCHOUT
+echo "# A search when aliased attribute is not requested..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 "uid=bjensen" pager \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving both the aliased attribute and the source..."
+echo >> $SEARCHOUT
+echo "# A search when both are requested (explicitly)..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 "uid=bjensen" mobile pager \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving both the aliased attribute and the source..."
+echo >> $SEARCHOUT
+echo "# A search when both are requested (implicitly)..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 "uid=bjensen" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on aliased attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on aliased attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(|(mobile=+1 313 555 3233)(mobile=*4474)(&(mobile=*)(uid=jdoe)))" \
+ mobile \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring alias definition..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches with new attributes..."
+echo >> $SEARCHOUT
+echo "# Testing search with new attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "employeetype=*director*" \
+ employeetype mobile \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0