blob: 0fb277d606d987c50b1f6a7b26910ad456670d2f [file] [log] [blame]
// Copyright 2015 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/signin/core/browser/child_account_info_fetcher_impl.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "components/invalidation/public/invalidation_service.h"
#include "components/invalidation/public/object_id_invalidation_map.h"
#include "components/signin/core/browser/account_fetcher_service.h"
#include "components/signin/core/browser/account_tracker_service.h"
#include "components/signin/core/browser/signin_client.h"
#include "google/cacheinvalidation/types.pb.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_constants.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
// TODO(maroun): Remove this file.
namespace {
const char kFetcherId[] = "ChildAccountInfoFetcherImpl";
// Exponential backoff policy on service flag fetching failure.
const net::BackoffEntry::Policy kBackoffPolicy = {
0, // Number of initial errors to ignore without backoff.
2000, // Initial delay for backoff in ms.
2, // Factor to multiply waiting time by.
0.2, // Fuzzing percentage. 20% will spread requests randomly between
// 80-100% of the calculated time.
1000 * 60 * 60* 4, // Maximum time to delay requests by (4 hours).
-1, // Don't discard entry even if unused.
false, // Don't use the initial delay unless the last request was an error.
};
// The invalidation object ID used for child account graduation event.
// The syntax is:
// 'U' -> This is a user specific invalidation.
// 'CA' -> Namespace used for all ChildAccount invalidations.
// 'GRAD' -> Indicates the actual event i.e. child account graduation.
const char kChildAccountGraduationId[] = "UCAGRAD";
} // namespace
ChildAccountInfoFetcherImpl::ChildAccountInfoFetcherImpl(
const std::string& account_id,
AccountFetcherService* fetcher_service,
OAuth2TokenService* token_service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
invalidation::InvalidationService* invalidation_service)
: OAuth2TokenService::Consumer(kFetcherId),
token_service_(token_service),
url_loader_factory_(url_loader_factory),
fetcher_service_(fetcher_service),
invalidation_service_(invalidation_service),
account_id_(account_id),
backoff_(&kBackoffPolicy),
fetch_in_progress_(false) {
TRACE_EVENT_ASYNC_BEGIN1("AccountFetcherService", kFetcherId, this,
"account_id", account_id);
// Invalidation service may not be available in tests.
if (invalidation_service_) {
invalidation_service_->RegisterInvalidationHandler(this);
syncer::ObjectIdSet ids;
ids.insert(invalidation::ObjectId(
ipc::invalidation::ObjectSource::CHROME_COMPONENTS,
kChildAccountGraduationId));
bool insert_success =
invalidation_service_->UpdateRegisteredInvalidationIds(this, ids);
DCHECK(insert_success);
}
FetchIfNotInProgress();
}
ChildAccountInfoFetcherImpl::~ChildAccountInfoFetcherImpl() {
TRACE_EVENT_ASYNC_END0("AccountFetcherService", kFetcherId, this);
if (invalidation_service_)
UnregisterInvalidationHandler();
}
void ChildAccountInfoFetcherImpl::FetchIfNotInProgress() {
DCHECK(thread_checker_.CalledOnValidThread());
if (fetch_in_progress_)
return;
fetch_in_progress_ = true;
OAuth2TokenService::ScopeSet scopes;
scopes.insert(GaiaConstants::kOAuth1LoginScope);
login_token_request_ =
token_service_->StartRequest(account_id_, scopes, this);
}
void ChildAccountInfoFetcherImpl::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) {
TRACE_EVENT_ASYNC_STEP_PAST0("AccountFetcherService", kFetcherId, this,
"OnGetTokenSuccess");
DCHECK_EQ(request, login_token_request_.get());
gaia_auth_fetcher_ = fetcher_service_->signin_client_->CreateGaiaAuthFetcher(
this, GaiaConstants::kChromeSource, url_loader_factory_);
gaia_auth_fetcher_->StartOAuthLogin(access_token,
GaiaConstants::kGaiaService);
}
void ChildAccountInfoFetcherImpl::OnGetTokenFailure(
const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
HandleFailure();
}
void ChildAccountInfoFetcherImpl::OnClientLoginSuccess(
const ClientLoginResult& result) {
gaia_auth_fetcher_->StartGetUserInfo(result.lsid);
}
void ChildAccountInfoFetcherImpl::OnClientLoginFailure(
const GoogleServiceAuthError& error) {
HandleFailure();
}
void ChildAccountInfoFetcherImpl::OnGetUserInfoSuccess(
const UserInfoMap& data) {
UserInfoMap::const_iterator services_iter = data.find("allServices");
if (services_iter != data.end()) {
std::vector<std::string> service_flags = base::SplitString(
services_iter->second, ",", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL);
bool is_child_account = base::ContainsValue(
service_flags, AccountTrackerService::kChildAccountServiceFlag);
if (!is_child_account && invalidation_service_) {
// Don't bother listening for invalidations as a non-child account can't
// become a child account.
bool insert_success =
invalidation_service_->UpdateRegisteredInvalidationIds(
this, syncer::ObjectIdSet());
DCHECK(insert_success);
UnregisterInvalidationHandler();
}
fetcher_service_->SetIsChildAccount(account_id_, is_child_account);
} else {
DLOG(ERROR) << "ChildAccountInfoFetcherImpl::OnGetUserInfoSuccess: "
<< "GetUserInfo response didn't include allServices field.";
}
fetch_in_progress_ = false;
}
void ChildAccountInfoFetcherImpl::OnGetUserInfoFailure(
const GoogleServiceAuthError& error) {
HandleFailure();
}
void ChildAccountInfoFetcherImpl::HandleFailure() {
fetch_in_progress_ = false;
backoff_.InformOfRequest(false);
timer_.Start(FROM_HERE, backoff_.GetTimeUntilRelease(), this,
&ChildAccountInfoFetcherImpl::FetchIfNotInProgress);
}
void ChildAccountInfoFetcherImpl::UnregisterInvalidationHandler() {
invalidation_service_->UnregisterInvalidationHandler(this);
invalidation_service_ = nullptr;
}
void ChildAccountInfoFetcherImpl::OnInvalidatorStateChange(
syncer::InvalidatorState state) {
if (state == syncer::INVALIDATOR_SHUTTING_DOWN)
UnregisterInvalidationHandler();
}
void ChildAccountInfoFetcherImpl::OnIncomingInvalidation(
const syncer::ObjectIdInvalidationMap& invalidation_map) {
FetchIfNotInProgress();
invalidation_map.AcknowledgeAll();
}
std::string ChildAccountInfoFetcherImpl::GetOwnerName() const {
return std::string(kFetcherId);
}