From 2a6f233aaedd604765aea39122add2b78c6dfb4c Mon Sep 17 00:00:00 2001 From: Nathan Kinder Date: Tue, 30 Mar 2010 13:13:31 -0700 Subject: [PATCH] Add managed entries plug-in This adds a new managed entries plug-in. This plug-in allows one to have the Directory Server automatically maintain a set of entries that are based off of another type of entry (such as user private group entries based off of user entries). For more details, see the design document at: http://directory.fedoraproject.org/wiki/Managed_Entry_Design --- Makefile.am | 12 +- Makefile.in | 47 +- ldap/ldif/template-dse.ldif.in | 12 + ldap/schema/10mep-plugin.ldif | 104 ++ ldap/servers/plugins/mep/mep.c | 2207 +++++++++++++++++++++++++++++++++++++ ldap/servers/plugins/mep/mep.h | 124 +++ ldap/servers/slapd/slapi-plugin.h | 2 +- 7 files changed, 2503 insertions(+), 5 deletions(-) mode change 100644 => 100755 Makefile.in create mode 100644 ldap/schema/10mep-plugin.ldif create mode 100644 ldap/servers/plugins/mep/mep.c create mode 100644 ldap/servers/plugins/mep/mep.h mode change 100644 => 100755 ltmain.sh diff --git a/Makefile.am b/Makefile.am index c3ec2f6..6cfbef8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -176,7 +176,7 @@ endif serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \ libback-ldbm.la libchainingdb-plugin.la libcollation-plugin.la \ libcos-plugin.la libderef-plugin.la libdes-plugin.la libdistrib-plugin.la \ - libhttp-client-plugin.la liblinkedattrs-plugin.la \ + libhttp-client-plugin.la liblinkedattrs-plugin.la libmanagedentries-plugin.la \ libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-plugin.la \ libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \ libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \ @@ -251,6 +251,7 @@ schema_DATA = $(srcdir)/ldap/schema/00core.ldif \ $(srcdir)/ldap/schema/05rfc4523.ldif \ $(srcdir)/ldap/schema/05rfc4524.ldif \ $(srcdir)/ldap/schema/06inetorgperson.ldif \ + $(srcdir)/ldap/schema/10mep-plugin.ldif \ $(srcdir)/ldap/schema/10rfc2307.ldif \ $(srcdir)/ldap/schema/20subscriber.ldif \ $(srcdir)/ldap/schema/25java-object.ldif \ @@ -874,6 +875,15 @@ liblinkedattrs_plugin_la_LIBADD = libslapd.la $(NSPR_LINK) liblinkedattrs_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libmanagedentries-plugin +#------------------------ +libmanagedentries_plugin_la_SOURCES = ldap/servers/plugins/mep/mep.c + +libmanagedentries_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libmanagedentries_plugin_la_LIBADD = libslapd.la $(NSPR_LINK) +libmanagedentries_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libmemberof-plugin #------------------------ libmemberof_plugin_la_SOURCES= ldap/servers/plugins/memberof/memberof.c \ diff --git a/Makefile.in b/Makefile.in old mode 100644 new mode 100755 index bc4b1a6..f7c82fb --- a/Makefile.in +++ b/Makefile.in @@ -279,6 +279,12 @@ am_liblinkedattrs_plugin_la_OBJECTS = ldap/servers/plugins/linkedattrs/liblinked ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo liblinkedattrs_plugin_la_OBJECTS = \ $(am_liblinkedattrs_plugin_la_OBJECTS) +libmanagedentries_plugin_la_DEPENDENCIES = libslapd.la \ + $(am__DEPENDENCIES_1) +am_libmanagedentries_plugin_la_OBJECTS = \ + ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.lo +libmanagedentries_plugin_la_OBJECTS = \ + $(am_libmanagedentries_plugin_la_OBJECTS) libmemberof_plugin_la_DEPENDENCIES = libslapd.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) am_libmemberof_plugin_la_OBJECTS = ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo \ @@ -855,6 +861,7 @@ SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \ $(libdistrib_plugin_la_SOURCES) $(libdna_plugin_la_SOURCES) \ $(libhttp_client_plugin_la_SOURCES) \ $(liblinkedattrs_plugin_la_SOURCES) \ + $(libmanagedentries_plugin_la_SOURCES) \ $(libmemberof_plugin_la_SOURCES) $(libns_dshttpd_la_SOURCES) \ $(libpam_passthru_plugin_la_SOURCES) \ $(libpassthru_plugin_la_SOURCES) \ @@ -883,6 +890,7 @@ DIST_SOURCES = $(libavl_a_SOURCES) $(libldaputil_a_SOURCES) \ $(libdistrib_plugin_la_SOURCES) $(libdna_plugin_la_SOURCES) \ $(libhttp_client_plugin_la_SOURCES) \ $(liblinkedattrs_plugin_la_SOURCES) \ + $(libmanagedentries_plugin_la_SOURCES) \ $(libmemberof_plugin_la_SOURCES) $(libns_dshttpd_la_SOURCES) \ $(libpam_passthru_plugin_la_SOURCES) \ $(libpassthru_plugin_la_SOURCES) \ @@ -1250,7 +1258,7 @@ server_LTLIBRARIES = libslapd.la libns-dshttpd.la serverplugin_LTLIBRARIES = libacl-plugin.la libattr-unique-plugin.la \ libback-ldbm.la libchainingdb-plugin.la libcollation-plugin.la \ libcos-plugin.la libderef-plugin.la libdes-plugin.la libdistrib-plugin.la \ - libhttp-client-plugin.la liblinkedattrs-plugin.la \ + libhttp-client-plugin.la liblinkedattrs-plugin.la libmanagedentries-plugin.la \ libmemberof-plugin.la libpassthru-plugin.la libpwdstorage-plugin.la \ libreferint-plugin.la libreplication-plugin.la libretrocl-plugin.la \ libroles-plugin.la libstatechange-plugin.la libsyntax-plugin.la \ @@ -1323,6 +1331,7 @@ schema_DATA = $(srcdir)/ldap/schema/00core.ldif \ $(srcdir)/ldap/schema/05rfc4523.ldif \ $(srcdir)/ldap/schema/05rfc4524.ldif \ $(srcdir)/ldap/schema/06inetorgperson.ldif \ + $(srcdir)/ldap/schema/10mep-plugin.ldif \ $(srcdir)/ldap/schema/10rfc2307.ldif \ $(srcdir)/ldap/schema/20subscriber.ldif \ $(srcdir)/ldap/schema/25java-object.ldif \ @@ -1890,6 +1899,14 @@ liblinkedattrs_plugin_la_LIBADD = libslapd.la $(NSPR_LINK) liblinkedattrs_plugin_la_LDFLAGS = -avoid-version #------------------------ +# libmanagedentries-plugin +#------------------------ +libmanagedentries_plugin_la_SOURCES = ldap/servers/plugins/mep/mep.c +libmanagedentries_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) +libmanagedentries_plugin_la_LIBADD = libslapd.la $(NSPR_LINK) +libmanagedentries_plugin_la_LDFLAGS = -avoid-version + +#------------------------ # libmemberof-plugin #------------------------ libmemberof_plugin_la_SOURCES = ldap/servers/plugins/memberof/memberof.c \ @@ -2992,6 +3009,17 @@ ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo: \ ldap/servers/plugins/linkedattrs/$(DEPDIR)/$(am__dirstamp) liblinkedattrs-plugin.la: $(liblinkedattrs_plugin_la_OBJECTS) $(liblinkedattrs_plugin_la_DEPENDENCIES) $(LINK) -rpath $(serverplugindir) $(liblinkedattrs_plugin_la_LDFLAGS) $(liblinkedattrs_plugin_la_OBJECTS) $(liblinkedattrs_plugin_la_LIBADD) $(LIBS) +ldap/servers/plugins/mep/$(am__dirstamp): + @$(mkdir_p) ldap/servers/plugins/mep + @: > ldap/servers/plugins/mep/$(am__dirstamp) +ldap/servers/plugins/mep/$(DEPDIR)/$(am__dirstamp): + @$(mkdir_p) ldap/servers/plugins/mep/$(DEPDIR) + @: > ldap/servers/plugins/mep/$(DEPDIR)/$(am__dirstamp) +ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.lo: \ + ldap/servers/plugins/mep/$(am__dirstamp) \ + ldap/servers/plugins/mep/$(DEPDIR)/$(am__dirstamp) +libmanagedentries-plugin.la: $(libmanagedentries_plugin_la_OBJECTS) $(libmanagedentries_plugin_la_DEPENDENCIES) + $(LINK) -rpath $(serverplugindir) $(libmanagedentries_plugin_la_LDFLAGS) $(libmanagedentries_plugin_la_OBJECTS) $(libmanagedentries_plugin_la_LIBADD) $(LIBS) ldap/servers/plugins/memberof/$(am__dirstamp): @$(mkdir_p) ldap/servers/plugins/memberof @: > ldap/servers/plugins/memberof/$(am__dirstamp) @@ -4420,6 +4448,8 @@ mostlyclean-compile: -rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo -rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof_config.$(OBJEXT) -rm -f ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof_config.lo + -rm -f ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.$(OBJEXT) + -rm -f ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.lo -rm -f ldap/servers/plugins/pam_passthru/libpam_passthru_plugin_la-pam_ptconfig.$(OBJEXT) -rm -f ldap/servers/plugins/pam_passthru/libpam_passthru_plugin_la-pam_ptconfig.lo -rm -f ldap/servers/plugins/pam_passthru/libpam_passthru_plugin_la-pam_ptdebug.$(OBJEXT) @@ -5198,6 +5228,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/linkedattrs/$(DEPDIR)/liblinkedattrs_plugin_la-linked_attrs.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof_config.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/mep/$(DEPDIR)/libmanagedentries_plugin_la-mep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/pam_passthru/$(DEPDIR)/libpam_passthru_plugin_la-pam_ptconfig.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/pam_passthru/$(DEPDIR)/libpam_passthru_plugin_la-pam_ptdebug.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@ldap/servers/plugins/pam_passthru/$(DEPDIR)/libpam_passthru_plugin_la-pam_ptimpl.Plo@am__quote@ @@ -6551,6 +6582,13 @@ ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo: ldap/ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblinkedattrs_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/linkedattrs/liblinkedattrs_plugin_la-linked_attrs.lo `test -f 'ldap/servers/plugins/linkedattrs/linked_attrs.c' || echo '$(srcdir)/'`ldap/servers/plugins/linkedattrs/linked_attrs.c +ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.lo: ldap/servers/plugins/mep/mep.c +@am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmanagedentries_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.lo -MD -MP -MF "ldap/servers/plugins/mep/$(DEPDIR)/libmanagedentries_plugin_la-mep.Tpo" -c -o ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.lo `test -f 'ldap/servers/plugins/mep/mep.c' || echo '$(srcdir)/'`ldap/servers/plugins/mep/mep.c; \ +@am__fastdepCC_TRUE@ then mv -f "ldap/servers/plugins/mep/$(DEPDIR)/libmanagedentries_plugin_la-mep.Tpo" "ldap/servers/plugins/mep/$(DEPDIR)/libmanagedentries_plugin_la-mep.Plo"; else rm -f "ldap/servers/plugins/mep/$(DEPDIR)/libmanagedentries_plugin_la-mep.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ldap/servers/plugins/mep/mep.c' object='ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmanagedentries_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ldap/servers/plugins/mep/libmanagedentries_plugin_la-mep.lo `test -f 'ldap/servers/plugins/mep/mep.c' || echo '$(srcdir)/'`ldap/servers/plugins/mep/mep.c + ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo: ldap/servers/plugins/memberof/memberof.c @am__fastdepCC_TRUE@ if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmemberof_plugin_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo -MD -MP -MF "ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Tpo" -c -o ldap/servers/plugins/memberof/libmemberof_plugin_la-memberof.lo `test -f 'ldap/servers/plugins/memberof/memberof.c' || echo '$(srcdir)/'`ldap/servers/plugins/memberof/memberof.c; \ @am__fastdepCC_TRUE@ then mv -f "ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Tpo" "ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Plo"; else rm -f "ldap/servers/plugins/memberof/$(DEPDIR)/libmemberof_plugin_la-memberof.Tpo"; exit 1; fi @@ -9280,6 +9318,7 @@ clean-libtool: -rm -rf ldap/servers/plugins/http/.libs ldap/servers/plugins/http/_libs -rm -rf ldap/servers/plugins/linkedattrs/.libs ldap/servers/plugins/linkedattrs/_libs -rm -rf ldap/servers/plugins/memberof/.libs ldap/servers/plugins/memberof/_libs + -rm -rf ldap/servers/plugins/mep/.libs ldap/servers/plugins/mep/_libs -rm -rf ldap/servers/plugins/pam_passthru/.libs ldap/servers/plugins/pam_passthru/_libs -rm -rf ldap/servers/plugins/passthru/.libs ldap/servers/plugins/passthru/_libs -rm -rf ldap/servers/plugins/presence/.libs ldap/servers/plugins/presence/_libs @@ -9797,6 +9836,8 @@ distclean-generic: -rm -f ldap/servers/plugins/linkedattrs/$(am__dirstamp) -rm -f ldap/servers/plugins/memberof/$(DEPDIR)/$(am__dirstamp) -rm -f ldap/servers/plugins/memberof/$(am__dirstamp) + -rm -f ldap/servers/plugins/mep/$(DEPDIR)/$(am__dirstamp) + -rm -f ldap/servers/plugins/mep/$(am__dirstamp) -rm -f ldap/servers/plugins/pam_passthru/$(DEPDIR)/$(am__dirstamp) -rm -f ldap/servers/plugins/pam_passthru/$(am__dirstamp) -rm -f ldap/servers/plugins/passthru/$(DEPDIR)/$(am__dirstamp) @@ -9867,7 +9908,7 @@ clean-am: clean-binPROGRAMS clean-generic clean-libtool clean-local \ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR) + -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags @@ -9901,7 +9942,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR) + -rm -rf ldap/libraries/libavl/$(DEPDIR) ldap/servers/plugins/acl/$(DEPDIR) ldap/servers/plugins/bitwise/$(DEPDIR) ldap/servers/plugins/chainingdb/$(DEPDIR) ldap/servers/plugins/collation/$(DEPDIR) ldap/servers/plugins/cos/$(DEPDIR) ldap/servers/plugins/deref/$(DEPDIR) ldap/servers/plugins/distrib/$(DEPDIR) ldap/servers/plugins/dna/$(DEPDIR) ldap/servers/plugins/http/$(DEPDIR) ldap/servers/plugins/linkedattrs/$(DEPDIR) ldap/servers/plugins/memberof/$(DEPDIR) ldap/servers/plugins/mep/$(DEPDIR) ldap/servers/plugins/pam_passthru/$(DEPDIR) ldap/servers/plugins/passthru/$(DEPDIR) ldap/servers/plugins/presence/$(DEPDIR) ldap/servers/plugins/pwdstorage/$(DEPDIR) ldap/servers/plugins/referint/$(DEPDIR) ldap/servers/plugins/replication/$(DEPDIR) ldap/servers/plugins/retrocl/$(DEPDIR) ldap/servers/plugins/rever/$(DEPDIR) ldap/servers/plugins/roles/$(DEPDIR) ldap/servers/plugins/schema_reload/$(DEPDIR) ldap/servers/plugins/shared/$(DEPDIR) ldap/servers/plugins/statechange/$(DEPDIR) ldap/servers/plugins/syntaxes/$(DEPDIR) ldap/servers/plugins/uiduniq/$(DEPDIR) ldap/servers/plugins/usn/$(DEPDIR) ldap/servers/plugins/views/$(DEPDIR) ldap/servers/slapd/$(DEPDIR) ldap/servers/slapd/back-ldbm/$(DEPDIR) ldap/servers/slapd/tools/$(DEPDIR) ldap/servers/slapd/tools/ldclt/$(DEPDIR) ldap/servers/slapd/tools/rsearch/$(DEPDIR) ldap/servers/snmp/$(DEPDIR) ldap/systools/$(DEPDIR) lib/base/$(DEPDIR) lib/ldaputil/$(DEPDIR) lib/libaccess/$(DEPDIR) lib/libadmin/$(DEPDIR) lib/libsi18n/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in index 8aa1b0b..ca2ede8 100644 --- a/ldap/ldif/template-dse.ldif.in +++ b/ldap/ldif/template-dse.ldif.in @@ -690,6 +690,18 @@ nsslapd-plugintype: preoperation nsslapd-pluginenabled: on nsslapd-plugin-depends-on-type: database +dn: cn=Managed Entries,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +objectclass: nsContainer +cn: Managed Entries +nsslapd-pluginpath: libmanagedentries-plugin +nsslapd-plugininitfunc: mep_init +nsslapd-plugintype: preoperation +nsslapd-pluginenabled: on +nsslapd-plugin-depends-on-type: database + dn: cn=Pass Through Authentication,cn=plugins,cn=config objectclass: top objectclass: nsSlapdPlugin diff --git a/ldap/schema/10mep-plugin.ldif b/ldap/schema/10mep-plugin.ldif new file mode 100644 index 0000000..fc1f2bd --- /dev/null +++ b/ldap/schema/10mep-plugin.ldif @@ -0,0 +1,104 @@ +# +# BEGIN COPYRIGHT BLOCK +# This Program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; version 2 of the License. +# +# This Program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with +# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple +# Place, Suite 330, Boston, MA 02111-1307 USA. +# +# In addition, as a special exception, Red Hat, Inc. gives You the additional +# right to link the code of this Program with code not covered under the GNU +# General Public License ("Non-GPL Code") and to distribute linked combinations +# including the two, subject to the limitations in this paragraph. Non-GPL Code +# permitted under this exception must only link to the code of this Program +# through those well defined interfaces identified in the file named EXCEPTION +# found in the source code files (the "Approved Interfaces"). The files of +# Non-GPL Code may instantiate templates or use macros or inline functions from +# the Approved Interfaces without causing the resulting work to be covered by +# the GNU General Public License. Only Red Hat, Inc. may make changes or +# additions to the list of Approved Interfaces. You must obey the GNU General +# Public License in all respects for all of the Program code and other code used +# in conjunction with the Program except the Non-GPL Code covered by this +# exception. If you modify this file, you may extend this exception to your +# version of the file, but you are not obligated to do so. If you do not wish to +# provide this exception without modification, you must delete this exception +# statement from your version and license this file solely under the GPL without +# exception. +# +# +# Copyright (C) 2010 Red Hat, Inc. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# Schema for Managed Entries Plugin +# +dn: cn=schema +# +################################################################################ +# +attributeTypes: ( 2.16.840.1.113730.3.1.2086 NAME 'mepManagedBy' + DESC 'Managed Entries backpointer' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 + X-ORIGIN '389 Directory Server' ) +# +################################################################################ +# +attributeTypes: ( 2.16.840.1.113730.3.1.2087 NAME 'mepManagedEntry' + DESC 'Managed Entries pointer' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 + X-ORIGIN '389 Directory Server' ) +# +################################################################################ +# +attributeTypes: ( 2.16.840.1.113730.3.1.2088 NAME 'mepStaticAttr' + DESC 'Managed Entries static attribute' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 + X-ORIGIN '389 Directory Server' ) +# +################################################################################ +# +attributeTypes: ( 2.16.840.1.113730.3.1.2089 NAME 'mepMappedAttr' + DESC 'Managed Entries mapped attribute' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 + X-ORIGIN '389 Directory Server' ) +# +################################################################################ +# +attributeTypes: ( 2.16.840.1.113730.3.1.2090 NAME 'mepRDNAttr' + DESC 'Managed Entries RDN attribute' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 + X-ORIGIN '389 Directory Server' ) +# +################################################################################ +# +objectClasses: ( 2.16.840.1.113730.3.2.319 NAME 'mepManagedEntry' + DESC 'Managed Entries Managed Entry' + SUP top + AUXILIARY + MAY ( mepManagedBy ) + X-ORIGIN '389 Directory Server' ) +# +################################################################################ +# +objectClasses: ( 2.16.840.1.113730.3.2.320 NAME 'mepOriginEntry' + DESC 'Managed Entries Origin Entry' + SUP top + AUXILIARY + MAY ( mepManagedEntry ) + X-ORIGIN '389 Directory Server' ) +# +################################################################################ +# +objectClasses: ( 2.16.840.1.113730.3.2.321 NAME 'mepTemplateEntry' + DESC 'Managed Entries Template Entry' + SUP top + AUXILIARY + MAY ( cn $ mepStaticAttr $ mepMappedAttr $ mepRDNAttr) + X-ORIGIN '389 Directory Server' ) diff --git a/ldap/servers/plugins/mep/mep.c b/ldap/servers/plugins/mep/mep.c new file mode 100644 index 0000000..932ff53 --- /dev/null +++ b/ldap/servers/plugins/mep/mep.c @@ -0,0 +1,2207 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2010 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* + * Managed Entries Plug-in + */ +#include "mep.h" + + +/* + * Plug-in globals + */ +static PRCList *g_mep_config = NULL; +static PRRWLock *g_mep_config_lock; + +static void *_PluginID = NULL; +static char *_PluginDN = NULL; +static int g_plugin_started = 0; + +static Slapi_PluginDesc pdesc = { MEP_FEATURE_DESC, + VENDOR, + DS_PACKAGE_VERSION, + MEP_PLUGIN_DESC }; + +/* + * Plug-in management functions + */ +int mep_init(Slapi_PBlock * pb); +static int mep_start(Slapi_PBlock * pb); +static int mep_close(Slapi_PBlock * pb); +static int mep_postop_init(Slapi_PBlock * pb); +static int mep_internal_postop_init(Slapi_PBlock *pb); + +/* + * Operation callbacks (where the real work is done) + */ +static int mep_mod_post_op(Slapi_PBlock *pb); +static int mep_add_post_op(Slapi_PBlock *pb); +static int mep_del_post_op(Slapi_PBlock *pb); +static int mep_modrdn_post_op(Slapi_PBlock *pb); +static int mep_pre_op(Slapi_PBlock *pb, int modop); +static int mep_mod_pre_op(Slapi_PBlock *pb); +static int mep_add_pre_op(Slapi_PBlock *pb); +static int mep_del_pre_op(Slapi_PBlock *pb); +static int mep_modrdn_pre_op(Slapi_PBlock *pb); + +/* + * Config cache management functions + */ +static int mep_load_config(); +static void mep_delete_config(); +static int mep_parse_config_entry(Slapi_Entry * e, int apply); +static void mep_insert_config_index(struct configEntry *entry); +static void mep_free_config_entry(struct configEntry ** entry); + +/* + * helpers + */ +static char *mep_get_dn(Slapi_PBlock * pb); +static int mep_dn_is_config(char *dn); +static int mep_dn_is_template(char *dn); +static void mep_find_config(Slapi_Entry *e, struct configEntry **config); +static void mep_find_config_by_template_dn(const char *template_dn, + struct configEntry **config); +static int mep_oktodo(Slapi_PBlock *pb); +static Slapi_Entry *mep_create_managed_entry(struct configEntry *config, + Slapi_Entry *origin); +static void mep_add_managed_entry(struct configEntry *config, + Slapi_Entry *origin); +static Slapi_Mods *mep_get_mapped_mods(struct configEntry *config, + Slapi_Entry *origin); +static int mep_parse_mapped_attr(char *mapping, Slapi_Entry *origin, + char **type, char **value); +static int mep_is_managed_entry(Slapi_Entry *e); +static int mep_is_mapped_attr(Slapi_Entry *template, char *type); + +/* + * Config cache locking functions + */ +void +mep_config_read_lock() +{ + PR_RWLock_Rlock(g_mep_config_lock); +} + +void +mep_config_write_lock() +{ + PR_RWLock_Wlock(g_mep_config_lock); +} + +void +mep_config_unlock() +{ + PR_RWLock_Unlock(g_mep_config_lock); +} + + +/* + * Plugin identity functions + */ +void +mep_set_plugin_id(void *pluginID) +{ + _PluginID = pluginID; +} + +void * +mep_get_plugin_id() +{ + return _PluginID; +} + +void +mep_set_plugin_dn(char *pluginDN) +{ + _PluginDN = pluginDN; +} + +char * +mep_get_plugin_dn() +{ + return _PluginDN; +} + + +/* + * Plug-in initialization functions + */ +int +mep_init(Slapi_PBlock *pb) +{ + int status = 0; + char *plugin_identity = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_init\n"); + + /* Store the plugin identity for later use. + * Used for internal operations. */ + slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity); + PR_ASSERT(plugin_identity); + mep_set_plugin_id(plugin_identity); + + /* Register callbacks */ + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, + (void *) mep_start) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, + (void *) mep_close) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *) &pdesc) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, + (void *) mep_mod_pre_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, + (void *) mep_add_pre_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_DELETE_FN, + (void *) mep_del_pre_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODRDN_FN, + (void *) mep_modrdn_pre_op) != 0 || + slapi_register_plugin("internalpostoperation", /* op type */ + 1, /* Enabled */ + "mep_init", /* this function desc */ + mep_internal_postop_init, /* init func */ + MEP_INT_POSTOP_DESC, /* plugin desc */ + NULL, /* ? */ + plugin_identity /* access control */ + ) || + slapi_register_plugin("postoperation", /* op type */ + 1, /* Enabled */ + "mep_init", /* this function desc */ + mep_postop_init, /* init func for post op */ + MEP_POSTOP_DESC, /* plugin desc */ + NULL, /* ? */ + plugin_identity /* access control */ + ) + ) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_init: failed to register plugin\n"); + status = -1; + } + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_init\n"); + return status; +} + +static int +mep_internal_postop_init(Slapi_PBlock *pb) +{ + int status = 0; + + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *) &pdesc) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, + (void *) mep_add_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, + (void *) mep_del_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN, + (void *) mep_mod_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN, + (void *) mep_modrdn_post_op) != 0) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_internal_postop_init: failed to register plugin\n"); + status = -1; + } + + return status; +} + +static int +mep_postop_init(Slapi_PBlock *pb) +{ + int status = 0; + + if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, + SLAPI_PLUGIN_VERSION_01) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, + (void *) &pdesc) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN, + (void *) mep_add_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN, + (void *) mep_del_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN, + (void *) mep_mod_post_op) != 0 || + slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN, + (void *) mep_modrdn_post_op) != 0) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_postop_init: failed to register plugin\n"); + status = -1; + } + + return status; +} + + +/* + * mep_start() + * + * Creates config lock and loads config cache. + */ +static int +mep_start(Slapi_PBlock * pb) +{ + char *plugindn = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_start\n"); + + /* Check if we're already started */ + if (g_plugin_started) { + goto done; + } + + g_mep_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "mep_config"); + + if (!g_mep_config_lock) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_start: lock creation failed\n"); + + return -1; + } + + /* + * Get the plug-in target dn from the system + * and store it for future use. */ + slapi_pblock_get(pb, SLAPI_TARGET_DN, &plugindn); + if (NULL == plugindn || 0 == strlen(plugindn)) { + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_start: unable to retrieve plugin dn\n"); + return -1; + } + + mep_set_plugin_dn(plugindn); + + /* + * Load the config cache + */ + g_mep_config = (PRCList *)slapi_ch_calloc(1, sizeof(struct configEntry)); + PR_INIT_CLIST(g_mep_config); + + if (mep_load_config() != 0) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_start: unable to load plug-in configuration\n"); + return -1; + } + + g_plugin_started = 1; + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "managed entries plug-in: ready for service\n"); + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_start\n"); + +done: + return 0; +} + +/* + * mep_close() + * + * Cleans up the config cache. + */ +static int +mep_close(Slapi_PBlock * pb) +{ + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_close\n"); + + mep_delete_config(); + + slapi_ch_free((void **)&g_mep_config); + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_close\n"); + + return 0; +} + +PRCList * +mep_get_config() +{ + return g_mep_config; +} + +/* + * config looks like this + * - cn=Managed Entries,cn=plugins,cn=config + * --- cn=user private groups,... + * --- cn=etc,... + */ +static int +mep_load_config() +{ + int status = 0; + int result; + int i; + Slapi_PBlock *search_pb; + Slapi_Entry **entries = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_load_config\n"); + + /* Clear out any old config. */ + mep_config_write_lock(); + mep_delete_config(); + + /* Find the config entries beneath our plugin entry. */ + search_pb = slapi_pblock_new(); + slapi_search_internal_set_pb(search_pb, mep_get_plugin_dn(), + LDAP_SCOPE_SUBTREE, "objectclass=*", + NULL, 0, NULL, NULL, mep_get_plugin_id(), 0); + slapi_search_internal_pb(search_pb); + slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (LDAP_SUCCESS != result) { + status = -1; + goto cleanup; + } + + slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, + &entries); + if (NULL == entries || NULL == entries[0]) { + /* If there are no config entries, we're done. */ + goto cleanup; + } + + /* Loop through all of the entries we found and parse them. */ + for (i = 0; (entries[i] != NULL); i++) { + /* We don't care about the status here because we may have + * some invalid config entries, but we just want to continue + * looking for valid ones. */ + mep_parse_config_entry(entries[i], 1); + } + + cleanup: + slapi_free_search_results_internal(search_pb); + slapi_pblock_destroy(search_pb); + mep_config_unlock(); + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_load_config\n"); + + return status; +} + +/* + * mep_parse_config_entry() + * + * Parses a single config entry. If apply is non-zero, then + * we will load and start using the new config. You can simply + * validate config without making any changes by setting apply + * to 0. + * + * Returns 0 if the entry is valid and -1 if it is invalid. + */ +static int +mep_parse_config_entry(Slapi_Entry * e, int apply) +{ + char *value; + struct configEntry *entry = NULL; + struct configEntry *config_entry; + PRCList *list; + int entry_added = 0; + int ret = 0; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_parse_config_entry\n"); + + /* If this is the main plug-in + * config entry, just bail. */ + if (strcasecmp(mep_get_plugin_dn(), slapi_entry_get_ndn(e)) == 0) { + ret = -1; + goto bail; + } + + entry = (struct configEntry *)slapi_ch_calloc(1, sizeof(struct configEntry)); + if (NULL == entry) { + ret = -1; + goto bail; + } + + value = slapi_entry_get_ndn(e); + if (value) { + entry->dn = slapi_ch_strdup(value); + } else { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: Error " + "reading dn from config entry\n"); + ret = -1; + goto bail; + } + + slapi_log_error(SLAPI_LOG_CONFIG, MEP_PLUGIN_SUBSYSTEM, + "----------> dn [%s]\n", entry->dn); + + /* Load the origin scope */ + value = slapi_entry_attr_get_charptr(e, MEP_SCOPE_TYPE); + if (value) { + entry->origin_scope = value; + } else { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: The %s config " + "setting is required for config entry \"%s\".\n", + MEP_SCOPE_TYPE, entry->dn); + ret = -1; + goto bail; + } + + /* Load the origin filter */ + value = slapi_entry_attr_get_charptr(e, MEP_FILTER_TYPE); + if (value) { + /* Convert to a Slapi_Filter to improve performance. */ + if (NULL == (entry->origin_filter = slapi_str2filter(value))) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM , + "mep_parse_config_entry: Invalid search filter in " + "%s config setting for config entry \"%s\".\n", + MEP_FILTER_TYPE, entry->dn); + ret = -1; + goto bail; + } + } else { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: The %s config " + "setting is required for config entry \"%s\".\n", + MEP_FILTER_TYPE, entry->dn); + ret = -1; + goto bail; + } + + /* Load the managed base */ + value = slapi_entry_attr_get_charptr(e, MEP_MANAGED_BASE_TYPE); + if (value) { + entry->managed_base = value; + } else { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: The %s config " + "setting is required for config entry \"%s\".\n", + MEP_MANAGED_BASE_TYPE, entry->dn); + ret = -1; + goto bail; + } + + /* Load the managed template */ + value = slapi_entry_attr_get_charptr(e, MEP_MANAGED_TEMPLATE_TYPE); + if (value) { + Slapi_Entry *test_entry = NULL; + Slapi_DN *template_sdn = NULL; + + entry->template_dn = value; + + /* Fetch the managed entry template */ + template_sdn = slapi_sdn_new_dn_byref(entry->template_dn); + slapi_search_internal_get_entry(template_sdn, 0, + &entry->template_entry, mep_get_plugin_id()); + slapi_sdn_free(&template_sdn); + + if (entry->template_entry == NULL) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: The managed entry " + "template \"%s\" does not exist. Please " + "add it or correct the %s config setting for " + "config entry \"%s\"\n", entry->template_dn, + MEP_MANAGED_TEMPLATE_TYPE, entry->dn); + ret = -1; + goto bail; + } + + /* Validate the template entry by creating a test managed + * entry and running a schema check on it */ + test_entry = mep_create_managed_entry(entry, NULL); + if (test_entry == NULL) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: Unable to create " + "a test managed entry from managed entry " + "template \"%s\". Please check the template " + "entry for errors.\n", entry->template_dn); + ret = -1; + goto bail; + } + + if (slapi_entry_schema_check(NULL, test_entry) != 0) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: Test managed " + "entry created from managed entry template " + "\"%s\" violates the schema. Please check " + "the template entry for schema errors.\n", + entry->template_dn); + slapi_entry_free(test_entry); + ret = -1; + goto bail; + } + + /* Dispose of the test entry */ + slapi_entry_free(test_entry); + + } else { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: The %s config " + "setting is required for config entry \"%s\".\n", + MEP_MANAGED_TEMPLATE_TYPE, entry->dn); + ret = -1; + goto bail; + } + + /* If we were only called to validate config, we can + * just bail out before applying the config changes */ + if (apply == 0) { + goto bail; + } + + /* Add the config object to the list. We order by scope. */ + if (!PR_CLIST_IS_EMPTY(g_mep_config)) { + list = PR_LIST_HEAD(g_mep_config); + while (list != g_mep_config) { + config_entry = (struct configEntry *) list; + + /* If the config entry we are adding has a scope that is + * a child of the scope of the current list item, we insert + * the entry before that list item. */ + if (slapi_dn_issuffix(entry->origin_scope, config_entry->origin_scope)) { + PR_INSERT_BEFORE(&(entry->list), list); + slapi_log_error(SLAPI_LOG_CONFIG, MEP_PLUGIN_SUBSYSTEM, + "store [%s] before [%s] \n", entry->dn, + config_entry->dn); + + entry_added = 1; + break; + } + + list = PR_NEXT_LINK(list); + + /* If we hit the end of the list, add to the tail. */ + if (g_mep_config == list) { + PR_INSERT_BEFORE(&(entry->list), list); + slapi_log_error(SLAPI_LOG_CONFIG, MEP_PLUGIN_SUBSYSTEM, + "store [%s] at tail\n", entry->dn); + + entry_added = 1; + break; + } + } + } else { + /* first entry */ + PR_INSERT_LINK(&(entry->list), g_mep_config); + slapi_log_error(SLAPI_LOG_CONFIG, MEP_PLUGIN_SUBSYSTEM, + "store [%s] at head \n", entry->dn); + + entry_added = 1; + } + + bail: + if (0 == entry_added) { + /* Don't log error if we weren't asked to apply config */ + if ((apply != 0) && (entry != NULL)) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_config_entry: Invalid config entry " + "[%s] skipped\n", entry->dn); + } + mep_free_config_entry(&entry); + } else { + ret = 0; + } + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_parse_config_entry\n"); + + return ret; +} + +static void +mep_free_config_entry(struct configEntry ** entry) +{ + struct configEntry *e = *entry; + + if (e == NULL) + return; + + if (e->dn) { + slapi_log_error(SLAPI_LOG_CONFIG, MEP_PLUGIN_SUBSYSTEM, + "freeing config entry [%s]\n", e->dn); + slapi_ch_free_string(&e->dn); + } + + if (e->origin_scope) { + slapi_ch_free_string(&e->origin_scope); + } + + if (e->origin_filter) { + slapi_filter_free(e->origin_filter, 1); + } + + if (e->managed_base) { + slapi_ch_free_string(&e->managed_base); + } + + if (e->template_dn) { + slapi_ch_free_string(&e->template_dn); + } + + if (e->template_entry) { + slapi_entry_free(e->template_entry); + } + + slapi_ch_free((void **) entry); +} + +static void +mep_delete_configEntry(PRCList *entry) +{ + PR_REMOVE_LINK(entry); + mep_free_config_entry((struct configEntry **) &entry); +} + +static void +mep_delete_config() +{ + PRCList *list; + + /* Delete the config cache. */ + while (!PR_CLIST_IS_EMPTY(g_mep_config)) { + list = PR_LIST_HEAD(g_mep_config); + mep_delete_configEntry(list); + } + + return; +} + + +/* + * Helper functions + */ +static char * +mep_get_dn(Slapi_PBlock * pb) +{ + char *dn = 0; + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_get_dn\n"); + + if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn)) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_get_dn: failed to get dn of changed entry"); + goto bail; + } + + bail: + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_get_dn\n"); + + return dn; +} + +/* + * mep_dn_is_config() + * + * Checks if dn is a managed entries config entry. + */ +static int +mep_dn_is_config(char *dn) +{ + int ret = 0; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_dn_is_config\n"); + + /* Return 1 if the passed in dn is a child of the main + * plugin config entry. */ + if (slapi_dn_issuffix(dn, mep_get_plugin_dn()) && + strcasecmp(dn, mep_get_plugin_dn())) { + ret = 1; + } + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_dn_is_config\n"); + + return ret; +} + +/* + * mep_dn_is_template() + * + * Checks if dn is a managed entries template. + */ +static int +mep_dn_is_template(char *dn) +{ + int ret = 0; + PRCList *list = NULL; + Slapi_DN *config_sdn = NULL; + Slapi_DN *sdn = slapi_sdn_new_dn_byref(dn); + + if (!PR_CLIST_IS_EMPTY(g_mep_config)) { + list = PR_LIST_HEAD(g_mep_config); + while (list != g_mep_config) { + config_sdn = slapi_sdn_new_dn_byref(((struct configEntry *)list)->template_dn); + + if (slapi_sdn_compare(config_sdn, sdn) == 0) { + ret = 1; + slapi_sdn_free(&config_sdn); + break; + } else { + list = PR_NEXT_LINK(list); + slapi_sdn_free(&config_sdn); + } + } + } + + slapi_sdn_free(&sdn); + + return ret; +} + +/* + * mep_find_config() + * + * Finds the appropriate config entry for a given entry + * by checking if the entry meets the origin scope and + * filter requirements of any config entry. A read lock + * must be held on the config before calling this function. + * The configEntry that is returned is a pointer to the + * actual config entry in the config cache. It should not + * be modified in any way. The read lock should not be + * released until you are finished with the config entry + * that is returned. + * + * Returns NULL if no applicable config entry is found. + */ +static void +mep_find_config(Slapi_Entry *e, struct configEntry **config) +{ + PRCList *list = NULL; + char *dn = NULL; + + *config = NULL; + + if (e && !PR_CLIST_IS_EMPTY(g_mep_config)) { + dn = slapi_entry_get_dn(e); + + list = PR_LIST_HEAD(g_mep_config); + while (list != g_mep_config) { + + /* See if the dn is within the scope of this config entry + * in addition to matching the origin filter. */ + if (slapi_dn_issuffix(dn, ((struct configEntry *)list)->origin_scope) && + (slapi_filter_test_simple(e, ((struct configEntry *)list)->origin_filter) == 0)) { + *config = (struct configEntry *)list; + break; + } + + list = PR_NEXT_LINK(list); + } + } +} + +/* + * mep_find_config_by_template_dn() + * + * Finds the config entry associated with a particular + * template dn. A read lock must be held on the config + * before calling this function. The configEntry that + * us returned is a pointer to the actual config entry + * in the config cache. It should not be modified in + * any way. The read lock should not be released until + * you are finished with the config entry that is returned. + * + * Returns NULL if no applicable config entry is found. + */ +static void +mep_find_config_by_template_dn(const char *template_dn, + struct configEntry **config) +{ + PRCList *list = NULL; + Slapi_DN *config_sdn = NULL; + Slapi_DN *template_sdn = slapi_sdn_new_dn_byref(template_dn); + + *config = NULL; + + if (!PR_CLIST_IS_EMPTY(g_mep_config)) { + list = PR_LIST_HEAD(g_mep_config); + while (list != g_mep_config) { + config_sdn = slapi_sdn_new_dn_byref(((struct configEntry *)list)->template_dn); + + if (slapi_sdn_compare(config_sdn, template_sdn) == 0) { + *config = (struct configEntry *)list; + slapi_sdn_free(&config_sdn); + break; + } else { + list = PR_NEXT_LINK(list); + slapi_sdn_free(&config_sdn); + } + } + } + + slapi_sdn_free(&template_sdn); +} + +/* + * mep_oktodo() + * + * Check if we want to process this operation. We need to be + * sure that the operation succeeded. We also respond to replicated + * ops so we don't test for that. This does require that the managed + * attributes not be replicated. + */ +static int +mep_oktodo(Slapi_PBlock *pb) +{ + int ret = 1; + int oprc = 0; + + slapi_log_error( SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_oktodo\n" ); + + if(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0) + { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_oktodo: could not get parameters\n" ); + ret = -1; + } + + /* This plugin should only execute if the operation succeeded. */ + if(oprc != 0) + { + ret = 0; + } + + slapi_log_error( SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_oktodo\n" ); + + return ret; +} + +/* + * mep_create_managed_entry() + * + * Creates a managed entry from a specified template and origin + * entry. If an origin entry is not passed in, the values of + * mapped attributes will not be filled in and the addition of + * a backlink to the origin is not added. This is useful for + * creating a test managed entry for template validation. + */ +static Slapi_Entry * +mep_create_managed_entry(struct configEntry *config, Slapi_Entry *origin) +{ + Slapi_Entry *managed_entry = NULL; + Slapi_Entry *template = NULL; + char *rdn_type = NULL; + char **vals = NULL; + char *p = NULL; + char *type = NULL; + char *value = NULL; + int found_rdn_map = 0; + int i = 0; + int err = 0; + + /* If no template was supplied, there's nothing we can do. */ + if ((config == NULL) || (config->template_entry == NULL)) { + return NULL; + } else { + template = config->template_entry; + } + + /* Ensure that a RDN type was specified in the template. */ + if ((rdn_type = slapi_entry_attr_get_charptr(template, MEP_RDN_ATTR_TYPE)) == NULL) { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_create_managed_entry: The %s config attribute " + "was not found in template \"%s\". This attribute " + "is required.\n", MEP_RDN_ATTR_TYPE, config->template_dn); + err = 1; + goto done; + } + + /* Create the entry to be returned. */ + managed_entry = slapi_entry_alloc(); + slapi_entry_init(managed_entry, NULL, NULL); + + /* Add all of the static attributes from the template to the newly + * created managed entry. */ + vals = slapi_entry_attr_get_charray(template, MEP_STATIC_ATTR_TYPE); + for (i = 0; vals && vals[i]; ++i) { + /* split out the type from the value (use the first ':') */ + if ((p = strchr(vals[i], ':')) == NULL) { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_create_managed_entry: Value for %s config setting " + "is not in the correct format in template \"%s\". " + "(value: \"%s\")\n", MEP_STATIC_ATTR_TYPE, + config->template_dn, vals[i]); + err = 1; + goto done; + } else { + /* Terminate the type so we can use it as a string. */ + *p = '\0'; + + /* Advance p to point to the beginning of the value. */ + p++; + while (*p == ' ') { + p++; + } + + /* At this point, vals[i] points to the type and p + * points to the value. */ + slapi_entry_add_string(managed_entry, vals[i], p); + } + } + + /* Clear out vals so we can use them again */ + slapi_ch_array_free(vals); + + /* Add the mapped attributes to the newly created managed entry. */ + vals = slapi_entry_attr_get_charray(template, MEP_MAPPED_ATTR_TYPE); + for (i = 0; vals && vals[i]; ++i) { + if (mep_parse_mapped_attr(vals[i], origin, &type, &value) == 0) { + /* Add the attribute to the managed entry. */ + slapi_entry_add_string(managed_entry, type, value); + + /* Check if this type is the RDN type. */ + if (slapi_attr_type_cmp(type, rdn_type, SLAPI_TYPE_CMP_EXACT) == 0) { + found_rdn_map = 1; + } + + slapi_ch_free_string(&type); + slapi_ch_free_string(&value); + } else { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_create_managed_entry: Error parsing mapped attribute " + "in template \"%s\".\n", config->template_dn); + err = 1; + goto done; + } + } + + /* The RDN attribute must be a mapped attribute. If we didn't find it, + * we need to bail. */ + if (!found_rdn_map) { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_create_managed_entry: The RDN type \"%s\" " + "was not found as a mapped attribute in template " + "\"%s\". It must be a mapped attribute.\n", + rdn_type, config->template_dn); + err = 1; + goto done; + } else { + /* Build the DN and set it in the entry. */ + char *dn = NULL; + char *rdn_val = NULL; + + /* If an origin entry was supplied, the RDN value will be + * the mapped value. If no origin entry was supplied, the + * value will be the mapping rule from the template. */ + rdn_val = slapi_entry_attr_get_charptr(managed_entry, rdn_type); + + /* Create the DN using the mapped RDN value + * and the base specified in the config. */ + dn = slapi_ch_smprintf("%s=%s,%s", rdn_type, rdn_val, config->managed_base); + + slapi_ch_free_string(&rdn_val); + + if (dn != NULL) { + slapi_sdn_set_dn_passin(slapi_entry_get_sdn(managed_entry), dn); + } else { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_create_managed_entry: Error setting DN " + "in managed entry based off of template entry " + "\"%s\" (origin entry \"%s\").\n", + config->template_dn, + origin ? slapi_entry_get_dn(origin) : "NULL"); + err = 1; + goto done; + } + } + + /* If an origin entry was supplied, set a backpointer to the + * origin in the managed entry. */ + if (origin) { + slapi_entry_add_string(managed_entry, + SLAPI_ATTR_OBJECTCLASS, MEP_MANAGED_OC); + slapi_entry_add_string(managed_entry, MEP_MANAGED_BY_TYPE, + slapi_entry_get_dn(origin)); + } + + done: + slapi_ch_array_free(vals); + slapi_ch_free_string(&rdn_type); + + if (err != 0) { + slapi_entry_free(managed_entry); + managed_entry = NULL; + } + + return managed_entry; +} + +/* + * mep_add_managed_entry() + * + * Creates and adds a managed entry to the database. The + * origin entry will also be modified to add a link to the + * newly created managed entry. + */ +static void +mep_add_managed_entry(struct configEntry *config, + Slapi_Entry *origin) +{ + Slapi_Entry *managed_entry = NULL; + char *managed_dn = NULL; + Slapi_PBlock *mod_pb = slapi_pblock_new(); + int result = LDAP_SUCCESS; + + /* Create the managed entry */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_add_managed_entry: Creating a managed " + "entry from origin entry \"%s\" using " + "config \"%s\".\n", slapi_entry_get_dn(origin), + config->dn); + managed_entry = mep_create_managed_entry(config, origin); + if (managed_entry == NULL) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_add_managed_entry: Unable to create a managed " + "entry from origin entry \"%s\" using config " + "\"%s\".\n", slapi_entry_get_dn(origin), config->dn); + } else { + /* Copy the managed entry DN to use when + * creating the pointer attribute. */ + managed_dn = slapi_ch_strdup(slapi_entry_get_dn(managed_entry)); + + /* Add managed entry to db. The entry will be consumed. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "Adding managed entry \"%s\" for origin " + "entry \"%s\"\n.", managed_dn, slapi_entry_get_dn(origin)); + slapi_add_entry_internal_set_pb(mod_pb, managed_entry, NULL, + mep_get_plugin_id(), 0); + slapi_add_internal_pb(mod_pb); + slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (result != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_add_managed_entry: Unable to add managed " + "entry \"%s\" for origin entry \"%s\" (%s).\n", + managed_dn, slapi_entry_get_dn(origin), + ldap_err2string(result)); + } else { + /* Add forward link to origin entry. */ + LDAPMod oc_mod; + LDAPMod pointer_mod; + LDAPMod *mods[3]; + char *oc_vals[2]; + char *pointer_vals[2]; + + /* Clear out the pblock for reuse. */ + slapi_pblock_init(mod_pb); + + /* Add the origin entry objectclass. */ + oc_vals[0] = MEP_ORIGIN_OC; + oc_vals[1] = 0; + oc_mod.mod_op = LDAP_MOD_ADD; + oc_mod.mod_type = SLAPI_ATTR_OBJECTCLASS; + oc_mod.mod_values = oc_vals; + + /* Add a pointer to the managed entry. */ + pointer_vals[0] = managed_dn; + pointer_vals[1] = 0; + pointer_mod.mod_op = LDAP_MOD_ADD; + pointer_mod.mod_type = MEP_MANAGED_ENTRY_TYPE; + pointer_mod.mod_values = pointer_vals; + + mods[0] = &oc_mod; + mods[1] = &pointer_mod; + mods[2] = 0; + + /* Perform the modify operation. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "Adding %s pointer to \"%s\" in entry \"%s\"\n.", + MEP_MANAGED_ENTRY_TYPE, managed_dn, slapi_entry_get_dn(origin)); + slapi_modify_internal_set_pb(mod_pb, slapi_entry_get_dn(origin), + mods, 0, 0, mep_get_plugin_id(), 0); + slapi_modify_internal_pb(mod_pb); + slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (result != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_add_managed_entry: Unable to add pointer to " + "managed entry \"%s\" in origin entry \"%s\" " + "(%s).\n", managed_dn, slapi_entry_get_dn(origin), + ldap_err2string(result)); + } + } + } + + slapi_ch_free_string(&managed_dn); + slapi_pblock_destroy(mod_pb); +} + +/* + * mep_get_mapped_mods() + * + * Creates the modifications needed to update the mapped values + * in a managed entry. It is up to the caller to free the + * returned mods when it is finished using them. + */ +static Slapi_Mods *mep_get_mapped_mods(struct configEntry *config, + Slapi_Entry *origin) +{ + Slapi_Mods *smods = NULL; + Slapi_Entry *template = NULL; + Slapi_Attr *attr = NULL; + char **vals = NULL; + char *type = NULL; + char *value = NULL; + int i = 0; + + /* If no template was supplied, there's nothing we can do. */ + if (origin == NULL || config == NULL || config->template_entry == NULL) { + return NULL; + } else { + template = config->template_entry; + } + + /* See how many mods we will have and initialize the smods. */ + if (slapi_entry_attr_find(config->template_entry, MEP_MAPPED_ATTR_TYPE, &attr) == 0) { + int numvals = 0; + + slapi_attr_get_numvalues(attr, &numvals); + smods = slapi_mods_new(); + slapi_mods_init(smods, numvals); + } + + /* Go through the template and find the mapped attrs. */ + vals = slapi_entry_attr_get_charray(template, MEP_MAPPED_ATTR_TYPE); + for (i = 0; vals && vals[i]; ++i) { + if (mep_parse_mapped_attr(vals[i], origin, &type, &value) == 0) { + /* Add a modify that replaces all values with the new value. */ + slapi_mods_add_string(smods, LDAP_MOD_REPLACE, type, value); + slapi_ch_free_string(&type); + slapi_ch_free_string(&value); + } else { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_get_mapped_mods: Error parsing mapped attribute " + "in template \"%s\".\n", config->template_dn); + slapi_mods_free(&smods); + goto bail; + } + } + + bail: + slapi_ch_array_free(vals); + + return smods; +} + +/* + * mep_parse_mapped_attr() + * + * Parses a mapped attribute setting from a template and + * fills in the type and value based off of the origin + * entry. If an origin entry is not supplied, the value + * is simply the mapping rule. + */ +static int +mep_parse_mapped_attr(char *mapping, Slapi_Entry *origin, + char **type, char **value) +{ + int ret = 0; + char *p = NULL; + char *pre_str = NULL; + char *post_str = NULL; + char *end = NULL; + char *var_start = NULL; + char *map_type = NULL; + + /* Clear out any existing type or value. */ + slapi_ch_free_string(type); + slapi_ch_free_string(value); + + /* split out the type from the value (use the first ':') */ + if ((p = strchr(mapping, ':')) == NULL) { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_mapped_attr: Value for mapped attribute " + "is not in the correct format. (value: \"%s\").\n", + mapping); + ret = 1; + goto bail; + } + + /* Ensure the type is not empty. */ + if (p == mapping) { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_mapped_attr: Value for mapped attribute " + "is not in the correct format. The type is missing. " + "(value: \"%s\").\n", + mapping); + ret = 1; + goto bail; + } + + /* Terminate the type so we can use it as a string. */ + *p = '\0'; + + /* Duplicate the type to be returned. */ + *type = slapi_ch_strdup(mapping); + + /* Advance p to point to the beginning of the value. */ + p++; + while (*p == ' ') { + p++; + } + + pre_str = p; + end = p + strlen(p); + + /* Find the variable that we need to substitute. */ + for (; p <= end; p++) { + if (*p == '$') { + if (p == end) { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_mapped_attr: Invalid mapped " + "attribute value for type \"%s\".\n", mapping); + ret = 1; + goto bail; + } + + if (*(p + 1) == '$') { + /* This is an escaped $, so just skip it. */ + p++; + } else { + /* We found a variable. Terminate the pre + * string and process the variable. */ + *p = '\0'; + p++; + + /* We should be pointing at the variable name now. */ + var_start = p; + + /* Move the pointer to the end of the variable name. */ + while ((p < end) && !isspace(*p)) { + p++; + } + + /* Check for a missing variable name. */ + if (p == var_start) { + break; + } + + /* Set the map type. */ + map_type = strndup(var_start, p - var_start); + + /* If we're at the end of the string, we + * don't have a post string. Just set + * it to an empty string. */ + if (p == end) { + post_str = ""; + } else { + post_str = p; + } + + /* We only support a single variable, so we're done. */ + break; + } + } + } + + if (map_type) { + if (origin) { + char *map_val = slapi_entry_attr_get_charptr(origin, map_type); + + if (map_val) { + /* Create the new mapped value. */ + *value = slapi_ch_smprintf("%s%s%s", pre_str, + map_val, post_str); + slapi_ch_free_string(&map_val); + } else { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_mapped_attr: Mapped attribute \"%s\" " + "is not present in origin entry \"%s\". Please " + "correct template to only map attributes " + "required by the schema.\n", map_type, + slapi_entry_get_dn(origin)); + ret = 1; + goto bail; + } + } else { + /* Just use the mapping since we have no origin entry. */ + *value = slapi_ch_smprintf("%s$%s%s", pre_str, map_type, + post_str); + } + } else { + slapi_log_error( SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_parse_mapped_attr: No variable found in " + "mapped attribute value for type \"%s\".\n", + mapping); + ret = 1; + goto bail; + } + + bail: + slapi_ch_free_string(&map_type); + + if (ret != 0) { + slapi_ch_free_string(type); + slapi_ch_free_string(value); + } + + return ret; +} + + +/* + * mep_is_managed_entry() + * + * Returns 1 if the entry is a managed entry, 0 otherwise. + * The check is performed by seeing if the managed entry + * objectclass is present. + */ +static int +mep_is_managed_entry(Slapi_Entry *e) +{ + int ret = 0; + Slapi_Attr *attr = NULL; + struct berval bv; + + bv.bv_val = MEP_MANAGED_OC; + bv.bv_len = strlen(bv.bv_val); + + if (e && (slapi_entry_attr_find(e, SLAPI_ATTR_OBJECTCLASS, &attr) == 0)) { + if (slapi_attr_value_find(attr, &bv) == 0) { + ret = 1; + } + } + + return ret; +} + +/* + * mep_is_mapped_attr() + * + * Checks if type is defined as a mapped attribute in template. + */ +static int +mep_is_mapped_attr(Slapi_Entry *template, char *type) +{ + int ret = 0; + int i = 0; + char **vals = NULL; + char *map_type = NULL; + char *value = NULL; + + if (template && type) { + vals = slapi_entry_attr_get_charray(template, MEP_MAPPED_ATTR_TYPE); + for (i = 0; vals && vals[i]; ++i) { + if (mep_parse_mapped_attr(vals[i], NULL, &map_type, &value) == 0) { + if (slapi_attr_type_cmp(map_type, type, SLAPI_TYPE_CMP_EXACT) == 0) { + ret = 1; + } + + slapi_ch_free_string(&map_type); + slapi_ch_free_string(&value); + + /* If we found a match, we're done. */ + if (ret == 1) { + break; + } + } + } + + slapi_ch_array_free(vals); + } + + return ret; +} + +/* + * Operation callback functions + */ + +/* + * mep_pre_op() + * + * Checks if an operation is modifying the managed + * entries config and validates the config changes. + */ +static int +mep_pre_op(Slapi_PBlock * pb, int modop) +{ + char *dn = 0; + Slapi_Entry *e = 0; + Slapi_Mods *smods = 0; + LDAPMod **mods; + int free_entry = 0; + char *errstr = NULL; + struct configEntry *config = NULL; + void *caller_id = NULL; + int ret = 0; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_pre_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started) + goto bail; + + /* See if we're calling ourselves. */ + slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &caller_id); + + if (0 == (dn = mep_get_dn(pb))) + goto bail; + + if (mep_dn_is_config(dn)) { + /* Validate config changes, but don't apply them. + * This allows us to reject invalid config changes + * here at the pre-op stage. Applying the config + * needs to be done at the post-op stage. */ + + if (LDAP_CHANGETYPE_ADD == modop) { + slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e); + } else { + /* Fetch the entry being modified so we can + * create the resulting entry for validation. */ + Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(dn); + if (tmp_dn) { + slapi_search_internal_get_entry(tmp_dn, 0, &e, mep_get_plugin_id()); + slapi_sdn_free(&tmp_dn); + free_entry = 1; + } + + /* If the entry doesn't exist, just bail and + * let the server handle it. */ + if (e == NULL) { + goto bail; + } + + /* Grab the mods. */ + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + smods = slapi_mods_new(); + slapi_mods_init_byref(smods, mods); + + /* Apply the mods to create the resulting entry. */ + if (mods && (slapi_entry_apply_mods(e, mods) != LDAP_SUCCESS)) { + /* The mods don't apply cleanly, so we just let this op go + * to let the main server handle it. */ + goto bailmod; + } + } + + if (mep_parse_config_entry(e, 0) != 0) { + /* Refuse the operation if config parsing failed. */ + ret = LDAP_UNWILLING_TO_PERFORM; + if (LDAP_CHANGETYPE_ADD == modop) { + errstr = slapi_ch_smprintf("Not a valid managed entries configuration entry."); + } else { + errstr = slapi_ch_smprintf("Changes result in an invalid " + "managed entries configuration."); + } + } + } else { + /* Check if an active template entry is being updated. If so, validate it. */ + mep_config_read_lock(); + mep_find_config_by_template_dn(dn, &config); + if (config) { + Slapi_Entry *test_entry = NULL; + struct configEntry *config_copy = NULL; + Slapi_DN *tmp_dn = NULL; + + config_copy = (struct configEntry *)slapi_ch_calloc(1, sizeof(struct configEntry)); + + /* Make a temporary copy of the config to use for validation. */ + config_copy->dn = slapi_ch_strdup(config->dn); + config_copy->managed_base = slapi_ch_strdup(config->managed_base); + config_copy->template_dn = slapi_ch_strdup(config->template_dn); + + /* Reject attempts to delete or rename an active template. + * Validate changes to an active template. */ + switch (modop) { + case LDAP_CHANGETYPE_DELETE: + errstr = slapi_ch_smprintf("Deleting an active managed " + "entries template is not allowed. " + "Delete the associated config " + "entry first."); + ret = LDAP_UNWILLING_TO_PERFORM; + break; + case LDAP_CHANGETYPE_MODDN: + errstr = slapi_ch_smprintf("Renaming an active managed " + "entries template is not allowed. " + "Create a new template and modify " + "the associated config entry instead."); + ret = LDAP_UNWILLING_TO_PERFORM; + break; + case LDAP_CHANGETYPE_MODIFY: + tmp_dn = slapi_sdn_new_dn_byref(dn); + + /* Fetch the existing template entry. */ + if (tmp_dn) { + slapi_search_internal_get_entry(tmp_dn, 0, &e, mep_get_plugin_id()); + slapi_sdn_free(&tmp_dn); + free_entry = 1; + } + + /* If the entry doesn't exist, we just skip + * validation and let the server handle it. */ + if (e) { + /* Grab the mods. */ + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + smods = slapi_mods_new(); + slapi_mods_init_byref(smods, mods); + + /* Apply the mods to create the resulting entry. */ + if (mods && (slapi_entry_apply_mods(e, mods) == LDAP_SUCCESS)) { + /* Set the resulting template in the config copy. + * The ownership of the resulting entry is handed + * over to the config copy. */ + config_copy->template_entry = e; + e = NULL; + + /* Validate the changed template. */ + test_entry = mep_create_managed_entry(config_copy, NULL); + if (test_entry == NULL) { + errstr = slapi_ch_smprintf("Changes result in an invalid " + "managed entries template."); + ret = LDAP_UNWILLING_TO_PERFORM; + } + + if (slapi_entry_schema_check(NULL, test_entry) != 0) { + errstr = slapi_ch_smprintf("Changes result in an invalid " + "managed entries template due " + "to a schema violation."); + ret = LDAP_UNWILLING_TO_PERFORM; + } + } + } + + /* Dispose of the test entry */ + slapi_entry_free(test_entry); + + /* Free the config copy */ + mep_free_config_entry(&config_copy); + break; + } + } + mep_config_unlock(); + + /* Check if a managed entry is being deleted or + * renamed and reject if it's not being done by + * this plugin. */ + if (((modop == LDAP_CHANGETYPE_DELETE) || (modop == LDAP_CHANGETYPE_MODDN) || + (modop == LDAP_CHANGETYPE_MODIFY)) && (caller_id != mep_get_plugin_id())) { + Slapi_DN *tmp_dn = slapi_sdn_new_dn_byref(dn); + Slapi_Entry *origin_e = NULL; + Slapi_Mod *smod = NULL; + Slapi_Mod *next_mod = NULL; + char *origin_dn = NULL; + Slapi_DN *origin_sdn = NULL; + + /* Fetch the target entry. */ + if (tmp_dn) { + /* Free any existing entry so we don't leak it. */ + if (e && free_entry) { + slapi_entry_free(e); + } + + slapi_search_internal_get_entry(tmp_dn, 0, &e, mep_get_plugin_id()); + slapi_sdn_free(&tmp_dn); + free_entry = 1; + } + + if (e && mep_is_managed_entry(e)) { + if (modop == LDAP_CHANGETYPE_MODIFY) { + /* Fetch the origin entry so we can locate the config template. */ + origin_dn = slapi_entry_attr_get_charptr(e, MEP_MANAGED_BY_TYPE); + if (origin_dn) { + origin_sdn = slapi_sdn_new_dn_byref(origin_dn); + slapi_search_internal_get_entry(origin_sdn, 0, + &origin_e, mep_get_plugin_id()); + slapi_sdn_free(&origin_sdn); + } + + if (origin_e) { + /* Fetch the config. */ + mep_config_read_lock(); + mep_find_config(origin_e, &config); + + if (config) { + /* Get the mods if we haven't already. */ + if (smods == NULL) { + slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); + smods = slapi_mods_new(); + slapi_mods_init_byref(smods, mods); + } + + next_mod = slapi_mod_new(); + smod = slapi_mods_get_first_smod(smods, next_mod); + while(smod) { + char *type = (char *)slapi_mod_get_type(smod); + + /* If this is a mapped attribute, reject the op. */ + if (mep_is_mapped_attr(config->template_entry, type)) { + errstr = slapi_ch_smprintf("Modifying a mapped attribute " + " in a managed entry is not allowed. " + "The \"%s\" attribute is mapped for " + "this entry.", type); + ret = LDAP_UNWILLING_TO_PERFORM; + break; + } + + slapi_mod_done(next_mod); + smod = slapi_mods_get_next_smod(smods, next_mod); + } + + slapi_mod_free(&next_mod); + } else { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_pre_op: Unable to fetch config for " + "origin entry \"%s\".\n", origin_dn); + } + + slapi_entry_free(origin_e); + mep_config_unlock(); + } else { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_pre_op: Unable to fetch origin entry " + "\"%s\".\n", origin_dn); + } + + slapi_ch_free_string(&origin_dn); + } else { + errstr = slapi_ch_smprintf("%s a managed entry is not allowed. " + "It needs to be manually unlinked first.", + modop == LDAP_CHANGETYPE_DELETE ? "Deleting" + : "Renaming"); + ret = LDAP_UNWILLING_TO_PERFORM; + } + } + } + } + + bailmod: + /* Clean up smods. */ + if (LDAP_CHANGETYPE_MODIFY == modop) { + slapi_mods_free(&smods); + } + + bail: + if (free_entry && e) + slapi_entry_free(e); + + if (ret) { + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_pre_op: operation failure [%d]\n", ret); + slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL); + slapi_ch_free((void **)&errstr); + ret = -1; + } + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_pre_op\n"); + + return ret; +} + +static int +mep_add_pre_op(Slapi_PBlock * pb) +{ + return mep_pre_op(pb, LDAP_CHANGETYPE_ADD); +} + +static int +mep_del_pre_op(Slapi_PBlock * pb) +{ + return mep_pre_op(pb, LDAP_CHANGETYPE_DELETE); +} + +static int +mep_mod_pre_op(Slapi_PBlock * pb) +{ + return mep_pre_op(pb, LDAP_CHANGETYPE_MODIFY); +} + +static int +mep_modrdn_pre_op(Slapi_PBlock * pb) +{ + return mep_pre_op(pb, LDAP_CHANGETYPE_MODDN); +} + +static int +mep_mod_post_op(Slapi_PBlock *pb) +{ + Slapi_Mods *smods = NULL; + Slapi_PBlock *mep_pb = NULL; + Slapi_Entry *e = NULL; + char *dn = NULL; + char *managed_dn = NULL; + struct configEntry *config = NULL; + int result = 0; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_mod_post_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started) + return 0; + + if (mep_oktodo(pb) && (dn = mep_get_dn(pb))) { + /* First check if the config or a template is being modified. */ + if (mep_dn_is_config(dn) || mep_dn_is_template(dn)) { + mep_load_config(); + } + + /* Fetch the modified entry. */ + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e); + if (e == NULL) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_mod_post_op: Unable to fetch postop entry.\n"); + goto bail; + } + + /* Check if we're an origin entry. Update the + * mapped attrs of it's managed entry if so. */ + managed_dn = slapi_entry_attr_get_charptr(e, MEP_MANAGED_ENTRY_TYPE); + if (managed_dn) { + mep_config_read_lock(); + mep_find_config(e, &config); + if (config) { + smods = mep_get_mapped_mods(config, e); + if (smods) { + /* Clear out the pblock for reuse. */ + mep_pb = slapi_pblock_new(); + + /* Perform the modify operation. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_mod_post_op: Updating mapped attributes " + "in entry \"%s\"\n.", managed_dn); + slapi_modify_internal_set_pb(mep_pb, managed_dn, + slapi_mods_get_ldapmods_byref(smods), 0, 0, + mep_get_plugin_id(), 0); + slapi_modify_internal_pb(mep_pb); + slapi_pblock_get(mep_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (result != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_mod_post_op: Unable to update mapped " + "attributes from origin entry \"%s\" in managed " + "entry \"%s\" (%s).\n", dn, managed_dn, + ldap_err2string(result)); + } + + slapi_mods_free(&smods); + slapi_pblock_destroy(mep_pb); + } + } else { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_mod_post_op: Unable to find config for origin " + "entry \"%s\".\n", dn); + } + + slapi_ch_free_string(&managed_dn); + + mep_config_unlock(); + } + + } + + bail: + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_mod_post_op\n"); + + return 0; +} + +static int +mep_add_post_op(Slapi_PBlock *pb) +{ + Slapi_Entry *e = NULL; + char *dn = NULL; + struct configEntry *config = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_add_post_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started || !mep_oktodo(pb)) + return 0; + + /* Reload config if a config entry was added. */ + if ((dn = mep_get_dn(pb))) { + if (mep_dn_is_config(dn)) { + mep_load_config(); + } + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_add_post_op: Error " + "retrieving dn\n"); + } + + /* Get the newly added entry. */ + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e); + + if (e) { + /* Check if a config entry applies + * to the entry being added. */ + mep_config_read_lock(); + mep_find_config(e, &config); + if (config) { + mep_add_managed_entry(config, e); + } + + mep_config_unlock(); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_add_post_op: Error " + "retrieving post-op entry %s\n", dn); + } + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_add_post_op\n"); + + return 0; +} + +static int +mep_del_post_op(Slapi_PBlock *pb) +{ + char *dn = NULL; + Slapi_Entry *e = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_del_post_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started || !mep_oktodo(pb)) { + return 0; + } + + /* Reload config if a config entry was deleted. */ + if ((dn = mep_get_dn(pb))) { + if (mep_dn_is_config(dn)) + mep_load_config(); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_del_post_op: Error " + "retrieving dn\n"); + } + + /* Get deleted entry, then go through types to find config. */ + slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e ); + + if (e) { + char *managed_dn = NULL; + + /* See if we're an origin entry . */ + managed_dn = slapi_entry_attr_get_charptr(e, MEP_MANAGED_ENTRY_TYPE); + if (managed_dn) { + Slapi_PBlock *mep_pb = slapi_pblock_new(); + + /* Delete the managed entry. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_del_post_op: Deleting managed entry " + "\"%s\" due to deletion of origin entry " + "\"%s\".\n ", managed_dn, dn); + slapi_delete_internal_set_pb(mep_pb, managed_dn, NULL, + NULL, mep_get_plugin_id(), 0); + slapi_delete_internal_pb(mep_pb); + + slapi_ch_free_string(&managed_dn); + slapi_pblock_destroy(mep_pb); + } + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_del_post_op: Error " + "retrieving pre-op entry %s\n", dn); + } + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_del_post_op\n"); + + return 0; +} + +static int +mep_modrdn_post_op(Slapi_PBlock *pb) +{ + char *old_dn = NULL; + char *new_dn = NULL; + Slapi_Entry *post_e = NULL; + char *managed_dn = NULL; + struct configEntry *config = NULL; + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "--> mep_modrdn_post_op\n"); + + /* Just bail if we aren't ready to service requests yet. */ + if (!g_plugin_started || !mep_oktodo(pb)) + return 0;; + + /* Reload config if an existing config entry was renamed, + * or if the new dn brings an entry into the scope of the + * config entries. */ + slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e); + if (post_e) { + new_dn = slapi_entry_get_ndn(post_e); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Error " + "retrieving post-op entry\n"); + } + + if ((old_dn = mep_get_dn(pb))) { + if (mep_dn_is_config(old_dn) || mep_dn_is_config(new_dn)) + mep_load_config(); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Error " + "retrieving dn\n"); + } + + /* See if we're an origin entry . */ + managed_dn = slapi_entry_attr_get_charptr(post_e, MEP_MANAGED_ENTRY_TYPE); + if (managed_dn) { + LDAPMod mod; + LDAPMod *mods[3]; + char *vals[2]; + int result = LDAP_SUCCESS; + Slapi_PBlock *mep_pb = slapi_pblock_new(); + Slapi_Entry *new_managed_entry = NULL; + Slapi_DN *managed_sdn = NULL; + Slapi_Mods *smods = NULL; + + mep_config_read_lock(); + mep_find_config(post_e, &config); + if (!config) { + LDAPMod mod2; + char *vals2[2]; + + /* Delete the associated managed entry. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Removing managed entry \"%s\" " + "since origin entry \"%s\" was moved out of scope.\n", + managed_dn, old_dn); + slapi_delete_internal_set_pb (mep_pb, managed_dn, NULL, NULL, + mep_get_plugin_id(), 0); + slapi_delete_internal_pb(mep_pb); + + /* Clear out the pblock for reuse. */ + slapi_pblock_init(mep_pb); + + /* Remove the pointer from the origin entry. */ + vals[0] = 0; + mod.mod_op = LDAP_MOD_DELETE; + mod.mod_type = MEP_MANAGED_ENTRY_TYPE; + mod.mod_values = vals; + + /* Remove the origin objectclass. */ + vals2[0] = MEP_ORIGIN_OC; + vals2[1] = 0; + mod2.mod_op = LDAP_MOD_DELETE; + mod2.mod_type = SLAPI_ATTR_OBJECTCLASS; + mod2.mod_values = vals2; + + mods[0] = &mod; + mods[1] = &mod2; + mods[2] = 0; + + /* Perform the modify operation. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Removing %s pointer and %s " + "objectclass from entry \"%s\".\n", + MEP_MANAGED_ENTRY_TYPE, MEP_ORIGIN_OC, new_dn); + slapi_modify_internal_set_pb(mep_pb, new_dn, mods, 0, 0, + mep_get_plugin_id(), 0); + slapi_modify_internal_pb(mep_pb); + slapi_pblock_get(mep_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (result != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Unable to remove %s " + "pointer and %s objectclass from entry " + "\"%s\".\n", MEP_MANAGED_ENTRY_TYPE, + MEP_ORIGIN_OC, new_dn); + } + } else { + /* Update backlink to new origin DN in managed entry. */ + vals[0] = new_dn; + vals[1] = 0; + mod.mod_op = LDAP_MOD_REPLACE; + mod.mod_type = MEP_MANAGED_BY_TYPE; + mod.mod_values = vals; + + mods[0] = &mod; + mods[1] = 0; + + /* Perform the modify operation. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Updating %s pointer to \"%s\" " + "in entry \"%s\".\n", MEP_MANAGED_BY_TYPE, new_dn, managed_dn); + slapi_modify_internal_set_pb(mep_pb, managed_dn, mods, 0, 0, + mep_get_plugin_id(), 0); + slapi_modify_internal_pb(mep_pb); + slapi_pblock_get(mep_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (result != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Unable to update pointer to " + "origin entry \"%s\" in managed entry \"%s\" " + "(%s).\n", new_dn, managed_dn, ldap_err2string(result)); + } else { + /* Create a new managed entry to determine what changes + * we need to make to the existing managed entry. */ + new_managed_entry = mep_create_managed_entry(config, post_e); + + /* See if we need to rename the managed entry. */ + managed_sdn = slapi_sdn_new_dn_byref(managed_dn); + if (slapi_sdn_compare(slapi_entry_get_sdn(new_managed_entry), managed_sdn) != 0) { + Slapi_RDN *srdn = slapi_rdn_new(); + + /* Clear out the pblock for reuse. */ + slapi_pblock_init(mep_pb); + + /* Create new RDN */ + slapi_rdn_set_dn(srdn, slapi_entry_get_dn(new_managed_entry)); + + /* Rename the managed entry. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Renaming managed entry " + "\"%s\" to \"%s\" due to rename of origin " + "entry \"%s\".\n ", managed_dn, + slapi_entry_get_dn(new_managed_entry), old_dn); + slapi_rename_internal_set_pb(mep_pb, managed_dn, + slapi_rdn_get_rdn(srdn), + NULL, 1, NULL, NULL, mep_get_plugin_id(), 0); + slapi_modrdn_internal_pb(mep_pb); + slapi_pblock_get(mep_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (result != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Unable to rename managed " + "entry \"%s\" to \"%s\" (%s).\n", managed_dn, + slapi_entry_get_dn(new_managed_entry), + ldap_err2string(result)); + } else { + /* Clear out the pblock for reuse. */ + slapi_pblock_init(mep_pb); + + /* Update the link to the managed entry in the origin + * entry. We can just reuse the mod structure. */ + vals[0] = slapi_entry_get_dn(new_managed_entry); + mod.mod_op = LDAP_MOD_REPLACE; + mod.mod_type = MEP_MANAGED_ENTRY_TYPE; + mod.mod_values = vals; + + /* Perform the modify operation. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Updating %s pointer to " + "\"%s\" in entry \"%s\"\n.", MEP_MANAGED_ENTRY_TYPE, + vals[0], new_dn); + slapi_modify_internal_set_pb(mep_pb, new_dn, mods, 0, 0, + mep_get_plugin_id(), 0); + slapi_modify_internal_pb(mep_pb); + slapi_pblock_get(mep_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (result != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Unable to rename managed " + "entry \"%s\" to \"%s\" (%s).\n", managed_dn, + slapi_entry_get_dn(new_managed_entry), + ldap_err2string(result)); + slapi_rdn_free(&srdn); + goto bail; + } + } + + slapi_rdn_free(&srdn); + } + + /* Update all of the mapped attributes + * to be sure they are up to date. */ + smods = mep_get_mapped_mods(config, post_e); + if (smods) { + /* Clear out the pblock for reuse. */ + slapi_pblock_init(mep_pb); + + /* Perform the modify operation. */ + slapi_log_error(SLAPI_LOG_PLUGIN, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Updating mapped attributes " + "in entry \"%s\"\n.", managed_dn); + slapi_modify_internal_set_pb(mep_pb, slapi_entry_get_dn(new_managed_entry), + slapi_mods_get_ldapmods_byref(smods), 0, 0, + mep_get_plugin_id(), 0); + slapi_modify_internal_pb(mep_pb); + slapi_pblock_get(mep_pb, SLAPI_PLUGIN_INTOP_RESULT, &result); + + if (result != LDAP_SUCCESS) { + slapi_log_error(SLAPI_LOG_FATAL, MEP_PLUGIN_SUBSYSTEM, + "mep_modrdn_post_op: Unable to update mapped " + "attributes from origin entry \"%s\" in managed " + "entry \"%s\" (%s).\n", new_dn, + slapi_entry_get_dn(new_managed_entry), + ldap_err2string(result)); + } + + slapi_mods_free(&smods); + } + + bail: + slapi_sdn_free(&managed_sdn); + slapi_entry_free(new_managed_entry); + } + } + + slapi_pblock_destroy(mep_pb); + + slapi_ch_free_string(&managed_dn); + + mep_config_unlock(); + } else { + /* Was this entry moved into scope of a config entry? + * If so, treat like an add and create the new managed + * entry and links. */ + mep_config_read_lock(); + mep_find_config(post_e, &config); + if (config) { + mep_add_managed_entry(config, post_e); + } + + mep_config_unlock(); + } + + slapi_log_error(SLAPI_LOG_TRACE, MEP_PLUGIN_SUBSYSTEM, + "<-- mep_modrdn_post_op\n"); + + return 0; +} + diff --git a/ldap/servers/plugins/mep/mep.h b/ldap/servers/plugins/mep/mep.h new file mode 100644 index 0000000..9cbd204 --- /dev/null +++ b/ldap/servers/plugins/mep/mep.h @@ -0,0 +1,124 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2010 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* + * Managed entries plug-in header file + */ +#include +#include +#include +#include +#include "portable.h" +#include "nspr.h" +#include "slapi-plugin.h" +#include "prclist.h" +#include "ldif.h" + +/* + * Plug-in defines + */ +#define MEP_PLUGIN_SUBSYSTEM "managed-entries-plugin" +#define MEP_FEATURE_DESC "Managed Entries" +#define MEP_PLUGIN_DESC "Managed Entries plugin" +#define MEP_INT_POSTOP_DESC "Managed Entries internal postop plugin" +#define MEP_POSTOP_DESC "Managed Entries postop plugin" + +/* + * Config type defines + */ +#define MEP_SCOPE_TYPE "originScope" +#define MEP_FILTER_TYPE "originFilter" +#define MEP_MANAGED_BASE_TYPE "managedBase" +#define MEP_MANAGED_TEMPLATE_TYPE "managedTemplate" + +/* + * Link type defines + */ +#define MEP_MANAGED_ENTRY_TYPE "mepManagedEntry" +#define MEP_MANAGED_BY_TYPE "mepManagedBy" + +/* + * Template type defines + */ +#define MEP_STATIC_ATTR_TYPE "mepStaticAttr" +#define MEP_MAPPED_ATTR_TYPE "mepMappedAttr" +#define MEP_RDN_ATTR_TYPE "mepRDNAttr" + +/* + * Objectclass defines + */ +#define MEP_MANAGED_OC "mepManagedEntry" +#define MEP_TEMPLATE_OC "mepTemplateEntry" +#define MEP_ORIGIN_OC "mepOriginEntry" + +/* + * Linked list of config entries. + */ +struct configEntry { + PRCList list; + char *dn; + char *origin_scope; + Slapi_Filter *origin_filter; + char *managed_base; + char *template_dn; + Slapi_Entry *template_entry; +}; + +/* + * Config fetch function + */ +PRCList *mep_get_config(); + +/* + * Config cache locking functions + */ +void mep_config_read_lock(); +void mep_config_write_lock(); +void mep_config_unlock(); + +/* + * Plugin identity functions + */ +void mep_set_plugin_id(void *pluginID); +void *mep_get_plugin_id(); +void mep_set_plugin_dn(char *pluginDN); +char *mep_get_plugin_dn(); + diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 47fc7b8..9229ea7 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -3384,7 +3384,7 @@ int slapi_attr_value_cmp( const Slapi_Attr *attr, const struct berval *v1, const * \param v Pointer to the \c berval structure containing the value for * which you want to search. * \return \c 0 if the attribute contains the specified value. - * \return \c 01 if the attribute does not contain the specified value. + * \return \c -1 if the attribute does not contain the specified value. * \see slapi_attr_add_value() * \see slapi_attr_first_value() * \see slapi_attr_next_value() diff --git a/ltmain.sh b/ltmain.sh old mode 100644 new mode 100755 -- 1.6.2.5