blob: 5a96ba5f2a60d96a96226690363140fac30411ee [file]
/* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#include <apr_strings.h>
#include "serf.h"
#include "test_serf.h"
static apr_status_t
authn_callback_expect_not_called(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;
tb->result_flags |= TEST_RESULT_AUTHNCB_CALLED;
/* Should not have been called. */
return REPORT_TEST_SUITE_ERROR();
}
/* Tests that authn fails if all authn schemes are disabled. */
static void test_authentication_disabled(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
handler_baton_t handler_ctx[1];
apr_status_t status;
/* 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, SERF_AUTHN_NONE);
serf_config_credentials_callback(tb->context,
authn_callback_expect_not_called);
Given(tb->mh)
GETRequest(URLEqualTo("/"), ChunkedBodyEqualTo("1"))
Respond(WithCode(401), WithChunkedBody("1"),
WithHeader("WWW-Authenticate", "Basic realm=\"Test Suite\""))
EndGiven
create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
status = run_client_and_mock_servers_loops(tb, 1,
handler_ctx, tb->pool);
Verify(tb->mh)
CuAssertTrue(tc, VerifyAllRequestsReceived);
EndVerify
CuAssertIntEquals(tc, SERF_ERROR_AUTHN_NOT_SUPPORTED, status);
CuAssertTrue(tc, !(tb->result_flags & TEST_RESULT_AUTHNCB_CALLED));
}
/* Tests that authn fails if encountered an unsupported scheme. */
static void test_unsupported_authentication(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
handler_baton_t handler_ctx[1];
apr_status_t status;
/* 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, SERF_AUTHN_ALL);
serf_config_credentials_callback(tb->context,
authn_callback_expect_not_called);
Given(tb->mh)
GETRequest(URLEqualTo("/"), ChunkedBodyEqualTo("1"))
Respond(WithCode(401), WithChunkedBody("1"),
WithHeader("WWW-Authenticate",
"NotExistent realm=\"Test Suite\""))
EndGiven
create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
status = run_client_and_mock_servers_loops(tb, 1,
handler_ctx, tb->pool);
Verify(tb->mh)
CuAssertTrue(tc, VerifyAllRequestsReceived);
EndVerify
CuAssertIntEquals(tc, SERF_ERROR_AUTHN_NOT_SUPPORTED, status);
CuAssertTrue(tc, !(tb->result_flags & TEST_RESULT_AUTHNCB_CALLED));
}
static apr_status_t
basic_authn_callback(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;
tb->result_flags |= TEST_RESULT_AUTHNCB_CALLED;
if (code != 401)
return REPORT_TEST_SUITE_ERROR();
if (strcmp("Basic", authn_type) != 0)
return REPORT_TEST_SUITE_ERROR();
if (strcmp(apr_psprintf(pool, "<http://localhost:%d> Test Suite",
tb->serv_port), realm) != 0)
return REPORT_TEST_SUITE_ERROR();
*username = "serf";
*password = "serftest";
return APR_SUCCESS;
}
/* Test template, used for KeepAlive Off and KeepAlive On test */
static void basic_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;
/* 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, SERF_AUTHN_BASIC);
serf_config_credentials_callback(tb->context, basic_authn_callback);
/* Test that a request is retried and authentication headers are set
correctly. */
num_requests_sent = 1;
/* c2VyZjpzZXJmdGVzdA== is base64 encoded serf:serftest . */
/* 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(401),WithChunkedBody("1"),
WithHeader("www-Authenticate", "bAsIc realm=\"Test Suite\""),
OnConditionThat(close_conn, WithConnectionCloseHeader))
GETRequest(URLEqualTo("/"),
HeaderEqualTo("Authorization", "Basic c2VyZjpzZXJmdGVzdA=="))
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
/* Test that credentials were cached by asserting that the authn callback
wasn't called again. */
Given(tb->mh)
GETRequest(URLEqualTo("/"),
HeaderEqualTo("Authorization", "Basic c2VyZjpzZXJmdGVzdA=="))
Respond(WithCode(200), WithChunkedBody(""))
Expect
AllRequestsReceivedInOrder
EndGiven
tb->result_flags = 0;
create_new_request(tb, &handler_ctx[0], "GET", "/", 2);
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
}
static void test_basic_authentication(CuTest *tc)
{
basic_authentication(tc, 0 /* don't close connection */);
}
static void test_basic_authentication_keepalive_off(CuTest *tc)
{
basic_authentication(tc, 1);
}
static apr_status_t
digest_authn_callback(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;
tb->result_flags |= TEST_RESULT_AUTHNCB_CALLED;
if (code != 401)
return REPORT_TEST_SUITE_ERROR();
if (strcmp("Digest", authn_type) != 0)
return REPORT_TEST_SUITE_ERROR();
if (strcmp(apr_psprintf(pool, "<http://localhost:%d> Test Suite",
tb->serv_port), realm) != 0)
return REPORT_TEST_SUITE_ERROR();
*username = "serf";
*password = "serftest";
return APR_SUCCESS;
}
/* Test template, used for KeepAlive Off and KeepAlive On test */
static void digest_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;
/* 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);
/* Add both Basic and Digest here, should use Digest only. */
serf_config_authn_types(tb->context, SERF_AUTHN_BASIC | SERF_AUTHN_DIGEST);
serf_config_credentials_callback(tb->context, digest_authn_callback);
create_new_request(tb, &handler_ctx[0], "GET", "/test/index.html", 1);
/* Test that a request is retried and authentication headers are set
correctly. */
num_requests_sent = 1;
/* Expected string relies on strict order of attributes of Digest, which are
not guaranteed.
6ff0d4cc201513ce970d5c6b25e1043b is encoded as:
md5hex(md5hex("serf:Test Suite:serftest") & ":" &
md5hex("ABCDEF1234567890") & ":" &
md5hex("GET:/test/index.html"))
*/
Given(tb->mh)
GETRequest(URLEqualTo("/test/index.html"), HeaderNotSet("Authorization"))
Respond(WithCode(401), WithChunkedBody("1"),
WithHeader("www-Authenticate", "Digest realm=\"Test Suite\","
"nonce=\"ABCDEF1234567890\",opaque=\"myopaque\","
"algorithm=\"MD5\",qop-options=\"auth\""),
OnConditionThat(close_conn, WithConnectionCloseHeader))
GETRequest(URLEqualTo("/test/index.html"),
HeaderEqualTo("Authorization", "Digest realm=\"Test Suite\", "
"username=\"serf\", nonce=\"ABCDEF1234567890\", "
"uri=\"/test/index.html\", "
"response=\"6ff0d4cc201513ce970d5c6b25e1043b\", "
"opaque=\"myopaque\", algorithm=\"MD5\""))
Respond(WithCode(200),WithChunkedBody(""))
Expect
AllRequestsReceivedInOrder
EndGiven
status = run_client_and_mock_servers_loops(tb, num_requests_sent,
handler_ctx, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
CuAssertIntEquals(tc, num_requests_sent, tb->handled_requests->nelts);
Verify(tb->mh)
CuAssertTrue(tc, VerifyAllExpectationsOk);
EndVerify
CuAssertTrue(tc, tb->result_flags & TEST_RESULT_AUTHNCB_CALLED);
}
static void test_digest_authentication(CuTest *tc)
{
digest_authentication(tc, 0 /* don't close connection */);
}
static void test_digest_authentication_keepalive_off(CuTest *tc)
{
/* Add the Connection: close header to the response with the Digest headers.
This to test that the Digest headers will be added to the retry of the
request on the new connection. */
digest_authentication(tc, 1);
}
static void digest_check_parameters(CuTest *tc,
apr_status_t expected_status,
const char *alg,
const char *qop)
{
test_baton_t *tb = tc->testBaton;
handler_baton_t handler_ctx[2];
const char *hdr;
apr_status_t status;
/* fprintf(stderr, "alg=%s qop=%s\n", alg, qop); */
/* 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, SERF_AUTHN_DIGEST);
serf_config_credentials_callback(tb->context, digest_authn_callback);
create_new_request(tb, &handler_ctx[0], "GET", "/", 1);
/* Construct the response header. */
alg = alg ? apr_psprintf(tb->pool, ",algorithm=\"%s\"", alg) : "";
qop = qop ? apr_psprintf(tb->pool, ",qop=\"%s\"", qop) : "";
hdr = apr_psprintf(tb->pool, "Digest realm=\"Test Suite\","
"nonce=\"ABCDEF1234567890\",opaque=\"myopaque\""
"%s%s", alg, qop);
/* fprintf(stderr, "hdr=%s\n", hdr); */
Given(tb->mh)
GETRequest(URLEqualTo("/"), HeaderNotSet("Authorization"))
Respond(WithCode(401), WithChunkedBody("1"),
WithHeader("www-Authenticate", hdr))
GETRequest(URLEqualTo("/"), HeaderSet("Authorization"))
Respond(WithCode(200), WithChunkedBody(""))
Expect
AllRequestsReceivedInOrder
EndGiven
status = run_client_and_mock_servers_loops(tb, 1, handler_ctx, tb->pool);
CuAssertIntEquals(tc, expected_status, status);
}
static void test_digest_valid_params(CuTest *tc)
{
digest_check_parameters(tc, APR_SUCCESS, NULL, NULL);
digest_check_parameters(tc, APR_SUCCESS, "MD5", NULL);
digest_check_parameters(tc, APR_SUCCESS, NULL, "auth");
digest_check_parameters(tc, APR_SUCCESS, "MD5", "auth");
digest_check_parameters(tc, APR_SUCCESS, "MD5", "auth-int auth");
digest_check_parameters(tc, APR_SUCCESS, "MD5", "crumple auth auth-int");
}
static void test_digest_invalid_params(CuTest *tc)
{
static const apr_status_t expected = SERF_ERROR_AUTHN_NOT_SUPPORTED;
digest_check_parameters(tc, expected, "", NULL);
digest_check_parameters(tc, expected, "MD5-sess", NULL);
digest_check_parameters(tc, expected, "SHA-256", NULL);
digest_check_parameters(tc, expected, "SHA-256-sess", NULL);
digest_check_parameters(tc, expected, "SHA-512-256", NULL);
digest_check_parameters(tc, expected, "SHA-512-256-sess", NULL);
digest_check_parameters(tc, expected, NULL, "");
digest_check_parameters(tc, expected, NULL, "auth-int");
digest_check_parameters(tc, expected, NULL, "crumple");
digest_check_parameters(tc, expected, NULL, "crumple auth-int");
}
static apr_status_t
switched_realm_authn_callback(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 *exp_realm = tb->user_baton;
tb->result_flags |= TEST_RESULT_AUTHNCB_CALLED;
if (code != 401)
return REPORT_TEST_SUITE_ERROR();
if (strcmp(exp_realm, realm) != 0)
return REPORT_TEST_SUITE_ERROR();
if (strcmp(apr_psprintf(pool, "<http://localhost:%d> Test Suite",
tb->serv_port), realm) == 0) {
*username = "serf";
*password = "serftest";
} else {
*username = "serf_newrealm";
*password = "serftest";
}
return APR_SUCCESS;
}
/* Test template, used for both Basic and Digest switch realms test */
static void authentication_switch_realms(CuTest *tc,
const char *scheme,
const char *authn_attr,
const char *authz_attr_test_suite,
const char *authz_attr_wrong_realm,
const char *authz_attr_new_realm)
{
test_baton_t *tb = tc->testBaton;
handler_baton_t handler_ctx[2];
int num_requests_sent;
apr_status_t status;
const char *exp_authz_test_suite = apr_psprintf(tb->pool, "%s %s", scheme,
authz_attr_test_suite);
const char *exp_authz_wrong_realm = apr_psprintf(tb->pool, "%s %s", scheme,
authz_attr_wrong_realm);
const char *exp_authz_new_realm = apr_psprintf(tb->pool, "%s %s", scheme,
authz_attr_new_realm);
/* 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, SERF_AUTHN_BASIC | SERF_AUTHN_DIGEST);
serf_config_credentials_callback(tb->context,
switched_realm_authn_callback);
/* Test that a request is retried and authentication headers are set
correctly. */
tb->user_baton = apr_psprintf(tb->pool, "<http://localhost:%d> Test Suite",
mhServerPortNr(tb->mh));
num_requests_sent = 1;
Given(tb->mh)
GETRequest(URLEqualTo("/"), ChunkedBodyEqualTo("1"),
HeaderNotSet("Authorization"))
Respond(WithCode(401), WithChunkedBody("1"),
WithHeader("WWW-Authenticate",
apr_psprintf(tb->pool, "%s realm=\"Test Suite\"%s",
scheme, authn_attr)))
GETRequest(URLEqualTo("/"), ChunkedBodyEqualTo("1"),
HeaderEqualTo("Authorization", exp_authz_test_suite))
Respond(WithCode(200), WithChunkedBody(""))
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);
CuAssertIntEquals(tc, num_requests_sent, tb->handled_requests->nelts);
Verify(tb->mh)
CuAssertTrue(tc, VerifyAllRequestsReceivedInOrder);
EndVerify
CuAssertTrue(tc, tb->result_flags & TEST_RESULT_AUTHNCB_CALLED);
/* Test that credentials were cached by asserting that the authn callback
wasn't called again. */
tb->result_flags = 0;
Given(tb->mh)
GETRequest(URLEqualTo("/"), ChunkedBodyEqualTo("2"),
HeaderEqualTo("Authorization", exp_authz_test_suite))
Respond(WithCode(200), WithChunkedBody(""))
EndGiven
create_new_request(tb, &handler_ctx[0], "GET", "/", 2);
status = run_client_and_mock_servers_loops(tb, num_requests_sent,
handler_ctx, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
Verify(tb->mh)
CuAssertTrue(tc, VerifyAllRequestsReceivedInOrder);
EndVerify
CuAssertTrue(tc, !(tb->result_flags & TEST_RESULT_AUTHNCB_CALLED));
/* Switch realms. Test that serf asks the application for new
credentials. */
tb->result_flags = 0;
tb->user_baton = apr_psprintf(tb->pool, "<http://localhost:%d> New Realm",
mhServerPortNr(tb->mh));
Given(tb->mh)
GETRequest(URLEqualTo("/newrealm/index.html"), ChunkedBodyEqualTo("3"),
HeaderEqualTo("Authorization", exp_authz_wrong_realm))
Respond(WithCode(401), WithChunkedBody("1"),
WithHeader("WWW-Authenticate",
apr_psprintf(tb->pool, "%s realm=\"New Realm\"%s",
scheme, authn_attr)))
GETRequest(URLEqualTo("/newrealm/index.html"), ChunkedBodyEqualTo("3"),
HeaderEqualTo("Authorization", exp_authz_new_realm))
Respond(WithCode(200), WithChunkedBody(""))
EndGiven
create_new_request(tb, &handler_ctx[0], "GET", "/newrealm/index.html", 3);
status = run_client_and_mock_servers_loops(tb, num_requests_sent,
handler_ctx, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
Verify(tb->mh)
CuAssertTrue(tc, VerifyAllRequestsReceivedInOrder);
EndVerify
CuAssertTrue(tc, tb->result_flags & TEST_RESULT_AUTHNCB_CALLED);
}
static void test_basic_switch_realms(CuTest *tc)
{
authentication_switch_realms(tc, "Basic", "", "c2VyZjpzZXJmdGVzdA==",
"c2VyZjpzZXJmdGVzdA==",
"c2VyZl9uZXdyZWFsbTpzZXJmdGVzdA==");
}
static void test_digest_switch_realms(CuTest *tc)
{
authentication_switch_realms(tc, "Digest", ",nonce=\"ABCDEF1234567890\","
"opaque=\"myopaque\", algorithm=\"MD5\",qop-options=\"auth\"",
/* response hdr attribute for Test Suite realm, uri / */
"realm=\"Test Suite\", username=\"serf\", nonce=\"ABCDEF1234567890\", "
"uri=\"/\", response=\"3511a71fec5c02ab1c9212711a8baa58\", "
"opaque=\"myopaque\", algorithm=\"MD5\"",
/* response hdr attribute for Test Suite realm, uri /newrealm/index.html */
"realm=\"Test Suite\", username=\"serf\", nonce=\"ABCDEF1234567890\", "
"uri=\"/newrealm/index.html\", response=\"c6b673cf44ad16ef379930856b607344\", "
"opaque=\"myopaque\", algorithm=\"MD5\"",
/* response hdr attribute for New Realm realm, uri /newrealm/index.html */
"realm=\"New Realm\", username=\"serf_newrealm\", nonce=\"ABCDEF1234567890\", "
"uri=\"/newrealm/index.html\", response=\"f93f07d1412e53c421f66741a89198cb\", "
"opaque=\"myopaque\", algorithm=\"MD5\"");
}
static void test_auth_on_HEAD(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
handler_baton_t handler_ctx[1];
int num_requests_sent;
apr_status_t status;
/* 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, SERF_AUTHN_BASIC);
serf_config_credentials_callback(tb->context, basic_authn_callback);
/* Test that a request is retried and authentication headers are set
correctly. */
num_requests_sent = 1;
Given(tb->mh)
HEADRequest(URLEqualTo("/"), BodyEqualTo(""),
HeaderNotSet("Authorization"))
Respond(WithCode(401), WithBody(""),
WithHeader("WWW-Authenticate", "Basic Realm=\"Test Suite\""))
HEADRequest(URLEqualTo("/"), BodyEqualTo(""),
HeaderEqualTo("Authorization", "Basic c2VyZjpzZXJmdGVzdA=="))
Respond(WithCode(200), WithBody(""))
EndGiven
create_new_request(tb, &handler_ctx[0], "HEAD", "/", -1);
status = run_client_and_mock_servers_loops(tb, num_requests_sent,
handler_ctx, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
CuAssertIntEquals(tc, num_requests_sent, tb->handled_requests->nelts);
Verify(tb->mh)
CuAssertTrue(tc, VerifyAllRequestsReceivedInOrder);
EndVerify
CuAssertTrue(tc, tb->result_flags & TEST_RESULT_AUTHNCB_CALLED);
}
static void test_authn_register_one(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
void *const baton = (void *)0xdeadbeef;
apr_status_t status;
int type;
status = setup_test_context(tb, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
/* Register an authentication scheme */
status = serf_authn_register_scheme(&type, tb->context,
"Fizzle", baton,
SERF_AUTHN_FLAG_NONE,
NULL, NULL, NULL, NULL, NULL,
tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
CuAssertTrue(tc, type != SERF_AUTHN_NONE);
/* Unregister the scheme */
status = serf_authn_unregister_scheme(tb->context,
type, "fiZzlE", tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
}
static void test_authn_register_two(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
void *const baton1 = (void *)0xdeadbeef;
void *const baton2 = (void *)0xbadf00d;
apr_status_t status;
int type1, type2;
status = setup_test_context(tb, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
/* Register the schemes */
status = serf_authn_register_scheme(&type1, tb->context,
"Tweedledee", baton1,
SERF_AUTHN_FLAG_NONE,
NULL, NULL, NULL, NULL, NULL,
tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
CuAssertTrue(tc, type1 != SERF_AUTHN_NONE);
status = serf_authn_register_scheme(&type2, tb->context,
"Tweedledum", baton2,
SERF_AUTHN_FLAG_NONE,
NULL, NULL, NULL, NULL, NULL,
tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
CuAssertTrue(tc, type2 != SERF_AUTHN_NONE);
CuAssertTrue(tc, type2 != type1);
/* Unregister the schemes */
status =serf_authn_unregister_scheme(tb->context,
type1, "tweedleDee", tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
status =serf_authn_unregister_scheme(tb->context,
type2, "tweedleDum", tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
}
static void test_authn_register_twice(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
void *const baton = (void *)0xdeadbeef;
apr_status_t status;
int type, epyt;
status = setup_test_context(tb, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
/* Register an authentication scheme */
status = serf_authn_register_scheme(&type, tb->context,
"Tweens", baton,
SERF_AUTHN_FLAG_NONE,
NULL, NULL, NULL, NULL, NULL,
tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
CuAssertTrue(tc, type != SERF_AUTHN_NONE);
status = serf_authn_register_scheme(&epyt, tb->context,
"Tweens", baton,
SERF_AUTHN_FLAG_NONE,
NULL, NULL, NULL, NULL, NULL,
tb->pool);
CuAssertIntEquals(tc, APR_EEXIST, status);
CuAssertTrue(tc, epyt == SERF_AUTHN_NONE);
/* Unregister the scheme */
status =serf_authn_unregister_scheme(tb->context,
type, "Tweens", tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
}
static void test_authn_unregister_unknown(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
apr_status_t status;
status = setup_test_context(tb, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
/* Unregister the scheme */
status = serf_authn_unregister_scheme(tb->context,
1 << 15, "Neverland", tb->pool);
CuAssertIntEquals(tc, APR_ENOENT, status);
}
static void test_authn_registered_pool_cleanup(CuTest *tc)
{
test_baton_t *tb = tc->testBaton;
void *const baton = (void *)0xdeadbeef;
apr_pool_t *scheme_pool;
apr_status_t status;
int type;
status = setup_test_context(tb, tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
/* Create a pool for the new scheme. */
apr_pool_create(&scheme_pool, tb->pool);
CuAssertTrue(tc, scheme_pool != NULL);
/* Register an authentication scheme */
status = serf_authn_register_scheme(&type, tb->context,
"Killed", baton,
SERF_AUTHN_FLAG_NONE,
NULL, NULL, NULL, NULL, NULL,
scheme_pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
CuAssertTrue(tc, type != SERF_AUTHN_NONE);
/* Destroy the pool. Its cleanup function should unregister the scheme. */
apr_pool_destroy(scheme_pool);
/* Try to unregister the scheme; this should fail. */
status = serf_authn_unregister_scheme(tb->context,
type, "Killed", tb->pool);
CuAssertIntEquals(tc, APR_ENOENT, status);
}
typedef struct user_authn_baton user_authn_t;
struct user_authn_baton {
CuTest *tc;
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;
};
#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(CuTest *tc, const char* name,
apr_pool_t *pool)
{
user_authn_t *baton = apr_pcalloc(pool, sizeof(*baton));
baton->tc = tc;
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 **authn_baton,
void *baton, int code,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
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_get_realm(const char **realm_name,
void *baton,
void *authn_baton,
const char *authn_header,
apr_hash_t *authn_parameters,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *name;
USER_AUTHN_COUNT(baton, get_realm);
test__log(TEST_VERBOSE, __FILE__,
"user_authn_get_realm, header %s\n", authn_header);
if (strncasecmp(authn_header, "TweedleDee", 10)) {
*realm_name = "Wonderland";
return APR_SUCCESS;
}
name = apr_hash_get(authn_parameters, "scope", 5);
if (!name)
return SERF_ERROR_AUTHN_MISSING_ATTRIBUTE;
*realm_name = apr_pstrdup(result_pool, name);
return APR_SUCCESS;
}
static apr_status_t user_authn_handle(int *reset_pipelining,
void *baton,
void *authn_baton,
int code,
const char *authn_header,
apr_hash_t *authn_parameters,
const char *response_header,
const char *username,
const char *password,
serf_request_t *request,
serf_bucket_t *response,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
user_authn_baton_t *const ab = authn_baton;
USER_AUTHN_COUNT(baton, handle);
CuAssertTrue(b->tc, username == NULL);
ab->header = apr_pstrdup(result_pool, response_header);
ab->value = apr_pstrcat(result_pool, b->name, " ", password, NULL);
*reset_pipelining = 1;
return APR_SUCCESS;
}
static apr_status_t user_authn_setup_request(int *reset_pipelining,
void *baton,
void *authn_baton,
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_baton_t *const ab = authn_baton;
USER_AUTHN_COUNT(baton, setup_request);
CuAssertPtrNotNull(b->tc, authn_baton);
CuAssertPtrNotNull(b->tc, ab->header);
CuAssertPtrNotNull(b->tc, 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);
*reset_pipelining = 1;
return APR_SUCCESS;
}
static apr_status_t user_authn_validate_response(int *reset_pipelining,
void *baton,
void *authn_baton,
int code,
serf_connection_t *conn,
apr_hash_t *authn_params,
serf_request_t *request,
serf_bucket_t *response,
apr_pool_t *scratch_pool)
{
const char *knight;
USER_AUTHN_COUNT(baton, validate_response);
CuAssertPtrNotNull(b->tc, authn_params);
knight = apr_hash_get(authn_params, "white", 5);
CuAssertPtrNotNull(b->tc, knight);
test__log(TEST_VERBOSE, __FILE__,
"user_authn_validate_response, info white=%s\n",
knight);
CuAssertStrEquals(b->tc, "Knight", knight);
*reset_pipelining = 1;
return APR_SUCCESS;
}
#undef USER_AUTHN_COUNT
static apr_status_t
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;
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();
realm_name = strrchr(realm, ' ');
if (!realm_name
|| (strcmp(" Alice", realm_name)
&& strcmp(" Cheshire", realm_name)))
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_name + 1, *password);
return APR_SUCCESS;
}
static void user_authentication(CuTest *tc,
int close_conn,
int use_pipelining,
const char *scope)
{
test_baton_t *tb = tc->testBaton;
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(tc, "TweedleDee", tb->pool);
user_authn_t *const tdum = user_authn_make_baton(tc, "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(&typedee, 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,
tb->pool);
CuAssertIntEquals(tc, APR_SUCCESS, status);
CuAssertTrue(tc, typedee != SERF_AUTHN_NONE);
status = serf_authn_register_scheme(&typedum, 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,
tb->pool);
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);
/* Adjust the authentication header. */
if (scope)
hdr_value = apr_pstrcat(tb->pool, hdr_value,
", scope=", scope, 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", hdr_value),
OnConditionThat(close_conn, WithConnectionCloseHeader))
GETRequest(URLEqualTo("/"),
HeaderEqualTo("Authorization", "TweedleDee TweedleDee"))
Respond(WithCode(200),
WithHeader("Authentication-Info", "White=Knight"),
WithChunkedBody(""))
Expect
AllRequestsReceivedInOrder
EndGiven
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);
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
/* 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);
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 */,
1 /* allow pipelining during authn */,
0 /* no custom scope */);
}
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 /* close connection */,
1 /* allow pipelining during authn */,
0 /* no custom scope */);
}
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 custom scope */);
}
/*****************************************************************************/
CuSuite *test_auth(void)
{
CuSuite *suite = CuSuiteNew();
CuSuiteSetSetupTeardownCallbacks(suite, test_setup, test_teardown);
SUITE_ADD_TEST(suite, test_authentication_disabled);
SUITE_ADD_TEST(suite, test_unsupported_authentication);
SUITE_ADD_TEST(suite, test_basic_authentication);
SUITE_ADD_TEST(suite, test_basic_authentication_keepalive_off);
SUITE_ADD_TEST(suite, test_digest_authentication);
SUITE_ADD_TEST(suite, test_digest_authentication_keepalive_off);
SUITE_ADD_TEST(suite, test_digest_valid_params);
SUITE_ADD_TEST(suite, test_digest_invalid_params);
SUITE_ADD_TEST(suite, test_basic_switch_realms);
SUITE_ADD_TEST(suite, test_digest_switch_realms);
SUITE_ADD_TEST(suite, test_auth_on_HEAD);
SUITE_ADD_TEST(suite, test_authn_register_one);
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_authn_registered_pool_cleanup);
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;
}