From f6e92852e82ebc460a4ed78733cd346cbe823520 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Fri, 23 Jan 2015 15:32:55 +0100
Subject: [PATCH 04/12] cache_req: add support for user by uid

---
 src/responder/common/responder_cache_req.c  | 105 +++++++-
 src/responder/common/responder_cache_req.h  |  17 +-
 src/responder/ifp/ifpsrv_cmd.c              |   4 +-
 src/tests/cmocka/test_responder_cache_req.c | 370 +++++++++++++++++++++++++++-
 4 files changed, 485 insertions(+), 11 deletions(-)

diff --git a/src/responder/common/responder_cache_req.c b/src/responder/common/responder_cache_req.c
index b60f91c2c48ffd3b0428bc654d3668ebaf9c7b26..5eb23f8dd5aecf2e1f2e055a4afecac1e9819be2 100644
--- a/src/responder/common/responder_cache_req.c
+++ b/src/responder/common/responder_cache_req.c
@@ -33,6 +33,7 @@ struct cache_req_input {
 
     /* Provided input. */
     const char *orig_name;
+    uint32_t id;
 
     /* Data Provider request type resolved from @type.
      * FIXME: This is currently needed for data provider calls. We should
@@ -54,7 +55,8 @@ struct cache_req_input {
 struct cache_req_input *
 cache_req_input_create(TALLOC_CTX *mem_ctx,
                        enum cache_req_type type,
-                       const char *name)
+                       const char *name,
+                       uint32_t id)
 {
     struct cache_req_input *input;
 
@@ -79,11 +81,20 @@ cache_req_input_create(TALLOC_CTX *mem_ctx,
             goto fail;
         }
         break;
+    case CACHE_REQ_USER_BY_ID:
+        if (id == 0) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0!\n");
+            goto fail;
+        }
+
+        input->id = id;
+        break;
     }
 
     /* Resolve Data Provider request type. */
     switch (type) {
     case CACHE_REQ_USER_BY_NAME:
+    case CACHE_REQ_USER_BY_ID:
         input->dp_type = SSS_DP_USER;
         break;
 
@@ -140,6 +151,14 @@ cache_req_input_set_domain(struct cache_req_input *input,
         }
 
         break;
+
+    case CACHE_REQ_USER_BY_ID:
+        fqn = talloc_asprintf(tmp_ctx, "UID:%d@%s", input->id, domain->name);
+        if (fqn == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        break;
     }
 
     input->domain = domain;
@@ -165,6 +184,9 @@ static errno_t cache_req_check_ncache(struct cache_req_input *input,
         ret = sss_ncache_check_user(ncache, neg_timeout,
                                     input->domain, input->dom_objname);
         break;
+    case CACHE_REQ_USER_BY_ID:
+        ret = sss_ncache_check_uid(ncache, neg_timeout, input->id);
+        break;
     default:
         ret = EINVAL;
         DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported cache request type\n");
@@ -190,6 +212,43 @@ static void cache_req_add_to_ncache(struct cache_req_input *input,
         ret = sss_ncache_set_user(ncache, false, input->domain,
                                   input->dom_objname);
         break;
+    case CACHE_REQ_USER_BY_ID:
+        /* Nothing to do. Those types must be unique among all domains so
+         * the don't contain domain part. Therefore they must be set only
+         * if all domains are search and the entry is not found. */
+        ret = EOK;
+        break;
+    default:
+        ret = EINVAL;
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported cache request type\n");
+        break;
+    }
+
+    if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set negcache for [%s] [%d]: %s\n",
+              input->debug_fqn, ret, sss_strerror(ret));
+
+        /* not fatal */
+    }
+
+    return;
+}
+
+static void cache_req_add_to_ncache_global(struct cache_req_input *input,
+                                           struct sss_nc_ctx *ncache)
+{
+    errno_t ret;
+
+    switch (input->type) {
+    case CACHE_REQ_USER_BY_NAME:
+    case CACHE_REQ_INITGROUPS:
+        /* Nothing to do. Those types are already in ncache for selected
+         * domains. */
+        ret = EOK;
+        break;
+    case CACHE_REQ_USER_BY_ID:
+        ret = sss_ncache_set_uid(ncache, false, input->id);
+        break;
     default:
         ret = EINVAL;
         DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported cache request type\n");
@@ -222,6 +281,11 @@ static errno_t cache_req_get_object(TALLOC_CTX *mem_ctx,
         ret = sysdb_getpwnam_with_views(mem_ctx, input->domain,
                                         input->dom_objname, &result);
         break;
+    case CACHE_REQ_USER_BY_ID:
+        one_item_only = true;
+        ret = sysdb_getpwuid_with_views(mem_ctx, input->domain,
+                                        input->id, &result);
+        break;
     case CACHE_REQ_INITGROUPS:
         one_item_only = false;
         ret = sysdb_initgroups_with_views(mem_ctx, input->domain,
@@ -385,7 +449,8 @@ static errno_t cache_req_cache_check(struct tevent_req *req)
         subreq = sss_dp_get_account_send(state, state->rctx,
                                          state->input->domain, true,
                                          state->input->dp_type,
-                                         state->input->dom_objname, 0, NULL);
+                                         state->input->dom_objname,
+                                         state->input->id, NULL);
         if (subreq == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory sending out-of-band "
                                        "data provider request\n");
@@ -406,8 +471,8 @@ static errno_t cache_req_cache_check(struct tevent_req *req)
         subreq = sss_dp_get_account_send(state, state->rctx,
                                          state->input->domain, true,
                                          state->input->dp_type,
-                                         state->input->dom_objname, 0,
-                                         extra_flag);
+                                         state->input->dom_objname,
+                                         state->input->id, extra_flag);
         if (subreq == NULL) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Out of memory sending data provider request\n");
@@ -612,6 +677,12 @@ static errno_t cache_req_next_domain(struct tevent_req *req)
         return EAGAIN;
     }
 
+    /* If the object searched has to be unique among all maintained domains,
+     * we have to add it into negative cache here when all domains have
+     * been searched. */
+
+    cache_req_add_to_ncache_global(state->input, state->ncache);
+
     return ENOENT;
 }
 
@@ -700,7 +771,29 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx,
 {
     struct cache_req_input *input;
 
-    input = cache_req_input_create(mem_ctx, CACHE_REQ_USER_BY_NAME, name);
+    input = cache_req_input_create(mem_ctx, CACHE_REQ_USER_BY_NAME, name, 0);
+    if (input == NULL) {
+        return NULL;
+    }
+
+    return cache_req_steal_input_and_send(mem_ctx, ev, rctx, ncache,
+                                          neg_timeout, cache_refresh_percent,
+                                          domain, input);
+}
+
+struct tevent_req *
+cache_req_user_by_id_send(TALLOC_CTX *mem_ctx,
+                          struct tevent_context *ev,
+                          struct resp_ctx *rctx,
+                          struct sss_nc_ctx *ncache,
+                          int neg_timeout,
+                          int cache_refresh_percent,
+                          const char *domain,
+                          uid_t uid)
+{
+    struct cache_req_input *input;
+
+    input = cache_req_input_create(mem_ctx, CACHE_REQ_USER_BY_ID, NULL, uid);
     if (input == NULL) {
         return NULL;
     }
@@ -722,7 +815,7 @@ cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx,
 {
     struct cache_req_input *input;
 
-    input = cache_req_input_create(mem_ctx, CACHE_REQ_INITGROUPS, name);
+    input = cache_req_input_create(mem_ctx, CACHE_REQ_INITGROUPS, name, 0);
     if (input == NULL) {
         return NULL;
     }
diff --git a/src/responder/common/responder_cache_req.h b/src/responder/common/responder_cache_req.h
index 3d11cd2344ee776c3b435bc16b75dc375ac87c5c..3ebcd1e8e8a6fd095b2fba8f8cd34793f825f394 100644
--- a/src/responder/common/responder_cache_req.h
+++ b/src/responder/common/responder_cache_req.h
@@ -29,6 +29,7 @@
 
 enum cache_req_type {
     CACHE_REQ_USER_BY_NAME,
+    CACHE_REQ_USER_BY_ID,
     CACHE_REQ_INITGROUPS
 };
 
@@ -37,7 +38,8 @@ struct cache_req_input;
 struct cache_req_input *
 cache_req_input_create(TALLOC_CTX *mem_ctx,
                        enum cache_req_type type,
-                       const char *name);
+                       const char *name,
+                       uint32_t id);
 
 /**
  * Currently only SSS_DP_USER and SSS_DP_INITGROUPS are supported.
@@ -72,6 +74,19 @@ cache_req_user_by_name_send(TALLOC_CTX *mem_ctx,
     cache_req_recv(mem_ctx, req, _result, _domain)
 
 struct tevent_req *
+cache_req_user_by_id_send(TALLOC_CTX *mem_ctx,
+                          struct tevent_context *ev,
+                          struct resp_ctx *rctx,
+                          struct sss_nc_ctx *ncache,
+                          int neg_timeout,
+                          int cache_refresh_percent,
+                          const char *domain,
+                          uid_t uid);
+
+#define cache_req_user_by_id_recv(mem_ctx, req, _result, _domain) \
+    cache_req_recv(mem_ctx, req, _result, _domain)
+
+struct tevent_req *
 cache_req_initgr_by_name_send(TALLOC_CTX *mem_ctx,
                               struct tevent_context *ev,
                               struct resp_ctx *rctx,
diff --git a/src/responder/ifp/ifpsrv_cmd.c b/src/responder/ifp/ifpsrv_cmd.c
index b57a33a8d03c0ce37bde791666b930817f30b7b9..0a4bd0530eab217d399d4a6f8a352824772815f0 100644
--- a/src/responder/ifp/ifpsrv_cmd.c
+++ b/src/responder/ifp/ifpsrv_cmd.c
@@ -494,11 +494,11 @@ ifp_user_get_attr_lookup(struct tevent_req *subreq)
     switch (state->search_type) {
     case SSS_DP_USER:
         input = cache_req_input_create(state, CACHE_REQ_USER_BY_NAME,
-                                       state->name);
+                                       state->name, 0);
         break;
     case SSS_DP_INITGROUPS:
         input = cache_req_input_create(state, CACHE_REQ_INITGROUPS,
-                                       state->name);
+                                       state->name, 0);
         break;
     default:
         DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported search type [%d]!\n",
diff --git a/src/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c
index bc551f8a3518aa777febd19cfe3947868a19b39c..64390e989cb74e440c93038db7b2487446353442 100644
--- a/src/tests/cmocka/test_responder_cache_req.c
+++ b/src/tests/cmocka/test_responder_cache_req.c
@@ -34,6 +34,7 @@
 #define TEST_ID_PROVIDER "ldap"
 
 #define TEST_USER_NAME "test-user"
+#define TEST_USER_ID 1000
 
 #define new_single_domain_test(test) \
     unit_test_setup_teardown(test_ ## test, \
@@ -89,7 +90,7 @@ __wrap_sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
 
     if (ctx->create_user) {
         ret = sysdb_store_user(ctx->tctx->dom, TEST_USER_NAME, "pwd",
-                               1000, 1000, NULL, NULL, NULL,
+                               TEST_USER_ID, 1000, NULL, NULL, NULL,
                                "cn=test-user,dc=test", NULL, NULL,
                                1000, time(NULL));
         assert_int_equal(ret, EOK);
@@ -111,6 +112,19 @@ static void cache_req_user_by_name_test_done(struct tevent_req *req)
     ctx->tctx->done = true;
 }
 
+static void cache_req_user_by_id_test_done(struct tevent_req *req)
+{
+    struct cache_req_test_ctx *ctx = NULL;
+
+    ctx = tevent_req_callback_data(req, struct cache_req_test_ctx);
+
+    ctx->tctx->error = cache_req_user_by_id_recv(ctx, req,
+                                                 &ctx->result, &ctx->domain);
+    talloc_zfree(req);
+
+    ctx->tctx->done = true;
+}
+
 void test_single_domain_setup(void **state)
 {
     struct cache_req_test_ctx *test_ctx = NULL;
@@ -484,6 +498,349 @@ void test_user_by_name_missing_notfound(void **state)
     assert_true(test_ctx->dp_called);
 }
 
+void test_user_by_id_multiple_domains_found(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    struct sss_domain_info *domain = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    const char *name = TEST_USER_NAME;
+    uid_t uid = TEST_USER_ID;
+    const char *ldbname = NULL;
+    uid_t ldbuid;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    domain = find_domain_by_name(test_ctx->tctx->dom,
+                                 "responder_cache_req_test_d", true);
+    assert_non_null(domain);
+
+    ret = sysdb_store_user(domain, name, "pwd", uid, 1000,
+                           NULL, NULL, NULL, "cn=test-user,dc=test", NULL,
+                           NULL, 1000, time(NULL));
+    assert_int_equal(ret, EOK);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+    will_return_always(sss_dp_get_account_recv, 0);
+
+    req = cache_req_user_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                    test_ctx->rctx, test_ctx->ncache, 10, 0,
+                                    NULL, uid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_user_by_id_test_done, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+    assert_true(check_leaks_pop(req_mem_ctx));
+
+    assert_true(test_ctx->dp_called);
+
+    assert_non_null(test_ctx->result);
+    assert_int_equal(test_ctx->result->count, 1);
+    assert_non_null(test_ctx->result->msgs);
+    assert_non_null(test_ctx->result->msgs[0]);
+
+    ldbname = ldb_msg_find_attr_as_string(test_ctx->result->msgs[0],
+                                          SYSDB_NAME, NULL);
+    assert_non_null(ldbname);
+    assert_string_equal(ldbname, name);
+
+    ldbuid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_UIDNUM, 0);
+    assert_int_equal(ldbuid, uid);
+
+    assert_non_null(test_ctx->domain);
+    assert_string_equal(domain->name, test_ctx->domain->name);
+}
+
+void test_user_by_id_multiple_domains_notfound(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    uid_t uid = TEST_USER_ID;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    will_return_always(__wrap_sss_dp_get_account_send, test_ctx);
+    will_return_always(sss_dp_get_account_recv, 0);
+
+    req = cache_req_user_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                    test_ctx->rctx, test_ctx->ncache, 10, 0,
+                                    NULL, uid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_user_by_id_test_done, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ENOENT);
+    assert_true(check_leaks_pop(req_mem_ctx));
+
+    assert_true(test_ctx->dp_called);
+}
+
+void test_user_by_id_cache_valid(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    const char *name = TEST_USER_NAME;
+    const char *ldbname = NULL;
+    uid_t uid = TEST_USER_ID;
+    uid_t ldbuid;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    ret = sysdb_store_user(test_ctx->tctx->dom, name, "pwd", uid, 1000,
+                           NULL, NULL, NULL, "cn=test-user,dc=test", NULL,
+                           NULL, 1000, time(NULL));
+    assert_int_equal(ret, EOK);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    req = cache_req_user_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                    test_ctx->rctx, test_ctx->ncache, 10, 0,
+                                    test_ctx->tctx->dom->name, uid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_user_by_id_test_done, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+    assert_true(check_leaks_pop(req_mem_ctx));
+
+    assert_non_null(test_ctx->result);
+    assert_int_equal(test_ctx->result->count, 1);
+    assert_non_null(test_ctx->result->msgs);
+    assert_non_null(test_ctx->result->msgs[0]);
+
+    ldbname = ldb_msg_find_attr_as_string(test_ctx->result->msgs[0],
+                                          SYSDB_NAME, NULL);
+    assert_non_null(ldbname);
+    assert_string_equal(ldbname, name);
+
+    ldbuid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_UIDNUM, 0);
+    assert_int_equal(ldbuid, uid);
+}
+
+void test_user_by_id_cache_expired(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    const char *name = TEST_USER_NAME;
+    const char *ldbname = NULL;
+    uid_t uid = TEST_USER_ID;
+    uid_t ldbuid;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    ret = sysdb_store_user(test_ctx->tctx->dom, name, "pwd", uid, 1000,
+                           NULL, NULL, NULL, "cn=test-user,dc=test", NULL,
+                           NULL, -1000, time(NULL));
+    assert_int_equal(ret, EOK);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    /* DP should be contacted */
+    will_return(__wrap_sss_dp_get_account_send, test_ctx);
+    mock_account_recv_simple();
+
+    req = cache_req_user_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                    test_ctx->rctx, test_ctx->ncache, 10, 0,
+                                    test_ctx->tctx->dom->name, uid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_user_by_id_test_done, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+    assert_true(check_leaks_pop(req_mem_ctx));
+
+    assert_true(test_ctx->dp_called);
+
+    assert_non_null(test_ctx->result);
+    assert_int_equal(test_ctx->result->count, 1);
+    assert_non_null(test_ctx->result->msgs);
+    assert_non_null(test_ctx->result->msgs[0]);
+
+    ldbname = ldb_msg_find_attr_as_string(test_ctx->result->msgs[0],
+                                          SYSDB_NAME, NULL);
+    assert_non_null(ldbname);
+    assert_string_equal(ldbname, name);
+
+    ldbuid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_UIDNUM, 0);
+    assert_int_equal(ldbuid, uid);
+}
+
+void test_user_by_id_cache_midpoint(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    const char *name = TEST_USER_NAME;
+    const char *ldbname = NULL;
+    uid_t uid = TEST_USER_ID;
+    uid_t ldbuid;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    ret = sysdb_store_user(test_ctx->tctx->dom, name, "pwd", uid, 1000,
+                           NULL, NULL, NULL, "cn=test-user,dc=test", NULL,
+                           NULL, 50, time(NULL) - 26);
+    assert_int_equal(ret, EOK);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    /* DP should be contacted without callback */
+    will_return(__wrap_sss_dp_get_account_send, test_ctx);
+
+    req = cache_req_user_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                    test_ctx->rctx, test_ctx->ncache, 10, 50,
+                                    test_ctx->tctx->dom->name, uid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_user_by_id_test_done, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+    assert_true(check_leaks_pop(req_mem_ctx));
+
+    assert_true(test_ctx->dp_called);
+
+    assert_non_null(test_ctx->result);
+    assert_int_equal(test_ctx->result->count, 1);
+    assert_non_null(test_ctx->result->msgs);
+    assert_non_null(test_ctx->result->msgs[0]);
+
+    ldbname = ldb_msg_find_attr_as_string(test_ctx->result->msgs[0],
+                                          SYSDB_NAME, NULL);
+    assert_non_null(ldbname);
+    assert_string_equal(ldbname, name);
+
+    ldbuid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_UIDNUM, 0);
+    assert_int_equal(ldbuid, uid);
+}
+
+void test_user_by_id_ncache(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    uid_t uid = TEST_USER_ID;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    ret = sss_ncache_set_uid(test_ctx->ncache, false, uid);
+    assert_int_equal(ret, EOK);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    req = cache_req_user_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                    test_ctx->rctx, test_ctx->ncache, 100, 0,
+                                    test_ctx->tctx->dom->name, uid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_user_by_id_test_done, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ENOENT);
+    assert_true(check_leaks_pop(req_mem_ctx));
+
+    assert_false(test_ctx->dp_called);
+}
+
+void test_user_by_id_missing_found(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    const char *name = TEST_USER_NAME;
+    const char *ldbname = NULL;
+    uid_t uid = TEST_USER_ID;
+    uid_t ldbuid;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    will_return(__wrap_sss_dp_get_account_send, test_ctx);
+    mock_account_recv_simple();
+
+    test_ctx->create_user = true;
+
+    req = cache_req_user_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                    test_ctx->rctx, test_ctx->ncache, 100, 0,
+                                    test_ctx->tctx->dom->name, uid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_user_by_id_test_done, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ERR_OK);
+    assert_true(check_leaks_pop(req_mem_ctx));
+
+    assert_true(test_ctx->dp_called);
+
+    assert_non_null(test_ctx->result);
+    assert_int_equal(test_ctx->result->count, 1);
+    assert_non_null(test_ctx->result->msgs);
+    assert_non_null(test_ctx->result->msgs[0]);
+
+    ldbname = ldb_msg_find_attr_as_string(test_ctx->result->msgs[0],
+                                          SYSDB_NAME, NULL);
+    assert_non_null(ldbname);
+    assert_string_equal(ldbname, name);
+
+    ldbuid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_UIDNUM, 0);
+    assert_int_equal(ldbuid, uid);
+}
+
+void test_user_by_id_missing_notfound(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    uid_t uid = TEST_USER_ID;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    will_return(__wrap_sss_dp_get_account_send, test_ctx);
+    mock_account_recv_simple();
+
+    req = cache_req_user_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                    test_ctx->rctx, test_ctx->ncache, 100, 0,
+                                    test_ctx->tctx->dom->name, uid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_user_by_id_test_done, test_ctx);
+
+    ret = test_ev_loop(test_ctx->tctx);
+    assert_int_equal(ret, ENOENT);
+    assert_true(check_leaks_pop(req_mem_ctx));
+
+    assert_true(test_ctx->dp_called);
+}
+
 int main(int argc, const char *argv[])
 {
     poptContext pc;
@@ -502,7 +859,16 @@ int main(int argc, const char *argv[])
         new_single_domain_test(user_by_name_missing_found),
         new_single_domain_test(user_by_name_missing_notfound),
         new_multi_domain_test(user_by_name_multiple_domains_found),
-        new_multi_domain_test(user_by_name_multiple_domains_notfound)
+        new_multi_domain_test(user_by_name_multiple_domains_notfound),
+
+        new_single_domain_test(user_by_id_cache_valid),
+        new_single_domain_test(user_by_id_cache_expired),
+        new_single_domain_test(user_by_id_cache_midpoint),
+        new_single_domain_test(user_by_id_ncache),
+        new_single_domain_test(user_by_id_missing_found),
+        new_single_domain_test(user_by_id_missing_notfound),
+        new_multi_domain_test(user_by_id_multiple_domains_found),
+        new_multi_domain_test(user_by_id_multiple_domains_notfound)
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
-- 
2.1.0

