Author: brane
Date: Mon Jun 16 18:38:31 2025
New Revision: 1926486

URL: http://svn.apache.org/viewvc?rev=1926486&view=rev
Log:
On the user-defined-authn branch: Implement all user-defined authentication
callbacks to the point that a simple test succeeds.

* serf.h
  (serf_authn_get_realm_func_t): New callback type for user-defined schemes.
  (serf_authn_handle_func_t,
   serf_authn_setup_request_func_t,
   serf_authn_validate_response_func_t): Update argument lists.
  (serf_authn_register_scheme): Add new argument get_realm to the prototype.

* auth/auth.h
  (serf__authn_scheme_t::user_get_realm_func): New struct member.
* auth/auth.c
  (serf_authn_register_scheme): Implement the new get_realm argument.
* auth/auth_user_defined.c
  (authn_baton_wrapper): Renamed from callback_authn_baton.
  (authn_baton_wrapper::pipelining_reset): New struct member.
  (callback_fn_t, validate_handler): Helper for callback argument validation.
  (serf__authn_user__init_conn): Defer to validate_handler and remember
   in the baton if we have to reset the connection's pipelining state.
  (serf__authn_user__handle,
   serf__authn_user__setup_request,
   serf__authn_user__validate_response): Implement these callbacks.

* test/test_auth.c
  (test_authn_register_one,
   test_authn_register_two,
   test_authn_register_twice,
   test_authn_registered_pool_cleanup): Update scheme registration.
  (user_authn_baton::get_realm_count): New struct member.
  (user_authn_get_realm): New test callback.
  (user_authn_handle,
   user_authn_setup_request,
   user_authn_validate_response): Implement these callbacks.
  (user_authn_credentials): Renamed from user_authn_credentials_callback.
   Extract the realm name and log the result.
  (user_authentication): Add two more test mode arguments: one to control
   request pipelining and one to set the scheme flags. Improve the state
   checks and verify that credentials are cached between requests.
  (test_user_authentication,
   test_user_authentication_keepalive_off): Update forwarded arguments.
  (test_user_authentication_tweaked,
   test_user_authentication_pipelining_off): New test cases.
  (test_auth): Register all four user_authentication test cases.

Modified:
    serf/branches/user-defined-authn/auth/auth.c
    serf/branches/user-defined-authn/auth/auth.h
    serf/branches/user-defined-authn/auth/auth_user_defined.c
    serf/branches/user-defined-authn/serf.h
    serf/branches/user-defined-authn/test/test_auth.c

Modified: serf/branches/user-defined-authn/auth/auth.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth.c?rev=1926486&r1=1926485&r2=1926486&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth.c (original)
+++ serf/branches/user-defined-authn/auth/auth.c Mon Jun 16 18:38:31 2025
@@ -662,6 +662,7 @@ static apr_status_t cleanup_user_scheme(
 apr_status_t serf_authn_register_scheme(
     serf_context_t *ctx, const char *name, void *baton, int flags,
     serf_authn_init_conn_func_t init_conn,
+    serf_authn_get_realm_func_t get_realm,
     serf_authn_handle_func_t handle,
     serf_authn_setup_request_func_t setup_request,
     serf_authn_validate_response_func_t validate_response,
@@ -702,6 +703,7 @@ apr_status_t serf_authn_register_scheme(
     authn_scheme->user_flags = flags;
     authn_scheme->user_baton = baton;
     authn_scheme->user_init_conn_func = init_conn;
+    authn_scheme->user_get_realm_func = get_realm;
     authn_scheme->user_handle_func = handle;
     authn_scheme->user_setup_request_func = setup_request;
     authn_scheme->user_validate_response_func = validate_response;

Modified: serf/branches/user-defined-authn/auth/auth.h
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth.h?rev=1926486&r1=1926485&r2=1926486&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth.h (original)
+++ serf/branches/user-defined-authn/auth/auth.h Mon Jun 16 18:38:31 2025
@@ -128,6 +128,7 @@ struct serf__authn_scheme_t {
 
     /* Authentication callbacks. */
     serf_authn_init_conn_func_t user_init_conn_func;
+    serf_authn_get_realm_func_t user_get_realm_func;
     serf_authn_handle_func_t user_handle_func;
     serf_authn_setup_request_func_t user_setup_request_func;
     serf_authn_validate_response_func_t user_validate_response_func;

Modified: serf/branches/user-defined-authn/auth/auth_user_defined.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth_user_defined.c?rev=1926486&r1=1926485&r2=1926486&view=diff
==============================================================================
--- serf/branches/user-defined-authn/auth/auth_user_defined.c (original)
+++ serf/branches/user-defined-authn/auth/auth_user_defined.c Mon Jun 16 
18:38:31 2025
@@ -48,46 +48,66 @@ static serf__authn_info_t *get_authn_inf
 
 
 /* Used for serf__authn_info_t::baton  */
-struct callback_authn_baton {
+struct authn_baton_wrapper {
     /* The connection's pipelining state before we changed it. */
     int pipelining;
 
+    /* Was pipelining reset to its previous value?. */
+    bool pipelining_reset;
+
     /* The user-defined scheme's per-connection baton. */
     void *user_authn_baton;
 };
 
 
-apr_status_t
-serf__authn_user__init_conn(const serf__authn_scheme_t *scheme,
-                            int code,
-                            serf_connection_t *conn,
-                            apr_pool_t *pool)
+typedef apr_status_t (*callback_fn_t)();
+static apr_status_t validate_handler(serf_config_t *config,
+                                     const serf__authn_scheme_t *scheme,
+                                     int peer_id,
+                                     const char *callback_name,
+                                     callback_fn_t callback_func,
+                                     serf__authn_info_t *authn_info)
 {
-    serf__authn_info_t *const authn_info = get_authn_info(code, conn);
-    struct callback_authn_baton *authn_baton;
-    apr_status_t status = APR_SUCCESS;
-
-    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
-              "User-defined scheme %s: callback: init-conn\n",
-              scheme->name);
-
     if (!validate_user_authn(scheme)) {
-        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, config,
                   "Not a user-defined scheme: %s\n", scheme->name);
         return APR_EINVAL;
     }
     if (!scheme->user_init_conn_func) {
-        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
-                  "User-defined scheme %s: missing callback: init-conn\n",
-                  scheme->name);
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, config,
+                  "User-defined scheme %s: missing callback: %s\n",
+                  scheme->name, callback_name);
         return APR_ENOTIMPL;
     }
     if (!authn_info) {
-        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
-                  "User-defined scheme %s init-conn: invalid code: %d\n",
-                  scheme->name, code);
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, config,
+                  "User-defined scheme %s %s: invalid peer: %d\n",
+                  scheme->name, callback_name, peer_id);
         return APR_ERANGE;
     }
+    return APR_SUCCESS;
+}
+
+
+apr_status_t
+serf__authn_user__init_conn(const serf__authn_scheme_t *scheme,
+                            int code,
+                            serf_connection_t *conn,
+                            apr_pool_t *pool)
+{
+    serf__authn_info_t *const authn_info = get_authn_info(code, conn);
+    struct authn_baton_wrapper *authn_baton;
+    apr_status_t status;
+
+    status = validate_handler(conn->config, scheme, code, "init-conn",
+                              scheme->user_init_conn_func,
+                              authn_info);
+    if (status)
+        return status;
+
+    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
+              "User-defined scheme %s: callback: init-conn\n",
+              scheme->name);
 
     authn_baton = authn_info->baton;
     if (authn_baton == NULL) {
@@ -115,6 +135,9 @@ serf__authn_user__init_conn(const serf__
                   "User-defined scheme %s: pipelining off for %s\n",
                   scheme->name, conn->host_url);
         authn_baton->pipelining = serf__connection_set_pipelining(conn, 0);
+        authn_baton->pipelining_reset = false;
+    } else {
+        authn_baton->pipelining_reset = true;
     }
     return status;
 }
@@ -129,23 +152,85 @@ serf__authn_user__handle(const serf__aut
                          const char *auth_attr,
                          apr_pool_t *pool)
 {
-    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, request->conn->config,
+    serf_connection_t *const conn = request->conn;
+    serf__authn_info_t *const authn_info = get_authn_info(code, conn);
+    serf_context_t *const ctx = conn->ctx;
+    struct authn_baton_wrapper *authn_baton;
+    char *username, *password;
+    apr_pool_t *scratch_pool;
+    apr_status_t status;
+
+    status = validate_handler(conn->config, scheme, code, "handle-auth",
+                              scheme->user_handle_func,
+                              authn_info);
+    if (status)
+        return status;
+
+    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, ctx->config,
               "User-defined scheme %s: callback: handle-auth\n",
               scheme->name);
 
-    if (!validate_user_authn(scheme)) {
-        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, request->conn->config,
-                  "Not a user-defined scheme: %s\n", scheme->name);
-        return APR_EINVAL;
-    }
-    if (!scheme->user_handle_func) {
-        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, request->conn->config,
-                  "User-defined scheme %s: missing callback: handle-auth\n",
-                  scheme->name);
-        return APR_ENOTIMPL;
+    /* The credentials callback must be set if the scheme requires it. */
+    if (scheme->user_flags & SERF_AUTHN_FLAG_CREDS) {
+        bool failed = false;
+
+        if (!ctx->cred_cb) {
+            serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                      "User-defined scheme %s handle-auth:"
+                      " missing credentials callback\n",
+                      scheme->name);
+            failed = true;
+        }
+
+        if (!scheme->user_get_realm_func) {
+            serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, ctx->config,
+                      "User-defined scheme %s handle-auth:"
+                      " missing callback: get-realm\n",
+                      scheme->name);
+            failed = true;
+        }
+
+        if (failed)
+            return SERF_ERROR_AUTHN_FAILED;
     }
 
-    return APR_ENOTIMPL;
+    authn_baton = authn_info->baton;
+    apr_pool_create(&scratch_pool, pool);
+
+    status = APR_SUCCESS;
+    if (scheme->user_flags & SERF_AUTHN_FLAG_CREDS) {
+        const char *realm_name;
+        status = scheme->user_get_realm_func(scheme->user_baton,
+                                             authn_baton->user_authn_baton,
+                                             auth_hdr, auth_attr,
+                                             scratch_pool, scratch_pool,
+                                             &realm_name);
+        if (!status) {
+            const char *const realm = serf__construct_realm(
+                SERF__PEER_FROM_CODE(code), conn, realm_name, scratch_pool);
+            status = serf__provide_credentials(ctx,
+                                               &username, &password,
+                                               request,
+                                               code, scheme->name,
+                                               realm, scratch_pool);
+        }
+        if (status)
+            goto cleanup;
+    } else {
+        username = password = NULL;
+    }
+
+    status = scheme->user_handle_func(scheme->user_baton,
+                                      authn_baton->user_authn_baton, code,
+                                      auth_hdr, auth_attr,
+                                      SERF__HEADER_FROM_CODE(code),
+                                      username, password,
+                                      request, response,
+                                      pool, scratch_pool);
+
+  cleanup:
+    apr_pool_destroy(scratch_pool);
+    return status;
 }
 
 
@@ -159,23 +244,38 @@ serf__authn_user__setup_request(const se
                                 const char *uri,
                                 serf_bucket_t *hdrs_bkt)
 {
-    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
-              "User-defined scheme %s: callback: setup-request\n",
-              scheme->name);
+    const int peer_id = SERF__CODE_FROM_PEER(peer);
+    serf__authn_info_t *const authn_info = get_authn_info(peer_id, conn);
+    struct authn_baton_wrapper *authn_baton;
+    apr_pool_t *scratch_pool;
+    apr_status_t status;
+
+    status = validate_handler(conn->config, scheme, peer_id, "setup-request",
+                              scheme->user_setup_request_func,
+                              authn_info);
+    if (status)
+        return status;
 
-    if (!validate_user_authn(scheme)) {
-        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
-                  "Not a user-defined scheme: %s\n", scheme->name);
-        return APR_EINVAL;
-    }
-    if (!scheme->user_setup_request_func) {
+    if (!authn_info->baton) {
         serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
-                  "User-defined scheme %s: missing callback: setup-request\n",
+                  "User-defined scheme %s setup-request: no authn data\n",
                   scheme->name);
-        return APR_ENOTIMPL;
+        return APR_ERANGE;
     }
 
-    return APR_ENOTIMPL;
+    serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
+              "User-defined scheme %s: callback: setup-request\n",
+              scheme->name);
+
+    authn_baton = authn_info->baton;
+    apr_pool_create(&scratch_pool, conn->pool);
+    status = scheme->user_setup_request_func(scheme->user_baton,
+                                             authn_baton->user_authn_baton,
+                                             code, conn, request,
+                                             method, uri, hdrs_bkt,
+                                             scratch_pool);
+    apr_pool_destroy(scratch_pool);
+    return status;
 }
 
 
@@ -188,22 +288,52 @@ serf__authn_user__validate_response(cons
                                     serf_bucket_t *response,
                                     apr_pool_t *pool)
 {
+    const int peer_id = SERF__CODE_FROM_PEER(peer);
+    serf__authn_info_t *const authn_info = get_authn_info(peer_id, conn);
+    int reset_pipelining = 0;
+    struct authn_baton_wrapper *authn_baton;
+    apr_pool_t *scratch_pool;
+    apr_status_t status;
+
+    status = validate_handler(conn->config, scheme, peer_id,
+                              "validate-response",
+                              scheme->user_validate_response_func,
+                              authn_info);
+    if (status)
+        return status;
+
+    if (!authn_info->baton) {
+        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "User-defined scheme %s validate-response: no authn data\n",
+                  scheme->name);
+        return APR_ERANGE;
+    }
+
     serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
               "User-defined scheme %s: callback: validate-response\n",
               scheme->name);
 
-    if (!validate_user_authn(scheme)) {
-        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
-                  "Not a user-defined scheme: %s\n", scheme->name);
-        return APR_EINVAL;
-    }
-    if (!scheme->user_validate_response_func) {
-        serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config,
-                  "User-defined scheme %s:"
-                  " missing callback: validate-response\n",
-                  scheme->name);
-        return APR_ENOTIMPL;
+    authn_baton = authn_info->baton;
+    apr_pool_create(&scratch_pool, conn->pool);
+    status = scheme->user_validate_response_func(scheme->user_baton,
+                                                 authn_baton->user_authn_baton,
+                                                 code, conn, request, response,
+                                                 scratch_pool,
+                                                 &reset_pipelining);
+
+    /* Reset pipelining if the scheme requires it. */
+    if (status == APR_SUCCESS && reset_pipelining
+        && !authn_baton->pipelining_reset
+        && !(scheme->user_flags & SERF_AUTHN_FLAG_PIPE)) {
+        serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config,
+                  "User-defined scheme %s: pipelining reset to %s for %s\n",
+                  scheme->name,
+                  authn_baton->pipelining ? "on" : "off",
+                  conn->host_url);
+        serf__connection_set_pipelining(conn, authn_baton->pipelining);
+        authn_baton->pipelining_reset = true;
     }
 
-    return APR_ENOTIMPL;
+    apr_pool_destroy(scratch_pool);
+    return status;
 }

Modified: serf/branches/user-defined-authn/serf.h
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf.h?rev=1926486&r1=1926485&r2=1926486&view=diff
==============================================================================
--- serf/branches/user-defined-authn/serf.h (original)
+++ serf/branches/user-defined-authn/serf.h Mon Jun 16 18:38:31 2025
@@ -980,19 +980,33 @@ typedef apr_status_t
 
 /** TODO:  */
 typedef apr_status_t
+(*serf_authn_get_realm_func_t)(void *baton,
+                               void *authn_baton,
+                               const char *authn_header,
+                               const char *authn_attributes,
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool,
+                               const char **realm_name);
+
+/** TODO:  */
+typedef apr_status_t
 (*serf_authn_handle_func_t)(void *baton,
+                            void *authn_baton,
                             int code,
+                            const char *authn_header,
+                            const char *authn_attributes,
+                            const char *response_header,
+                            const char *username,
+                            const char *password,
                             serf_request_t *request,
                             serf_bucket_t *response,
-                            const char *auth_hdr,
-                            const char *auth_attr,
                             apr_pool_t *result_pool,
                             apr_pool_t *scratch_pool);
 
 /** TODO:  */
 typedef apr_status_t
 (*serf_authn_setup_request_func_t)(void *baton,
-                                   int peer,
+                                   void *authn_baton,
                                    int code,
                                    serf_connection_t *conn,
                                    serf_request_t *request,
@@ -1004,12 +1018,13 @@ typedef apr_status_t
 /** TODO:  */
 typedef apr_status_t
 (*serf_authn_validate_response_func_t)(void *baton,
-                                       int peer,
+                                       void *authn_baton,
                                        int code,
                                        serf_connection_t *conn,
                                        serf_request_t *request,
                                        serf_bucket_t *response,
-                                       apr_pool_t *scratch_pool);
+                                       apr_pool_t *scratch_pool,
+                                       int *reset_pipelining);
 
 /**
  * Register an autehtication scheme.
@@ -1044,6 +1059,7 @@ typedef apr_status_t
 apr_status_t serf_authn_register_scheme(
     serf_context_t *ctx, const char *name, void *baton, int flags,
     serf_authn_init_conn_func_t init_conn,
+    serf_authn_get_realm_func_t get_realm,
     serf_authn_handle_func_t handle,
     serf_authn_setup_request_func_t setup_request,
     serf_authn_validate_response_func_t validate_response,

Modified: serf/branches/user-defined-authn/test/test_auth.c
URL: 
http://svn.apache.org/viewvc/serf/branches/user-defined-authn/test/test_auth.c?rev=1926486&r1=1926485&r2=1926486&view=diff
==============================================================================
--- serf/branches/user-defined-authn/test/test_auth.c (original)
+++ serf/branches/user-defined-authn/test/test_auth.c Mon Jun 16 18:38:31 2025
@@ -533,7 +533,7 @@ static void test_authn_register_one(CuTe
     /* Register an authentication scheme */
     status = serf_authn_register_scheme(tb->context, "Fizzle", baton,
                                         SERF_AUTHN_FLAG_NONE,
-                                        NULL, NULL, NULL, NULL,
+                                        NULL, NULL, NULL, NULL, NULL,
                                         tb->pool, &type);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type != SERF_AUTHN_NONE);
@@ -558,14 +558,14 @@ static void test_authn_register_two(CuTe
     /* Register the schemes */
     status = serf_authn_register_scheme(tb->context, "Tweedledee", baton1,
                                         SERF_AUTHN_FLAG_NONE,
-                                        NULL, NULL, NULL, NULL,
+                                        NULL, NULL, NULL, NULL, NULL,
                                         tb->pool, &type1);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type1 != SERF_AUTHN_NONE);
 
     status = serf_authn_register_scheme(tb->context, "Tweedledum", baton2,
                                         SERF_AUTHN_FLAG_NONE,
-                                        NULL, NULL, NULL, NULL,
+                                        NULL, NULL, NULL, NULL, NULL,
                                         tb->pool, &type2);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type2 != SERF_AUTHN_NONE);
@@ -593,14 +593,14 @@ static void test_authn_register_twice(Cu
     /* Register an authentication scheme */
     status = serf_authn_register_scheme(tb->context, "Tweens", baton,
                                         SERF_AUTHN_FLAG_NONE,
-                                        NULL, NULL, NULL, NULL,
+                                        NULL, NULL, NULL, NULL, NULL,
                                         tb->pool, &type);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type != SERF_AUTHN_NONE);
 
     status = serf_authn_register_scheme(tb->context, "Tweens", baton,
                                         SERF_AUTHN_FLAG_NONE,
-                                        NULL, NULL, NULL, NULL,
+                                        NULL, NULL, NULL, NULL, NULL,
                                         tb->pool, &epyt);
     CuAssertIntEquals(tc, APR_EEXIST, status);
     CuAssertTrue(tc, epyt == SERF_AUTHN_NONE);
@@ -643,7 +643,7 @@ static void test_authn_registered_pool_c
     /* Register an authentication scheme */
     status = serf_authn_register_scheme(tb->context, "Killed", baton,
                                         SERF_AUTHN_FLAG_NONE,
-                                        NULL, NULL, NULL, NULL,
+                                        NULL, NULL, NULL, NULL, NULL,
                                         scheme_pool, &type);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, type != SERF_AUTHN_NONE);
@@ -663,6 +663,7 @@ struct user_authn_baton {
     const char *name;
     int all_count;
     int init_conn_count;
+    int get_realm_count;
     int handle_count;
     int setup_request_count;
     int validate_response_count;
@@ -704,21 +705,67 @@ static apr_status_t user_authn_init_conn
     return APR_SUCCESS;
 }
 
+static apr_status_t user_authn_get_realm(void *baton,
+                                         void *authn_baton,
+                                         const char *authn_header,
+                                         const char *authn_attributes,
+                                         apr_pool_t *result_pool,
+                                         apr_pool_t *scratch_pool,
+                                         const char **realm_name)
+{
+    const char *end;
+    apr_size_t length;
+    USER_AUTHN_COUNT(baton, get_realm);
+
+    test__log(TEST_VERBOSE, __FILE__,
+              "user_authn_get_realm, header %s, attrs %s\n",
+              authn_header, authn_attributes);
+
+    if (strncasecmp(authn_header, "TweedleDee", 10)) {
+        *realm_name = "Wonderland";
+        return APR_SUCCESS;
+    }
+
+    if (strncasecmp(authn_attributes, "scope=", 6))
+        return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
+
+    authn_attributes += 6;
+    if ((end = strchr(authn_attributes, ' '))) {
+        length = end - authn_attributes;
+    } else {
+        length = strlen(authn_attributes);
+    }
+    *realm_name = apr_pstrndup(result_pool, authn_attributes, length);
+    return APR_SUCCESS;
+}
+
 static apr_status_t user_authn_handle(void *baton,
+                                      void *authn_baton,
                                       int code,
+                                      const char *authn_header,
+                                      const char *authn_attributes,
+                                      const char *response_header,
+                                      const char *username,
+                                      const char *password,
                                       serf_request_t *request,
                                       serf_bucket_t *response,
-                                      const char *auth_hdr,
-                                      const char *auth_attr,
                                       apr_pool_t *result_pool,
                                       apr_pool_t *scratch_pool)
 {
+    user_authn_baton_t *const ab = authn_baton;
     USER_AUTHN_COUNT(baton, handle);
+
+    if (username != NULL)
+        return SERF_ERROR_AUTHN_INITALIZATION_FAILED;
+
+    ab->header = apr_pstrdup(result_pool, response_header);
+    ab->value = apr_pstrcat(result_pool, b->name, " ", password, NULL);
+
     return APR_SUCCESS;
 }
 
 static apr_status_t user_authn_setup_request(void *baton,
-                                             int peer,
+                                             void *authn_baton,
                                              int code,
                                              serf_connection_t *conn,
                                              serf_request_t *request,
@@ -727,33 +774,48 @@ static apr_status_t user_authn_setup_req
                                              serf_bucket_t *headers,
                                              apr_pool_t *scratch_pool)
 {
+    user_authn_baton_t *const ab = authn_baton;
     USER_AUTHN_COUNT(baton, setup_request);
-    return APR_SUCCESS;
+
+
+    if (ab && ab->header && ab->value) {
+        test__log(TEST_VERBOSE, __FILE__,
+                  "user_authn_setup_request, header %s: %s\n",
+                  ab->header, ab->value);
+
+        serf_bucket_headers_setn(headers, ab->header, ab->value);
+        return APR_SUCCESS;
+    }
+
+    return SERF_ERROR_AUTHN_FAILED;
 }
 
 static apr_status_t user_authn_validate_response(void *baton,
-                                                 int peer,
+                                                 void *authn_baton,
                                                  int code,
                                                  serf_connection_t *conn,
                                                  serf_request_t *request,
                                                  serf_bucket_t *response,
-                                                 apr_pool_t *scratch_pool)
+                                                 apr_pool_t *scratch_pool,
+                                                 int *reset_pipelining)
 {
     USER_AUTHN_COUNT(baton, validate_response);
+    *reset_pipelining = 1;
     return APR_SUCCESS;
 }
 #undef USER_AUTHN_COUNT
 
 static apr_status_t
-user_authn_credentials_callback(char **username,
-                    char **password,
-                    serf_request_t *request, void *baton,
-                    int code, const char *authn_type,
-                    const char *scope,
-                    apr_pool_t *pool)
+user_authn_credentials(char **username,
+                       char **password,
+                       serf_request_t *request, void *baton,
+                       int code, const char *authn_type,
+                       const char *realm,
+                       apr_pool_t *pool)
 {
     handler_baton_t *handler_ctx = baton;
     test_baton_t *tb = handler_ctx->tb;
+    const char *realm_name;
 
     tb->result_flags |= TEST_RESULT_AUTHNCB_CALLED;
 
@@ -761,32 +823,42 @@ user_authn_credentials_callback(char **u
         return REPORT_TEST_SUITE_ERROR();
     if (strncmp(user_authn_prefix, authn_type, strlen(user_authn_prefix)) != 0)
         return REPORT_TEST_SUITE_ERROR();
-    if (strcmp("Alice", scope) != 0)
+
+    realm_name = realm + strlen(realm) - strlen("Alice");
+    if (strcmp("Alice", realm_name) != 0)
         return REPORT_TEST_SUITE_ERROR();
 
     *username = NULL;
     *password = apr_pstrdup(pool, authn_type);
+    test__log(TEST_VERBOSE, __FILE__,
+              "user credentials, realm %s, password %s\n",
+              realm, *password);
 
     return APR_SUCCESS;
 }
 
-static void user_authentication(CuTest *tc, int close_conn)
+static void user_authentication(CuTest *tc,
+                                int close_conn,
+                                int use_pipelining,
+                                const char *tweak)
 {
     test_baton_t *tb = tc->testBaton;
-    handler_baton_t handler_ctx[2];
+    handler_baton_t handler_ctx;
     int num_requests_sent;
     apr_status_t status;
     int typedee, typedum;
+    const char *hdr_value = "tweeDlEdee scope=Alice";
     user_authn_t *const tdee = user_authn_make_baton("TweedleDee", tb->pool);
     user_authn_t *const tdum = user_authn_make_baton("TweedleDum", tb->pool);
+    const int flags = (SERF_AUTHN_FLAG_CREDS
+                       | (use_pipelining ? SERF_AUTHN_FLAG_PIPE : 0));
 
     status = setup_test_context(tb, tb->pool);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
-    status = serf_authn_register_scheme(tb->context, tdee->name, tdee,
-                                        SERF_AUTHN_FLAG_CREDS
-                                        | SERF_AUTHN_FLAG_PIPE,
+    status = serf_authn_register_scheme(tb->context, tdee->name, tdee, flags,
                                         user_authn_init_conn,
+                                        user_authn_get_realm,
                                         user_authn_handle,
                                         user_authn_setup_request,
                                         user_authn_validate_response,
@@ -794,10 +866,9 @@ static void user_authentication(CuTest *
     CuAssertIntEquals(tc, APR_SUCCESS, status);
     CuAssertTrue(tc, typedee != SERF_AUTHN_NONE);
 
-    status = serf_authn_register_scheme(tb->context, tdum->name, tdum,
-                                        SERF_AUTHN_FLAG_CREDS
-                                        | SERF_AUTHN_FLAG_PIPE,
+    status = serf_authn_register_scheme(tb->context, tdum->name, tdum, flags,
                                         user_authn_init_conn,
+                                        user_authn_get_realm,
                                         user_authn_handle,
                                         user_authn_setup_request,
                                         user_authn_validate_response,
@@ -816,14 +887,19 @@ static void user_authentication(CuTest *
     CuAssertIntEquals(tc, APR_SUCCESS, status);
 
     serf_config_authn_types(tb->context, typedee | typedum);
-    serf_config_credentials_callback(tb->context, 
user_authn_credentials_callback);
+    serf_config_credentials_callback(tb->context, user_authn_credentials);
+
+    /* Adjust the authentication header. */
+    if (tweak)
+        hdr_value = apr_pstrcat(tb->pool, hdr_value,
+                                " tweak=", tweak, NULL);
 
     /* Use non-standard case WWW-Authenticate header and scheme name to test
        for case insensitive comparisons. */
     Given(tb->mh)
       GETRequest(URLEqualTo("/"), HeaderNotSet("Authorization"))
         Respond(WithCode(SERF_AUTHN_CODE_HOST),WithChunkedBody("1"),
-                WithHeader("www-Authenticate", "tweeDlEdee scope=Alice"),
+                WithHeader("www-Authenticate", hdr_value),
                 OnConditionThat(close_conn, WithConnectionCloseHeader))
       GETRequest(URLEqualTo("/"),
                  HeaderEqualTo("Authorization", "TweedleDee TweedleDee"))
@@ -832,17 +908,51 @@ static void user_authentication(CuTest *
       AllRequestsReceivedInOrder
     EndGiven
 
-    create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
+    tb->result_flags = 0;
+    create_new_request(tb, &handler_ctx, "GET", "/", 1);
     status = run_client_and_mock_servers_loops(tb, num_requests_sent,
-                                               handler_ctx, tb->pool);
+                                               &handler_ctx, tb->pool);
     CuAssertIntEquals(tc, APR_SUCCESS, status);
+    CuAssertIntEquals(tc, num_requests_sent, tb->handled_requests->nelts);
     CuAssertTrue(tc, tb->result_flags & TEST_RESULT_AUTHNCB_CALLED);
+    CuAssertIntEquals(tc, 0, tdum->all_count);
+    CuAssertIntEquals(tc, 1, tdee->init_conn_count);
+    CuAssertIntEquals(tc, 1, tdee->get_realm_count);
+    CuAssertIntEquals(tc, 1, tdee->handle_count);
+    CuAssertIntEquals(tc, 1, tdee->setup_request_count);
+    CuAssertIntEquals(tc, 1, tdee->validate_response_count);
+    CuAssertIntEquals(tc, 5, tdee->all_count);
     Verify(tb->mh)
       CuAssertTrue(tc, VerifyAllExpectationsOk);
     EndVerify
 
-    CuAssertIntEquals(tc, 4, tdee->all_count);
+    /* Test that credentials were cached by asserting that the
+       authn callback wasn't called again. */
+    Given(tb->mh)
+      GETRequest(URLEqualTo("/"),
+                 HeaderEqualTo("Authorization", "TweedleDee TweedleDee"))
+        Respond(WithCode(200), WithChunkedBody(""))
+    Expect
+      AllRequestsReceivedInOrder
+    EndGiven
+
+    tb->result_flags = 0;
+    create_new_request(tb, &handler_ctx, "GET", "/", 2);
+    status = run_client_and_mock_servers_loops(tb, num_requests_sent,
+                                               &handler_ctx, tb->pool);
+    CuAssertIntEquals(tc, APR_SUCCESS, status);
+    CuAssertIntEquals(tc, 1 + num_requests_sent, tb->handled_requests->nelts);
+    CuAssertTrue(tc, !(tb->result_flags & TEST_RESULT_AUTHNCB_CALLED));
     CuAssertIntEquals(tc, 0, tdum->all_count);
+    CuAssertIntEquals(tc, 1, tdee->init_conn_count);
+    CuAssertIntEquals(tc, 1, tdee->get_realm_count);
+    CuAssertIntEquals(tc, 1, tdee->handle_count);
+    CuAssertIntEquals(tc, 2, tdee->setup_request_count);
+    CuAssertIntEquals(tc, 2, tdee->validate_response_count);
+    CuAssertIntEquals(tc, 7, tdee->all_count);
+    Verify(tb->mh)
+      CuAssertTrue(tc, VerifyAllExpectationsOk);
+    EndVerify
 
     status = serf_authn_unregister_scheme(tb->context,
                                           typedum, tdum->name, tb->pool);
@@ -854,14 +964,35 @@ static void user_authentication(CuTest *
 
 static void test_user_authentication(CuTest *tc)
 {
-    user_authentication(tc, 0 /* don't close connection */);
+    user_authentication(tc,
+                        0 /* don't close connection */,
+                        1 /* allow pipelining during authn */,
+                        0 /* no authn header tweaks */);
+}
+
+static void test_user_authentication_tweaked(CuTest *tc)
+{
+    user_authentication(tc,
+                        0 /* don't close connection */,
+                        1 /* allow pipelining during authn */,
+                        "Cheshire" /* tweak autn header */);
 }
 
 static void test_user_authentication_keepalive_off(CuTest *tc)
 {
-    user_authentication(tc, 1);
+    user_authentication(tc,
+                        1 /* close connection */,
+                        1 /* allow pipelining during authn */,
+                        0 /* no authn header tweaks */);
 }
 
+static void test_user_authentication_pipelining_off(CuTest *tc)
+{
+    user_authentication(tc,
+                        0 /* don't close connection */,
+                        0 /* don't allow pipelining during authn */,
+                        0 /* no authn header tweaks */);
+}
 
 
 /*****************************************************************************/
@@ -885,8 +1016,10 @@ CuSuite *test_auth(void)
     SUITE_ADD_TEST(suite, test_authn_register_twice);
     SUITE_ADD_TEST(suite, test_authn_unregister_unknown);
     SUITE_ADD_TEST(suite, test_authn_registered_pool_cleanup);
-    /* SUITE_ADD_TEST(suite, test_user_authentication); */
-    /* SUITE_ADD_TEST(suite, test_user_authentication_keepalive_off); */
+    SUITE_ADD_TEST(suite, test_user_authentication);
+    SUITE_ADD_TEST(suite, test_user_authentication_tweaked);
+    SUITE_ADD_TEST(suite, test_user_authentication_keepalive_off);
+    SUITE_ADD_TEST(suite, test_user_authentication_pipelining_off);
 
     return suite;
 }



Reply via email to