]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#10104 Add slapo-alias to contrib
authorOndřej Kuzník <ondra@mistotebe.net>
Mon, 25 Sep 2023 08:44:35 +0000 (09:44 +0100)
committerQuanah Gibson-Mount <quanah@openldap.org>
Tue, 2 Apr 2024 20:07:30 +0000 (20:07 +0000)
33 files changed:
contrib/ConfigOIDs
contrib/slapd-modules/alias/.gitignore [new file with mode: 0644]
contrib/slapd-modules/alias/Makefile [new file with mode: 0644]
contrib/slapd-modules/alias/alias.c [new file with mode: 0644]
contrib/slapd-modules/alias/slapo-alias.5 [new file with mode: 0644]
contrib/slapd-modules/alias/tests/.gitignore [new file with mode: 0644]
contrib/slapd-modules/alias/tests/Rules.mk [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/alias.conf [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/config.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-00a-invalid.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-00b-invalid.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-01a-same-alias.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-01b-same-attr.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-01c-chained.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-01d-chained.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-02a-operational.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-02b-single.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-02c-syntax.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-02d-matching.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test001-02e-no-ordering.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test002-add-rdn.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test002-add.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test002-delete.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test002-modify.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test002-modrdn.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test003-config.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/data/test003-out.ldif [new file with mode: 0644]
contrib/slapd-modules/alias/tests/run [new file with mode: 0755]
contrib/slapd-modules/alias/tests/scripts/all [new file with mode: 0755]
contrib/slapd-modules/alias/tests/scripts/common.sh [new file with mode: 0755]
contrib/slapd-modules/alias/tests/scripts/test001-config [new file with mode: 0755]
contrib/slapd-modules/alias/tests/scripts/test002-add-delete [new file with mode: 0755]
contrib/slapd-modules/alias/tests/scripts/test003-search [new file with mode: 0755]

index fc76af6b0817f07b0b2636dbf146af702037f90b..04ebe1d417148edf93264fa317b2dc37944d842e 100644 (file)
@@ -9,3 +9,4 @@ OLcfgCt{Oc|At}:6        adremap
 OLcfgCt{Oc|At}:7       rbac
 OLcfgCt{Oc|At}:8       datamorph
 OLcfgCt{Oc|At}:9       variant
+OLcfgCt{Oc|At}:10      alias
diff --git a/contrib/slapd-modules/alias/.gitignore b/contrib/slapd-modules/alias/.gitignore
new file mode 100644 (file)
index 0000000..e5fb152
--- /dev/null
@@ -0,0 +1,3 @@
+# test suite
+clients
+servers
diff --git a/contrib/slapd-modules/alias/Makefile b/contrib/slapd-modules/alias/Makefile
new file mode 100644 (file)
index 0000000..c1be15b
--- /dev/null
@@ -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 (file)
index 0000000..5d74aaa
--- /dev/null
@@ -0,0 +1,671 @@
+/* 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 );
+               } else {
+                       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 (file)
index 0000000..4f5fb29
--- /dev/null
@@ -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/.gitignore b/contrib/slapd-modules/alias/tests/.gitignore
new file mode 100644 (file)
index 0000000..9253831
--- /dev/null
@@ -0,0 +1,4 @@
+progs
+schema
+testdata
+testrun
diff --git a/contrib/slapd-modules/alias/tests/Rules.mk b/contrib/slapd-modules/alias/tests/Rules.mk
new file mode 100644 (file)
index 0000000..c25c1d2
--- /dev/null
@@ -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 (file)
index 0000000..5997666
--- /dev/null
@@ -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 (file)
index 0000000..9c676a9
--- /dev/null
@@ -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 (file)
index 0000000..f0eff6b
--- /dev/null
@@ -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 (file)
index 0000000..f351ced
--- /dev/null
@@ -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 (file)
index 0000000..db851ff
--- /dev/null
@@ -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 (file)
index 0000000..07275be
--- /dev/null
@@ -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 (file)
index 0000000..92d466d
--- /dev/null
@@ -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 (file)
index 0000000..efeaac0
--- /dev/null
@@ -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 (file)
index 0000000..1c10aa6
--- /dev/null
@@ -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 (file)
index 0000000..fe464e7
--- /dev/null
@@ -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 (file)
index 0000000..8c24f5d
--- /dev/null
@@ -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 (file)
index 0000000..7f80402
--- /dev/null
@@ -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 (file)
index 0000000..ce2a7ae
--- /dev/null
@@ -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 (file)
index 0000000..23e17c0
--- /dev/null
@@ -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 (file)
index 0000000..330bd9a
--- /dev/null
@@ -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 (file)
index 0000000..e6932e4
--- /dev/null
@@ -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 (file)
index 0000000..730dcbb
--- /dev/null
@@ -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 (file)
index 0000000..1ad729b
--- /dev/null
@@ -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 (file)
index 0000000..322fcd5
--- /dev/null
@@ -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 (file)
index 0000000..0aa02e6
--- /dev/null
@@ -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 (executable)
index 0000000..239bff7
--- /dev/null
@@ -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 (executable)
index 0000000..5af7083
--- /dev/null
@@ -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 (executable)
index 0000000..a2e2922
--- /dev/null
@@ -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 (executable)
index 0000000..fa68e67
--- /dev/null
@@ -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 (executable)
index 0000000..c080859
--- /dev/null
@@ -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 (executable)
index 0000000..467ec9f
--- /dev/null
@@ -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