|
|
e4a41f |
From b2e0a1d405d15383064e547fd15008bc136d3efe Mon Sep 17 00:00:00 2001
|
|
|
e4a41f |
From: Firstyear <william@blackhats.net.au>
|
|
|
e4a41f |
Date: Thu, 17 Dec 2020 08:22:23 +1000
|
|
|
e4a41f |
Subject: [PATCH 05/12] Issue 4498 - BUG - entryuuid replication may not work
|
|
|
e4a41f |
(#4503)
|
|
|
e4a41f |
|
|
|
e4a41f |
Bug Description: EntryUUID can be duplicated in replication,
|
|
|
e4a41f |
due to a missing check in assign_uuid
|
|
|
e4a41f |
|
|
|
e4a41f |
Fix Description: Add a test case to determine how this occurs,
|
|
|
e4a41f |
and add the correct check for existing entryUUID.
|
|
|
e4a41f |
|
|
|
e4a41f |
fixes: https://github.com/389ds/389-ds-base/issues/4498
|
|
|
e4a41f |
|
|
|
e4a41f |
Author: William Brown <william@blackhats.net.au>
|
|
|
e4a41f |
|
|
|
e4a41f |
Review by: @mreynolds389
|
|
|
e4a41f |
---
|
|
|
e4a41f |
.../tests/suites/entryuuid/replicated_test.py | 77 +++++++++++++++++++
|
|
|
e4a41f |
rpm.mk | 2 +-
|
|
|
e4a41f |
src/plugins/entryuuid/src/lib.rs | 20 ++++-
|
|
|
e4a41f |
src/slapi_r_plugin/src/constants.rs | 2 +
|
|
|
e4a41f |
src/slapi_r_plugin/src/pblock.rs | 7 ++
|
|
|
e4a41f |
5 files changed, 106 insertions(+), 2 deletions(-)
|
|
|
e4a41f |
create mode 100644 dirsrvtests/tests/suites/entryuuid/replicated_test.py
|
|
|
e4a41f |
|
|
|
e4a41f |
diff --git a/dirsrvtests/tests/suites/entryuuid/replicated_test.py b/dirsrvtests/tests/suites/entryuuid/replicated_test.py
|
|
|
e4a41f |
new file mode 100644
|
|
|
e4a41f |
index 000000000..a2ebc8ff7
|
|
|
e4a41f |
--- /dev/null
|
|
|
e4a41f |
+++ b/dirsrvtests/tests/suites/entryuuid/replicated_test.py
|
|
|
e4a41f |
@@ -0,0 +1,77 @@
|
|
|
e4a41f |
+# --- BEGIN COPYRIGHT BLOCK ---
|
|
|
e4a41f |
+# Copyright (C) 2020 William Brown <william@blackhats.net.au>
|
|
|
e4a41f |
+# All rights reserved.
|
|
|
e4a41f |
+#
|
|
|
e4a41f |
+# License: GPL (version 3 or any later version).
|
|
|
e4a41f |
+# See LICENSE for details.
|
|
|
e4a41f |
+# --- END COPYRIGHT BLOCK ---
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+import ldap
|
|
|
e4a41f |
+import pytest
|
|
|
e4a41f |
+import logging
|
|
|
e4a41f |
+from lib389.topologies import topology_m2 as topo_m2
|
|
|
e4a41f |
+from lib389.idm.user import nsUserAccounts
|
|
|
e4a41f |
+from lib389.paths import Paths
|
|
|
e4a41f |
+from lib389.utils import ds_is_older
|
|
|
e4a41f |
+from lib389._constants import *
|
|
|
e4a41f |
+from lib389.replica import ReplicationManager
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+default_paths = Paths()
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+pytestmark = pytest.mark.tier1
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+@pytest.mark.skipif(not default_paths.rust_enabled or ds_is_older('1.4.2.0'), reason="Entryuuid is not available in older versions")
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+def test_entryuuid_with_replication(topo_m2):
|
|
|
e4a41f |
+ """ Check that entryuuid works with replication
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ :id: a5f15bf9-7f63-473a-840c-b9037b787024
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ :setup: two node mmr
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ :steps:
|
|
|
e4a41f |
+ 1. Create an entry on one server
|
|
|
e4a41f |
+ 2. Wait for replication
|
|
|
e4a41f |
+ 3. Assert it is on the second
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ :expectedresults:
|
|
|
e4a41f |
+ 1. Success
|
|
|
e4a41f |
+ 1. Success
|
|
|
e4a41f |
+ 1. Success
|
|
|
e4a41f |
+ """
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ server_a = topo_m2.ms["supplier1"]
|
|
|
e4a41f |
+ server_b = topo_m2.ms["supplier2"]
|
|
|
e4a41f |
+ server_a.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.TRACE))
|
|
|
e4a41f |
+ server_b.config.loglevel(vals=(ErrorLog.DEFAULT,ErrorLog.TRACE))
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ repl = ReplicationManager(DEFAULT_SUFFIX)
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ account_a = nsUserAccounts(server_a, DEFAULT_SUFFIX).create_test_user(uid=2000)
|
|
|
e4a41f |
+ euuid_a = account_a.get_attr_vals_utf8('entryUUID')
|
|
|
e4a41f |
+ print("🧩 %s" % euuid_a)
|
|
|
e4a41f |
+ assert(euuid_a is not None)
|
|
|
e4a41f |
+ assert(len(euuid_a) == 1)
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ repl.wait_for_replication(server_a, server_b)
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ account_b = nsUserAccounts(server_b, DEFAULT_SUFFIX).get("test_user_2000")
|
|
|
e4a41f |
+ euuid_b = account_b.get_attr_vals_utf8('entryUUID')
|
|
|
e4a41f |
+ print("🧩 %s" % euuid_b)
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ server_a.config.loglevel(vals=(ErrorLog.DEFAULT,))
|
|
|
e4a41f |
+ server_b.config.loglevel(vals=(ErrorLog.DEFAULT,))
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ assert(euuid_b is not None)
|
|
|
e4a41f |
+ assert(len(euuid_b) == 1)
|
|
|
e4a41f |
+ assert(euuid_b == euuid_a)
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ account_b.set("description", "update")
|
|
|
e4a41f |
+ repl.wait_for_replication(server_b, server_a)
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ euuid_c = account_a.get_attr_vals_utf8('entryUUID')
|
|
|
e4a41f |
+ print("🧩 %s" % euuid_c)
|
|
|
e4a41f |
+ assert(euuid_c is not None)
|
|
|
e4a41f |
+ assert(len(euuid_c) == 1)
|
|
|
e4a41f |
+ assert(euuid_c == euuid_a)
|
|
|
e4a41f |
+
|
|
|
e4a41f |
diff --git a/rpm.mk b/rpm.mk
|
|
|
e4a41f |
index 02f5bba37..d1cdff7df 100644
|
|
|
e4a41f |
--- a/rpm.mk
|
|
|
e4a41f |
+++ b/rpm.mk
|
|
|
e4a41f |
@@ -25,7 +25,7 @@ TSAN_ON = 0
|
|
|
e4a41f |
# Undefined Behaviour Sanitizer
|
|
|
e4a41f |
UBSAN_ON = 0
|
|
|
e4a41f |
|
|
|
e4a41f |
-RUST_ON = 0
|
|
|
e4a41f |
+RUST_ON = 1
|
|
|
e4a41f |
|
|
|
e4a41f |
# PERL_ON is deprecated and turns on the LEGACY_ON, this for not breaking people's workflows.
|
|
|
e4a41f |
PERL_ON = 1
|
|
|
e4a41f |
diff --git a/src/plugins/entryuuid/src/lib.rs b/src/plugins/entryuuid/src/lib.rs
|
|
|
e4a41f |
index 92977db05..0197c5e83 100644
|
|
|
e4a41f |
--- a/src/plugins/entryuuid/src/lib.rs
|
|
|
e4a41f |
+++ b/src/plugins/entryuuid/src/lib.rs
|
|
|
e4a41f |
@@ -30,6 +30,16 @@ slapi_r_search_callback_mapfn!(entryuuid, entryuuid_fixup_cb, entryuuid_fixup_ma
|
|
|
e4a41f |
fn assign_uuid(e: &mut EntryRef) {
|
|
|
e4a41f |
let sdn = e.get_sdnref();
|
|
|
e4a41f |
|
|
|
e4a41f |
+ // 🚧 safety barrier 🚧
|
|
|
e4a41f |
+ if e.contains_attr("entryUUID") {
|
|
|
e4a41f |
+ log_error!(
|
|
|
e4a41f |
+ ErrorLevel::Trace,
|
|
|
e4a41f |
+ "assign_uuid -> entryUUID exists, skipping dn {}",
|
|
|
e4a41f |
+ sdn.to_dn_string()
|
|
|
e4a41f |
+ );
|
|
|
e4a41f |
+ return;
|
|
|
e4a41f |
+ }
|
|
|
e4a41f |
+
|
|
|
e4a41f |
// We could consider making these lazy static.
|
|
|
e4a41f |
let config_sdn = Sdn::try_from("cn=config").expect("Invalid static dn");
|
|
|
e4a41f |
let schema_sdn = Sdn::try_from("cn=schema").expect("Invalid static dn");
|
|
|
e4a41f |
@@ -66,7 +76,15 @@ impl SlapiPlugin3 for EntryUuid {
|
|
|
e4a41f |
}
|
|
|
e4a41f |
|
|
|
e4a41f |
fn betxn_pre_add(pb: &mut PblockRef) -> Result<(), PluginError> {
|
|
|
e4a41f |
- log_error!(ErrorLevel::Trace, "betxn_pre_add");
|
|
|
e4a41f |
+ if pb.get_is_replicated_operation() {
|
|
|
e4a41f |
+ log_error!(
|
|
|
e4a41f |
+ ErrorLevel::Trace,
|
|
|
e4a41f |
+ "betxn_pre_add -> replicated operation, will not change"
|
|
|
e4a41f |
+ );
|
|
|
e4a41f |
+ return Ok(());
|
|
|
e4a41f |
+ }
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ log_error!(ErrorLevel::Trace, "betxn_pre_add -> start");
|
|
|
e4a41f |
|
|
|
e4a41f |
let mut e = pb.get_op_add_entryref().map_err(|_| PluginError::Pblock)?;
|
|
|
e4a41f |
assign_uuid(&mut e);
|
|
|
e4a41f |
diff --git a/src/slapi_r_plugin/src/constants.rs b/src/slapi_r_plugin/src/constants.rs
|
|
|
e4a41f |
index 34845c2f4..aa0691acc 100644
|
|
|
e4a41f |
--- a/src/slapi_r_plugin/src/constants.rs
|
|
|
e4a41f |
+++ b/src/slapi_r_plugin/src/constants.rs
|
|
|
e4a41f |
@@ -164,6 +164,8 @@ pub(crate) enum PblockType {
|
|
|
e4a41f |
AddEntry = 60,
|
|
|
e4a41f |
/// SLAPI_BACKEND
|
|
|
e4a41f |
Backend = 130,
|
|
|
e4a41f |
+ /// SLAPI_IS_REPLICATED_OPERATION
|
|
|
e4a41f |
+ IsReplicationOperation = 142,
|
|
|
e4a41f |
/// SLAPI_PLUGIN_MR_NAMES
|
|
|
e4a41f |
MRNames = 624,
|
|
|
e4a41f |
/// SLAPI_PLUGIN_SYNTAX_NAMES
|
|
|
e4a41f |
diff --git a/src/slapi_r_plugin/src/pblock.rs b/src/slapi_r_plugin/src/pblock.rs
|
|
|
e4a41f |
index 0f83914f3..718ff2ca7 100644
|
|
|
e4a41f |
--- a/src/slapi_r_plugin/src/pblock.rs
|
|
|
e4a41f |
+++ b/src/slapi_r_plugin/src/pblock.rs
|
|
|
e4a41f |
@@ -279,4 +279,11 @@ impl PblockRef {
|
|
|
e4a41f |
pub fn get_op_result(&mut self) -> i32 {
|
|
|
e4a41f |
self.get_value_i32(PblockType::OpResult).unwrap_or(-1)
|
|
|
e4a41f |
}
|
|
|
e4a41f |
+
|
|
|
e4a41f |
+ pub fn get_is_replicated_operation(&mut self) -> bool {
|
|
|
e4a41f |
+ let i = self.get_value_i32(PblockType::IsReplicationOperation).unwrap_or(0);
|
|
|
e4a41f |
+ // Because rust returns the result of the last evaluation, we can
|
|
|
e4a41f |
+ // just return if not equal 0.
|
|
|
e4a41f |
+ i != 0
|
|
|
e4a41f |
+ }
|
|
|
e4a41f |
}
|
|
|
e4a41f |
--
|
|
|
e4a41f |
2.26.3
|
|
|
e4a41f |
|