Blame SOURCES/0003-Ticket-137-Implement-EntryUUID-plugin.patch

91ce38
From eff14f0c884f3d2f541e3be6d9df86087177a76d Mon Sep 17 00:00:00 2001
91ce38
From: William Brown <william@blackhats.net.au>
91ce38
Date: Mon, 16 Mar 2020 14:59:56 +1000
91ce38
Subject: [PATCH 03/12] Ticket 137 - Implement EntryUUID plugin
91ce38
91ce38
Bug Description: This implements EntryUUID - A plugin that generates
91ce38
uuid's on attributes, which can be used by external applications to
91ce38
associate an entry uniquely.
91ce38
91ce38
Fix Description: This change is quite large as it contains multiple parts:
91ce38
91ce38
* Schema for entryuuid.
91ce38
    ldap/schema/02common.ldif
91ce38
    ldap/schema/03entryuuid.ldif
91ce38
* Documentation of the plugin design
91ce38
    src/README.md
91ce38
* A rust plugin api.
91ce38
    src/slapi_r_plugin/Cargo.toml
91ce38
    src/slapi_r_plugin/README.md
91ce38
    src/slapi_r_plugin/build.rs
91ce38
    src/slapi_r_plugin/src/backend.rs
91ce38
    src/slapi_r_plugin/src/ber.rs
91ce38
    src/slapi_r_plugin/src/constants.rs
91ce38
    src/slapi_r_plugin/src/dn.rs
91ce38
    src/slapi_r_plugin/src/entry.rs
91ce38
    src/slapi_r_plugin/src/error.rs
91ce38
    src/slapi_r_plugin/src/init.c
91ce38
    src/slapi_r_plugin/src/lib.rs
91ce38
    src/slapi_r_plugin/src/log.rs
91ce38
    src/slapi_r_plugin/src/macros.rs
91ce38
    src/slapi_r_plugin/src/pblock.rs
91ce38
    src/slapi_r_plugin/src/plugin.rs
91ce38
    src/slapi_r_plugin/src/search.rs
91ce38
    src/slapi_r_plugin/src/syntax_plugin.rs
91ce38
    src/slapi_r_plugin/src/task.rs
91ce38
    src/slapi_r_plugin/src/value.rs
91ce38
* An entry uuid syntax plugin, that has functional indexing
91ce38
    src/plugins/entryuuid_syntax/Cargo.toml
91ce38
    src/plugins/entryuuid_syntax/src/lib.rs
91ce38
* A entry uuid plugin that generates entryuuid's and has a fixup task.
91ce38
    src/plugins/entryuuid/Cargo.toml
91ce38
    src/plugins/entryuuid/src/lib.rs
91ce38
* Supporting changes in the server core to enable and provide apis for the plugins.
91ce38
    ldap/servers/slapd/config.c
91ce38
    ldap/servers/slapd/entry.c
91ce38
    ldap/servers/slapd/fedse.c
91ce38
* A test suite for for the entryuuid plugin
91ce38
    dirsrvtests/tests/data/entryuuid/localhost-userRoot-2020_03_30_13_14_47.ldif
91ce38
    dirsrvtests/tests/suites/entryuuid/basic_test.py
91ce38
* Supporting changes in lib389
91ce38
    src/lib389/lib389/_constants.py
91ce38
    src/lib389/lib389/backend.py
91ce38
    src/lib389/lib389/instance/setup.py
91ce38
    src/lib389/lib389/plugins.py
91ce38
    src/lib389/lib389/tasks.py
91ce38
* Changes to support building the plugins
91ce38
    Makefile.am
91ce38
    configure.ac
91ce38
* Execution of cargo fmt on the tree, causing some clean up of files.
91ce38
    src/Cargo.lock
91ce38
    src/Cargo.toml
91ce38
    src/librnsslapd/build.rs
91ce38
    src/librnsslapd/src/lib.rs
91ce38
    src/librslapd/Cargo.toml
91ce38
    src/librslapd/build.rs
91ce38
    src/librslapd/src/lib.rs
91ce38
    src/libsds/sds/lib.rs
91ce38
    src/libsds/sds/tqueue.rs
91ce38
    src/slapd/src/error.rs
91ce38
    src/slapd/src/fernet.rs
91ce38
    src/slapd/src/lib.rs
91ce38
91ce38
https://pagure.io/389-ds-base/issue/137
91ce38
91ce38
Author: William Brown <william@blackhats.net.au>
91ce38
91ce38
Review by: mreynolds, lkrispenz (Thanks)
91ce38
---
91ce38
 Makefile.am                                   |  96 +-
91ce38
 ...ocalhost-userRoot-2020_03_30_13_14_47.ldif | 233 +++++
91ce38
 .../tests/suites/entryuuid/basic_test.py      | 226 +++++
91ce38
 ldap/schema/02common.ldif                     |   1 +
91ce38
 ldap/schema/03entryuuid.ldif                  |  16 +
91ce38
 ldap/servers/slapd/config.c                   |  17 +
91ce38
 ldap/servers/slapd/entry.c                    |  12 +
91ce38
 ldap/servers/slapd/fedse.c                    |  28 +
91ce38
 src/Cargo.lock                                | 241 +++--
91ce38
 src/Cargo.toml                                |  11 +-
91ce38
 src/README.md                                 |   0
91ce38
 src/lib389/lib389/_constants.py               |   1 +
91ce38
 src/lib389/lib389/backend.py                  |   2 +-
91ce38
 src/lib389/lib389/instance/setup.py           |  14 +
91ce38
 src/lib389/lib389/plugins.py                  |  30 +
91ce38
 src/lib389/lib389/tasks.py                    |  14 +
91ce38
 src/librnsslapd/build.rs                      |  19 +-
91ce38
 src/librnsslapd/src/lib.rs                    |  16 +-
91ce38
 src/librslapd/Cargo.toml                      |   4 -
91ce38
 src/librslapd/build.rs                        |  19 +-
91ce38
 src/librslapd/src/lib.rs                      |  11 +-
91ce38
 src/libsds/sds/lib.rs                         |   2 -
91ce38
 src/libsds/sds/tqueue.rs                      |  23 +-
91ce38
 src/plugins/entryuuid/Cargo.toml              |  21 +
91ce38
 src/plugins/entryuuid/src/lib.rs              | 196 ++++
91ce38
 src/plugins/entryuuid_syntax/Cargo.toml       |  21 +
91ce38
 src/plugins/entryuuid_syntax/src/lib.rs       | 145 +++
91ce38
 src/slapd/src/error.rs                        |   2 -
91ce38
 src/slapd/src/fernet.rs                       |  31 +-
91ce38
 src/slapd/src/lib.rs                          |   3 -
91ce38
 src/slapi_r_plugin/Cargo.toml                 |  19 +
91ce38
 src/slapi_r_plugin/README.md                  | 216 +++++
91ce38
 src/slapi_r_plugin/build.rs                   |   8 +
91ce38
 src/slapi_r_plugin/src/backend.rs             |  71 ++
91ce38
 src/slapi_r_plugin/src/ber.rs                 |  90 ++
91ce38
 src/slapi_r_plugin/src/constants.rs           | 203 +++++
91ce38
 src/slapi_r_plugin/src/dn.rs                  | 108 +++
91ce38
 src/slapi_r_plugin/src/entry.rs               |  92 ++
91ce38
 src/slapi_r_plugin/src/error.rs               |  61 ++
91ce38
 src/slapi_r_plugin/src/init.c                 |   8 +
91ce38
 src/slapi_r_plugin/src/lib.rs                 |  36 +
91ce38
 src/slapi_r_plugin/src/log.rs                 |  87 ++
91ce38
 src/slapi_r_plugin/src/macros.rs              | 835 ++++++++++++++++++
91ce38
 src/slapi_r_plugin/src/pblock.rs              | 275 ++++++
91ce38
 src/slapi_r_plugin/src/plugin.rs              | 117 +++
91ce38
 src/slapi_r_plugin/src/search.rs              | 127 +++
91ce38
 src/slapi_r_plugin/src/syntax_plugin.rs       | 169 ++++
91ce38
 src/slapi_r_plugin/src/task.rs                | 148 ++++
91ce38
 src/slapi_r_plugin/src/value.rs               | 235 +++++
91ce38
 49 files changed, 4213 insertions(+), 147 deletions(-)
91ce38
 create mode 100644 dirsrvtests/tests/data/entryuuid/localhost-userRoot-2020_03_30_13_14_47.ldif
91ce38
 create mode 100644 dirsrvtests/tests/suites/entryuuid/basic_test.py
91ce38
 create mode 100644 ldap/schema/03entryuuid.ldif
91ce38
 create mode 100644 src/README.md
91ce38
 create mode 100644 src/plugins/entryuuid/Cargo.toml
91ce38
 create mode 100644 src/plugins/entryuuid/src/lib.rs
91ce38
 create mode 100644 src/plugins/entryuuid_syntax/Cargo.toml
91ce38
 create mode 100644 src/plugins/entryuuid_syntax/src/lib.rs
91ce38
 create mode 100644 src/slapi_r_plugin/Cargo.toml
91ce38
 create mode 100644 src/slapi_r_plugin/README.md
91ce38
 create mode 100644 src/slapi_r_plugin/build.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/backend.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/ber.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/constants.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/dn.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/entry.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/error.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/init.c
91ce38
 create mode 100644 src/slapi_r_plugin/src/lib.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/log.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/macros.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/pblock.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/plugin.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/search.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/syntax_plugin.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/task.rs
91ce38
 create mode 100644 src/slapi_r_plugin/src/value.rs
91ce38
91ce38
diff --git a/Makefile.am b/Makefile.am
91ce38
index 668a095da..627953850 100644
91ce38
--- a/Makefile.am
91ce38
+++ b/Makefile.am
91ce38
@@ -38,6 +38,7 @@ if RUST_ENABLE
91ce38
 RUST_ON = 1
91ce38
 CARGO_FLAGS = @cargo_defs@
91ce38
 RUSTC_FLAGS = @asan_rust_defs@ @msan_rust_defs@ @tsan_rust_defs@ @debug_rust_defs@
91ce38
+# -L@abs_top_builddir@/rs/@rust_target_dir@
91ce38
 RUST_LDFLAGS = -ldl -lpthread -lgcc_s -lc -lm -lrt -lutil
91ce38
 RUST_DEFINES = -DRUST_ENABLE
91ce38
 if RUST_ENABLE_OFFLINE
91ce38
@@ -298,7 +299,7 @@ clean-local:
91ce38
 	-rm -rf $(abs_top_builddir)/html
91ce38
 	-rm -rf $(abs_top_builddir)/man/man3
91ce38
 if RUST_ENABLE
91ce38
-	CARGO_TARGET_DIR=$(abs_top_builddir)/rs cargo clean --manifest-path=$(srcdir)/src/libsds/Cargo.toml
91ce38
+	CARGO_TARGET_DIR=$(abs_top_builddir)/rs cargo clean --manifest-path=$(srcdir)/src/Cargo.toml
91ce38
 endif
91ce38
 
91ce38
 dberrstrs.h: Makefile
91ce38
@@ -416,6 +417,11 @@ serverplugin_LTLIBRARIES = libacl-plugin.la \
91ce38
 	$(LIBPAM_PASSTHRU_PLUGIN) $(LIBDNA_PLUGIN) \
91ce38
 	$(LIBBITWISE_PLUGIN) $(LIBPRESENCE_PLUGIN) $(LIBPOSIX_WINSYNC_PLUGIN)
91ce38
 
91ce38
+if RUST_ENABLE
91ce38
+serverplugin_LTLIBRARIES += libentryuuid-plugin.la libentryuuid-syntax-plugin.la
91ce38
+endif
91ce38
+
91ce38
+
91ce38
 noinst_LIBRARIES = libavl.a
91ce38
 
91ce38
 dist_noinst_HEADERS = \
91ce38
@@ -757,6 +763,10 @@ systemschema_DATA = $(srcdir)/ldap/schema/00core.ldif \
91ce38
 	$(srcdir)/ldap/schema/60nss-ldap.ldif \
91ce38
 	$(LIBACCTPOLICY_SCHEMA)
91ce38
 
91ce38
+if RUST_ENABLE
91ce38
+systemschema_DATA += $(srcdir)/ldap/schema/03entryuuid.ldif
91ce38
+endif
91ce38
+
91ce38
 schema_DATA = $(srcdir)/ldap/schema/99user.ldif
91ce38
 
91ce38
 libexec_SCRIPTS =
91ce38
@@ -1227,7 +1237,7 @@ libsds_la_LDFLAGS = $(AM_LDFLAGS) $(SDS_LDFLAGS)
91ce38
 
91ce38
 if RUST_ENABLE
91ce38
 
91ce38
-noinst_LTLIBRARIES = librsds.la librslapd.la librnsslapd.la
91ce38
+noinst_LTLIBRARIES = librsds.la librslapd.la librnsslapd.la libentryuuid.la libentryuuid_syntax.la
91ce38
 
91ce38
 ### Why does this exist?
91ce38
 #
91ce38
@@ -1252,6 +1262,8 @@ librsds_la_EXTRA = src/libsds/Cargo.lock
91ce38
 @abs_top_builddir@/rs/@rust_target_dir@/librsds.a: $(librsds_la_SOURCES)
91ce38
 	RUST_BACKTRACE=1 RUSTC_BOOTSTRAP=1 \
91ce38
 	CARGO_TARGET_DIR=$(abs_top_builddir)/rs \
91ce38
+	SLAPD_DYLIB_DIR=$(abs_top_builddir)/ \
91ce38
+	SLAPD_HEADER_DIR=$(abs_top_builddir)/ \
91ce38
 		cargo rustc $(RUST_OFFLINE) --manifest-path=$(srcdir)/src/libsds/Cargo.toml \
91ce38
 		$(CARGO_FLAGS) --verbose -- $(RUSTC_FLAGS)
91ce38
 
91ce38
@@ -1268,6 +1280,7 @@ librslapd_la_EXTRA = src/librslapd/Cargo.lock
91ce38
 @abs_top_builddir@/rs/@rust_target_dir@/librslapd.a: $(librslapd_la_SOURCES)
91ce38
 	RUST_BACKTRACE=1 RUSTC_BOOTSTRAP=1 \
91ce38
 	CARGO_TARGET_DIR=$(abs_top_builddir)/rs \
91ce38
+	SLAPD_DYLIB_DIR=$(abs_top_builddir)/ \
91ce38
 	SLAPD_HEADER_DIR=$(abs_top_builddir)/ \
91ce38
 		cargo rustc $(RUST_OFFLINE) --manifest-path=$(srcdir)/src/librslapd/Cargo.toml \
91ce38
 		$(CARGO_FLAGS) --verbose -- $(RUSTC_FLAGS)
91ce38
@@ -1288,6 +1301,7 @@ librnsslapd_la_EXTRA = src/librnsslapd/Cargo.lock
91ce38
 @abs_top_builddir@/rs/@rust_target_dir@/librnsslapd.a: $(librnsslapd_la_SOURCES)
91ce38
 	RUST_BACKTRACE=1 RUSTC_BOOTSTRAP=1 \
91ce38
 	CARGO_TARGET_DIR=$(abs_top_builddir)/rs \
91ce38
+	SLAPD_DYLIB_DIR=$(abs_top_builddir)/ \
91ce38
 	SLAPD_HEADER_DIR=$(abs_top_builddir)/ \
91ce38
 		cargo rustc $(RUST_OFFLINE) --manifest-path=$(srcdir)/src/librnsslapd/Cargo.toml \
91ce38
 		$(CARGO_FLAGS) --verbose -- $(RUSTC_FLAGS)
91ce38
@@ -1295,8 +1309,64 @@ librnsslapd_la_EXTRA = src/librnsslapd/Cargo.lock
91ce38
 # The header needs the lib build first.
91ce38
 rust-nsslapd-private.h: @abs_top_builddir@/rs/@rust_target_dir@/librnsslapd.a
91ce38
 
91ce38
+libslapi_r_plugin_SOURCES = \
91ce38
+	src/slapi_r_plugin/src/backend.rs \
91ce38
+	src/slapi_r_plugin/src/ber.rs \
91ce38
+	src/slapi_r_plugin/src/constants.rs \
91ce38
+	src/slapi_r_plugin/src/dn.rs \
91ce38
+	src/slapi_r_plugin/src/entry.rs \
91ce38
+	src/slapi_r_plugin/src/error.rs \
91ce38
+	src/slapi_r_plugin/src/log.rs \
91ce38
+	src/slapi_r_plugin/src/macros.rs \
91ce38
+	src/slapi_r_plugin/src/pblock.rs \
91ce38
+	src/slapi_r_plugin/src/plugin.rs \
91ce38
+	src/slapi_r_plugin/src/search.rs \
91ce38
+	src/slapi_r_plugin/src/syntax_plugin.rs \
91ce38
+	src/slapi_r_plugin/src/task.rs \
91ce38
+	src/slapi_r_plugin/src/value.rs \
91ce38
+	src/slapi_r_plugin/src/lib.rs
91ce38
+
91ce38
+# Build rust ns-slapd components as a library.
91ce38
+ENTRYUUID_LIB = @abs_top_builddir@/rs/@rust_target_dir@/libentryuuid.a
91ce38
+
91ce38
+libentryuuid_la_SOURCES = \
91ce38
+	src/plugins/entryuuid/Cargo.toml \
91ce38
+	src/plugins/entryuuid/src/lib.rs \
91ce38
+	$(libslapi_r_plugin_SOURCES)
91ce38
+
91ce38
+libentryuuid_la_EXTRA = src/plugin/entryuuid/Cargo.lock
91ce38
+
91ce38
+@abs_top_builddir@/rs/@rust_target_dir@/libentryuuid.a: $(libentryuuid_la_SOURCES) libslapd.la libentryuuid.la
91ce38
+	RUST_BACKTRACE=1 RUSTC_BOOTSTRAP=1 \
91ce38
+	CARGO_TARGET_DIR=$(abs_top_builddir)/rs \
91ce38
+	SLAPD_DYLIB_DIR=$(abs_top_builddir)/ \
91ce38
+	SLAPD_HEADER_DIR=$(abs_top_builddir)/ \
91ce38
+		cargo rustc $(RUST_OFFLINE) --manifest-path=$(srcdir)/src/plugins/entryuuid/Cargo.toml \
91ce38
+		$(CARGO_FLAGS) --verbose -- $(RUSTC_FLAGS)
91ce38
+	cp $(ENTRYUUID_LIB) @abs_top_builddir@/.libs/libentryuuid.a
91ce38
+
91ce38
+ENTRYUUID_SYNTAX_LIB = @abs_top_builddir@/rs/@rust_target_dir@/libentryuuid_syntax.a
91ce38
+
91ce38
+libentryuuid_syntax_la_SOURCES = \
91ce38
+	src/plugins/entryuuid_syntax/Cargo.toml \
91ce38
+	src/plugins/entryuuid_syntax/src/lib.rs \
91ce38
+	$(libslapi_r_plugin_SOURCES)
91ce38
+
91ce38
+libentryuuid_syntax_la_EXTRA = src/plugin/entryuuid_syntax/Cargo.lock
91ce38
+
91ce38
+@abs_top_builddir@/rs/@rust_target_dir@/libentryuuid_syntax.a: $(libentryuuid_syntax_la_SOURCES) libslapd.la libentryuuid_syntax.la
91ce38
+	RUST_BACKTRACE=1 RUSTC_BOOTSTRAP=1 \
91ce38
+	CARGO_TARGET_DIR=$(abs_top_builddir)/rs \
91ce38
+	SLAPD_DYLIB_DIR=$(abs_top_builddir)/ \
91ce38
+	SLAPD_HEADER_DIR=$(abs_top_builddir)/ \
91ce38
+		cargo rustc $(RUST_OFFLINE) --manifest-path=$(srcdir)/src/plugins/entryuuid_syntax/Cargo.toml \
91ce38
+		$(CARGO_FLAGS) --verbose -- $(RUSTC_FLAGS)
91ce38
+	cp $(ENTRYUUID_SYNTAX_LIB) @abs_top_builddir@/.libs/libentryuuid_syntax.a
91ce38
+
91ce38
 EXTRA_DIST = $(librsds_la_SOURCES) $(librsds_la_EXTRA) \
91ce38
 			$(librslapd_la_SOURCES) $(librslapd_la_EXTRA) \
91ce38
+			$(libentryuuid_la_SOURCES) $(libentryuuid_la_EXTRA) \
91ce38
+			$(libentryuuid_syntax_la_SOURCES) $(libentryuuid_syntax_la_EXTRA) \
91ce38
 			$(librnsslapd_la_SOURCES) $(librnsslapd_la_EXTRA)
91ce38
 
91ce38
 ## Run rust tests
91ce38
@@ -1306,13 +1376,17 @@ else
91ce38
 check-local:
91ce38
 	RUST_BACKTRACE=1 RUSTC_BOOTSTRAP=1 \
91ce38
 	CARGO_TARGET_DIR=$(abs_top_builddir)/rs \
91ce38
+	SLAPD_DYLIB_DIR=$(abs_top_builddir)/ \
91ce38
+	SLAPD_HEADER_DIR=$(abs_top_builddir)/ \
91ce38
 		cargo test $(RUST_OFFLINE) --manifest-path=$(srcdir)/src/libsds/Cargo.toml
91ce38
 	RUST_BACKTRACE=1 RUSTC_BOOTSTRAP=1 \
91ce38
 	CARGO_TARGET_DIR=$(abs_top_builddir)/rs \
91ce38
+	SLAPD_DYLIB_DIR=$(abs_top_builddir)/ \
91ce38
 	SLAPD_HEADER_DIR=$(abs_top_builddir)/ \
91ce38
 		cargo test $(RUST_OFFLINE) --manifest-path=$(srcdir)/src/librslapd/Cargo.toml
91ce38
 	RUST_BACKTRACE=1 RUSTC_BOOTSTRAP=1 \
91ce38
 	CARGO_TARGET_DIR=$(abs_top_builddir)/rs \
91ce38
+	SLAPD_DYLIB_DIR=$(abs_top_builddir)/ \
91ce38
 	SLAPD_HEADER_DIR=$(abs_top_builddir)/ \
91ce38
 		cargo test $(RUST_OFFLINE) --manifest-path=$(srcdir)/src/librnsslapd/Cargo.toml
91ce38
 endif
91ce38
@@ -1735,6 +1809,24 @@ libderef_plugin_la_LIBADD = libslapd.la $(LDAPSDK_LINK) $(NSPR_LINK)
91ce38
 libderef_plugin_la_DEPENDENCIES = libslapd.la
91ce38
 libderef_plugin_la_LDFLAGS = -avoid-version
91ce38
 
91ce38
+if RUST_ENABLE
91ce38
+#------------------------
91ce38
+# libentryuuid-syntax-plugin
91ce38
+#-----------------------
91ce38
+libentryuuid_syntax_plugin_la_SOURCES = src/slapi_r_plugin/src/init.c
91ce38
+libentryuuid_syntax_plugin_la_LIBADD = libslapd.la $(LDAPSDK_LINK) $(NSPR_LINK) -lentryuuid_syntax
91ce38
+libentryuuid_syntax_plugin_la_DEPENDENCIES = libslapd.la $(ENTRYUUID_SYNTAX_LIB)
91ce38
+libentryuuid_syntax_plugin_la_LDFLAGS = -avoid-version
91ce38
+
91ce38
+#------------------------
91ce38
+# libentryuuid-plugin
91ce38
+#-----------------------
91ce38
+libentryuuid_plugin_la_SOURCES = src/slapi_r_plugin/src/init.c
91ce38
+libentryuuid_plugin_la_LIBADD = libslapd.la $(LDAPSDK_LINK) $(NSPR_LINK) -lentryuuid
91ce38
+libentryuuid_plugin_la_DEPENDENCIES = libslapd.la $(ENTRYUUID_LIB)
91ce38
+libentryuuid_plugin_la_LDFLAGS = -avoid-version
91ce38
+endif
91ce38
+
91ce38
 #------------------------
91ce38
 # libpbe-plugin
91ce38
 #-----------------------
91ce38
diff --git a/dirsrvtests/tests/data/entryuuid/localhost-userRoot-2020_03_30_13_14_47.ldif b/dirsrvtests/tests/data/entryuuid/localhost-userRoot-2020_03_30_13_14_47.ldif
91ce38
new file mode 100644
91ce38
index 000000000..b64090af7
91ce38
--- /dev/null
91ce38
+++ b/dirsrvtests/tests/data/entryuuid/localhost-userRoot-2020_03_30_13_14_47.ldif
91ce38
@@ -0,0 +1,233 @@
91ce38
+version: 1
91ce38
+
91ce38
+# entry-id: 1
91ce38
+dn: dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: domain
91ce38
+dc: example
91ce38
+description: dc=example,dc=com
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015542Z
91ce38
+modifyTimestamp: 20200325015542Z
91ce38
+nsUniqueId: a2b33229-6e3b11ea-8de0c78c-83e27eda
91ce38
+aci: (targetattr="dc || description || objectClass")(targetfilter="(objectClas
91ce38
+ s=domain)")(version 3.0; acl "Enable anyone domain read"; allow (read, search
91ce38
+ , compare)(userdn="ldap:///anyone");)
91ce38
+aci: (targetattr="ou || objectClass")(targetfilter="(objectClass=organizationa
91ce38
+ lUnit)")(version 3.0; acl "Enable anyone ou read"; allow (read, search, compa
91ce38
+ re)(userdn="ldap:///anyone");)
91ce38
+
91ce38
+# entry-id: 2
91ce38
+dn: cn=389_ds_system,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: nscontainer
91ce38
+objectClass: ldapsubentry
91ce38
+cn: 389_ds_system
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015542Z
91ce38
+modifyTimestamp: 20200325015542Z
91ce38
+nsUniqueId: a2b3322a-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 3
91ce38
+dn: ou=groups,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: organizationalunit
91ce38
+ou: groups
91ce38
+aci: (targetattr="cn || member || gidNumber || nsUniqueId || description || ob
91ce38
+ jectClass")(targetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enab
91ce38
+ le anyone group read"; allow (read, search, compare)(userdn="ldap:///anyone")
91ce38
+ ;)
91ce38
+aci: (targetattr="member")(targetfilter="(objectClass=groupOfNames)")(version 
91ce38
+ 3.0; acl "Enable group_modify to alter members"; allow (write)(groupdn="ldap:
91ce38
+ ///cn=group_modify,ou=permissions,dc=example,dc=com");)
91ce38
+aci: (targetattr="cn || member || gidNumber || description || objectClass")(ta
91ce38
+ rgetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enable group_admin
91ce38
+  to manage groups"; allow (write, add, delete)(groupdn="ldap:///cn=group_admi
91ce38
+ n,ou=permissions,dc=example,dc=com");)
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015543Z
91ce38
+modifyTimestamp: 20200325015543Z
91ce38
+nsUniqueId: a2b3322b-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 4
91ce38
+dn: ou=people,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: organizationalunit
91ce38
+ou: people
91ce38
+aci: (targetattr="objectClass || description || nsUniqueId || uid || displayNa
91ce38
+ me || loginShell || uidNumber || gidNumber || gecos || homeDirectory || cn ||
91ce38
+  memberOf || mail || nsSshPublicKey || nsAccountLock || userCertificate")(tar
91ce38
+ getfilter="(objectClass=posixaccount)")(version 3.0; acl "Enable anyone user 
91ce38
+ read"; allow (read, search, compare)(userdn="ldap:///anyone");)
91ce38
+aci: (targetattr="displayName || legalName || userPassword || nsSshPublicKey")
91ce38
+ (version 3.0; acl "Enable self partial modify"; allow (write)(userdn="ldap://
91ce38
+ /self");)
91ce38
+aci: (targetattr="legalName || telephoneNumber || mobile || sn")(targetfilter=
91ce38
+ "(|(objectClass=nsPerson)(objectClass=inetOrgPerson))")(version 3.0; acl "Ena
91ce38
+ ble self legalname read"; allow (read, search, compare)(userdn="ldap:///self"
91ce38
+ );)
91ce38
+aci: (targetattr="legalName || telephoneNumber")(targetfilter="(objectClass=ns
91ce38
+ Person)")(version 3.0; acl "Enable user legalname read"; allow (read, search,
91ce38
+  compare)(groupdn="ldap:///cn=user_private_read,ou=permissions,dc=example,dc=
91ce38
+ com");)
91ce38
+aci: (targetattr="uid || description || displayName || loginShell || uidNumber
91ce38
+  || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalNam
91ce38
+ e || telephoneNumber || mobile")(targetfilter="(&(objectClass=nsPerson)(objec
91ce38
+ tClass=nsAccount))")(version 3.0; acl "Enable user admin create"; allow (writ
91ce38
+ e, add, delete, read)(groupdn="ldap:///cn=user_admin,ou=permissions,dc=exampl
91ce38
+ e,dc=com");)
91ce38
+aci: (targetattr="uid || description || displayName || loginShell || uidNumber
91ce38
+  || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalNam
91ce38
+ e || telephoneNumber || mobile")(targetfilter="(&(objectClass=nsPerson)(objec
91ce38
+ tClass=nsAccount))")(version 3.0; acl "Enable user modify to change users"; a
91ce38
+ llow (write, read)(groupdn="ldap:///cn=user_modify,ou=permissions,dc=example,
91ce38
+ dc=com");)
91ce38
+aci: (targetattr="userPassword || nsAccountLock || userCertificate || nsSshPub
91ce38
+ licKey")(targetfilter="(objectClass=nsAccount)")(version 3.0; acl "Enable use
91ce38
+ r password reset"; allow (write, read)(groupdn="ldap:///cn=user_passwd_reset,
91ce38
+ ou=permissions,dc=example,dc=com");)
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015543Z
91ce38
+modifyTimestamp: 20200325015543Z
91ce38
+nsUniqueId: a2b3322c-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 5
91ce38
+dn: ou=permissions,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: organizationalunit
91ce38
+ou: permissions
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015543Z
91ce38
+modifyTimestamp: 20200325015543Z
91ce38
+nsUniqueId: a2b3322d-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 6
91ce38
+dn: ou=services,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: organizationalunit
91ce38
+ou: services
91ce38
+aci: (targetattr="objectClass || description || nsUniqueId || cn || memberOf |
91ce38
+ | nsAccountLock ")(targetfilter="(objectClass=netscapeServer)")(version 3.0; 
91ce38
+ acl "Enable anyone service account read"; allow (read, search, compare)(userd
91ce38
+ n="ldap:///anyone");)
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015544Z
91ce38
+modifyTimestamp: 20200325015544Z
91ce38
+nsUniqueId: a2b3322e-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 7
91ce38
+dn: uid=demo_user,ou=people,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: nsPerson
91ce38
+objectClass: nsAccount
91ce38
+objectClass: nsOrgPerson
91ce38
+objectClass: posixAccount
91ce38
+uid: demo_user
91ce38
+cn: Demo User
91ce38
+displayName: Demo User
91ce38
+legalName: Demo User Name
91ce38
+uidNumber: 99998
91ce38
+gidNumber: 99998
91ce38
+homeDirectory: /var/empty
91ce38
+loginShell: /bin/false
91ce38
+nsAccountLock: true
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015544Z
91ce38
+modifyTimestamp: 20200325061615Z
91ce38
+nsUniqueId: a2b3322f-6e3b11ea-8de0c78c-83e27eda
91ce38
+entryUUID: 973e1bbf-ba9c-45d4-b01b-ff7371fd9008
91ce38
+
91ce38
+# entry-id: 8
91ce38
+dn: cn=demo_group,ou=groups,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: groupOfNames
91ce38
+objectClass: posixGroup
91ce38
+objectClass: nsMemberOf
91ce38
+cn: demo_group
91ce38
+gidNumber: 99999
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015544Z
91ce38
+modifyTimestamp: 20200325015544Z
91ce38
+nsUniqueId: a2b33230-6e3b11ea-8de0c78c-83e27eda
91ce38
+entryUUID: f6df8fe9-6b30-46aa-aa13-f0bf755371e8
91ce38
+
91ce38
+# entry-id: 9
91ce38
+dn: cn=group_admin,ou=permissions,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: groupOfNames
91ce38
+objectClass: nsMemberOf
91ce38
+cn: group_admin
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015545Z
91ce38
+modifyTimestamp: 20200325015545Z
91ce38
+nsUniqueId: a2b33231-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 10
91ce38
+dn: cn=group_modify,ou=permissions,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: groupOfNames
91ce38
+objectClass: nsMemberOf
91ce38
+cn: group_modify
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015545Z
91ce38
+modifyTimestamp: 20200325015545Z
91ce38
+nsUniqueId: a2b33232-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 11
91ce38
+dn: cn=user_admin,ou=permissions,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: groupOfNames
91ce38
+objectClass: nsMemberOf
91ce38
+cn: user_admin
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015545Z
91ce38
+modifyTimestamp: 20200325015545Z
91ce38
+nsUniqueId: a2b33233-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 12
91ce38
+dn: cn=user_modify,ou=permissions,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: groupOfNames
91ce38
+objectClass: nsMemberOf
91ce38
+cn: user_modify
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015546Z
91ce38
+modifyTimestamp: 20200325015546Z
91ce38
+nsUniqueId: a2b33234-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 13
91ce38
+dn: cn=user_passwd_reset,ou=permissions,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: groupOfNames
91ce38
+objectClass: nsMemberOf
91ce38
+cn: user_passwd_reset
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015546Z
91ce38
+modifyTimestamp: 20200325015546Z
91ce38
+nsUniqueId: a2b33235-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
+# entry-id: 14
91ce38
+dn: cn=user_private_read,ou=permissions,dc=example,dc=com
91ce38
+objectClass: top
91ce38
+objectClass: groupOfNames
91ce38
+objectClass: nsMemberOf
91ce38
+cn: user_private_read
91ce38
+creatorsName: cn=Directory Manager
91ce38
+modifiersName: cn=Directory Manager
91ce38
+createTimestamp: 20200325015547Z
91ce38
+modifyTimestamp: 20200325015547Z
91ce38
+nsUniqueId: a2b33236-6e3b11ea-8de0c78c-83e27eda
91ce38
+
91ce38
diff --git a/dirsrvtests/tests/suites/entryuuid/basic_test.py b/dirsrvtests/tests/suites/entryuuid/basic_test.py
91ce38
new file mode 100644
91ce38
index 000000000..beb73701d
91ce38
--- /dev/null
91ce38
+++ b/dirsrvtests/tests/suites/entryuuid/basic_test.py
91ce38
@@ -0,0 +1,226 @@
91ce38
+# --- BEGIN COPYRIGHT BLOCK ---
91ce38
+# Copyright (C) 2020 William Brown <william@blackhats.net.au>
91ce38
+# All rights reserved.
91ce38
+#
91ce38
+# License: GPL (version 3 or any later version).
91ce38
+# See LICENSE for details.
91ce38
+# --- END COPYRIGHT BLOCK ---
91ce38
+
91ce38
+import ldap
91ce38
+import pytest
91ce38
+import time
91ce38
+import shutil
91ce38
+from lib389.idm.user import nsUserAccounts, UserAccounts
91ce38
+from lib389.idm.account import Accounts
91ce38
+from lib389.topologies import topology_st as topology
91ce38
+from lib389.backend import Backends
91ce38
+from lib389.paths import Paths
91ce38
+from lib389.utils import ds_is_older
91ce38
+from lib389._constants import *
91ce38
+from lib389.plugins import EntryUUIDPlugin
91ce38
+
91ce38
+default_paths = Paths()
91ce38
+
91ce38
+pytestmark = pytest.mark.tier1
91ce38
+
91ce38
+DATADIR1 = os.path.join(os.path.dirname(__file__), '../../data/entryuuid/')
91ce38
+IMPORT_UUID_A = "973e1bbf-ba9c-45d4-b01b-ff7371fd9008"
91ce38
+UUID_BETWEEN = "eeeeeeee-0000-0000-0000-000000000000"
91ce38
+IMPORT_UUID_B = "f6df8fe9-6b30-46aa-aa13-f0bf755371e8"
91ce38
+UUID_MIN = "00000000-0000-0000-0000-000000000000"
91ce38
+UUID_MAX = "ffffffff-ffff-ffff-ffff-ffffffffffff"
91ce38
+
91ce38
+def _entryuuid_import_and_search(topology):
91ce38
+    # 1
91ce38
+    ldif_dir = topology.standalone.get_ldif_dir()
91ce38
+    target_ldif = os.path.join(ldif_dir, 'localhost-userRoot-2020_03_30_13_14_47.ldif')
91ce38
+    import_ldif = os.path.join(DATADIR1, 'localhost-userRoot-2020_03_30_13_14_47.ldif')
91ce38
+    shutil.copyfile(import_ldif, target_ldif)
91ce38
+
91ce38
+    be = Backends(topology.standalone).get('userRoot')
91ce38
+    task = be.import_ldif([target_ldif])
91ce38
+    task.wait()
91ce38
+    assert(task.is_complete() and task.get_exit_code() == 0)
91ce38
+
91ce38
+    accounts = Accounts(topology.standalone, DEFAULT_SUFFIX)
91ce38
+    # 2 - positive eq test
91ce38
+    r2 = accounts.filter("(entryUUID=%s)" % IMPORT_UUID_A)
91ce38
+    assert(len(r2) == 1)
91ce38
+    r3 = accounts.filter("(entryuuid=%s)" % IMPORT_UUID_B)
91ce38
+    assert(len(r3) == 1)
91ce38
+    # 3 - negative eq test
91ce38
+    r4 = accounts.filter("(entryuuid=%s)" % UUID_MAX)
91ce38
+    assert(len(r4) == 0)
91ce38
+    # 4 - le search
91ce38
+    r5 = accounts.filter("(entryuuid<=%s)" % UUID_BETWEEN)
91ce38
+    assert(len(r5) == 1)
91ce38
+    # 5 - ge search
91ce38
+    r6 = accounts.filter("(entryuuid>=%s)" % UUID_BETWEEN)
91ce38
+    assert(len(r6) == 1)
91ce38
+    # 6 - le 0 search
91ce38
+    r7 = accounts.filter("(entryuuid<=%s)" % UUID_MIN)
91ce38
+    assert(len(r7) == 0)
91ce38
+    # 7 - ge f search
91ce38
+    r8 = accounts.filter("(entryuuid>=%s)" % UUID_MAX)
91ce38
+    assert(len(r8) == 0)
91ce38
+    # 8 - export db
91ce38
+    task = be.export_ldif()
91ce38
+    task.wait()
91ce38
+    assert(task.is_complete() and task.get_exit_code() == 0)
91ce38
+
91ce38
+
91ce38
+@pytest.mark.skipif(not default_paths.rust_enabled or ds_is_older('1.4.2.0'), reason="Entryuuid is not available in older versions")
91ce38
+def test_entryuuid_indexed_import_and_search(topology):
91ce38
+    """ Test that an ldif of entries containing entryUUID's can be indexed and searched
91ce38
+    correctly. As https://tools.ietf.org/html/rfc4530 states, the MR's are equality and
91ce38
+    ordering, so we check these are correct.
91ce38
+
91ce38
+    :id: c98ee6dc-a7ee-4bd4-974d-597ea966dad9
91ce38
+
91ce38
+    :setup: Standalone instance
91ce38
+
91ce38
+    :steps:
91ce38
+        1. Import the db from the ldif
91ce38
+        2. EQ search for an entryuuid (match)
91ce38
+        3. EQ search for an entryuuid that does not exist
91ce38
+        4. LE search for an entryuuid lower (1 res)
91ce38
+        5. GE search for an entryuuid greater (1 res)
91ce38
+        6. LE for the 0 uuid (0 res)
91ce38
+        7. GE for the f uuid (0 res)
91ce38
+        8. export the db to ldif
91ce38
+
91ce38
+    :expectedresults:
91ce38
+        1. Success
91ce38
+        2. 1 match
91ce38
+        3. 0 match
91ce38
+        4. 1 match
91ce38
+        5. 1 match
91ce38
+        6. 0 match
91ce38
+        7. 0 match
91ce38
+        8. success
91ce38
+    """
91ce38
+    # Assert that the index correctly exists.
91ce38
+    be = Backends(topology.standalone).get('userRoot')
91ce38
+    indexes = be.get_indexes()
91ce38
+    indexes.ensure_state(properties={
91ce38
+        'cn': 'entryUUID',
91ce38
+        'nsSystemIndex': 'false',
91ce38
+        'nsIndexType': ['eq', 'pres'],
91ce38
+    })
91ce38
+    _entryuuid_import_and_search(topology)
91ce38
+
91ce38
+@pytest.mark.skipif(not default_paths.rust_enabled or ds_is_older('1.4.2.0'), reason="Entryuuid is not available in older versions")
91ce38
+def test_entryuuid_unindexed_import_and_search(topology):
91ce38
+    """ Test that an ldif of entries containing entryUUID's can be UNindexed searched
91ce38
+    correctly. As https://tools.ietf.org/html/rfc4530 states, the MR's are equality and
91ce38
+    ordering, so we check these are correct.
91ce38
+
91ce38
+    :id: b652b54d-f009-464b-b5bd-299a33f97243
91ce38
+
91ce38
+    :setup: Standalone instance
91ce38
+
91ce38
+    :steps:
91ce38
+        1. Import the db from the ldif
91ce38
+        2. EQ search for an entryuuid (match)
91ce38
+        3. EQ search for an entryuuid that does not exist
91ce38
+        4. LE search for an entryuuid lower (1 res)
91ce38
+        5. GE search for an entryuuid greater (1 res)
91ce38
+        6. LE for the 0 uuid (0 res)
91ce38
+        7. GE for the f uuid (0 res)
91ce38
+        8. export the db to ldif
91ce38
+
91ce38
+    :expectedresults:
91ce38
+        1. Success
91ce38
+        2. 1 match
91ce38
+        3. 0 match
91ce38
+        4. 1 match
91ce38
+        5. 1 match
91ce38
+        6. 0 match
91ce38
+        7. 0 match
91ce38
+        8. success
91ce38
+    """
91ce38
+    # Assert that the index does NOT exist for this test.
91ce38
+    be = Backends(topology.standalone).get('userRoot')
91ce38
+    indexes = be.get_indexes()
91ce38
+    try:
91ce38
+        idx = indexes.get('entryUUID')
91ce38
+        idx.delete()
91ce38
+    except ldap.NO_SUCH_OBJECT:
91ce38
+        # It's already not present, move along, nothing to see here.
91ce38
+        pass
91ce38
+    _entryuuid_import_and_search(topology)
91ce38
+
91ce38
+# Test entryUUID generation
91ce38
+@pytest.mark.skipif(not default_paths.rust_enabled or ds_is_older('1.4.2.0'), reason="Entryuuid is not available in older versions")
91ce38
+def test_entryuuid_generation_on_add(topology):
91ce38
+    """ Test that when an entry is added, the entryuuid is added.
91ce38
+
91ce38
+    :id: a7439b0a-dcee-4cd6-b8ef-771476c0b4f6
91ce38
+
91ce38
+    :setup: Standalone instance
91ce38
+
91ce38
+    :steps:
91ce38
+        1. Create a new entry in the db
91ce38
+        2. Check it has an entry uuid
91ce38
+
91ce38
+    :expectedresults:
91ce38
+        1. Success
91ce38
+        2. An entry uuid is present
91ce38
+    """
91ce38
+    # Step one - create a user!
91ce38
+    account = nsUserAccounts(topology.standalone, DEFAULT_SUFFIX).create_test_user()
91ce38
+    # Step two - does it have an entryuuid?
91ce38
+    euuid = account.get_attr_val_utf8('entryUUID')
91ce38
+    print(euuid)
91ce38
+    assert(euuid is not None)
91ce38
+
91ce38
+# Test fixup task
91ce38
+@pytest.mark.skipif(not default_paths.rust_enabled or ds_is_older('1.4.2.0'), reason="Entryuuid is not available in older versions")
91ce38
+def test_entryuuid_fixup_task(topology):
91ce38
+    """Test that when an entries without UUID's can have one generated via
91ce38
+    the fixup process.
91ce38
+
91ce38
+    :id: ad42bba2-ffb2-4c22-a37d-cbe7bcf73d6b
91ce38
+
91ce38
+    :setup: Standalone instance
91ce38
+
91ce38
+    :steps:
91ce38
+        1. Disable the entryuuid plugin
91ce38
+        2. Create an entry
91ce38
+        3. Enable the entryuuid plugin
91ce38
+        4. Run the fixup
91ce38
+        5. Assert the entryuuid now exists
91ce38
+
91ce38
+    :expectedresults:
91ce38
+        1. Success
91ce38
+        2. Success
91ce38
+        3. Success
91ce38
+        4. Success
91ce38
+        5. Suddenly EntryUUID!
91ce38
+    """
91ce38
+    # 1. Disable the plugin
91ce38
+    plug = EntryUUIDPlugin(topology.standalone)
91ce38
+    plug.disable()
91ce38
+    topology.standalone.restart()
91ce38
+
91ce38
+    # 2. create the account
91ce38
+    account = nsUserAccounts(topology.standalone, DEFAULT_SUFFIX).create_test_user(uid=2000)
91ce38
+    euuid = account.get_attr_val_utf8('entryUUID')
91ce38
+    assert(euuid is None)
91ce38
+
91ce38
+    # 3. enable the plugin
91ce38
+    plug.enable()
91ce38
+    topology.standalone.restart()
91ce38
+
91ce38
+    # 4. run the fix up
91ce38
+    # For now set the log level to high!
91ce38
+    topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.TRACE))
91ce38
+    task = plug.fixup(DEFAULT_SUFFIX)
91ce38
+    task.wait()
91ce38
+    assert(task.is_complete() and task.get_exit_code() == 0)
91ce38
+    topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,))
91ce38
+
91ce38
+    # 5. Assert the uuid.
91ce38
+    euuid = account.get_attr_val_utf8('entryUUID')
91ce38
+    assert(euuid is not None)
91ce38
+
91ce38
diff --git a/ldap/schema/02common.ldif b/ldap/schema/02common.ldif
91ce38
index 57e6be3b3..3b0ad0a97 100644
91ce38
--- a/ldap/schema/02common.ldif
91ce38
+++ b/ldap/schema/02common.ldif
91ce38
@@ -11,6 +11,7 @@
91ce38
 #
91ce38
 # Core schema, highly recommended but not required to start the Directory Server itself.
91ce38
 #
91ce38
+#
91ce38
 dn: cn=schema
91ce38
 #
91ce38
 # attributes
91ce38
diff --git a/ldap/schema/03entryuuid.ldif b/ldap/schema/03entryuuid.ldif
91ce38
new file mode 100644
91ce38
index 000000000..cbde981fe
91ce38
--- /dev/null
91ce38
+++ b/ldap/schema/03entryuuid.ldif
91ce38
@@ -0,0 +1,16 @@
91ce38
+#
91ce38
+# BEGIN COPYRIGHT BLOCK
91ce38
+# Copyright (C) 2020 William Brown <william@blackhats.net.au>
91ce38
+# All rights reserved.
91ce38
+#
91ce38
+# License: GPL (version 3 or any later version).
91ce38
+# See LICENSE for details.
91ce38
+# END COPYRIGHT BLOCK
91ce38
+#
91ce38
+# Core schema, highly recommended but not required to start the Directory Server itself.
91ce38
+#
91ce38
+dn: cn=schema
91ce38
+#
91ce38
+# attributes
91ce38
+#
91ce38
+attributeTypes: ( 1.3.6.1.1.16.4 NAME 'entryUUID' DESC 'UUID of the entry' EQUALITY UUIDMatch ORDERING UUIDOrderingMatch SYNTAX 1.3.6.1.1.16.1 SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )
91ce38
diff --git a/ldap/servers/slapd/config.c b/ldap/servers/slapd/config.c
91ce38
index 7e1618e79..bf5476272 100644
91ce38
--- a/ldap/servers/slapd/config.c
91ce38
+++ b/ldap/servers/slapd/config.c
91ce38
@@ -35,6 +35,10 @@ extern char *slapd_SSL3ciphers;
91ce38
 extern char *localuser;
91ce38
 char *rel2abspath(char *);
91ce38
 
91ce38
+/*
91ce38
+ * WARNING - this can only bootstrap PASSWORD and SYNTAX plugins!
91ce38
+ * see fedse.c instead!
91ce38
+ */
91ce38
 static char *bootstrap_plugins[] = {
91ce38
     "dn: cn=PBKDF2_SHA256,cn=Password Storage Schemes,cn=plugins,cn=config\n"
91ce38
     "objectclass: top\n"
91ce38
@@ -45,6 +49,19 @@ static char *bootstrap_plugins[] = {
91ce38
     "nsslapd-plugintype: pwdstoragescheme\n"
91ce38
     "nsslapd-pluginenabled: on",
91ce38
 
91ce38
+    "dn: cn=entryuuid_syntax,cn=plugins,cn=config\n"
91ce38
+    "objectclass: top\n"
91ce38
+    "objectclass: nsSlapdPlugin\n"
91ce38
+    "cn: entryuuid_syntax\n"
91ce38
+    "nsslapd-pluginpath: libentryuuid-syntax-plugin\n"
91ce38
+    "nsslapd-plugininitfunc: entryuuid_syntax_plugin_init\n"
91ce38
+    "nsslapd-plugintype: syntax\n"
91ce38
+    "nsslapd-pluginenabled: on\n"
91ce38
+    "nsslapd-pluginId: entryuuid_syntax\n"
91ce38
+    "nsslapd-pluginVersion: none\n"
91ce38
+    "nsslapd-pluginVendor: 389 Project\n"
91ce38
+    "nsslapd-pluginDescription: entryuuid_syntax\n",
91ce38
+
91ce38
     NULL
91ce38
 };
91ce38
 
91ce38
diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c
91ce38
index 7697e2b88..9ae9523e2 100644
91ce38
--- a/ldap/servers/slapd/entry.c
91ce38
+++ b/ldap/servers/slapd/entry.c
91ce38
@@ -2882,6 +2882,18 @@ slapi_entry_attr_get_bool(const Slapi_Entry *e, const char *type)
91ce38
     return slapi_entry_attr_get_bool_ext(e, type, PR_FALSE);
91ce38
 }
91ce38
 
91ce38
+const struct slapi_value **
91ce38
+slapi_entry_attr_get_valuearray(const Slapi_Entry *e, const char *attrname)
91ce38
+{
91ce38
+    Slapi_Attr *attr;
91ce38
+
91ce38
+    if (slapi_entry_attr_find(e, attrname, &attr) != 0) {
91ce38
+        return NULL;
91ce38
+    }
91ce38
+
91ce38
+    return attr->a_present_values.va;
91ce38
+}
91ce38
+
91ce38
 /*
91ce38
  * Extract a single value from an entry (as a string). You do not need
91ce38
  * to free the returned string value.
91ce38
diff --git a/ldap/servers/slapd/fedse.c b/ldap/servers/slapd/fedse.c
91ce38
index 3b076eb17..0d645f909 100644
91ce38
--- a/ldap/servers/slapd/fedse.c
91ce38
+++ b/ldap/servers/slapd/fedse.c
91ce38
@@ -119,6 +119,34 @@ static const char *internal_entries[] =
91ce38
         "cn:SNMP\n"
91ce38
         "nsSNMPEnabled: on\n",
91ce38
 
91ce38
+#ifdef RUST_ENABLE
91ce38
+        "dn: cn=entryuuid_syntax,cn=plugins,cn=config\n"
91ce38
+        "objectclass: top\n"
91ce38
+        "objectclass: nsSlapdPlugin\n"
91ce38
+        "cn: entryuuid_syntax\n"
91ce38
+        "nsslapd-pluginpath: libentryuuid-syntax-plugin\n"
91ce38
+        "nsslapd-plugininitfunc: entryuuid_syntax_plugin_init\n"
91ce38
+        "nsslapd-plugintype: syntax\n"
91ce38
+        "nsslapd-pluginenabled: on\n"
91ce38
+        "nsslapd-pluginId: entryuuid_syntax\n"
91ce38
+        "nsslapd-pluginVersion: none\n"
91ce38
+        "nsslapd-pluginVendor: 389 Project\n"
91ce38
+        "nsslapd-pluginDescription: entryuuid_syntax\n",
91ce38
+
91ce38
+        "dn: cn=entryuuid,cn=plugins,cn=config\n"
91ce38
+        "objectclass: top\n"
91ce38
+        "objectclass: nsSlapdPlugin\n"
91ce38
+        "cn: entryuuid\n"
91ce38
+        "nsslapd-pluginpath: libentryuuid-plugin\n"
91ce38
+        "nsslapd-plugininitfunc: entryuuid_plugin_init\n"
91ce38
+        "nsslapd-plugintype: betxnpreoperation\n"
91ce38
+        "nsslapd-pluginenabled: on\n"
91ce38
+        "nsslapd-pluginId: entryuuid\n"
91ce38
+        "nsslapd-pluginVersion: none\n"
91ce38
+        "nsslapd-pluginVendor: 389 Project\n"
91ce38
+        "nsslapd-pluginDescription: entryuuid\n",
91ce38
+#endif
91ce38
+
91ce38
         "dn: cn=Password Storage Schemes,cn=plugins,cn=config\n"
91ce38
         "objectclass: top\n"
91ce38
         "objectclass: nsContainer\n"
91ce38
diff --git a/src/Cargo.lock b/src/Cargo.lock
91ce38
index ce3c7ed27..33d7b8f23 100644
91ce38
--- a/src/Cargo.lock
91ce38
+++ b/src/Cargo.lock
91ce38
@@ -28,12 +28,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "base64"
91ce38
-version = "0.10.1"
91ce38
+version = "0.13.0"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
91ce38
-dependencies = [
91ce38
- "byteorder",
91ce38
-]
91ce38
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "bitflags"
91ce38
@@ -43,9 +40,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "byteorder"
91ce38
-version = "1.4.2"
91ce38
+version = "1.4.3"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
91ce38
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "cbindgen"
91ce38
@@ -66,15 +63,12 @@ dependencies = [
91ce38
 
91ce38
 [[package]]
91ce38
 name = "cc"
91ce38
-version = "1.0.66"
91ce38
+version = "1.0.67"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
91ce38
-
91ce38
-[[package]]
91ce38
-name = "cfg-if"
91ce38
-version = "0.1.10"
91ce38
-source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
91ce38
+checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
91ce38
+dependencies = [
91ce38
+ "jobserver",
91ce38
+]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "cfg-if"
91ce38
@@ -97,16 +91,39 @@ dependencies = [
91ce38
  "vec_map",
91ce38
 ]
91ce38
 
91ce38
+[[package]]
91ce38
+name = "entryuuid"
91ce38
+version = "0.1.0"
91ce38
+dependencies = [
91ce38
+ "cc",
91ce38
+ "libc",
91ce38
+ "paste",
91ce38
+ "slapi_r_plugin",
91ce38
+ "uuid",
91ce38
+]
91ce38
+
91ce38
+[[package]]
91ce38
+name = "entryuuid_syntax"
91ce38
+version = "0.1.0"
91ce38
+dependencies = [
91ce38
+ "cc",
91ce38
+ "libc",
91ce38
+ "paste",
91ce38
+ "slapi_r_plugin",
91ce38
+ "uuid",
91ce38
+]
91ce38
+
91ce38
 [[package]]
91ce38
 name = "fernet"
91ce38
-version = "0.1.3"
91ce38
+version = "0.1.4"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "e7ac567fd75ce6bc28b68e63b5beaa3ce34f56bafd1122f64f8647c822e38a8b"
91ce38
+checksum = "93804560e638370a8be6d59ce71ed803e55e230abdbf42598e666b41adda9b1f"
91ce38
 dependencies = [
91ce38
  "base64",
91ce38
  "byteorder",
91ce38
  "getrandom",
91ce38
  "openssl",
91ce38
+ "zeroize",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
@@ -126,20 +143,20 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "getrandom"
91ce38
-version = "0.1.16"
91ce38
+version = "0.2.3"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
91ce38
+checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
91ce38
 dependencies = [
91ce38
- "cfg-if 1.0.0",
91ce38
+ "cfg-if",
91ce38
  "libc",
91ce38
  "wasi",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "hermit-abi"
91ce38
-version = "0.1.17"
91ce38
+version = "0.1.18"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
91ce38
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
91ce38
 dependencies = [
91ce38
  "libc",
91ce38
 ]
91ce38
@@ -150,6 +167,15 @@ version = "0.4.7"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
 checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
91ce38
 
91ce38
+[[package]]
91ce38
+name = "jobserver"
91ce38
+version = "0.1.22"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
91ce38
+dependencies = [
91ce38
+ "libc",
91ce38
+]
91ce38
+
91ce38
 [[package]]
91ce38
 name = "lazy_static"
91ce38
 version = "1.4.0"
91ce38
@@ -158,9 +184,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "libc"
91ce38
-version = "0.2.82"
91ce38
+version = "0.2.94"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
91ce38
+checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "librnsslapd"
91ce38
@@ -182,32 +208,38 @@ dependencies = [
91ce38
 
91ce38
 [[package]]
91ce38
 name = "log"
91ce38
-version = "0.4.11"
91ce38
+version = "0.4.14"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
91ce38
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
91ce38
 dependencies = [
91ce38
- "cfg-if 0.1.10",
91ce38
+ "cfg-if",
91ce38
 ]
91ce38
 
91ce38
+[[package]]
91ce38
+name = "once_cell"
91ce38
+version = "1.7.2"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
91ce38
+
91ce38
 [[package]]
91ce38
 name = "openssl"
91ce38
-version = "0.10.32"
91ce38
+version = "0.10.34"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70"
91ce38
+checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8"
91ce38
 dependencies = [
91ce38
  "bitflags",
91ce38
- "cfg-if 1.0.0",
91ce38
+ "cfg-if",
91ce38
  "foreign-types",
91ce38
- "lazy_static",
91ce38
  "libc",
91ce38
+ "once_cell",
91ce38
  "openssl-sys",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "openssl-sys"
91ce38
-version = "0.9.60"
91ce38
+version = "0.9.63"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
91ce38
+checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98"
91ce38
 dependencies = [
91ce38
  "autocfg",
91ce38
  "cc",
91ce38
@@ -216,6 +248,25 @@ dependencies = [
91ce38
  "vcpkg",
91ce38
 ]
91ce38
 
91ce38
+[[package]]
91ce38
+name = "paste"
91ce38
+version = "0.1.18"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
91ce38
+dependencies = [
91ce38
+ "paste-impl",
91ce38
+ "proc-macro-hack",
91ce38
+]
91ce38
+
91ce38
+[[package]]
91ce38
+name = "paste-impl"
91ce38
+version = "0.1.18"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
91ce38
+dependencies = [
91ce38
+ "proc-macro-hack",
91ce38
+]
91ce38
+
91ce38
 [[package]]
91ce38
 name = "pkg-config"
91ce38
 version = "0.3.19"
91ce38
@@ -228,31 +279,36 @@ version = "0.2.10"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
 checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
91ce38
 
91ce38
+[[package]]
91ce38
+name = "proc-macro-hack"
91ce38
+version = "0.5.19"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
91ce38
+
91ce38
 [[package]]
91ce38
 name = "proc-macro2"
91ce38
-version = "1.0.24"
91ce38
+version = "1.0.27"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
91ce38
+checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
91ce38
 dependencies = [
91ce38
  "unicode-xid",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "quote"
91ce38
-version = "1.0.8"
91ce38
+version = "1.0.9"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
91ce38
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
91ce38
 dependencies = [
91ce38
  "proc-macro2",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "rand"
91ce38
-version = "0.7.3"
91ce38
+version = "0.8.3"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
91ce38
+checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
91ce38
 dependencies = [
91ce38
- "getrandom",
91ce38
  "libc",
91ce38
  "rand_chacha",
91ce38
  "rand_core",
91ce38
@@ -261,9 +317,9 @@ dependencies = [
91ce38
 
91ce38
 [[package]]
91ce38
 name = "rand_chacha"
91ce38
-version = "0.2.2"
91ce38
+version = "0.3.0"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
91ce38
+checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
91ce38
 dependencies = [
91ce38
  "ppv-lite86",
91ce38
  "rand_core",
91ce38
@@ -271,27 +327,30 @@ dependencies = [
91ce38
 
91ce38
 [[package]]
91ce38
 name = "rand_core"
91ce38
-version = "0.5.1"
91ce38
+version = "0.6.2"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
91ce38
+checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
91ce38
 dependencies = [
91ce38
  "getrandom",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "rand_hc"
91ce38
-version = "0.2.0"
91ce38
+version = "0.3.0"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
91ce38
+checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
91ce38
 dependencies = [
91ce38
  "rand_core",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "redox_syscall"
91ce38
-version = "0.1.57"
91ce38
+version = "0.2.8"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
91ce38
+checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
91ce38
+dependencies = [
91ce38
+ "bitflags",
91ce38
+]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "remove_dir_all"
91ce38
@@ -314,18 +373,18 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "serde"
91ce38
-version = "1.0.118"
91ce38
+version = "1.0.126"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
91ce38
+checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
91ce38
 dependencies = [
91ce38
  "serde_derive",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "serde_derive"
91ce38
-version = "1.0.118"
91ce38
+version = "1.0.126"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
91ce38
+checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
91ce38
 dependencies = [
91ce38
  "proc-macro2",
91ce38
  "quote",
91ce38
@@ -334,9 +393,9 @@ dependencies = [
91ce38
 
91ce38
 [[package]]
91ce38
 name = "serde_json"
91ce38
-version = "1.0.61"
91ce38
+version = "1.0.64"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
91ce38
+checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
91ce38
 dependencies = [
91ce38
  "itoa",
91ce38
  "ryu",
91ce38
@@ -350,6 +409,16 @@ dependencies = [
91ce38
  "fernet",
91ce38
 ]
91ce38
 
91ce38
+[[package]]
91ce38
+name = "slapi_r_plugin"
91ce38
+version = "0.1.0"
91ce38
+dependencies = [
91ce38
+ "lazy_static",
91ce38
+ "libc",
91ce38
+ "paste",
91ce38
+ "uuid",
91ce38
+]
91ce38
+
91ce38
 [[package]]
91ce38
 name = "strsim"
91ce38
 version = "0.8.0"
91ce38
@@ -358,22 +427,34 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "syn"
91ce38
-version = "1.0.58"
91ce38
+version = "1.0.72"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
91ce38
+dependencies = [
91ce38
+ "proc-macro2",
91ce38
+ "quote",
91ce38
+ "unicode-xid",
91ce38
+]
91ce38
+
91ce38
+[[package]]
91ce38
+name = "synstructure"
91ce38
+version = "0.12.4"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
91ce38
+checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
91ce38
 dependencies = [
91ce38
  "proc-macro2",
91ce38
  "quote",
91ce38
+ "syn",
91ce38
  "unicode-xid",
91ce38
 ]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "tempfile"
91ce38
-version = "3.1.0"
91ce38
+version = "3.2.0"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
91ce38
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
91ce38
 dependencies = [
91ce38
- "cfg-if 0.1.10",
91ce38
+ "cfg-if",
91ce38
  "libc",
91ce38
  "rand",
91ce38
  "redox_syscall",
91ce38
@@ -407,15 +488,24 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "unicode-xid"
91ce38
-version = "0.2.1"
91ce38
+version = "0.2.2"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
91ce38
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
91ce38
+
91ce38
+[[package]]
91ce38
+name = "uuid"
91ce38
+version = "0.8.2"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
91ce38
+dependencies = [
91ce38
+ "getrandom",
91ce38
+]
91ce38
 
91ce38
 [[package]]
91ce38
 name = "vcpkg"
91ce38
-version = "0.2.11"
91ce38
+version = "0.2.12"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
91ce38
+checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "vec_map"
91ce38
@@ -425,9 +515,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "wasi"
91ce38
-version = "0.9.0+wasi-snapshot-preview1"
91ce38
+version = "0.10.2+wasi-snapshot-preview1"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
-checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
91ce38
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
91ce38
 
91ce38
 [[package]]
91ce38
 name = "winapi"
91ce38
@@ -450,3 +540,24 @@ name = "winapi-x86_64-pc-windows-gnu"
91ce38
 version = "0.4.0"
91ce38
 source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
91ce38
+
91ce38
+[[package]]
91ce38
+name = "zeroize"
91ce38
+version = "1.3.0"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
91ce38
+dependencies = [
91ce38
+ "zeroize_derive",
91ce38
+]
91ce38
+
91ce38
+[[package]]
91ce38
+name = "zeroize_derive"
91ce38
+version = "1.1.0"
91ce38
+source = "registry+https://github.com/rust-lang/crates.io-index"
91ce38
+checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1"
91ce38
+dependencies = [
91ce38
+ "proc-macro2",
91ce38
+ "quote",
91ce38
+ "syn",
91ce38
+ "synstructure",
91ce38
+]
91ce38
diff --git a/src/Cargo.toml b/src/Cargo.toml
91ce38
index f6dac010f..1ad2b21b0 100644
91ce38
--- a/src/Cargo.toml
91ce38
+++ b/src/Cargo.toml
91ce38
@@ -1,10 +1,13 @@
91ce38
 
91ce38
 [workspace]
91ce38
 members = [
91ce38
-	"librslapd",
91ce38
-	"librnsslapd",
91ce38
-	"libsds",
91ce38
-	"slapd",
91ce38
+    "librslapd",
91ce38
+    "librnsslapd",
91ce38
+    "libsds",
91ce38
+    "slapd",
91ce38
+    "slapi_r_plugin",
91ce38
+    "plugins/entryuuid",
91ce38
+    "plugins/entryuuid_syntax",
91ce38
 ]
91ce38
 
91ce38
 [profile.release]
91ce38
diff --git a/src/README.md b/src/README.md
91ce38
new file mode 100644
91ce38
index 000000000..e69de29bb
91ce38
diff --git a/src/lib389/lib389/_constants.py b/src/lib389/lib389/_constants.py
91ce38
index 52aac0f21..c184c8d4f 100644
91ce38
--- a/src/lib389/lib389/_constants.py
91ce38
+++ b/src/lib389/lib389/_constants.py
91ce38
@@ -150,6 +150,7 @@ DN_IMPORT_TASK = "cn=import,%s" % DN_TASKS
91ce38
 DN_BACKUP_TASK = "cn=backup,%s" % DN_TASKS
91ce38
 DN_RESTORE_TASK = "cn=restore,%s" % DN_TASKS
91ce38
 DN_MBO_TASK = "cn=memberOf task,%s" % DN_TASKS
91ce38
+DN_EUUID_TASK = "cn=entryuuid task,%s" % DN_TASKS
91ce38
 DN_TOMB_FIXUP_TASK = "cn=fixup tombstones,%s" % DN_TASKS
91ce38
 DN_FIXUP_LINKED_ATTIBUTES = "cn=fixup linked attributes,%s" % DN_TASKS
91ce38
 DN_AUTOMEMBER_REBUILD_TASK = "cn=automember rebuild membership,%s" % DN_TASKS
91ce38
diff --git a/src/lib389/lib389/backend.py b/src/lib389/lib389/backend.py
91ce38
index aab07c028..bcd7b383f 100644
91ce38
--- a/src/lib389/lib389/backend.py
91ce38
+++ b/src/lib389/lib389/backend.py
91ce38
@@ -765,7 +765,7 @@ class Backend(DSLdapObject):
91ce38
                 enc_attr.delete()
91ce38
                 break
91ce38
 
91ce38
-    def import_ldif(self, ldifs, chunk_size=None, encrypted=False, gen_uniq_id=False, only_core=False,
91ce38
+    def import_ldif(self, ldifs, chunk_size=None, encrypted=False, gen_uniq_id=None, only_core=False,
91ce38
                     include_suffixes=None, exclude_suffixes=None):
91ce38
         """Do an import of the suffix"""
91ce38
 
91ce38
diff --git a/src/lib389/lib389/instance/setup.py b/src/lib389/lib389/instance/setup.py
91ce38
index 530fb367a..ac0fe1a8c 100644
91ce38
--- a/src/lib389/lib389/instance/setup.py
91ce38
+++ b/src/lib389/lib389/instance/setup.py
91ce38
@@ -34,6 +34,7 @@ from lib389.instance.options import General2Base, Slapd2Base, Backend2Base
91ce38
 from lib389.paths import Paths
91ce38
 from lib389.saslmap import SaslMappings
91ce38
 from lib389.instance.remove import remove_ds_instance
91ce38
+from lib389.index import Indexes
91ce38
 from lib389.utils import (
91ce38
     assert_c,
91ce38
     is_a_dn,
91ce38
@@ -928,6 +929,19 @@ class SetupDs(object):
91ce38
         if slapd['self_sign_cert']:
91ce38
             ds_instance.config.set('nsslapd-security', 'on')
91ce38
 
91ce38
+        # Before we create any backends, create any extra default indexes that may be
91ce38
+        # dynamicly provisioned, rather than from template-dse.ldif. Looking at you
91ce38
+        # entryUUID (requires rust enabled).
91ce38
+        #
91ce38
+        # Indexes defaults to default_index_dn
91ce38
+        indexes = Indexes(ds_instance)
91ce38
+        if ds_instance.ds_paths.rust_enabled:
91ce38
+            indexes.create(properties={
91ce38
+                'cn': 'entryUUID',
91ce38
+                'nsSystemIndex': 'false',
91ce38
+                'nsIndexType': ['eq', 'pres'],
91ce38
+            })
91ce38
+
91ce38
         # Create the backends as listed
91ce38
         # Load example data if needed.
91ce38
         for backend in backends:
91ce38
diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py
91ce38
index 16899f6d3..2d88e60bd 100644
91ce38
--- a/src/lib389/lib389/plugins.py
91ce38
+++ b/src/lib389/lib389/plugins.py
91ce38
@@ -2244,3 +2244,33 @@ class ContentSyncPlugin(Plugin):
91ce38
 
91ce38
     def __init__(self, instance, dn="cn=Content Synchronization,cn=plugins,cn=config"):
91ce38
         super(ContentSyncPlugin, self).__init__(instance, dn)
91ce38
+
91ce38
+
91ce38
+class EntryUUIDPlugin(Plugin):
91ce38
+    """The EntryUUID plugin configuration
91ce38
+    :param instance: An instance
91ce38
+    :type instance: lib389.DirSrv
91ce38
+    :param dn: Entry DN
91ce38
+    :type dn: str
91ce38
+    """
91ce38
+    def __init__(self, instance, dn="cn=entryuuid,cn=plugins,cn=config"):
91ce38
+        super(EntryUUIDPlugin, self).__init__(instance, dn)
91ce38
+
91ce38
+    def fixup(self, basedn, _filter=None):
91ce38
+        """Create an entryuuid fixup task
91ce38
+
91ce38
+        :param basedn: Basedn to fix up
91ce38
+        :type basedn: str
91ce38
+        :param _filter: a filter for entries to fix up
91ce38
+        :type _filter: str
91ce38
+
91ce38
+        :returns: an instance of Task(DSLdapObject)
91ce38
+        """
91ce38
+
91ce38
+        task = tasks.EntryUUIDFixupTask(self._instance)
91ce38
+        task_properties = {'basedn': basedn}
91ce38
+        if _filter is not None:
91ce38
+            task_properties['filter'] = _filter
91ce38
+        task.create(properties=task_properties)
91ce38
+
91ce38
+        return task
91ce38
diff --git a/src/lib389/lib389/tasks.py b/src/lib389/lib389/tasks.py
91ce38
index b19e7918d..590c6ee79 100644
91ce38
--- a/src/lib389/lib389/tasks.py
91ce38
+++ b/src/lib389/lib389/tasks.py
91ce38
@@ -203,6 +203,20 @@ class USNTombstoneCleanupTask(Task):
91ce38
         return super(USNTombstoneCleanupTask, self)._validate(rdn, properties, basedn)
91ce38
 
91ce38
 
91ce38
+class EntryUUIDFixupTask(Task):
91ce38
+    """A single instance of memberOf task entry
91ce38
+
91ce38
+    :param instance: An instance
91ce38
+    :type instance: lib389.DirSrv
91ce38
+    """
91ce38
+
91ce38
+    def __init__(self, instance, dn=None):
91ce38
+        self.cn = 'entryuuid_fixup_' + Task._get_task_date()
91ce38
+        dn = "cn=" + self.cn + "," + DN_EUUID_TASK
91ce38
+        super(EntryUUIDFixupTask, self).__init__(instance, dn)
91ce38
+        self._must_attributes.extend(['basedn'])
91ce38
+
91ce38
+
91ce38
 class SchemaReloadTask(Task):
91ce38
     """A single instance of schema reload task entry
91ce38
 
91ce38
diff --git a/src/librnsslapd/build.rs b/src/librnsslapd/build.rs
91ce38
index 9b953b246..13f6d2e03 100644
91ce38
--- a/src/librnsslapd/build.rs
91ce38
+++ b/src/librnsslapd/build.rs
91ce38
@@ -3,13 +3,14 @@ extern crate cbindgen;
91ce38
 use std::env;
91ce38
 
91ce38
 fn main() {
91ce38
-    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
91ce38
-    let out_dir = env::var("SLAPD_HEADER_DIR").unwrap();
91ce38
-
91ce38
-    cbindgen::Builder::new()
91ce38
-        .with_language(cbindgen::Language::C)
91ce38
-        .with_crate(crate_dir)
91ce38
-        .generate()
91ce38
-        .expect("Unable to generate bindings")
91ce38
-        .write_to_file(format!("{}/rust-nsslapd-private.h", out_dir));
91ce38
+    if let Ok(crate_dir) = env::var("CARGO_MANIFEST_DIR") {
91ce38
+        if let Ok(out_dir) = env::var("SLAPD_HEADER_DIR") {
91ce38
+            cbindgen::Builder::new()
91ce38
+                .with_language(cbindgen::Language::C)
91ce38
+                .with_crate(crate_dir)
91ce38
+                .generate()
91ce38
+                .expect("Unable to generate bindings")
91ce38
+                .write_to_file(format!("{}/rust-nsslapd-private.h", out_dir));
91ce38
+        }
91ce38
+    }
91ce38
 }
91ce38
diff --git a/src/librnsslapd/src/lib.rs b/src/librnsslapd/src/lib.rs
91ce38
index c5fd2bbaf..dffe4ce1c 100644
91ce38
--- a/src/librnsslapd/src/lib.rs
91ce38
+++ b/src/librnsslapd/src/lib.rs
91ce38
@@ -4,9 +4,9 @@
91ce38
 // Remember this is just a c-bindgen stub, all logic should come from slapd!
91ce38
 
91ce38
 extern crate libc;
91ce38
-use slapd;
91ce38
 use libc::c_char;
91ce38
-use std::ffi::{CString, CStr};
91ce38
+use slapd;
91ce38
+use std::ffi::{CStr, CString};
91ce38
 
91ce38
 #[no_mangle]
91ce38
 pub extern "C" fn do_nothing_again_rust() -> usize {
91ce38
@@ -29,9 +29,7 @@ pub extern "C" fn fernet_generate_token(dn: *const c_char, raw_key: *const c_cha
91ce38
                     // We have to move string memory ownership by copying so the system
91ce38
                     // allocator has it.
91ce38
                     let raw = tok.into_raw();
91ce38
-                    let dup_tok = unsafe {
91ce38
-                        libc::strdup(raw)
91ce38
-                    };
91ce38
+                    let dup_tok = unsafe { libc::strdup(raw) };
91ce38
                     unsafe {
91ce38
                         CString::from_raw(raw);
91ce38
                     };
91ce38
@@ -45,7 +43,12 @@ pub extern "C" fn fernet_generate_token(dn: *const c_char, raw_key: *const c_cha
91ce38
 }
91ce38
 
91ce38
 #[no_mangle]
91ce38
-pub extern "C" fn fernet_verify_token(dn: *const c_char, token: *const c_char, raw_key: *const c_char, ttl: u64) -> bool {
91ce38
+pub extern "C" fn fernet_verify_token(
91ce38
+    dn: *const c_char,
91ce38
+    token: *const c_char,
91ce38
+    raw_key: *const c_char,
91ce38
+    ttl: u64,
91ce38
+) -> bool {
91ce38
     if dn.is_null() || raw_key.is_null() || token.is_null() {
91ce38
         return false;
91ce38
     }
91ce38
@@ -67,4 +70,3 @@ pub extern "C" fn fernet_verify_token(dn: *const c_char, token: *const c_char, r
91ce38
         Err(_) => false,
91ce38
     }
91ce38
 }
91ce38
-
91ce38
diff --git a/src/librslapd/Cargo.toml b/src/librslapd/Cargo.toml
91ce38
index 1dd715ed2..08309c224 100644
91ce38
--- a/src/librslapd/Cargo.toml
91ce38
+++ b/src/librslapd/Cargo.toml
91ce38
@@ -12,10 +12,6 @@ path = "src/lib.rs"
91ce38
 name = "rslapd"
91ce38
 crate-type = ["staticlib", "lib"]
91ce38
 
91ce38
-# [profile.release]
91ce38
-# panic = "abort"
91ce38
-# lto = true
91ce38
-
91ce38
 [dependencies]
91ce38
 slapd = { path = "../slapd" }
91ce38
 libc = "0.2"
91ce38
diff --git a/src/librslapd/build.rs b/src/librslapd/build.rs
91ce38
index 4d4c1ce42..84aff156b 100644
91ce38
--- a/src/librslapd/build.rs
91ce38
+++ b/src/librslapd/build.rs
91ce38
@@ -3,13 +3,14 @@ extern crate cbindgen;
91ce38
 use std::env;
91ce38
 
91ce38
 fn main() {
91ce38
-    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
91ce38
-    let out_dir = env::var("SLAPD_HEADER_DIR").unwrap();
91ce38
-
91ce38
-    cbindgen::Builder::new()
91ce38
-        .with_language(cbindgen::Language::C)
91ce38
-        .with_crate(crate_dir)
91ce38
-        .generate()
91ce38
-        .expect("Unable to generate bindings")
91ce38
-        .write_to_file(format!("{}/rust-slapi-private.h", out_dir));
91ce38
+    if let Ok(crate_dir) = env::var("CARGO_MANIFEST_DIR") {
91ce38
+        if let Ok(out_dir) = env::var("SLAPD_HEADER_DIR") {
91ce38
+            cbindgen::Builder::new()
91ce38
+                .with_language(cbindgen::Language::C)
91ce38
+                .with_crate(crate_dir)
91ce38
+                .generate()
91ce38
+                .expect("Unable to generate bindings")
91ce38
+                .write_to_file(format!("{}/rust-slapi-private.h", out_dir));
91ce38
+        }
91ce38
+    }
91ce38
 }
91ce38
diff --git a/src/librslapd/src/lib.rs b/src/librslapd/src/lib.rs
91ce38
index 9cce193a0..cf283a7ce 100644
91ce38
--- a/src/librslapd/src/lib.rs
91ce38
+++ b/src/librslapd/src/lib.rs
91ce38
@@ -8,7 +8,7 @@ extern crate libc;
91ce38
 use slapd;
91ce38
 
91ce38
 use libc::c_char;
91ce38
-use std::ffi::{CString, CStr};
91ce38
+use std::ffi::{CStr, CString};
91ce38
 
91ce38
 #[no_mangle]
91ce38
 pub extern "C" fn do_nothing_rust() -> usize {
91ce38
@@ -18,9 +18,7 @@ pub extern "C" fn do_nothing_rust() -> usize {
91ce38
 #[no_mangle]
91ce38
 pub extern "C" fn rust_free_string(s: *mut c_char) {
91ce38
     if !s.is_null() {
91ce38
-        let _ = unsafe {
91ce38
-            CString::from_raw(s)
91ce38
-        };
91ce38
+        let _ = unsafe { CString::from_raw(s) };
91ce38
     }
91ce38
 }
91ce38
 
91ce38
@@ -35,9 +33,7 @@ pub extern "C" fn fernet_generate_new_key() -> *mut c_char {
91ce38
     match res_key {
91ce38
         Ok(key) => {
91ce38
             let raw = key.into_raw();
91ce38
-            let dup_key = unsafe {
91ce38
-                libc::strdup(raw)
91ce38
-            };
91ce38
+            let dup_key = unsafe { libc::strdup(raw) };
91ce38
             rust_free_string(raw);
91ce38
             dup_key
91ce38
         }
91ce38
@@ -53,4 +49,3 @@ pub extern "C" fn fernet_validate_key(raw_key: *const c_char) -> bool {
91ce38
         Err(_) => false,
91ce38
     }
91ce38
 }
91ce38
-
91ce38
diff --git a/src/libsds/sds/lib.rs b/src/libsds/sds/lib.rs
91ce38
index aa70c7a8e..9e2973222 100644
91ce38
--- a/src/libsds/sds/lib.rs
91ce38
+++ b/src/libsds/sds/lib.rs
91ce38
@@ -28,5 +28,3 @@ pub enum sds_result {
91ce38
     /// The list is exhausted, no more elements can be returned.
91ce38
     ListExhausted = 16,
91ce38
 }
91ce38
-
91ce38
-
91ce38
diff --git a/src/libsds/sds/tqueue.rs b/src/libsds/sds/tqueue.rs
91ce38
index b7042e514..ebe1f4b6c 100644
91ce38
--- a/src/libsds/sds/tqueue.rs
91ce38
+++ b/src/libsds/sds/tqueue.rs
91ce38
@@ -9,8 +9,8 @@
91ce38
 #![warn(missing_docs)]
91ce38
 
91ce38
 use super::sds_result;
91ce38
-use std::sync::Mutex;
91ce38
 use std::collections::LinkedList;
91ce38
+use std::sync::Mutex;
91ce38
 
91ce38
 // Borrow from libc
91ce38
 #[doc(hidden)]
91ce38
@@ -75,7 +75,10 @@ impl Drop for TQueue {
91ce38
 /// C compatible wrapper around the TQueue. Given a valid point, a TQueue pointer
91ce38
 /// is allocated on the heap and referenced in retq. free_fn_ptr may be NULL
91ce38
 /// but if it references a function, this will be called during drop of the TQueue.
91ce38
-pub extern fn sds_tqueue_init(retq: *mut *mut TQueue, free_fn_ptr: Option<extern "C" fn(*const c_void)>) -> sds_result {
91ce38
+pub extern "C" fn sds_tqueue_init(
91ce38
+    retq: *mut *mut TQueue,
91ce38
+    free_fn_ptr: Option<extern "C" fn(*const c_void)>,
91ce38
+) -> sds_result {
91ce38
     // This piece of type signature magic is because in rust types that extern C,
91ce38
     // with option has None resolve to null. What this causes is we can wrap
91ce38
     // our fn ptr with Option in rust, but the C side gives us fn ptr or NULL, and
91ce38
@@ -93,7 +96,7 @@ pub extern fn sds_tqueue_init(retq: *mut *mut TQueue, free_fn_ptr: Option
91ce38
 
91ce38
 #[no_mangle]
91ce38
 /// Push an element to the tail of the queue. The element may be NULL
91ce38
-pub extern fn sds_tqueue_enqueue(q: *const TQueue, elem: *const c_void) -> sds_result {
91ce38
+pub extern "C" fn sds_tqueue_enqueue(q: *const TQueue, elem: *const c_void) -> sds_result {
91ce38
     // Check for null ....
91ce38
     unsafe { (*q).enqueue(elem) };
91ce38
     sds_result::Success
91ce38
@@ -103,29 +106,27 @@ pub extern fn sds_tqueue_enqueue(q: *const TQueue, elem: *const c_void) -> sds_r
91ce38
 /// Dequeue from the head of the queue. The result will be placed into elem.
91ce38
 /// if elem is NULL no dequeue is attempted. If there are no more items
91ce38
 /// ListExhausted is returned.
91ce38
-pub extern fn sds_tqueue_dequeue(q: *const TQueue, elem: *mut *const c_void) -> sds_result {
91ce38
+pub extern "C" fn sds_tqueue_dequeue(q: *const TQueue, elem: *mut *const c_void) -> sds_result {
91ce38
     if elem.is_null() {
91ce38
         return sds_result::NullPointer;
91ce38
     }
91ce38
     match unsafe { (*q).dequeue() } {
91ce38
         Some(e) => {
91ce38
-            unsafe { *elem = e; };
91ce38
+            unsafe {
91ce38
+                *elem = e;
91ce38
+            };
91ce38
             sds_result::Success
91ce38
         }
91ce38
-        None => {
91ce38
-            sds_result::ListExhausted
91ce38
-        }
91ce38
+        None => sds_result::ListExhausted,
91ce38
     }
91ce38
 }
91ce38
 
91ce38
 #[no_mangle]
91ce38
 /// Free the queue and all remaining elements. After this point it is
91ce38
 /// not safe to access the queue.
91ce38
-pub extern fn sds_tqueue_destroy(q: *mut TQueue) -> sds_result {
91ce38
+pub extern "C" fn sds_tqueue_destroy(q: *mut TQueue) -> sds_result {
91ce38
     // This will drop the queue and free it's content
91ce38
     // mem::drop(q);
91ce38
     let _q = unsafe { Box::from_raw(q) };
91ce38
     sds_result::Success
91ce38
 }
91ce38
-
91ce38
-
91ce38
diff --git a/src/plugins/entryuuid/Cargo.toml b/src/plugins/entryuuid/Cargo.toml
91ce38
new file mode 100644
91ce38
index 000000000..c43d7a771
91ce38
--- /dev/null
91ce38
+++ b/src/plugins/entryuuid/Cargo.toml
91ce38
@@ -0,0 +1,21 @@
91ce38
+[package]
91ce38
+name = "entryuuid"
91ce38
+version = "0.1.0"
91ce38
+authors = ["William Brown <william@blackhats.net.au>"]
91ce38
+edition = "2018"
91ce38
+
91ce38
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
91ce38
+
91ce38
+[lib]
91ce38
+path = "src/lib.rs"
91ce38
+name = "entryuuid"
91ce38
+crate-type = ["staticlib", "lib"]
91ce38
+
91ce38
+[dependencies]
91ce38
+libc = "0.2"
91ce38
+paste = "0.1"
91ce38
+slapi_r_plugin = { path="../../slapi_r_plugin" }
91ce38
+uuid = { version = "0.8", features = [ "v4" ] }
91ce38
+
91ce38
+[build-dependencies]
91ce38
+cc = { version = "1.0", features = ["parallel"] }
91ce38
diff --git a/src/plugins/entryuuid/src/lib.rs b/src/plugins/entryuuid/src/lib.rs
91ce38
new file mode 100644
91ce38
index 000000000..6b5e8d1bb
91ce38
--- /dev/null
91ce38
+++ b/src/plugins/entryuuid/src/lib.rs
91ce38
@@ -0,0 +1,196 @@
91ce38
+#[macro_use]
91ce38
+extern crate slapi_r_plugin;
91ce38
+use slapi_r_plugin::prelude::*;
91ce38
+use std::convert::{TryFrom, TryInto};
91ce38
+use std::os::raw::c_char;
91ce38
+use uuid::Uuid;
91ce38
+
91ce38
+#[derive(Debug)]
91ce38
+struct FixupData {
91ce38
+    basedn: Sdn,
91ce38
+    raw_filter: String,
91ce38
+}
91ce38
+
91ce38
+struct EntryUuid;
91ce38
+/*
91ce38
+ *                    /---- plugin ident
91ce38
+ *                    |          /---- Struct name.
91ce38
+ *                    V          V
91ce38
+ */
91ce38
+slapi_r_plugin_hooks!(entryuuid, EntryUuid);
91ce38
+
91ce38
+/*
91ce38
+ *                             /---- plugin ident
91ce38
+ *                             |          /---- cb ident
91ce38
+ *                             |          |                   /---- map function
91ce38
+ *                             V          V                   V
91ce38
+ */
91ce38
+slapi_r_search_callback_mapfn!(entryuuid, entryuuid_fixup_cb, entryuuid_fixup_mapfn);
91ce38
+
91ce38
+fn assign_uuid(e: &mut EntryRef) {
91ce38
+    let sdn = e.get_sdnref();
91ce38
+
91ce38
+    // We could consider making these lazy static.
91ce38
+    let config_sdn = Sdn::try_from("cn=config").expect("Invalid static dn");
91ce38
+    let schema_sdn = Sdn::try_from("cn=schema").expect("Invalid static dn");
91ce38
+
91ce38
+    if sdn.is_below_suffix(&*config_sdn) || sdn.is_below_suffix(&*schema_sdn) {
91ce38
+        // We don't need to assign to these suffixes.
91ce38
+        log_error!(
91ce38
+            ErrorLevel::Trace,
91ce38
+            "assign_uuid -> not assigning to {:?} as part of system suffix",
91ce38
+            sdn.to_dn_string()
91ce38
+        );
91ce38
+        return;
91ce38
+    }
91ce38
+
91ce38
+    // Generate a new Uuid.
91ce38
+    let u: Uuid = Uuid::new_v4();
91ce38
+    log_error!(
91ce38
+        ErrorLevel::Trace,
91ce38
+        "assign_uuid -> assigning {:?} to dn {}",
91ce38
+        u,
91ce38
+        sdn.to_dn_string()
91ce38
+    );
91ce38
+
91ce38
+    let uuid_value = Value::from(&u);
91ce38
+
91ce38
+    // Add it to the entry
91ce38
+    e.add_value("entryUUID", &uuid_value);
91ce38
+}
91ce38
+
91ce38
+impl SlapiPlugin3 for EntryUuid {
91ce38
+    // Indicate we have pre add
91ce38
+    fn has_betxn_pre_add() -> bool {
91ce38
+        true
91ce38
+    }
91ce38
+
91ce38
+    fn betxn_pre_add(pb: &mut PblockRef) -> Result<(), PluginError> {
91ce38
+        log_error!(ErrorLevel::Trace, "betxn_pre_add");
91ce38
+
91ce38
+        let mut e = pb.get_op_add_entryref().map_err(|_| PluginError::Pblock)?;
91ce38
+        assign_uuid(&mut e);
91ce38
+
91ce38
+        Ok(())
91ce38
+    }
91ce38
+
91ce38
+    fn has_task_handler() -> Option<&'static str> {
91ce38
+        Some("entryuuid task")
91ce38
+    }
91ce38
+
91ce38
+    type TaskData = FixupData;
91ce38
+
91ce38
+    fn task_validate(e: &EntryRef) -> Result<Self::TaskData, LDAPError> {
91ce38
+        // Does the entry have what we need?
91ce38
+        let basedn: Sdn = match e.get_attr("basedn") {
91ce38
+            Some(values) => values
91ce38
+                .first()
91ce38
+                .ok_or_else(|| {
91ce38
+                    log_error!(
91ce38
+                        ErrorLevel::Trace,
91ce38
+                        "task_validate basedn error -> empty value array?"
91ce38
+                    );
91ce38
+                    LDAPError::Operation
91ce38
+                })?
91ce38
+                .as_ref()
91ce38
+                .try_into()
91ce38
+                .map_err(|e| {
91ce38
+                    log_error!(ErrorLevel::Trace, "task_validate basedn error -> {:?}", e);
91ce38
+                    LDAPError::Operation
91ce38
+                })?,
91ce38
+            None => return Err(LDAPError::ObjectClassViolation),
91ce38
+        };
91ce38
+
91ce38
+        let raw_filter: String = match e.get_attr("filter") {
91ce38
+            Some(values) => values
91ce38
+                .first()
91ce38
+                .ok_or_else(|| {
91ce38
+                    log_error!(
91ce38
+                        ErrorLevel::Trace,
91ce38
+                        "task_validate filter error -> empty value array?"
91ce38
+                    );
91ce38
+                    LDAPError::Operation
91ce38
+                })?
91ce38
+                .as_ref()
91ce38
+                .try_into()
91ce38
+                .map_err(|e| {
91ce38
+                    log_error!(ErrorLevel::Trace, "task_validate filter error -> {:?}", e);
91ce38
+                    LDAPError::Operation
91ce38
+                })?,
91ce38
+            None => {
91ce38
+                // Give a default filter.
91ce38
+                "(objectClass=*)".to_string()
91ce38
+            }
91ce38
+        };
91ce38
+
91ce38
+        // Error if the first filter is empty?
91ce38
+
91ce38
+        // Now, to make things faster, we wrap the filter in a exclude term.
91ce38
+        let raw_filter = format!("(&{}(!(entryuuid=*)))", raw_filter);
91ce38
+
91ce38
+        Ok(FixupData { basedn, raw_filter })
91ce38
+    }
91ce38
+
91ce38
+    fn task_be_dn_hint(data: &Self::TaskData) -> Option<Sdn> {
91ce38
+        Some(data.basedn.clone())
91ce38
+    }
91ce38
+
91ce38
+    fn task_handler(_task: &Task, data: Self::TaskData) -> Result<Self::TaskData, PluginError> {
91ce38
+        log_error!(
91ce38
+            ErrorLevel::Trace,
91ce38
+            "task_handler -> start thread with -> {:?}",
91ce38
+            data
91ce38
+        );
91ce38
+
91ce38
+        let search = Search::new_map_entry(
91ce38
+            &(*data.basedn),
91ce38
+            SearchScope::Subtree,
91ce38
+            &data.raw_filter,
91ce38
+            plugin_id(),
91ce38
+            &(),
91ce38
+            entryuuid_fixup_cb,
91ce38
+        )
91ce38
+        .map_err(|e| {
91ce38
+            log_error!(
91ce38
+                ErrorLevel::Error,
91ce38
+                "task_handler -> Unable to construct search -> {:?}",
91ce38
+                e
91ce38
+            );
91ce38
+            e
91ce38
+        })?;
91ce38
+
91ce38
+        match search.execute() {
91ce38
+            Ok(_) => {
91ce38
+                log_error!(ErrorLevel::Info, "task_handler -> fixup complete, success!");
91ce38
+                Ok(data)
91ce38
+            }
91ce38
+            Err(e) => {
91ce38
+                // log, and return
91ce38
+                log_error!(