diff options
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/auth-tests.c | 343 | ||||
-rw-r--r-- | src/tests/check_and_open-tests.c | 218 | ||||
-rw-r--r-- | src/tests/common.c | 109 | ||||
-rw-r--r-- | src/tests/common.h | 21 | ||||
-rw-r--r-- | src/tests/fail_over-tests.c | 304 | ||||
-rw-r--r-- | src/tests/files-tests.c | 323 | ||||
-rw-r--r-- | src/tests/find_uid-tests.c | 125 | ||||
-rw-r--r-- | src/tests/ipa_ldap_opt-tests.c | 59 | ||||
-rw-r--r-- | src/tests/ipa_timerules-tests.c | 580 | ||||
-rw-r--r-- | src/tests/krb5_utils-tests.c | 307 | ||||
-rw-r--r-- | src/tests/python-test.py | 445 | ||||
-rw-r--r-- | src/tests/refcount-tests.c | 232 | ||||
-rw-r--r-- | src/tests/resolv-tests.c | 598 | ||||
-rw-r--r-- | src/tests/stress-tests.c | 328 | ||||
-rw-r--r-- | src/tests/strtonum-tests.c | 455 | ||||
-rw-r--r-- | src/tests/sysdb-tests.c | 3330 |
16 files changed, 7777 insertions, 0 deletions
diff --git a/src/tests/auth-tests.c b/src/tests/auth-tests.c new file mode 100644 index 000000000..71215bcd2 --- /dev/null +++ b/src/tests/auth-tests.c @@ -0,0 +1,343 @@ +/* + SSSD + + Test for local authentication utilities + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2010 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <string.h> +#include <popt.h> +#include <talloc.h> +#include <tevent.h> + +#include <check.h> + +#include "util/util.h" +#include "confdb/confdb.h" +#include "db/sysdb.h" + +#define TESTS_PATH "tests_auth" +#define TEST_CONF_FILE "tests_conf.ldb" + +struct sysdb_test_ctx { + struct sysdb_ctx *sysdb; + struct confdb_ctx *confdb; + struct tevent_context *ev; + struct sss_domain_info *domain; +}; + +static int setup_sysdb_tests(struct sysdb_test_ctx **ctx) +{ + struct sysdb_test_ctx *test_ctx; + char *conf_db; + int ret; + + const char *val[2]; + val[1] = NULL; + + /* Create tests directory if it doesn't exist */ + /* (relative to current dir) */ + ret = mkdir(TESTS_PATH, 0775); + if (ret == -1 && errno != EEXIST) { + fail("Could not create %s directory", TESTS_PATH); + return EFAULT; + } + + test_ctx = talloc_zero(NULL, struct sysdb_test_ctx); + if (test_ctx == NULL) { + fail("Could not allocate memory for test context"); + return ENOMEM; + } + + /* Create an event context + * It will not be used except in confdb_init and sysdb_init + */ + test_ctx->ev = tevent_context_init(test_ctx); + if (test_ctx->ev == NULL) { + fail("Could not create event context"); + talloc_free(test_ctx); + return EIO; + } + + conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_FILE); + if (conf_db == NULL) { + fail("Out of memory, aborting!"); + talloc_free(test_ctx); + return ENOMEM; + } + DEBUG(3, ("CONFDB: %s\n", conf_db)); + + /* Connect to the conf db */ + ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db); + if (ret != EOK) { + fail("Could not initialize connection to the confdb"); + talloc_free(test_ctx); + return ret; + } + + val[0] = "LOCAL"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "domains", val); + if (ret != EOK) { + fail("Could not initialize domains placeholder"); + talloc_free(test_ctx); + return ret; + } + + val[0] = "local"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/LOCAL", "id_provider", val); + if (ret != EOK) { + fail("Could not initialize provider"); + talloc_free(test_ctx); + return ret; + } + + val[0] = "TRUE"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/LOCAL", "enumerate", val); + if (ret != EOK) { + fail("Could not initialize LOCAL domain"); + talloc_free(test_ctx); + return ret; + } + + val[0] = "TRUE"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/LOCAL", "cache_credentials", val); + if (ret != EOK) { + fail("Could not initialize LOCAL domain"); + talloc_free(test_ctx); + return ret; + } + + ret = confdb_get_domain(test_ctx->confdb, "local", &test_ctx->domain); + if (ret != EOK) { + fail("Could not retrieve LOCAL domain"); + talloc_free(test_ctx); + return ret; + } + + ret = sysdb_domain_init(test_ctx, test_ctx->ev, + test_ctx->domain, TESTS_PATH, &test_ctx->sysdb); + if (ret != EOK) { + fail("Could not initialize connection to the sysdb (%d)", ret); + talloc_free(test_ctx); + return ret; + } + + *ctx = test_ctx; + return EOK; +} + +static void do_failed_login_test(uint32_t failed_login_attempts, + time_t last_failed_login, + int offline_failed_login_attempts, + int offline_failed_login_delay, + int expected_result, + int expected_counter, + time_t expected_delay) +{ + struct sysdb_test_ctx *test_ctx; + int ret; + const char *val[2]; + val[1] = NULL; + struct ldb_message *ldb_msg; + uint32_t returned_failed_login_attempts; + time_t delayed_until; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + fail_unless(ret == EOK, "Could not set up the test"); + + val[0] = talloc_asprintf(test_ctx, "%u", offline_failed_login_attempts); + fail_unless(val[0] != NULL, "talloc_sprintf failed"); + ret = confdb_add_param(test_ctx->confdb, true, + "config/pam", CONFDB_PAM_FAILED_LOGIN_ATTEMPTS, val); + fail_unless(ret == EOK, "Could not set offline_failed_login_attempts"); + + val[0] = talloc_asprintf(test_ctx, "%u", offline_failed_login_delay); + ret = confdb_add_param(test_ctx->confdb, true, + "config/pam", CONFDB_PAM_FAILED_LOGIN_DELAY, val); + fail_unless(ret == EOK, "Could not set offline_failed_login_delay"); + + ldb_msg = ldb_msg_new(test_ctx); + fail_unless(ldb_msg != NULL, "ldb_msg_new failed"); + + ret = ldb_msg_add_fmt(ldb_msg, SYSDB_FAILED_LOGIN_ATTEMPTS, "%u", + failed_login_attempts); + fail_unless(ret == EOK, "ldb_msg_add_string failed"); + + ret = ldb_msg_add_fmt(ldb_msg, SYSDB_LAST_FAILED_LOGIN, "%lld", + (long long) last_failed_login); + fail_unless(ret == EOK, "ldb_msg_add_string failed"); + + ret = check_failed_login_attempts(test_ctx, test_ctx->confdb, ldb_msg, + &returned_failed_login_attempts, + &delayed_until); + fail_unless(ret == expected_result, + "check_failed_login_attempts returned wrong error code, " + "expected [%d], got [%d]", expected_result, ret); + + fail_unless(returned_failed_login_attempts == expected_counter, + "check_failed_login_attempts returned wrong number of failed " + "login attempts, expected [%d], got [%d]", + expected_counter, failed_login_attempts); + + fail_unless(delayed_until == expected_delay, + "check_failed_login_attempts wrong delay, " + "expected [%d], got [%d]", + expected_delay, delayed_until); + + talloc_free(test_ctx); +} + +START_TEST(test_failed_login_attempts) +{ + time_t now; + + /* if offline_failed_login_attempts == 0 a login is never denied */ + do_failed_login_test(0, 0, 0, 5, EOK, 0, -1); + do_failed_login_test(0, time(NULL), 0, 5, EOK, 0, -1); + do_failed_login_test(2, 0, 0, 5, EOK, 2, -1); + do_failed_login_test(2, time(NULL), 0, 5, EOK, 2, -1); + + do_failed_login_test(0, 0, 0, 0, EOK, 0, -1); + do_failed_login_test(0, time(NULL), 0, 0, EOK, 0, -1); + do_failed_login_test(2, 0, 0, 0, EOK, 2, -1); + do_failed_login_test(2, time(NULL), 0, 0, EOK, 2, -1); + + /* if offline_failed_login_attempts != 0 and + * offline_failed_login_delay == 0 a login is denied if the number of + * failed attempts >= offline_failed_login_attempts */ + do_failed_login_test(0, 0, 2, 0, EOK, 0, -1); + do_failed_login_test(0, time(NULL), 2, 0, EOK, 0, -1); + do_failed_login_test(2, 0, 2, 0, EACCES, 2, -1); + do_failed_login_test(2, time(NULL), 2, 0, EACCES, 2, -1); + + /* if offline_failed_login_attempts != 0 and + * offline_failed_login_delay != 0 a login is denied only if the number of + * failed attempts >= offline_failed_login_attempts AND the last failed + * login attempt is not longer than offline_failed_login_delay ago */ + do_failed_login_test(0, 0, 2, 5, EOK, 0, -1); + do_failed_login_test(0, time(NULL), 2, 5, EOK, 0, -1); + do_failed_login_test(2, 0, 2, 5, EOK, 0, -1); + now = time(NULL); + do_failed_login_test(2, now, 2, 5, EACCES, 2, (now + 5 * 60)); + +} +END_TEST + +Suite *auth_suite (void) +{ + Suite *s = suite_create ("auth"); + + TCase *tc_auth = tcase_create ("auth"); + + tcase_add_test (tc_auth, test_failed_login_attempts); + tcase_set_timeout(tc_auth, 60); + + suite_add_tcase (s, tc_auth); + + return s; +} + +static int clean_db_dir(void) +{ + int ret; + + ret = unlink(TESTS_PATH"/"TEST_CONF_FILE); + if (ret != EOK && errno != ENOENT) { + fprintf(stderr, "Could not delete the test config ldb file (%d) (%s)\n", + errno, strerror(errno)); + return ret; + } + + ret = unlink(TESTS_PATH"/"LOCAL_SYSDB_FILE); + if (ret != EOK && errno != ENOENT) { + fprintf(stderr, "Could not delete the test config ldb file (%d) (%s)\n", + errno, strerror(errno)); + return ret; + } + + ret = rmdir(TESTS_PATH); + if (ret != EOK && errno != ENOENT) { + fprintf(stderr, "Could not delete the test directory (%d) (%s)\n", + errno, strerror(errno)); + return ret; + } + + return EOK; +} + +int main(int argc, const char *argv[]) +{ + int ret; + int opt; + int failure_count; + poptContext pc; + Suite *s = auth_suite (); + SRunner *sr = srunner_create (s); + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + { NULL } + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + ret = clean_db_dir(); + if (ret != EOK) { + fprintf(stderr, "Could not delete the db directory (%d) (%s)\n", + errno, strerror(errno)); + return EXIT_FAILURE; + } + + srunner_run_all(sr, CK_ENV); + failure_count = srunner_ntests_failed (sr); + srunner_free (sr); + if (failure_count == 0) { + ret = clean_db_dir(); + if (ret != EOK) { + fprintf(stderr, "Could not delete the db directory (%d) (%s)\n", + errno, strerror(errno)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} diff --git a/src/tests/check_and_open-tests.c b/src/tests/check_and_open-tests.c new file mode 100644 index 000000000..b0d638b55 --- /dev/null +++ b/src/tests/check_and_open-tests.c @@ -0,0 +1,218 @@ +/* + SSSD + + Utilities tests check_and_open + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <check.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "util/util.h" + +#define SUFFIX ".symlink" + +#define FILENAME_TEMPLATE "check_and_open-tests-XXXXXX" +char *filename; +uid_t uid; +gid_t gid; +mode_t mode; +int fd; + +void setup_check_and_open(void) +{ + int ret; + + filename = strdup(FILENAME_TEMPLATE); + fail_unless(filename != NULL, "strdup failed"); + ret = mkstemp(filename); + fail_unless(ret != -1, "mkstemp failed [%d][%s]", errno, strerror(errno)); + close(ret); + + uid = getuid(); + gid = getgid(); + mode = (S_IRUSR | S_IWUSR); + fd = -1; +} + +void teardown_check_and_open(void) +{ + int ret; + + if (fd != -1) { + ret = close(fd); + fail_unless(ret == 0, "close failed [%d][%s]", errno, strerror(errno)); + } + + fail_unless(filename != NULL, "unknown filename"); + ret = unlink(filename); + free(filename); + fail_unless(ret == 0, "unlink failed [%d][%s]", errno, strerror(errno)); +} + +START_TEST(test_wrong_filename) +{ + int ret; + + ret = check_and_open_readonly("/bla/bla/bla", &fd, uid, gid, mode); + fail_unless(ret == ENOENT, + "check_and_open_readonly succeeded on non-existing file"); + fail_unless(fd == -1, "check_and_open_readonly file descriptor not -1"); +} +END_TEST + +START_TEST(test_symlink) +{ + int ret; + char *newpath; + size_t newpath_length; + + newpath_length = strlen(filename) + strlen(SUFFIX) + 1; + newpath = malloc((newpath_length) * sizeof(char)); + fail_unless(newpath != NULL, "malloc failed"); + + ret = snprintf(newpath, newpath_length, "%s%s", filename, SUFFIX); + fail_unless(ret == newpath_length - 1, + "snprintf failed: expected [%d] got [%d]", newpath_length -1, + ret); + + ret = symlink(filename, newpath); + fail_unless(ret == 0, "symlink failed [%d][%s]", ret, strerror(ret)); + + ret = check_and_open_readonly(newpath, &fd, uid, gid, mode); + unlink(newpath); + fail_unless(ret == EINVAL, + "check_and_open_readonly succeeded on symlink"); + fail_unless(fd == -1, "check_and_open_readonly file descriptor not -1"); +} +END_TEST + +START_TEST(test_not_regular_file) +{ + int ret; + + ret = check_and_open_readonly("/dev/null", &fd, uid, gid, mode); + fail_unless(ret == EINVAL, + "check_and_open_readonly succeeded on non-regular file"); + fail_unless(fd == -1, "check_and_open_readonly file descriptor not -1"); +} +END_TEST + +START_TEST(test_wrong_uid) +{ + int ret; + + ret = check_and_open_readonly(filename, &fd, uid+1, gid, mode); + fail_unless(ret == EINVAL, + "check_and_open_readonly succeeded with wrong uid"); + fail_unless(fd == -1, "check_and_open_readonly file descriptor not -1"); +} +END_TEST + +START_TEST(test_wrong_gid) +{ + int ret; + + ret = check_and_open_readonly(filename, &fd, uid, gid+1, mode); + fail_unless(ret == EINVAL, + "check_and_open_readonly succeeded with wrong gid"); + fail_unless(fd == -1, "check_and_open_readonly file descriptor not -1"); +} +END_TEST + +START_TEST(test_wrong_permission) +{ + int ret; + + ret = check_and_open_readonly(filename, &fd, uid, gid, (mode|S_IWOTH)); + fail_unless(ret == EINVAL, + "check_and_open_readonly succeeded with wrong mode"); + fail_unless(fd == -1, "check_and_open_readonly file descriptor not -1"); +} +END_TEST + +START_TEST(test_ok) +{ + int ret; + + ret = check_and_open_readonly(filename, &fd, uid, gid, mode); + fail_unless(ret == EOK, + "check_and_open_readonly failed"); + fail_unless(fd >= 0, + "check_and_open_readonly returned illegal file descriptor"); +} +END_TEST + +START_TEST(test_write) +{ + int ret; + ssize_t size; + errno_t my_errno; + + ret = check_and_open_readonly(filename, &fd, uid, gid, mode); + fail_unless(ret == EOK, + "check_and_open_readonly failed"); + fail_unless(fd >= 0, + "check_and_open_readonly returned illegal file descriptor"); + + size = write(fd, "abc", 3); + my_errno = errno; + fail_unless(size == -1, "check_and_open_readonly file is not readonly"); + fail_unless(my_errno == EBADF, + "write failed for other reason than readonly"); +} +END_TEST + +Suite *check_and_open_suite (void) +{ + Suite *s = suite_create ("check_and_open"); + + TCase *tc_check_and_open_readonly = tcase_create ("check_and_open_readonly"); + tcase_add_checked_fixture (tc_check_and_open_readonly, + setup_check_and_open, + teardown_check_and_open); + tcase_add_test (tc_check_and_open_readonly, test_wrong_filename); + tcase_add_test (tc_check_and_open_readonly, test_not_regular_file); + tcase_add_test (tc_check_and_open_readonly, test_symlink); + tcase_add_test (tc_check_and_open_readonly, test_wrong_uid); + tcase_add_test (tc_check_and_open_readonly, test_wrong_gid); + tcase_add_test (tc_check_and_open_readonly, test_wrong_permission); + tcase_add_test (tc_check_and_open_readonly, test_ok); + tcase_add_test (tc_check_and_open_readonly, test_write); + suite_add_tcase (s, tc_check_and_open_readonly); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s = check_and_open_suite (); + SRunner *sr = srunner_create (s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/src/tests/common.c b/src/tests/common.c new file mode 100644 index 000000000..50dc61b16 --- /dev/null +++ b/src/tests/common.c @@ -0,0 +1,109 @@ +/* + SSSD + + Common utilities for check-based tests using talloc. + + Authors: + Martin Nagy <mnagy@redhat.com> + + Copyright (C) Red Hat, Inc 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <check.h> +#include <stdio.h> + +#include "tests/common.h" +#include "util/dlinklist.h" +#include "util/util.h" + +TALLOC_CTX *global_talloc_context = NULL; + +struct size_snapshot { + struct size_snapshot *prev; + struct size_snapshot *next; + + TALLOC_CTX *ctx; + size_t bytes_allocated; +}; + +static struct size_snapshot *snapshot_stack; + +void +_check_leaks(TALLOC_CTX *ctx, size_t bytes, const char *location) +{ + size_t bytes_allocated; + + bytes_allocated = talloc_total_size(ctx); + if (bytes_allocated != bytes) { + fprintf(stderr, "Leak report for %s:\n", location); + talloc_report_full(ctx, stderr); + fail("%s: memory leaks detected, %d bytes still allocated", + location, bytes_allocated - bytes); + } +} + +void +check_leaks_push(TALLOC_CTX *ctx) +{ + struct size_snapshot *snapshot; + + snapshot = talloc(NULL, struct size_snapshot); + snapshot->ctx = ctx; + snapshot->bytes_allocated = talloc_total_size(ctx); + DLIST_ADD(snapshot_stack, snapshot); +} + +void +_check_leaks_pop(TALLOC_CTX *ctx, const char *location) +{ + struct size_snapshot *snapshot; + TALLOC_CTX *old_ctx; + size_t bytes_allocated; + + if (snapshot_stack == NULL) { + fail("%s: trying to pop an empty stack"); + } + + snapshot = snapshot_stack; + DLIST_REMOVE(snapshot_stack, snapshot); + + old_ctx = snapshot->ctx; + bytes_allocated = snapshot->bytes_allocated; + + fail_if(old_ctx != ctx, "Bad push/pop order"); + + talloc_zfree(snapshot); + _check_leaks(old_ctx, bytes_allocated, location); +} + +void +leak_check_setup(void) +{ + talloc_enable_null_tracking(); + global_talloc_context = talloc_new(NULL); + fail_unless(global_talloc_context != NULL, "talloc_new failed"); + check_leaks_push(global_talloc_context); +} + +void +leak_check_teardown(void) +{ + check_leaks_pop(global_talloc_context); + if (snapshot_stack != NULL) { + fail("Exiting with a non-empty stack"); + } + check_leaks(global_talloc_context, 0); +} diff --git a/src/tests/common.h b/src/tests/common.h new file mode 100644 index 000000000..0e954d7db --- /dev/null +++ b/src/tests/common.h @@ -0,0 +1,21 @@ +#ifndef __TESTS_COMMON_H__ +#define __TESTS_COMMON_H__ + +#include <talloc.h> + +extern TALLOC_CTX *global_talloc_context; + +#define check_leaks(ctx, bytes) _check_leaks((ctx), (bytes), __location__) +void _check_leaks(TALLOC_CTX *ctx, + size_t bytes, + const char *location); + +void check_leaks_push(TALLOC_CTX *ctx); + +#define check_leaks_pop(ctx) _check_leaks_pop((ctx), __location__) +void _check_leaks_pop(TALLOC_CTX *ctx, const char *location); + +void leak_check_setup(void); +void leak_check_teardown(void); + +#endif /* !__TESTS_COMMON_H__ */ diff --git a/src/tests/fail_over-tests.c b/src/tests/fail_over-tests.c new file mode 100644 index 000000000..e794f03b5 --- /dev/null +++ b/src/tests/fail_over-tests.c @@ -0,0 +1,304 @@ +/* + SSSD + + Fail over tests. + + Authors: + Martin Nagy <mnagy@redhat.com> + + Copyright (C) Red Hat, Inc 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <arpa/inet.h> + +#include <check.h> +#include <popt.h> +#include <stdlib.h> +#include <stdio.h> +#include <talloc.h> +#include <tevent.h> + +#include "resolv/async_resolv.h" +#include "tests/common.h" +#include "util/util.h" + +/* Interface under test */ +#include "providers/fail_over.h" + +int use_net_test; + +struct test_ctx { + struct tevent_context *ev; + struct resolv_ctx *resolv; + struct fo_ctx *fo_ctx; + int tasks; +}; + +struct task { + struct test_ctx *test_ctx; + const char *location; + int recv; + int port; + int new_server_status; + int new_port_status; +}; + +static struct test_ctx * +setup_test(void) +{ + struct test_ctx *ctx; + int ret; + + ctx = talloc_zero(global_talloc_context, struct test_ctx); + fail_if(ctx == NULL, "Could not allocate memory for test context"); + + ctx->ev = tevent_context_init(ctx); + if (ctx->ev == NULL) { + talloc_free(ctx); + fail("Could not init tevent context"); + } + + ret = resolv_init(ctx, ctx->ev, 5, &ctx->resolv); + if (ret != EOK) { + talloc_free(ctx); + fail("Could not init resolv context"); + } + + ctx->fo_ctx = fo_context_init(ctx, 5 * 60); + if (ctx->fo_ctx == NULL) { + talloc_free(ctx); + fail("Could not init fail over context"); + } + + return ctx; +} + +static void +test_loop(struct test_ctx *data) +{ + while (data->tasks != 0) + tevent_loop_once(data->ev); +} + +START_TEST(test_fo_new_service) +{ + int i; + int ret; + struct test_ctx *ctx; + struct fo_service *service; + struct fo_service *services[10]; + + ctx = setup_test(); + check_leaks_push(ctx); + + for (i = 0; i < 10; i++) { + char buf[16]; + sprintf(buf, "service_%d", i); + + check_leaks_push(ctx); + ret = fo_new_service(ctx->fo_ctx, buf, &services[i]); + fail_if(ret != EOK); + } + + ret = fo_new_service(ctx->fo_ctx, "service_3", &service); + fail_if(ret != EEXIST); + + for (i = 9; i >= 0; i--) { + char buf[16]; + sprintf(buf, "service_%d", i); + + ret = fo_get_service(ctx->fo_ctx, buf, &service); + fail_if(ret != EOK); + fail_if(service != services[i]); + talloc_free(service); + check_leaks_pop(ctx); + + ret = fo_get_service(ctx->fo_ctx, buf, &service); + fail_if(ret != ENOENT); + } + + check_leaks_pop(ctx); + talloc_free(ctx); +} +END_TEST + +static void +test_resolve_service_callback(struct tevent_req *req) +{ + uint64_t recv_status; + int port; + struct task *task; + struct fo_server *server = NULL; + struct hostent *he; + int i; + + task = tevent_req_callback_data(req, struct task); + + task->test_ctx->tasks--; + + recv_status = fo_resolve_service_recv(req, &server); + talloc_free(req); + fail_if(recv_status != task->recv, "%s: Expected return of %d, got %d", + task->location, task->recv, recv_status); + if (recv_status != EOK) + return; + fail_if(server == NULL); + port = fo_get_server_port(server); + fail_if(port != task->port, "%s: Expected port %d, got %d", task->location, + task->port, port); + + if (task->new_port_status >= 0) + fo_set_port_status(server, task->new_port_status); + if (task->new_server_status >= 0) + fo_set_server_status(server, task->new_server_status); + + he = fo_get_server_hostent(server); + fail_if(he == NULL, "%s: fo_get_server_hostent() returned NULL"); + for (i = 0; he->h_addr_list[i]; i++) { + char buf[256]; + + inet_ntop(he->h_addrtype, he->h_addr_list[i], buf, sizeof(buf)); + fail_if(strcmp(buf, "127.0.0.1") != 0 && strcmp(buf, "::1") != 0); + } + +} + +#define get_request(a, b, c, d, e, f) \ + _get_request(a, b, c, d, e, f, __location__) + +static void +_get_request(struct test_ctx *test_ctx, struct fo_service *service, + int expected_recv, int expected_port, int new_port_status, + int new_server_status, const char *location) +{ + struct tevent_req *req; + struct task *task; + + task = talloc(test_ctx, struct task); + fail_if(task == NULL); + + task->test_ctx = test_ctx; + task->recv = expected_recv; + task->port = expected_port; + task->new_port_status = new_port_status; + task->new_server_status = new_server_status; + task->location = location; + test_ctx->tasks++; + + req = fo_resolve_service_send(test_ctx, test_ctx->ev, test_ctx->resolv, service); + fail_if(req == NULL, "%s: fo_resolve_service_send() failed", location); + + tevent_req_set_callback(req, test_resolve_service_callback, task); + test_loop(test_ctx); +} + +START_TEST(test_fo_resolve_service) +{ + struct test_ctx *ctx; + struct fo_service *service[2]; + + ctx = setup_test(); + fail_if(ctx == NULL); + + /* Add service. */ + fail_if(fo_new_service(ctx->fo_ctx, "http", &service[0]) != EOK); + + fail_if(fo_new_service(ctx->fo_ctx, "ldap", &service[1]) != EOK); + + /* Add servers. */ + fail_if(fo_add_server(service[0], "localhost", 20, NULL) != EOK); + fail_if(fo_add_server(service[0], "127.0.0.1", 80, NULL) != EOK); + + fail_if(fo_add_server(service[1], "localhost", 30, NULL) != EOK); + fail_if(fo_add_server(service[1], "127.0.0.1", 389, NULL) != EOK); + fail_if(fo_add_server(service[1], "127.0.0.1", 389, NULL) != EEXIST); + + /* Make requests. */ + get_request(ctx, service[0], EOK, 20, PORT_WORKING, -1); + get_request(ctx, service[0], EOK, 20, -1, SERVER_NOT_WORKING); + get_request(ctx, service[0], EOK, 80, PORT_WORKING, -1); + get_request(ctx, service[0], EOK, 80, PORT_NOT_WORKING, -1); + get_request(ctx, service[0], ENOENT, 0, -1, -1); + + get_request(ctx, service[1], EOK, 389, PORT_WORKING, -1); + get_request(ctx, service[1], EOK, 389, -1, SERVER_NOT_WORKING); + get_request(ctx, service[1], ENOENT, 0, -1, -1); + + talloc_free(ctx); +} +END_TEST + +Suite * +create_suite(void) +{ + Suite *s = suite_create("fail_over"); + + TCase *tc = tcase_create("FAIL_OVER Tests"); + + tcase_add_checked_fixture(tc, leak_check_setup, leak_check_teardown); + /* Do some testing */ + tcase_add_test(tc, test_fo_new_service); + tcase_add_test(tc, test_fo_resolve_service); + if (use_net_test) { + } + /* Add all test cases to the test suite */ + suite_add_tcase(s, tc); + + return s; +} + +int +main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + int failure_count; + Suite *suite; + SRunner *sr; + int debug = 0; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "debug-level", 'd', POPT_ARG_INT, &debug, 0, "Set debug level", NULL }, + { "use-net-test", 'n', POPT_ARG_NONE, 0, 'n', "Run tests that need an active internet connection", NULL }, + POPT_TABLEEND + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + case 'n': + use_net_test = 1; + break; + + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + debug_level = debug; + + suite = create_suite(); + sr = srunner_create(suite); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + failure_count = srunner_ntests_failed(sr); + srunner_free(sr); + return (failure_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/src/tests/files-tests.c b/src/tests/files-tests.c new file mode 100644 index 000000000..90b971779 --- /dev/null +++ b/src/tests/files-tests.c @@ -0,0 +1,323 @@ +/* + * Authors: + * Jakub Hrozek <jhrozek@redhat.com> + * + * Copyright (C) 2008 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 3 or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> +#include <check.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <talloc.h> +#include <popt.h> + +#include "config.h" +#include "tools/tools_util.h" +#include "util/util.h" + +static char tpl_dir[] = "file-tests-dir-XXXXXX"; +static char *dir_path; +static char *dst_path; +static uid_t uid; +static gid_t gid; +static TALLOC_CTX *test_ctx = NULL; + +static void setup_files_test(void) +{ + /* create a temporary directory that we fill with stuff later on */ + test_ctx = talloc_new(NULL); + dir_path = mkdtemp(talloc_strdup(test_ctx, tpl_dir)); + dst_path = mkdtemp(talloc_strdup(test_ctx, tpl_dir)); + + uid = getuid(); + gid = getgid(); +} + +static void teardown_files_test(void) +{ + char *cmd = NULL; + + /* OK this is crude but since the functions to remove tree are under test.. */ + if (dir_path && test_ctx) { + cmd = talloc_asprintf(test_ctx, "/bin/rm -rf %s\n", dir_path); + system(cmd); + } + if (dst_path && test_ctx) { + cmd = talloc_asprintf(test_ctx, "/bin/rm -rf %s\n", dst_path); + system(cmd); + } + + /* clean up */ + talloc_zfree(test_ctx); +} + +static int create_simple_file(const char *name, const char *content) +{ + int fd; + ssize_t size; + int ret; + + fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0700); + fail_if(fd == -1, "Cannot create simple file\n"); + + size = write(fd, "abc", 3); + fail_if(size == -1, "Cannot write to file\n"); + + ret = fsync(fd); + fail_if(ret == -1, "Cannot sync file\n"); + + ret = close(fd); + fail_if(ret == -1, "Cannot close file\n"); + + return ret; +} + +START_TEST(test_remove_tree) +{ + int ret; + char origpath[PATH_MAX+1]; + + errno = 0; + getcwd(origpath, PATH_MAX); + fail_unless(errno == 0, "Cannot getcwd\n"); + + DEBUG(5, ("About to delete %s\n", dir_path)); + + /* create a file */ + ret = chdir(dir_path); + fail_if(ret == -1, "Cannot chdir1\n"); + + ret = create_simple_file("bar", "bar"); + fail_if(ret == -1, "Cannot create file1\n"); + + /* create a subdir and file inside it */ + ret = mkdir("subdir", 0700); + fail_if(ret == -1, "Cannot create subdir\n"); + + ret = chdir("subdir"); + fail_if(ret == -1, "Cannot chdir\n"); + + ret = create_simple_file("foo", "foo"); + fail_if(ret == -1, "Cannot create file\n"); + + /* create another subdir, empty this time */ + ret = mkdir("subdir2", 0700); + fail_if(ret == -1, "Cannot create subdir\n"); + + ret = chdir(origpath); + fail_if(ret == -1, "Cannot chdir2\n"); + + /* go back */ + ret = chdir(origpath); + fail_if(ret == -1, "Cannot chdir\n"); + + /* and finally wipe it out.. */ + ret = remove_tree(dir_path); + fail_unless(ret == EOK, "remove_tree failed\n"); + + /* check if really gone */ + ret = access(dir_path, F_OK); + fail_unless(ret == -1, "directory still there after remove_tree\n"); +} +END_TEST + +START_TEST(test_simple_copy) +{ + int ret; + char origpath[PATH_MAX+1]; + char *tmp; + int fd = -1; + + errno = 0; + getcwd(origpath, PATH_MAX); + fail_unless(errno == 0, "Cannot getcwd\n"); + + /* create a file */ + ret = chdir(dir_path); + fail_if(ret == -1, "Cannot chdir1\n"); + + ret = create_simple_file("bar", "bar"); + fail_if(ret == -1, "Cannot create file1\n"); + + /* create a subdir and file inside it */ + ret = mkdir("subdir", 0700); + fail_if(ret == -1, "Cannot create subdir\n"); + + ret = chdir("subdir"); + fail_if(ret == -1, "Cannot chdir\n"); + + ret = create_simple_file("foo", "foo"); + fail_if(ret == -1, "Cannot create file\n"); + + /* go back */ + ret = chdir(origpath); + fail_if(ret == -1, "Cannot chdir\n"); + + /* and finally copy.. */ + DEBUG(5, ("Will copy from '%s' to '%s'\n", dir_path, dst_path)); + ret = copy_tree(dir_path, dst_path, uid, gid); + fail_unless(ret == EOK, "copy_tree failed\n"); + + /* check if really copied */ + ret = access(dst_path, F_OK); + fail_unless(ret == 0, "destination directory not there\n"); + + tmp = talloc_asprintf(test_ctx, "%s/bar", dst_path); + ret = check_and_open_readonly(tmp, &fd, uid, gid, 0700); + fail_unless(ret == EOK, "Cannot open %s\n"); + close(fd); + talloc_free(tmp); +} +END_TEST + +START_TEST(test_copy_symlink) +{ + int ret; + char origpath[PATH_MAX+1]; + char *tmp; + struct stat statbuf; + + errno = 0; + getcwd(origpath, PATH_MAX); + fail_unless(errno == 0, "Cannot getcwd\n"); + + /* create a subdir */ + ret = chdir(dir_path); + fail_if(ret == -1, "Cannot chdir\n"); + + ret = create_simple_file("footarget", "foo"); + fail_if(ret == -1, "Cannot create file\n"); + + ret = symlink("footarget", "foolink"); + fail_if(ret == -1, "Cannot create symlink\n"); + + /* go back */ + ret = chdir(origpath); + fail_if(ret == -1, "Cannot chdir\n"); + + /* and finally copy.. */ + DEBUG(5, ("Will copy from '%s' to '%s'\n", dir_path, dst_path)); + ret = copy_tree(dir_path, dst_path, uid, gid); + fail_unless(ret == EOK, "copy_tree failed\n"); + + /* check if really copied */ + ret = access(dst_path, F_OK); + fail_unless(ret == 0, "destination directory not there\n"); + + tmp = talloc_asprintf(test_ctx, "%s/foolink", dst_path); + ret = lstat(tmp, &statbuf); + fail_unless(ret == 0, "cannot stat the symlink %s\n", tmp); + fail_unless(S_ISLNK(statbuf.st_mode), "%s not a symlink?\n", tmp); + talloc_free(tmp); +} +END_TEST + +START_TEST(test_copy_node) +{ + int ret; + char origpath[PATH_MAX+1]; + char *tmp; + struct stat statbuf; + + errno = 0; + getcwd(origpath, PATH_MAX); + fail_unless(errno == 0, "Cannot getcwd\n"); + + /* create a node */ + ret = chdir(dir_path); + fail_if(ret == -1, "Cannot chdir\n"); + + ret = mknod("testnode", S_IFIFO | S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, 0); + fail_unless(ret == 0, "cannot stat /dev/null: %s", strerror(errno)); + + /* go back */ + ret = chdir(origpath); + fail_if(ret == -1, "Cannot chdir\n"); + + /* and finally copy.. */ + DEBUG(5, ("Will copy from '%s' to '%s'\n", dir_path, dst_path)); + ret = copy_tree(dir_path, dst_path, uid, gid); + fail_unless(ret == EOK, "copy_tree failed\n"); + + /* check if really copied */ + ret = access(dst_path, F_OK); + fail_unless(ret == 0, "destination directory not there\n"); + + tmp = talloc_asprintf(test_ctx, "%s/testnode", dst_path); + ret = lstat(tmp, &statbuf); + fail_unless(ret == 0, "cannot stat the node %s\n", tmp); + fail_unless(S_ISFIFO(statbuf.st_mode), "%s not a char device??\n", tmp); + talloc_free(tmp); +} +END_TEST + +static Suite *files_suite(void) +{ + Suite *s = suite_create("files_suite"); + + TCase *tc_files = tcase_create("files"); + tcase_add_checked_fixture(tc_files, + setup_files_test, + teardown_files_test); + + tcase_add_test(tc_files, test_remove_tree); + tcase_add_test(tc_files, test_simple_copy); + tcase_add_test(tc_files, test_copy_symlink); + tcase_add_test(tc_files, test_copy_node); + suite_add_tcase(s, tc_files); + + return s; +} + +int main(int argc, char *argv[]) +{ + int number_failed; + int opt; + poptContext pc; + int debug = 0; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "debug-level", 'd', POPT_ARG_INT, &debug, 0, "Set debug level", NULL }, + POPT_TABLEEND + }; + + pc = poptGetContext(argv[0], argc, (const char **) argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + poptFreeContext(pc); + debug_level = debug; + + Suite *s = files_suite(); + SRunner *sr = srunner_create(s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/src/tests/find_uid-tests.c b/src/tests/find_uid-tests.c new file mode 100644 index 000000000..9eafadd45 --- /dev/null +++ b/src/tests/find_uid-tests.c @@ -0,0 +1,125 @@ +/* + SSSD + + find_uid - Utilities tests + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> + +#include <check.h> + +#include "util/find_uid.h" + + +START_TEST(test_check_if_uid_is_active_success) +{ + uid_t uid; + bool result; + int ret; + + uid = getuid(); + + ret = check_if_uid_is_active(uid, &result); + fail_unless(ret == EOK, "check_if_uid_is_active failed."); + fail_unless(result, "check_if_uid_is_active did not found my uid [%d]", + uid); +} +END_TEST + +START_TEST(test_check_if_uid_is_active_fail) +{ + uid_t uid; + bool result; + int ret; + + uid = (uid_t) -4; + + ret = check_if_uid_is_active(uid, &result); + fail_unless(ret == EOK, "check_if_uid_is_active failed."); + fail_unless(!result, "check_if_uid_is_active found (hopefully not active) " + "uid [%d]", uid); +} +END_TEST + +START_TEST(test_get_uid_table) +{ + uid_t uid; + int ret; + TALLOC_CTX *tmp_ctx; + hash_table_t *table; + hash_key_t key; + hash_value_t value; + + tmp_ctx = talloc_new(NULL); + fail_unless(tmp_ctx != NULL, "talloc_new failed."); + + ret = get_uid_table(tmp_ctx, &table); + fail_unless(ret == EOK, "get_uid_table failed."); + + uid = getuid(); + key.type = HASH_KEY_ULONG; + key.ul = (unsigned long) uid; + + ret = hash_lookup(table, &key, &value); + + fail_unless(ret == HASH_SUCCESS, "Cannot find my uid [%d] in the table", uid); + + uid = (uid_t) -4; + key.type = HASH_KEY_ULONG; + key.ul = (unsigned long) uid; + + ret = hash_lookup(table, &key, &value); + + fail_unless(ret == HASH_ERROR_KEY_NOT_FOUND, "Found (hopefully not active) " + "uid [%d] in the table", uid); + + talloc_free(tmp_ctx); +} +END_TEST + +Suite *find_uid_suite (void) +{ + Suite *s = suite_create ("find_uid"); + + TCase *tc_find_uid = tcase_create ("find_uid"); + + tcase_add_test (tc_find_uid, test_check_if_uid_is_active_success); + tcase_add_test (tc_find_uid, test_check_if_uid_is_active_fail); + tcase_add_test (tc_find_uid, test_get_uid_table); + suite_add_tcase (s, tc_find_uid); + + return s; +} + +int main(void) +{ + debug_level = 255; + int number_failed; + Suite *s = find_uid_suite (); + SRunner *sr = srunner_create (s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/tests/ipa_ldap_opt-tests.c b/src/tests/ipa_ldap_opt-tests.c new file mode 100644 index 000000000..215f94a4d --- /dev/null +++ b/src/tests/ipa_ldap_opt-tests.c @@ -0,0 +1,59 @@ +/* + SSSD + + Tests if IPA and LDAP backend options are in sync + + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2010 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <check.h> +#include <stdlib.h> + +#include "providers/ipa/ipa_common.h" +#include "providers/ldap/sdap.h" + +START_TEST(test_check_num_opts) +{ + fail_if(IPA_OPTS_BASIC_TEST != SDAP_OPTS_BASIC); +} +END_TEST + +Suite *ipa_ldap_opt_suite (void) +{ + Suite *s = suite_create ("ipa_ldap_opt"); + + TCase *tc_ipa_ldap_opt = tcase_create ("ipa_ldap_opt"); + + tcase_add_test (tc_ipa_ldap_opt, test_check_num_opts); + suite_add_tcase (s, tc_ipa_ldap_opt); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s = ipa_ldap_opt_suite (); + SRunner *sr = srunner_create (s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/tests/ipa_timerules-tests.c b/src/tests/ipa_timerules-tests.c new file mode 100644 index 000000000..0a7be90be --- /dev/null +++ b/src/tests/ipa_timerules-tests.c @@ -0,0 +1,580 @@ +/* + Timelib + + test_timelib.c + + Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _XOPEN_SOURCE /* strptime */ + +#include <check.h> +#include <popt.h> +#include <time.h> +#include <errno.h> +#include <stdlib.h> +#include <stdbool.h> + +#include "providers/ipa/ipa_timerules.h" +#include "util/util.h" +#include "tests/common.h" + +#define CHECK_TIME_RULE_LEAK(ctx, tctx, str, now, result) do { \ + check_leaks_push(ctx); \ + ret = check_time_rule(ctx, tctx, str, now, result); \ + check_leaks_pop(ctx); \ +} while (0) + +static TALLOC_CTX *ctx; + +static void usage(poptContext pc, const char *error) +{ + poptPrintUsage(pc, stderr, 0); + if (error) fprintf(stderr, "%s", error); +} + +int str2time_t(const char *fmt, const char *str, time_t *out) +{ + char *err; + struct tm stm; + memset(&stm, 0, sizeof(struct tm)); + + err = strptime(str, fmt, &stm); + if(!err || err[0] != '\0') + return EINVAL; + + DEBUG(9, ("after strptime: %s", asctime(&stm))); + stm.tm_isdst = -1; + *out = mktime(&stm); + DEBUG(9, ("after mktime: %s", ctime(out))); + return (*out == -1) ? EINVAL : EOK; +} + +/* Fixtures - open the time library before every test, close it afterwards */ +void setup(void) +{ + leak_check_setup(); + + ctx = talloc_new(NULL); + fail_if(ctx == NULL); +} + +void teardown(void) +{ + leak_check_teardown(); +} + +/* Test that timelib detects a time rule inside the absolute range */ +START_TEST(test_timelib_absolute_in_range) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%F", "2000-1-1", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "absolute 199412161032.5 ~ 200512161032,5", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error"); + + talloc_free(tctx); +} +END_TEST + +/* Test that timelib detects a time rule outside the absolute range */ +START_TEST(test_timelib_absolute_out_of_range) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%F", "2007-1-1", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "absolute 199412161032.5 ~ 200512161032,5", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error"); + + talloc_free(tctx); +} +END_TEST + +/* Test that absolute timeranges work OK with only minimal data supplied */ +START_TEST(test_timelib_absolute_minimal) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%F", "2000-1-1", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "absolute 19941216 ~ 20051216", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error"); + + talloc_free(tctx); +} +END_TEST + +/* Test a time value "right off the edge" of the time specifier */ +START_TEST(test_timelib_absolute_one_off) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M-%S", "1994-12-16-10-32-29", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "absolute 19941216103230 ~ 19941216103231", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M-%S", "1994-12-16-10-32-32", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "absolute 19941216103230 ~ 19941216103231", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error"); + + talloc_free(tctx); +} +END_TEST + + +/* Test a time value "right on the edge" of the time specifier */ +START_TEST(test_timelib_absolute_one_on) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M-%S", "1994-12-16-10-32-30", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "absolute 19941216103230 ~ 19941216103231", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M-%S", "1994-12-16-10-32-31", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "absolute 19941216103230 ~ 19941216103231", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error"); + + talloc_free(tctx); +} +END_TEST + +START_TEST(test_timelib_periodic_daily_in) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error"); + + /* test edges */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-09-30", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 0930 ~ 1830", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (edge1)"); + fail_unless(result == true, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-18-30", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 0930 ~ 1830", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (edge2)"); + fail_unless(result == true, "Range check error"); + + /* test wrap around */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-16-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 1500 ~ 0600", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (edge1)"); + fail_unless(result == true, "Range check error1"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-15-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 1500 ~ 0600", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (edge1)"); + fail_unless(result == true, "Range check error1"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-06-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 1500 ~ 0600", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (edge1)"); + fail_unless(result == true, "Range check error1"); + + talloc_free(tctx); +} +END_TEST + +START_TEST(test_timelib_periodic_daily_out) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-21-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error"); + + /* test one-off errors */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-09-29", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 0930 ~ 1830", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (one-off 1)"); + fail_unless(result == false, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-18-31", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 0930 ~ 1830", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (one-off 2)"); + fail_unless(result == false, "Range check error"); + + /* test wrap around */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-10-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 1500 ~ 0600", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (edge1)"); + fail_unless(result == false, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-14-59", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 1500 ~ 0600", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (edge1)"); + fail_unless(result == false, "Range check error1"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-03-30-06-01", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic daily 1500 ~ 0600", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (edge1)"); + fail_unless(result == false, "Range check error2"); + + talloc_free(tctx); +} +END_TEST + +START_TEST(test_timelib_periodic_weekly_in) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-04-02-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Mon-Fri 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error1"); + + /* test edges - monday */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-22-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Mon-Fri 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error2"); + + /* test edges - friday */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-26-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Mon-Fri 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error3"); + + /* test wrap around */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-11-03-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Fri-Tue 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error2"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-11-06-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Fri-Tue 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == true, "Range check error3"); + + talloc_free(tctx); +} +END_TEST + +START_TEST(test_timelib_periodic_weekly_out) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-04-04-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Mon-Fri 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error"); + + /* test one-off error - monday */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-22-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Tue-Thu 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error"); + + /* test one-off error - friday */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-26-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Tue-Thu 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error"); + + /* test wrap around */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-11-04-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Fri-Tue 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error2"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-11-05-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic weekly day Fri-Tue 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule"); + fail_unless(result == false, "Range check error3"); + + talloc_free(tctx); +} +END_TEST + +START_TEST(test_timelib_periodic_monthly_in) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-04-07-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly week 1,2 day Mon,Tue 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule1 (ret = %d: %s)", ret, strerror(ret)); + fail_unless(result == true, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-04-05-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly day 1-5,10,15,20-25 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule2 (ret = %d: %s)", ret, strerror(ret)); + fail_unless(result == true, "Range check error"); + + /* edges - week in */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-13-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly week 1,2 day Sat 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (week edge 1)"); + fail_unless(result == true, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-29-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly week 5 day Mon,Tue 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (week edge 2)"); + fail_unless(result == true, "Range check error"); + + /* edges - day in */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-04-01-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (day edge 1)"); + fail_unless(result == true, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-04-10-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (day edge 2)"); + fail_unless(result == true, "Range check error"); + + talloc_free(tctx); +} +END_TEST + +START_TEST(test_timelib_periodic_monthly_out) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-03-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly week 1,2 day Mon,Tue 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (ret = %d)", ret); + fail_unless(result == false, "Range check error"); + + /* one-off error - week out */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-15-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly week 1 day Sun-Sat 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (week edge 1)"); + fail_unless(result == false, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-28-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly week 5 day Mon,Tue 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (week edge 2)"); + fail_unless(result == false, "Range check error"); + + /* one-off error - day out */ + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-04-01-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly day 2-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (day edge 1)"); + fail_unless(result == false, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-04-11-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic monthly day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (day edge 2)"); + fail_unless(result == false, "Range check error"); + + talloc_free(tctx); +} +END_TEST + +START_TEST(test_timelib_periodic_yearly_in) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-08-03-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic yearly month 1,7-8 day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (ret = %d)", ret); + fail_unless(result == true, "Range check error1"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-01-01-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic yearly month 1,7-8 day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (ret = %d)", ret); + fail_unless(result == true, "Range check error2"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-01-01-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic yearly week 1 day 1-7 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (ret = %d)", ret); + fail_unless(result == true, "Range check error3"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-07-10-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic yearly month 1,7-8 day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule (ret = %d)", ret); + fail_unless(result == true, "Range check error4"); + + talloc_free(tctx); +} +END_TEST + +START_TEST(test_timelib_periodic_yearly_out) +{ + time_t now; + int ret; + bool result; + static struct time_rules_ctx *tctx = NULL; + + ret = init_time_rules_parser(ctx, &tctx); + fail_if(ret != EOK); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-06-13-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic yearly month 7-8 day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule1 (ret = %d)", ret); + fail_unless(result == false, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-09-13-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic yearly month 7-8 day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule2 (ret = %d)", ret); + fail_unless(result == false, "Range check error"); + + fail_unless(str2time_t("%Y-%m-%d-%H-%M", "2009-01-11-12-00", &now) == 0, "Cannot parse time spec"); + CHECK_TIME_RULE_LEAK(ctx, tctx, "periodic yearly month 1,7-8 day 1-10 0900 ~ 1800", now, &result); + fail_unless(ret == EOK, "Fail to check the time rule3 (ret = %d)", ret); + fail_unless(result == false, "Range check error"); + + talloc_free(tctx); +} +END_TEST + +Suite *create_timelib_suite(void) +{ + Suite *s = suite_create("timelib"); + + TCase *tc_timelib = tcase_create("Timelib Tests"); + + + /* Add setup() and teardown() methods */ + tcase_add_checked_fixture(tc_timelib, setup, teardown); + + /* Do some testing */ + tcase_add_test(tc_timelib, test_timelib_absolute_in_range); + tcase_add_test(tc_timelib, test_timelib_absolute_out_of_range); + tcase_add_test(tc_timelib, test_timelib_absolute_minimal); + tcase_add_test(tc_timelib, test_timelib_absolute_one_off); + tcase_add_test(tc_timelib, test_timelib_absolute_one_on); + + tcase_add_test(tc_timelib, test_timelib_periodic_daily_in); + tcase_add_test(tc_timelib, test_timelib_periodic_daily_out); + tcase_add_test(tc_timelib, test_timelib_periodic_weekly_in); + tcase_add_test(tc_timelib, test_timelib_periodic_weekly_out); + tcase_add_test(tc_timelib, test_timelib_periodic_monthly_in); + tcase_add_test(tc_timelib, test_timelib_periodic_monthly_out); + tcase_add_test(tc_timelib, test_timelib_periodic_yearly_in); + tcase_add_test(tc_timelib, test_timelib_periodic_yearly_out); + + /* Add all test cases to the test suite */ + suite_add_tcase(s, tc_timelib); + + return s; +} + +int main(int argc, const char *argv[]) +{ + int ret; + poptContext pc; + int failure_count; + Suite *timelib_suite; + SRunner *sr; + int debug = 0; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "debug-level", 'd', POPT_ARG_INT, &debug, 0, "Set debug level", NULL }, + POPT_TABLEEND + }; + + pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0); + if((ret = poptGetNextOpt(pc)) < -1) { + usage(pc, poptStrerror(ret)); + return EXIT_FAILURE; + } + debug_level = debug; + + timelib_suite = create_timelib_suite(); + sr = srunner_create(timelib_suite); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + failure_count = srunner_ntests_failed(sr); + srunner_free(sr); + return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + diff --git a/src/tests/krb5_utils-tests.c b/src/tests/krb5_utils-tests.c new file mode 100644 index 000000000..8676f3bfa --- /dev/null +++ b/src/tests/krb5_utils-tests.c @@ -0,0 +1,307 @@ +/* + SSSD + + Kerberos 5 Backend Module -- Utilities tests + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2009 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <check.h> + +#include "providers/krb5/krb5_utils.h" +#include "providers/krb5/krb5_auth.h" + +#define BASE "/abc/def" + +#define USERNAME "testuser" +#define UID "12345" +#define PRINCIPLE_NAME "testuser@EXAMPLE.COM" +#define REALM "REALM.ORG" +#define HOME_DIRECTORY "/home/testuser" +#define CCACHE_DIR "/var/tmp" +#define PID "4321" + +extern struct dp_option default_krb5_opts[]; + +TALLOC_CTX *tmp_ctx = NULL; +struct krb5child_req *kr; + +void setup_talloc_context(void) +{ + int ret; + int i; + + struct pam_data *pd; + struct krb5_ctx *krb5_ctx; + fail_unless(tmp_ctx == NULL, "Talloc context already initialized."); + tmp_ctx = talloc_new(NULL); + fail_unless(tmp_ctx != NULL, "Cannot create talloc context."); + + kr = talloc_zero(tmp_ctx, struct krb5child_req); + fail_unless(kr != NULL, "Cannot create krb5child_req structure."); + + pd = talloc_zero(tmp_ctx, struct pam_data); + fail_unless(pd != NULL, "Cannot create pam_data structure."); + + krb5_ctx = talloc_zero(tmp_ctx, struct krb5_ctx); + fail_unless(pd != NULL, "Cannot create krb5_ctx structure."); + + pd->user = discard_const(USERNAME); + pd->pw_uid = atoi(UID); + pd->upn = PRINCIPLE_NAME; + pd->cli_pid = atoi(PID); + + krb5_ctx->opts = talloc_zero_array(tmp_ctx, struct dp_option, KRB5_OPTS); + fail_unless(krb5_ctx->opts != NULL, "Cannot created options."); + for (i = 0; i < KRB5_OPTS; i++) { + krb5_ctx->opts[i].opt_name = default_krb5_opts[i].opt_name; + krb5_ctx->opts[i].type = default_krb5_opts[i].type; + krb5_ctx->opts[i].def_val = default_krb5_opts[i].def_val; + } + ret = dp_opt_set_string(krb5_ctx->opts, KRB5_REALM, REALM); + fail_unless(ret == EOK, "Failed to set Realm"); + ret = dp_opt_set_string(krb5_ctx->opts, KRB5_CCACHEDIR, CCACHE_DIR); + fail_unless(ret == EOK, "Failed to set Ccache dir"); + + kr->homedir = HOME_DIRECTORY; + + kr->pd = pd; + kr->krb5_ctx = krb5_ctx; + +} + +void free_talloc_context(void) +{ + int ret; + fail_unless(tmp_ctx != NULL, "Talloc context already freed."); + ret = talloc_free(tmp_ctx); + tmp_ctx = NULL; + fail_unless(ret == 0, "Connot free talloc context."); +} + +START_TEST(test_multiple_substitutions) +{ + const char *test_template = BASE"_%u_%U_%u"; + const char *expected = BASE"_"USERNAME"_"UID"_"USERNAME; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_username) +{ + const char *test_template = BASE"_%u"; + const char *expected = BASE"_"USERNAME; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_uid) +{ + const char *test_template = BASE"_%U"; + const char *expected = BASE"_"UID; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_upn) +{ + const char *test_template = BASE"_%p"; + const char *expected = BASE"_"PRINCIPLE_NAME; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_realm) +{ + const char *test_template = BASE"_%r"; + const char *expected = BASE"_"REALM; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_home) +{ + const char *test_template = BASE"_%h"; + const char *expected = BASE"_"HOME_DIRECTORY; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_ccache_dir) +{ + const char *test_template = BASE"_%d"; + const char *expected = BASE"_"CCACHE_DIR; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_pid) +{ + const char *test_template = BASE"_%P"; + const char *expected = BASE"_"PID; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_percent) +{ + const char *test_template = BASE"_%%"; + const char *expected = BASE"_%"; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, expected) == 0, + "Expansion failed, result [%s], expected [%s].", + result, expected); +} +END_TEST + +START_TEST(test_unknow_template) +{ + const char *test_template = BASE"_%X"; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result == NULL, "Unknown template [%s] should fail.", + test_template); +} +END_TEST + +START_TEST(test_NULL) +{ + char *test_template = NULL; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result == NULL, "Expected NULL as a result for an empty input.", + test_template); +} +END_TEST + +START_TEST(test_no_substitution) +{ + const char *test_template = BASE; + char *result; + + result = expand_ccname_template(tmp_ctx, kr, test_template); + + fail_unless(result != NULL, "Cannot expand template [%s].", test_template); + fail_unless(strcmp(result, test_template) == 0, + "Expansion failed, result [%s], expected [%s].", + result, test_template); +} +END_TEST + +Suite *krb5_utils_suite (void) +{ + Suite *s = suite_create ("krb5_utils"); + + TCase *tc_ccname_template = tcase_create ("ccname_template"); + tcase_add_checked_fixture (tc_ccname_template, setup_talloc_context, + free_talloc_context); + tcase_add_test (tc_ccname_template, test_no_substitution); + tcase_add_test (tc_ccname_template, test_NULL); + tcase_add_test (tc_ccname_template, test_unknow_template); + tcase_add_test (tc_ccname_template, test_username); + tcase_add_test (tc_ccname_template, test_uid); + tcase_add_test (tc_ccname_template, test_upn); + tcase_add_test (tc_ccname_template, test_realm); + tcase_add_test (tc_ccname_template, test_home); + tcase_add_test (tc_ccname_template, test_ccache_dir); + tcase_add_test (tc_ccname_template, test_pid); + tcase_add_test (tc_ccname_template, test_percent); + tcase_add_test (tc_ccname_template, test_multiple_substitutions); + suite_add_tcase (s, tc_ccname_template); + + return s; +} + +int main(void) +{ + int number_failed; + Suite *s = krb5_utils_suite (); + SRunner *sr = srunner_create (s); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed (sr); + srunner_free (sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} + diff --git a/src/tests/python-test.py b/src/tests/python-test.py new file mode 100644 index 000000000..e1eaab2d1 --- /dev/null +++ b/src/tests/python-test.py @@ -0,0 +1,445 @@ +#!/usr/bin/python +#coding=utf-8 + +# Authors: +# Jakub Hrozek <jhrozek@redhat.com> +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import os +import tempfile +import shutil +import unittest +import commands +import errno + +# module under test +import pysss + +class LocalTest(unittest.TestCase): + local_path = "/var/lib/sss/db/sssd.ldb" + + def setUp(self): + self.local = pysss.local() + + def _run_and_check(self, runme): + (status, output) = commands.getstatusoutput(runme) + self.failUnlessEqual(status, 0, output) + + def _get_object_info(self, name, subtree, domain): + search_dn = "dn=name=%s,cn=%s,cn=%s,cn=sysdb" % (name, subtree, domain) + (status, output) = commands.getstatusoutput("ldbsearch -H %s %s" % (self.local_path,search_dn)) + + if status: return {} + + kw = {} + for key, value in [ l.split(':') for l in output.split('\n') if ":" in l ]: + kw[key] = value.strip() + + del kw['asq'] + return kw + + def get_user_info(self, name, domain="LOCAL"): + return self._get_object_info(name, "users", domain) + + def get_group_info(self, name, domain="LOCAL"): + return self._get_object_info(name, "groups", domain) + + def _validate_object(self, kw, name, **kwargs): + if kw == {}: self.fail("Could not get %s info" % name) + for key in kwargs.keys(): + self.assert_(str(kwargs[key]) == str(kw[key]), "%s %s != %s %s" % (key, kwargs[key], key, kw[key])) + + def validate_user(self, username, **kwargs): + return self._validate_object(self.get_user_info(username), "user", **kwargs) + + def validate_group(self, groupname, **kwargs): + return self._validate_object(self.get_group_info(groupname), "group", **kwargs) + + def _validate_no_object(self, kw, name): + if kw != {}: + self.fail("Got %s info" % name) + + def validate_no_user(self, username): + return self._validate_no_object(self.get_user_info(username), "user") + + def validate_no_group(self, groupname): + return self._validate_no_object(self.get_group_info(groupname), "group") + + def _get_object_membership(self, name, subtree, domain): + search_dn = "dn=name=%s,cn=%s,cn=%s,cn=sysdb" % (name, subtree, domain) + (status, output) = commands.getstatusoutput("ldbsearch -H %s %s" % (self.local_path,search_dn)) + + if status: + return [] + + members = [ value.strip() for key, value in [ l.split(':') for l in output.split('\n') if ":" in l ] if key == "memberof" ] + return members + + def _assertMembership(self, name, group_list, subtree, domain): + members = self._get_object_membership(name, subtree, domain) + for group in group_list: + group_dn = "name=%s,cn=groups,cn=%s,cn=sysdb" % (group, domain) + if group_dn in members: + members.remove(group_dn) + else: + self.fail("Cannot find required group %s" % group_dn) + + if len(members) > 0: + self.fail("More groups than selected") + + def assertUserMembership(self, name, group_list, domain="LOCAL"): + return self._assertMembership(name, group_list, "users", domain) + + def assertGroupMembership(self, name, group_list, domain="LOCAL"): + return self._assertMembership(name, group_list, "groups", domain) + + def get_user_membership(self, name, domain="LOCAL"): + return self._get_object_membership(name, "users", domain) + + def get_group_membership(self, name, domain="LOCAL"): + return self._get_object_membership(name, "groups", domain) + + def add_group(self, groupname): + self._run_and_check("sss_groupadd %s" % (groupname)) + + def remove_group(self, groupname): + self._run_and_check("sss_groupdel %s" % (groupname)) + + def add_user(self, username): + self._run_and_check("sss_useradd %s" % (username)) + + def add_user_not_home(self, username): + self._run_and_check("sss_useradd -M %s" % (username)) + + def remove_user(self, username): + self._run_and_check("sss_userdel %s" % (username)) + + def remove_user_not_home(self, username): + self._run_and_check("sss_userdel -R %s" % (username)) + +class SanityTest(unittest.TestCase): + def testInstantiate(self): + "Test that the local backed binding can be instantiated" + local = pysss.local() + self.assert_(local.__class__, "<type 'sss.local'>") + +class UseraddTest(LocalTest): + def tearDown(self): + if self.username: + self.remove_user(self.username) + + def testUseradd(self): + "Test adding a local user" + self.username = "testUseradd" + self.local.useradd(self.username) + self.validate_user(self.username) + # check home directory was created with default name + self.assertEquals(os.access("/home/%s" % self.username, os.F_OK), True) + + def testUseraddWithParams(self): + "Test adding a local user with modified parameters" + self.username = "testUseraddWithParams" + self.local.useradd(self.username, + gecos="foo bar", + homedir="/home/foobar", + shell="/bin/zsh") + self.validate_user(self.username, + gecos="foo bar", + homeDirectory="/home/foobar", + loginShell="/bin/zsh") + # check home directory was created with nondefault name + self.assertEquals(os.access("/home/foobar", os.F_OK), True) + + def testUseraddNoHomedir(self): + "Test adding a local user without creating his home dir" + self.username = "testUseraddNoHomedir" + self.local.useradd(self.username, create_home = False) + self.validate_user(self.username) + # check home directory was not created + self.assertEquals(os.access("/home/%s" % self.username, os.F_OK), False) + self.local.userdel(self.username, remove = False) + self.username = None # fool tearDown into not removing the user + + def testUseraddAlternateSkeldir(self): + "Test adding a local user and init his homedir from a custom location" + self.username = "testUseraddAlternateSkeldir" + + skeldir = tempfile.mkdtemp() + fd, path = tempfile.mkstemp(dir=skeldir) + fdo = os.fdopen(fd) + fdo.flush() + fdo.close + self.assertEquals(os.access(path, os.F_OK), True) + filename = os.path.basename(path) + + try: + self.local.useradd(self.username, skel = skeldir) + self.validate_user(self.username) + self.assertEquals(os.access("/home/%s/%s"%(self.username,filename), os.F_OK), True) + finally: + shutil.rmtree(skeldir) + + def testUseraddToGroups(self): + "Test adding a local user with group membership" + self.username = "testUseraddToGroups" + self.add_group("gr1") + self.add_group("gr2") + try: + self.local.useradd(self.username, + groups=["gr1","gr2"]) + self.assertUserMembership(self.username, + ["gr1","gr2"]) + finally: + self.remove_group("gr1") + self.remove_group("gr2") + + def testUseraddWithUID(self): + "Test adding a local user with a custom UID" + self.username = "testUseraddWithUID" + self.local.useradd(self.username, + uid=1024) + self.validate_user(self.username, + uidNumber=1024) + +class UseraddTestNegative(LocalTest): + def testUseraddNoParams(self): + "Test that local.useradd() requires the username parameter" + self.assertRaises(TypeError, self.local.useradd) + + def testUseraddUserAlreadyExists(self): + "Test adding a local with a duplicite name" + self.username = "testUseraddUserAlreadyExists" + self.local.useradd(self.username) + try: + self.local.useradd(self.username) + except IOError, e: + self.assertEquals(e.errno, errno.EEXIST) + else: + self.fail("Was expecting exception") + finally: + self.remove_user(self.username) + + def testUseraddUIDAlreadyExists(self): + "Test adding a local with a duplicite user ID" + self.username = "testUseraddUIDAlreadyExists1" + self.local.useradd(self.username, uid=1025) + try: + self.local.useradd("testUseraddUIDAlreadyExists2", uid=1025) + except IOError, e: + self.assertEquals(e.errno, errno.EEXIST) + else: + self.fail("Was expecting exception") + finally: + self.remove_user(self.username) + +class UserdelTest(LocalTest): + def testUserdel(self): + self.add_user("testUserdel") + self.assertEquals(os.access("/home/testUserdel", os.F_OK), True) + self.validate_user("testUserdel") + self.local.userdel("testUserdel") + self.validate_no_user("testUserdel") + self.assertEquals(os.access("/home/testUserdel", os.F_OK), False) + + def testUserdelNotHomedir(self): + self.add_user("testUserdel") + self.assertEquals(os.access("/home/testUserdel", os.F_OK), True) + self.validate_user("testUserdel") + self.local.userdel("testUserdel", remove=False) + self.validate_no_user("testUserdel") + self.assertEquals(os.access("/home/testUserdel", os.F_OK), True) + shutil.rmtree("/home/testUserdel") + os.remove("/var/mail/testUserdel") + + def testUserdelNegative(self): + self.validate_no_user("testUserdelNegative") + try: + self.local.userdel("testUserdelNegative") + except IOError, e: + self.assertEquals(e.errno, errno.ENOENT) + else: + fail("Was expecting exception") + +class UsermodTest(LocalTest): + def setUp(self): + self.local = pysss.local() + self.username = "UsermodTest" + self.add_user_not_home(self.username) + + def tearDown(self): + self.remove_user_not_home(self.username) + + def testUsermod(self): + "Test modifying user attributes" + self.local.usermod(self.username, + gecos="foo bar", + homedir="/home/foobar", + shell="/bin/zsh") + self.validate_user(self.username, + gecos="foo bar", + homeDirectory="/home/foobar", + loginShell="/bin/zsh") + + def testUsermodUID(self): + "Test modifying UID" + self.local.usermod(self.username, + uid=1024) + self.validate_user(self.username, + uidNumber=1024) + + def testUsermodGroupMembership(self): + "Test adding to and removing from groups" + self.add_group("gr1") + self.add_group("gr2") + + try: + self.local.usermod(self.username, + addgroups=["gr1","gr2"]) + self.assertUserMembership(self.username, + ["gr1","gr2"]) + self.local.usermod(self.username, + rmgroups=["gr2"]) + self.assertUserMembership(self.username, + ["gr1"]) + self.local.usermod(self.username, + rmgroups=["gr1"]) + self.assertUserMembership(self.username, + []) + finally: + self.remove_group("gr1") + self.remove_group("gr2") + + def testUsermodLockUnlock(self): + "Test locking and unlocking user" + self.local.usermod(self.username, + lock=self.local.lock) + self.validate_user(self.username, + disabled="true") + self.local.usermod(self.username, + lock=self.local.unlock) + self.validate_user(self.username, + disabled="false") + +class GroupaddTest(LocalTest): + def tearDown(self): + if self.groupname: + self.remove_group(self.groupname) + + def testGroupadd(self): + "Test adding a local group" + self.groupname = "testGroupadd" + self.local.groupadd(self.groupname) + self.validate_group(self.groupname) + + def testGroupaddWithGID(self): + "Test adding a local group with a custom GID" + self.groupname = "testUseraddWithGID" + self.local.groupadd(self.groupname, + gid=1024) + self.validate_group(self.groupname, + gidNumber=1024) + +class GroupaddTestNegative(LocalTest): + def testGroupaddNoParams(self): + "Test that local.groupadd() requires the groupname parameter" + self.assertRaises(TypeError, self.local.groupadd) + + def testGroupaddUserAlreadyExists(self): + "Test adding a local with a duplicite name" + self.groupname = "testGroupaddUserAlreadyExists" + self.local.groupadd(self.groupname) + try: + self.local.groupadd(self.groupname) + except IOError, e: + self.assertEquals(e.errno, errno.EEXIST) + else: + self.fail("Was expecting exception") + finally: + self.remove_group(self.groupname) + + def testGroupaddGIDAlreadyExists(self): + "Test adding a local with a duplicite group ID" + self.groupname = "testGroupaddGIDAlreadyExists1" + self.local.groupadd(self.groupname, gid=1025) + try: + self.local.groupadd("testGroupaddGIDAlreadyExists2", gid=1025) + except IOError, e: + self.assertEquals(e.errno, errno.EEXIST) + else: + self.fail("Was expecting exception") + finally: + self.remove_group(self.groupname) + +class GroupdelTest(LocalTest): + def testGroupdel(self): + self.add_group("testGroupdel") + self.validate_group("testGroupdel") + self.local.groupdel("testGroupdel") + self.validate_no_group("testGroupdel") + + def testGroupdelNegative(self): + self.validate_no_group("testGroupdelNegative") + try: + self.local.groupdel("testGroupdelNegative") + except IOError, e: + self.assertEquals(e.errno, errno.ENOENT) + else: + fail("Was expecting exception") + + +class GroupmodTest(LocalTest): + def setUp(self): + self.local = pysss.local() + self.groupname = "GroupmodTest" + self.add_group(self.groupname) + + def tearDown(self): + self.remove_group(self.groupname) + + def testGroupmodGID(self): + "Test modifying UID" + self.local.groupmod(self.groupname, + gid=1024) + self.validate_group(self.groupname, + gidNumber=1024) + + def testGroupmodGroupMembership(self): + "Test adding to groups" + self.add_group("gr1") + self.add_group("gr2") + try: + self.local.groupmod(self.groupname, + addgroups=["gr1","gr2"]) + self.assertGroupMembership(self.groupname, + ["gr1","gr2"]) + self.local.groupmod(self.groupname, + rmgroups=["gr2"]) + self.assertGroupMembership(self.groupname, + ["gr1"]) + self.local.groupmod(self.groupname, + rmgroups=["gr1"]) + self.assertGroupMembership(self.groupname, + []) + finally: + self.remove_group("gr1") + self.remove_group("gr2") + +# -------------- run the test suite -------------- # +if __name__ == "__main__": + unittest.main() + diff --git a/src/tests/refcount-tests.c b/src/tests/refcount-tests.c new file mode 100644 index 000000000..db2a256ee --- /dev/null +++ b/src/tests/refcount-tests.c @@ -0,0 +1,232 @@ +/* + SSSD + + Reference counting tests. + + Authors: + Martin Nagy <mnagy@redhat.com> + + Copyright (C) Red Hat, Inc 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <check.h> +#include <talloc.h> +#include <tevent.h> +#include <popt.h> + +#include "tests/common.h" +#include "util/util.h" + +/* Interface under test */ +#include "util/refcount.h" + +/* Fail the test if object 'obj' does not have 'num' references. */ +#define REF_ASSERT(obj, num) \ + fail_unless(((obj)->DO_NOT_TOUCH_THIS_MEMBER_refcount == (num)), \ + "Reference count of " #obj " should be %d but is %d", \ + (num), (obj)->DO_NOT_TOUCH_THIS_MEMBER_refcount) + +#define FILLER_SIZE 32 + +struct foo { + REFCOUNT_COMMON; + char a[FILLER_SIZE]; + char b[FILLER_SIZE]; +}; + +struct bar { + char a[FILLER_SIZE]; + REFCOUNT_COMMON; + char b[FILLER_SIZE]; +}; + +struct baz { + char a[FILLER_SIZE]; + char b[FILLER_SIZE]; + REFCOUNT_COMMON; +}; + +#define SET_FILLER(target) do { \ + memset((target)->a, 'a', FILLER_SIZE); \ + memset((target)->b, 'b', FILLER_SIZE); \ +} while (0) + +#define CHECK_FILLER(target) do { \ + int _counter; \ + for (_counter = 0; _counter < FILLER_SIZE; _counter++) { \ + fail_unless((target)->a[_counter] == 'a', "Corrupted memory in " \ + #target "->a[%d] of size %d", _counter, FILLER_SIZE); \ + fail_unless((target)->b[_counter] == 'b', "Corrupted memory in " \ + #target "->b[%d] of size %d", _counter, FILLER_SIZE); \ + } \ +} while (0) + +struct container { + struct foo *foo; + struct bar *bar; + struct baz *baz; +}; + +static struct container *global; + +START_TEST(test_refcount_basic) +{ + struct container *containers; + int i; + + /* First allocate our global storage place. */ + global = talloc(NULL, struct container); + fail_if(global == NULL); + + /* Allocate foo. */ + global->foo = rc_alloc(global, struct foo); + fail_if(global->foo == NULL); + SET_FILLER(global->foo); + REF_ASSERT(global->foo, 1); + + /* Allocate bar. */ + global->bar = rc_alloc(global, struct bar); + fail_if(global->bar == NULL); + SET_FILLER(global->bar); + REF_ASSERT(global->bar, 1); + + /* Allocate baz. */ + global->baz = rc_alloc(global, struct baz); + fail_if(global->baz == NULL); + SET_FILLER(global->baz); + REF_ASSERT(global->baz, 1); + + /* Try multiple attaches. */ + containers = talloc_array(NULL, struct container, 100); + fail_if(containers == NULL); + for (i = 0; i < 100; i++) { + containers[i].foo = rc_reference(containers, struct foo, global->foo); + containers[i].bar = rc_reference(containers, struct bar, global->bar); + containers[i].baz = rc_reference(containers, struct baz, global->baz); + REF_ASSERT(containers[i].foo, i + 2); + REF_ASSERT(global->foo, i + 2); + REF_ASSERT(containers[i].bar, i + 2); + REF_ASSERT(global->bar, i + 2); + REF_ASSERT(containers[i].baz, i + 2); + REF_ASSERT(global->baz, i + 2); + } + talloc_free(containers); + + CHECK_FILLER(global->foo); + CHECK_FILLER(global->bar); + CHECK_FILLER(global->baz); + + REF_ASSERT(global->foo, 1); + REF_ASSERT(global->bar, 1); + REF_ASSERT(global->baz, 1); + + talloc_free(global); +} +END_TEST + +START_TEST(test_refcount_swap) +{ + void *tmp_ctx; + struct container *container1; + struct container *container2; + + tmp_ctx = talloc_new(NULL); + + check_leaks_push(tmp_ctx); + + container1 = talloc(tmp_ctx, struct container); + container2 = talloc(tmp_ctx, struct container); + + /* Allocate. */ + container1->foo = rc_alloc(container1, struct foo); + fail_if(container1->foo == NULL); + SET_FILLER(container1->foo); + + /* Reference. */ + container2->foo = rc_reference(container2, struct foo, container1->foo); + fail_if(container2->foo == NULL); + + /* Make sure everything is as it should be. */ + fail_unless(container1->foo == container2->foo); + REF_ASSERT(container1->foo, 2); + + /* Free in reverse order. */ + talloc_free(container1); + REF_ASSERT(container2->foo, 1); + CHECK_FILLER(container2->foo); + talloc_free(container2); + + check_leaks_pop(tmp_ctx); + talloc_free(tmp_ctx); +} +END_TEST + +Suite *create_suite(void) +{ + Suite *s = suite_create("refcount"); + + TCase *tc = tcase_create("REFCOUNT Tests"); + + /* Do some testing */ + tcase_add_checked_fixture(tc, leak_check_setup, leak_check_teardown); + tcase_add_test(tc, test_refcount_basic); + tcase_add_test(tc, test_refcount_swap); + + /* Add all test cases to the test suite */ + suite_add_tcase(s, tc); + + return s; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + int failure_count; + Suite *suite; + SRunner *sr; + int debug = 0; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "debug-level", 'd', POPT_ARG_INT, &debug, 0, "Set debug level", NULL }, + POPT_TABLEEND + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + debug_level = debug; + + suite = create_suite(); + sr = srunner_create(suite); + srunner_set_fork_status(sr, CK_FORK); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + failure_count = srunner_ntests_failed(sr); + srunner_free(sr); + return (failure_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + diff --git a/src/tests/resolv-tests.c b/src/tests/resolv-tests.c new file mode 100644 index 000000000..04b9e2e76 --- /dev/null +++ b/src/tests/resolv-tests.c @@ -0,0 +1,598 @@ +/* + SSSD + + Async resolver tests + + Authors: + Martin Nagy <mnagy@redhat.com> + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) Red Hat, Inc 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <check.h> +#include <string.h> +#include <talloc.h> +#include <tevent.h> +#include <popt.h> +#include <arpa/inet.h> + +#include "tests/common.h" +#include "util/util.h" + +/* Interface under test */ +#include "resolv/async_resolv.h" + +static int use_net_test; +static char *txt_host; +static char *srv_host; + +struct resolv_test_ctx { + struct tevent_context *ev; + struct resolv_ctx *resolv; + + enum { + TESTING_HOSTNAME, + TESTING_TXT, + TESTING_SRV, + } tested_function; + + int error; + bool done; +}; + +static int setup_resolv_test(struct resolv_test_ctx **ctx) +{ + struct resolv_test_ctx *test_ctx; + int ret; + + test_ctx = talloc_zero(global_talloc_context, struct resolv_test_ctx); + if (test_ctx == NULL) { + fail("Could not allocate memory for test context"); + return ENOMEM; + } + + test_ctx->ev = tevent_context_init(test_ctx); + if (test_ctx->ev == NULL) { + fail("Could not init tevent context"); + talloc_free(test_ctx); + return EFAULT; + } + + ret = resolv_init(test_ctx, test_ctx->ev, 5, &test_ctx->resolv); + if (ret != EOK) { + fail("Could not init resolv context"); + talloc_free(test_ctx); + return ret; + } + + *ctx = test_ctx; + return EOK; +} + +static int test_loop(struct resolv_test_ctx *data) +{ + while (!data->done) + tevent_loop_once(data->ev); + + return data->error; +} + +START_TEST(test_copy_hostent) +{ + void *ctx; + struct hostent *new_he; + + char name[] = "foo.example.com"; + char alias_1[] = "bar.example.com"; + char alias_2[] = "baz.example.com"; + char *aliases[] = { alias_1, alias_2, NULL }; + char addr_1[] = { 1, 2, 3, 4 }; + char addr_2[] = { 4, 3, 2, 1 }; + char *addr_list[] = { addr_1, addr_2, NULL }; + struct hostent he = { + name, aliases, 123 /* Whatever. */, + sizeof(addr_1), addr_list + }; + + ctx = talloc_new(global_talloc_context); + fail_if(ctx == NULL); + + check_leaks_push(ctx); + new_he = resolv_copy_hostent(ctx, &he); + fail_if(new_he == NULL); + fail_if(strcmp(new_he->h_name, name)); + fail_if(strcmp(new_he->h_aliases[0], alias_1)); + fail_if(strcmp(new_he->h_aliases[1], alias_2)); + fail_if(new_he->h_aliases[2] != NULL); + fail_if(new_he->h_addrtype != 123); + fail_if(new_he->h_length != sizeof(addr_1)); + fail_if(memcmp(new_he->h_addr_list[0], addr_1, sizeof(addr_1))); + fail_if(memcmp(new_he->h_addr_list[1], addr_2, sizeof(addr_1))); + fail_if(new_he->h_addr_list[2] != NULL); + + talloc_free(new_he); + check_leaks_pop(ctx); +} +END_TEST + +static void test_localhost(struct tevent_req *req) +{ + int recv_status; + int status; + struct hostent *hostent; + int i; + struct resolv_test_ctx *test_ctx = tevent_req_callback_data(req, + struct resolv_test_ctx); + + test_ctx->done = true; + + recv_status = resolv_gethostbyname_recv(req, test_ctx, + &status, NULL, &hostent); + talloc_zfree(req); + if (recv_status != EOK) { + DEBUG(2, ("resolv_gethostbyname_recv failed: %d\n", recv_status)); + test_ctx->error = recv_status; + return; + } + DEBUG(7, ("resolv_gethostbyname_recv status: %d\n", status)); + + test_ctx->error = ENOENT; + for (i = 0; hostent->h_addr_list[i]; i++) { + char addr_buf[256]; + inet_ntop(hostent->h_addrtype, hostent->h_addr_list[i], addr_buf, sizeof(addr_buf)); + + /* test that localhost resolves to 127.0.0.1 or ::1 */ + if (strcmp(addr_buf, "127.0.0.1") == 0 || strcmp(addr_buf, "::1") == 0) { + test_ctx->error = EOK; + } + } + talloc_free(hostent); +} + +START_TEST(test_resolv_localhost) +{ + struct resolv_test_ctx *test_ctx; + int ret = EOK; + struct tevent_req *req; + const char *hostname = "localhost.localdomain"; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + check_leaks_push(test_ctx); + req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_localhost, test_ctx); + ret = test_loop(test_ctx); + } + + check_leaks_pop(test_ctx); + fail_unless(ret == EOK); + + talloc_zfree(test_ctx); +} +END_TEST + +static void test_negative(struct tevent_req *req) +{ + int recv_status; + int status; + struct hostent *hostent; + struct resolv_test_ctx *test_ctx; + + test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); + test_ctx->done = true; + + recv_status = resolv_gethostbyname_recv(req, test_ctx, + &status, NULL, &hostent); + talloc_zfree(req); + if (recv_status == EOK) { + DEBUG(7, ("resolv_gethostbyname_recv succeeded in a negative test")); + return; + } + + test_ctx->error = status; + DEBUG(2, ("resolv_gethostbyname_recv status: %d: %s\n", status, resolv_strerror(status))); +} + +START_TEST(test_resolv_negative) +{ + int ret = EOK; + struct tevent_req *req; + const char *hostname = "sssd.foo"; + struct resolv_test_ctx *test_ctx; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + check_leaks_push(test_ctx); + req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_negative, test_ctx); + ret = test_loop(test_ctx); + } + + check_leaks_pop(test_ctx); + + fail_unless(ret != EOK); + fail_unless(test_ctx->error == ARES_ENOTFOUND); + talloc_zfree(test_ctx); +} +END_TEST + +static void test_internet(struct tevent_req *req) +{ + int recv_status; + int status; + struct resolv_test_ctx *test_ctx; + void *tmp_ctx; + struct hostent *hostent = NULL; + struct ares_txt_reply *txt_replies = NULL, *txtptr; + struct ares_srv_reply *srv_replies = NULL, *srvptr; + + test_ctx = tevent_req_callback_data(req, struct resolv_test_ctx); + + test_ctx->done = true; + + tmp_ctx = talloc_new(test_ctx); + check_leaks_push(tmp_ctx); + + switch (test_ctx->tested_function) { + case TESTING_HOSTNAME: + recv_status = resolv_gethostbyname_recv(req, tmp_ctx, + &status, NULL, &hostent); + test_ctx->error = (hostent->h_length == 0) ? ENOENT : EOK; + break; + case TESTING_TXT: + recv_status = resolv_gettxt_recv(tmp_ctx, req, &status, NULL, + &txt_replies); + test_ctx->error = (txt_replies == NULL) ? ENOENT : EOK; + for (txtptr = txt_replies; txtptr != NULL; txtptr = txtptr->next) { + DEBUG(2, ("TXT Record: %s\n", txtptr->txt)); + } + break; + case TESTING_SRV: + recv_status = resolv_getsrv_recv(tmp_ctx, req, &status, NULL, + &srv_replies); + test_ctx->error = (srv_replies == NULL) ? ENOENT : EOK; + for (srvptr = srv_replies; srvptr != NULL; srvptr = srvptr->next) { + DEBUG(2, ("SRV Record: %d %d %d %s\n", srvptr->weight, + srvptr->priority, srvptr->port, + srvptr->host)); + } + break; + } + talloc_zfree(req); + fail_if(recv_status != EOK, "The recv function failed: %d", recv_status); + DEBUG(7, ("recv status: %d\n", status)); + + if (hostent != NULL) { + talloc_free(hostent); + } else if (txt_replies != NULL) { + talloc_free(txt_replies); + } else if (srv_replies != NULL) { + talloc_free(srv_replies); + } + check_leaks_pop(tmp_ctx); +} + +START_TEST(test_resolv_internet) +{ + int ret = EOK; + struct tevent_req *req; + const char *hostname = "redhat.com"; + struct resolv_test_ctx *test_ctx; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + test_ctx->tested_function = TESTING_HOSTNAME; + + check_leaks_push(test_ctx); + req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_internet, test_ctx); + ret = test_loop(test_ctx); + } + + fail_unless(ret == EOK); + check_leaks_pop(test_ctx); + talloc_zfree(test_ctx); +} +END_TEST + +START_TEST(test_resolv_internet_txt) +{ + int ret; + struct tevent_req *req; + struct resolv_test_ctx *test_ctx; + + ret = setup_resolv_test(&test_ctx); + fail_if(ret != EOK, "Could not set up test"); + test_ctx->tested_function = TESTING_TXT; + + check_leaks_push(test_ctx); + + req = resolv_gettxt_send(test_ctx, test_ctx->ev, test_ctx->resolv, txt_host); + fail_if(req == NULL, "Function resolv_gettxt_send failed"); + + tevent_req_set_callback(req, test_internet, test_ctx); + ret = test_loop(test_ctx); + fail_unless(ret == EOK); + + check_leaks_pop(test_ctx); + + talloc_zfree(test_ctx); +} +END_TEST + +START_TEST(test_resolv_internet_srv) +{ + int ret; + struct tevent_req *req; + struct resolv_test_ctx *test_ctx; + + ret = setup_resolv_test(&test_ctx); + fail_if(ret != EOK, "Could not set up test"); + test_ctx->tested_function = TESTING_SRV; + + check_leaks_push(test_ctx); + + req = resolv_getsrv_send(test_ctx, test_ctx->ev, test_ctx->resolv, srv_host); + fail_if(req == NULL, "Function resolv_getsrv_send failed"); + + tevent_req_set_callback(req, test_internet, test_ctx); + ret = test_loop(test_ctx); + fail_unless(ret == EOK); + + check_leaks_pop(test_ctx); + + talloc_zfree(test_ctx); +} +END_TEST + +static void resolv_free_context(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct resolv_ctx *rctx = talloc_get_type(ptr, struct resolv_ctx); + DEBUG(7, ("freeing the context\n")); + + talloc_free(rctx); +} + +static void resolv_free_done(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct resolv_test_ctx *tctx = talloc_get_type(ptr, struct resolv_test_ctx); + DEBUG(7, ("marking test as done\n")); + + tctx->error = EOK; + tctx->done = true; +} + +START_TEST(test_resolv_free_context) +{ + int ret = EOK; + struct tevent_req *req; + const char *hostname = "redhat.com"; + struct resolv_test_ctx *test_ctx; + struct tevent_timer *free_timer, *terminate_timer; + struct timeval free_tv, terminate_tv; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + fail("Error calling resolv_gethostbyname_send"); + goto done; + } + + gettimeofday(&free_tv, NULL); + free_tv.tv_sec += 1; + free_tv.tv_usec = 0; + terminate_tv.tv_sec = free_tv.tv_sec + 1; + terminate_tv.tv_usec = 0; + + free_timer = tevent_add_timer(test_ctx->ev, test_ctx, free_tv, resolv_free_context, test_ctx->resolv); + if (free_timer == NULL) { + fail("Error calling tevent_add_timer"); + goto done; + } + + terminate_timer = tevent_add_timer(test_ctx->ev, test_ctx, terminate_tv, resolv_free_done, test_ctx); + if (terminate_timer == NULL) { + fail("Error calling tevent_add_timer"); + goto done; + } + + ret = test_loop(test_ctx); + fail_unless(ret == EOK); + +done: + talloc_zfree(test_ctx); +} +END_TEST + +static void resolv_free_req(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *ptr) +{ + struct tevent_req *req = talloc_get_type(ptr, struct tevent_req); + DEBUG(7, ("freeing the request\n")); + + talloc_free(req); +} + +START_TEST(test_resolv_free_req) +{ + int ret = EOK; + struct tevent_req *req; + const char *hostname = "redhat.com"; + struct resolv_test_ctx *test_ctx; + struct tevent_timer *free_timer, *terminate_timer; + struct timeval free_tv, terminate_tv; + + ret = setup_resolv_test(&test_ctx); + if (ret != EOK) { + fail("Could not set up test"); + return; + } + + check_leaks_push(test_ctx); + req = resolv_gethostbyname_send(test_ctx, test_ctx->ev, test_ctx->resolv, hostname); + DEBUG(7, ("Sent resolv_gethostbyname\n")); + if (req == NULL) { + fail("Error calling resolv_gethostbyname_send"); + goto done; + } + + gettimeofday(&free_tv, NULL); + free_tv.tv_sec += 1; + free_tv.tv_usec = 0; + terminate_tv.tv_sec = free_tv.tv_sec + 1; + terminate_tv.tv_usec = 0; + + free_timer = tevent_add_timer(test_ctx->ev, test_ctx, free_tv, resolv_free_req, req); + if (free_timer == NULL) { + fail("Error calling tevent_add_timer"); + goto done; + } + + terminate_timer = tevent_add_timer(test_ctx->ev, test_ctx, terminate_tv, resolv_free_done, test_ctx); + if (terminate_timer == NULL) { + fail("Error calling tevent_add_timer"); + goto done; + } + + ret = test_loop(test_ctx); + check_leaks_pop(test_ctx); + fail_unless(ret == EOK); + +done: + talloc_zfree(test_ctx); +} +END_TEST + +Suite *create_resolv_suite(void) +{ + Suite *s = suite_create("resolv"); + + TCase *tc_resolv = tcase_create("RESOLV Tests"); + + tcase_add_checked_fixture(tc_resolv, leak_check_setup, leak_check_teardown); + /* Do some testing */ + tcase_add_test(tc_resolv, test_copy_hostent); + tcase_add_test(tc_resolv, test_resolv_localhost); + tcase_add_test(tc_resolv, test_resolv_negative); + if (use_net_test) { + tcase_add_test(tc_resolv, test_resolv_internet); + if (txt_host != NULL) { + tcase_add_test(tc_resolv, test_resolv_internet_txt); + } + if (srv_host != NULL) { + tcase_add_test(tc_resolv, test_resolv_internet_srv); + } + } + tcase_add_test(tc_resolv, test_resolv_free_context); + tcase_add_test(tc_resolv, test_resolv_free_req); + + /* Add all test cases to the test suite */ + suite_add_tcase(s, tc_resolv); + + return s; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + int failure_count; + Suite *resolv_suite; + SRunner *sr; + int debug = 0; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "debug-level", 'd', POPT_ARG_INT, &debug, 0, "Set debug level", NULL }, + { "use-net-test", 'n', POPT_ARG_NONE, 0, 'n', "Run tests that need an active internet connection", NULL }, + { "txt-host", 't', POPT_ARG_STRING, 0, 't', "Specify the host used for TXT record testing", NULL }, + { "srv-host", 's', POPT_ARG_STRING, 0, 's', "Specify the host used for SRV record testing", NULL }, + POPT_TABLEEND + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + case 'n': + use_net_test = 1; + break; + case 't': + txt_host = poptGetOptArg(pc); + break; + case 's': + srv_host = poptGetOptArg(pc); + break; + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + debug_level = debug; + + resolv_suite = create_resolv_suite(); + sr = srunner_create(resolv_suite); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + failure_count = srunner_ntests_failed(sr); + srunner_free(sr); + return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + diff --git a/src/tests/stress-tests.c b/src/tests/stress-tests.c new file mode 100644 index 000000000..945053185 --- /dev/null +++ b/src/tests/stress-tests.c @@ -0,0 +1,328 @@ +/* + SSSD + + Stress tests + + Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <signal.h> +#include <stdlib.h> +#include <talloc.h> +#include <popt.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> + +#include "util/util.h" + +#define DEFAULT_START 10 +#define DEFAULT_STOP 20 + +#define NAME_SIZE 255 +#define CHUNK 64 + + +/* How many tests failed */ +int failure_count; + +/* Be chatty */ +int verbose; + +/* + * Look up one user. If the user is not found using getpwnam, the success + * or failure depends on enoent_fail being set. + */ +int test_lookup_user(const char *name, int enoent_fail) +{ + struct passwd *pwd = NULL; + int ret = 0; + int error; + + errno = 0; + pwd = getpwnam(name); + error = errno; + if (pwd == NULL) { + if (errno == 0 || errno == ENOENT) { + ret = (enoent_fail == 1) ? ENOENT : 0; + } + } + + if (ret != 0 && verbose) { + fprintf(stderr, + "getpwnam failed (name: %s): errno = %d, error = %s\n", + name, ret, strerror(ret)); + } + + return ret; +} + +/* + * Look up one group. If the user is not found using getgrnam, the success + * or failure depends on enoent_fail being set. + */ +int test_lookup_group(const char *name, int enoent_fail) +{ + struct group *grp = NULL; + int ret = 0; + + errno = 0; + grp = getgrnam(name); + if (grp == NULL) { + if (errno == 0 || errno == ENOENT) { + ret = enoent_fail ? ENOENT : 0; + } + } + + if (ret != 0 && verbose) { + fprintf(stderr, + "getgrnam failed (name %s): errno = %d, error = %s\n", + name, ret, strerror(ret)); + } + + return ret; +} + +int run_one_testcase(const char *name, int group, int enoent_fail) +{ + if (group) { + return test_lookup_group(name, enoent_fail); + } else { + return test_lookup_user(name, enoent_fail); + } +} + +/* + * Beware, has side-effects: changes global variable failure_count + */ +void child_handler(int signum) +{ + int status, ret; + + while ((ret = wait(&status)) > 0) { + if (ret == -1) { + perror("wait"); + exit(EXIT_FAILURE); + } + + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + if (ret) { + if (verbose) { + fprintf(stderr, + "A child exited with error code %d\n", + WEXITSTATUS(status)); + } + ++failure_count; + } + } else ++failure_count; + } +} + +int generate_names(TALLOC_CTX *mem_ctx, const char *prefix, + int start, int stop, char ***_out) +{ + char **out; + int num_names = stop-start+1; + int idx = 0; + + out = talloc_array(mem_ctx, char *, num_names+1); + if (out == NULL) { + return ENOMEM; + } + + for (idx = 0; idx < num_names; ++idx) { + out[idx] = talloc_asprintf(mem_ctx, "%s%d", prefix, idx); + if (out[idx] == NULL) { + return ENOMEM; + } + } + out[idx] = NULL; + + *_out = out; + return EOK; +} + +int read_names(TALLOC_CTX *mem_ctx, FILE *stream, char ***_out) +{ + char one_name[NAME_SIZE]; + int n = 0; + int array_size = CHUNK; + int ret; + char **out; + + out = talloc_array(mem_ctx, char *, CHUNK+1); + if (out == NULL) { + return ENOMEM; + } + while (fgets(one_name, NAME_SIZE, stream)) { + out[n] = talloc_strdup(mem_ctx, one_name); + if (out[n] == NULL) { + return ENOMEM; + } + if ((n++ % CHUNK) == 0) { + array_size += CHUNK; + out = talloc_realloc(mem_ctx, out, char *, array_size); + if (out == NULL) { + return ENOMEM; + } + } + } + + if ((ret = ferror(stream))) { + return ret; + } + out[n] = NULL; + + *_out = out; + return EOK; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + int pc_start=DEFAULT_START; + int pc_stop=DEFAULT_STOP; + int pc_enoent_fail=0; + int pc_groups=0; + int pc_verbosity = 0; + char *pc_prefix = NULL; + TALLOC_CTX *ctx = NULL; + char **names = NULL; + + int status, idx, ret; + pid_t pid; + struct sigaction action, old_action; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "groups", 'g', POPT_ARG_NONE, &pc_groups, 0, + "Lookup in groups instead of users" }, + { "prefix", '\0', POPT_ARG_STRING, &pc_prefix, 0, + "The username prefix", NULL }, + { "start", '\0', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, + &pc_start, 0, + "Start value to append to prefix", NULL }, + { "stop", '\0', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, + &pc_stop, 0, + "End value to append to prefix", NULL }, + { "enoent-fail", '\0', POPT_ARG_NONE, &pc_enoent_fail, 0, + "Fail on not getting the requested NSS data (default: No)", + NULL }, + { "verbose", 'v', POPT_ARG_NONE, 0, 'v', + "Be verbose" }, + POPT_TABLEEND + }; + + /* parse the params */ + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'v': + pc_verbosity = 1; + break; + + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + verbose = pc_verbosity; + + if (pc_prefix) { + ret = generate_names(ctx, pc_prefix, pc_start, pc_stop, &names); + if (ret != EOK) { + if (verbose) { + errno = ret; + perror("generate_names"); + } + exit(EXIT_FAILURE); + } + } else { + ret = read_names(ctx, stdin, &names); + if (ret != EOK) { + if (verbose) { + errno = ret; + perror("read_names"); + } + exit(EXIT_FAILURE); + } + } + + /* Reap the children in a handler asynchronously so we can + * somehow protect against too many processes */ + action.sa_handler = child_handler; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGCHLD); + action.sa_flags = SA_NOCLDSTOP; + + sigaction(SIGCHLD, &action, &old_action); + + /* Fire up the child processes */ + idx = 0; + for (idx=0; names[idx]; idx++) { + pid = fork(); + if (pid == -1) { + /* Try again in hope that some child has exited */ + if (errno == EAGAIN) { + continue; + } + perror("fork"); + exit(EXIT_FAILURE); + } else if ( pid == 0 ) { + /* child */ + ret = run_one_testcase(names[idx], pc_groups, pc_enoent_fail); + exit(ret); + } + } + + /* Process the rest of the children here in main */ + sigaction(SIGCHLD, &old_action, NULL); + while ((ret = wait(&status)) > 0) { + if (ret == -1) { + perror("wait"); + exit(EXIT_FAILURE); + } + + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + if (ret) { + if (verbose) { + fprintf(stderr, + "A child exited with error code %d\n", + WEXITSTATUS(status)); + } + ++failure_count; + } + } else ++failure_count; + } + + if (pc_verbosity) { + fprintf(stderr, + "Total tests run: %d\nPassed: %d\nFailed: %d\n", + idx, + idx - failure_count, + failure_count); + } + return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/src/tests/strtonum-tests.c b/src/tests/strtonum-tests.c new file mode 100644 index 000000000..7b9cf522c --- /dev/null +++ b/src/tests/strtonum-tests.c @@ -0,0 +1,455 @@ +/* + SSSD + + InfoPipe + + Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <check.h> +#include <errno.h> +#include <popt.h> +#include "util/util.h" +#include "util/strtonum.h" + +/******************** + * Utility routines * + ********************/ +#define EXPECT_UNSET_ERRNO(error) \ + do { \ + fail_unless(error == 0, "errno unexpectedly set to %d[%s]", \ + error, strerror(error)); \ + } while(0) + +#define CHECK_RESULT(expected, actual) \ + do { \ + fail_unless(result == expected, "Expected %ld, got %ld", \ + expected, result); \ + } while(0) + +#define CHECK_ERRNO(expected, actual) \ + do { \ + fail_unless(error == ERANGE, "Expected errno %d[%s], got %d[%s]", \ + ERANGE, strerror(ERANGE), \ + error, strerror(ERANGE)); \ + } while(0) + +#define CHECK_ENDPTR(expected, actual) \ + do { \ + fail_unless(actual == expected, "Expected endptr %p, got %p", \ + expected, actual); \ + } while(0) + +#define CHECK_ZERO_ENDPTR(endptr) \ + do { \ + fail_unless(endptr && *endptr == '\0', "Invalid endptr"); \ + } while(0); + +/****************** + * strtoint tests * + ******************/ + +/* Base-10 */ +START_TEST (test_strtoint32_pos_integer_base_10) +{ + int32_t result; + const char *input = "123"; + int32_t expected = 123; + char *endptr; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtoint32_neg_integer_base_10) +{ + int32_t result; + const char *input = "-123"; + int32_t expected = -123; + char *endptr; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtoint32_pos_integer_intmax_base_10) +{ + int32_t result; + const char *input = "2147483647"; + int32_t expected = INT32_MAX; + char *endptr; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtoint32_neg_integer_intmin_base_10) +{ + int32_t result; + const char *input = "-2147483648"; + int32_t expected = INT32_MIN; + char *endptr; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtoint32_pos_integer_overflow_base_10) +{ + int32_t result; + const char *input = "8589934592"; + int32_t expected = INT32_MAX; + char *endptr; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + CHECK_ERRNO(ERANGE, error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, actual); +} +END_TEST + +START_TEST (test_strtoint32_pos_integer_underflow_base_10) +{ + int32_t result; + const char *input = "-8589934592"; + int32_t expected = INT32_MIN; + char *endptr; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + CHECK_ERRNO(ERANGE, error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, actual); +} +END_TEST + +START_TEST (test_strtoint32_mixed_alphanumeric_base_10) +{ + int32_t result; + const char *input = "12b13"; + int32_t expected = 12; + char *endptr; + const char *expected_endptr = input+2; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ENDPTR(expected_endptr, endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtoint32_alphaonly_base_10) +{ + int32_t result; + const char *input = "alpha"; + int32_t expected = 0; + char *endptr; + const char *expected_endptr = input; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ENDPTR(expected_endptr, endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtoint32_alphastart_base_10) +{ + int32_t result; + const char *input = "alpha12345"; + int32_t expected = 0; + char *endptr; + const char *expected_endptr = input; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ENDPTR(expected_endptr, endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtoint32_emptystring_base_10) +{ + int32_t result; + const char *input = ""; + int32_t expected = 0; + char *endptr; + const char *expected_endptr = input; + errno_t error; + + result = strtoint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ENDPTR(expected_endptr, endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +/******************* + * strtouint tests * + *******************/ + +/* Base-10 */ +START_TEST (test_strtouint32_pos_integer_base_10) +{ + uint32_t result; + const char *input = "123"; + uint32_t expected = 123; + char *endptr; + errno_t error; + + result = strtouint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtouint32_neg_integer_base_10) +{ + uint32_t result; + const char *input = "-123"; + uint32_t expected = -123; + char *endptr; + errno_t error; + + result = strtouint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtouint32_pos_integer_uintmax_base_10) +{ + uint32_t result; + const char *input = "4294967295"; + uint32_t expected = UINT32_MAX; + char *endptr; + errno_t error; + + result = strtouint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, result); +} +END_TEST + + +START_TEST (test_strtouint32_pos_integer_overflow_base_10) +{ + uint32_t result; + const char *input = "8589934592"; + uint32_t expected = UINT32_MAX; + char *endptr; + errno_t error; + + result = strtouint32(input, &endptr, 10); + error = errno; + + CHECK_ERRNO(ERANGE, error); + CHECK_ZERO_ENDPTR(endptr); + CHECK_RESULT(expected, actual); +} +END_TEST + +START_TEST (test_strtouint32_mixed_alphanumeric_base_10) +{ + uint32_t result; + const char *input = "12b13"; + uint32_t expected = 12; + char *endptr; + const char *expected_endptr = input+2; + errno_t error; + + result = strtouint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ENDPTR(expected_endptr, endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtouint32_alphaonly_base_10) +{ + uint32_t result; + const char *input = "alpha"; + uint32_t expected = 0; + char *endptr; + const char *expected_endptr = input; + errno_t error; + + result = strtouint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ENDPTR(expected_endptr, endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtouint32_alphastart_base_10) +{ + uint32_t result; + const char *input = "alpha12345"; + uint32_t expected = 0; + char *endptr; + const char *expected_endptr = input; + errno_t error; + + result = strtouint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ENDPTR(expected_endptr, endptr); + CHECK_RESULT(expected, result); +} +END_TEST + +START_TEST (test_strtouint32_emptystring_base_10) +{ + uint32_t result; + const char *input = ""; + uint32_t expected = 0; + char *endptr; + const char *expected_endptr = input; + errno_t error; + + result = strtouint32(input, &endptr, 10); + error = errno; + + EXPECT_UNSET_ERRNO(error); + CHECK_ENDPTR(expected_endptr, endptr); + CHECK_RESULT(expected, result); +} +END_TEST + + + +Suite *create_strtonum_suite(void) +{ + Suite *s = suite_create("strtonum"); + + TCase *tc_strtoint32 = tcase_create("strtoint32 Tests"); + tcase_add_test(tc_strtoint32, test_strtoint32_pos_integer_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_neg_integer_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_pos_integer_intmax_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_neg_integer_intmin_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_pos_integer_overflow_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_pos_integer_underflow_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_mixed_alphanumeric_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_alphaonly_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_alphastart_base_10); + tcase_add_test(tc_strtoint32, test_strtoint32_emptystring_base_10); + + TCase *tc_strtouint32 = tcase_create("strtouint32 Tests"); + tcase_add_test(tc_strtouint32, test_strtouint32_pos_integer_base_10); + tcase_add_test(tc_strtouint32, test_strtouint32_neg_integer_base_10); + tcase_add_test(tc_strtouint32, test_strtouint32_pos_integer_uintmax_base_10); + tcase_add_test(tc_strtouint32, test_strtouint32_pos_integer_overflow_base_10); + tcase_add_test(tc_strtouint32, test_strtouint32_mixed_alphanumeric_base_10); + tcase_add_test(tc_strtouint32, test_strtouint32_alphaonly_base_10); + tcase_add_test(tc_strtouint32, test_strtouint32_alphastart_base_10); + tcase_add_test(tc_strtouint32, test_strtouint32_emptystring_base_10); + + /* Add all test cases to the suite */ + suite_add_tcase(s, tc_strtoint32); + suite_add_tcase(s, tc_strtouint32); + + return s; +} + + +int main(int argc, const char *argv[]) { + int opt; + poptContext pc; + int failure_count; + Suite *strtonum_suite; + SRunner *sr; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + { NULL } + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + strtonum_suite = create_strtonum_suite(); + sr = srunner_create(strtonum_suite); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + failure_count = srunner_ntests_failed(sr); + srunner_free(sr); + return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c new file mode 100644 index 000000000..8b486b691 --- /dev/null +++ b/src/tests/sysdb-tests.c @@ -0,0 +1,3330 @@ +/* + SSSD + + System Database + + Copyright (C) Stephen Gallagher <sgallagh@redhat.com> 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <check.h> +#include <talloc.h> +#include <tevent.h> +#include <popt.h> +#include <sys/stat.h> +#include <sys/types.h> +#include "util/util.h" +#include "confdb/confdb_setup.h" +#include "db/sysdb_private.h" + +#define TESTS_PATH "tests_sysdb" +#define TEST_CONF_FILE "tests_conf.ldb" + +#define TEST_ATTR_NAME "test_attr_name" +#define TEST_ATTR_VALUE "test_attr_value" +#define TEST_ATTR_UPDATE_VALUE "test_attr_update_value" +#define TEST_ATTR_ADD_NAME "test_attr_add_name" +#define TEST_ATTR_ADD_VALUE "test_attr_add_value" +#define CUSTOM_TEST_CONTAINER "custom_test_container" +#define CUSTOM_TEST_OBJECT "custom_test_object" + +#define ASQ_TEST_USER "testuser27010" +#define ASQ_TEST_USER_UID 27010 + +#define MBO_USER_BASE 27500 +#define MBO_GROUP_BASE 28500 + +struct sysdb_test_ctx { + struct sysdb_ctx *sysdb; + struct confdb_ctx *confdb; + struct tevent_context *ev; + struct sss_domain_info *domain; +}; + +static int setup_sysdb_tests(struct sysdb_test_ctx **ctx) +{ + struct sysdb_test_ctx *test_ctx; + char *conf_db; + int ret; + + const char *val[2]; + val[1] = NULL; + + /* Create tests directory if it doesn't exist */ + /* (relative to current dir) */ + ret = mkdir(TESTS_PATH, 0775); + if (ret == -1 && errno != EEXIST) { + fail("Could not create %s directory", TESTS_PATH); + return EFAULT; + } + + test_ctx = talloc_zero(NULL, struct sysdb_test_ctx); + if (test_ctx == NULL) { + fail("Could not allocate memory for test context"); + return ENOMEM; + } + + /* Create an event context + * It will not be used except in confdb_init and sysdb_init + */ + test_ctx->ev = tevent_context_init(test_ctx); + if (test_ctx->ev == NULL) { + fail("Could not create event context"); + talloc_free(test_ctx); + return EIO; + } + + conf_db = talloc_asprintf(test_ctx, "%s/%s", TESTS_PATH, TEST_CONF_FILE); + if (conf_db == NULL) { + fail("Out of memory, aborting!"); + talloc_free(test_ctx); + return ENOMEM; + } + DEBUG(3, ("CONFDB: %s\n", conf_db)); + + /* Connect to the conf db */ + ret = confdb_init(test_ctx, &test_ctx->confdb, conf_db); + if (ret != EOK) { + fail("Could not initialize connection to the confdb"); + talloc_free(test_ctx); + return ret; + } + + val[0] = "LOCAL"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/sssd", "domains", val); + if (ret != EOK) { + fail("Could not initialize domains placeholder"); + talloc_free(test_ctx); + return ret; + } + + val[0] = "local"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/LOCAL", "id_provider", val); + if (ret != EOK) { + fail("Could not initialize provider"); + talloc_free(test_ctx); + return ret; + } + + val[0] = "TRUE"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/LOCAL", "enumerate", val); + if (ret != EOK) { + fail("Could not initialize LOCAL domain"); + talloc_free(test_ctx); + return ret; + } + + val[0] = "TRUE"; + ret = confdb_add_param(test_ctx->confdb, true, + "config/domain/LOCAL", "cache_credentials", val); + if (ret != EOK) { + fail("Could not initialize LOCAL domain"); + talloc_free(test_ctx); + return ret; + } + + ret = confdb_get_domain(test_ctx->confdb, "local", &test_ctx->domain); + if (ret != EOK) { + fail("Could not retrieve LOCAL domain"); + talloc_free(test_ctx); + return ret; + } + + ret = sysdb_domain_init(test_ctx, test_ctx->ev, + test_ctx->domain, TESTS_PATH, &test_ctx->sysdb); + if (ret != EOK) { + fail("Could not initialize connection to the sysdb (%d)", ret); + talloc_free(test_ctx); + return ret; + } + + *ctx = test_ctx; + return EOK; +} + +struct test_data { + struct tevent_context *ev; + struct sysdb_handle *handle; + struct sysdb_test_ctx *ctx; + + const char *username; + const char *groupname; + uid_t uid; + gid_t gid; + const char *shell; + + bool finished; + int error; + + struct sysdb_attrs *attrs; + const char *attrval; /* testing sysdb_get_user_attr */ + const char **attrlist; + struct ldb_message *msg; + + size_t msgs_count; + struct ldb_message **msgs; +}; + +static int test_loop(struct test_data *data) +{ + while (!data->finished) + tevent_loop_once(data->ctx->ev); + + return data->error; +} + +static void test_req_done(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + + data->error = sysdb_transaction_commit_recv(req); + data->finished = true; +} + +static void test_return(struct test_data *data, int error) +{ + struct tevent_req *req; + + if (error != EOK) { + goto fail; + } + + req = sysdb_transaction_commit_send(data, data->ev, data->handle); + if (!req) { + error = ENOMEM; + goto fail; + } + tevent_req_set_callback(req, test_req_done, data); + + return; + +fail: + /* free transaction */ + talloc_zfree(data->handle); + + data->error = error; + data->finished = true; +} + +static void test_add_user_done(struct tevent_req *subreq); + +static void test_add_user(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + char *homedir; + char *gecos; + int ret; + + ret = sysdb_transaction_recv(subreq, data, &data->handle); + talloc_zfree(subreq); + if (ret != EOK) { + return test_return(data, ret); + } + + homedir = talloc_asprintf(data, "/home/testuser%d", data->uid); + gecos = talloc_asprintf(data, "Test User %d", data->uid); + + subreq = sysdb_add_user_send(data, data->ev, data->handle, + data->ctx->domain, data->username, + data->uid, 0, + gecos, homedir, "/bin/bash", + NULL, 0); + if (!subreq) { + return test_return(data, ENOMEM); + } + tevent_req_set_callback(subreq, test_add_user_done, data); +} + +static void test_add_user_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_add_user_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_store_user_done(struct tevent_req *subreq); + +static void test_store_user(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + char *homedir; + char *gecos; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + homedir = talloc_asprintf(data, "/home/testuser%d", data->uid); + gecos = talloc_asprintf(data, "Test User %d", data->uid); + + subreq = sysdb_store_user_send(data, data->ev, data->handle, + data->ctx->domain, data->username, "x", + data->uid, 0, + gecos, homedir, + data->shell ? data->shell : "/bin/bash", + NULL, -1); + if (!subreq) { + test_return(data, ENOMEM); + return; + } + tevent_req_set_callback(subreq, test_store_user_done, data); +} + +static void test_store_user_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_store_user_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_remove_user_done(struct tevent_req *subreq); + +static void test_remove_user(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct ldb_dn *user_dn; + struct tevent_req *subreq; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + user_dn = sysdb_user_dn(data->ctx->sysdb, data, "LOCAL", data->username); + if (!user_dn) return test_return(data, ENOMEM); + + subreq = sysdb_delete_entry_send(data, data->ev, data->handle, user_dn, true); + if (!subreq) return test_return(data, ENOMEM); + + tevent_req_set_callback(subreq, test_remove_user_done, data); +} + +static void test_remove_user_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_remove_user_by_uid_done(struct tevent_req *subreq); + +static void test_remove_user_by_uid(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + subreq = sysdb_delete_user_send(data, data->ev, + NULL, data->handle, + data->ctx->domain, + NULL, data->uid); + if (!subreq) return test_return(data, ENOMEM); + + tevent_req_set_callback(subreq, test_remove_user_by_uid_done, data); +} + +static void test_remove_user_by_uid_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + + ret = sysdb_delete_user_recv(subreq); + if (ret == ENOENT) ret = EOK; + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_remove_nonexistent_group_done(struct tevent_req *subreq); + +static void test_remove_nonexistent_group(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + subreq = sysdb_delete_group_send(data, data->ev, + NULL, data->handle, + data->ctx->domain, + NULL, data->uid); + if (!subreq) return test_return(data, ENOMEM); + + tevent_req_set_callback(subreq, test_remove_nonexistent_group_done, data); +} + +static void test_remove_nonexistent_group_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + + ret = sysdb_delete_group_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_remove_nonexistent_user_done(struct tevent_req *subreq); + +static void test_remove_nonexistent_user(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + subreq = sysdb_delete_user_send(data, data->ev, + NULL, data->handle, + data->ctx->domain, + NULL, data->uid); + if (!subreq) return test_return(data, ENOMEM); + + tevent_req_set_callback(subreq, test_remove_nonexistent_user_done, data); +} + +static void test_remove_nonexistent_user_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + + ret = sysdb_delete_user_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_add_group_done(struct tevent_req *subreq); + +static void test_add_group(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, + struct test_data); + struct tevent_req *subreq; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + subreq = sysdb_add_group_send(data, data->ev, data->handle, + data->ctx->domain, data->groupname, + data->gid, NULL, 0); + if (!subreq) { + test_return(data, ret); + } + tevent_req_set_callback(subreq, test_add_group_done, data); +} + +static void test_add_group_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_add_group_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_store_group_done(struct tevent_req *subreq); + +static void test_store_group(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + subreq = sysdb_store_group_send(data, data->ev, data->handle, + data->ctx->domain, data->groupname, + data->gid, NULL, -1); + if (!subreq) { + test_return(data, ret); + } + tevent_req_set_callback(subreq, test_store_group_done, data); +} + +static void test_store_group_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_store_group_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_remove_group_done(struct tevent_req *subreq); + +static void test_remove_group(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + struct ldb_dn *group_dn; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + group_dn = sysdb_group_dn(data->ctx->sysdb, data, "LOCAL", data->groupname); + if (!group_dn) return test_return(data, ENOMEM); + + subreq = sysdb_delete_entry_send(data, data->ev, data->handle, group_dn, true); + if (!subreq) return test_return(data, ENOMEM); + + tevent_req_set_callback(subreq, test_remove_group_done, data); +} + +static void test_remove_group_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + + ret = sysdb_delete_entry_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_remove_group_by_gid_done(struct tevent_req *subreq); +static void test_remove_group_by_gid(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + subreq = sysdb_delete_group_send(data, data->ev, + NULL, data->handle, + data->ctx->domain, + NULL, data->gid); + if (!subreq) return test_return(data, ENOMEM); + + tevent_req_set_callback(subreq, test_remove_group_by_gid_done, data); +} + +static void test_remove_group_by_gid_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + + ret = sysdb_delete_group_recv(subreq); + if (ret == ENOENT) ret = EOK; + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_getpwent(void *pvt, int error, struct ldb_result *res) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + data->finished = true; + + if (error != EOK) { + data->error = error; + return; + } + + switch (res->count) { + case 0: + data->error = ENOENT; + break; + + case 1: + data->uid = ldb_msg_find_attr_as_uint(res->msgs[0], SYSDB_UIDNUM, 0); + break; + + default: + data->error = EFAULT; + break; + } +} + +static void test_getgrent(void *pvt, int error, struct ldb_result *res) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + data->finished = true; + + if (error != EOK) { + data->error = error; + return; + } + + switch (res->count) { + case 0: + data->error = ENOENT; + break; + + case 1: + data->gid = ldb_msg_find_attr_as_uint(res->msgs[0], SYSDB_GIDNUM, 0); + break; + + default: + data->error = EFAULT; + break; + } +} + +static void test_getgrgid(void *pvt, int error, struct ldb_result *res) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + data->finished = true; + + if (error != EOK) { + data->error = error; + return; + } + + switch (res->count) { + case 0: + data->error = ENOENT; + break; + + case 1: + data->groupname = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, 0); + break; + + default: + data->error = EFAULT; + break; + } +} + +static void test_getpwuid(void *pvt, int error, struct ldb_result *res) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + data->finished = true; + + if (error != EOK) { + data->error = error; + return; + } + + switch (res->count) { + case 0: + data->error = ENOENT; + break; + + case 1: + data->username = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, 0); + break; + + default: + data->error = EFAULT; + break; + } +} + +static void test_enumgrent(void *pvt, int error, struct ldb_result *res) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + const int expected = 20; /* 10 groups + 10 users (we're MPG) */ + + data->finished = true; + + if (error != EOK) { + data->error = error; + return; + } + + if (res->count != expected) { + data->error = EINVAL; + return; + } + + data->error = EOK; +} + +static void test_enumpwent(void *pvt, int error, struct ldb_result *res) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + const int expected = 10; + + data->finished = true; + + if (error != EOK) { + data->error = error; + return; + } + + if (res->count != expected) { + data->error = EINVAL; + return; + } + + data->error = EOK; +} + +static void test_set_user_attr_done(struct tevent_req *subreq); +static void test_set_user_attr(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + subreq = sysdb_set_user_attr_send(data, data->ev, data->handle, + data->ctx->domain, data->username, + data->attrs, SYSDB_MOD_REP); + if (!subreq) return test_return(data, ENOMEM); + + tevent_req_set_callback(subreq, test_set_user_attr_done, data); +} + +static void test_set_user_attr_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + + ret = sysdb_set_user_attr_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_get_user_attr(void *pvt, int error, struct ldb_result *res) +{ + struct test_data *data = talloc_get_type(pvt, struct test_data); + data->finished = true; + + if (error != EOK) { + data->error = error; + return; + } + + switch (res->count) { + case 0: + data->error = ENOENT; + break; + + case 1: + data->attrval = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SHELL, 0); + break; + + default: + data->error = EFAULT; + break; + } +} + +static void test_add_group_member_done(struct tevent_req *subreq); + +static void test_add_group_member(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + const char *username; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + username = talloc_asprintf(data, "testuser%d", data->uid); + if (username == NULL) { + test_return(data, ENOMEM); + } + + subreq = sysdb_add_group_member_send(data, data->ev, + data->handle, data->ctx->domain, + data->groupname, username); + if (!subreq) { + test_return(data, ENOMEM); + } + + tevent_req_set_callback(subreq, test_add_group_member_done, data); +} + +static void test_add_group_member_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret = sysdb_add_group_member_recv(subreq); + + test_return(data, ret); +} + +static void test_remove_group_member_done(struct tevent_req *subreq); + +static void test_remove_group_member(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + const char *username; + int ret; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + username = talloc_asprintf(data, "testuser%d", data->uid); + if (username == NULL) { + test_return(data, ENOMEM); + } + + subreq = sysdb_remove_group_member_send(data, data->ev, + data->handle, data->ctx->domain, + data->groupname, username); + if (!subreq) { + test_return(data, ENOMEM); + } + + tevent_req_set_callback(subreq, test_remove_group_member_done, data); +} + +static void test_remove_group_member_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret = sysdb_remove_group_member_recv(subreq); + + test_return(data, ret); +} + +static void test_store_custom_done(struct tevent_req *subreq); + +static void test_store_custom(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + char *object_name; + + ret = sysdb_transaction_recv(subreq, data, &data->handle); + talloc_zfree(subreq); + if (ret != EOK) { + return test_return(data, ret); + } + + object_name = talloc_asprintf(data, "%s_%d", CUSTOM_TEST_OBJECT, data->uid); + if (!object_name) { + return test_return(data, ENOMEM); + } + + subreq = sysdb_store_custom_send(data, data->ev, data->handle, + data->ctx->domain, object_name, + CUSTOM_TEST_CONTAINER, data->attrs); + if (!subreq) { + return test_return(data, ENOMEM); + } + tevent_req_set_callback(subreq, test_store_custom_done, data); +} + +static void test_store_custom_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_store_custom_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_search_done(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + + data->finished = true; + return; +} + +static void test_delete_custom_done(struct tevent_req *subreq); + +static void test_delete_custom(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + + ret = sysdb_transaction_recv(subreq, data, &data->handle); + talloc_zfree(subreq); + if (ret != EOK) { + return test_return(data, ret); + } + + + subreq = sysdb_delete_custom_send(data, data->ev, data->handle, + data->ctx->domain, CUSTOM_TEST_OBJECT, + CUSTOM_TEST_CONTAINER); + if (!subreq) { + return test_return(data, ENOMEM); + } + tevent_req_set_callback(subreq, test_delete_custom_done, data); +} + +static void test_delete_custom_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_delete_custom_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +static void test_search_all_users_done(struct tevent_req *subreq); +static void test_search_all_users(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + struct ldb_dn *base_dn; + int ret; + + ret = sysdb_transaction_recv(subreq, data, &data->handle); + talloc_zfree(subreq); + if (ret != EOK) { + return test_return(data, ret); + } + + base_dn = ldb_dn_new_fmt(data, data->ctx->sysdb->ldb, SYSDB_TMPL_USER_BASE, + "LOCAL"); + if (base_dn == NULL) { + return test_return(data, ENOMEM); + } + + subreq = sysdb_search_entry_send(data, data->ev, data->handle, + base_dn, LDB_SCOPE_SUBTREE, + "objectClass=user", data->attrlist); + if (!subreq) { + return test_return(data, ENOMEM); + } + tevent_req_set_callback(subreq, test_search_all_users_done, data); +} + +static void test_search_all_users_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_search_entry_recv(subreq, data, &data->msgs_count, &data->msgs); + talloc_zfree(subreq); + + test_return(data, ret); + return; +} + +static void test_delete_recursive_done(struct tevent_req *subreq); + +static void test_delete_recursive(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, + struct test_data); + int ret; + struct ldb_dn *dn; + + ret = sysdb_transaction_recv(subreq, data, &data->handle); + talloc_zfree(subreq); + if (ret != EOK) { + return test_return(data, ret); + } + + dn = ldb_dn_new_fmt(data, data->handle->ctx->ldb, SYSDB_DOM_BASE, + "LOCAL"); + if (!dn) { + return test_return(data, ENOMEM); + } + + subreq = sysdb_delete_recursive_send(data, data->ev, data->handle, dn, + false); + if (!subreq) { + return test_return(data, ENOMEM); + } + tevent_req_set_callback(subreq, test_delete_recursive_done, data); +} + +static void test_delete_recursive_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_delete_recursive_recv(subreq); + talloc_zfree(subreq); + fail_unless(ret == EOK, "sysdb_delete_recursive_recv returned [%d]", ret); + return test_return(data, ret); +} + +static void test_memberof_store_group_done(struct tevent_req *subreq); +static void test_memberof_store_group(struct tevent_req *req) +{ + struct test_data *data = tevent_req_callback_data(req, struct test_data); + struct tevent_req *subreq; + int ret; + struct sysdb_attrs *attrs = NULL; + char *member; + int i; + + ret = sysdb_transaction_recv(req, data, &data->handle); + if (ret != EOK) { + return test_return(data, ret); + } + + attrs = sysdb_new_attrs(data); + if (!attrs) { + return test_return(data, ENOMEM); + } + for (i = 0; data->attrlist && data->attrlist[i]; i++) { + member = sysdb_group_strdn(data, data->ctx->domain->name, + data->attrlist[i]); + if (!member) { + return test_return(data, ENOMEM); + } + ret = sysdb_attrs_steal_string(attrs, SYSDB_MEMBER, member); + if (ret != EOK) { + return test_return(data, ret); + } + } + + subreq = sysdb_store_group_send(data, data->ev, data->handle, + data->ctx->domain, data->groupname, + data->gid, attrs, -1); + if (!subreq) { + test_return(data, ret); + } + tevent_req_set_callback(subreq, test_memberof_store_group_done, data); +} + +static void test_memberof_store_group_done(struct tevent_req *subreq) +{ + struct test_data *data = tevent_req_callback_data(subreq, struct test_data); + int ret; + + ret = sysdb_store_group_recv(subreq); + talloc_zfree(subreq); + + return test_return(data, ret); +} + +START_TEST (test_sysdb_store_user) +{ + 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; + data->uid = _i; + data->gid = _i; + data->username = talloc_asprintf(data, "testuser%d", _i); + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_store_user, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not store user %s", data->username); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_store_user_existing) +{ + 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; + data->uid = _i; + data->gid = _i; + data->username = talloc_asprintf(data, "testuser%d", _i); + data->shell = talloc_asprintf(data, "/bin/ksh"); + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_store_user, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not store user %s", data->username); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_store_group) +{ + 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; + data->gid = _i; + data->groupname = talloc_asprintf(data, "testgroup%d", _i); + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_store_group, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not store POSIX group #%d", _i); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_remove_local_user) +{ + 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; + data->username = talloc_asprintf(data, "testuser%d", _i); + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_remove_user, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not remove user %s", data->username); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_remove_local_user_by_uid) +{ + 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; + data->uid = _i; + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_remove_user_by_uid, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not remove user with uid %d", _i); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_remove_local_group) +{ + 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; + data->groupname = talloc_asprintf(data, "testgroup%d", _i); + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_remove_group, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not remove group %s", data->groupname); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_remove_local_group_by_gid) +{ + 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; + data->gid = _i; + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_remove_group_by_gid, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not remove group with gid %d", _i); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_add_user) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + 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; + data->uid = _i; + data->gid = _i; + data->username = talloc_asprintf(data, "testuser%d", _i); + + subreq = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_add_user, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not add user %s", data->username); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_add_group) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + 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; + data->uid = _i; + data->gid = _i; + data->groupname = talloc_asprintf(data, "testgroup%d", _i); + + subreq = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_add_group, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not add group %s", data->groupname); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_getpwnam) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct test_data *data_uc; + 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->username = talloc_asprintf(data, "testuser%d", _i); + + ret = sysdb_getpwnam(test_ctx, + test_ctx->sysdb, + data->ctx->domain, + data->username, + test_getpwent, + data); + if (ret == EOK) { + ret = test_loop(data); + } + + if (ret) { + fail("sysdb_getpwnam failed for username %s (%d: %s)", + data->username, ret, strerror(ret)); + goto done; + } + fail_unless(data->uid == _i, + "Did not find the expected UID"); + + /* Search for the user with the wrong case */ + data_uc = talloc_zero(test_ctx, struct test_data); + data_uc->ctx = test_ctx; + data_uc->username = talloc_asprintf(data_uc, "TESTUSER%d", _i); + + ret = sysdb_getpwnam(test_ctx, + test_ctx->sysdb, + data_uc->ctx->domain, + data_uc->username, + test_getpwent, + data_uc); + if (ret == EOK) { + ret = test_loop(data_uc); + } + + fail_unless(ret == ENOENT, + "The upper-case username search should fail. "); + +done: + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_getgrnam) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct test_data *data_uc; + 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->groupname = talloc_asprintf(data, "testgroup%d", _i); + + ret = sysdb_getgrnam(test_ctx, + test_ctx->sysdb, + data->ctx->domain, + data->groupname, + test_getgrent, + data); + if (ret == EOK) { + ret = test_loop(data); + } + + if (ret) { + fail("sysdb_getgrnam failed for groupname %s (%d: %s)", + data->groupname, ret, strerror(ret)); + goto done; + } + fail_unless(data->gid == _i, + "Did not find the expected GID (found %d expected %d)", + data->gid, _i); + + /* Search for the group with the wrong case */ + data_uc = talloc_zero(test_ctx, struct test_data); + data_uc->ctx = test_ctx; + data_uc->groupname = talloc_asprintf(data_uc, "TESTGROUP%d", _i); + + ret = sysdb_getgrnam(test_ctx, + test_ctx->sysdb, + data_uc->ctx->domain, + data_uc->groupname, + test_getgrent, + data_uc); + if (ret == EOK) { + ret = test_loop(data_uc); + } + + fail_unless(ret == ENOENT, + "The upper-case groupname search should fail. "); +done: + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_getgrgid) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + int ret; + const char *groupname = NULL; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + if (ret != EOK) { + fail("Could not set up the test"); + return; + } + + groupname = talloc_asprintf(test_ctx, "testgroup%d", _i); + if (groupname == NULL) { + fail("Cannot allocate memory"); + return; + } + + data = talloc_zero(test_ctx, struct test_data); + data->ctx = test_ctx; + data->gid = _i; + + ret = sysdb_getgrgid(test_ctx, + test_ctx->sysdb, + data->ctx->domain, + data->gid, + test_getgrgid, + data); + if (ret == EOK) { + ret = test_loop(data); + } + + if (ret) { + fail("sysdb_getgrgid failed for gid %d (%d: %s)", + data->gid, ret, strerror(ret)); + goto done; + } + fail_unless(strcmp(data->groupname, groupname) == 0, + "Did not find the expected groupname (found %s expected %s)", + data->groupname, groupname); +done: + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_getpwuid) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + int ret; + const char *username = NULL; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + if (ret != EOK) { + fail("Could not set up the test"); + return; + } + + username = talloc_asprintf(test_ctx, "testuser%d", _i); + if (username == NULL) { + fail("Cannot allocate memory"); + return; + } + + data = talloc_zero(test_ctx, struct test_data); + data->ctx = test_ctx; + data->uid = _i; + + ret = sysdb_getpwuid(test_ctx, + test_ctx->sysdb, + data->ctx->domain, + data->uid, + test_getpwuid, + data); + if (ret == EOK) { + ret = test_loop(data); + } + + if (ret) { + fail("sysdb_getpwuid failed for uid %d (%d: %s)", + data->uid, ret, strerror(ret)); + goto done; + } + + fail_unless(strcmp(data->username, username) == 0, + "Did not find the expected username (found %s expected %s)", + data->username, username); +done: + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_enumgrent) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + 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; + + ret = sysdb_enumgrent(test_ctx, + test_ctx->sysdb, + data->ctx->domain, + test_enumgrent, + data); + if (ret == EOK) { + ret = test_loop(data); + } + + fail_unless(ret == EOK, + "sysdb_enumgrent failed (%d: %s)", + ret, strerror(ret)); + + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_enumpwent) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + 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; + + ret = sysdb_enumpwent(test_ctx, + test_ctx->sysdb, + data->ctx->domain, + NULL, + test_enumpwent, + data); + if (ret == EOK) { + ret = test_loop(data); + } + + fail_unless(ret == EOK, + "sysdb_enumpwent failed (%d: %s)", + ret, strerror(ret)); + + talloc_free(test_ctx); +} +END_TEST + + +START_TEST (test_sysdb_set_user_attr) +{ + 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; + data->username = talloc_asprintf(data, "testuser%d", _i); + + data->attrs = sysdb_new_attrs(test_ctx); + if (ret != EOK) { + fail("Could not create the changeset"); + return; + } + + ret = sysdb_attrs_add_string(data->attrs, + SYSDB_SHELL, + "/bin/ksh"); + if (ret != EOK) { + fail("Could not create the changeset"); + return; + } + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_set_user_attr, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not modify user %s", data->username); + + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_get_user_attr) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + int ret; + const char *attrs[] = { SYSDB_SHELL, NULL }; + + /* 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->username = talloc_asprintf(data, "testuser%d", _i); + + ret = sysdb_get_user_attr(data, + data->ctx->sysdb, + data->ctx->domain, + data->username, + attrs, + test_get_user_attr, + data); + if (ret == EOK) { + ret = test_loop(data); + } + + if (ret) { + fail("Could not get attributes for user %s", data->username); + goto done; + } + fail_if(strcmp(data->attrval, "/bin/ksh"), + "Got bad attribute value for user %s", + data->username); +done: + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_add_group_member) +{ + 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; + data->groupname = talloc_asprintf(data, "testgroup%d", _i); + data->uid = _i - 1000; /* the UID of user to add */ + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_add_group_member, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not modify group %s", data->groupname); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_remove_group_member) +{ + 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; + data->groupname = talloc_asprintf(data, "testgroup%d", _i); + data->uid = _i - 1000; /* the UID of user to add */ + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_remove_group_member, data); + + ret = test_loop(data); + } + + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_remove_nonexistent_user) +{ + 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; + data->uid = 12345; + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_remove_nonexistent_user, data); + + ret = test_loop(data); + } + + fail_if(ret != ENOENT, "Unexpected return code %d, expected ENOENT", ret); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_remove_nonexistent_group) +{ + 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; + data->uid = 12345; + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_remove_nonexistent_group, data); + + ret = test_loop(data); + } + + fail_if(ret != ENOENT, "Unexpected return code %d, expected ENOENT", ret); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_store_custom) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + 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; + data->uid = _i; + data->attrs = sysdb_new_attrs(test_ctx); + if (ret != EOK) { + fail("Could not create attribute list"); + return; + } + + ret = sysdb_attrs_add_string(data->attrs, + TEST_ATTR_NAME, + TEST_ATTR_VALUE); + if (ret != EOK) { + fail("Could not add attribute"); + return; + } + + subreq = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_store_custom, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not add custom object"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_search_custom_by_name) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + int ret; + char *object_name; + + /* 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); + fail_unless(data != NULL, "talloc_zero failed"); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->attrlist = talloc_array(test_ctx, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed"); + data->attrlist[0] = TEST_ATTR_NAME; + data->attrlist[1] = NULL; + + object_name = talloc_asprintf(data, "%s_%d", CUSTOM_TEST_OBJECT, 29010); + fail_unless(object_name != NULL, "talloc_asprintf failed"); + + subreq = sysdb_search_custom_by_name_send(data, data->ev, + data->ctx->sysdb, NULL, + data->ctx->domain, + object_name, + CUSTOM_TEST_CONTAINER, + data->attrlist); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_search_done, data); + + ret = test_loop(data); + + ret = sysdb_search_custom_recv(subreq, data, &data->msgs_count, + &data->msgs); + talloc_zfree(subreq); + fail_unless(ret == EOK, "sysdb_search_custom_by_name_send failed"); + + fail_unless(data->msgs_count == 1, + "Wrong number of objects, exptected [1] got [%d]", + data->msgs_count); + fail_unless(data->msgs[0]->num_elements == 1, + "Wrong number of results, expected [1] got [%d]", + data->msgs[0]->num_elements); + fail_unless(strcmp(data->msgs[0]->elements[0].name, TEST_ATTR_NAME) == 0, + "Wrong attribute name"); + fail_unless(data->msgs[0]->elements[0].num_values == 1, + "Wrong number of attribute values"); + fail_unless(strncmp((const char *)data->msgs[0]->elements[0].values[0].data, + TEST_ATTR_VALUE, + data->msgs[0]->elements[0].values[0].length) == 0, + "Wrong attribute value"); + } + + fail_if(ret != EOK, "Could not search custom object"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_update_custom) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + 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; + data->uid = 29010; + data->attrs = sysdb_new_attrs(test_ctx); + if (ret != EOK) { + fail("Could not create attribute list"); + return; + } + + ret = sysdb_attrs_add_string(data->attrs, + TEST_ATTR_NAME, + TEST_ATTR_UPDATE_VALUE); + if (ret != EOK) { + fail("Could not add attribute"); + return; + } + + ret = sysdb_attrs_add_string(data->attrs, + TEST_ATTR_ADD_NAME, + TEST_ATTR_ADD_VALUE); + if (ret != EOK) { + fail("Could not add attribute"); + return; + } + + subreq = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_store_custom, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not add custom object"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_search_custom_update) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + int ret; + char *object_name; + struct ldb_message_element *el; + + /* 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); + fail_unless(data != NULL, "talloc_zero failed"); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->attrlist = talloc_array(test_ctx, const char *, 3); + fail_unless(data->attrlist != NULL, "talloc_array failed"); + data->attrlist[0] = TEST_ATTR_NAME; + data->attrlist[1] = TEST_ATTR_ADD_NAME; + data->attrlist[2] = NULL; + + object_name = talloc_asprintf(data, "%s_%d", CUSTOM_TEST_OBJECT, 29010); + fail_unless(object_name != NULL, "talloc_asprintf failed"); + + subreq = sysdb_search_custom_by_name_send(data, data->ev, + data->ctx->sysdb, NULL, + data->ctx->domain, + object_name, + CUSTOM_TEST_CONTAINER, + data->attrlist); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_search_done, data); + + ret = test_loop(data); + + ret = sysdb_search_custom_recv(subreq, data, &data->msgs_count, + &data->msgs); + talloc_zfree(subreq); + fail_unless(ret == EOK, "sysdb_search_custom_by_name_send failed"); + + fail_unless(data->msgs_count == 1, + "Wrong number of objects, exptected [1] got [%d]", + data->msgs_count); + fail_unless(data->msgs[0]->num_elements == 2, + "Wrong number of results, expected [2] got [%d]", + data->msgs[0]->num_elements); + + el = ldb_msg_find_element(data->msgs[0], TEST_ATTR_NAME); + fail_unless(el != NULL, "Attribute [%s] not found", TEST_ATTR_NAME); + fail_unless(el->num_values == 1, "Wrong number ([%d] instead of 1) " + "of attribute values for [%s]", el->num_values, TEST_ATTR_NAME); + fail_unless(strncmp((const char *) el->values[0].data, TEST_ATTR_UPDATE_VALUE, + el->values[0].length) == 0, + "Wrong attribute value"); + + el = ldb_msg_find_element(data->msgs[0], TEST_ATTR_ADD_NAME); + fail_unless(el != NULL, "Attribute [%s] not found", TEST_ATTR_ADD_NAME); + fail_unless(el->num_values == 1, "Wrong number ([%d] instead of 1) " + "of attribute values for [%s]", el->num_values, TEST_ATTR_ADD_NAME); + fail_unless(strncmp((const char *) el->values[0].data, TEST_ATTR_ADD_VALUE, + el->values[0].length) == 0, + "Wrong attribute value"); + + } + + fail_if(ret != EOK, "Could not search custom object"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_search_custom) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + int ret; + const char *filter = "(distinguishedName=*)"; + + /* 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); + fail_unless(data != NULL, "talloc_zero failed"); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->attrlist = talloc_array(test_ctx, const char *, 3); + fail_unless(data->attrlist != NULL, "talloc_array failed"); + data->attrlist[0] = TEST_ATTR_NAME; + data->attrlist[1] = TEST_ATTR_ADD_NAME; + data->attrlist[2] = NULL; + + subreq = sysdb_search_custom_send(data, data->ev, + data->ctx->sysdb, NULL, + data->ctx->domain, + filter, + CUSTOM_TEST_CONTAINER, + data->attrlist); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_search_done, data); + + ret = test_loop(data); + + ret = sysdb_search_custom_recv(subreq, data, &data->msgs_count, + &data->msgs); + talloc_zfree(subreq); + fail_unless(ret == EOK, "sysdb_search_custom_send failed"); + + fail_unless(data->msgs_count == 10, + "Wrong number of objects, exptected [10] got [%d]", + data->msgs_count); + } + + fail_if(ret != EOK, "Could not search custom object"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_delete_custom) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + 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; + + subreq = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_delete_custom, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not delete custom object"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_cache_password) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *req; + int ret; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + fail_unless(ret == EOK, "Could not set up the test"); + + data = talloc_zero(test_ctx, struct test_data); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->username = talloc_asprintf(data, "testuser%d", _i); + + req = sysdb_cache_password_send(data, test_ctx->ev, test_ctx->sysdb, NULL, + test_ctx->domain, data->username, + data->username); + fail_unless(req != NULL, "sysdb_cache_password_send failed [%d].", ret); + + tevent_req_set_callback(req, test_search_done, data); + + ret = test_loop(data); + fail_unless(ret == EOK, "test_loop failed [%d].", ret); + + ret = sysdb_cache_password_recv(req); + fail_unless(ret == EOK, "sysdb_cache_password request failed [%d].", ret); + + talloc_free(test_ctx); +} +END_TEST + +static void cached_authentication_without_expiration(const char *username, + const char *password, + int expected_result) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *req; + int ret; + time_t expire_date; + time_t delayed_until; + const char *val[2]; + val[1] = NULL; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + fail_unless(ret == EOK, "Could not set up the test"); + + data = talloc_zero(test_ctx, struct test_data); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->username = username; + + val[0] = "0"; + ret = confdb_add_param(test_ctx->confdb, true, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_CRED_TIMEOUT, val); + if (ret != EOK) { + fail("Could not initialize provider"); + talloc_free(test_ctx); + return; + } + + req = sysdb_cache_auth_send(data, test_ctx->ev, test_ctx->sysdb, + test_ctx->domain, data->username, + (const uint8_t *) password, strlen(password), + test_ctx->confdb); + fail_unless(req != NULL, "sysdb_cache_password_send failed."); + + tevent_req_set_callback(req, test_search_done, data); + + ret = test_loop(data); + fail_unless(ret == EOK, "test_loop failed."); + + ret = sysdb_cache_auth_recv(req, &expire_date, &delayed_until); + fail_unless(ret == expected_result, "sysdb_cache_auth request does not " + "return expected result [%d].", + expected_result); + + fail_unless(expire_date == 0, "Wrong expire date, expected [%d], got [%d]", + 0, expire_date); + + fail_unless(delayed_until == -1, "Wrong delay, expected [%d], got [%d]", + -1, delayed_until); + + talloc_free(test_ctx); +} + +static void cached_authentication_with_expiration(const char *username, + const char *password, + int expected_result) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *req; + int ret; + time_t expire_date; + const char *val[2]; + val[1] = NULL; + time_t now; + time_t expected_expire_date; + time_t delayed_until; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + fail_unless(ret == EOK, "Could not set up the test"); + + data = talloc_zero(test_ctx, struct test_data); + data->ctx = test_ctx; + data->ev = test_ctx->ev; + data->username = username; + + val[0] = "1"; + ret = confdb_add_param(test_ctx->confdb, true, CONFDB_PAM_CONF_ENTRY, + CONFDB_PAM_CRED_TIMEOUT, val); + if (ret != EOK) { + fail("Could not initialize provider"); + talloc_free(test_ctx); + return; + } + + now = time(NULL); + expected_expire_date = now + (24 * 60 * 60); + DEBUG(9, ("Setting SYSDB_LAST_ONLINE_AUTH to [%lld].\n", (long long) now)); + + data->attrs = sysdb_new_attrs(data); + ret = sysdb_attrs_add_time_t(data->attrs, SYSDB_LAST_ONLINE_AUTH, now); + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + fail_unless(req != NULL, "sysdb_transaction_send failed."); + + tevent_req_set_callback(req, test_set_user_attr, data); + + ret = test_loop(data); + fail_unless(ret == EOK, "Could not modify user %s", data->username); + talloc_zfree(req); + + data->finished = false; + req = sysdb_cache_auth_send(data, test_ctx->ev, test_ctx->sysdb, + test_ctx->domain, data->username, + (const uint8_t *) password, strlen(password), + test_ctx->confdb); + fail_unless(req != NULL, "sysdb_cache_password_send failed."); + + tevent_req_set_callback(req, test_search_done, data); + + ret = test_loop(data); + fail_unless(ret == EOK, "test_loop failed."); + + ret = sysdb_cache_auth_recv(req, &expire_date, &delayed_until); + fail_unless(ret == expected_result, "sysdb_cache_auth request does not " + "return expected result [%d], got [%d].", + expected_result, ret); + + fail_unless(expire_date == expected_expire_date, + "Wrong expire date, expected [%d], got [%d]", + expected_expire_date, expire_date); + + fail_unless(delayed_until == -1, "Wrong delay, expected [%d], got [%d]", + -1, delayed_until); + + talloc_free(test_ctx); +} + +START_TEST (test_sysdb_cached_authentication_missing_password) +{ + TALLOC_CTX *tmp_ctx; + char *username; + + tmp_ctx = talloc_new(NULL); + fail_unless(tmp_ctx != NULL, "talloc_new failed."); + + username = talloc_asprintf(tmp_ctx, "testuser%d", _i); + fail_unless(username != NULL, "talloc_asprintf failed."); + + cached_authentication_without_expiration(username, "abc", ENOENT); + cached_authentication_with_expiration(username, "abc", ENOENT); + + talloc_free(tmp_ctx); + +} +END_TEST + +START_TEST (test_sysdb_cached_authentication_wrong_password) +{ + TALLOC_CTX *tmp_ctx; + char *username; + + tmp_ctx = talloc_new(NULL); + fail_unless(tmp_ctx != NULL, "talloc_new failed."); + + username = talloc_asprintf(tmp_ctx, "testuser%d", _i); + fail_unless(username != NULL, "talloc_asprintf failed."); + + cached_authentication_without_expiration(username, "abc", EINVAL); + cached_authentication_with_expiration(username, "abc", EINVAL); + + talloc_free(tmp_ctx); + +} +END_TEST + +START_TEST (test_sysdb_cached_authentication) +{ + TALLOC_CTX *tmp_ctx; + char *username; + + tmp_ctx = talloc_new(NULL); + fail_unless(tmp_ctx != NULL, "talloc_new failed."); + + username = talloc_asprintf(tmp_ctx, "testuser%d", _i); + fail_unless(username != NULL, "talloc_asprintf failed."); + + cached_authentication_without_expiration(username, username, EOK); + cached_authentication_with_expiration(username, username, EOK); + + talloc_free(tmp_ctx); + +} +END_TEST + +START_TEST (test_sysdb_prepare_asq_test_user) +{ + 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; + data->groupname = talloc_asprintf(data, "testgroup%d", _i); + data->uid = ASQ_TEST_USER_UID; + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_add_group_member, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not modify group %s", data->groupname); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_asq_search) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *req; + struct ldb_dn *user_dn; + int ret; + size_t msgs_count; + struct ldb_message **msgs; + int i; + char *gid_str; + + /* 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->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed"); + + data->attrlist[0] = "gidNumber"; + data->attrlist[1] = NULL; + + user_dn = sysdb_user_dn(data->ctx->sysdb, data, "LOCAL", ASQ_TEST_USER); + fail_unless(user_dn != NULL, "sysdb_user_dn failed"); + + req = sysdb_asq_search_send(data, data->ev, test_ctx->sysdb, NULL, + test_ctx->domain, user_dn, NULL, "memberof", + data->attrlist); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_search_done, data); + + ret = test_loop(data); + + ret = sysdb_asq_search_recv(req, data, &msgs_count, &msgs); + talloc_zfree(req); + fail_unless(ret == EOK, "sysdb_asq_search_send failed"); + + fail_unless(msgs_count == 10, "wrong number of results, " + "found [%d] expected [10]", msgs_count); + + for (i = 0; i < msgs_count; i++) { + fail_unless(msgs[i]->num_elements == 1, "wrong number of elements, " + "found [%d] expected [1]", + msgs[i]->num_elements); + + fail_unless(msgs[i]->elements[0].num_values == 1, + "wrong number of values, found [%d] expected [1]", + msgs[i]->elements[0].num_values); + + gid_str = talloc_asprintf(data, "%d", 28010 + i); + fail_unless(gid_str != NULL, "talloc_asprintf failed."); + fail_unless(strncmp(gid_str, + (const char *) msgs[i]->elements[0].values[0].data, + msgs[i]->elements[0].values[0].length) == 0, + "wrong value, found [%.*s] expected [%s]", + msgs[i]->elements[0].values[0].length, + msgs[i]->elements[0].values[0].data, gid_str); + } + } + + fail_if(ret != EOK, "Failed to send ASQ search request.\n"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_search_all_users) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *req; + int ret; + int i; + char *uid_str; + + /* 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->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed"); + + data->attrlist[0] = "uidNumber"; + data->attrlist[1] = NULL; + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_search_all_users, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Search failed"); + + fail_unless(data->msgs_count == 10, + "wrong number of results, found [%d] expected [10]", + data->msgs_count); + + for (i = 0; i < data->msgs_count; i++) { + fail_unless(data->msgs[i]->num_elements == 1, + "wrong number of elements, found [%d] expected [1]", + data->msgs[i]->num_elements); + + fail_unless(data->msgs[i]->elements[0].num_values == 1, + "wrong number of values, found [%d] expected [1]", + data->msgs[i]->elements[0].num_values); + + uid_str = talloc_asprintf(data, "%d", 27010 + i); + fail_unless(uid_str != NULL, "talloc_asprintf failed."); + fail_unless(strncmp(uid_str, + (char *) data->msgs[i]->elements[0].values[0].data, + data->msgs[i]->elements[0].values[0].length) == 0, + "wrong value, found [%.*s] expected [%s]", + data->msgs[i]->elements[0].values[0].length, + data->msgs[i]->elements[0].values[0].data, uid_str); + } + + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_delete_recursive) +{ + struct sysdb_test_ctx *test_ctx; + struct test_data *data; + struct tevent_req *subreq; + 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; + + subreq = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!subreq) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(subreq, test_delete_recursive, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Recursive delete failed"); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_attrs_replace_name) +{ + struct sysdb_attrs *attrs; + struct ldb_message_element *el; + int ret; + + attrs = sysdb_new_attrs(NULL); + fail_unless(attrs != NULL, "sysdb_new_attrs failed"); + + ret = sysdb_attrs_add_string(attrs, "foo", "bar"); + fail_unless(ret == EOK, "sysdb_attrs_add_string failed"); + + ret = sysdb_attrs_add_string(attrs, "fool", "bool"); + fail_unless(ret == EOK, "sysdb_attrs_add_string failed"); + + ret = sysdb_attrs_add_string(attrs, "foot", "boot"); + fail_unless(ret == EOK, "sysdb_attrs_add_string failed"); + + ret = sysdb_attrs_replace_name(attrs, "foo", "foot"); + fail_unless(ret == EEXIST, + "sysdb_attrs_replace overwrites existing attribute"); + + ret = sysdb_attrs_replace_name(attrs, "foo", "oof"); + fail_unless(ret == EOK, "sysdb_attrs_replace failed"); + + ret = sysdb_attrs_get_el(attrs, "foo", &el); + fail_unless(ret == EOK, "sysdb_attrs_get_el failed"); + fail_unless(el->num_values == 0, "Attribute foo is not empty."); + + ret = sysdb_attrs_get_el(attrs, "oof", &el); + fail_unless(ret == EOK, "sysdb_attrs_get_el failed"); + fail_unless(el->num_values == 1, + "Wrong number of values for attribute oof, " + "expected [1] got [%d].", el->num_values); + fail_unless(strncmp("bar", (char *) el->values[0].data, + el->values[0].length) == 0, + "Wrong value, expected [bar] got [%.*s]", el->values[0].length, + el->values[0].data); + + talloc_free(attrs); +} +END_TEST + +START_TEST (test_sysdb_memberof_store_group) +{ + 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; + data->gid = MBO_GROUP_BASE + _i; + data->groupname = talloc_asprintf(data, "testgroup%d", data->gid); + + if (_i == 0) { + data->attrlist = NULL; + } else { + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed."); + data->attrlist[0] = talloc_asprintf(data, "testgroup%d", data->gid - 1); + data->attrlist[1] = NULL; + } + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_memberof_store_group, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not store POSIX group #%d", data->gid); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_memberof_close_loop) +{ + 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; + data->gid = MBO_GROUP_BASE; + data->groupname = talloc_asprintf(data, "testgroup%d", data->gid); + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "talloc_array failed."); + data->attrlist[0] = talloc_asprintf(data, "testgroup%d", data->gid + 9); + data->attrlist[1] = NULL; + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_memberof_store_group, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not store POSIX group #%d", data->gid); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_memberof_store_user) +{ + 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; + data->uid = MBO_USER_BASE + _i; + data->gid = 0; /* MPG domain */ + data->username = talloc_asprintf(data, "testuser%d", data->uid); + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_store_user, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not store user %s", data->username); + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_memberof_add_group_member) +{ + 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; + data->groupname = talloc_asprintf(data, "testgroup%d", _i + MBO_GROUP_BASE); + data->uid = MBO_USER_BASE + _i; + + req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_add_group_member, data); + + ret = test_loop(data); + } + + fail_if(ret != EOK, "Could not modify group %s", data->groupname); + + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_memberof_check_memberuid_without_group_5) +{ + 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; + data->gid = _i + MBO_GROUP_BASE; + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "tallo_array failed."); + data->attrlist[0] = "memberuid"; + data->attrlist[1] = NULL; + + req = sysdb_search_group_by_gid_send(data, data->ev, test_ctx->sysdb, NULL, + data->ctx->domain, + _i + MBO_GROUP_BASE, + data->attrlist); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_search_done, data); + + ret = test_loop(data); + + ret = sysdb_search_group_recv(req, data, &data->msg); + talloc_zfree(req); + if (_i == 5) { + fail_unless(ret == ENOENT, + "sysdb_search_group_by_gid_send found " + "already deleted group"); + ret = EOK; + } else { + fail_unless(ret == EOK, "sysdb_search_group_by_gid_send failed"); + + fail_unless(data->msg->num_elements == 1, + "Wrong number of results, expected [1] got [%d]", + data->msg->num_elements); + fail_unless(strcmp(data->msg->elements[0].name, "memberuid") == 0, + "Wrong attribute name"); + fail_unless(data->msg->elements[0].num_values == ((_i + 1) % 6), + "Wrong number of attribute values, " + "expected [%d] got [%d]", ((_i + 1) % 6), + data->msg->elements[0].num_values); + } + } + + fail_if(ret != EOK, "Could not check group %d", data->gid); + + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_memberof_check_memberuid) +{ + 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; + data->gid = _i + MBO_GROUP_BASE; + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "tallo_array failed."); + data->attrlist[0] = "memberuid"; + data->attrlist[1] = NULL; + + req = sysdb_search_group_by_gid_send(data, data->ev, test_ctx->sysdb, NULL, + data->ctx->domain, + _i + MBO_GROUP_BASE, + data->attrlist); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_search_done, data); + + ret = test_loop(data); + + ret = sysdb_search_group_recv(req, data, &data->msg); + talloc_zfree(req); + fail_unless(ret == EOK, "sysdb_search_group_by_gid_send failed"); + + fail_unless(data->msg->num_elements == 1, + "Wrong number of results, expected [1] got [%d]", + data->msg->num_elements); + fail_unless(strcmp(data->msg->elements[0].name, "memberuid") == 0, + "Wrong attribute name"); + fail_unless(data->msg->elements[0].num_values == _i + 1, + "Wrong number of attribute values, expected [%d] got [%d]", + _i + 1, data->msg->elements[0].num_values); + } + + fail_if(ret != EOK, "Could not check group %d", data->gid); + + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_memberof_check_memberuid_loop) +{ + 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; + data->gid = _i + MBO_GROUP_BASE; + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "tallo_array failed."); + data->attrlist[0] = "memberuid"; + data->attrlist[1] = NULL; + + req = sysdb_search_group_by_gid_send(data, data->ev, test_ctx->sysdb, NULL, + data->ctx->domain, + _i + MBO_GROUP_BASE, + data->attrlist); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_search_done, data); + + ret = test_loop(data); + + ret = sysdb_search_group_recv(req, data, &data->msg); + talloc_zfree(req); + fail_unless(ret == EOK, "sysdb_search_group_by_gid_send failed"); + + fail_unless(data->msg->num_elements == 1, + "Wrong number of results, expected [1] got [%d]", + data->msg->num_elements); + fail_unless(strcmp(data->msg->elements[0].name, "memberuid") == 0, + "Wrong attribute name"); + fail_unless(data->msg->elements[0].num_values == 10, + "Wrong number of attribute values, expected [%d] got [%d]", + 10, data->msg->elements[0].num_values); + } + + fail_if(ret != EOK, "Could not check group %d", data->gid); + + talloc_free(test_ctx); +} +END_TEST + +START_TEST (test_sysdb_memberof_check_memberuid_loop_without_group_5) +{ + 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; + data->gid = _i + MBO_GROUP_BASE; + + data->attrlist = talloc_array(data, const char *, 2); + fail_unless(data->attrlist != NULL, "tallo_array failed."); + data->attrlist[0] = "memberuid"; + data->attrlist[1] = NULL; + + req = sysdb_search_group_by_gid_send(data, data->ev, test_ctx->sysdb, NULL, + data->ctx->domain, + _i + MBO_GROUP_BASE, + data->attrlist); + if (!req) { + ret = ENOMEM; + } + + if (ret == EOK) { + tevent_req_set_callback(req, test_search_done, data); + + ret = test_loop(data); + + ret = sysdb_search_group_recv(req, data, &data->msg); + talloc_zfree(req); + if (_i == 5) { + fail_unless(ret == ENOENT, + "sysdb_search_group_by_gid_send found " + "already deleted group"); + ret = EOK; + } else { + fail_unless(ret == EOK, "sysdb_search_group_by_gid_send failed"); + + fail_unless(data->msg->num_elements == 1, + "Wrong number of results, expected [1] got [%d]", + data->msg->num_elements); + fail_unless(strcmp(data->msg->elements[0].name, "memberuid") == 0, + "Wrong attribute name"); + fail_unless(data->msg->elements[0].num_values == ((_i + 5) % 10), + "Wrong number of attribute values, expected [%d] got [%d]", + ((_i + 5) % 10), data->msg->elements[0].num_values); + } + } + + fail_if(ret != EOK, "Could not check group %d", data->gid); + + talloc_free(test_ctx); +} +END_TEST + +Suite *create_sysdb_suite(void) +{ + Suite *s = suite_create("sysdb"); + + TCase *tc_sysdb = tcase_create("SYSDB Tests"); + + /* Create a new user */ + tcase_add_loop_test(tc_sysdb, test_sysdb_add_user,27000,27010); + + /* Verify the users were added */ + tcase_add_loop_test(tc_sysdb, test_sysdb_getpwnam, 27000, 27010); + + /* Create a new group */ + tcase_add_loop_test(tc_sysdb, test_sysdb_add_group, 28000, 28010); + + /* Verify the groups were added */ + tcase_add_loop_test(tc_sysdb, test_sysdb_getgrnam, 28000, 28010); + + /* sysdb_store_user allows setting attributes for existing users */ + tcase_add_loop_test(tc_sysdb, test_sysdb_store_user_existing, 27000, 27010); + + /* test the change */ + tcase_add_loop_test(tc_sysdb, test_sysdb_get_user_attr, 27000, 27010); + + /* Remove the other half by gid */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group_by_gid, 28000, 28010); + + /* Remove the other half by uid */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_user_by_uid, 27000, 27010); + + /* Create a new user */ + tcase_add_loop_test(tc_sysdb, test_sysdb_store_user, 27010, 27020); + + /* Verify the users were added */ + tcase_add_loop_test(tc_sysdb, test_sysdb_getpwnam, 27010, 27020); + + /* Verify the users can be queried by UID */ + tcase_add_loop_test(tc_sysdb, test_sysdb_getpwuid, 27010, 27020); + + /* Enumerate the users */ + tcase_add_test(tc_sysdb, test_sysdb_enumpwent); + + /* Change their attribute */ + tcase_add_loop_test(tc_sysdb, test_sysdb_set_user_attr, 27010, 27020); + + /* Verify the change */ + tcase_add_loop_test(tc_sysdb, test_sysdb_get_user_attr, 27010, 27020); + + /* Create a new group */ + tcase_add_loop_test(tc_sysdb, test_sysdb_store_group, 28010, 28020); + + /* Verify the groups were added */ + + /* Verify the groups can be queried by GID */ + tcase_add_loop_test(tc_sysdb, test_sysdb_getgrgid, 28010, 28020); + + /* Enumerate the groups */ + tcase_add_test(tc_sysdb, test_sysdb_enumgrent); + + /* Add some members to the groups */ + tcase_add_loop_test(tc_sysdb, test_sysdb_add_group_member, 28010, 28020); + + /* Authenticate with missing cached password */ + tcase_add_loop_test(tc_sysdb, test_sysdb_cached_authentication_missing_password, + 27010, 27011); + + /* Add a cached password */ + tcase_add_loop_test(tc_sysdb, test_sysdb_cache_password, 27010, 27011); + + /* Authenticate against cached password */ + tcase_add_loop_test(tc_sysdb, test_sysdb_cached_authentication_wrong_password, + 27010, 27011); + tcase_add_loop_test(tc_sysdb, test_sysdb_cached_authentication, 27010, 27011); + + /* ASQ search test */ + tcase_add_loop_test(tc_sysdb, test_sysdb_prepare_asq_test_user, 28011, 28020); + tcase_add_test(tc_sysdb, test_sysdb_asq_search); + + /* Test search with more than one result */ + tcase_add_test(tc_sysdb, test_sysdb_search_all_users); + + /* Remove the members from the groups */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_group_member, 28010, 28020); + + /* Remove the users by name */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_user, 27010, 27020); + + /* Remove the groups by name */ + tcase_add_loop_test(tc_sysdb, test_sysdb_remove_local_group, 28010, 28020); + + /* test the ignore_not_found parameter for users */ + tcase_add_test(tc_sysdb, test_sysdb_remove_nonexistent_user); + + /* test the ignore_not_found parameter for groups */ + tcase_add_test(tc_sysdb, test_sysdb_remove_nonexistent_group); + + /* test custom operations */ + tcase_add_loop_test(tc_sysdb, test_sysdb_store_custom, 29010, 29020); + tcase_add_test(tc_sysdb, test_sysdb_search_custom_by_name); + tcase_add_test(tc_sysdb, test_sysdb_update_custom); + tcase_add_test(tc_sysdb, test_sysdb_search_custom_update); + tcase_add_test(tc_sysdb, test_sysdb_search_custom); + tcase_add_test(tc_sysdb, test_sysdb_delete_custom); + + /* test recursive delete */ + tcase_add_test(tc_sysdb, test_sysdb_delete_recursive); + + tcase_add_test(tc_sysdb, test_sysdb_attrs_replace_name); + +/* Add all test cases to the test suite */ + suite_add_tcase(s, tc_sysdb); + + TCase *tc_memberof = tcase_create("SYSDB member/memberof/memberuid Tests"); + + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_store_group, 0, 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_store_user, 0, 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_add_group_member, + 0, 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_check_memberuid, + 0, 10); + tcase_add_loop_test(tc_memberof, test_sysdb_remove_local_group_by_gid, + MBO_GROUP_BASE + 5, MBO_GROUP_BASE + 6); + tcase_add_loop_test(tc_memberof, + test_sysdb_memberof_check_memberuid_without_group_5, + 0, 10); + tcase_add_loop_test(tc_memberof, test_sysdb_remove_local_group_by_gid, + MBO_GROUP_BASE , MBO_GROUP_BASE + 10); + + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_store_group, 0, 10); + tcase_add_test(tc_memberof, test_sysdb_memberof_close_loop); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_store_user, 0, 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_add_group_member, + 0, 10); + tcase_add_loop_test(tc_memberof, test_sysdb_memberof_check_memberuid_loop, + 0, 10); + tcase_add_loop_test(tc_memberof, test_sysdb_remove_local_group_by_gid, + MBO_GROUP_BASE + 5, MBO_GROUP_BASE + 6); + tcase_add_loop_test(tc_memberof, + test_sysdb_memberof_check_memberuid_loop_without_group_5, + 0, 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); + + return s; +} + +int main(int argc, const char *argv[]) { + int opt; + int ret; + poptContext pc; + int failure_count; + Suite *sysdb_suite; + SRunner *sr; + + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_MAIN_OPTS + { NULL } + }; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + ret = unlink(TESTS_PATH"/"LOCAL_SYSDB_FILE); + if (ret != EOK && errno != ENOENT) { + fprintf(stderr, "Could not delete the test ldb file (%d) (%s)\n", + errno, strerror(errno)); + return EXIT_FAILURE; + } + + sysdb_suite = create_sysdb_suite(); + sr = srunner_create(sysdb_suite); + /* If CK_VERBOSITY is set, use that, otherwise it defaults to CK_NORMAL */ + srunner_run_all(sr, CK_ENV); + failure_count = srunner_ntests_failed(sr); + srunner_free(sr); + if (failure_count == 0) { + ret = unlink(TESTS_PATH"/"TEST_CONF_FILE); + if (ret != EOK) { + fprintf(stderr, "Could not delete the test config ldb file (%d) (%s)\n", + errno, strerror(errno)); + return EXIT_FAILURE; + } + ret = unlink(TESTS_PATH"/"LOCAL_SYSDB_FILE); + if (ret != EOK) { + fprintf(stderr, "Could not delete the test config ldb file (%d) (%s)\n", + errno, strerror(errno)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} |