From 77448b444104339a02ad9e50dd79aaa8c57b3312 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 27 Jan 2015 11:56:56 +0100
Subject: [PATCH 08/11] cache_req: add support for group by id

---
 src/responder/common/responder_cache_req.c  |  44 ++++
 src/responder/common/responder_cache_req.h  |  14 ++
 src/tests/cmocka/test_responder_cache_req.c | 363 +++++++++++++++++++++++++++-
 3 files changed, 420 insertions(+), 1 deletion(-)

diff --git a/src/responder/common/responder_cache_req.c b/src/responder/common/responder_cache_req.c
index d4b275748aebfd9007dddf52591506b67605d448..f7bc18fdbe3b8489e0806794962c4ea749befb60 100644
--- a/src/responder/common/responder_cache_req.c
+++ b/src/responder/common/responder_cache_req.c
@@ -83,6 +83,7 @@ cache_req_input_create(TALLOC_CTX *mem_ctx,
         }
         break;
     case CACHE_REQ_USER_BY_ID:
+    case CACHE_REQ_GROUP_BY_ID:
         if (id == 0) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0!\n");
             goto fail;
@@ -100,6 +101,7 @@ cache_req_input_create(TALLOC_CTX *mem_ctx,
         break;
 
     case CACHE_REQ_GROUP_BY_NAME:
+    case CACHE_REQ_GROUP_BY_ID:
         input->dp_type = SSS_DP_GROUP;
         break;
 
@@ -165,6 +167,14 @@ cache_req_input_set_domain(struct cache_req_input *input,
             goto done;
         }
         break;
+
+    case CACHE_REQ_GROUP_BY_ID:
+        fqn = talloc_asprintf(tmp_ctx, "GID:%d@%s", input->id, domain->name);
+        if (fqn == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        break;
     }
 
     input->domain = domain;
@@ -197,6 +207,9 @@ static errno_t cache_req_check_ncache(struct cache_req_input *input,
     case CACHE_REQ_USER_BY_ID:
         ret = sss_ncache_check_uid(ncache, neg_timeout, input->id);
         break;
+    case CACHE_REQ_GROUP_BY_ID:
+        ret = sss_ncache_check_gid(ncache, neg_timeout, input->id);
+        break;
     }
 
     if (ret == EEXIST) {
@@ -223,6 +236,7 @@ static void cache_req_add_to_ncache(struct cache_req_input *input,
                                    input->dom_objname);
         break;
     case CACHE_REQ_USER_BY_ID:
+    case CACHE_REQ_GROUP_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. */
@@ -256,6 +270,9 @@ static void cache_req_add_to_ncache_global(struct cache_req_input *input,
     case CACHE_REQ_USER_BY_ID:
         ret = sss_ncache_set_uid(ncache, false, input->id);
         break;
+    case CACHE_REQ_GROUP_BY_ID:
+        ret = sss_ncache_set_gid(ncache, false, input->id);
+        break;
     }
 
     if (ret != EOK) {
@@ -294,6 +311,11 @@ static errno_t cache_req_get_object(TALLOC_CTX *mem_ctx,
         ret = sysdb_getgrnam_with_views(mem_ctx, input->domain,
                                         input->dom_objname, &result);
         break;
+    case CACHE_REQ_GROUP_BY_ID:
+        one_item_only = true;
+        ret = sysdb_getgrgid_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,
@@ -830,6 +852,28 @@ cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
 }
 
 struct tevent_req *
+cache_req_group_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,
+                           gid_t gid)
+{
+    struct cache_req_input *input;
+
+    input = cache_req_input_create(mem_ctx, CACHE_REQ_GROUP_BY_ID, NULL, gid);
+    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_initgr_by_name_send(TALLOC_CTX *mem_ctx,
                               struct tevent_context *ev,
                               struct resp_ctx *rctx,
diff --git a/src/responder/common/responder_cache_req.h b/src/responder/common/responder_cache_req.h
index 65a0908f91478a8d847c261bda0c13e4e41a0465..3b8f3f713ec0fc6558f91ef07988af4237f016b4 100644
--- a/src/responder/common/responder_cache_req.h
+++ b/src/responder/common/responder_cache_req.h
@@ -31,6 +31,7 @@ enum cache_req_type {
     CACHE_REQ_USER_BY_NAME,
     CACHE_REQ_USER_BY_ID,
     CACHE_REQ_GROUP_BY_NAME,
+    CACHE_REQ_GROUP_BY_ID,
     CACHE_REQ_INITGROUPS
 };
 
@@ -101,6 +102,19 @@ cache_req_group_by_name_send(TALLOC_CTX *mem_ctx,
     cache_req_recv(mem_ctx, req, _result, _domain)
 
 struct tevent_req *
+cache_req_group_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,
+                           gid_t gid);
+
+#define cache_req_group_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/tests/cmocka/test_responder_cache_req.c b/src/tests/cmocka/test_responder_cache_req.c
index 1311efc9e00d3d7e7e7916a6adeb46bfd6065585..43a0491b93afda069a926132834ba592b7ff7339 100644
--- a/src/tests/cmocka/test_responder_cache_req.c
+++ b/src/tests/cmocka/test_responder_cache_req.c
@@ -147,6 +147,19 @@ static void cache_req_group_by_name_test_done(struct tevent_req *req)
     ctx->tctx->done = true;
 }
 
+static void cache_req_group_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_group_by_id_recv(ctx, req,
+                                                  &ctx->result, &ctx->domain);
+    talloc_zfree(req);
+
+    ctx->tctx->done = true;
+}
+
 static int test_single_domain_setup(void **state)
 {
     struct cache_req_test_ctx *test_ctx = NULL;
@@ -1177,6 +1190,345 @@ void test_group_by_name_missing_notfound(void **state)
     assert_true(test_ctx->dp_called);
 }
 
+void test_group_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_GROUP_NAME;
+    const char *ldbname = NULL;
+    gid_t gid = TEST_GROUP_ID;
+    gid_t ldbgid;
+    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_group(domain, name, gid, 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_group_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                     test_ctx->rctx, test_ctx->ncache, 10, 0,
+                                     NULL, gid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_group_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);
+
+    ldbgid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_GIDNUM, 0);
+    assert_int_equal(ldbgid, gid);
+
+    assert_non_null(test_ctx->domain);
+    assert_string_equal(domain->name, test_ctx->domain->name);
+}
+
+void test_group_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;
+    gid_t gid = TEST_GROUP_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_group_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                     test_ctx->rctx, test_ctx->ncache, 10, 0,
+                                     NULL, gid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_group_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_group_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_GROUP_NAME;
+    const char *ldbname = NULL;
+    gid_t gid = TEST_GROUP_ID;
+    gid_t ldbgid;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    ret = sysdb_store_group(test_ctx->tctx->dom, name, gid, 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_group_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                     test_ctx->rctx, test_ctx->ncache, 10, 0,
+                                     test_ctx->tctx->dom->name, gid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_group_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);
+
+    ldbgid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_GIDNUM, 0);
+    assert_int_equal(ldbgid, gid);
+}
+
+void test_group_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_GROUP_NAME;
+    const char *ldbname = NULL;
+    gid_t gid = TEST_GROUP_ID;
+    gid_t ldbgid;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    ret = sysdb_store_group(test_ctx->tctx->dom, name, gid, 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_group_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                     test_ctx->rctx, test_ctx->ncache, 10, 0,
+                                     test_ctx->tctx->dom->name, gid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_group_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);
+
+    ldbgid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_GIDNUM, 0);
+    assert_int_equal(ldbgid, gid);
+}
+
+void test_group_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_GROUP_NAME;
+    const char *ldbname = NULL;
+    gid_t gid = TEST_GROUP_ID;
+    gid_t ldbgid;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    ret = sysdb_store_group(test_ctx->tctx->dom, name, gid, 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_group_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                     test_ctx->rctx, test_ctx->ncache, 10, 50,
+                                     test_ctx->tctx->dom->name, gid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_group_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);
+
+    ldbgid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_GIDNUM, 0);
+    assert_int_equal(ldbgid, gid);
+}
+
+void test_group_by_id_ncache(void **state)
+{
+    struct cache_req_test_ctx *test_ctx = NULL;
+    TALLOC_CTX *req_mem_ctx = NULL;
+    struct tevent_req *req = NULL;
+    gid_t gid = TEST_GROUP_ID;
+    errno_t ret;
+
+    test_ctx = talloc_get_type_abort(*state, struct cache_req_test_ctx);
+
+    ret = sss_ncache_set_gid(test_ctx->ncache, false, gid);
+    assert_int_equal(ret, EOK);
+
+    req_mem_ctx = talloc_new(global_talloc_context);
+    check_leaks_push(req_mem_ctx);
+
+    req = cache_req_group_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                     test_ctx->rctx, test_ctx->ncache, 100, 0,
+                                     test_ctx->tctx->dom->name, gid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_group_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_group_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_GROUP_NAME;
+    const char *ldbname = NULL;
+    gid_t gid = TEST_GROUP_ID;
+    gid_t ldbgid;
+    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_group = true;
+
+    req = cache_req_group_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                     test_ctx->rctx, test_ctx->ncache, 100, 0,
+                                     test_ctx->tctx->dom->name, gid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_group_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);
+
+    ldbgid = ldb_msg_find_attr_as_uint(test_ctx->result->msgs[0],
+                                       SYSDB_GIDNUM, 0);
+    assert_int_equal(ldbgid, gid);
+}
+
+void test_group_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;
+    gid_t gid = TEST_GROUP_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_group_by_id_send(req_mem_ctx, test_ctx->tctx->ev,
+                                     test_ctx->rctx, test_ctx->ncache, 100, 0,
+                                     test_ctx->tctx->dom->name, gid);
+    assert_non_null(req);
+    tevent_req_set_callback(req, cache_req_group_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;
@@ -1213,7 +1565,16 @@ int main(int argc, const char *argv[])
         new_single_domain_test(group_by_name_missing_found),
         new_single_domain_test(group_by_name_missing_notfound),
         new_multi_domain_test(group_by_name_multiple_domains_found),
-        new_multi_domain_test(group_by_name_multiple_domains_notfound)
+        new_multi_domain_test(group_by_name_multiple_domains_notfound),
+
+        new_single_domain_test(group_by_id_cache_valid),
+        new_single_domain_test(group_by_id_cache_expired),
+        new_single_domain_test(group_by_id_cache_midpoint),
+        new_single_domain_test(group_by_id_ncache),
+        new_single_domain_test(group_by_id_missing_found),
+        new_single_domain_test(group_by_id_missing_notfound),
+        new_multi_domain_test(group_by_id_multiple_domains_found),
+        new_multi_domain_test(group_by_id_multiple_domains_notfound)
     };
 
     /* Set debug level to invalid value so we can deside if -d 0 was used. */
-- 
2.1.0

