From 4f49c7fb0086674ccfbc5eed7bc2ee5fc7838c6b Mon Sep 17 00:00:00 2001
From: Martin Nagy <mnagy@redhat.com>
Date: Tue, 16 Mar 2010 18:25:32 +0100
Subject: [PATCH 2/2] Add a test for LDAP bind

---
 src/Makefile.am              |   18 ++-
 src/tests/async_ldap-tests.c |  378 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 394 insertions(+), 2 deletions(-)
 create mode 100644 src/tests/async_ldap-tests.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 6d46cda..f773798 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -76,7 +76,8 @@ if HAVE_CHECK
         find_uid-tests \
         auth-tests \
         ipa_ldap_opt-tests \
-        simple_access-tests
+        simple_access-tests \
+        async_ldap-tests
 endif
 
 check_PROGRAMS = \
@@ -577,7 +578,6 @@ refcount_tests_LDADD = \
 fail_over_tests_SOURCES = \
     tests/fail_over-tests.c \
     $(SSSD_FAILOVER_OBJ) \
-    $(CHECK_OBJ) \
     $(SSSD_UTIL_OBJ)
 fail_over_tests_CFLAGS = \
     $(CHECK_CFLAGS)
@@ -650,6 +650,20 @@ simple_access_tests_LDADD = \
     $(SSSD_LIBS) \
     $(CHECK_LIBS)
 
+async_ldap_tests_SOURCES = \
+    tests/async_ldap-tests.c \
+    tests/common_check.c \
+    providers/data_provider_fo.c \
+    providers/data_provider_opts.c \
+    $(SSSD_UTIL_OBJ) \
+    $(SSSD_FAILOVER_OBJ)
+async_ldap_tests_CFLAGS = \
+    $(CHECK_CFLAGS)
+async_ldap_tests_LDADD = \
+    $(SSSD_LIBS) \
+    $(CHECK_LIBS) \
+    $(CARES_LIBS) \
+    libsss_ldap.la
 endif
 
 stress_tests_SOURCES = \
diff --git a/src/tests/async_ldap-tests.c b/src/tests/async_ldap-tests.c
new file mode 100644
index 0000000..adc6cb6
--- /dev/null
+++ b/src/tests/async_ldap-tests.c
@@ -0,0 +1,378 @@
+/*
+   SSSD
+
+   Test asynchronous LDAP connection.
+
+   Authors:
+        Martin Nagy <mnagy@redhat.com>
+
+   Copyright (C) Red Hat, Inc 2010
+
+   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 <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "providers/dp_backend.h"
+#include "providers/ldap/ldap_common.h"
+#include "resolv/async_resolv.h"
+#include "tests/common.h"
+#include "util/util.h"
+
+/* Interface under test */
+#include "providers/ldap/sdap_async.h"
+
+#define PING_USEC 10000
+
+#define check(fun) \
+    ({ \
+        ret = (fun); \
+        if (ret != EOK) { fail(); goto done; } \
+        ret; \
+     })
+
+#define check_null(fun) \
+    ({ \
+        void *ret = (void *)(fun); \
+        if (ret == NULL) { fail(); goto done; } \
+        ret; \
+     })
+
+
+/* Settings from the command line. */
+static int use_net_test;
+static int print_dots;
+const char **addresses;
+
+/* Used by ping() */
+static int printed_dot;
+
+struct test_ctx {
+    struct be_ctx *be;
+    struct tevent_timer *ping_timer;
+    int tasks;
+};
+
+struct connect_state {
+    const char *address;
+    struct test_ctx *ctx;
+    struct sdap_handle *sh;
+};
+
+/************* FIXME: These are from data_provider_be.c *************/
+bool be_is_offline(struct be_ctx *ctx)
+{
+    time_t now = time(NULL);
+
+    if (ctx->offstat.went_offline + 60 < now) {
+        ctx->offstat.offline = false;
+    }
+
+    return ctx->offstat.offline;
+}
+
+void be_mark_offline(struct be_ctx *ctx)
+{
+    DEBUG(8, ("Going offline!\n"));
+
+    ctx->offstat.went_offline = time(NULL);
+    ctx->offstat.offline = true;
+}
+/********************************************************************/
+
+
+static int
+init_be_ctx(void *mem_ctx, struct be_ctx **_ctx)
+{
+    struct be_ctx *ctx;
+    int ret = ENOMEM;
+
+    ctx = check_null(talloc_zero(mem_ctx, struct be_ctx));
+    ctx->ev = check_null(tevent_context_init(ctx));
+    check(confdb_init(ctx, &ctx->cdb, DB_PATH "/" CONFDB_FILE));
+    ctx->conf_path = talloc_asprintf(ctx, CONFDB_DOMAIN_PATH_TMPL, "LDAP");
+    check_null(ctx->conf_path);
+
+    check(be_init_failover(ctx));
+
+    *_ctx = ctx;
+    return EOK;
+
+done:
+    talloc_free(ctx);
+    return ret;
+}
+
+static void ping(struct tevent_context *ev, struct tevent_timer *te,
+                 struct timeval tv, void *private_data);
+
+static struct test_ctx *
+setup_test(void)
+{
+    struct test_ctx *ctx = NULL;
+    struct timeval now;
+
+    ctx = check_null(talloc_zero(global_talloc_context, struct test_ctx));
+    ctx->tasks = 0;
+
+    /* Initialize be */
+    if (init_be_ctx(ctx, &ctx->be) != EOK) {
+        fail("couldn't initialize backend context");
+        goto done;
+    }
+
+    /* Register a periodic timer that prints dots. */
+    if (print_dots) {
+        now = tevent_timeval_current_ofs(0, PING_USEC);
+        ctx->ping_timer = tevent_add_timer(ctx->be->ev, ctx, now, ping, ctx);
+        check_null(ctx->ping_timer);
+    }
+
+    return ctx;
+
+done:
+    talloc_free(ctx);
+    return NULL;
+}
+
+static void
+ping(struct tevent_context *ev, struct tevent_timer *te,
+     struct timeval tv, void *private_data)
+{
+    struct test_ctx *ctx = talloc_get_type(private_data, struct test_ctx);
+
+    fprintf(stderr, ".");
+    fflush(stderr);
+    printed_dot = 1;
+
+/*
+    FIXME: tevent_timeval_current() used by
+    tevent_timeval_current_ofs() is broken.
+    tv = tevent_timeval_current_ofs(0, PING_USEC);
+*/
+    gettimeofday(&tv, NULL);
+    tv.tv_usec += PING_USEC;
+    tv.tv_sec += tv.tv_usec / 1000000;
+    tv.tv_usec = tv.tv_usec % 1000000;
+    ctx->ping_timer = tevent_add_timer(ev, ctx, tv, ping, ctx);
+}
+
+static void
+test_loop(struct test_ctx *data)
+{
+    while (data->tasks != 0) {
+        printed_dot = 0;
+        tevent_loop_once(data->be->ev);
+        if (print_dots && !printed_dot) {
+            fprintf(stderr, "*");
+        }
+    }
+}
+
+static void connect_done(struct tevent_req *req);
+
+START_TEST(test_ldap_bind)
+{
+    struct test_ctx *ctx;
+    struct tevent_req *req;
+    struct sdap_options *opts;
+    struct connect_state *cs;
+    int ret;
+    int i;
+
+    if (addresses == NULL) {
+        return;
+    }
+
+    ctx = setup_test();
+    if (ctx == NULL) {
+        return;
+    }
+
+    check(ldap_get_options(ctx, ctx->be->cdb, ctx->be->conf_path, &opts));
+
+    for (i = 0; addresses[i]; i++) {
+        cs = check_null(talloc(ctx, struct connect_state));
+        cs->address = addresses[i];
+        cs->ctx = ctx;
+
+        req = sdap_connect_send(ctx, ctx->be->ev, opts, addresses[i], false);
+        check_null(req);
+        tevent_req_set_callback(req, connect_done, cs);
+        ctx->tasks++;
+    }
+
+    test_loop(ctx);
+
+done:
+    talloc_free(ctx);
+}
+END_TEST
+
+static void auth_done(struct tevent_req *req);
+
+/* FIXME: Add test with start_tls enabled. */
+static void
+connect_done(struct tevent_req *req)
+{
+    struct connect_state *cs;
+    struct tevent_req *subreq;
+    struct dp_opt_blob authok;
+
+    cs = tevent_req_callback_data(req, struct connect_state);
+    cs->ctx->tasks--;
+
+    if (sdap_connect_recv(req, cs, &cs->sh) != EOK) {
+        fail("Failed to connect to %s", cs->address);
+        goto done;
+    }
+    talloc_free(req);
+
+    DEBUG(1, ("Successfully initialized %s\n", cs->address));
+
+    authok.data = NULL;
+    authok.length = 0;
+    subreq = check_null(sdap_auth_send(cs, cs->ctx->be->ev, cs->sh,
+                                       NULL, NULL, NULL, NULL, authok));
+    tevent_req_set_callback(subreq, auth_done, cs);
+    cs->ctx->tasks++;
+
+    return;
+
+done:
+    talloc_free(cs);
+}
+
+static void
+auth_done(struct tevent_req *req)
+{
+    struct connect_state *cs;
+    enum sdap_result result;
+    const char *result_str;
+
+    cs = tevent_req_callback_data(req, struct connect_state);
+    cs->ctx->tasks--;
+
+    if (sdap_auth_recv(req, &result) != EOK) {
+        talloc_free(req);
+        DEBUG(1, ("sdap_auth_recv() failed to authenticate to %s\n",
+                  cs->address));
+        goto done;
+    }
+    talloc_free(req);
+
+    switch (result) {
+    case SDAP_AUTH_SUCCESS:
+        result_str = "success";
+        break;
+    case SDAP_AUTH_FAILED:
+        result_str = "fail";
+        break;
+    case SDAP_AUTH_PW_EXPIRED:
+        result_str = "password expired";
+        break;
+    case SDAP_ERROR:
+    default:
+        result_str = "unknown";
+        break;
+    }
+
+    DEBUG(1, ("Authentication to %s is done (%s)\n", cs->address, result_str));
+
+    return;
+
+done:
+    talloc_free(cs);
+}
+
+Suite *
+create_suite(void)
+{
+    Suite *s = suite_create("async_ldap");
+
+    TCase *tc = tcase_create("ASYNC_LDAP Tests");
+
+    tcase_add_checked_fixture(tc, leak_check_setup, leak_check_teardown);
+    /* Do some testing */
+    if (use_net_test) {
+        tcase_add_test(tc, test_ldap_bind);
+    }
+    /* 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 },
+        { "print-dots", 'p', POPT_ARG_NONE, 0, 'p',
+            "Output a dot every few milliseconds", 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 'p':
+            print_dots = 1;
+            break;
+
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    debug_level = debug;
+    addresses = poptGetArgs(pc);
+
+    suite = create_suite();
+    sr = srunner_create(suite);
+    srunner_set_fork_status(sr, CK_NOFORK);
+    /* 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);
+
+    poptFreeContext(pc);
+
+    return (failure_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
-- 
1.6.2.5

