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;
}