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

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