Blame SOURCES/0026-SYSDB-Add-sysdb_search_with_ts_attr.patch

d6181b
From f46afb46a1705d41e21451cd0adf6981936b21c1 Mon Sep 17 00:00:00 2001
d6181b
From: Jakub Hrozek <jhrozek@redhat.com>
d6181b
Date: Tue, 28 May 2019 14:56:05 +0200
d6181b
Subject: [PATCH 26/48] SYSDB: Add sysdb_search_with_ts_attr
d6181b
d6181b
Adds a new public sysdb call sysdb_search_with_ts_attr() that allows to
d6181b
search on the timestamp cache attributes, but merge back persistent
d6181b
cache attributes. The converse also works, when searching the persistent
d6181b
cache the timestamp attributes or even entries matches only in the
d6181b
timestamp cache are merged.
d6181b
d6181b
What does not work is AND-ed complex filter that contains both
d6181b
attributes from the timestamp cache and the persistent cache because
d6181b
the searches use the same filter, which doesn't match. We would need to
d6181b
decompose the filter ourselves.
d6181b
d6181b
Because matching and merging the results can be time-consuming, two
d6181b
flags are provided:
d6181b
    SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER that only searches the timestamp
d6181b
    cache, but merges back the corresponding entries from the persistent
d6181b
    cache
d6181b
    SYSDB_SEARCH_WITH_TS_ONLY_SYSDB_FILTER that only searches the
d6181b
    persistent cache but merges back the attributes from the timestamp
d6181b
    cache
d6181b
d6181b
Related:
d6181b
https://pagure.io/SSSD/sssd/issue/4012
d6181b
d6181b
Reviewed-by: Sumit Bose <sbose@redhat.com>
d6181b
---
d6181b
 src/db/sysdb.h                         |  12 ++
d6181b
 src/db/sysdb_ops.c                     |  16 +-
d6181b
 src/db/sysdb_private.h                 |  10 ++
d6181b
 src/db/sysdb_search.c                  | 231 +++++++++++++++++++++++--
d6181b
 src/tests/cmocka/test_sysdb_ts_cache.c | 198 +++++++++++++++++++++
d6181b
 5 files changed, 446 insertions(+), 21 deletions(-)
d6181b
d6181b
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
d6181b
index 89b0d9571..28801e030 100644
d6181b
--- a/src/db/sysdb.h
d6181b
+++ b/src/db/sysdb.h
d6181b
@@ -1181,6 +1181,18 @@ int sysdb_search_users(TALLOC_CTX *mem_ctx,
d6181b
                        size_t *msgs_count,
d6181b
                        struct ldb_message ***msgs);
d6181b
 
d6181b
+#define SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER     0x0001
d6181b
+#define SYSDB_SEARCH_WITH_TS_ONLY_SYSDB_FILTER  0x0002
d6181b
+
d6181b
+errno_t sysdb_search_with_ts_attr(TALLOC_CTX *mem_ctx,
d6181b
+                                  struct sss_domain_info *domain,
d6181b
+                                  struct ldb_dn *base_dn,
d6181b
+                                  enum ldb_scope scope,
d6181b
+                                  int optflags,
d6181b
+                                  const char *filter,
d6181b
+                                  const char *attrs[],
d6181b
+                                  struct ldb_result **_result);
d6181b
+
d6181b
 int sysdb_search_users_by_timestamp(TALLOC_CTX *mem_ctx,
d6181b
                                     struct sss_domain_info *domain,
d6181b
                                     const char *sub_filter,
d6181b
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
d6181b
index 59fb227a4..55ba62140 100644
d6181b
--- a/src/db/sysdb_ops.c
d6181b
+++ b/src/db/sysdb_ops.c
d6181b
@@ -261,14 +261,14 @@ done:
d6181b
 
d6181b
 /* =Search-Entry========================================================== */
d6181b
 
d6181b
-static int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx,
d6181b
-                                    struct ldb_context *ldb,
d6181b
-                                    struct ldb_dn *base_dn,
d6181b
-                                    enum ldb_scope scope,
d6181b
-                                    const char *filter,
d6181b
-                                    const char **attrs,
d6181b
-                                    size_t *_msgs_count,
d6181b
-                                    struct ldb_message ***_msgs)
d6181b
+int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx,
d6181b
+                             struct ldb_context *ldb,
d6181b
+                             struct ldb_dn *base_dn,
d6181b
+                             enum ldb_scope scope,
d6181b
+                             const char *filter,
d6181b
+                             const char **attrs,
d6181b
+                             size_t *_msgs_count,
d6181b
+                             struct ldb_message ***_msgs)
d6181b
 {
d6181b
     TALLOC_CTX *tmp_ctx;
d6181b
     struct ldb_result *res;
d6181b
diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
d6181b
index 58544d826..53603b30e 100644
d6181b
--- a/src/db/sysdb_private.h
d6181b
+++ b/src/db/sysdb_private.h
d6181b
@@ -252,6 +252,16 @@ errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx,
d6181b
 struct ldb_result *sss_merge_ldb_results(struct ldb_result *res,
d6181b
                                          struct ldb_result *subres);
d6181b
 
d6181b
+/* Search Entry in an ldb cache */
d6181b
+int sysdb_cache_search_entry(TALLOC_CTX *mem_ctx,
d6181b
+                             struct ldb_context *ldb,
d6181b
+                             struct ldb_dn *base_dn,
d6181b
+                             enum ldb_scope scope,
d6181b
+                             const char *filter,
d6181b
+                             const char **attrs,
d6181b
+                             size_t *_msgs_count,
d6181b
+                             struct ldb_message ***_msgs);
d6181b
+
d6181b
 /* Search Entry in the timestamp cache */
d6181b
 int sysdb_search_ts_entry(TALLOC_CTX *mem_ctx,
d6181b
                           struct sysdb_ctx *sysdb,
d6181b
diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
d6181b
index f0918bf9a..a71c43112 100644
d6181b
--- a/src/db/sysdb_search.c
d6181b
+++ b/src/db/sysdb_search.c
d6181b
@@ -68,6 +68,29 @@ static errno_t merge_ts_attr(struct ldb_message *ts_msg,
d6181b
     return EOK;
d6181b
 }
d6181b
 
d6181b
+static errno_t merge_all_ts_attrs(struct ldb_message *ts_msg,
d6181b
+                                  struct ldb_message *sysdb_msg,
d6181b
+                                  const char *want_attrs[])
d6181b
+{
d6181b
+    int ret;
d6181b
+
d6181b
+    /* Deliberately start from 2 in order to not merge
d6181b
+     * objectclass/objectcategory and avoid breaking MPGs where the OC might
d6181b
+     * be made up
d6181b
+     */
d6181b
+    for (size_t c = 2; sysdb_ts_cache_attrs[c]; c++) {
d6181b
+        ret = merge_ts_attr(ts_msg, sysdb_msg,
d6181b
+                            sysdb_ts_cache_attrs[c], want_attrs);
d6181b
+        if (ret != EOK) {
d6181b
+            DEBUG(SSSDBG_MINOR_FAILURE,
d6181b
+                  "Cannot merge ts attr %s\n", sysdb_ts_cache_attrs[c]);
d6181b
+            return ret;
d6181b
+        }
d6181b
+    }
d6181b
+
d6181b
+    return EOK;
d6181b
+}
d6181b
+
d6181b
 static errno_t merge_msg_ts_attrs(struct sysdb_ctx *sysdb,
d6181b
                                   struct ldb_message *sysdb_msg,
d6181b
                                   const char *attrs[])
d6181b
@@ -114,21 +137,46 @@ static errno_t merge_msg_ts_attrs(struct sysdb_ctx *sysdb,
d6181b
         return EIO;
d6181b
     }
d6181b
 
d6181b
-    /* Deliberately start from 2 in order to not merge
d6181b
-     * objectclass/objectcategory and avoid breaking MPGs where the OC might
d6181b
-     * be made up
d6181b
-     */
d6181b
-    for (size_t c = 2; sysdb_ts_cache_attrs[c]; c++) {
d6181b
-        ret = merge_ts_attr(ts_msgs[0], sysdb_msg,
d6181b
-                            sysdb_ts_cache_attrs[c], attrs);
d6181b
-        if (ret != EOK) {
d6181b
-            DEBUG(SSSDBG_MINOR_FAILURE,
d6181b
-                  "Cannot merge ts attr %s\n", sysdb_ts_cache_attrs[c]);
d6181b
-            goto done;
d6181b
-        }
d6181b
+    ret = merge_all_ts_attrs(ts_msgs[0], sysdb_msg, attrs);
d6181b
+done:
d6181b
+    talloc_zfree(tmp_ctx);
d6181b
+    return ret;
d6181b
+}
d6181b
+
d6181b
+static errno_t merge_msg_sysdb_attrs(TALLOC_CTX *mem_ctx,
d6181b
+                                     struct sysdb_ctx *sysdb,
d6181b
+                                     struct ldb_message *ts_msg,
d6181b
+                                     struct ldb_message **_sysdb_msg,
d6181b
+                                     const char *attrs[])
d6181b
+{
d6181b
+    errno_t ret;
d6181b
+    TALLOC_CTX *tmp_ctx;
d6181b
+    size_t msgs_count;
d6181b
+    struct ldb_message **sysdb_msgs;
d6181b
+
d6181b
+    tmp_ctx = talloc_new(NULL);
d6181b
+    if (tmp_ctx == NULL) {
d6181b
+        return ENOMEM;
d6181b
     }
d6181b
 
d6181b
-    ret = EOK;
d6181b
+    ret = sysdb_cache_search_entry(tmp_ctx, sysdb->ldb, ts_msg->dn, LDB_SCOPE_BASE,
d6181b
+                                   NULL, attrs, &msgs_count, &sysdb_msgs);
d6181b
+    if (ret != EOK) {
d6181b
+        goto done;
d6181b
+    }
d6181b
+
d6181b
+    if (msgs_count != 1) {
d6181b
+        DEBUG(SSSDBG_CRIT_FAILURE,
d6181b
+              "Expected 1 result for base search, got %zu\n", msgs_count);
d6181b
+        goto done;
d6181b
+    }
d6181b
+
d6181b
+    ret = merge_all_ts_attrs(ts_msg, sysdb_msgs[0], attrs);
d6181b
+    if (ret != EOK) {
d6181b
+        goto done;
d6181b
+    }
d6181b
+
d6181b
+    *_sysdb_msg = talloc_steal(mem_ctx, sysdb_msgs[0]);
d6181b
 done:
d6181b
     talloc_zfree(tmp_ctx);
d6181b
     return ret;
d6181b
@@ -166,6 +214,50 @@ errno_t sysdb_merge_res_ts_attrs(struct sysdb_ctx *ctx,
d6181b
     return EOK;
d6181b
 }
d6181b
 
d6181b
+static errno_t merge_res_sysdb_attrs(TALLOC_CTX *mem_ctx,
d6181b
+                                     struct sysdb_ctx *ctx,
d6181b
+                                     struct ldb_result *ts_res,
d6181b
+                                     struct ldb_result **_ts_cache_res,
d6181b
+                                     const char *attrs[])
d6181b
+{
d6181b
+    errno_t ret;
d6181b
+    struct ldb_result *ts_cache_res = NULL;
d6181b
+
d6181b
+    if (ts_res == NULL || ctx->ldb_ts == NULL) {
d6181b
+        return EOK;
d6181b
+    }
d6181b
+
d6181b
+    ts_cache_res = talloc_zero(mem_ctx, struct ldb_result);
d6181b
+    if (ts_cache_res == NULL) {
d6181b
+        return ENOMEM;
d6181b
+    }
d6181b
+    ts_cache_res->count = ts_res->count;
d6181b
+    ts_cache_res->msgs = talloc_zero_array(ts_cache_res,
d6181b
+                                           struct ldb_message *,
d6181b
+                                           ts_res->count);
d6181b
+    if (ts_cache_res->msgs == NULL) {
d6181b
+        talloc_free(ts_cache_res);
d6181b
+        return ENOMEM;
d6181b
+    }
d6181b
+
d6181b
+    for (size_t c = 0; c < ts_res->count; c++) {
d6181b
+        ret = merge_msg_sysdb_attrs(ts_cache_res->msgs,
d6181b
+                                    ctx,
d6181b
+                                    ts_res->msgs[c],
d6181b
+                                    &ts_cache_res->msgs[c], attrs);
d6181b
+        if (ret != EOK) {
d6181b
+            DEBUG(SSSDBG_MINOR_FAILURE,
d6181b
+                  "Cannot merge sysdb cache values for %s\n",
d6181b
+                  ldb_dn_get_linearized(ts_res->msgs[c]->dn));
d6181b
+            /* non-fatal, we just get only the non-timestamp attrs */
d6181b
+            continue;
d6181b
+        }
d6181b
+    }
d6181b
+
d6181b
+    *_ts_cache_res = ts_cache_res;
d6181b
+    return EOK;
d6181b
+}
d6181b
+
d6181b
 errno_t sysdb_merge_msg_list_ts_attrs(struct sysdb_ctx *ctx,
d6181b
                                       size_t msgs_count,
d6181b
                                       struct ldb_message **msgs,
d6181b
@@ -543,6 +635,119 @@ done:
d6181b
     return ret;
d6181b
 }
d6181b
 
d6181b
+errno_t sysdb_search_with_ts_attr(TALLOC_CTX *mem_ctx,
d6181b
+                                  struct sss_domain_info *domain,
d6181b
+                                  struct ldb_dn *base_dn,
d6181b
+                                  enum ldb_scope scope,
d6181b
+                                  int optflags,
d6181b
+                                  const char *filter,
d6181b
+                                  const char *attrs[],
d6181b
+                                  struct ldb_result **_res)
d6181b
+{
d6181b
+    TALLOC_CTX *tmp_ctx = NULL;
d6181b
+    struct ldb_result *res;
d6181b
+    errno_t ret;
d6181b
+    struct ldb_message **ts_msgs = NULL;
d6181b
+    struct ldb_result *ts_cache_res = NULL;
d6181b
+    size_t ts_count;
d6181b
+
d6181b
+    if (filter == NULL) {
d6181b
+        return EINVAL;
d6181b
+    }
d6181b
+
d6181b
+    tmp_ctx = talloc_new(NULL);
d6181b
+    if (tmp_ctx == NULL) {
d6181b
+        return ENOMEM;
d6181b
+    }
d6181b
+
d6181b
+    res = talloc_zero(tmp_ctx, struct ldb_result);
d6181b
+    if (res == NULL) {
d6181b
+        ret = ENOMEM;
d6181b
+        goto done;
d6181b
+    }
d6181b
+
d6181b
+    if (optflags & SYSDB_SEARCH_WITH_TS_ONLY_SYSDB_FILTER) {
d6181b
+        /* We only care about searching the persistent db */
d6181b
+        ts_cache_res = talloc_zero(tmp_ctx, struct ldb_result);
d6181b
+        if (ts_cache_res == NULL) {
d6181b
+            ret = ENOMEM;
d6181b
+            goto done;
d6181b
+        }
d6181b
+        ts_cache_res->count = 0;
d6181b
+        ts_cache_res->msgs = NULL;
d6181b
+    } else {
d6181b
+        /* Because the timestamp database does not contain all the
d6181b
+         * attributes, we need to search the persistent db for each
d6181b
+         * of the entries found and merge the results
d6181b
+         */
d6181b
+        struct ldb_result ts_res;
d6181b
+
d6181b
+        /* We assume that some of the attributes are more up-to-date in
d6181b
+         * timestamps db and we're supposed to search by them, so let's
d6181b
+         * first search the timestamp db
d6181b
+         */
d6181b
+        ret = sysdb_search_ts_entry(tmp_ctx, domain->sysdb, base_dn,
d6181b
+                                    scope, filter, attrs,
d6181b
+                                    &ts_count, &ts_msgs);
d6181b
+        if (ret == ENOENT) {
d6181b
+            ts_count = 0;
d6181b
+        } else if (ret != EOK) {
d6181b
+            goto done;
d6181b
+        }
d6181b
+
d6181b
+        memset(&ts_res, 0, sizeof(struct ldb_result));
d6181b
+        ts_res.count = ts_count;
d6181b
+        ts_res.msgs = ts_msgs;
d6181b
+
d6181b
+        /* Overlay the results from the main cache with the ts attrs */
d6181b
+        ret = merge_res_sysdb_attrs(tmp_ctx,
d6181b
+                                    domain->sysdb,
d6181b
+                                    &ts_res,
d6181b
+                                    &ts_cache_res,
d6181b
+                                    attrs);
d6181b
+        if (ret != EOK) {
d6181b
+            goto done;
d6181b
+        }
d6181b
+    }
d6181b
+
d6181b
+    if (optflags & SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER) {
d6181b
+        /* The filter only contains timestamp attrs, no need to search the
d6181b
+         * persistent db
d6181b
+         */
d6181b
+        if (ts_cache_res) {
d6181b
+            res->count = ts_cache_res->count;
d6181b
+            res->msgs = talloc_steal(res, ts_cache_res->msgs);
d6181b
+        }
d6181b
+    } else {
d6181b
+        /* Because some of the attributes being searched might exist in the persistent
d6181b
+         * database only, we also search the persistent db
d6181b
+         */
d6181b
+        size_t count;
d6181b
+
d6181b
+        ret = sysdb_search_entry(res, domain->sysdb, base_dn, scope,
d6181b
+                                 filter, attrs, &count, &res->msgs);
d6181b
+        if (ret == ENOENT) {
d6181b
+            res->count = 0;
d6181b
+        } else if (ret != EOK) {
d6181b
+            goto done;
d6181b
+        }
d6181b
+        res->count = count; /* Just to cleanly assign size_t to unsigned */
d6181b
+
d6181b
+        res = sss_merge_ldb_results(res, ts_cache_res);
d6181b
+        if (res == NULL) {
d6181b
+            ret = ENOMEM;
d6181b
+            goto done;
d6181b
+        }
d6181b
+    }
d6181b
+
d6181b
+    *_res = talloc_steal(mem_ctx, res);
d6181b
+    ret = EOK;
d6181b
+
d6181b
+done:
d6181b
+    talloc_zfree(tmp_ctx);
d6181b
+    return ret;
d6181b
+}
d6181b
+
d6181b
 static errno_t sysdb_enum_dn_filter(TALLOC_CTX *mem_ctx,
d6181b
                                     struct ldb_result *ts_res,
d6181b
                                     const char *name_filter,
d6181b
diff --git a/src/tests/cmocka/test_sysdb_ts_cache.c b/src/tests/cmocka/test_sysdb_ts_cache.c
d6181b
index fdf9935da..d2296d1b8 100644
d6181b
--- a/src/tests/cmocka/test_sysdb_ts_cache.c
d6181b
+++ b/src/tests/cmocka/test_sysdb_ts_cache.c
d6181b
@@ -1411,6 +1411,201 @@ static void test_sysdb_zero_now(void **state)
d6181b
     assert_true(cache_expire_ts > TEST_CACHE_TIMEOUT);
d6181b
 }
d6181b
 
d6181b
+static void test_sysdb_search_with_ts(void **state)
d6181b
+{
d6181b
+    int ret;
d6181b
+    struct sysdb_ts_test_ctx *test_ctx = talloc_get_type_abort(*state,
d6181b
+                                                     struct sysdb_ts_test_ctx);
d6181b
+    struct ldb_result *res = NULL;
d6181b
+    struct ldb_dn *base_dn;
d6181b
+    const char *attrs[] = { SYSDB_NAME,
d6181b
+                            SYSDB_OBJECTCATEGORY,
d6181b
+                            SYSDB_GIDNUM,
d6181b
+                            SYSDB_CACHE_EXPIRE,
d6181b
+                            NULL };
d6181b
+    struct sysdb_attrs *group_attrs = NULL;
d6181b
+    char *filter;
d6181b
+    uint64_t cache_expire_sysdb;
d6181b
+    uint64_t cache_expire_ts;
d6181b
+    size_t count;
d6181b
+    struct ldb_message **msgs;
d6181b
+
d6181b
+    base_dn = sysdb_base_dn(test_ctx->tctx->dom->sysdb, test_ctx);
d6181b
+    assert_non_null(base_dn);
d6181b
+
d6181b
+    /* Nothing must be stored in either cache at the beginning of the test */
d6181b
+    ret = sysdb_search_with_ts_attr(test_ctx,
d6181b
+                                    test_ctx->tctx->dom,
d6181b
+                                    base_dn,
d6181b
+                                    LDB_SCOPE_SUBTREE,
d6181b
+                                    0,
d6181b
+                                    SYSDB_NAME"=*",
d6181b
+                                    attrs,
d6181b
+                                    &res;;
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+    assert_int_equal(res->count, 0);
d6181b
+    talloc_free(res);
d6181b
+
d6181b
+    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
d6181b
+    assert_non_null(group_attrs);
d6181b
+
d6181b
+    ret = sysdb_store_group(test_ctx->tctx->dom,
d6181b
+                            TEST_GROUP_NAME,
d6181b
+                            TEST_GROUP_GID,
d6181b
+                            group_attrs,
d6181b
+                            TEST_CACHE_TIMEOUT,
d6181b
+                            TEST_NOW_1);
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+    talloc_zfree(group_attrs);
d6181b
+
d6181b
+    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
d6181b
+    assert_non_null(group_attrs);
d6181b
+
d6181b
+    ret = sysdb_store_group(test_ctx->tctx->dom,
d6181b
+                            TEST_GROUP_NAME_2,
d6181b
+                            TEST_GROUP_GID_2,
d6181b
+                            group_attrs,
d6181b
+                            TEST_CACHE_TIMEOUT,
d6181b
+                            TEST_NOW_2);
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+    talloc_zfree(group_attrs);
d6181b
+
d6181b
+    /* Bump the timestamps in the cache so that the ts cache
d6181b
+     * and sysdb differ
d6181b
+     */
d6181b
+
d6181b
+    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
d6181b
+    assert_non_null(group_attrs);
d6181b
+
d6181b
+    ret = sysdb_store_group(test_ctx->tctx->dom,
d6181b
+                            TEST_GROUP_NAME,
d6181b
+                            TEST_GROUP_GID,
d6181b
+                            group_attrs,
d6181b
+                            TEST_CACHE_TIMEOUT,
d6181b
+                            TEST_NOW_3);
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+
d6181b
+    talloc_zfree(group_attrs);
d6181b
+
d6181b
+
d6181b
+    group_attrs = create_modstamp_attrs(test_ctx, TEST_MODSTAMP_1);
d6181b
+    assert_non_null(group_attrs);
d6181b
+
d6181b
+    ret = sysdb_store_group(test_ctx->tctx->dom,
d6181b
+                            TEST_GROUP_NAME_2,
d6181b
+                            TEST_GROUP_GID_2,
d6181b
+                            group_attrs,
d6181b
+                            TEST_CACHE_TIMEOUT,
d6181b
+                            TEST_NOW_4);
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+
d6181b
+    talloc_zfree(group_attrs);
d6181b
+
d6181b
+    get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME,
d6181b
+                           &cache_expire_sysdb, &cache_expire_ts);
d6181b
+    assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_1);
d6181b
+    assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_3);
d6181b
+
d6181b
+    get_gr_timestamp_attrs(test_ctx, TEST_GROUP_NAME_2,
d6181b
+                           &cache_expire_sysdb, &cache_expire_ts);
d6181b
+    assert_int_equal(cache_expire_sysdb, TEST_CACHE_TIMEOUT + TEST_NOW_2);
d6181b
+    assert_int_equal(cache_expire_ts, TEST_CACHE_TIMEOUT + TEST_NOW_4);
d6181b
+
d6181b
+    /* Search for groups that don't expire until TEST_NOW_4 */
d6181b
+    filter = talloc_asprintf(test_ctx, SYSDB_CACHE_EXPIRE">=%d", TEST_NOW_4);
d6181b
+    assert_non_null(filter);
d6181b
+
d6181b
+    /* This search should yield only one group (so, it needs to search the ts
d6181b
+     * cache to hit the TEST_NOW_4), but should return attributes merged from
d6181b
+     * both caches
d6181b
+     */
d6181b
+    ret = sysdb_search_with_ts_attr(test_ctx,
d6181b
+                                    test_ctx->tctx->dom,
d6181b
+                                    base_dn,
d6181b
+                                    LDB_SCOPE_SUBTREE,
d6181b
+                                    0,
d6181b
+                                    filter,
d6181b
+                                    attrs,
d6181b
+                                    &res;;
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+    assert_int_equal(res->count, 1);
d6181b
+    assert_int_equal(TEST_GROUP_GID_2, ldb_msg_find_attr_as_uint64(res->msgs[0],
d6181b
+                                                                   SYSDB_GIDNUM, 0));
d6181b
+    talloc_free(res);
d6181b
+
d6181b
+    /*
d6181b
+     * In contrast, sysdb_search_entry merges the timestamp attributes, but does
d6181b
+     * not search the timestamp cache
d6181b
+     */
d6181b
+    ret = sysdb_search_entry(test_ctx,
d6181b
+                             test_ctx->tctx->dom->sysdb,
d6181b
+                             base_dn,
d6181b
+                             LDB_SCOPE_SUBTREE,
d6181b
+                             filter,
d6181b
+                             attrs,
d6181b
+                             &count,
d6181b
+                             &msgs);
d6181b
+    assert_int_equal(ret, ENOENT);
d6181b
+
d6181b
+    /* Should get the same result when searching by ts attrs only */
d6181b
+    ret = sysdb_search_with_ts_attr(test_ctx,
d6181b
+                                    test_ctx->tctx->dom,
d6181b
+                                    base_dn,
d6181b
+                                    LDB_SCOPE_SUBTREE,
d6181b
+                                    SYSDB_SEARCH_WITH_TS_ONLY_TS_FILTER,
d6181b
+                                    filter,
d6181b
+                                    attrs,
d6181b
+                                    &res;;
d6181b
+    talloc_zfree(filter);
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+    assert_int_equal(res->count, 1);
d6181b
+    assert_int_equal(TEST_GROUP_GID_2, ldb_msg_find_attr_as_uint64(res->msgs[0],
d6181b
+                                                                   SYSDB_GIDNUM, 0));
d6181b
+    talloc_free(res);
d6181b
+
d6181b
+    /* We can also search in sysdb only as well, we should get back ts attrs */
d6181b
+    filter = talloc_asprintf(test_ctx, SYSDB_GIDNUM"=%d", TEST_GROUP_GID);
d6181b
+    assert_non_null(filter);
d6181b
+
d6181b
+    ret = sysdb_search_with_ts_attr(test_ctx,
d6181b
+                                    test_ctx->tctx->dom,
d6181b
+                                    base_dn,
d6181b
+                                    LDB_SCOPE_SUBTREE,
d6181b
+                                    SYSDB_SEARCH_WITH_TS_ONLY_SYSDB_FILTER,
d6181b
+                                    filter,
d6181b
+                                    attrs,
d6181b
+                                    &res;;
d6181b
+    talloc_zfree(filter);
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+    assert_int_equal(res->count, 1);
d6181b
+    assert_int_equal(TEST_GROUP_GID, ldb_msg_find_attr_as_uint64(res->msgs[0],
d6181b
+                                                                 SYSDB_GIDNUM, 0));
d6181b
+    assert_int_equal(TEST_CACHE_TIMEOUT + TEST_NOW_3,
d6181b
+                     ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_CACHE_EXPIRE, 0));
d6181b
+    talloc_free(res);
d6181b
+
d6181b
+    /* We can also search in both using an OR-filter. Note that an AND-filter is not possible
d6181b
+     * unless we deconstruct the filter..
d6181b
+     */
d6181b
+    filter = talloc_asprintf(test_ctx, "(|("SYSDB_GIDNUM"=%d)"
d6181b
+                                         "("SYSDB_CACHE_EXPIRE">=%d))",
d6181b
+                                         TEST_GROUP_GID, TEST_NOW_4);
d6181b
+    assert_non_null(filter);
d6181b
+
d6181b
+    ret = sysdb_search_with_ts_attr(test_ctx,
d6181b
+                                    test_ctx->tctx->dom,
d6181b
+                                    base_dn,
d6181b
+                                    LDB_SCOPE_SUBTREE,
d6181b
+                                    0,
d6181b
+                                    filter,
d6181b
+                                    attrs,
d6181b
+                                    &res;;
d6181b
+    talloc_zfree(filter);
d6181b
+    assert_int_equal(ret, EOK);
d6181b
+    assert_int_equal(res->count, 2);
d6181b
+    talloc_free(res);
d6181b
+}
d6181b
+
d6181b
 int main(int argc, const char *argv[])
d6181b
 {
d6181b
     int rv;
d6181b
@@ -1462,6 +1657,9 @@ int main(int argc, const char *argv[])
d6181b
         cmocka_unit_test_setup_teardown(test_sysdb_zero_now,
d6181b
                                         test_sysdb_ts_setup,
d6181b
                                         test_sysdb_ts_teardown),
d6181b
+        cmocka_unit_test_setup_teardown(test_sysdb_search_with_ts,
d6181b
+                                        test_sysdb_ts_setup,
d6181b
+                                        test_sysdb_ts_teardown),
d6181b
     };
d6181b
 
d6181b
     /* Set debug level to invalid value so we can decide if -d 0 was used. */
d6181b
-- 
d6181b
2.20.1
d6181b