]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
ldb ldb_index: Add an attriubute flag to require a unique value.
authorGary Lockyer <gary@catalyst.net.nz>
Mon, 20 Nov 2017 18:35:11 +0000 (07:35 +1300)
committerAndrew Bartlett <abartlet@samba.org>
Sat, 9 Dec 2017 23:47:29 +0000 (00:47 +0100)
Add attribute flag LDB_FLAG_INTERNAL_UNIQUE_VALUE, to request that the
added attribute is unique on the index.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13004

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
lib/ldb/include/ldb_module.h
lib/ldb/ldb_tdb/ldb_index.c
lib/ldb/tests/ldb_mod_op_test.c

index ffa6c2eedb82c81d7474382780f2e9e8e97c0387..fd88c6218ebd0e9b795cccd1f9637b4c729336d0 100644 (file)
@@ -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"
 
index c71e866037c5cccba151bcd2346460de0424fe18..0afeae57a607280491817446c00f0966c247512f 100644 (file)
@@ -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
index 48ad20c7662bab6d49cb8ed6244158aa2c047aa6..cf2288c7bce1cb2411d003b20e3b28f84d72302e 100644 (file)
@@ -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);