Author: brane Date: Mon Jun 16 03:19:00 2025 New Revision: 1926453 URL: http://svn.apache.org/viewvc?rev=1926453&view=rev Log: On the user-defined-authn branch: start figuring out how the public authn callbacks will look like, and what sort of support from the implementation they'll need.
* serf.h (SERF_AUTHN_FLAG_NONE, SERF_AUTHN_FLAG_PIPE, SERF_AUTHN_FLAG_CREDS): Bit mask constants for authn scheme requirements. (serf_authn_handle_func_t, serf_authn_handle_func_t, serf_authn_setup_request_func_t, serf_authn_validate_response_func_t): Declare callback types. Still mostly guesswork as to what the actual arguments will be like. (serf_authn_register_scheme): Add the flags and callback arguments. (serf_request_bucket_request_create): Fix typo in docstring. * auth/auth.h (serf__authn_scheme_t::user_magic): Renamed from 'magic'. (serf__authn_scheme_t::user_baton): Renamed from baton'. (serf__authn_scheme_t::user_flags, serf__authn_scheme_t::user_init_conn_func, serf__authn_scheme_t::user_handle_func, serf__authn_scheme_t::user_setup_request_func, serf__authn_scheme_t::user_validate_response_func): New members. * auth/auth.c (serf_authn_register_scheme): Add and store the new parameters to the authn descriptor struct. * auth/auth_user_defined.c (validate_user_authn): Change magic -> user_magic. (get_authn_info): New helper function. (authn_baton_wrapper): New; the per-connection authentication baton. (serf__authn_user__init_conn): Implement connection initialization. (serf__authn_user__handle, serf__authn_user__setup_request, serf__authn_user__validate_response): Add logging to argument validation. * serf_private.h (serf__connection_set_pipelining): Change the return type. * src/outgoing.c (serf__connection_set_pipelining): Return the previous pipelining state. * test/test_auth.c (test_authn_register_one, test_authn_register_two, test_authn_register_twice): Update calls to serf_authn_register_scheme. (user_authn_baton, user_authn_t): Baton type for user-defined authn tests. (USER_AUTHN_COUNT): Helper macro for counting callback invocations. (user_authn_make_baton): Allocation helper for user_authn_t. (user_authn_cache, user_authn_baton_t): Per-connection authn baton type. (user_authn_prefix): Helper string for validation. (user_authn_init_conn): Complete connection-init callback. (user_authn_handle, user_authn_setup_request, user_authn_validate_response): Placeholders for the other three callbacks. (user_authn_credentials_callback): Credentials callback for user-authn tests. (user_authentication): Partial test for user authentication. (test_user_authentication, test_user_authentication_keepalive_off): Two entry points for this test. (test_auth): Register only test_user_authentication for now. * test/test_util.c (setup_test_context): Duh. Set up logging only once per new context. 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/serf_private.h serf/branches/user-defined-authn/src/outgoing.c serf/branches/user-defined-authn/test/test_auth.c serf/branches/user-defined-authn/test/test_util.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=1926453&r1=1926452&r2=1926453&view=diff ============================================================================== --- serf/branches/user-defined-authn/auth/auth.c (original) +++ serf/branches/user-defined-authn/auth/auth.c Mon Jun 16 03:19:00 2025 @@ -623,11 +623,14 @@ static unsigned int find_next_user_schem return avail & -avail; } -apr_status_t serf_authn_register_scheme(serf_context_t *ctx, - const char *name, - void *baton, - apr_pool_t *result_pool, - int *type) +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_handle_func_t handle, + serf_authn_setup_request_func_t setup_request, + serf_authn_validate_response_func_t validate_response, + apr_pool_t *result_pool, + int *type) { serf__authn_scheme_t *authn_scheme; apr_status_t lock_status; @@ -655,8 +658,13 @@ apr_status_t serf_authn_register_scheme( authn_scheme->validate_response_func = serf__authn_user__validate_response; /* User-defined scheme data. */ - authn_scheme->magic = serf__authn_user__magic; - authn_scheme->baton = baton; + authn_scheme->user_magic = serf__authn_user__magic; + authn_scheme->user_baton = baton; + authn_scheme->user_flags = flags; + authn_scheme->user_init_conn_func = init_conn; + authn_scheme->user_handle_func = handle; + authn_scheme->user_setup_request_func = setup_request; + authn_scheme->user_validate_response_func = validate_response; lock_status = lock_authn_schemes(ctx->config); if (lock_status) Modified: serf/branches/user-defined-authn/auth/auth.h URL: http://svn.apache.org/viewvc/serf/branches/user-defined-authn/auth/auth.h?rev=1926453&r1=1926452&r2=1926453&view=diff ============================================================================== --- serf/branches/user-defined-authn/auth/auth.h (original) +++ serf/branches/user-defined-authn/auth/auth.h Mon Jun 16 03:19:00 2025 @@ -114,10 +114,19 @@ struct serf__authn_scheme_t { */ /* The magic number that helps verify the user-defined scheme data. */ - apr_uint64_t magic; + apr_uint64_t user_magic; + + /* The flags for this authentication scheme */ + int user_flags; /* The baton used by the callbacks. */ - void *baton; + void *user_baton; + + /* Authentication callbacks. */ + serf_authn_init_conn_func_t user_init_conn_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=1926453&r1=1926452&r2=1926453&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 03:19:00 2025 @@ -25,25 +25,101 @@ #include "auth.h" -static const bool -validate_user_authn(const serf__authn_scheme_t *scheme) +static bool validate_user_authn(const serf__authn_scheme_t *scheme) { return (scheme->type & *serf__authn_user__type_mask - && scheme->magic == serf__authn_user__magic); + && scheme->user_magic == serf__authn_user__magic); } + +static serf__authn_info_t *get_authn_info(int code, serf_connection_t *conn) +{ + switch (code) + { + case SERF_AUTHN_CODE_HOST: + return serf__get_authn_info_for_server(conn); + case SERF_AUTHN_CODE_PROXY: + return &conn->ctx->proxy_authn_info; + } + + /* FIXME: ??? Shouldn't be possible. */ + return NULL; +} + + +/* Used for serf__authn_info_t::baton */ +struct callback_authn_baton { + /* The connection's pipelining state before we changed it. */ + int pipelining; + + /* 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) { - if (!validate_user_authn(scheme)) + 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, + "Not a user-defined scheme: %s\n", scheme->name); return APR_EINVAL; - - return APR_ENOTIMPL; + } + 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); + 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); + return APR_ERANGE; + } + + authn_baton = authn_info->baton; + if (authn_baton == NULL) { + apr_pool_t *scratch_pool; + + serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config, + "User-defined scheme %s: create authn baton\n", + scheme->name); + + apr_pool_create(&scratch_pool, pool); + authn_baton = apr_pcalloc(pool, sizeof(*authn_baton)); + status = scheme->user_init_conn_func(scheme->user_baton, code, + pool, scratch_pool, + &authn_baton->user_authn_baton); + apr_pool_destroy(scratch_pool); + + if (status == APR_SUCCESS) + authn_info->baton = authn_baton; + } + + /* Turn off pipelining if the scheme requires it. */ + if (status == APR_SUCCESS + && !(scheme->user_flags & SERF_AUTHN_FLAG_PIPE)) { + serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config, + "User-defined scheme %s: pipelining off for %s\n", + scheme->name, conn->host_url); + authn_baton->pipelining = serf__connection_set_pipelining(conn, 0); + } + return status; } + apr_status_t serf__authn_user__handle(const serf__authn_scheme_t *scheme, int code, @@ -53,12 +129,26 @@ serf__authn_user__handle(const serf__aut const char *auth_attr, apr_pool_t *pool) { - if (!validate_user_authn(scheme)) + serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, request->conn->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; + } return APR_ENOTIMPL; } + apr_status_t serf__authn_user__setup_request(const serf__authn_scheme_t *scheme, peer_t peer, @@ -69,12 +159,26 @@ serf__authn_user__setup_request(const se const char *uri, serf_bucket_t *hdrs_bkt) { - if (!validate_user_authn(scheme)) + serf__log(LOGLVL_DEBUG, LOGCOMP_AUTHN, __FILE__, conn->config, + "User-defined scheme %s: callback: setup-request\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_setup_request_func) { + serf__log(LOGLVL_ERROR, LOGCOMP_AUTHN, __FILE__, conn->config, + "User-defined scheme %s: missing callback: setup-request\n", + scheme->name); + return APR_ENOTIMPL; + } return APR_ENOTIMPL; } + apr_status_t serf__authn_user__validate_response(const serf__authn_scheme_t *scheme, peer_t peer, @@ -84,8 +188,22 @@ serf__authn_user__validate_response(cons serf_bucket_t *response, apr_pool_t *pool) { - if (!validate_user_authn(scheme)) + 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; + } return APR_ENOTIMPL; } Modified: serf/branches/user-defined-authn/serf.h URL: http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf.h?rev=1926453&r1=1926452&r2=1926453&view=diff ============================================================================== --- serf/branches/user-defined-authn/serf.h (original) +++ serf/branches/user-defined-authn/serf.h Mon Jun 16 03:19:00 2025 @@ -966,18 +966,73 @@ serf_bucket_t *serf_request_bucket_reque #define SERF_AUTHN_CODE_HOST 401 /**< Authentication request from a host */ #define SERF_AUTHN_CODE_PROXY 407 /**< Authentication requset from a proxy */ +/* Flags returned from the init-connection callback. */ +#define SERF_AUTHN_FLAG_NONE 0x00 /**< Authn flags: None */ +#define SERF_AUTHN_FLAG_PIPE 0x01 /**< Authn flags: Allow pipelining */ +#define SERF_AUTHN_FLAG_CREDS 0x02 /**< Authn flags: Require credentials */ + +/** TODO: */ +typedef apr_status_t +(*serf_authn_init_conn_func_t)(void *baton, int code, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool, + void **authn_baton); + +/** TODO: */ +typedef apr_status_t +(*serf_authn_handle_func_t)(void *baton, + int code, + 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, + int code, + serf_connection_t *conn, + serf_request_t *request, + const char *method, + const char *uri, + serf_bucket_t *headers, + apr_pool_t *scratch_pool); + +/** TODO: */ +typedef apr_status_t +(*serf_authn_validate_response_func_t)(void *baton, + int peer, + int code, + serf_connection_t *conn, + serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *scratch_pool); + /** * Register an autehtication scheme. * - * The number returned in @a type can be used as a bit mask in - * serf_config_authn_types(). If an error occurs during registration, - * @a type will be set to @c SERF_AUTHN_NONE. + * The context in @a ctx is used for logging. * * The @a name is the name of the authentication scheme as it appears in the * authorization headers. It must be a valid token as defined in RFC-9110 * (see reference, below). * - * The context in @a ctx is used for logging. + * @a baton will be passed unchanged to the callbacks. + * + * @a flags is a bitmask of @c APR_AUTHN_FLAG_* constants that define the + * scheme's requirements; e.g., whether the credentials callback should be + * invoked, or whether pipelining should be disabled while the authentication + * handshake is in progress. + * + * @a init_conn, @a handle, @a setup_request and @a validate_response are the + * callbacks that implement the authentication handshake for this scheme. + * + * The number returned in @a type can be used as a bit mask in + * serf_config_authn_types(). If an error occurs during registration, + * @a type will be set to @c SERF_AUTHN_NONE. * * Internal structures related to this provider will be allocated from * @a result_pool, so take care that it lives as long as the autehtication @@ -986,11 +1041,14 @@ serf_bucket_t *serf_request_bucket_reque * @see https://www.rfc-editor.org/rfc/rfc9110#section-11.1 * @since New in 1.4 */ -apr_status_t serf_authn_register_scheme(serf_context_t *ctx, - const char *name, - void *baton, - apr_pool_t *result_pool, - int *type); +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_handle_func_t handle, + serf_authn_setup_request_func_t setup_request, + serf_authn_validate_response_func_t validate_response, + apr_pool_t *result_pool, + int *type); /* FIXME: Think some more about whether unregistering schemes makes sense. */ /** @@ -999,8 +1057,8 @@ apr_status_t serf_authn_register_scheme( * Removes the scheme, identified by @a type that was returned from and * @a name that was supplied to serf_authn_register_scheme(), from the * list of supported authentication schemes. Uses @a scratch_pool for - * temporary allocations; this pool can be destroyed afterthe function - * returns. + * temporary allocations; this pool can be destroyed after the function + * has returned. * * The context in @a ctx is used for logging. * Modified: serf/branches/user-defined-authn/serf_private.h URL: http://svn.apache.org/viewvc/serf/branches/user-defined-authn/serf_private.h?rev=1926453&r1=1926452&r2=1926453&view=diff ============================================================================== --- serf/branches/user-defined-authn/serf_private.h (original) +++ serf/branches/user-defined-authn/serf_private.h Mon Jun 16 03:19:00 2025 @@ -741,7 +741,7 @@ apr_status_t serf__conn_update_pollset(s serf_request_t *serf__ssltunnel_request_create(serf_connection_t *conn, serf_request_setup_t setup, void *setup_baton); -void serf__connection_set_pipelining(serf_connection_t *conn, int enabled); +int serf__connection_set_pipelining(serf_connection_t *conn, int enabled); apr_status_t serf__connection_flush(serf_connection_t *conn, bool fetch_new); Modified: serf/branches/user-defined-authn/src/outgoing.c URL: http://svn.apache.org/viewvc/serf/branches/user-defined-authn/src/outgoing.c?rev=1926453&r1=1926452&r2=1926453&view=diff ============================================================================== --- serf/branches/user-defined-authn/src/outgoing.c (original) +++ serf/branches/user-defined-authn/src/outgoing.c Mon Jun 16 03:19:00 2025 @@ -1460,9 +1460,11 @@ void serf_connection_set_max_outstanding HTTP pipelining can achieve this by calling: serf_connection_set_max_outstanding_requests(conn, 1) . */ -void serf__connection_set_pipelining(serf_connection_t *conn, int enabled) +int serf__connection_set_pipelining(serf_connection_t *conn, int enabled) { + int pipelining = conn->pipelining; conn->pipelining = enabled; + return pipelining; } void serf_connection_set_async_responses( 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=1926453&r1=1926452&r2=1926453&view=diff ============================================================================== --- serf/branches/user-defined-authn/test/test_auth.c (original) +++ serf/branches/user-defined-authn/test/test_auth.c Mon Jun 16 03:19:00 2025 @@ -531,8 +531,10 @@ static void test_authn_register_one(CuTe CuAssertIntEquals(tc, APR_SUCCESS, status); /* Register an authentication scheme */ - status = serf_authn_register_scheme(tb->context, - "Fizzle", baton, tb->pool, &type); + status = serf_authn_register_scheme(tb->context, "Fizzle", baton, + SERF_AUTHN_FLAG_NONE, + NULL, NULL, NULL, NULL, + tb->pool, &type); CuAssertIntEquals(tc, APR_SUCCESS, status); CuAssertTrue(tc, type != SERF_AUTHN_NONE); @@ -554,13 +556,17 @@ static void test_authn_register_two(CuTe CuAssertIntEquals(tc, APR_SUCCESS, status); /* Register the schemes */ - status = serf_authn_register_scheme(tb->context, - "Tweedledee", baton1, tb->pool, &type1); + status = serf_authn_register_scheme(tb->context, "Tweedledee", baton1, + SERF_AUTHN_FLAG_NONE, + 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, tb->pool, &type2); + status = serf_authn_register_scheme(tb->context, "Tweedledum", baton2, + SERF_AUTHN_FLAG_NONE, + NULL, NULL, NULL, NULL, + tb->pool, &type2); CuAssertIntEquals(tc, APR_SUCCESS, status); CuAssertTrue(tc, type2 != SERF_AUTHN_NONE); CuAssertTrue(tc, type2 != type1); @@ -585,13 +591,17 @@ static void test_authn_register_twice(Cu CuAssertIntEquals(tc, APR_SUCCESS, status); /* Register an authentication scheme */ - status = serf_authn_register_scheme(tb->context, - "Tweens", baton, tb->pool, &type); + status = serf_authn_register_scheme(tb->context, "Tweens", baton, + SERF_AUTHN_FLAG_NONE, + 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, tb->pool, &epyt); + status = serf_authn_register_scheme(tb->context, "Tweens", baton, + SERF_AUTHN_FLAG_NONE, + NULL, NULL, NULL, NULL, + tb->pool, &epyt); CuAssertIntEquals(tc, APR_EEXIST, status); CuAssertTrue(tc, epyt == SERF_AUTHN_NONE); @@ -615,6 +625,213 @@ static void test_authn_unregister_unknow CuAssertIntEquals(tc, APR_ENOENT, status); } + +typedef struct user_authn_baton user_authn_t; +struct user_authn_baton { + const char *name; + int all_count; + int init_conn_count; + int handle_count; + int setup_request_count; + int validate_response_count; +}; + +#define USER_AUTHN_COUNT(baton, callback) \ + user_authn_t *const b = (baton); \ + do { \ + ++b->callback##_count; \ + ++b->all_count; \ + } while(0) + +static user_authn_t *user_authn_make_baton(const char* name, apr_pool_t *pool) +{ + user_authn_t *baton = apr_pcalloc(pool, sizeof(*baton)); + baton->name = apr_pstrdup(pool, name); + return baton; +} + +typedef struct user_authn_cache user_authn_baton_t; +struct user_authn_cache { + const char *header; + const char *value; +}; + +static const char *const user_authn_prefix = "Tweedle"; + +static apr_status_t user_authn_init_conn(void *baton, int code, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool, + void **authn_baton) +{ + USER_AUTHN_COUNT(baton, init_conn); + + if (strncmp(user_authn_prefix, b->name, strlen(user_authn_prefix)) != 0) + return SERF_ERROR_AUTHN_INITALIZATION_FAILED; + + *authn_baton = apr_pcalloc(result_pool, sizeof(user_authn_baton_t)); + return APR_SUCCESS; +} + +static apr_status_t user_authn_handle(void *baton, + int code, + 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_COUNT(baton, handle); + return APR_SUCCESS; +} + +static apr_status_t user_authn_setup_request(void *baton, + int peer, + int code, + serf_connection_t *conn, + serf_request_t *request, + const char *method, + const char *uri, + serf_bucket_t *headers, + apr_pool_t *scratch_pool) +{ + USER_AUTHN_COUNT(baton, setup_request); + return APR_SUCCESS; +} + +static apr_status_t user_authn_validate_response(void *baton, + int peer, + int code, + serf_connection_t *conn, + serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *scratch_pool) +{ + USER_AUTHN_COUNT(baton, validate_response); + 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) +{ + handler_baton_t *handler_ctx = baton; + test_baton_t *tb = handler_ctx->tb; + + tb->result_flags |= TEST_RESULT_AUTHNCB_CALLED; + + if (code != SERF_AUTHN_CODE_HOST) + 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) + return REPORT_TEST_SUITE_ERROR(); + + *username = NULL; + *password = apr_pstrdup(pool, authn_type); + + return APR_SUCCESS; +} + +static void user_authentication(CuTest *tc, int close_conn) +{ + test_baton_t *tb = tc->testBaton; + handler_baton_t handler_ctx[2]; + int num_requests_sent; + apr_status_t status; + int typedee, typedum; + user_authn_t *const tdee = user_authn_make_baton("TweedleDee", tb->pool); + user_authn_t *const tdum = user_authn_make_baton("TweedleDum", tb->pool); + + 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, + user_authn_init_conn, + user_authn_handle, + user_authn_setup_request, + user_authn_validate_response, + tb->pool, &typedee); + 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, + user_authn_init_conn, + user_authn_handle, + user_authn_setup_request, + user_authn_validate_response, + tb->pool, &typedum); + CuAssertIntEquals(tc, APR_SUCCESS, status); + CuAssertTrue(tc, typedum != SERF_AUTHN_NONE); + CuAssertTrue(tc, typedum != typedee); + + /* Test that a request is retried and authentication + headers are set correctly. */ + num_requests_sent = 1; + + /* Set up a test context with a server */ + setup_test_mock_server(tb); + status = setup_test_client_context(tb, NULL, tb->pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + + serf_config_authn_types(tb->context, typedee | typedum); + serf_config_credentials_callback(tb->context, user_authn_credentials_callback); + + /* 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"), + OnConditionThat(close_conn, WithConnectionCloseHeader)) + GETRequest(URLEqualTo("/"), + HeaderEqualTo("Authorization", "TweedleDee TweedleDee")) + Respond(WithCode(200),WithChunkedBody("")) + Expect + AllRequestsReceivedInOrder + EndGiven + + create_new_request(tb, &handler_ctx[0], "GET", "/", 1); + status = run_client_and_mock_servers_loops(tb, num_requests_sent, + handler_ctx, tb->pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + CuAssertTrue(tc, tb->result_flags & TEST_RESULT_AUTHNCB_CALLED); + Verify(tb->mh) + CuAssertTrue(tc, VerifyAllExpectationsOk); + EndVerify + + CuAssertIntEquals(tc, 4, tdee->all_count); + CuAssertIntEquals(tc, 0, tdum->all_count); + + status = serf_authn_unregister_scheme(tb->context, + typedum, tdum->name, tb->pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); + status = serf_authn_unregister_scheme(tb->context, + typedee, tdee->name, tb->pool); + CuAssertIntEquals(tc, APR_SUCCESS, status); +} + +static void test_user_authentication(CuTest *tc) +{ + user_authentication(tc, 0 /* don't close connection */); +} + +static void test_user_authentication_keepalive_off(CuTest *tc) +{ + user_authentication(tc, 1); +} + + + /*****************************************************************************/ CuSuite *test_auth(void) { @@ -635,5 +852,8 @@ CuSuite *test_auth(void) SUITE_ADD_TEST(suite, test_authn_register_two); SUITE_ADD_TEST(suite, test_authn_register_twice); SUITE_ADD_TEST(suite, test_authn_unregister_unknown); + SUITE_ADD_TEST(suite, test_user_authentication); + /* SUITE_ADD_TEST(suite, test_user_authentication_keepalive_off); */ + return suite; } Modified: serf/branches/user-defined-authn/test/test_util.c URL: http://svn.apache.org/viewvc/serf/branches/user-defined-authn/test/test_util.c?rev=1926453&r1=1926452&r2=1926453&view=diff ============================================================================== --- serf/branches/user-defined-authn/test/test_util.c (original) +++ serf/branches/user-defined-authn/test/test_util.c Mon Jun 16 03:19:00 2025 @@ -427,17 +427,18 @@ setup_test_context(test_baton_t *tb, apr serf_log_output_t *output; apr_status_t status = APR_SUCCESS; - if (!tb->context) + if (!tb->context) { tb->context = serf_context_create(pool); - if (TEST_VERBOSE) { - status = serf_logging_create_stream_output(&output, tb->context, - SERF_LOG_DEBUG, - SERF_LOGCOMP_ALL, - SERF_LOG_DEFAULT_LAYOUT, - stderr, pool); - if (status == APR_SUCCESS) - status = serf_logging_add_output(tb->context, output); + if (TEST_VERBOSE) { + status = serf_logging_create_stream_output(&output, tb->context, + SERF_LOG_DEBUG, + SERF_LOGCOMP_ALL, + SERF_LOG_DEFAULT_LAYOUT, + stderr, pool); + if (status == APR_SUCCESS) + status = serf_logging_add_output(tb->context, output); + } } return status;