mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
When NetworkService is enabled requests created using CefFrame::CreateURLRequest will call CefRequestHandler::GetAuthCredentials for the associated browser after calling CefURLRequestClient::GetAuthCredentials if that call returns false.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "include/base/cef_bind.h"
|
||||
#include "include/cef_parser.h"
|
||||
#include "include/cef_request_context_handler.h"
|
||||
#include "include/cef_scheme.h"
|
||||
#include "include/cef_server.h"
|
||||
@@ -65,6 +66,7 @@ enum RequestTestMode {
|
||||
REQTEST_GET_REDIRECT_STOP,
|
||||
REQTEST_GET_REDIRECT_LOCATION,
|
||||
REQTEST_GET_REFERRER,
|
||||
REQTEST_GET_AUTH,
|
||||
REQTEST_POST,
|
||||
REQTEST_POST_FILE,
|
||||
REQTEST_POST_WITHPROGRESS,
|
||||
@@ -144,6 +146,16 @@ struct RequestRunSettings {
|
||||
// If true the response cookie should be saved.
|
||||
bool expect_save_cookie = false;
|
||||
|
||||
// If true the test will begin by requiring Basic authentication and then
|
||||
// continue with the actual request. The UR_FLAG_ALLOW_STORED_CREDENTIALS
|
||||
// flag must be set on the request. When using the global request context
|
||||
// CefRequestContext::ClearHttpAuthCredentials should be called to avoid
|
||||
// leaking state across test runs. Authentication is only supported with
|
||||
// browser-initiated requests and the server backend.
|
||||
bool expect_authentication = false;
|
||||
std::string username;
|
||||
std::string password;
|
||||
|
||||
// If specified the test will begin with this redirect request and response.
|
||||
CefRefPtr<CefRequest> redirect_request;
|
||||
CefRefPtr<CefResponse> redirect_response;
|
||||
@@ -266,6 +278,24 @@ class RequestDataMap {
|
||||
RedirectDataMap redirect_data_map_;
|
||||
};
|
||||
|
||||
class TestCompletionCallback : public CefCompletionCallback {
|
||||
public:
|
||||
explicit TestCompletionCallback(const base::Closure& complete_callback)
|
||||
: complete_callback_(complete_callback) {
|
||||
EXPECT_FALSE(complete_callback_.is_null());
|
||||
}
|
||||
|
||||
void OnComplete() override {
|
||||
complete_callback_.Run();
|
||||
complete_callback_.Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
base::Closure complete_callback_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(TestCompletionCallback);
|
||||
};
|
||||
|
||||
std::string GetRequestScheme(bool server_backend) {
|
||||
return server_backend ? kRequestSchemeServer : kRequestSchemeCustom;
|
||||
}
|
||||
@@ -472,6 +502,47 @@ void GetNormalResponse(const RequestRunSettings* settings,
|
||||
response->SetHeaderMap(headerMap);
|
||||
}
|
||||
|
||||
// Based on https://en.wikipedia.org/wiki/Basic_access_authentication#Protocol
|
||||
void GetAuthResponse(CefRefPtr<CefResponse> response) {
|
||||
response->SetStatus(401);
|
||||
response->SetStatusText("Unauthorized");
|
||||
response->SetMimeType("text/html");
|
||||
|
||||
CefResponse::HeaderMap headerMap;
|
||||
headerMap.insert(
|
||||
std::make_pair("WWW-Authenticate", "Basic realm=\"Test Realm\""));
|
||||
response->SetHeaderMap(headerMap);
|
||||
}
|
||||
|
||||
bool IsAuthorized(CefRefPtr<CefRequest> request,
|
||||
const std::string& username,
|
||||
const std::string& password) {
|
||||
const std::string& authHeader = request->GetHeaderByName("Authorization");
|
||||
if (authHeader.empty())
|
||||
return false;
|
||||
|
||||
if (authHeader.find("Basic ") == 0) {
|
||||
const std::string& base64 = authHeader.substr(6);
|
||||
CefRefPtr<CefBinaryValue> data = CefBase64Decode(base64);
|
||||
EXPECT_TRUE(data);
|
||||
if (!data) {
|
||||
LOG(ERROR) << "Failed to decode Authorization value: " << base64;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string decoded;
|
||||
decoded.resize(data->GetSize());
|
||||
data->GetData(&decoded[0], data->GetSize(), 0);
|
||||
|
||||
const std::string& expected = username + ":" + password;
|
||||
EXPECT_STREQ(expected.c_str(), decoded.c_str());
|
||||
return decoded == expected;
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Unexpected Authorization value: " << authHeader;
|
||||
return false;
|
||||
}
|
||||
|
||||
// SCHEME HANDLER BACKEND
|
||||
|
||||
// Serves request responses.
|
||||
@@ -1011,6 +1082,14 @@ class RequestServerHandler : public CefServerHandler {
|
||||
CefRefPtr<CefRequest> request) {
|
||||
RequestDataMap::Entry entry = data_map_.Find(request->GetURL());
|
||||
if (entry.type == RequestDataMap::Entry::TYPE_NORMAL) {
|
||||
const bool needs_auth = entry.settings->expect_authentication &&
|
||||
!IsAuthorized(request, entry.settings->username,
|
||||
entry.settings->password);
|
||||
if (needs_auth) {
|
||||
HandleAuthRequest(server, connection_id, request);
|
||||
return;
|
||||
}
|
||||
|
||||
HandleNormalRequest(server, connection_id, request, entry.settings);
|
||||
} else if (entry.type == RequestDataMap::Entry::TYPE_REDIRECT) {
|
||||
HandleRedirectRequest(server, connection_id, request,
|
||||
@@ -1022,6 +1101,14 @@ class RequestServerHandler : public CefServerHandler {
|
||||
}
|
||||
}
|
||||
|
||||
void HandleAuthRequest(CefRefPtr<CefServer> server,
|
||||
int connection_id,
|
||||
CefRefPtr<CefRequest> request) {
|
||||
CefRefPtr<CefResponse> response = CefResponse::Create();
|
||||
GetAuthResponse(response);
|
||||
SendResponse(server, connection_id, response, std::string());
|
||||
}
|
||||
|
||||
void HandleNormalRequest(CefRefPtr<CefServer> server,
|
||||
int connection_id,
|
||||
CefRefPtr<CefRequest> request,
|
||||
@@ -1189,6 +1276,11 @@ class RequestClient : public CefURLRequestClient {
|
||||
const CefString& realm,
|
||||
const CefString& scheme,
|
||||
CefRefPtr<CefAuthCallback> callback) override {
|
||||
auth_credentials_ct_++;
|
||||
if (has_authentication_) {
|
||||
callback->Continue(username_, password_);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1196,10 +1288,15 @@ class RequestClient : public CefURLRequestClient {
|
||||
const RequestCompleteCallback complete_callback_;
|
||||
|
||||
public:
|
||||
bool has_authentication_ = false;
|
||||
std::string username_;
|
||||
std::string password_;
|
||||
|
||||
int request_complete_ct_ = 0;
|
||||
int upload_progress_ct_ = 0;
|
||||
int download_progress_ct_ = 0;
|
||||
int download_data_ct_ = 0;
|
||||
int auth_credentials_ct_ = 0;
|
||||
|
||||
int64 upload_total_ = 0;
|
||||
int64 download_total_ = 0;
|
||||
@@ -1251,6 +1348,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe<RequestTestRunner> {
|
||||
REGISTER_TEST(REQTEST_GET_REDIRECT_LOCATION, SetupGetRedirectLocationTest,
|
||||
SingleRunTest);
|
||||
REGISTER_TEST(REQTEST_GET_REFERRER, SetupGetReferrerTest, SingleRunTest);
|
||||
REGISTER_TEST(REQTEST_GET_AUTH, SetupGetAuthTest, SingleRunTest);
|
||||
REGISTER_TEST(REQTEST_POST, SetupPostTest, SingleRunTest);
|
||||
REGISTER_TEST(REQTEST_POST_FILE, SetupPostFileTest, SingleRunTest);
|
||||
REGISTER_TEST(REQTEST_POST_WITHPROGRESS, SetupPostWithProgressTest,
|
||||
@@ -1530,6 +1628,28 @@ class RequestTestRunner : public base::RefCountedThreadSafe<RequestTestRunner> {
|
||||
complete_callback.Run();
|
||||
}
|
||||
|
||||
void SetupGetAuthTest(const base::Closure& complete_callback) {
|
||||
// Start with the normal get test.
|
||||
SetupGetTestShared();
|
||||
|
||||
// Require Basic authentication.
|
||||
settings_.expect_authentication = true;
|
||||
settings_.username = "user";
|
||||
settings_.password = "pass";
|
||||
|
||||
// This flag is required to support credentials, which means we'll also get
|
||||
// the cookies.
|
||||
settings_.request->SetFlags(UR_FLAG_ALLOW_STORED_CREDENTIALS);
|
||||
settings_.expect_save_cookie = true;
|
||||
settings_.expect_send_cookie = true;
|
||||
|
||||
// The authentication request will come first, then the actual request.
|
||||
settings_.expected_receive_count = 2;
|
||||
settings_.expected_send_count = 2;
|
||||
|
||||
complete_callback.Run();
|
||||
}
|
||||
|
||||
void SetupPostTestShared() {
|
||||
settings_.request = CefRequest::Create();
|
||||
settings_.request->SetURL(GetTestURL("PostTest.html"));
|
||||
@@ -2053,6 +2173,16 @@ class RequestTestRunner : public base::RefCountedThreadSafe<RequestTestRunner> {
|
||||
EXPECT_TRUE(request.get());
|
||||
|
||||
CefRefPtr<RequestClient> client = new RequestClient(complete_callback);
|
||||
|
||||
// Delegation to CefRequestHandler::GetAuthCredentials is only supported
|
||||
// with NetworkService.
|
||||
if ((!IsNetworkServiceEnabled() || !use_frame_method_) &&
|
||||
settings_.expect_authentication) {
|
||||
client->has_authentication_ = true;
|
||||
client->username_ = settings_.username;
|
||||
client->password_ = settings_.password;
|
||||
}
|
||||
|
||||
if (use_frame_method_) {
|
||||
EXPECT_TRUE(frame_);
|
||||
frame_->CreateURLRequest(request, client.get());
|
||||
@@ -2123,6 +2253,12 @@ class RequestTestRunner : public base::RefCountedThreadSafe<RequestTestRunner> {
|
||||
EXPECT_EQ(0, client->download_data_ct_);
|
||||
EXPECT_TRUE(client->download_data_.empty());
|
||||
}
|
||||
|
||||
if (settings_.expect_authentication) {
|
||||
EXPECT_EQ(1, client->auth_credentials_ct_);
|
||||
} else {
|
||||
EXPECT_EQ(0, client->auth_credentials_ct_);
|
||||
}
|
||||
}
|
||||
|
||||
// Run a test with a single request.
|
||||
@@ -2530,7 +2666,7 @@ class RequestTestHandler : public TestHandler {
|
||||
// Continue the test once supported schemes has been set.
|
||||
request_context->GetCookieManager(NULL)->SetSupportedSchemes(
|
||||
supported_schemes, true,
|
||||
new SupportedSchemesCompletionCallback(
|
||||
new TestCompletionCallback(
|
||||
base::Bind(&RequestTestHandler::PreSetupComplete, this)));
|
||||
} else {
|
||||
PreSetupComplete();
|
||||
@@ -2603,6 +2739,25 @@ class RequestTestHandler : public TestHandler {
|
||||
return TestHandler::OnBeforeResourceLoad(browser, frame, request, callback);
|
||||
}
|
||||
|
||||
bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
|
||||
const CefString& origin_url,
|
||||
bool isProxy,
|
||||
const CefString& host,
|
||||
int port,
|
||||
const CefString& realm,
|
||||
const CefString& scheme,
|
||||
CefRefPtr<CefAuthCallback> callback) override {
|
||||
EXPECT_TRUE(test_in_browser_);
|
||||
EXPECT_TRUE(test_frame_method_);
|
||||
auth_credentials_ct_++;
|
||||
if (test_runner_->settings_.expect_authentication) {
|
||||
callback->Continue(test_runner_->settings_.username,
|
||||
test_runner_->settings_.password);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int httpStatusCode) override {
|
||||
@@ -2713,7 +2868,21 @@ class RequestTestHandler : public TestHandler {
|
||||
|
||||
// Shut down the browser side of the test.
|
||||
test_runner_->ShutdownTest(
|
||||
base::Bind(&RequestTestHandler::DestroyTest, this));
|
||||
base::Bind(&RequestTestHandler::MaybeClearAuthCredentials, this));
|
||||
}
|
||||
|
||||
void MaybeClearAuthCredentials() {
|
||||
if (test_runner_->settings_.expect_authentication &&
|
||||
context_mode_ == CONTEXT_GLOBAL) {
|
||||
// Clear the HTTP authentication cache to avoid leaking state between
|
||||
// test runs when using the global request context.
|
||||
test_runner_->GetRequestContext()->ClearHttpAuthCredentials(
|
||||
new TestCompletionCallback(
|
||||
base::Bind(&RequestTestHandler::DestroyTest, this)));
|
||||
return;
|
||||
}
|
||||
|
||||
DestroyTest();
|
||||
}
|
||||
|
||||
void DestroyTest() override {
|
||||
@@ -2730,6 +2899,16 @@ class RequestTestHandler : public TestHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// CefRequestHandler::GetAuthCredentials should be called after
|
||||
// CefURLRequestClient::GetAuthCredentials when the request has an
|
||||
// associated frame.
|
||||
if (IsNetworkServiceEnabled() && test_in_browser_ && test_frame_method_ &&
|
||||
test_runner_->settings_.expect_authentication) {
|
||||
EXPECT_EQ(1, auth_credentials_ct_);
|
||||
} else {
|
||||
EXPECT_EQ(0, auth_credentials_ct_);
|
||||
}
|
||||
|
||||
TestHandler::DestroyTest();
|
||||
|
||||
// For non-global contexts OnTestComplete() will be called when the
|
||||
@@ -2799,26 +2978,6 @@ class RequestTestHandler : public TestHandler {
|
||||
IMPLEMENT_REFCOUNTING(RequestContextHandler);
|
||||
};
|
||||
|
||||
// Continue the rest once supported schemes have been set.
|
||||
class SupportedSchemesCompletionCallback : public CefCompletionCallback {
|
||||
public:
|
||||
explicit SupportedSchemesCompletionCallback(
|
||||
const base::Closure& complete_callback)
|
||||
: complete_callback_(complete_callback) {
|
||||
EXPECT_FALSE(complete_callback_.is_null());
|
||||
}
|
||||
|
||||
void OnComplete() override {
|
||||
complete_callback_.Run();
|
||||
complete_callback_.Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
base::Closure complete_callback_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(SupportedSchemesCompletionCallback);
|
||||
};
|
||||
|
||||
const RequestTestMode test_mode_;
|
||||
const ContextTestMode context_mode_;
|
||||
const bool test_in_browser_;
|
||||
@@ -2841,6 +3000,7 @@ class RequestTestHandler : public TestHandler {
|
||||
TrackCallback got_message_;
|
||||
TrackCallback got_success_;
|
||||
|
||||
int auth_credentials_ct_ = 0;
|
||||
TrackCallback got_on_test_complete_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(RequestTestHandler);
|
||||
@@ -3007,7 +3167,7 @@ REQ_TEST_SET(WithFrame, true)
|
||||
|
||||
REQ_TEST_FRAME_SET()
|
||||
|
||||
// Cache tests can only be run with the server backend.
|
||||
// Cache and authentication tests can only be run with the server backend.
|
||||
#define REQ_TEST_CACHE_SET_EX(suffix, context_mode, test_frame_method) \
|
||||
REQ_TEST(BrowserGETCacheWithControl##suffix, REQTEST_CACHE_WITH_CONTROL, \
|
||||
context_mode, true, true, test_frame_method) \
|
||||
@@ -3039,6 +3199,8 @@ REQ_TEST_FRAME_SET()
|
||||
REQ_TEST(RendererGETCacheWithoutControl##suffix, \
|
||||
REQTEST_CACHE_WITHOUT_CONTROL, context_mode, false, true, \
|
||||
test_frame_method) \
|
||||
REQ_TEST(BrowserGETAuth##suffix, REQTEST_GET_AUTH, context_mode, true, true, \
|
||||
test_frame_method) \
|
||||
REQ_TEST(RendererGETCacheSkipFlag##suffix, REQTEST_CACHE_SKIP_FLAG, \
|
||||
context_mode, false, true, test_frame_method) \
|
||||
REQ_TEST(RendererGETCacheSkipHeader##suffix, REQTEST_CACHE_SKIP_HEADER, \
|
||||
|
Reference in New Issue
Block a user