blob: 1659e1bea839f87b6d1b38b140ec805aeb6c5219 [file] [log] [blame]
// Copyright 2020 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 "components/account_manager_core/account_manager_facade_impl.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "chromeos/crosapi/mojom/account_manager.mojom.h"
#include "components/account_manager_core/account.h"
#include "components/account_manager_core/account_addition_result.h"
#include "components/account_manager_core/account_manager_util.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "google_apis/gaia/oauth2_access_token_consumer.h"
#include "google_apis/gaia/oauth2_access_token_fetcher.h"
#include "google_apis/gaia/oauth2_access_token_fetcher_immediate_error.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace account_manager {
namespace {
using RemoteMinVersions = crosapi::mojom::AccountManager::MethodMinVersions;
// UMA histogram name.
const char kAccountAdditionResultStatus[] =
"AccountManager.AccountAdditionResultStatus";
void UnmarshalAccounts(
base::OnceCallback<void(const std::vector<Account>&)> callback,
std::vector<crosapi::mojom::AccountPtr> mojo_accounts) {
std::vector<Account> accounts;
for (const auto& mojo_account : mojo_accounts) {
absl::optional<Account> maybe_account = FromMojoAccount(mojo_account);
if (!maybe_account) {
// Skip accounts we couldn't unmarshal. No logging, as it would produce
// a lot of noise.
continue;
}
accounts.emplace_back(std::move(maybe_account.value()));
}
std::move(callback).Run(std::move(accounts));
}
void UnmarshalPersistentError(
base::OnceCallback<void(const GoogleServiceAuthError&)> callback,
crosapi::mojom::GoogleServiceAuthErrorPtr mojo_error) {
absl::optional<GoogleServiceAuthError> maybe_error =
FromMojoGoogleServiceAuthError(mojo_error);
if (!maybe_error) {
// Couldn't unmarshal GoogleServiceAuthError, report the account as not
// having an error. This is safe to do, as GetPersistentErrorForAccount is
// best-effort (there's no way to know that the token was revoked on the
// server).
std::move(callback).Run(GoogleServiceAuthError::AuthErrorNone());
return;
}
std::move(callback).Run(maybe_error.value());
}
} // namespace
// Fetches access tokens over the Mojo remote to `AccountManager`.
class AccountManagerFacadeImpl::AccessTokenFetcher
: public OAuth2AccessTokenFetcher {
public:
AccessTokenFetcher(AccountManagerFacadeImpl* account_manager_facade_impl,
const account_manager::AccountKey& account_key,
const std::string& oauth_consumer_name,
OAuth2AccessTokenConsumer* consumer)
: OAuth2AccessTokenFetcher(consumer),
account_manager_facade_impl_(account_manager_facade_impl),
account_key_(account_key),
oauth_consumer_name_(oauth_consumer_name) {}
AccessTokenFetcher(const AccessTokenFetcher&) = delete;
AccessTokenFetcher& operator=(const AccessTokenFetcher&) = delete;
~AccessTokenFetcher() override = default;
// Returns a closure, which marks `this` instance as ready for use. This
// happens when `AccountManagerFacadeImpl`'s initialization sequence is
// complete.
base::OnceClosure UnblockTokenRequest() {
return base::BindOnce(&AccessTokenFetcher::UnblockTokenRequestInternal,
weak_factory_.GetWeakPtr());
}
// Returns a closure which handles Mojo connection errors tied to Account
// Manager.
base::OnceClosure MojoDisconnectionClosure() {
return base::BindOnce(&AccessTokenFetcher::OnMojoError,
weak_factory_.GetWeakPtr());
}
// OAuth2AccessTokenFetcher override:
// Note: This implementation ignores `client_id` and `client_secret` because
// AccountManager's Mojo API does not support overriding OAuth client id and
// secret.
void Start(const std::string& client_id,
const std::string& client_secret,
const std::vector<std::string>& scopes) override {
DCHECK(!is_request_pending_);
is_request_pending_ = true;
scopes_ = scopes;
if (!are_token_requests_allowed_) {
return;
}
StartInternal();
}
// OAuth2AccessTokenFetcher override:
void CancelRequest() override {
access_token_fetcher_.reset();
is_request_pending_ = false;
}
private:
void UnblockTokenRequestInternal() {
are_token_requests_allowed_ = true;
if (is_request_pending_) {
StartInternal();
}
}
void StartInternal() {
DCHECK(are_token_requests_allowed_);
bool is_remote_connected =
account_manager_facade_impl_->CreateAccessTokenFetcher(
account_manager::ToMojoAccountKey(account_key_),
oauth_consumer_name_,
base::BindOnce(&AccessTokenFetcher::FetchAccessToken,
weak_factory_.GetWeakPtr()));
if (!is_remote_connected) {
OnMojoError();
}
}
void FetchAccessToken(
mojo::PendingRemote<crosapi::mojom::AccessTokenFetcher> pending_remote) {
access_token_fetcher_.Bind(std::move(pending_remote));
access_token_fetcher_->Start(
scopes_, base::BindOnce(&AccessTokenFetcher::OnAccessTokenFetchComplete,
weak_factory_.GetWeakPtr()));
}
void OnAccessTokenFetchComplete(crosapi::mojom::AccessTokenResultPtr result) {
DCHECK(is_request_pending_);
is_request_pending_ = false;
if (result->is_error()) {
absl::optional<GoogleServiceAuthError> maybe_error =
account_manager::FromMojoGoogleServiceAuthError(result->get_error());
if (!maybe_error.has_value()) {
LOG(ERROR) << "Unable to parse error result of access token fetch: "
<< result->get_error()->state;
FireOnGetTokenFailure(GoogleServiceAuthError(
GoogleServiceAuthError::State::UNEXPECTED_SERVICE_RESPONSE));
} else {
FireOnGetTokenFailure(maybe_error.value());
}
return;
}
FireOnGetTokenSuccess(
OAuth2AccessTokenConsumer::TokenResponse::Builder()
.WithAccessToken(result->get_access_token_info()->access_token)
.WithExpirationTime(
result->get_access_token_info()->expiration_time)
.WithIdToken(result->get_access_token_info()->id_token)
.build());
}
void OnMojoError() {
if (!is_request_pending_)
return;
CancelRequest();
FireOnGetTokenFailure(
GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR));
}
AccountManagerFacadeImpl* const account_manager_facade_impl_;
const account_manager::AccountKey account_key_;
const std::string oauth_consumer_name_;
bool are_token_requests_allowed_ = false;
bool is_request_pending_ = false;
std::vector<std::string> scopes_;
mojo::Remote<crosapi::mojom::AccessTokenFetcher> access_token_fetcher_;
base::WeakPtrFactory<AccessTokenFetcher> weak_factory_{this};
};
AccountManagerFacadeImpl::AccountManagerFacadeImpl(
mojo::Remote<crosapi::mojom::AccountManager> account_manager_remote,
uint32_t remote_version,
base::OnceClosure init_finished)
: remote_version_(remote_version),
account_manager_remote_(std::move(account_manager_remote)) {
DCHECK(init_finished);
initialization_callbacks_.emplace_back(std::move(init_finished));
if (!account_manager_remote_ ||
remote_version_ < RemoteMinVersions::kGetAccountsMinVersion) {
LOG(WARNING) << "Found remote at: " << remote_version_
<< ", expected: " << RemoteMinVersions::kGetAccountsMinVersion
<< ". Account consistency will be disabled";
FinishInitSequenceIfNotAlreadyFinished();
return;
}
account_manager_remote_.set_disconnect_handler(base::BindOnce(
&AccountManagerFacadeImpl::OnMojoError, weak_factory_.GetWeakPtr()));
account_manager_remote_->AddObserver(
base::BindOnce(&AccountManagerFacadeImpl::OnReceiverReceived,
weak_factory_.GetWeakPtr()));
}
AccountManagerFacadeImpl::~AccountManagerFacadeImpl() = default;
void AccountManagerFacadeImpl::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void AccountManagerFacadeImpl::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void AccountManagerFacadeImpl::GetAccounts(
base::OnceCallback<void(const std::vector<Account>&)> callback) {
if (!account_manager_remote_ ||
remote_version_ < RemoteMinVersions::kGetAccountsMinVersion) {
// Remote side doesn't support GetAccounts, return an empty list.
std::move(callback).Run({});
return;
}
RunAfterInitializationSequence(
base::BindOnce(&AccountManagerFacadeImpl::GetAccountsInternal,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void AccountManagerFacadeImpl::GetPersistentErrorForAccount(
const AccountKey& account,
base::OnceCallback<void(const GoogleServiceAuthError&)> callback) {
if (!account_manager_remote_ ||
remote_version_ <
RemoteMinVersions::kGetPersistentErrorForAccountMinVersion) {
// Remote side doesn't support GetPersistentErrorForAccount.
std::move(callback).Run(GoogleServiceAuthError::AuthErrorNone());
return;
}
RunAfterInitializationSequence(
base::BindOnce(&AccountManagerFacadeImpl::GetPersistentErrorInternal,
weak_factory_.GetWeakPtr(), account, std::move(callback)));
}
void AccountManagerFacadeImpl::ShowAddAccountDialog(
AccountAdditionSource source) {
ShowAddAccountDialog(
source,
base::DoNothing::Once<const account_manager::AccountAdditionResult&>());
}
void AccountManagerFacadeImpl::ShowAddAccountDialog(
AccountAdditionSource source,
base::OnceCallback<
void(const account_manager::AccountAdditionResult& result)> callback) {
if (!account_manager_remote_ ||
remote_version_ < RemoteMinVersions::kShowAddAccountDialogMinVersion) {
LOG(WARNING) << "Found remote at: " << remote_version_ << ", expected: "
<< RemoteMinVersions::kShowAddAccountDialogMinVersion
<< " for ShowAddAccountDialog.";
FinishAddAccount(std::move(callback),
account_manager::AccountAdditionResult(
account_manager::AccountAdditionResult::Status::
kUnexpectedResponse));
return;
}
base::UmaHistogramEnumeration(kAccountAdditionSource, source);
account_manager_remote_->ShowAddAccountDialog(
base::BindOnce(&AccountManagerFacadeImpl::OnShowAddAccountDialogFinished,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void AccountManagerFacadeImpl::ShowReauthAccountDialog(
AccountAdditionSource source,
const std::string& email) {
if (!account_manager_remote_ ||
remote_version_ < RemoteMinVersions::kShowReauthAccountDialogMinVersion) {
LOG(WARNING) << "Found remote at: " << remote_version_ << ", expected: "
<< RemoteMinVersions::kShowReauthAccountDialogMinVersion
<< " for ShowReauthAccountDialog.";
return;
}
base::UmaHistogramEnumeration(kAccountAdditionSource, source);
account_manager_remote_->ShowReauthAccountDialog(email, base::DoNothing());
}
void AccountManagerFacadeImpl::ShowManageAccountsSettings() {
if (!account_manager_remote_ ||
remote_version_ <
RemoteMinVersions::kShowManageAccountsSettingsMinVersion) {
LOG(WARNING) << "Found remote at: " << remote_version_ << ", expected: "
<< RemoteMinVersions::kShowManageAccountsSettingsMinVersion
<< " for ShowManageAccountsSettings.";
return;
}
account_manager_remote_->ShowManageAccountsSettings();
}
std::unique_ptr<OAuth2AccessTokenFetcher>
AccountManagerFacadeImpl::CreateAccessTokenFetcher(
const AccountKey& account,
const std::string& oauth_consumer_name,
OAuth2AccessTokenConsumer* consumer) {
if (!account_manager_remote_ ||
remote_version_ <
RemoteMinVersions::kCreateAccessTokenFetcherMinVersion) {
VLOG(1) << "Found remote at: " << remote_version_ << ", expected: "
<< RemoteMinVersions::kCreateAccessTokenFetcherMinVersion
<< " for CreateAccessTokenFetcher";
return std::make_unique<OAuth2AccessTokenFetcherImmediateError>(
consumer,
GoogleServiceAuthError(GoogleServiceAuthError::State::SERVICE_ERROR));
}
auto access_token_fetcher = std::make_unique<AccessTokenFetcher>(
/*account_manager_facade_impl=*/this, account, oauth_consumer_name,
consumer);
RunAfterInitializationSequence(access_token_fetcher->UnblockTokenRequest());
RunOnMojoDisconnection(access_token_fetcher->MojoDisconnectionClosure());
return std::move(access_token_fetcher);
}
// static
std::string AccountManagerFacadeImpl::
GetAccountAdditionResultStatusHistogramNameForTesting() {
return kAccountAdditionResultStatus;
}
void AccountManagerFacadeImpl::OnReceiverReceived(
mojo::PendingReceiver<AccountManagerObserver> receiver) {
receiver_ =
std::make_unique<mojo::Receiver<crosapi::mojom::AccountManagerObserver>>(
this, std::move(receiver));
// At this point (`receiver_` exists), we are subscribed to Account Manager.
FinishInitSequenceIfNotAlreadyFinished();
}
void AccountManagerFacadeImpl::OnShowAddAccountDialogFinished(
base::OnceCallback<
void(const account_manager::AccountAdditionResult& result)> callback,
crosapi::mojom::AccountAdditionResultPtr mojo_result) {
absl::optional<account_manager::AccountAdditionResult> result =
account_manager::FromMojoAccountAdditionResult(mojo_result);
if (!result.has_value()) {
FinishAddAccount(std::move(callback),
account_manager::AccountAdditionResult(
account_manager::AccountAdditionResult::Status::
kUnexpectedResponse));
return;
}
FinishAddAccount(std::move(callback), result.value());
}
void AccountManagerFacadeImpl::FinishAddAccount(
base::OnceCallback<
void(const account_manager::AccountAdditionResult& result)> callback,
const account_manager::AccountAdditionResult& result) {
base::UmaHistogramEnumeration(kAccountAdditionResultStatus, result.status);
std::move(callback).Run(result);
}
void AccountManagerFacadeImpl::OnTokenUpserted(
crosapi::mojom::AccountPtr account) {
absl::optional<Account> maybe_account = FromMojoAccount(account);
if (!maybe_account) {
LOG(WARNING) << "Can't unmarshal account of type: "
<< account->key->account_type;
return;
}
for (auto& observer : observer_list_) {
observer.OnAccountUpserted(maybe_account.value());
}
}
void AccountManagerFacadeImpl::OnAccountRemoved(
crosapi::mojom::AccountPtr account) {
absl::optional<Account> maybe_account = FromMojoAccount(account);
if (!maybe_account) {
LOG(WARNING) << "Can't unmarshal account of type: "
<< account->key->account_type;
return;
}
for (auto& observer : observer_list_) {
observer.OnAccountRemoved(maybe_account.value());
}
}
void AccountManagerFacadeImpl::GetAccountsInternal(
base::OnceCallback<void(const std::vector<Account>&)> callback) {
account_manager_remote_->GetAccounts(
base::BindOnce(&UnmarshalAccounts, std::move(callback)));
}
void AccountManagerFacadeImpl::GetPersistentErrorInternal(
const AccountKey& account,
base::OnceCallback<void(const GoogleServiceAuthError&)> callback) {
account_manager_remote_->GetPersistentErrorForAccount(
ToMojoAccountKey(account),
base::BindOnce(&UnmarshalPersistentError, std::move(callback)));
}
bool AccountManagerFacadeImpl::CreateAccessTokenFetcher(
crosapi::mojom::AccountKeyPtr account_key,
const std::string& oauth_consumer_name,
crosapi::mojom::AccountManager::CreateAccessTokenFetcherCallback callback) {
if (!account_manager_remote_) {
return false;
}
account_manager_remote_->CreateAccessTokenFetcher(
std::move(account_key), oauth_consumer_name, std::move(callback));
return true;
}
void AccountManagerFacadeImpl::FinishInitSequenceIfNotAlreadyFinished() {
if (is_initialized_) {
return;
}
is_initialized_ = true;
for (auto& cb : initialization_callbacks_) {
std::move(cb).Run();
}
initialization_callbacks_.clear();
}
void AccountManagerFacadeImpl::RunAfterInitializationSequence(
base::OnceClosure closure) {
if (!is_initialized_) {
initialization_callbacks_.emplace_back(std::move(closure));
} else {
std::move(closure).Run();
}
}
void AccountManagerFacadeImpl::RunOnMojoDisconnection(
base::OnceClosure closure) {
if (!account_manager_remote_) {
std::move(closure).Run();
return;
}
mojo_disconnection_handlers_.emplace_back(std::move(closure));
}
void AccountManagerFacadeImpl::OnMojoError() {
LOG(ERROR) << "Account Manager disconnected";
for (auto& cb : mojo_disconnection_handlers_) {
std::move(cb).Run();
}
mojo_disconnection_handlers_.clear();
account_manager_remote_.reset();
}
bool AccountManagerFacadeImpl::IsInitialized() {
return is_initialized_;
}
void AccountManagerFacadeImpl::FlushMojoForTesting() {
if (!account_manager_remote_) {
return;
}
account_manager_remote_.FlushForTesting();
}
} // namespace account_manager