summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ldb/include/ldb_module.h9
-rw-r--r--lib/ldb/ldb_tdb/ldb_index.c3
-rw-r--r--lib/ldb/tests/ldb_mod_op_test.c326
3 files changed, 337 insertions, 1 deletions
diff --git a/lib/ldb/include/ldb_module.h b/lib/ldb/include/ldb_module.h
index ffa6c2eedb8..fd88c6218eb 100644
--- a/lib/ldb/include/ldb_module.h
+++ b/lib/ldb/include/ldb_module.h
@@ -87,6 +87,15 @@ struct ldb_module;
/* force single value checking on this attribute */
#define LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK 0x80
+/*
+ * ensure that this value is unique on an index
+ * (despite the index not otherwise being configured as UNIQUE).
+ * For example, all words starting with 'a' must be unique, but duplicates of
+ * words starting with b are allowed. This is specifically for Samba's
+ * objectSid index which is unique in the primary domain only.
+ */
+#define LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX 0x100
+
/* an extended match rule that always fails to match */
#define SAMBA_LDAP_MATCH_ALWAYS_FALSE "1.3.6.1.4.1.7165.4.5.1"
diff --git a/lib/ldb/ldb_tdb/ldb_index.c b/lib/ldb/ldb_tdb/ldb_index.c
index c71e866037c..0afeae57a60 100644
--- a/lib/ldb/ldb_tdb/ldb_index.c
+++ b/lib/ldb/ldb_tdb/ldb_index.c
@@ -1761,7 +1761,8 @@ static int ltdb_index_add1(struct ldb_module *module,
*/
if (list->count > 0 &&
((a != NULL
- && (a->flags & LDB_ATTR_FLAG_UNIQUE_INDEX)) ||
+ && (a->flags & LDB_ATTR_FLAG_UNIQUE_INDEX ||
+ (el->flags & LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX))) ||
ldb_attr_cmp(el->name, LTDB_IDXDN) == 0)) {
/*
* We do not want to print info about a possibly
diff --git a/lib/ldb/tests/ldb_mod_op_test.c b/lib/ldb/tests/ldb_mod_op_test.c
index 48ad20c7662..cf2288c7bce 100644
--- a/lib/ldb/tests/ldb_mod_op_test.c
+++ b/lib/ldb/tests/ldb_mod_op_test.c
@@ -3009,7 +3009,317 @@ static void test_read_only(void **state)
TALLOC_FREE(tmp_ctx);
}
+static bool unique_values = false;
+
+static int unique_index_test_module_add(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ if (unique_values) {
+ struct ldb_message *msg = discard_const(req->op.add.message);
+ struct ldb_message_element *el = NULL;
+ el = ldb_msg_find_element(msg, "cn");
+ if (el != NULL) {
+ el->flags |= LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX;
+ }
+ }
+
+ return ldb_next_request(module, req);
+}
+
+static int unique_index_test_module_init(struct ldb_module *module)
+{
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_unique_index_test_module_ops = {
+ .name = "unique_index_test",
+ .init_context = unique_index_test_module_init,
+ .add = unique_index_test_module_add,
+};
+
+static int ldb_unique_index_test_setup(void **state)
+{
+ int ret;
+ struct ldb_ldif *ldif;
+ struct ldbtest_ctx *ldb_test_ctx;
+ const char *attrs_ldif = \
+ "dn: @ATTRIBUTES\n"
+ "cn: UNIQUE_INDEX\n"
+ "\n";
+ const char *index_ldif = \
+ "dn: @INDEXLIST\n"
+ "@IDXATTR: cn\n"
+ "\n";
+ const char *options[] = {"modules:unique_index_test"};
+
+
+ ret = ldb_register_module(&ldb_unique_index_test_module_ops);
+ assert_true(ret == LDB_SUCCESS || ret == LDB_ERR_ENTRY_ALREADY_EXISTS);
+ ldbtest_noconn_setup((void **) &ldb_test_ctx);
+
+
+ ret = ldb_connect(ldb_test_ctx->ldb, ldb_test_ctx->dbpath, 0, options);
+ assert_int_equal(ret, 0);
+
+ while ((ldif = ldb_ldif_read_string(ldb_test_ctx->ldb, &attrs_ldif))) {
+ ret = ldb_add(ldb_test_ctx->ldb, ldif->msg);
+ assert_int_equal(ret, LDB_SUCCESS);
+ }
+
+ while ((ldif = ldb_ldif_read_string(ldb_test_ctx->ldb, &index_ldif))) {
+ ret = ldb_add(ldb_test_ctx->ldb, ldif->msg);
+ assert_int_equal(ret, LDB_SUCCESS);
+ }
+
+ unique_values = true;
+
+ *state = ldb_test_ctx;
+ return 0;
+}
+
+static int ldb_unique_index_test_teardown(void **state)
+{
+ int ret;
+ struct ldbtest_ctx *ldb_test_ctx = talloc_get_type_abort(*state,
+ struct ldbtest_ctx);
+ struct ldb_dn *del_dn;
+
+ del_dn = ldb_dn_new_fmt(ldb_test_ctx,
+ ldb_test_ctx->ldb,
+ "@INDEXLIST");
+ assert_non_null(del_dn);
+
+ ret = ldb_delete(ldb_test_ctx->ldb, del_dn);
+ if (ret != LDB_ERR_NO_SUCH_OBJECT) {
+ assert_int_equal(ret, LDB_SUCCESS);
+ }
+
+ assert_dn_doesnt_exist(ldb_test_ctx,
+ "@INDEXLIST");
+
+ TALLOC_FREE(del_dn);
+
+ del_dn = ldb_dn_new_fmt(ldb_test_ctx,
+ ldb_test_ctx->ldb,
+ "@ATTRIBUTES");
+ assert_non_null(del_dn);
+
+ ret = ldb_delete(ldb_test_ctx->ldb, del_dn);
+ if (ret != LDB_ERR_NO_SUCH_OBJECT) {
+ assert_int_equal(ret, LDB_SUCCESS);
+ }
+
+ assert_dn_doesnt_exist(ldb_test_ctx,
+ "@ATTRIBUTES");
+
+ ldbtest_teardown((void **) &ldb_test_ctx);
+ return 0;
+}
+
+
+static void test_ldb_add_unique_value_to_unique_index(void **state)
+{
+ int ret;
+ struct ldb_message *msg;
+ struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
+ struct ldbtest_ctx);
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(test_ctx);
+ assert_non_null(tmp_ctx);
+
+ msg = ldb_msg_new(tmp_ctx);
+ assert_non_null(msg);
+
+ msg->dn = ldb_dn_new_fmt(msg, test_ctx->ldb, "dc=test");
+ assert_non_null(msg->dn);
+
+ ret = ldb_msg_add_string(msg, "cn", "test_unique_index");
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ ret = ldb_add(test_ctx->ldb, msg);
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ talloc_free(tmp_ctx);
+}
+
+static int ldb_non_unique_index_test_setup(void **state)
+{
+ int ret;
+ struct ldb_ldif *ldif;
+ struct ldbtest_ctx *ldb_test_ctx;
+ const char *index_ldif = \
+ "dn: @INDEXLIST\n"
+ "@IDXATTR: cn\n"
+ "\n";
+ const char *options[] = {"modules:unique_index_test"};
+
+ ret = ldb_register_module(&ldb_unique_index_test_module_ops);
+ assert_true(ret == LDB_SUCCESS || ret == LDB_ERR_ENTRY_ALREADY_EXISTS);
+ ldbtest_noconn_setup((void **) &ldb_test_ctx);
+
+
+ ret = ldb_connect(ldb_test_ctx->ldb, ldb_test_ctx->dbpath, 0, options);
+ assert_int_equal(ret, 0);
+
+ while ((ldif = ldb_ldif_read_string(ldb_test_ctx->ldb, &index_ldif))) {
+ ret = ldb_add(ldb_test_ctx->ldb, ldif->msg);
+ assert_int_equal(ret, LDB_SUCCESS);
+ }
+
+ unique_values = true;
+
+ *state = ldb_test_ctx;
+ return 0;
+}
+
+static int ldb_non_unique_index_test_teardown(void **state)
+{
+ int ret;
+ struct ldbtest_ctx *ldb_test_ctx = talloc_get_type_abort(*state,
+ struct ldbtest_ctx);
+ struct ldb_dn *del_dn;
+
+ del_dn = ldb_dn_new_fmt(ldb_test_ctx,
+ ldb_test_ctx->ldb,
+ "@INDEXLIST");
+ assert_non_null(del_dn);
+
+ ret = ldb_delete(ldb_test_ctx->ldb, del_dn);
+ if (ret != LDB_ERR_NO_SUCH_OBJECT) {
+ assert_int_equal(ret, LDB_SUCCESS);
+ }
+
+ assert_dn_doesnt_exist(ldb_test_ctx,
+ "@INDEXLIST");
+
+ TALLOC_FREE(del_dn);
+
+ ldbtest_teardown((void **) &ldb_test_ctx);
+ return 0;
+}
+
+static void test_ldb_add_duplicate_value_to_unique_index(void **state)
+{
+ int ret;
+ struct ldb_message *msg01;
+ struct ldb_message *msg02;
+ struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
+ struct ldbtest_ctx);
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(test_ctx);
+ assert_non_null(tmp_ctx);
+
+ msg01 = ldb_msg_new(tmp_ctx);
+ assert_non_null(msg01);
+
+ msg01->dn = ldb_dn_new_fmt(msg01, test_ctx->ldb, "dc=test01");
+ assert_non_null(msg01->dn);
+
+ ret = ldb_msg_add_string(msg01, "cn", "test_unique_index");
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ ret = ldb_add(test_ctx->ldb, msg01);
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ msg02 = ldb_msg_new(tmp_ctx);
+ assert_non_null(msg01);
+
+ msg02->dn = ldb_dn_new_fmt(msg02, test_ctx->ldb, "dc=test02");
+ assert_non_null(msg02->dn);
+
+ ret = ldb_msg_add_string(msg02, "cn", "test_unique_index");
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ ret = ldb_add(test_ctx->ldb, msg02);
+ assert_int_equal(ret, LDB_ERR_CONSTRAINT_VIOLATION);
+ talloc_free(tmp_ctx);
+}
+
+static void test_ldb_add_to_index_duplicates_allowed(void **state)
+{
+ int ret;
+ struct ldb_message *msg01;
+ struct ldb_message *msg02;
+ struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
+ struct ldbtest_ctx);
+ TALLOC_CTX *tmp_ctx;
+
+ unique_values = false;
+
+ tmp_ctx = talloc_new(test_ctx);
+ assert_non_null(tmp_ctx);
+
+
+ msg01 = ldb_msg_new(tmp_ctx);
+ assert_non_null(msg01);
+
+ msg01->dn = ldb_dn_new_fmt(msg01, test_ctx->ldb, "dc=test01");
+ assert_non_null(msg01->dn);
+
+ ret = ldb_msg_add_string(msg01, "cn", "test_unique_index");
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ ret = ldb_add(test_ctx->ldb, msg01);
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ msg02 = ldb_msg_new(tmp_ctx);
+ assert_non_null(msg01);
+
+ msg02->dn = ldb_dn_new_fmt(msg02, test_ctx->ldb, "dc=test02");
+ assert_non_null(msg02->dn);
+
+ ret = ldb_msg_add_string(msg02, "cn", "test_unique_index");
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ ret = ldb_add(test_ctx->ldb, msg02);
+ assert_int_equal(ret, LDB_SUCCESS);
+ talloc_free(tmp_ctx);
+}
+
+static void test_ldb_add_to_index_unique_values_required(void **state)
+{
+ int ret;
+ struct ldb_message *msg01;
+ struct ldb_message *msg02;
+ struct ldbtest_ctx *test_ctx = talloc_get_type_abort(*state,
+ struct ldbtest_ctx);
+ TALLOC_CTX *tmp_ctx;
+
+ unique_values = true;
+
+ tmp_ctx = talloc_new(test_ctx);
+ assert_non_null(tmp_ctx);
+
+
+ msg01 = ldb_msg_new(tmp_ctx);
+ assert_non_null(msg01);
+
+ msg01->dn = ldb_dn_new_fmt(msg01, test_ctx->ldb, "dc=test01");
+ assert_non_null(msg01->dn);
+
+ ret = ldb_msg_add_string(msg01, "cn", "test_unique_index");
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ ret = ldb_add(test_ctx->ldb, msg01);
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ msg02 = ldb_msg_new(tmp_ctx);
+ assert_non_null(msg01);
+
+ msg02->dn = ldb_dn_new_fmt(msg02, test_ctx->ldb, "dc=test02");
+ assert_non_null(msg02->dn);
+
+ ret = ldb_msg_add_string(msg02, "cn", "test_unique_index");
+ assert_int_equal(ret, LDB_SUCCESS);
+
+ ret = ldb_add(test_ctx->ldb, msg02);
+ assert_int_equal(ret, LDB_ERR_CONSTRAINT_VIOLATION);
+ talloc_free(tmp_ctx);
+}
int main(int argc, const char **argv)
{
const struct CMUnitTest tests[] = {
@@ -3133,6 +3443,22 @@ int main(int argc, const char **argv)
cmocka_unit_test_setup_teardown(test_read_only,
ldb_read_only_setup,
ldb_read_only_teardown),
+ cmocka_unit_test_setup_teardown(
+ test_ldb_add_unique_value_to_unique_index,
+ ldb_unique_index_test_setup,
+ ldb_unique_index_test_teardown),
+ cmocka_unit_test_setup_teardown(
+ test_ldb_add_duplicate_value_to_unique_index,
+ ldb_unique_index_test_setup,
+ ldb_unique_index_test_teardown),
+ cmocka_unit_test_setup_teardown(
+ test_ldb_add_to_index_duplicates_allowed,
+ ldb_non_unique_index_test_setup,
+ ldb_non_unique_index_test_teardown),
+ cmocka_unit_test_setup_teardown(
+ test_ldb_add_to_index_unique_values_required,
+ ldb_non_unique_index_test_setup,
+ ldb_non_unique_index_test_teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);