>From 47b772b68553bd516ceb9a3a75615d49149b6add Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 27 Nov 2012 16:09:23 +0100 Subject: [PATCH 5/6] MEMBEROF: Implement the modify operation for ghost users Similar to the add and delete operation, we also need to propagate the changes of the ghost user attribute to the parent groups so that if a nested group updates memberships, its parents also get the membership updated. --- src/ldb_modules/memberof.c | 285 +++++++++++++++++++++++---- src/tests/sysdb-tests.c | 480 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 722 insertions(+), 43 deletions(-) diff --git a/src/ldb_modules/memberof.c b/src/ldb_modules/memberof.c index 4899b03c67183db2081cca593a677856d1a07e4b..cb6f8113f2b863cc6f8758f7de3baa5b38e053e8 100644 --- a/src/ldb_modules/memberof.c +++ b/src/ldb_modules/memberof.c @@ -41,6 +41,11 @@ #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif +struct mbof_val_array { + struct ldb_val *vals; + int num; +}; + struct mbof_dn_array { struct ldb_dn **dns; int num; @@ -143,11 +148,15 @@ struct mbof_mod_ctx { struct mbof_ctx *ctx; const struct ldb_message_element *membel; + const struct ldb_message_element *ghel; struct ldb_message *entry; struct mbof_dn_array *mb_add; struct mbof_dn_array *mb_remove; + struct mbof_val_array *gh_add; + struct mbof_val_array *gh_remove; + struct ldb_message *msg; bool terminate; }; @@ -2275,7 +2284,8 @@ static int mbof_del_mod_callback(struct ldb_request *req, } static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx, - struct mbof_dn_array *ael); + struct mbof_dn_array *ael, + struct mbof_val_array *addgh); static int mbof_del_progeny(struct mbof_del_operation *delop) { @@ -2334,7 +2344,8 @@ static int mbof_del_progeny(struct mbof_del_operation *delop) /* see if there are follow functions to run */ if (del_ctx->follow_mod) { return mbof_mod_add(del_ctx->follow_mod, - del_ctx->follow_mod->mb_add); + del_ctx->follow_mod->mb_add, + del_ctx->follow_mod->gh_add); } /* ok, no more ops, this means our job is done */ @@ -2615,7 +2626,8 @@ static int mbof_del_muop_callback(struct ldb_request *req, /* see if there are follow functions to run */ else if (del_ctx->follow_mod) { return mbof_mod_add(del_ctx->follow_mod, - del_ctx->follow_mod->mb_add); + del_ctx->follow_mod->mb_add, + del_ctx->follow_mod->gh_add); } else { return ldb_module_done(ctx->req, @@ -2727,7 +2739,8 @@ static int mbof_del_ghop_callback(struct ldb_request *req, /* see if there are follow functions to run */ else if (del_ctx->follow_mod) { return mbof_mod_add(del_ctx->follow_mod, - del_ctx->follow_mod->mb_add); + del_ctx->follow_mod->mb_add, + del_ctx->follow_mod->gh_add); } else { return ldb_module_done(ctx->req, @@ -2780,19 +2793,30 @@ static int mbof_mod_process_membel(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, const struct ldb_message_element *membel, struct mbof_dn_array **_added, struct mbof_dn_array **_removed); +static int mbof_mod_process_ghel(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, + struct ldb_message *entry, + const struct ldb_message_element *membel, + struct mbof_val_array **_added, + struct mbof_val_array **_removed); static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx, - struct mbof_dn_array *del); + struct mbof_dn_array *del, + struct mbof_val_array *delgh); static int mbof_fill_dn_array(TALLOC_CTX *memctx, struct ldb_context *ldb, const struct ldb_message_element *el, struct mbof_dn_array **dn_array); +static int mbof_fill_vals_array(TALLOC_CTX *memctx, + struct ldb_context *ldb, + const struct ldb_message_element *el, + struct mbof_val_array **val_array); static int memberof_mod(struct ldb_module *module, struct ldb_request *req) { struct ldb_message_element *el; struct mbof_mod_ctx *mod_ctx; struct mbof_ctx *ctx; - static const char *attrs[] = {DB_MEMBER, DB_MEMBEROF, NULL}; + static const char *attrs[] = { DB_OC, DB_GHOST, + DB_MEMBER, DB_MEMBEROF, NULL}; struct ldb_context *ldb = ldb_module_get_ctx(module); struct ldb_request *search; int ret; @@ -2835,15 +2859,15 @@ static int memberof_mod(struct ldb_module *module, struct ldb_request *req) return LDB_ERR_OPERATIONS_ERROR; } - /* continue with normal ops if there are no members */ - el = ldb_msg_find_element(mod_ctx->msg, DB_MEMBER); - if (!el) { + mod_ctx->membel = ldb_msg_find_element(mod_ctx->msg, DB_MEMBER); + mod_ctx->ghel = ldb_msg_find_element(mod_ctx->msg, DB_GHOST); + + /* continue with normal ops if there are no members and no ghosts */ + if (mod_ctx->membel == NULL && mod_ctx->ghel == NULL) { mod_ctx->terminate = true; return mbof_orig_mod(mod_ctx); } - mod_ctx->membel = el; - /* can't do anything, * must check first what's on the entry */ ret = ldb_build_search_req(&search, ldb, mod_ctx, @@ -3016,16 +3040,24 @@ static int mbof_mod_process(struct mbof_mod_ctx *mod_ctx, bool *done) return ret; } + ret = mbof_mod_process_ghel(mod_ctx, ldb, mod_ctx->entry, mod_ctx->ghel, + &mod_ctx->gh_add, &mod_ctx->gh_remove); + if (ret != LDB_SUCCESS) { + return ret; + } + /* Process the operations */ /* if we have something to remove do it first */ - if (mod_ctx->mb_remove && mod_ctx->mb_remove->num) { - return mbof_mod_delete(mod_ctx, mod_ctx->mb_remove); + if ((mod_ctx->mb_remove && mod_ctx->mb_remove->num) || + (mod_ctx->gh_remove && mod_ctx->gh_remove->num)) { + return mbof_mod_delete(mod_ctx, mod_ctx->mb_remove, mod_ctx->gh_remove); } /* if there is nothing to remove and we have stuff to add * do it right away */ - if (mod_ctx->mb_add && mod_ctx->mb_add->num) { - return mbof_mod_add(mod_ctx, mod_ctx->mb_add); + if ((mod_ctx->mb_add && mod_ctx->mb_add->num) || + (mod_ctx->gh_add && mod_ctx->gh_add->num)) { + return mbof_mod_add(mod_ctx, mod_ctx->mb_add, mod_ctx->gh_add); } /* the replacement function resulted in a null op, @@ -3133,8 +3165,109 @@ static int mbof_mod_process_membel(TALLOC_CTX *mem_ctx, return LDB_SUCCESS; } +static int mbof_mod_process_ghel(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, + struct ldb_message *entry, + const struct ldb_message_element *ghel, + struct mbof_val_array **_added, + struct mbof_val_array **_removed) +{ + const struct ldb_message_element *el; + struct mbof_val_array *removed = NULL; + struct mbof_val_array *added = NULL; + int i, j, ret; + + if (!ghel) { + /* Nothing to do.. */ + return LDB_SUCCESS; + } + + el = ldb_msg_find_element(entry, DB_MEMBEROF); + if (!el || el->num_values == 0) { + /* no memberof attributes ... */ + return LDB_SUCCESS; + } + + switch (ghel->flags) { + case LDB_FLAG_MOD_ADD: + ret = mbof_fill_vals_array(mem_ctx, ldb, ghel, &added); + if (ret != LDB_SUCCESS) { + return ret; + } + break; + + case LDB_FLAG_MOD_DELETE: + if (ghel->num_values == 0) { + el = ldb_msg_find_element(entry, DB_GHOST); + } else { + el = ghel; + } + + if (!el) { + /* nothing to do really */ + break; + } + + ret = mbof_fill_vals_array(mem_ctx, ldb, ghel, &removed); + if (ret != LDB_SUCCESS) { + return ret; + } + break; + + case LDB_FLAG_MOD_REPLACE: + el = ldb_msg_find_element(entry, DB_GHOST); + if (el) { + ret = mbof_fill_vals_array(mem_ctx, ldb, el, &removed); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + el = ghel; + if (el) { + ret = mbof_fill_vals_array(mem_ctx, ldb, el, &added); + if (ret != LDB_SUCCESS) { + talloc_free(removed); + return ret; + } + } + + /* remove from arrays values that ended up unchanged */ + if (removed && removed->num && added && added->num) { + for (i = 0; i < added->num; i++) { + for (j = 0; j < removed->num; j++) { + if (strcmp((const char *) added->vals[i].data, + (const char *) removed->vals[j].data) == 0) { + break; + } + } + if (j < removed->num) { + /* preexisting one, not removed, nor added */ + for (; j+1 < removed->num; j++) { + removed->vals[j] = removed->vals[j+1]; + } + removed->num--; + for (j = i; j+1 < added->num; j++) { + added->vals[j] = added->vals[j+1]; + } + added->num--; + i--; + } + } + } + break; + + default: + return LDB_ERR_OPERATIONS_ERROR; + } + + *_added = added; + *_removed = removed; + return LDB_SUCCESS; +} + static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx, - struct mbof_dn_array *ael) + struct mbof_dn_array *ael, + struct mbof_val_array *addgh) { const struct ldb_message_element *el; struct mbof_dn_array *parents; @@ -3154,14 +3287,6 @@ static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx, return ret; } - parents->dns = talloc_realloc(parents, parents->dns, - struct ldb_dn *, parents->num + 1); - if (!parents->dns) { - return LDB_ERR_OPERATIONS_ERROR; - } - parents->dns[parents->num] = mod_ctx->entry->dn; - parents->num++; - add_ctx = talloc_zero(mod_ctx, struct mbof_add_ctx); if (!add_ctx) { return LDB_ERR_OPERATIONS_ERROR; @@ -3169,18 +3294,42 @@ static int mbof_mod_add(struct mbof_mod_ctx *mod_ctx, add_ctx->ctx = ctx; add_ctx->msg_dn = mod_ctx->msg->dn; - for (i = 0; i < ael->num; i++) { - ret = mbof_append_addop(add_ctx, parents, ael->dns[i]); + if (addgh != NULL) { + /* Build the memberuid add op */ + ret = mbof_add_fill_ghop_ex(add_ctx, mod_ctx->entry, + parents, addgh->vals, addgh->num); if (ret != LDB_SUCCESS) { return ret; } } - return mbof_next_add(add_ctx->add_list); + if (ael != NULL) { + /* Add itself to the list of the parents to also get the memberuid */ + parents->dns = talloc_realloc(parents, parents->dns, + struct ldb_dn *, parents->num + 1); + if (!parents->dns) { + return LDB_ERR_OPERATIONS_ERROR; + } + parents->dns[parents->num] = mod_ctx->entry->dn; + parents->num++; + + /* Build the member-add array */ + for (i = 0; i < ael->num; i++) { + ret = mbof_append_addop(add_ctx, parents, ael->dns[i]); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return mbof_next_add(add_ctx->add_list); + } + + return mbof_add_muop(add_ctx); } static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx, - struct mbof_dn_array *del) + struct mbof_dn_array *del, + struct mbof_val_array *delgh) { struct mbof_del_operation *first; struct mbof_del_ctx *del_ctx; @@ -3205,25 +3354,39 @@ static int mbof_mod_delete(struct mbof_mod_ctx *mod_ctx, } del_ctx->first = first; - first->del_ctx = del_ctx; - first->entry = mod_ctx->entry; - first->entry_dn = mod_ctx->entry->dn; - - /* prepare del sets */ - for (i = 0; i < del->num; i++) { - ret = mbof_append_delop(first, del->dns[i]); - if (ret != LDB_SUCCESS) { - return ret; - } - } - /* add followup function if we also have stuff to add */ - if (mod_ctx->mb_add) { + if ((mod_ctx->mb_add && mod_ctx->mb_add->num > 0) || + (mod_ctx->gh_add && mod_ctx->gh_add->num > 0)) { del_ctx->follow_mod = mod_ctx; } - /* now that sets are built, start processing */ - return mbof_del_execute_op(first->children[0]); + first->del_ctx = del_ctx; + first->entry = mod_ctx->entry; + first->entry_dn = mod_ctx->entry->dn; + + if (delgh != NULL) { + ret = mbof_del_fill_ghop_ex(del_ctx, del_ctx->first->entry, + delgh->vals, delgh->num); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + /* prepare del sets */ + if (del != NULL) { + for (i = 0; i < del->num; i++) { + ret = mbof_append_delop(first, del->dns[i]); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + /* now that sets are built, start processing */ + return mbof_del_execute_op(first->children[0]); + } + + /* No member processing, just delete ghosts */ + return mbof_del_ghop(del_ctx); } static int mbof_fill_dn_array(TALLOC_CTX *memctx, @@ -3264,6 +3427,42 @@ static int mbof_fill_dn_array(TALLOC_CTX *memctx, return LDB_SUCCESS; } +static int mbof_fill_vals_array(TALLOC_CTX *memctx, + struct ldb_context *ldb, + const struct ldb_message_element *el, + struct mbof_val_array **val_array) +{ + struct mbof_val_array *var; + int i; + + var = talloc_zero(memctx, struct mbof_val_array); + if (!var) { + return LDB_ERR_OPERATIONS_ERROR; + } + *val_array = var; + + if (!el || el->num_values == 0) { + return LDB_SUCCESS; + } + + var->vals = talloc_array(var, struct ldb_val, el->num_values); + if (!var->vals) { + return LDB_ERR_OPERATIONS_ERROR; + } + var->num = el->num_values; + + for (i = 0; i < var->num; i++) { + var->vals[i].length = strlen((const char *) el->values[i].data); + var->vals[i].data = (uint8_t *) talloc_strdup(var, + (const char *) el->values[i].data); + if (var->vals[i].data == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + } + + return LDB_SUCCESS; +} + /************************* * Cleanup task routines * diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c index 7b69876ff0dc3dba5168d6f3ac096526bf7ff576..d513c30f9b370d030bea72901eacdec8eaeb0e87 100644 --- a/src/tests/sysdb-tests.c +++ b/src/tests/sysdb-tests.c @@ -1996,6 +1996,190 @@ START_TEST (test_sysdb_memberof_store_group_with_ghosts) } END_TEST + +START_TEST (test_sysdb_memberof_mod_add) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + char *ghostname; + int ret; + struct ldb_message_element *el; + struct ldb_val gv, *test_gv; + gid_t itergid; + + /* 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; + data->gid = _i; + data->groupname = talloc_asprintf(data, "testgroup%d", data->gid); + + data->attrs = sysdb_new_attrs(data); + if (ret != EOK) { + fail("Could not create the changeset"); + return; + } + + ghostname = talloc_asprintf(data, "testghost%d", _i); + fail_unless(ghostname != NULL, "Out of memory\n"); + ret = sysdb_attrs_steal_string(data->attrs, SYSDB_GHOST, ghostname); + fail_unless(ret == EOK, "Cannot add attr\n"); + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed."); + data->attrlist[0] = SYSDB_GHOST; + data->attrlist[1] = NULL; + + /* Before the add, the groups should not contain the ghost attribute */ + for (itergid = data->gid ; itergid < MBO_GROUP_BASE + NUM_GHOSTS; itergid++) { + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + itergid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname; + gv.length = strlen(ghostname); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + if (data->gid > MBO_GROUP_BASE) { + /* The first group would have the ghost attribute gone completely */ + fail_if(el == NULL, "Cannot find ghost element\n"); + test_gv = ldb_msg_find_val(el, &gv); + fail_unless(test_gv == NULL, + "Ghost user %s unexpectedly found\n", ghostname); + } else { + fail_unless(el == NULL, "Stray values in ghost element?\n"); + } + } + + /* Perform the add operation */ + ret = sysdb_set_group_attr(test_ctx->sysdb, data->groupname, + data->attrs, SYSDB_MOD_ADD); + fail_unless(ret == EOK, "Cannot set group attrs\n"); + + /* Before the delete, all groups with gid >= _i have the testuser%_i + * as a member + */ + for (itergid = data->gid ; itergid < MBO_GROUP_BASE + NUM_GHOSTS; itergid++) { + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + itergid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname; + gv.length = strlen(ghostname); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + fail_if(el == NULL, "Cannot find ghost element\n"); + + test_gv = ldb_msg_find_val(el, &gv); + fail_if(test_gv == NULL, "Cannot find ghost user %s\n", ghostname); + } + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_memberof_mod_replace) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + char *ghostname_del; + char *ghostname_add; + int ret; + struct ldb_message_element *el; + struct ldb_val gv, *test_gv; + gid_t itergid; + + /* 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; + data->gid = _i; + data->groupname = talloc_asprintf(data, "testgroup%d", data->gid); + + data->attrs = sysdb_new_attrs(data); + if (ret != EOK) { + fail("Could not create the changeset"); + return; + } + + /* The test replaces the testuser%i attribute with testghost%i */ + ghostname_del = talloc_asprintf(data, "testuser%d", _i); + fail_unless(ghostname_del != NULL, "Out of memory\n"); + + ghostname_add = talloc_asprintf(data, "testghost%d", _i); + fail_unless(ghostname_add != NULL, "Out of memory\n"); + ret = sysdb_attrs_steal_string(data->attrs, SYSDB_GHOST, ghostname_add); + fail_unless(ret == EOK, "Cannot add attr\n"); + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed."); + data->attrlist[0] = SYSDB_GHOST; + data->attrlist[1] = NULL; + + /* Before the replace, all groups with gid >= _i have the testuser%_i + * as a member + */ + for (itergid = data->gid ; itergid < MBO_GROUP_BASE + NUM_GHOSTS; itergid++) { + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + itergid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname_del; + gv.length = strlen(ghostname_del); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + fail_if(el == NULL, "Cannot find ghost element\n"); + + test_gv = ldb_msg_find_val(el, &gv); + fail_if(test_gv == NULL, "Cannot find ghost user %s\n", ghostname_del); + } + + /* Perform the replace operation */ + ret = sysdb_set_group_attr(test_ctx->sysdb, data->groupname, + data->attrs, SYSDB_MOD_REP); + fail_unless(ret == EOK, "Cannot set group attrs\n"); + + /* After the replace, all groups with gid >= _i have the testghost%_i + * as a member + */ + for (itergid = data->gid ; itergid < MBO_GROUP_BASE + NUM_GHOSTS; itergid++) { + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + itergid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname_add; + gv.length = strlen(ghostname_add); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + fail_if(el == NULL, "Cannot find ghost element\n"); + + test_gv = ldb_msg_find_val(el, &gv); + fail_if(test_gv == NULL, "Cannot find ghost user %s\n", ghostname_add); + } + + talloc_free(test_ctx); +} +END_TEST + START_TEST (test_sysdb_memberof_close_loop) { struct sysdb_test_ctx *test_ctx; @@ -2367,6 +2551,97 @@ START_TEST (test_sysdb_memberof_remove_child_group_and_check_ghost) } END_TEST +START_TEST (test_sysdb_memberof_mod_del) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + char *ghostname; + int ret; + struct ldb_message_element *el; + struct ldb_val gv, *test_gv; + gid_t itergid; + + /* 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; + data->gid = _i; + data->groupname = talloc_asprintf(data, "testgroup%d", data->gid); + + data->attrs = sysdb_new_attrs(data); + if (ret != EOK) { + fail("Could not create the changeset"); + return; + } + + ghostname = talloc_asprintf(data, "testuser%d", _i); + fail_unless(ghostname != NULL, "Out of memory\n"); + ret = sysdb_attrs_steal_string(data->attrs, SYSDB_GHOST, ghostname); + fail_unless(ret == EOK, "Cannot add attr\n"); + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed."); + data->attrlist[0] = SYSDB_GHOST; + data->attrlist[1] = NULL; + + /* Before the delete, all groups with gid >= _i have the testuser%_i + * as a member + */ + for (itergid = data->gid ; itergid < MBO_GROUP_BASE + NUM_GHOSTS; itergid++) { + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + itergid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname; + gv.length = strlen(ghostname); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + fail_if(el == NULL, "Cannot find ghost element\n"); + + test_gv = ldb_msg_find_val(el, &gv); + fail_if(test_gv == NULL, "Cannot find ghost user %s\n", ghostname); + } + + /* Delete the attribute */ + ret = sysdb_set_group_attr(test_ctx->sysdb, data->groupname, + data->attrs, SYSDB_MOD_DEL); + fail_unless(ret == EOK, "Cannot set group attrs\n"); + + /* After the delete, we shouldn't be able to find the ghost attribute */ + for (itergid = data->gid ; itergid < MBO_GROUP_BASE + NUM_GHOSTS; itergid++) { + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + itergid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname; + gv.length = strlen(ghostname); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + if (itergid > data->gid) { + /* The first group would have the ghost attribute gone completely */ + fail_if(el == NULL, "Cannot find ghost element\n"); + test_gv = ldb_msg_find_val(el, &gv); + fail_unless(test_gv == NULL, + "Ghost user %s unexpectedly found\n", ghostname); + } else { + fail_unless(el == NULL, "Stray values in ghost element?\n"); + } + } + + talloc_free(test_ctx); +} +END_TEST + START_TEST (test_sysdb_memberof_check_ghost) { struct sysdb_test_ctx *test_ctx; @@ -2521,6 +2796,170 @@ START_TEST (test_sysdb_memberof_check_convert) } END_TEST +START_TEST (test_sysdb_memberof_ghost_replace) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + char *ghostname_del; + char *ghostname_add; + int ret; + struct ldb_message_element *el; + struct ldb_val gv, *test_gv; + + /* 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; + data->gid = _i; + data->groupname = talloc_asprintf(data, "testgroup%d", data->gid); + + data->attrs = sysdb_new_attrs(data); + if (ret != EOK) { + fail("Could not create the changeset"); + return; + } + + /* The test replaces the testghost%i attribute with testuser%i */ + ghostname_del = talloc_asprintf(data, "testghost%d", _i - 1); + fail_unless(ghostname_del != NULL, "Out of memory\n"); + + ghostname_add = talloc_asprintf(data, "testuser%d", _i - 1); + fail_unless(ghostname_add != NULL, "Out of memory\n"); + ret = sysdb_attrs_steal_string(data->attrs, SYSDB_GHOST, ghostname_add); + fail_unless(ret == EOK, "Cannot add attr\n"); + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed."); + data->attrlist[0] = SYSDB_GHOST; + data->attrlist[1] = NULL; + + /* Before the replace, the group has the testghost%_i as a member */ + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + data->gid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname_del; + gv.length = strlen(ghostname_del); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + fail_if(el == NULL, "Cannot find ghost element\n"); + + test_gv = ldb_msg_find_val(el, &gv); + fail_if(test_gv == NULL, "Cannot find ghost user %s\n", ghostname_del); + + /* Perform the replace operation */ + ret = sysdb_set_group_attr(test_ctx->sysdb, data->groupname, + data->attrs, SYSDB_MOD_REP); + fail_unless(ret == EOK, "Cannot set group attrs\n"); + + /* After the replace, the group has the testghost%_i as a member */ + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + data->gid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname_add; + gv.length = strlen(ghostname_add); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + fail_if(el == NULL, "Cannot find ghost element\n"); + + test_gv = ldb_msg_find_val(el, &gv); + fail_if(test_gv == NULL, "Cannot find ghost user %s\n", ghostname_add); +} +END_TEST + +START_TEST (test_sysdb_memberof_ghost_replace_noop) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + char *ghostname_del; + char *ghostname_add; + int ret; + struct ldb_message_element *el; + struct ldb_val gv, *test_gv; + + /* 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; + data->gid = _i; + data->groupname = talloc_asprintf(data, "testgroup%d", data->gid); + + data->attrs = sysdb_new_attrs(data); + if (ret != EOK) { + fail("Could not create the changeset"); + return; + } + + /* The test replaces the testghost%i attribute with testuser%i */ + ghostname_del = talloc_asprintf(data, "testuser%d", _i - 1); + fail_unless(ghostname_del != NULL, "Out of memory\n"); + + ghostname_add = talloc_asprintf(data, "testuser%d", _i - 1); + fail_unless(ghostname_add != NULL, "Out of memory\n"); + ret = sysdb_attrs_steal_string(data->attrs, SYSDB_GHOST, ghostname_add); + fail_unless(ret == EOK, "Cannot add attr\n"); + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed."); + data->attrlist[0] = SYSDB_GHOST; + data->attrlist[1] = NULL; + + /* Before the replace, the group has the testghost%_i as a member */ + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + data->gid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname_del; + gv.length = strlen(ghostname_del); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + fail_if(el == NULL, "Cannot find ghost element\n"); + + test_gv = ldb_msg_find_val(el, &gv); + fail_if(test_gv == NULL, "Cannot find ghost user %s\n", ghostname_del); + + /* Perform the replace operation */ + ret = sysdb_set_group_attr(test_ctx->sysdb, data->groupname, + data->attrs, SYSDB_MOD_REP); + fail_unless(ret == EOK, "Cannot set group attrs\n"); + + /* After the replace, the group has the testghost%_i as a member */ + ret = sysdb_search_group_by_gid(data, test_ctx->sysdb, + data->gid, + data->attrlist, &data->msg); + fail_if(ret != EOK, "Cannot retrieve group %llu\n", + (unsigned long long) data->gid); + + gv.data = (uint8_t *) ghostname_add; + gv.length = strlen(ghostname_add); + + el = ldb_msg_find_element(data->msg, SYSDB_GHOST); + fail_if(el == NULL, "Cannot find ghost element\n"); + + test_gv = ldb_msg_find_val(el, &gv); + fail_if(test_gv == NULL, "Cannot find ghost user %s\n", ghostname_add); +} +END_TEST + START_TEST (test_sysdb_memberof_user_cleanup) { struct sysdb_test_ctx *test_ctx; @@ -4456,12 +4895,53 @@ Suite *create_sysdb_suite(void) /* Check the members and ghosts are there as appropriate */ tcase_add_loop_test(tc_memberof, test_sysdb_memberof_check_convert, MBO_GROUP_BASE , MBO_GROUP_BASE + NUM_GHOSTS); + /* Rename the other half */ + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_ghost_replace, + MBO_GROUP_BASE + NUM_GHOSTS/2 + 1, + MBO_GROUP_BASE + NUM_GHOSTS); + /* Attempt to replace with the same data to check if noop works correctly */ + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_ghost_replace_noop, + MBO_GROUP_BASE + NUM_GHOSTS/2 + 1, + MBO_GROUP_BASE + NUM_GHOSTS); /* Remove the real users */ tcase_add_loop_test(tc_memberof, test_sysdb_memberof_user_cleanup, MBO_GROUP_BASE , MBO_GROUP_BASE + NUM_GHOSTS/2); tcase_add_loop_test(tc_memberof, test_sysdb_remove_local_group_by_gid, MBO_GROUP_BASE , MBO_GROUP_BASE + NUM_GHOSTS); + /* ghost users - memberof mod_del */ + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_store_group_with_ghosts, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_check_nested_ghosts, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_mod_del, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + tcase_add_loop_test(tc_memberof, test_sysdb_remove_local_group_by_gid, + MBO_GROUP_BASE , MBO_GROUP_BASE + NUM_GHOSTS); + + /* ghost users - memberof mod_add */ + /* Add groups without ghosts first */ + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_store_group, 0, 10); + /* Add ghosts to groups so that they propagate */ + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_mod_add, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + /* Check if the ghosts in fact propagated */ + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_check_nested_ghosts, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + /* Clean up */ + tcase_add_loop_test(tc_memberof, test_sysdb_remove_local_group_by_gid, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + + /* ghost users - replace */ + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_store_group_with_ghosts, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_check_nested_ghosts, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_mod_replace, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + tcase_add_loop_test(tc_memberof, test_sysdb_remove_local_group_by_gid, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + suite_add_tcase(s, tc_memberof); TCase *tc_subdomain = tcase_create("SYSDB sub-domain Tests"); -- 1.8.0.1