blob: 2923eb22ab9d660fd99268dc97ca1b6398139826 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/identity/public/cpp/identity_test_environment.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/image_fetcher/core/fake_image_decoder.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
#include "components/signin/core/browser/gaia_cookie_manager_service.h"
#include "components/signin/core/browser/identity_manager_wrapper.h"
#include "components/signin/core/browser/signin_manager.h"
#include "components/signin/core/browser/test_signin_client.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "google_apis/gaia/oauth2_access_token_consumer.h"
#include "services/identity/public/cpp/accounts_cookie_mutator.h"
#include "services/identity/public/cpp/accounts_cookie_mutator_impl.h"
#include "services/identity/public/cpp/accounts_mutator.h"
#include "services/identity/public/cpp/diagnostics_provider_impl.h"
#include "services/identity/public/cpp/identity_test_utils.h"
#include "services/identity/public/cpp/primary_account_mutator.h"
#include "services/identity/public/cpp/test_identity_manager_observer.h"
#if defined(OS_IOS)
#include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h"
#include "components/signin/ios/browser/profile_oauth2_token_service_ios_provider.h"
#endif
#if !defined(OS_CHROMEOS)
#include "services/identity/public/cpp/primary_account_mutator_impl.h"
#endif
#if !defined(OS_ANDROID) && !defined(OS_IOS)
#include "services/identity/public/cpp/accounts_mutator_impl.h"
#endif
#if defined(OS_ANDROID)
#include "components/signin/core/browser/child_account_info_fetcher_android.h"
#endif
namespace identity {
class IdentityManagerDependenciesOwner {
public:
IdentityManagerDependenciesOwner(
sync_preferences::TestingPrefServiceSyncable* pref_service,
TestSigninClient* test_signin_client);
~IdentityManagerDependenciesOwner();
sync_preferences::TestingPrefServiceSyncable* pref_service();
TestSigninClient* signin_client();
private:
// Depending on whether a |pref_service| instance is passed in
// the constructor, exactly one of these will be non-null.
std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
owned_pref_service_;
sync_preferences::TestingPrefServiceSyncable* raw_pref_service_ = nullptr;
std::unique_ptr<TestSigninClient> owned_signin_client_;
TestSigninClient* raw_signin_client_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(IdentityManagerDependenciesOwner);
};
IdentityManagerDependenciesOwner::IdentityManagerDependenciesOwner(
sync_preferences::TestingPrefServiceSyncable* pref_service_param,
TestSigninClient* signin_client_param)
: owned_pref_service_(
pref_service_param
? nullptr
: std::make_unique<
sync_preferences::TestingPrefServiceSyncable>()),
raw_pref_service_(pref_service_param),
owned_signin_client_(
signin_client_param
? nullptr
: std::make_unique<TestSigninClient>(pref_service())),
raw_signin_client_(signin_client_param) {}
IdentityManagerDependenciesOwner::~IdentityManagerDependenciesOwner() = default;
sync_preferences::TestingPrefServiceSyncable*
IdentityManagerDependenciesOwner::pref_service() {
DCHECK(raw_pref_service_ || owned_pref_service_);
DCHECK(!(raw_pref_service_ && owned_pref_service_));
return raw_pref_service_ ? raw_pref_service_ : owned_pref_service_.get();
}
TestSigninClient* IdentityManagerDependenciesOwner::signin_client() {
DCHECK(raw_signin_client_ || owned_signin_client_);
DCHECK(!(raw_signin_client_ && owned_signin_client_));
return raw_signin_client_ ? raw_signin_client_ : owned_signin_client_.get();
}
IdentityTestEnvironment::IdentityTestEnvironment(
network::TestURLLoaderFactory* test_url_loader_factory,
sync_preferences::TestingPrefServiceSyncable* pref_service,
signin::AccountConsistencyMethod account_consistency,
TestSigninClient* test_signin_client)
: IdentityTestEnvironment(
std::make_unique<IdentityManagerDependenciesOwner>(
pref_service,
test_signin_client),
test_url_loader_factory,
account_consistency) {}
IdentityTestEnvironment::IdentityTestEnvironment(
IdentityManager* identity_manager)
: weak_ptr_factory_(this) {
DCHECK(identity_manager);
raw_identity_manager_ = identity_manager;
Initialize();
}
void IdentityTestEnvironment::Initialize() {
DCHECK(base::ThreadTaskRunnerHandle::Get())
<< "IdentityTestEnvironment requires a properly set up task "
"environment. "
"If your test has an existing one, move it to be initialized before "
"IdentityTestEnvironment. Otherwise, use "
"base::test::ScopedTaskEnvironment.";
test_identity_manager_observer_ =
std::make_unique<TestIdentityManagerObserver>(this->identity_manager());
this->identity_manager()->AddDiagnosticsObserver(this);
}
IdentityTestEnvironment::IdentityTestEnvironment(
std::unique_ptr<IdentityManagerDependenciesOwner> dependencies_owner,
network::TestURLLoaderFactory* test_url_loader_factory,
signin::AccountConsistencyMethod account_consistency)
: test_url_loader_factory_(test_url_loader_factory),
weak_ptr_factory_(this) {
dependencies_owner_ = std::move(dependencies_owner);
TestSigninClient* test_signin_client = dependencies_owner_->signin_client();
sync_preferences::TestingPrefServiceSyncable* test_pref_service =
dependencies_owner_->pref_service();
IdentityManager::RegisterProfilePrefs(test_pref_service->registry());
IdentityManager::RegisterLocalStatePrefs(test_pref_service->registry());
owned_identity_manager_ = BuildIdentityManagerForTests(
test_signin_client, test_pref_service, base::FilePath(),
#if defined(OS_IOS)
std::unique_ptr<ProfileOAuth2TokenServiceIOSProvider>(),
#endif
account_consistency, test_url_loader_factory);
Initialize();
}
// static
std::unique_ptr<IdentityManagerWrapper>
IdentityTestEnvironment::BuildIdentityManagerForTests(
SigninClient* signin_client,
PrefService* pref_service,
base::FilePath user_data_dir,
#if defined(OS_IOS)
std::unique_ptr<ProfileOAuth2TokenServiceIOSProvider>
token_service_ios_provider,
#endif
signin::AccountConsistencyMethod account_consistency,
network::TestURLLoaderFactory* test_url_loader_factory) {
auto account_tracker_service = std::make_unique<AccountTrackerService>();
account_tracker_service->Initialize(pref_service, user_data_dir);
#if defined(OS_IOS)
std::unique_ptr<ProfileOAuth2TokenService> token_service;
if (token_service_ios_provider) {
token_service = std::make_unique<FakeProfileOAuth2TokenService>(
pref_service, std::make_unique<ProfileOAuth2TokenServiceIOSDelegate>(
signin_client, std::move(token_service_ios_provider),
account_tracker_service.get()));
} else {
token_service =
std::make_unique<FakeProfileOAuth2TokenService>(pref_service);
}
#else
auto token_service =
std::make_unique<FakeProfileOAuth2TokenService>(pref_service);
#endif
auto account_fetcher_service = std::make_unique<AccountFetcherService>();
account_fetcher_service->Initialize(
signin_client, token_service.get(), account_tracker_service.get(),
std::make_unique<image_fetcher::FakeImageDecoder>());
#if defined(OS_CHROMEOS)
std::unique_ptr<SigninManagerBase> signin_manager =
std::make_unique<SigninManagerBase>(signin_client, token_service.get(),
account_tracker_service.get());
#else
std::unique_ptr<SigninManagerBase> signin_manager =
std::make_unique<SigninManager>(signin_client, token_service.get(),
account_tracker_service.get(), nullptr,
account_consistency);
#endif
signin_manager->Initialize(pref_service);
std::unique_ptr<GaiaCookieManagerService> gaia_cookie_manager_service;
if (test_url_loader_factory != nullptr) {
gaia_cookie_manager_service = std::make_unique<GaiaCookieManagerService>(
token_service.get(), signin_client,
base::BindRepeating(
[](network::TestURLLoaderFactory* test_url_loader_factory)
-> scoped_refptr<network::SharedURLLoaderFactory> {
return test_url_loader_factory->GetSafeWeakWrapper();
},
test_url_loader_factory));
} else {
gaia_cookie_manager_service = std::make_unique<GaiaCookieManagerService>(
token_service.get(), signin_client);
}
std::unique_ptr<PrimaryAccountMutator> primary_account_mutator;
std::unique_ptr<AccountsMutator> accounts_mutator;
#if !defined(OS_CHROMEOS)
primary_account_mutator = std::make_unique<PrimaryAccountMutatorImpl>(
account_tracker_service.get(),
static_cast<SigninManager*>(signin_manager.get()));
#endif
#if !defined(OS_ANDROID) && !defined(OS_IOS)
accounts_mutator = std::make_unique<AccountsMutatorImpl>(
token_service.get(), account_tracker_service.get(), signin_manager.get(),
pref_service);
#endif
auto diagnostics_provider = std::make_unique<DiagnosticsProviderImpl>(
token_service.get(), gaia_cookie_manager_service.get());
auto accounts_cookie_mutator = std::make_unique<AccountsCookieMutatorImpl>(
gaia_cookie_manager_service.get());
return std::make_unique<IdentityManagerWrapper>(
std::move(account_tracker_service), std::move(token_service),
std::move(gaia_cookie_manager_service), std::move(signin_manager),
std::move(account_fetcher_service), std::move(primary_account_mutator),
std::move(accounts_mutator), std::move(accounts_cookie_mutator),
std::move(diagnostics_provider));
}
IdentityTestEnvironment::~IdentityTestEnvironment() {
// Remove the Observer that IdentityTestEnvironment added during its
// initialization.
identity_manager()->RemoveDiagnosticsObserver(this);
}
IdentityManager* IdentityTestEnvironment::identity_manager() {
DCHECK(raw_identity_manager_ || owned_identity_manager_);
DCHECK(!(raw_identity_manager_ && owned_identity_manager_));
return raw_identity_manager_ ? raw_identity_manager_
: owned_identity_manager_.get();
}
TestIdentityManagerObserver*
IdentityTestEnvironment::identity_manager_observer() {
return test_identity_manager_observer_.get();
}
CoreAccountInfo IdentityTestEnvironment::SetPrimaryAccount(
const std::string& email) {
return identity::SetPrimaryAccount(identity_manager(), email);
}
void IdentityTestEnvironment::SetRefreshTokenForPrimaryAccount() {
identity::SetRefreshTokenForPrimaryAccount(identity_manager());
}
void IdentityTestEnvironment::SetInvalidRefreshTokenForPrimaryAccount() {
identity::SetInvalidRefreshTokenForPrimaryAccount(identity_manager());
}
void IdentityTestEnvironment::RemoveRefreshTokenForPrimaryAccount() {
identity::RemoveRefreshTokenForPrimaryAccount(identity_manager());
}
AccountInfo IdentityTestEnvironment::MakePrimaryAccountAvailable(
const std::string& email) {
return identity::MakePrimaryAccountAvailable(identity_manager(), email);
}
void IdentityTestEnvironment::ClearPrimaryAccount(
ClearPrimaryAccountPolicy policy) {
identity::ClearPrimaryAccount(identity_manager(), policy);
}
AccountInfo IdentityTestEnvironment::MakeAccountAvailable(
const std::string& email) {
return identity::MakeAccountAvailable(identity_manager(), email);
}
void IdentityTestEnvironment::SetRefreshTokenForAccount(
const std::string& account_id) {
return identity::SetRefreshTokenForAccount(identity_manager(), account_id);
}
void IdentityTestEnvironment::SetInvalidRefreshTokenForAccount(
const std::string& account_id) {
return identity::SetInvalidRefreshTokenForAccount(identity_manager(),
account_id);
}
void IdentityTestEnvironment::RemoveRefreshTokenForAccount(
const std::string& account_id) {
return identity::RemoveRefreshTokenForAccount(identity_manager(), account_id);
}
void IdentityTestEnvironment::UpdatePersistentErrorOfRefreshTokenForAccount(
const std::string& account_id,
const GoogleServiceAuthError& auth_error) {
return identity::UpdatePersistentErrorOfRefreshTokenForAccount(
identity_manager(), account_id, auth_error);
}
void IdentityTestEnvironment::SetCookieAccounts(
const std::vector<CookieParams>& cookie_accounts) {
DCHECK(test_url_loader_factory_)
<< "IdentityTestEnvironment constructor must have been passed a "
"test_url_loader_factory in order to use this method.";
identity::SetCookieAccounts(identity_manager(), test_url_loader_factory_,
cookie_accounts);
}
void IdentityTestEnvironment::SetAutomaticIssueOfAccessTokens(bool grant) {
fake_token_service()->set_auto_post_fetch_response_on_message_loop(grant);
}
void IdentityTestEnvironment::
WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
const std::string& token,
const base::Time& expiration,
const std::string& id_token) {
WaitForAccessTokenRequestIfNecessary(base::nullopt);
fake_token_service()->IssueTokenForAllPendingRequests(
OAuth2AccessTokenConsumer::TokenResponse(token, expiration, id_token));
}
void IdentityTestEnvironment::
WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
const std::string& account_id,
const std::string& token,
const base::Time& expiration,
const std::string& id_token) {
WaitForAccessTokenRequestIfNecessary(account_id);
fake_token_service()->IssueAllTokensForAccount(
account_id,
OAuth2AccessTokenConsumer::TokenResponse(token, expiration, id_token));
}
void IdentityTestEnvironment::
WaitForAccessTokenRequestIfNecessaryAndRespondWithTokenForScopes(
const std::string& token,
const base::Time& expiration,
const std::string& id_token,
const identity::ScopeSet& scopes) {
WaitForAccessTokenRequestIfNecessary(base::nullopt);
fake_token_service()->IssueTokenForScope(
scopes,
OAuth2AccessTokenConsumer::TokenResponse(token, expiration, id_token));
}
void IdentityTestEnvironment::
WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
const GoogleServiceAuthError& error) {
WaitForAccessTokenRequestIfNecessary(base::nullopt);
fake_token_service()->IssueErrorForAllPendingRequests(error);
}
void IdentityTestEnvironment::
WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
const std::string& account_id,
const GoogleServiceAuthError& error) {
WaitForAccessTokenRequestIfNecessary(account_id);
fake_token_service()->IssueErrorForAllPendingRequestsForAccount(account_id,
error);
}
void IdentityTestEnvironment::SetCallbackForNextAccessTokenRequest(
base::OnceClosure callback) {
on_access_token_requested_callback_ = std::move(callback);
}
IdentityTestEnvironment::AccessTokenRequestState::AccessTokenRequestState() =
default;
IdentityTestEnvironment::AccessTokenRequestState::~AccessTokenRequestState() =
default;
IdentityTestEnvironment::AccessTokenRequestState::AccessTokenRequestState(
AccessTokenRequestState&& other) = default;
IdentityTestEnvironment::AccessTokenRequestState&
IdentityTestEnvironment::AccessTokenRequestState::operator=(
AccessTokenRequestState&& other) = default;
void IdentityTestEnvironment::OnAccessTokenRequested(
const std::string& account_id,
const std::string& consumer_id,
const identity::ScopeSet& scopes) {
// Post a task to handle this access token request in order to support the
// case where the access token request is handled synchronously in the
// production code, in which case this callback could be coming in ahead
// of an invocation of WaitForAccessTokenRequestIfNecessary() that will be
// made in this same iteration of the run loop.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&IdentityTestEnvironment::HandleOnAccessTokenRequested,
weak_ptr_factory_.GetWeakPtr(), account_id));
}
void IdentityTestEnvironment::HandleOnAccessTokenRequested(
std::string account_id) {
if (on_access_token_requested_callback_) {
std::move(on_access_token_requested_callback_).Run();
return;
}
for (auto it = requesters_.begin(); it != requesters_.end(); ++it) {
if (!it->account_id || (it->account_id.value() == account_id)) {
if (it->state == AccessTokenRequestState::kAvailable)
return;
if (it->on_available)
std::move(it->on_available).Run();
requesters_.erase(it);
return;
}
}
// A requests came in for a request for which we are not waiting. Record
// that it's available.
requesters_.emplace_back();
requesters_.back().state = AccessTokenRequestState::kAvailable;
requesters_.back().account_id = account_id;
}
void IdentityTestEnvironment::WaitForAccessTokenRequestIfNecessary(
base::Optional<std::string> account_id) {
// Handle HandleOnAccessTokenRequested getting called before
// WaitForAccessTokenRequestIfNecessary.
if (account_id) {
for (auto it = requesters_.begin(); it != requesters_.end(); ++it) {
if (it->account_id && it->account_id.value() == account_id.value()) {
// Can't wait twice for same thing.
DCHECK_EQ(AccessTokenRequestState::kAvailable, it->state);
requesters_.erase(it);
return;
}
}
} else {
for (auto it = requesters_.begin(); it != requesters_.end(); ++it) {
if (it->state == AccessTokenRequestState::kAvailable) {
requesters_.erase(it);
return;
}
}
}
base::RunLoop run_loop;
requesters_.emplace_back();
requesters_.back().state = AccessTokenRequestState::kPending;
requesters_.back().account_id = std::move(account_id);
requesters_.back().on_available = run_loop.QuitClosure();
run_loop.Run();
}
FakeProfileOAuth2TokenService* IdentityTestEnvironment::fake_token_service() {
// We can't absolutely guarantee that IdentityTestEnvironment was not given an
// IdentityManager that uses a non-fake FakeProfileOAuth2TokenService. If that
// ever happens, this will blow up. There doesn't seem to be a better option.
return static_cast<FakeProfileOAuth2TokenService*>(
identity_manager()->GetTokenService());
}
void IdentityTestEnvironment::UpdateAccountInfoForAccount(
AccountInfo account_info) {
identity::UpdateAccountInfoForAccount(identity_manager(), account_info);
}
void IdentityTestEnvironment::ResetToAccountsNotYetLoadedFromDiskState() {
fake_token_service()->set_all_credentials_loaded_for_testing(false);
}
void IdentityTestEnvironment::ReloadAccountsFromDisk() {
fake_token_service()->LoadCredentials("");
}
bool IdentityTestEnvironment::IsAccessTokenRequestPending() {
return fake_token_service()->GetPendingRequests().size();
}
void IdentityTestEnvironment::SetFreshnessOfAccountsInGaiaCookie(
bool accounts_are_fresh) {
identity::SetFreshnessOfAccountsInGaiaCookie(identity_manager(),
accounts_are_fresh);
}
void IdentityTestEnvironment::EnableRemovalOfExtendedAccountInfo() {
identity_manager()->GetAccountFetcherService()->EnableAccountRemovalForTest();
}
void IdentityTestEnvironment::SimulateSuccessfulFetchOfAccountInfo(
const std::string& account_id,
const std::string& email,
const std::string& gaia,
const std::string& hosted_domain,
const std::string& full_name,
const std::string& given_name,
const std::string& locale,
const std::string& picture_url) {
identity::SimulateSuccessfulFetchOfAccountInfo(
identity_manager(), account_id, email, gaia, hosted_domain, full_name,
given_name, locale, picture_url);
}
void IdentityTestEnvironment::SimulateMergeSessionFailure(
const GoogleServiceAuthError& auth_error) {
// GaiaCookieManagerService changes the visibility of inherited method
// OnMergeSessionFailure from public to private. Cast to a base class
// pointer to call the method.
static_cast<GaiaAuthConsumer*>(
identity_manager()->GetGaiaCookieManagerService())
->OnMergeSessionFailure(auth_error);
}
} // namespace identity