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

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