// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/credential_provider/gaiacp/gaia_credential.h"
#include <wrl/client.h>
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/atl.h"
#include "chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
#include "chrome/credential_provider/gaiacp/gaia_resources.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/gcpw_strings.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "chrome/credential_provider/test/com_fakes.h"
#include "chrome/credential_provider/test/gcp_fakes.h"
#include "chrome/credential_provider/test/gls_runner_test_base.h"
#include "chrome/credential_provider/test/test_credential.h"
#include "content/public/common/content_switches.h"
#include "google_apis/gaia/gaia_switches.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace credential_provider {
namespace testing {
class GcpGaiaCredentialTest : public GlsRunnerTestBase {
protected:
GcpGaiaCredentialTest();
BSTR signin_result() { return signin_result_; }
CComBSTR MakeSigninResult(const std::string& password);
private:
CComBSTR signin_result_;
};
GcpGaiaCredentialTest::GcpGaiaCredentialTest() {
signin_result_ = MakeSigninResult("password");
}
CComBSTR GcpGaiaCredentialTest::MakeSigninResult(const std::string& password) {
USES_CONVERSION;
CredentialProviderSigninDialogTestDataStorage test_data_storage;
test_data_storage.SetSigninPassword(password);
std::string signin_result_utf8;
EXPECT_TRUE(base::JSONWriter::Write(test_data_storage.expected_full_result(),
&signin_result_utf8));
return CComBSTR(A2OLE(signin_result_utf8.c_str()));
}
TEST_F(GcpGaiaCredentialTest, OnUserAuthenticated) {
USES_CONVERSION;
Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
Microsoft::WRL::ComPtr<IGaiaCredential> gaia_cred;
ASSERT_EQ(S_OK, cred.As(&gaia_cred));
CComBSTR error;
ASSERT_EQ(S_OK, gaia_cred->OnUserAuthenticated(signin_result(), &error));
Microsoft::WRL::ComPtr<ITestCredentialProvider> test_provider;
ASSERT_EQ(S_OK, created_provider().As(&test_provider));
EXPECT_TRUE(test_provider->credentials_changed_fired());
}
TEST_F(GcpGaiaCredentialTest, OnUserAuthenticated_SamePassword) {
USES_CONVERSION;
Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
Microsoft::WRL::ComPtr<IGaiaCredential> gaia_cred;
ASSERT_EQ(S_OK, cred.As(&gaia_cred));
CComBSTR error;
ASSERT_EQ(S_OK, gaia_cred->OnUserAuthenticated(signin_result(), &error));
Microsoft::WRL::ComPtr<ITestCredentialProvider> test_provider;
ASSERT_EQ(S_OK, created_provider().As(&test_provider));
CComBSTR first_sid = test_provider->sid();
// Report to register the user.
wchar_t* report_status_text = nullptr;
CREDENTIAL_PROVIDER_STATUS_ICON report_icon;
EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon));
// Finishing with the same username+password should succeed.
CComBSTR error2;
ASSERT_EQ(S_OK, gaia_cred->OnUserAuthenticated(signin_result(), &error2));
EXPECT_TRUE(test_provider->credentials_changed_fired());
EXPECT_EQ(first_sid, test_provider->sid());
}
TEST_F(GcpGaiaCredentialTest, OnUserAuthenticated_DiffPassword) {
USES_CONVERSION;
CredentialProviderSigninDialogTestDataStorage test_data_storage;
CComBSTR sid;
ASSERT_EQ(
S_OK,
fake_os_user_manager()->CreateTestOSUser(
L"foo_bar",
base::UTF8ToWide(test_data_storage.GetSuccessPassword()).c_str(),
base::UTF8ToWide(test_data_storage.GetSuccessFullName()).c_str(),
L"comment",
base::UTF8ToWide(test_data_storage.GetSuccessId()).c_str(),
base::UTF8ToWide(test_data_storage.GetSuccessEmail()).c_str(), &sid));
Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
Microsoft::WRL::ComPtr<IGaiaCredential> gaia_cred;
ASSERT_EQ(S_OK, cred.As(&gaia_cred));
CComBSTR error;
ASSERT_EQ(S_OK, gaia_cred->OnUserAuthenticated(signin_result(), &error));
Microsoft::WRL::ComPtr<ITestCredentialProvider> test_provider;
ASSERT_EQ(S_OK, created_provider().As(&test_provider));
EXPECT_TRUE(test_provider->credentials_changed_fired());
test_provider->ResetCredentialsChangedFired();
CComBSTR new_signin_result = MakeSigninResult("password2");
// Finishing with the same username but different password should mark
// the password as stale and not fire the credentials changed event.
EXPECT_EQ(S_FALSE, gaia_cred->OnUserAuthenticated(new_signin_result, &error));
EXPECT_FALSE(test_provider->credentials_changed_fired());
}
class GcpGaiaCredentialGlsRunnerTest : public GlsRunnerTestBase {};
// Tests the GetUserGlsCommandline method overridden by IGaiaCredential.
// Parameters are:
// 1. Is gem features enabled / disabled.
// 2. Is ep_url already set via registry.
// 3. List of allowed domains.
class GcpGaiaCredentialGlsTest : public GcpGaiaCredentialGlsRunnerTest,
public ::testing::WithParamInterface<
std::tuple<bool, bool, std::wstring>> {};
TEST_P(GcpGaiaCredentialGlsTest, GetUserGlsCommandLine) {
USES_CONVERSION;
CredentialProviderSigninDialogTestDataStorage test_data_storage;
const bool is_gem_features_enabled = std::get<0>(GetParam());
if (is_gem_features_enabled)
ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kKeyEnableGemFeatures, 1u));
else
ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kKeyEnableGemFeatures, 0u));
const std::wstring email_domains = std::get<2>(GetParam());
SetGlobalFlagForTesting(L"domains_allowed_to_login", email_domains);
// Create provider and start logon.
Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
ASSERT_TRUE(cred);
// Get user gls command line and extract the kGaiaUrl &
// kGcpwEndpointPathSwitch switch from it.
Microsoft::WRL::ComPtr<ITestCredential> test_cred;
ASSERT_EQ(S_OK, cred.As(&test_cred));
std::string device_id;
ASSERT_EQ(S_OK, GenerateDeviceId(&device_id));
const bool is_ep_url_set = std::get<1>(GetParam());
if (is_ep_url_set)
SetGlobalFlagForTesting(L"ep_setup_url", L"http://login.com");
GoogleChromePathForTesting google_chrome_path_for_testing(
base::FilePath(L"chrome.exe"));
EXPECT_EQ(S_OK, test_cred->UseRealGlsBaseCommandLine(true));
base::CommandLine command_line = test_cred->GetTestGlsCommandline();
std::string gcpw_path =
command_line.GetSwitchValueASCII(kGcpwEndpointPathSwitch);
EXPECT_TRUE(command_line.HasSwitch(kGcpwSigninSwitch));
EXPECT_TRUE(command_line.HasSwitch(switches::kDisableExtensions));
// If domain list has more than one domain, they shouldn't exist in the
// command line.
if (email_domains.find(L",") != std::wstring::npos) {
EXPECT_EQ(command_line.GetSwitchValueASCII(kEmailDomainsSwitch), "");
} else {
EXPECT_EQ(command_line.GetSwitchValueASCII(kEmailDomainsSwitch),
base::WideToUTF8(email_domains));
}
if (is_ep_url_set) {
ASSERT_EQ("http://login.com/",
command_line.GetSwitchValueASCII(switches::kGaiaUrl));
ASSERT_TRUE(gcpw_path.empty());
} else if (is_gem_features_enabled) {
ASSERT_EQ(gcpw_path, base::StringPrintf(
"embedded/setup/windows?device_id=%s&show_tos=1",
device_id.c_str()));
ASSERT_TRUE(command_line.GetSwitchValueASCII(switches::kGaiaUrl).empty());
} else {
ASSERT_TRUE(command_line.GetSwitchValueASCII(switches::kGaiaUrl).empty());
ASSERT_TRUE(gcpw_path.empty());
}
}
INSTANTIATE_TEST_SUITE_P(
All,
GcpGaiaCredentialGlsTest,
::testing::Combine(::testing::Bool(),
::testing::Bool(),
::testing::Values(L"test.com", L"test1.com,test2.com")));
TEST_F(GcpGaiaCredentialGlsRunnerTest,
AssociateToExistingAssociatedUser_LongUsername) {
USES_CONVERSION;
// Create a fake user that has the same username but a different gaia id
// as the test gaia id.
CComBSTR sid;
std::wstring base_username(L"foo1234567890abcdefg");
std::wstring base_gaia_id(L"other-gaia-id");
ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
base_username.c_str(), L"password", L"name", L"comment",
base_gaia_id, std::wstring(), &sid));
ASSERT_EQ(2u, fake_os_user_manager()->GetUserCount());
// Start logon.
Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
Microsoft::WRL::ComPtr<IGaiaCredential> gaia_cred;
ASSERT_EQ(S_OK, cred.As(&gaia_cred));
Microsoft::WRL::ComPtr<ITestCredential> test;
ASSERT_EQ(S_OK, cred.As(&test));
ASSERT_EQ(S_OK, test->SetGlsEmailAddress(base::WideToUTF8(base_username) +
"@gmail.com"));
ASSERT_EQ(S_OK, StartLogonProcessAndWait());
// New username should be truncated at the end and have the last character
// replaced with a new index
EXPECT_STREQ((base_username.substr(0, base_username.size() - 1) +
base::NumberToWString(kInitialDuplicateUsernameIndex))
.c_str(),
test->GetFinalUsername());
// New user should be created.
EXPECT_EQ(3u, fake_os_user_manager()->GetUserCount());
}
// This test checks the expected success / failure of user creation when
// GCPW wants to associate a gaia id to a user that may already be associated
// to another gaia id.
// Parameters:
// int: Number of existing users to create before trying to associate the
// new user.
// bool: Whether the final user creation is expected to succeed. For
// bool: whether the created users are associated to a gaia id.
// kMaxAttempts = 10, 0 to 8 users can be created and still have the
// test succeed. If a 9th user is create the test will fail.
class GcpAssociatedUserRunnableGaiaCredentialTest
: public GcpGaiaCredentialGlsRunnerTest,
public ::testing::WithParamInterface<std::tuple<int, bool, bool>> {};
TEST_P(GcpAssociatedUserRunnableGaiaCredentialTest,
AssociateToExistingAssociatedUser) {
USES_CONVERSION;
int last_user_index = std::get<0>(GetParam());
bool should_succeed = std::get<1>(GetParam());
bool should_associate = std::get<2>(GetParam());
// Create many fake users that has the same username but a different gaia id
// as the test gaia id.
CComBSTR sid;
std::wstring base_username(L"foo");
std::wstring base_gaia_id(L"other-gaia-id");
ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
base_username.c_str(), L"password", L"name", L"comment",
should_associate ? base_gaia_id : std::wstring(),
std::wstring(), &sid));
ASSERT_EQ(S_OK, SetUserProperty(OLE2CW(sid), kUserId, base_gaia_id));
for (int i = 0; i < last_user_index; ++i) {
std::wstring user_suffix =
base::NumberToWString(i + kInitialDuplicateUsernameIndex);
ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
(base_username + user_suffix).c_str(), L"password",
L"name", L"comment",
should_associate ? base_gaia_id + user_suffix
: std::wstring(),
std::wstring(), &sid));
}
ASSERT_EQ(static_cast<size_t>(1 + last_user_index + 1),
fake_os_user_manager()->GetUserCount());
// Create provider.
Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
Microsoft::WRL::ComPtr<IGaiaCredential> gaia_cred;
ASSERT_EQ(S_OK, cred.As(&gaia_cred));
Microsoft::WRL::ComPtr<ITestCredential> test;
ASSERT_EQ(S_OK, cred.As(&test));
// Start logon.
ASSERT_EQ(S_OK, StartLogonProcessAndWait());
if (should_succeed) {
EXPECT_STREQ(
(base_username + base::NumberToWString(last_user_index +
kInitialDuplicateUsernameIndex))
.c_str(),
OLE2CW(test->GetFinalUsername()));
// New user should be created.
EXPECT_EQ(static_cast<size_t>(last_user_index + 2 + 1),
fake_os_user_manager()->GetUserCount());
ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0));
} else {
// No new user should be created.
EXPECT_EQ(static_cast<size_t>(last_user_index + 1 + 1),
fake_os_user_manager()->GetUserCount());
ASSERT_EQ(S_OK, FinishLogonProcess(false, false, IDS_INTERNAL_ERROR_BASE));
}
}
// For a max retry of 10, it is possible to create users 'username',
// 'username0' ... 'username8' before failing. At 'username9' the test should
// fail.
INSTANTIATE_TEST_SUITE_P(
AvailableUsername,
GcpAssociatedUserRunnableGaiaCredentialTest,
::testing::Combine(::testing::Range(0, kMaxUsernameAttempts - 2),
::testing::Values(true),
::testing::Values(true, false)));
INSTANTIATE_TEST_SUITE_P(
UnavailableUsername,
GcpAssociatedUserRunnableGaiaCredentialTest,
::testing::Combine(::testing::Values(kMaxUsernameAttempts - 1),
::testing::Values(false),
::testing::Values(true, false)));
} // namespace testing
} // namespace credential_provider