>From 7b963c4d6ffcfb793feb22ca9a966a3f4c9d284f Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 27 Nov 2012 16:09:23 +0100 Subject: [PATCH 4/4] MEMBEROF: Implement the modify operation for ghost users --- 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 5df5e1c3dde6c6174f63a22bf06acd50ee0cb2c4..0d16ac966a76605899103a71519bbfda33387a66 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; }; @@ -2256,7 +2265,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) { @@ -2315,7 +2325,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 */ @@ -2596,7 +2607,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, @@ -2708,7 +2720,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, @@ -2761,19 +2774,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; @@ -2816,15 +2840,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, @@ -2997,13 +3021,21 @@ 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 (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 (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, @@ -3111,8 +3143,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; @@ -3132,14 +3265,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; @@ -3147,18 +3272,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; @@ -3183,25 +3332,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, @@ -3242,6 +3405,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