andykimpe / rpms / 389-ds-base

Forked from rpms/389-ds-base 5 months ago
Clone

Blame SOURCES/0036-Issue-4877-RFE-EntryUUID-to-validate-UUIDs-on-fixup-.patch

b03d2c
From 0ed5299841733f9e18c5fe8c6a3f9d3fbd49c75a Mon Sep 17 00:00:00 2001
b03d2c
From: Firstyear <william@blackhats.net.au>
b03d2c
Date: Fri, 20 Aug 2021 09:18:50 +1000
b03d2c
Subject: [PATCH 3/4] Issue 4877 - RFE - EntryUUID to validate UUIDs on fixup
b03d2c
 (#4878)
b03d2c
b03d2c
Bug Description: Due to changing the syntax of EntryUUID's
b03d2c
to string, we may have invalid EntryUUID's imported into
b03d2c
the database.
b03d2c
b03d2c
Fix Description: To resolve this during a fixup we validate
b03d2c
that Uuid's have a valid syntax. If they do not, we regenerate
b03d2c
them.
b03d2c
b03d2c
fixes: https://github.com/389ds/389-ds-base/issues/4877
b03d2c
b03d2c
Author: William Brown <william@blackhats.net.au>
b03d2c
b03d2c
Review by: @mreynolds389
b03d2c
---
b03d2c
 .../entryuuid/localhost-userRoot-invalid.ldif | 233 ++++++++++++++++++
b03d2c
 .../tests/suites/entryuuid/basic_test.py      |  58 ++++-
b03d2c
 src/plugins/entryuuid/src/lib.rs              |  28 ++-
b03d2c
 src/slapi_r_plugin/src/pblock.rs              |   4 +-
b03d2c
 src/slapi_r_plugin/src/value.rs               |  10 +-
b03d2c
 5 files changed, 322 insertions(+), 11 deletions(-)
b03d2c
 create mode 100644 dirsrvtests/tests/data/entryuuid/localhost-userRoot-invalid.ldif
b03d2c
b03d2c
diff --git a/dirsrvtests/tests/data/entryuuid/localhost-userRoot-invalid.ldif b/dirsrvtests/tests/data/entryuuid/localhost-userRoot-invalid.ldif
b03d2c
new file mode 100644
b03d2c
index 000000000..9703babed
b03d2c
--- /dev/null
b03d2c
+++ b/dirsrvtests/tests/data/entryuuid/localhost-userRoot-invalid.ldif
b03d2c
@@ -0,0 +1,233 @@
b03d2c
+version: 1
b03d2c
+
b03d2c
+# entry-id: 1
b03d2c
+dn: dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: domain
b03d2c
+dc: example
b03d2c
+description: dc=example,dc=com
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015542Z
b03d2c
+modifyTimestamp: 20200325015542Z
b03d2c
+nsUniqueId: a2b33229-6e3b11ea-8de0c78c-83e27eda
b03d2c
+aci: (targetattr="dc || description || objectClass")(targetfilter="(objectClas
b03d2c
+ s=domain)")(version 3.0; acl "Enable anyone domain read"; allow (read, search
b03d2c
+ , compare)(userdn="ldap:///anyone");)
b03d2c
+aci: (targetattr="ou || objectClass")(targetfilter="(objectClass=organizationa
b03d2c
+ lUnit)")(version 3.0; acl "Enable anyone ou read"; allow (read, search, compa
b03d2c
+ re)(userdn="ldap:///anyone");)
b03d2c
+
b03d2c
+# entry-id: 2
b03d2c
+dn: cn=389_ds_system,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: nscontainer
b03d2c
+objectClass: ldapsubentry
b03d2c
+cn: 389_ds_system
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015542Z
b03d2c
+modifyTimestamp: 20200325015542Z
b03d2c
+nsUniqueId: a2b3322a-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 3
b03d2c
+dn: ou=groups,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: organizationalunit
b03d2c
+ou: groups
b03d2c
+aci: (targetattr="cn || member || gidNumber || nsUniqueId || description || ob
b03d2c
+ jectClass")(targetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enab
b03d2c
+ le anyone group read"; allow (read, search, compare)(userdn="ldap:///anyone")
b03d2c
+ ;)
b03d2c
+aci: (targetattr="member")(targetfilter="(objectClass=groupOfNames)")(version 
b03d2c
+ 3.0; acl "Enable group_modify to alter members"; allow (write)(groupdn="ldap:
b03d2c
+ ///cn=group_modify,ou=permissions,dc=example,dc=com");)
b03d2c
+aci: (targetattr="cn || member || gidNumber || description || objectClass")(ta
b03d2c
+ rgetfilter="(objectClass=groupOfNames)")(version 3.0; acl "Enable group_admin
b03d2c
+  to manage groups"; allow (write, add, delete)(groupdn="ldap:///cn=group_admi
b03d2c
+ n,ou=permissions,dc=example,dc=com");)
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015543Z
b03d2c
+modifyTimestamp: 20200325015543Z
b03d2c
+nsUniqueId: a2b3322b-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 4
b03d2c
+dn: ou=people,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: organizationalunit
b03d2c
+ou: people
b03d2c
+aci: (targetattr="objectClass || description || nsUniqueId || uid || displayNa
b03d2c
+ me || loginShell || uidNumber || gidNumber || gecos || homeDirectory || cn ||
b03d2c
+  memberOf || mail || nsSshPublicKey || nsAccountLock || userCertificate")(tar
b03d2c
+ getfilter="(objectClass=posixaccount)")(version 3.0; acl "Enable anyone user 
b03d2c
+ read"; allow (read, search, compare)(userdn="ldap:///anyone");)
b03d2c
+aci: (targetattr="displayName || legalName || userPassword || nsSshPublicKey")
b03d2c
+ (version 3.0; acl "Enable self partial modify"; allow (write)(userdn="ldap://
b03d2c
+ /self");)
b03d2c
+aci: (targetattr="legalName || telephoneNumber || mobile || sn")(targetfilter=
b03d2c
+ "(|(objectClass=nsPerson)(objectClass=inetOrgPerson))")(version 3.0; acl "Ena
b03d2c
+ ble self legalname read"; allow (read, search, compare)(userdn="ldap:///self"
b03d2c
+ );)
b03d2c
+aci: (targetattr="legalName || telephoneNumber")(targetfilter="(objectClass=ns
b03d2c
+ Person)")(version 3.0; acl "Enable user legalname read"; allow (read, search,
b03d2c
+  compare)(groupdn="ldap:///cn=user_private_read,ou=permissions,dc=example,dc=
b03d2c
+ com");)
b03d2c
+aci: (targetattr="uid || description || displayName || loginShell || uidNumber
b03d2c
+  || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalNam
b03d2c
+ e || telephoneNumber || mobile")(targetfilter="(&(objectClass=nsPerson)(objec
b03d2c
+ tClass=nsAccount))")(version 3.0; acl "Enable user admin create"; allow (writ
b03d2c
+ e, add, delete, read)(groupdn="ldap:///cn=user_admin,ou=permissions,dc=exampl
b03d2c
+ e,dc=com");)
b03d2c
+aci: (targetattr="uid || description || displayName || loginShell || uidNumber
b03d2c
+  || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalNam
b03d2c
+ e || telephoneNumber || mobile")(targetfilter="(&(objectClass=nsPerson)(objec
b03d2c
+ tClass=nsAccount))")(version 3.0; acl "Enable user modify to change users"; a
b03d2c
+ llow (write, read)(groupdn="ldap:///cn=user_modify,ou=permissions,dc=example,
b03d2c
+ dc=com");)
b03d2c
+aci: (targetattr="userPassword || nsAccountLock || userCertificate || nsSshPub
b03d2c
+ licKey")(targetfilter="(objectClass=nsAccount)")(version 3.0; acl "Enable use
b03d2c
+ r password reset"; allow (write, read)(groupdn="ldap:///cn=user_passwd_reset,
b03d2c
+ ou=permissions,dc=example,dc=com");)
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015543Z
b03d2c
+modifyTimestamp: 20200325015543Z
b03d2c
+nsUniqueId: a2b3322c-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 5
b03d2c
+dn: ou=permissions,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: organizationalunit
b03d2c
+ou: permissions
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015543Z
b03d2c
+modifyTimestamp: 20200325015543Z
b03d2c
+nsUniqueId: a2b3322d-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 6
b03d2c
+dn: ou=services,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: organizationalunit
b03d2c
+ou: services
b03d2c
+aci: (targetattr="objectClass || description || nsUniqueId || cn || memberOf |
b03d2c
+ | nsAccountLock ")(targetfilter="(objectClass=netscapeServer)")(version 3.0; 
b03d2c
+ acl "Enable anyone service account read"; allow (read, search, compare)(userd
b03d2c
+ n="ldap:///anyone");)
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015544Z
b03d2c
+modifyTimestamp: 20200325015544Z
b03d2c
+nsUniqueId: a2b3322e-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 7
b03d2c
+dn: uid=demo_user,ou=people,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: nsPerson
b03d2c
+objectClass: nsAccount
b03d2c
+objectClass: nsOrgPerson
b03d2c
+objectClass: posixAccount
b03d2c
+uid: demo_user
b03d2c
+cn: Demo User
b03d2c
+displayName: Demo User
b03d2c
+legalName: Demo User Name
b03d2c
+uidNumber: 99998
b03d2c
+gidNumber: 99998
b03d2c
+homeDirectory: /var/empty
b03d2c
+loginShell: /bin/false
b03d2c
+nsAccountLock: true
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015544Z
b03d2c
+modifyTimestamp: 20200325061615Z
b03d2c
+nsUniqueId: a2b3322f-6e3b11ea-8de0c78c-83e27eda
b03d2c
+entryUUID: INVALID_UUID
b03d2c
+
b03d2c
+# entry-id: 8
b03d2c
+dn: cn=demo_group,ou=groups,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: groupOfNames
b03d2c
+objectClass: posixGroup
b03d2c
+objectClass: nsMemberOf
b03d2c
+cn: demo_group
b03d2c
+gidNumber: 99999
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015544Z
b03d2c
+modifyTimestamp: 20200325015544Z
b03d2c
+nsUniqueId: a2b33230-6e3b11ea-8de0c78c-83e27eda
b03d2c
+entryUUID: f6df8fe9-6b30-46aa-aa13-f0bf755371e8
b03d2c
+
b03d2c
+# entry-id: 9
b03d2c
+dn: cn=group_admin,ou=permissions,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: groupOfNames
b03d2c
+objectClass: nsMemberOf
b03d2c
+cn: group_admin
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015545Z
b03d2c
+modifyTimestamp: 20200325015545Z
b03d2c
+nsUniqueId: a2b33231-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 10
b03d2c
+dn: cn=group_modify,ou=permissions,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: groupOfNames
b03d2c
+objectClass: nsMemberOf
b03d2c
+cn: group_modify
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015545Z
b03d2c
+modifyTimestamp: 20200325015545Z
b03d2c
+nsUniqueId: a2b33232-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 11
b03d2c
+dn: cn=user_admin,ou=permissions,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: groupOfNames
b03d2c
+objectClass: nsMemberOf
b03d2c
+cn: user_admin
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015545Z
b03d2c
+modifyTimestamp: 20200325015545Z
b03d2c
+nsUniqueId: a2b33233-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 12
b03d2c
+dn: cn=user_modify,ou=permissions,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: groupOfNames
b03d2c
+objectClass: nsMemberOf
b03d2c
+cn: user_modify
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015546Z
b03d2c
+modifyTimestamp: 20200325015546Z
b03d2c
+nsUniqueId: a2b33234-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 13
b03d2c
+dn: cn=user_passwd_reset,ou=permissions,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: groupOfNames
b03d2c
+objectClass: nsMemberOf
b03d2c
+cn: user_passwd_reset
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015546Z
b03d2c
+modifyTimestamp: 20200325015546Z
b03d2c
+nsUniqueId: a2b33235-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
+# entry-id: 14
b03d2c
+dn: cn=user_private_read,ou=permissions,dc=example,dc=com
b03d2c
+objectClass: top
b03d2c
+objectClass: groupOfNames
b03d2c
+objectClass: nsMemberOf
b03d2c
+cn: user_private_read
b03d2c
+creatorsName: cn=Directory Manager
b03d2c
+modifiersName: cn=Directory Manager
b03d2c
+createTimestamp: 20200325015547Z
b03d2c
+modifyTimestamp: 20200325015547Z
b03d2c
+nsUniqueId: a2b33236-6e3b11ea-8de0c78c-83e27eda
b03d2c
+
b03d2c
diff --git a/dirsrvtests/tests/suites/entryuuid/basic_test.py b/dirsrvtests/tests/suites/entryuuid/basic_test.py
b03d2c
index 4d8a40909..41ffbfe10 100644
b03d2c
--- a/dirsrvtests/tests/suites/entryuuid/basic_test.py
b03d2c
+++ b/dirsrvtests/tests/suites/entryuuid/basic_test.py
b03d2c
@@ -10,6 +10,7 @@ import ldap
b03d2c
 import pytest
b03d2c
 import time
b03d2c
 import shutil
b03d2c
+import uuid
b03d2c
 from lib389.idm.user import nsUserAccounts, UserAccounts
b03d2c
 from lib389.idm.account import Accounts
b03d2c
 from lib389.idm.domain import Domain
b03d2c
@@ -217,7 +218,7 @@ def test_entryuuid_fixup_task(topology):
b03d2c
 
b03d2c
     # 4. run the fix up
b03d2c
     # For now set the log level to high!
b03d2c
-    topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.TRACE))
b03d2c
+    topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.PLUGIN))
b03d2c
     task = plug.fixup(DEFAULT_SUFFIX)
b03d2c
     task.wait()
b03d2c
     assert(task.is_complete() and task.get_exit_code() == 0)
b03d2c
@@ -242,3 +243,58 @@ def test_entryuuid_fixup_task(topology):
b03d2c
     euuid_domain_2 = domain.get_attr_val_utf8('entryUUID')
b03d2c
     assert(euuid_domain_2 == euuid_domain)
b03d2c
 
b03d2c
+@pytest.mark.skipif(not default_paths.rust_enabled or ds_is_older('1.4.2.0'), reason="Entryuuid is not available in older versions")
b03d2c
+def test_entryuuid_import_and_fixup_of_invalid_values(topology):
b03d2c
+    """ Test that when we import a database with an invalid entryuuid
b03d2c
+    that it is accepted *and* that subsequently we can fix the invalid
b03d2c
+    entryuuid during a fixup.
b03d2c
+
b03d2c
+    :id: ec8ef3a7-3cd2-4cbd-b6f1-2449fa17be75
b03d2c
+
b03d2c
+    :setup: Standalone instance
b03d2c
+
b03d2c
+    :steps:
b03d2c
+        1. Import the db from the ldif
b03d2c
+        2. Check the entryuuid is invalid
b03d2c
+        3. Run the fixup
b03d2c
+        4. Check the entryuuid is now valid (regenerated)
b03d2c
+
b03d2c
+    :expectedresults:
b03d2c
+        1. Success
b03d2c
+        2. The entryuuid is invalid
b03d2c
+        3. Success
b03d2c
+        4. The entryuuid is valid
b03d2c
+    """
b03d2c
+
b03d2c
+    # 1. Import the db
b03d2c
+    ldif_dir = topology.standalone.get_ldif_dir()
b03d2c
+    target_ldif = os.path.join(ldif_dir, 'localhost-userRoot-invalid.ldif')
b03d2c
+    import_ldif = os.path.join(DATADIR1, 'localhost-userRoot-invalid.ldif')
b03d2c
+    shutil.copyfile(import_ldif, target_ldif)
b03d2c
+    os.chmod(target_ldif, 0o777)
b03d2c
+
b03d2c
+    be = Backends(topology.standalone).get('userRoot')
b03d2c
+    task = be.import_ldif([target_ldif])
b03d2c
+    task.wait()
b03d2c
+    assert(task.is_complete() and task.get_exit_code() == 0)
b03d2c
+
b03d2c
+    # 2. Check the entryuuid is invalid
b03d2c
+    account = nsUserAccounts(topology.standalone, DEFAULT_SUFFIX).get("demo_user")
b03d2c
+    euuid = account.get_attr_val_utf8('entryUUID')
b03d2c
+    assert(euuid == "INVALID_UUID")
b03d2c
+
b03d2c
+    # 3. Run the fixup
b03d2c
+    topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.PLUGIN))
b03d2c
+    plug = EntryUUIDPlugin(topology.standalone)
b03d2c
+    task = plug.fixup(DEFAULT_SUFFIX)
b03d2c
+    task.wait()
b03d2c
+    assert(task.is_complete() and task.get_exit_code() == 0)
b03d2c
+    topology.standalone.config.loglevel(vals=(ErrorLog.DEFAULT,))
b03d2c
+
b03d2c
+    # 4. Check the entryuuid is valid
b03d2c
+    euuid = account.get_attr_val_utf8('entryUUID')
b03d2c
+    print(f"❄️   account entryUUID -> {euuid}");
b03d2c
+    assert(euuid != "INVALID_UUID")
b03d2c
+    # Raises an error if invalid
b03d2c
+    uuid.UUID(euuid)
b03d2c
+
b03d2c
diff --git a/src/plugins/entryuuid/src/lib.rs b/src/plugins/entryuuid/src/lib.rs
b03d2c
index 29a9f1258..ad3faef4b 100644
b03d2c
--- a/src/plugins/entryuuid/src/lib.rs
b03d2c
+++ b/src/plugins/entryuuid/src/lib.rs
b03d2c
@@ -144,11 +144,17 @@ impl SlapiPlugin3 for EntryUuid {
b03d2c
         // Error if the first filter is empty?
b03d2c
 
b03d2c
         // Now, to make things faster, we wrap the filter in a exclude term.
b03d2c
+
b03d2c
+        // 2021 - #4877 because we allow entryuuid to be strings, on import these may
b03d2c
+        // be invalid. As a result, we DO need to allow the fixup to check the entryuuid
b03d2c
+        // value is correct, so we can not exclude these during the search.
b03d2c
+        /*
b03d2c
         let raw_filter = if !raw_filter.starts_with('(') && !raw_filter.ends_with('(') {
b03d2c
             format!("(&({})(!(entryuuid=*)))", raw_filter)
b03d2c
         } else {
b03d2c
             format!("(&{}(!(entryuuid=*)))", raw_filter)
b03d2c
         };
b03d2c
+        */
b03d2c
 
b03d2c
         Ok(FixupData { basedn, raw_filter })
b03d2c
     }
b03d2c
@@ -213,14 +219,20 @@ pub fn entryuuid_fixup_mapfn(e: &EntryRef, _data: &()) -> Result<(), PluginError
b03d2c
     /* Supply a modification to the entry. */
b03d2c
     let sdn = e.get_sdnref();
b03d2c
 
b03d2c
-    /* Sanity check that entryuuid doesn't already exist */
b03d2c
-    if e.contains_attr("entryUUID") {
b03d2c
-        log_error!(
b03d2c
-            ErrorLevel::Plugin,
b03d2c
-            "skipping fixup for -> {}",
b03d2c
-            sdn.to_dn_string()
b03d2c
-        );
b03d2c
-        return Ok(());
b03d2c
+    /* Check that entryuuid doesn't already exist, and is valid */
b03d2c
+    if let Some(valueset) = e.get_attr("entryUUID") {
b03d2c
+        if valueset.iter().all(|v| {
b03d2c
+            let u: Result<Uuid, _> = (&v).try_into();
b03d2c
+            u.is_ok()
b03d2c
+        }) {
b03d2c
+            // All values were valid uuid, move on!
b03d2c
+            log_error!(
b03d2c
+                ErrorLevel::Plugin,
b03d2c
+                "skipping fixup for -> {}",
b03d2c
+                sdn.to_dn_string()
b03d2c
+            );
b03d2c
+            return Ok(());
b03d2c
+        }
b03d2c
     }
b03d2c
 
b03d2c
     // Setup the modifications
b03d2c
diff --git a/src/slapi_r_plugin/src/pblock.rs b/src/slapi_r_plugin/src/pblock.rs
b03d2c
index 718ff2ca7..ac1b3c33b 100644
b03d2c
--- a/src/slapi_r_plugin/src/pblock.rs
b03d2c
+++ b/src/slapi_r_plugin/src/pblock.rs
b03d2c
@@ -281,7 +281,9 @@ impl PblockRef {
b03d2c
     }
b03d2c
 
b03d2c
     pub fn get_is_replicated_operation(&mut self) -> bool {
b03d2c
-        let i = self.get_value_i32(PblockType::IsReplicationOperation).unwrap_or(0);
b03d2c
+        let i = self
b03d2c
+            .get_value_i32(PblockType::IsReplicationOperation)
b03d2c
+            .unwrap_or(0);
b03d2c
         // Because rust returns the result of the last evaluation, we can
b03d2c
         // just return if not equal 0.
b03d2c
         i != 0
b03d2c
diff --git a/src/slapi_r_plugin/src/value.rs b/src/slapi_r_plugin/src/value.rs
b03d2c
index 46246837a..cd565295c 100644
b03d2c
--- a/src/slapi_r_plugin/src/value.rs
b03d2c
+++ b/src/slapi_r_plugin/src/value.rs
b03d2c
@@ -1,6 +1,6 @@
b03d2c
 use crate::ber::{ol_berval, BerValRef};
b03d2c
 use crate::dn::Sdn;
b03d2c
-use std::convert::{From, TryFrom};
b03d2c
+use std::convert::{From, TryFrom, TryInto};
b03d2c
 use std::ffi::CString;
b03d2c
 use std::iter::once;
b03d2c
 use std::iter::FromIterator;
b03d2c
@@ -213,6 +213,14 @@ impl TryFrom<&ValueRef> for String {
b03d2c
     }
b03d2c
 }
b03d2c
 
b03d2c
+impl TryFrom<&ValueRef> for Uuid {
b03d2c
+    type Error = ();
b03d2c
+
b03d2c
+    fn try_from(value: &ValueRef) -> Result<Self, Self::Error> {
b03d2c
+        (&value.bvr).try_into().map_err(|_| ())
b03d2c
+    }
b03d2c
+}
b03d2c
+
b03d2c
 impl TryFrom<&ValueRef> for Sdn {
b03d2c
     type Error = ();
b03d2c
 
b03d2c
-- 
b03d2c
2.31.1
b03d2c