#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/json/json_writer.h"
#include "base/process/launch.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "base/win/windows_version.h"
#include "chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "google_apis/gaia/gaia_id.h"
#include "google_apis/gaia/gaia_switches.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "services/network/public/cpp/network_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace credential_provider {
class GcpUsingChromeTest : public ::testing::Test {
public:
GcpUsingChromeTest(const GcpUsingChromeTest&) = delete;
GcpUsingChromeTest& operator=(const GcpUsingChromeTest&) = delete;
protected:
struct TestGoogleApiResponse {
TestGoogleApiResponse()
: info_code_(net::HTTP_BAD_REQUEST), response_given_(false) {}
TestGoogleApiResponse(net::HttpStatusCode info_code,
const std::string& data)
: info_code_(info_code), data_(data), response_given_(false) {}
net::HttpStatusCode info_code_;
std::string data_;
bool response_given_;
};
GcpUsingChromeTest() = default;
void SetUp() override;
void TearDown() override;
void SetPasswordForSignin(const std::string& password) {
test_data_storage_.SetSigninPassword(password);
}
void SetUserInfoResponse(TestGoogleApiResponse response) {
user_info_response_ = response;
}
void SetTokenInfoResponse(TestGoogleApiResponse response) {
token_info_response_ = response;
}
void SetMdmTokenResponse(TestGoogleApiResponse response) {
mdm_token_response_ = response;
}
void SetSigninTokenResponse(TestGoogleApiResponse response) {
signin_token_response_ = response;
}
std::string MakeInlineSigninCompletionScript(const std::string& email,
const std::string& password,
const GaiaId& gaia_id) const;
std::string RunChromeAndExtractOutput() const;
base::CommandLine GetCommandLineForChromeGls(
const base::FilePath& user_data_dir) const;
std::string RunProcessAndExtractOutput(
const base::CommandLine& command_line) const;
std::unique_ptr<net::test_server::HttpResponse> GaiaHtmlResponseHandler(
const net::test_server::HttpRequest& request);
std::unique_ptr<net::test_server::HttpResponse> GoogleApisHtmlResponseHandler(
const net::test_server::HttpRequest& request);
CredentialProviderSigninDialogTestDataStorage test_data_storage_;
net::test_server::EmbeddedTestServer gaia_server_;
net::test_server::EmbeddedTestServer google_apis_server_;
TestGoogleApiResponse signin_token_response_;
TestGoogleApiResponse user_info_response_;
TestGoogleApiResponse token_info_response_;
TestGoogleApiResponse mdm_token_response_;
};
void GcpUsingChromeTest::SetUp() {
gaia_server_.RegisterRequestHandler(base::BindRepeating(
&GcpUsingChromeTest::GaiaHtmlResponseHandler, base::Unretained(this)));
EXPECT_TRUE(gaia_server_.Start());
google_apis_server_.RegisterRequestHandler(
base::BindRepeating(&GcpUsingChromeTest::GoogleApisHtmlResponseHandler,
base::Unretained(this)));
EXPECT_TRUE(google_apis_server_.Start());
}
void GcpUsingChromeTest::TearDown() {
EXPECT_TRUE(gaia_server_.ShutdownAndWaitUntilComplete());
EXPECT_TRUE(google_apis_server_.ShutdownAndWaitUntilComplete());
}
std::string GcpUsingChromeTest::RunChromeAndExtractOutput() const {
base::ScopedTempDir user_data_dir;
EXPECT_TRUE(user_data_dir.CreateUniqueTempDir());
return RunProcessAndExtractOutput(
GetCommandLineForChromeGls(user_data_dir.GetPath()));
}
base::CommandLine GcpUsingChromeTest::GetCommandLineForChromeGls(
const base::FilePath& user_data_dir) const {
auto* process_command_line = base::CommandLine::ForCurrentProcess();
base::CommandLine command_line(
process_command_line->GetProgram().DirName().Append(L"chrome.exe"));
const GURL& gaia_url = gaia_server_.base_url();
command_line.AppendSwitchASCII(::switches::kGaiaUrl, gaia_url.spec());
command_line.AppendSwitchASCII(::switches::kLsoUrl, gaia_url.spec());
const GURL& google_apis_url = google_apis_server_.base_url();
command_line.AppendSwitchASCII(::switches::kGoogleApisUrl,
google_apis_url.spec());
command_line.AppendSwitch(kGcpwSigninSwitch);
command_line.AppendSwitchPath("user-data-dir", user_data_dir);
command_line.AppendSwitchASCII(
network::switches::kHostResolverRules,
base::StringPrintf(
"MAP * %s, EXCLUDE %s",
gaia_server_.host_port_pair().ToString().c_str(),
google_apis_server_.host_port_pair().ToString().c_str()));
return command_line;
}
std::string GcpUsingChromeTest::RunProcessAndExtractOutput(
const base::CommandLine& command_line) const {
base::win::ScopedHandle read_handle;
base::Process process;
{
base::win::ScopedHandle write_handle;
EXPECT_EQ(
CreatePipeForChildProcess(false, false, &read_handle, &write_handle),
S_OK);
base::LaunchOptions options;
options.stdin_handle = INVALID_HANDLE_VALUE;
options.stdout_handle = write_handle.Get();
options.stderr_handle = INVALID_HANDLE_VALUE;
options.handles_to_inherit.push_back(write_handle.Get());
process = base::Process(
base::LaunchProcess(command_line.GetCommandLineString(), options));
EXPECT_TRUE(process.IsValid());
}
constexpr DWORD kTimeout = 1000;
std::string output_from_process;
char buffer[1024];
for (bool is_done = false; !is_done;) {
DWORD length = std::size(buffer) - 1;
DWORD ret = ::WaitForSingleObject(read_handle.Get(), kTimeout);
if (ret == WAIT_OBJECT_0) {
if (!::ReadFile(read_handle.Get(), buffer, length, &length, nullptr)) {
break;
}
UNSAFE_TODO(buffer[length]) = 0;
output_from_process += buffer;
} else if (ret != WAIT_IO_COMPLETION) {
break;
}
}
int exit_code;
EXPECT_TRUE(
process.WaitForExitWithTimeout(base::Milliseconds(kTimeout), &exit_code));
EXPECT_EQ(exit_code, 0);
return output_from_process;
}
std::string GcpUsingChromeTest::MakeInlineSigninCompletionScript(
const std::string& email,
const std::string& password,
const GaiaId& gaia_id) const {
return "<script>"
"let webview = null;"
"let onMessageEventHandler = function(event) {"
" if (!webview) {"
" webview = event.source;"
" var attempt_login_msg = {"
" 'method' : 'attemptLogin',"
" 'email' : '" +
email +
"',"
" 'password' : '" +
password +
"',"
" 'attemptToken' : 'attemptToken'"
" };"
" webview.postMessage(attempt_login_msg, '*');"
" var user_info_msg = {"
" 'method' : 'userInfo',"
" 'email' : '" +
email +
"',"
" 'gaiaId' : '" +
gaia_id.ToString() +
"',"
" 'services' : []"
" };"
" webview.postMessage(user_info_msg, '*');"
" }"
"};"
"window.addEventListener('message', onMessageEventHandler);"
"</script>";
}
std::unique_ptr<net::test_server::HttpResponse>
GcpUsingChromeTest::GaiaHtmlResponseHandler(
const net::test_server::HttpRequest& request) {
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->set_content_type("text/html");
std::string content = "<html><head>";
if (request.GetURL().GetPath().find("/embedded/setup/chrome") == 0) {
http_response->AddCustomHeader("google-accounts-signin",
"email=\"" +
test_data_storage_.GetSuccessEmail() +
"\","
"obfuscatedid=\"" +
test_data_storage_.GetSuccessId() +
"\", "
"sessionindex=0");
http_response->AddCustomHeader("Set-Cookie",
"oauth_code=oauth_code; Path=/");
http_response->AddCustomHeader("google-accounts-embedded", std::string());
content += MakeInlineSigninCompletionScript(
test_data_storage_.GetSuccessEmail(),
test_data_storage_.GetSuccessPassword(),
GaiaId(test_data_storage_.GetSuccessId()));
}
content += "</head></html>";
http_response->set_content(content);
return std::move(http_response);
}
std::unique_ptr<net::test_server::HttpResponse>
GcpUsingChromeTest::GoogleApisHtmlResponseHandler(
const net::test_server::HttpRequest& request) {
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
TestGoogleApiResponse* api_response = nullptr;
if (request.GetURL().GetPath().find("/oauth2/v2/tokeninfo") == 0) {
api_response = &token_info_response_;
} else if (request.GetURL().GetPath().find("/oauth2/v1/userinfo") == 0) {
EXPECT_TRUE(mdm_token_response_.response_given_);
api_response = &user_info_response_;
} else if (request.GetURL().GetPath().find("/oauth2/v4/token") == 0) {
if (request.content.find("grant_type=authorization_code") ==
std::string::npos) {
api_response = &mdm_token_response_;
} else {
api_response = &signin_token_response_;
}
}
if (api_response) {
EXPECT_FALSE(api_response->response_given_);
api_response->response_given_ = true;
http_response->set_content_type("text/html");
http_response->set_content(api_response->data_);
http_response->set_code(api_response->info_code_);
} else {
http_response->set_code(net::HTTP_NOT_FOUND);
}
return std::move(http_response);
}
TEST_F(GcpUsingChromeTest, DISABLED_VerifyMissingSigninInfoOutput) {
SetPasswordForSignin(std::string());
SetTokenInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulTokenInfoFetchResult()});
SetUserInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulUserInfoFetchResult()});
SetMdmTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulMdmTokenFetchResult()});
SetSigninTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulSigninTokenFetchResult()});
std::string output_from_chrome = RunChromeAndExtractOutput();
EXPECT_EQ(output_from_chrome, std::string());
EXPECT_TRUE(signin_token_response_.response_given_);
EXPECT_FALSE(user_info_response_.response_given_);
EXPECT_FALSE(token_info_response_.response_given_);
EXPECT_FALSE(mdm_token_response_.response_given_);
}
TEST_F(GcpUsingChromeTest, DISABLED_VerifySigninFailureOutput) {
SetTokenInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulTokenInfoFetchResult()});
SetUserInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulUserInfoFetchResult()});
SetMdmTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulMdmTokenFetchResult()});
SetSigninTokenResponse({net::HTTP_OK,
CredentialProviderSigninDialogTestDataStorage::
kInvalidTokenFetchResponse});
std::string output_from_chrome = RunChromeAndExtractOutput();
EXPECT_EQ(output_from_chrome, std::string());
EXPECT_TRUE(signin_token_response_.response_given_);
EXPECT_FALSE(user_info_response_.response_given_);
EXPECT_FALSE(token_info_response_.response_given_);
EXPECT_FALSE(mdm_token_response_.response_given_);
}
TEST_F(GcpUsingChromeTest, DISABLED_VerifyTokenInfoFailureOutput) {
SetTokenInfoResponse({net::HTTP_OK,
CredentialProviderSigninDialogTestDataStorage::
kInvalidTokenInfoResponse});
SetUserInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulUserInfoFetchResult()});
SetMdmTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulMdmTokenFetchResult()});
SetSigninTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulSigninTokenFetchResult()});
std::string output_from_chrome = RunChromeAndExtractOutput();
EXPECT_EQ(output_from_chrome, std::string());
EXPECT_TRUE(signin_token_response_.response_given_);
EXPECT_TRUE(token_info_response_.response_given_);
}
TEST_F(GcpUsingChromeTest, DISABLED_VerifyUserInfoFailureOutput) {
SetTokenInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulTokenInfoFetchResult()});
SetUserInfoResponse({net::HTTP_OK,
CredentialProviderSigninDialogTestDataStorage::
kInvalidUserInfoResponse});
SetMdmTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulMdmTokenFetchResult()});
SetSigninTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulSigninTokenFetchResult()});
std::string output_from_chrome = RunChromeAndExtractOutput();
EXPECT_EQ(output_from_chrome, std::string());
EXPECT_TRUE(signin_token_response_.response_given_);
EXPECT_TRUE(user_info_response_.response_given_);
EXPECT_TRUE(mdm_token_response_.response_given_);
}
TEST_F(GcpUsingChromeTest, DISABLED_VerifyMdmFailureOutput) {
SetTokenInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulTokenInfoFetchResult()});
SetUserInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulUserInfoFetchResult()});
SetMdmTokenResponse({net::HTTP_OK,
CredentialProviderSigninDialogTestDataStorage::
kInvalidTokenFetchResponse});
SetSigninTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulSigninTokenFetchResult()});
std::string output_from_chrome = RunChromeAndExtractOutput();
EXPECT_EQ(output_from_chrome, std::string());
EXPECT_TRUE(mdm_token_response_.response_given_);
EXPECT_FALSE(user_info_response_.response_given_);
}
TEST_F(GcpUsingChromeTest, DISABLED_VerifySuccessOutput) {
SetTokenInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulTokenInfoFetchResult()});
SetUserInfoResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulUserInfoFetchResult()});
SetMdmTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulMdmTokenFetchResult()});
SetSigninTokenResponse(
{net::HTTP_OK, test_data_storage_.GetSuccessfulSigninTokenFetchResult()});
std::string output_from_chrome = RunChromeAndExtractOutput();
std::string expected_result =
base::WriteJson(test_data_storage_.expected_full_result()).value_or("");
EXPECT_EQ(output_from_chrome, expected_result);
EXPECT_TRUE(signin_token_response_.response_given_);
EXPECT_TRUE(user_info_response_.response_given_);
EXPECT_TRUE(token_info_response_.response_given_);
EXPECT_TRUE(mdm_token_response_.response_given_);
}
}