From fc716a9382ec94479867db68d902188022617f45 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Wed, 28 Jul 2010 12:24:39 -0400
Subject: [PATCH 5/6] Add sysdb_update_members function

This function will take a user, a list of groups that this user
should be added to and a list of groups the user should be removed
from and will recursively call sysdb_[add|remove]_group_member

Includes a unit test
---
 src/db/sysdb.h          |   10 +++
 src/db/sysdb_ops.c      |  191 +++++++++++++++++++++++++++++++++++++++++++++++
 src/tests/sysdb-tests.c |  168 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 369 insertions(+), 0 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 2de2a64855dcf31dc77582e9efa731889de00e97..04ce49e66c3d056b8b4a81375674816d9a91cc3e 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -537,6 +537,16 @@ struct tevent_req *sysdb_remove_group_member_send(TALLOC_CTX *mem_ctx,
                                                   const char *member);
 int sysdb_remove_group_member_recv(struct tevent_req *req);
 
+
+struct tevent_req * sysdb_update_members_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct sysdb_handle *handle,
+                                              struct sss_domain_info *domain,
+                                              char *user,
+                                              char **add_groups,
+                                              char **del_groups);
+errno_t sysdb_update_members_recv(struct tevent_req *req);
+
 /* Password caching function.
  * If you are in a transaction ignore sysdb and pass in the handle.
  * If you are not in a transaction pass NULL in handle and provide sysdb,
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index 6fcc95b19223ae9ee029519f54da524f16514c28..14b21c32d5a65955f36f01c9f432ff167b1b45fa 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -5065,3 +5065,194 @@ int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date,
 
     return (state->authentication_successful ? EOK : EINVAL);
 }
+
+struct sysdb_update_members_ctx {
+    char *user;
+    struct sss_domain_info *domain;
+    struct tevent_context *ev;
+    struct sysdb_handle *handle;
+
+    char **add_groups;
+    int add_group_iter;
+
+    char **del_groups;
+    int del_group_iter;
+};
+
+static char **empty_string_list(TALLOC_CTX *mem_ctx)
+{
+    char **empty;
+    empty = talloc_array(mem_ctx, char *, 1);
+    if (!empty) {
+        return NULL;
+    }
+
+    empty[0] = NULL;
+
+    return empty;
+}
+
+static errno_t
+sysdb_update_members_step(struct tevent_req *req);
+
+struct tevent_req *sysdb_update_members_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct sysdb_handle *handle,
+                                             struct sss_domain_info *domain,
+                                             char *user,
+                                             char **add_groups,
+                                             char **del_groups)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct sysdb_update_members_ctx *state;
+
+    req = tevent_req_create(mem_ctx, &state, struct sysdb_update_members_ctx);
+    if (!req) {
+        return NULL;
+    }
+
+    state->user = talloc_strdup(state, user);
+    if (!state->user) {
+        goto error;
+    }
+
+    state->domain = domain;
+    state->ev = ev;
+    state->handle = handle;
+
+    if (add_groups) {
+        state->add_groups = dup_string_list(state, add_groups);
+    }
+    else {
+        state->add_groups = empty_string_list(state);
+    }
+    if (!state->add_groups) {
+        goto error;
+    }
+    state->add_group_iter = 0;
+
+    if (del_groups) {
+        state->del_groups = dup_string_list(state, del_groups);
+    }
+    else {
+        state->del_groups = empty_string_list(state);
+    }
+    if (!state->del_groups) {
+        goto error;
+    }
+    state->del_group_iter = 0;
+
+    ret = sysdb_update_members_step(req);
+    if (ret != EOK) {
+        /* Nothing to do. Finish up */
+        tevent_req_error(req, ret);
+        tevent_req_post(req, state->ev);
+    }
+
+    return req;
+
+error:
+    talloc_free(req);
+    return NULL;
+}
+
+static void
+sysdb_update_members_add_done(struct tevent_req *subreq);
+static void
+sysdb_update_members_del_done(struct tevent_req *subreq);
+
+static errno_t
+sysdb_update_members_step(struct tevent_req *req)
+{
+    struct tevent_req *subreq;
+    struct sysdb_update_members_ctx *state;
+
+    state = tevent_req_data(req, struct sysdb_update_members_ctx);
+
+    if (state->add_groups[state->add_group_iter]) {
+        subreq = sysdb_add_group_member_send(
+                state, state->ev, state->handle,
+                state->domain,
+                state->add_groups[state->add_group_iter],
+                state->user);
+        if (!subreq) {
+            return EIO;
+        }
+
+        tevent_req_set_callback(subreq, sysdb_update_members_add_done, req);
+        return EOK;
+    }
+
+    if (state->del_groups[state->del_group_iter]) {
+        subreq = sysdb_remove_group_member_send(
+                state, state->ev,
+                state->handle, state->domain,
+                state->del_groups[state->del_group_iter],
+                state->user);
+        if (!subreq) {
+            return EIO;
+        }
+
+        tevent_req_set_callback(subreq, sysdb_update_members_del_done, req);
+        return EOK;
+    }
+
+    /* No more members to handle */
+    tevent_req_done(req);
+    return EOK;
+}
+
+static void
+sysdb_update_members_add_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct sysdb_update_members_ctx *state =
+            tevent_req_data(req, struct sysdb_update_members_ctx);
+
+    ret = sysdb_add_group_member_recv(subreq);
+    talloc_zfree(subreq);
+    if(ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    state->add_group_iter++;
+    ret = sysdb_update_members_step(req);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+}
+
+static void
+sysdb_update_members_del_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req =
+            tevent_req_callback_data(subreq, struct tevent_req);
+    struct sysdb_update_members_ctx *state =
+            tevent_req_data(req, struct sysdb_update_members_ctx);
+
+    ret = sysdb_remove_group_member_recv(subreq);
+    talloc_zfree(subreq);
+    if(ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    state->del_group_iter++;
+    ret = sysdb_update_members_step(req);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+}
+
+errno_t
+sysdb_update_members_recv(struct tevent_req *req)
+{
+    return sysdb_op_default_recv(req);
+}
diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
index d5cef4b498f704008550c1b02b9c2f5f9a4941b6..b5a885e474c63d04115f6b55171615e832d1e791 100644
--- a/src/tests/sysdb-tests.c
+++ b/src/tests/sysdb-tests.c
@@ -3153,6 +3153,171 @@ START_TEST (test_sysdb_attrs_to_list)
 }
 END_TEST
 
+static void test_sysdb_update_members_add(struct tevent_req *req);
+START_TEST (test_sysdb_update_members)
+{
+    struct sysdb_test_ctx *test_ctx;
+    struct test_data *data;
+    struct tevent_req *req;
+    int ret;
+
+    /* Setup */
+    ret = setup_sysdb_tests(&test_ctx);
+    if (ret != EOK) {
+        fail("Could not set up the test");
+        return;
+    }
+
+    data = talloc_zero(test_ctx, struct test_data);
+    data->ctx = test_ctx;
+    data->ev = test_ctx->ev;
+
+    /* Start the transaction */
+    req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb);
+    if (!req) {
+        ret = ENOMEM;
+    }
+
+    if (ret == EOK) {
+        tevent_req_set_callback(req, test_sysdb_update_members_add, data);
+
+        ret = test_loop(data);
+    }
+
+    fail_if(ret != EOK, "Could not test sysdb_update_members");
+    talloc_free(test_ctx);
+}
+END_TEST
+
+static void test_sysdb_update_members_add_del(struct tevent_req *req);
+static void test_sysdb_update_members_add(struct tevent_req *req)
+{
+    struct test_data *data = tevent_req_callback_data(req, struct test_data);
+    char **add_groups;
+    char *user;
+    errno_t ret;
+
+    ret = sysdb_transaction_recv(req, data, &data->handle);
+    talloc_zfree(req);
+    if (ret != EOK) {
+        DEBUG(0, ("Could not start transaction\n"));
+        test_return(data, ret);
+        return;
+    }
+
+    /* Add a user to two groups */
+    data->username = talloc_strdup(data, "testuser27000");
+    user = talloc_strdup(data, data->username);
+    add_groups = talloc_array(data, char *, 3);
+    add_groups[0] = talloc_strdup(data, "testgroup28001");
+    add_groups[1] = talloc_strdup(data, "testgroup28002");
+    add_groups[2] = NULL;
+
+    req = sysdb_update_members_send(data, data->ev, data->handle,
+                                    data->ctx->domain, user,
+                                    add_groups, NULL);
+    talloc_free(add_groups);
+    talloc_free(user);
+    if (!req) {
+        DEBUG(0, ("Could not add groups\n"));
+        test_return(data, EIO);
+        return;
+    }
+
+    tevent_req_set_callback(req, test_sysdb_update_members_add_del, data);
+}
+
+static void test_sysdb_update_members_del(struct tevent_req *req);
+static void test_sysdb_update_members_add_del(struct tevent_req *req)
+{
+    struct test_data *data = tevent_req_callback_data(req, struct test_data);
+    errno_t ret;
+    char **add_groups = NULL;
+    char **del_groups = NULL;
+    char *user;
+
+    ret = sysdb_update_members_recv(req);
+    talloc_zfree(req);
+    if (ret != EOK) {
+        DEBUG(0, ("Group addition failed [%d](%s)\n", ret, strerror(ret)));
+        test_return(data, ret);
+        return;
+    }
+
+    /* Remove a user from one group and add to another */
+    user = talloc_strdup(data, data->username);
+    del_groups = talloc_array(data, char *, 2);
+    del_groups[0] = talloc_strdup(del_groups, "testgroup28001");
+    del_groups[1] = NULL;
+    add_groups = talloc_array(data, char *, 2);
+    add_groups[0] = talloc_strdup(add_groups, "testgroup28003");
+    add_groups[1] = NULL;
+
+    req = sysdb_update_members_send(data, data->ev, data->handle,
+                                    data->ctx->domain, user,
+                                    add_groups, del_groups);
+    talloc_free(add_groups);
+    talloc_free(del_groups);
+    talloc_free(user);
+    if (!req) {
+        DEBUG(0, ("Could not add/del groups\n"));
+        test_return(data, EIO);
+        return;
+    }
+
+    tevent_req_set_callback(req, test_sysdb_update_members_del, data);
+}
+
+static void test_sysdb_update_members_done(struct tevent_req *req);
+static void test_sysdb_update_members_del(struct tevent_req *req)
+{
+    struct test_data *data = tevent_req_callback_data(req, struct test_data);
+    errno_t ret;
+    char **del_groups = NULL;
+    char *user;
+
+    ret = sysdb_update_members_recv(req);
+    talloc_zfree(req);
+    if (ret != EOK) {
+        DEBUG(0, ("Group replace failed [%d](%s)\n", ret, strerror(ret)));
+        test_return(data, EIO);
+        return;
+    }
+
+    /* Remove a user from one group and add to another */
+    user = talloc_strdup(data, data->username);
+    del_groups = talloc_array(data, char *, 3);
+    del_groups[0] = talloc_strdup(del_groups, "testgroup28002");
+    del_groups[1] = talloc_strdup(del_groups, "testgroup28003");
+    del_groups[2] = NULL;
+
+    req = sysdb_update_members_send(data, data->ev, data->handle,
+                                    data->ctx->domain, user,
+                                    NULL, del_groups);
+    talloc_free(del_groups);
+    talloc_free(user);
+    if (!req) {
+        DEBUG(0, ("Could not del groups\n"));
+        test_return(data, EIO);
+        return;
+    }
+
+    tevent_req_set_callback(req, test_sysdb_update_members_done, data);
+}
+
+static void test_sysdb_update_members_done(struct tevent_req *req)
+{
+    struct test_data *data = tevent_req_callback_data(req, struct test_data);
+    errno_t ret;
+
+    ret = sysdb_update_members_recv(req);
+    talloc_zfree(req);
+    if (ret != EOK) {
+        DEBUG(0, ("Group delete failed [%d](%s)\n", ret, strerror(ret)));
+    }
+    test_return(data, ret);
+}
+
 Suite *create_sysdb_suite(void)
 {
     Suite *s = suite_create("sysdb");
@@ -3177,6 +3342,9 @@ Suite *create_sysdb_suite(void)
     /* test the change */
     tcase_add_loop_test(tc_sysdb, test_sysdb_get_user_attr, 27000, 27010);
 
+    /* Add and remove users in a group with sysdb_update_members */
+    tcase_add_test(tc_sysdb, test_sysdb_update_members);
+
     /* Remove the other half by gid */
     tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group_by_gid, 28000, 28010);
 
-- 
1.7.2

