summaryrefslogtreecommitdiffstats
path: root/components/signin
diff options
context:
space:
mode:
authorblundell@chromium.org <blundell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-19 18:07:40 +0000
committerblundell@chromium.org <blundell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-19 18:07:40 +0000
commit835e98e142061272a6c46dfc73626ba3f8f64caa (patch)
treec4a3e81a4ffbd16659b009191068d5afd081a171 /components/signin
parentee786bf8adf20631adab484690f44b3af0fd5783 (diff)
downloadchromium_src-835e98e142061272a6c46dfc73626ba3f8f64caa.zip
chromium_src-835e98e142061272a6c46dfc73626ba3f8f64caa.tar.gz
chromium_src-835e98e142061272a6c46dfc73626ba3f8f64caa.tar.bz2
Componentize (Mutable)ProfileOAuth2TokenService.
This CL componentizes the following: - ProfileOAuth2TokenService (obsolete references to Profile removed) - MutableProfileOAuth2TokenService (no changes required) - SigninErrorController (no changes required) BUG=334156,333993,334217 TBR=jochen Review URL: https://codereview.chromium.org/201163002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@258039 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components/signin')
-rw-r--r--components/signin/DEPS4
-rw-r--r--components/signin/core/mutable_profile_oauth2_token_service.cc384
-rw-r--r--components/signin/core/mutable_profile_oauth2_token_service.h144
-rw-r--r--components/signin/core/profile_oauth2_token_service.cc65
-rw-r--r--components/signin/core/profile_oauth2_token_service.h104
-rw-r--r--components/signin/core/signin_error_controller.cc106
-rw-r--r--components/signin/core/signin_error_controller.h79
7 files changed, 884 insertions, 2 deletions
diff --git a/components/signin/DEPS b/components/signin/DEPS
index eddcc27..046e713 100644
--- a/components/signin/DEPS
+++ b/components/signin/DEPS
@@ -1,11 +1,11 @@
include_rules = [
+ "+components/keyed_service/core",
"+components/os_crypt",
"+components/webdata/common",
- "+google_apis/gaia/gaia_urls.h",
+ "+google_apis/gaia",
"+grit", # For generated headers
"+net",
"+sql",
- "+ui",
# Signin is a layered component; subdirectories must explicitly introduce
# the ability to use the content layer as appropriate.
"-components/signin/content",
diff --git a/components/signin/core/mutable_profile_oauth2_token_service.cc b/components/signin/core/mutable_profile_oauth2_token_service.cc
new file mode 100644
index 0000000..75e7c86
--- /dev/null
+++ b/components/signin/core/mutable_profile_oauth2_token_service.cc
@@ -0,0 +1,384 @@
+// Copyright 2014 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/mutable_profile_oauth2_token_service.h"
+
+#include "components/signin/core/signin_client.h"
+#include "components/signin/core/webdata/token_web_data.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "google_apis/gaia/gaia_auth_fetcher.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace {
+
+const char kAccountIdPrefix[] = "AccountId-";
+const size_t kAccountIdPrefixLength = 10;
+
+std::string ApplyAccountIdPrefix(const std::string& account_id) {
+ return kAccountIdPrefix + account_id;
+}
+
+bool IsLegacyRefreshTokenId(const std::string& service_id) {
+ return service_id == GaiaConstants::kGaiaOAuth2LoginRefreshToken;
+}
+
+bool IsLegacyServiceId(const std::string& account_id) {
+ return account_id.compare(0u, kAccountIdPrefixLength, kAccountIdPrefix) != 0;
+}
+
+std::string RemoveAccountIdPrefix(const std::string& prefixed_account_id) {
+ return prefixed_account_id.substr(kAccountIdPrefixLength);
+}
+
+} // namespace
+
+// This class sends a request to GAIA to revoke the given refresh token from
+// the server. This is a best effort attempt only. This class deletes itself
+// when done sucessfully or otherwise.
+class MutableProfileOAuth2TokenService::RevokeServerRefreshToken
+ : public GaiaAuthConsumer {
+ public:
+ RevokeServerRefreshToken(MutableProfileOAuth2TokenService* token_service,
+ const std::string& account_id);
+ virtual ~RevokeServerRefreshToken();
+
+ private:
+ // GaiaAuthConsumer overrides:
+ virtual void OnOAuth2RevokeTokenCompleted() OVERRIDE;
+
+ MutableProfileOAuth2TokenService* token_service_;
+ GaiaAuthFetcher fetcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(RevokeServerRefreshToken);
+};
+
+MutableProfileOAuth2TokenService::
+ RevokeServerRefreshToken::RevokeServerRefreshToken(
+ MutableProfileOAuth2TokenService* token_service,
+ const std::string& refresh_token)
+ : token_service_(token_service),
+ fetcher_(this, GaiaConstants::kChromeSource,
+ token_service_->GetRequestContext()) {
+ fetcher_.StartRevokeOAuth2Token(refresh_token);
+}
+
+MutableProfileOAuth2TokenService::
+ RevokeServerRefreshToken::~RevokeServerRefreshToken() {}
+
+void MutableProfileOAuth2TokenService::
+ RevokeServerRefreshToken::OnOAuth2RevokeTokenCompleted() {
+ // |this| pointer will be deleted when removed from the vector, so don't
+ // access any members after call to erase().
+ token_service_->server_revokes_.erase(
+ std::find(token_service_->server_revokes_.begin(),
+ token_service_->server_revokes_.end(),
+ this));
+}
+
+MutableProfileOAuth2TokenService::AccountInfo::AccountInfo(
+ ProfileOAuth2TokenService* token_service,
+ const std::string& account_id,
+ const std::string& refresh_token)
+ : token_service_(token_service),
+ account_id_(account_id),
+ refresh_token_(refresh_token),
+ last_auth_error_(GoogleServiceAuthError::NONE) {
+ DCHECK(token_service_);
+ DCHECK(!account_id_.empty());
+ token_service_->signin_error_controller()->AddProvider(this);
+}
+
+MutableProfileOAuth2TokenService::AccountInfo::~AccountInfo() {
+ token_service_->signin_error_controller()->RemoveProvider(this);
+}
+
+void MutableProfileOAuth2TokenService::AccountInfo::SetLastAuthError(
+ const GoogleServiceAuthError& error) {
+ if (error.state() != last_auth_error_.state()) {
+ last_auth_error_ = error;
+ token_service_->signin_error_controller()->AuthStatusChanged();
+ }
+}
+
+std::string
+MutableProfileOAuth2TokenService::AccountInfo::GetAccountId() const {
+ return account_id_;
+}
+
+GoogleServiceAuthError
+MutableProfileOAuth2TokenService::AccountInfo::GetAuthStatus() const {
+ return last_auth_error_;
+}
+
+MutableProfileOAuth2TokenService::MutableProfileOAuth2TokenService()
+ : web_data_service_request_(0) {
+}
+
+MutableProfileOAuth2TokenService::~MutableProfileOAuth2TokenService() {
+ DCHECK(server_revokes_.empty());
+}
+
+void MutableProfileOAuth2TokenService::Shutdown() {
+ server_revokes_.clear();
+ CancelWebTokenFetch();
+ CancelAllRequests();
+ refresh_tokens_.clear();
+
+ ProfileOAuth2TokenService::Shutdown();
+}
+
+bool MutableProfileOAuth2TokenService::RefreshTokenIsAvailable(
+ const std::string& account_id) const {
+ return !GetRefreshToken(account_id).empty();
+}
+
+std::string MutableProfileOAuth2TokenService::GetRefreshToken(
+ const std::string& account_id) const {
+ AccountInfoMap::const_iterator iter = refresh_tokens_.find(account_id);
+ if (iter != refresh_tokens_.end())
+ return iter->second->refresh_token();
+ return std::string();
+}
+
+OAuth2AccessTokenFetcher*
+MutableProfileOAuth2TokenService::CreateAccessTokenFetcher(
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ OAuth2AccessTokenConsumer* consumer) {
+ std::string refresh_token = GetRefreshToken(account_id);
+ DCHECK(!refresh_token.empty());
+ return new OAuth2AccessTokenFetcherImpl(consumer, getter, refresh_token);
+}
+
+net::URLRequestContextGetter*
+MutableProfileOAuth2TokenService::GetRequestContext() {
+ return client()->GetURLRequestContext();
+}
+
+void MutableProfileOAuth2TokenService::LoadCredentials(
+ const std::string& primary_account_id) {
+ DCHECK(!primary_account_id.empty());
+ DCHECK(loading_primary_account_id_.empty());
+ DCHECK_EQ(0, web_data_service_request_);
+
+ CancelAllRequests();
+ refresh_tokens().clear();
+ loading_primary_account_id_ = primary_account_id;
+ scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
+ if (token_web_data.get())
+ web_data_service_request_ = token_web_data->GetAllTokens(this);
+}
+
+void MutableProfileOAuth2TokenService::OnWebDataServiceRequestDone(
+ WebDataServiceBase::Handle handle,
+ const WDTypedResult* result) {
+ DCHECK_EQ(web_data_service_request_, handle);
+ web_data_service_request_ = 0;
+
+ if (result) {
+ DCHECK(result->GetType() == TOKEN_RESULT);
+ const WDResult<std::map<std::string, std::string> > * token_result =
+ static_cast<const WDResult<std::map<std::string, std::string> > * > (
+ result);
+ LoadAllCredentialsIntoMemory(token_result->GetValue());
+ }
+
+ // Make sure that we have an entry for |loading_primary_account_id_| in the
+ // map. The entry could be missing if there is a corruption in the token DB
+ // while this profile is connected to an account.
+ DCHECK(!loading_primary_account_id_.empty());
+ if (refresh_tokens().count(loading_primary_account_id_) == 0) {
+ refresh_tokens()[loading_primary_account_id_].reset(
+ new AccountInfo(this, loading_primary_account_id_, std::string()));
+ }
+
+ // If we don't have a refresh token for a known account, signal an error.
+ for (AccountInfoMap::const_iterator i = refresh_tokens_.begin();
+ i != refresh_tokens_.end(); ++i) {
+ if (!RefreshTokenIsAvailable(i->first)) {
+ UpdateAuthError(
+ i->first,
+ GoogleServiceAuthError(
+ GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
+ break;
+ }
+ }
+
+ loading_primary_account_id_.clear();
+}
+
+void MutableProfileOAuth2TokenService::LoadAllCredentialsIntoMemory(
+ const std::map<std::string, std::string>& db_tokens) {
+ std::string old_login_token;
+
+ for (std::map<std::string, std::string>::const_iterator iter =
+ db_tokens.begin();
+ iter != db_tokens.end();
+ ++iter) {
+ std::string prefixed_account_id = iter->first;
+ std::string refresh_token = iter->second;
+
+ if (IsLegacyRefreshTokenId(prefixed_account_id) && !refresh_token.empty())
+ old_login_token = refresh_token;
+
+ if (IsLegacyServiceId(prefixed_account_id)) {
+ scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
+ if (token_web_data.get())
+ token_web_data->RemoveTokenForService(prefixed_account_id);
+ } else {
+ DCHECK(!refresh_token.empty());
+ std::string account_id = RemoveAccountIdPrefix(prefixed_account_id);
+ refresh_tokens()[account_id].reset(
+ new AccountInfo(this, account_id, refresh_token));
+ FireRefreshTokenAvailable(account_id);
+ // TODO(fgorski): Notify diagnostic observers.
+ }
+ }
+
+ if (!old_login_token.empty()) {
+ DCHECK(!loading_primary_account_id_.empty());
+ if (refresh_tokens().count(loading_primary_account_id_) == 0)
+ UpdateCredentials(loading_primary_account_id_, old_login_token);
+ }
+
+ FireRefreshTokensLoaded();
+}
+
+void MutableProfileOAuth2TokenService::UpdateAuthError(
+ const std::string& account_id,
+ const GoogleServiceAuthError& error) {
+ // Do not report connection errors as these are not actually auth errors.
+ // We also want to avoid masking a "real" auth error just because we
+ // subsequently get a transient network error.
+ if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
+ error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE)
+ return;
+
+#if defined(OS_IOS)
+ // ProfileOauth2TokenService does not manage the refresh tokens on iOS - the
+ // account info on iOS is only used to manage the authentication error state.
+ // Simply add an account info entry with empty refresh token if none exists.
+ if (refresh_tokens_.count(account_id) == 0) {
+ refresh_tokens_[account_id].reset(
+ new AccountInfo(this, account_id, std::string()));
+ }
+#endif
+
+ if (refresh_tokens_.count(account_id) == 0) {
+ // This could happen if the preferences have been corrupted (see
+ // http://crbug.com/321370). In a Debug build that would be a bug, but in a
+ // Release build we want to deal with it gracefully.
+ NOTREACHED();
+ return;
+ }
+ refresh_tokens_[account_id]->SetLastAuthError(error);
+}
+
+std::vector<std::string> MutableProfileOAuth2TokenService::GetAccounts() {
+ std::vector<std::string> account_ids;
+ for (AccountInfoMap::const_iterator iter = refresh_tokens_.begin();
+ iter != refresh_tokens_.end(); ++iter) {
+ account_ids.push_back(iter->first);
+ }
+ return account_ids;
+}
+
+void MutableProfileOAuth2TokenService::UpdateCredentials(
+ const std::string& account_id,
+ const std::string& refresh_token) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!account_id.empty());
+ DCHECK(!refresh_token.empty());
+
+ bool refresh_token_present = refresh_tokens_.count(account_id) > 0;
+ if (!refresh_token_present ||
+ refresh_tokens_[account_id]->refresh_token() != refresh_token) {
+ // If token present, and different from the new one, cancel its requests,
+ // and clear the entries in cache related to that account.
+ if (refresh_token_present) {
+ std::string revoke_reason = refresh_token_present ? "token differs" :
+ "token is missing";
+ LOG(WARNING) << "Revoking refresh token on server. "
+ << "Reason: token update, " << revoke_reason;
+ RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
+ CancelRequestsForAccount(account_id);
+ ClearCacheForAccount(account_id);
+ refresh_tokens_[account_id]->set_refresh_token(refresh_token);
+ } else {
+ refresh_tokens_[account_id].reset(
+ new AccountInfo(this, account_id, refresh_token));
+ }
+
+ // Save the token in memory and in persistent store.
+ PersistCredentials(account_id, refresh_token);
+
+ UpdateAuthError(account_id, GoogleServiceAuthError::AuthErrorNone());
+ FireRefreshTokenAvailable(account_id);
+ }
+}
+
+void MutableProfileOAuth2TokenService::RevokeCredentials(
+ const std::string& account_id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (refresh_tokens_.count(account_id) > 0) {
+ RevokeCredentialsOnServer(refresh_tokens_[account_id]->refresh_token());
+ CancelRequestsForAccount(account_id);
+ ClearCacheForAccount(account_id);
+ refresh_tokens_.erase(account_id);
+ ClearPersistedCredentials(account_id);
+ FireRefreshTokenRevoked(account_id);
+ }
+}
+
+void MutableProfileOAuth2TokenService::PersistCredentials(
+ const std::string& account_id,
+ const std::string& refresh_token) {
+ scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
+ if (token_web_data.get()) {
+ token_web_data->SetTokenForService(ApplyAccountIdPrefix(account_id),
+ refresh_token);
+ }
+}
+
+void MutableProfileOAuth2TokenService::ClearPersistedCredentials(
+ const std::string& account_id) {
+ scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
+ if (token_web_data.get())
+ token_web_data->RemoveTokenForService(ApplyAccountIdPrefix(account_id));
+}
+
+void MutableProfileOAuth2TokenService::RevokeAllCredentials() {
+ if (!client()->CanRevokeCredentials())
+ return;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ CancelWebTokenFetch();
+ CancelAllRequests();
+ ClearCache();
+ AccountInfoMap tokens = refresh_tokens_;
+ for (AccountInfoMap::iterator i = tokens.begin(); i != tokens.end(); ++i)
+ RevokeCredentials(i->first);
+
+ DCHECK_EQ(0u, refresh_tokens_.size());
+}
+
+void MutableProfileOAuth2TokenService::RevokeCredentialsOnServer(
+ const std::string& refresh_token) {
+ // Keep track or all server revoke requests. This way they can be deleted
+ // before the token service is shutdown and won't outlive the profile.
+ server_revokes_.push_back(
+ new RevokeServerRefreshToken(this, refresh_token));
+}
+
+void MutableProfileOAuth2TokenService::CancelWebTokenFetch() {
+ if (web_data_service_request_ != 0) {
+ scoped_refptr<TokenWebData> token_web_data = client()->GetDatabase();
+ DCHECK(token_web_data.get());
+ token_web_data->CancelRequest(web_data_service_request_);
+ web_data_service_request_ = 0;
+ }
+}
diff --git a/components/signin/core/mutable_profile_oauth2_token_service.h b/components/signin/core/mutable_profile_oauth2_token_service.h
new file mode 100644
index 0000000..b9ebc6e
--- /dev/null
+++ b/components/signin/core/mutable_profile_oauth2_token_service.h
@@ -0,0 +1,144 @@
+// Copyright 2014 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.
+
+#ifndef COMPONENTS_SIGNIN_CORE_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_H_
+#define COMPONENTS_SIGNIN_CORE_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_H_
+
+#include "base/memory/scoped_vector.h"
+#include "base/threading/thread_checker.h"
+#include "components/signin/core/profile_oauth2_token_service.h"
+#include "components/webdata/common/web_data_service_base.h"
+#include "components/webdata/common/web_data_service_consumer.h"
+
+// A specialization of ProfileOAuth2TokenService that can can mutate its OAuth2
+// tokens.
+//
+// Note: This class is just a placeholder for now. Methods used to mutate
+// the tokens are currently being migrated from ProfileOAuth2TokenService.
+class MutableProfileOAuth2TokenService : public ProfileOAuth2TokenService,
+ public WebDataServiceConsumer {
+ public:
+ // ProfileOAuth2TokenService overrides.
+ virtual void Shutdown() OVERRIDE;
+ virtual std::vector<std::string> GetAccounts() OVERRIDE;
+
+ // The below three methods should be called only on the thread on which this
+ // object was created.
+ virtual void LoadCredentials(const std::string& primary_account_id) OVERRIDE;
+ virtual void UpdateCredentials(const std::string& account_id,
+ const std::string& refresh_token) OVERRIDE;
+ virtual void RevokeAllCredentials() OVERRIDE;
+ virtual bool RefreshTokenIsAvailable(const std::string& account_id) const
+ OVERRIDE;
+
+ // Revokes credentials related to |account_id|.
+ void RevokeCredentials(const std::string& account_id);
+
+ protected:
+ class AccountInfo : public SigninErrorController::AuthStatusProvider {
+ public:
+ AccountInfo(ProfileOAuth2TokenService* token_service,
+ const std::string& account_id,
+ const std::string& refresh_token);
+ virtual ~AccountInfo();
+
+ const std::string& refresh_token() const { return refresh_token_; }
+ void set_refresh_token(const std::string& token) {
+ refresh_token_ = token;
+ }
+
+ void SetLastAuthError(const GoogleServiceAuthError& error);
+
+ // SigninErrorController::AuthStatusProvider implementation.
+ virtual std::string GetAccountId() const OVERRIDE;
+ virtual GoogleServiceAuthError GetAuthStatus() const OVERRIDE;
+
+ private:
+ ProfileOAuth2TokenService* token_service_;
+ std::string account_id_;
+ std::string refresh_token_;
+ GoogleServiceAuthError last_auth_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccountInfo);
+ };
+
+ // Maps the |account_id| of accounts known to ProfileOAuth2TokenService
+ // to information about the account.
+ typedef std::map<std::string, linked_ptr<AccountInfo> > AccountInfoMap;
+
+ friend class ProfileOAuth2TokenServiceWrapperImpl;
+
+ MutableProfileOAuth2TokenService();
+ virtual ~MutableProfileOAuth2TokenService();
+
+ // OAuth2TokenService implementation.
+ virtual OAuth2AccessTokenFetcher* CreateAccessTokenFetcher(
+ const std::string& account_id,
+ net::URLRequestContextGetter* getter,
+ OAuth2AccessTokenConsumer* consumer) OVERRIDE;
+ virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE;
+
+ // Updates the internal cache of the result from the most-recently-completed
+ // auth request (used for reporting errors to the user).
+ virtual void UpdateAuthError(const std::string& account_id,
+ const GoogleServiceAuthError& error) OVERRIDE;
+
+ virtual std::string GetRefreshToken(const std::string& account_id) const;
+
+ AccountInfoMap& refresh_tokens() { return refresh_tokens_; }
+
+ private:
+ class RevokeServerRefreshToken;
+
+ FRIEND_TEST_ALL_PREFIXES(MutableProfileOAuth2TokenServiceTest,
+ TokenServiceUpdateClearsCache);
+ FRIEND_TEST_ALL_PREFIXES(MutableProfileOAuth2TokenServiceTest,
+ PersistenceDBUpgrade);
+ FRIEND_TEST_ALL_PREFIXES(MutableProfileOAuth2TokenServiceTest,
+ PersistenceLoadCredentials);
+
+ // WebDataServiceConsumer implementation:
+ virtual void OnWebDataServiceRequestDone(
+ WebDataServiceBase::Handle handle,
+ const WDTypedResult* result) OVERRIDE;
+
+ // Loads credentials into in memory stucture.
+ void LoadAllCredentialsIntoMemory(
+ const std::map<std::string, std::string>& db_tokens);
+
+ // Persists credentials for |account_id|. Enables overriding for
+ // testing purposes, or other cases, when accessing the DB is not desired.
+ void PersistCredentials(const std::string& account_id,
+ const std::string& refresh_token);
+
+ // Clears credentials persisted for |account_id|. Enables overriding for
+ // testing purposes, or other cases, when accessing the DB is not desired.
+ void ClearPersistedCredentials(const std::string& account_id);
+
+ // Revokes the refresh token on the server.
+ void RevokeCredentialsOnServer(const std::string& refresh_token);
+
+ // Cancels any outstanding fetch for tokens from the web database.
+ void CancelWebTokenFetch();
+
+ // In memory refresh token store mapping account_id to refresh_token.
+ AccountInfoMap refresh_tokens_;
+
+ // Handle to the request reading tokens from database.
+ WebDataServiceBase::Handle web_data_service_request_;
+
+ // The primary account id of this service's profile during the loading of
+ // credentials. This member is empty otherwise.
+ std::string loading_primary_account_id_;
+
+ ScopedVector<RevokeServerRefreshToken> server_revokes_;
+
+ // Used to verify that certain methods are called only on the thread on which
+ // this instance was created.
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(MutableProfileOAuth2TokenService);
+};
+
+#endif // COMPONENTS_SIGNIN_CORE_MUTABLE_PROFILE_OAUTH2_TOKEN_SERVICE_H_
diff --git a/components/signin/core/profile_oauth2_token_service.cc b/components/signin/core/profile_oauth2_token_service.cc
new file mode 100644
index 0000000..21270124
--- /dev/null
+++ b/components/signin/core/profile_oauth2_token_service.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 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/profile_oauth2_token_service.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/time/time.h"
+#include "components/signin/core/signin_error_controller.h"
+#include "net/url_request/url_request_context_getter.h"
+
+ProfileOAuth2TokenService::ProfileOAuth2TokenService()
+ : client_(NULL) {}
+
+ProfileOAuth2TokenService::~ProfileOAuth2TokenService() {
+ DCHECK(!signin_error_controller_.get()) <<
+ "ProfileOAuth2TokenService::Initialize called but not "
+ "ProfileOAuth2TokenService::Shutdown";
+}
+
+void ProfileOAuth2TokenService::Initialize(SigninClient* client) {
+ DCHECK(client);
+ DCHECK(!client_);
+ client_ = client;
+
+ signin_error_controller_.reset(new SigninErrorController());
+}
+
+void ProfileOAuth2TokenService::Shutdown() {
+ DCHECK(client_) << "Shutdown() called without matching call to Initialize()";
+ signin_error_controller_.reset();
+}
+
+net::URLRequestContextGetter* ProfileOAuth2TokenService::GetRequestContext() {
+ return NULL;
+}
+
+void ProfileOAuth2TokenService::UpdateAuthError(
+ const std::string& account_id,
+ const GoogleServiceAuthError& error) {
+ NOTREACHED();
+}
+
+std::vector<std::string> ProfileOAuth2TokenService::GetAccounts() {
+ NOTREACHED();
+ return std::vector<std::string>();
+}
+
+void ProfileOAuth2TokenService::LoadCredentials(
+ const std::string& primary_account_id) {
+ // Empty implementation by default.
+}
+
+void ProfileOAuth2TokenService::UpdateCredentials(
+ const std::string& account_id,
+ const std::string& refresh_token) {
+ NOTREACHED();
+}
+
+void ProfileOAuth2TokenService::RevokeAllCredentials() {
+ // Empty implementation by default.
+}
+
diff --git a/components/signin/core/profile_oauth2_token_service.h b/components/signin/core/profile_oauth2_token_service.h
new file mode 100644
index 0000000..b4ede3a
--- /dev/null
+++ b/components/signin/core/profile_oauth2_token_service.h
@@ -0,0 +1,104 @@
+// Copyright 2014 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.
+
+#ifndef COMPONENTS_SIGNIN_CORE_PROFILE_OAUTH2_TOKEN_SERVICE_H_
+#define COMPONENTS_SIGNIN_CORE_PROFILE_OAUTH2_TOKEN_SERVICE_H_
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/linked_ptr.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/signin/core/signin_error_controller.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+class GoogleServiceAuthError;
+class SigninClient;
+
+// ProfileOAuth2TokenService is a class that retrieves
+// OAuth2 access tokens for a given set of scopes using the OAuth2 login
+// refresh tokens.
+//
+// See |OAuth2TokenService| for usage details.
+//
+// Note: after StartRequest returns, in-flight requests will continue
+// even if the TokenService refresh token that was used to initiate
+// the request changes or is cleared. When the request completes,
+// Consumer::OnGetTokenSuccess will be invoked, but the access token
+// won't be cached.
+//
+// Note: requests should be started from the UI thread. To start a
+// request from other thread, please use ProfileOAuth2TokenServiceRequest.
+class ProfileOAuth2TokenService : public OAuth2TokenService {
+ public:
+ virtual ~ProfileOAuth2TokenService();
+
+ // Initializes this token service with the SigninClient.
+ virtual void Initialize(SigninClient* client);
+
+ virtual void Shutdown();
+
+ // Lists account IDs of all accounts with a refresh token.
+ virtual std::vector<std::string> GetAccounts() OVERRIDE;
+
+ // Loads credentials from a backing persistent store to make them available
+ // after service is used between profile restarts.
+ //
+ // Only call this method if there is at least one account connected to the
+ // profile, otherwise startup will cause unneeded work on the IO thread. The
+ // primary account is specified with the |primary_account_id| argument and
+ // should not be empty. For a regular profile, the primary account id comes
+ // from SigninManager. For a managed account, the id comes from
+ // ManagedUserService.
+ virtual void LoadCredentials(const std::string& primary_account_id);
+
+ // Updates a |refresh_token| for an |account_id|. Credentials are persisted,
+ // and available through |LoadCredentials| after service is restarted.
+ virtual void UpdateCredentials(const std::string& account_id,
+ const std::string& refresh_token);
+
+ // Revokes all credentials handled by the object.
+ virtual void RevokeAllCredentials();
+
+ SigninErrorController* signin_error_controller() {
+ return signin_error_controller_.get();
+ }
+
+ const SigninErrorController* signin_error_controller() const {
+ return signin_error_controller_.get();
+ }
+
+ SigninClient* client() const { return client_; }
+
+ protected:
+ ProfileOAuth2TokenService();
+
+ // OAuth2TokenService overrides.
+ // Note: These methods are overriden so that ProfileOAuth2TokenService is a
+ // concrete class.
+
+ // Simply returns NULL and should be overriden by subsclasses.
+ virtual net::URLRequestContextGetter* GetRequestContext() OVERRIDE;
+
+ // Updates the internal cache of the result from the most-recently-completed
+ // auth request (used for reporting errors to the user).
+ virtual void UpdateAuthError(
+ const std::string& account_id,
+ const GoogleServiceAuthError& error) OVERRIDE;
+
+ private:
+ // The client with which this instance was initialized, or NULL.
+ SigninClient* client_;
+
+ // Used to expose auth errors to the UI.
+ scoped_ptr<SigninErrorController> signin_error_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenService);
+};
+
+#endif // COMPONENTS_SIGNIN_CORE_PROFILE_OAUTH2_TOKEN_SERVICE_H_
diff --git a/components/signin/core/signin_error_controller.cc b/components/signin/core/signin_error_controller.cc
new file mode 100644
index 0000000..e3e93c8
--- /dev/null
+++ b/components/signin/core/signin_error_controller.cc
@@ -0,0 +1,106 @@
+// Copyright 2014 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/signin_error_controller.h"
+
+namespace {
+
+typedef std::set<const SigninErrorController::AuthStatusProvider*>
+ AuthStatusProviderSet;
+
+} // namespace
+
+SigninErrorController::AuthStatusProvider::AuthStatusProvider() {
+}
+
+SigninErrorController::AuthStatusProvider::~AuthStatusProvider() {
+}
+
+SigninErrorController::SigninErrorController()
+ : auth_error_(GoogleServiceAuthError::AuthErrorNone()) {
+}
+
+SigninErrorController::~SigninErrorController() {
+ DCHECK(provider_set_.empty())
+ << "All AuthStatusProviders should be unregistered before"
+ << " SigninErrorController::Shutdown() is called";
+}
+
+void SigninErrorController::AddProvider(const AuthStatusProvider* provider) {
+ DCHECK(provider_set_.find(provider) == provider_set_.end())
+ << "Adding same AuthStatusProvider multiple times";
+ provider_set_.insert(provider);
+ AuthStatusChanged();
+}
+
+void SigninErrorController::RemoveProvider(const AuthStatusProvider* provider) {
+ AuthStatusProviderSet::iterator iter = provider_set_.find(provider);
+ DCHECK(iter != provider_set_.end())
+ << "Removing provider that was never added";
+ provider_set_.erase(iter);
+ AuthStatusChanged();
+}
+
+void SigninErrorController::AuthStatusChanged() {
+ GoogleServiceAuthError::State prev_state = auth_error_.state();
+ std::string prev_account_id = error_account_id_;
+ bool error_changed = false;
+
+ // Find an error among the status providers. If |auth_error_| has an
+ // actionable error state and some provider exposes a similar error and
+ // account id, use that error. Otherwise, just take the first actionable
+ // error we find.
+ for (AuthStatusProviderSet::const_iterator it = provider_set_.begin();
+ it != provider_set_.end(); ++it) {
+ GoogleServiceAuthError error = (*it)->GetAuthStatus();
+
+ // Ignore the states we don't want to elevate to the user.
+ if (error.state() == GoogleServiceAuthError::NONE ||
+ error.state() == GoogleServiceAuthError::CONNECTION_FAILED) {
+ continue;
+ }
+
+ std::string account_id = (*it)->GetAccountId();
+
+ // Prioritize this error if it matches the previous |auth_error_|.
+ if (error.state() == prev_state && account_id == prev_account_id) {
+ auth_error_ = error;
+ error_account_id_ = account_id;
+ error_changed = true;
+ break;
+ }
+
+ // Use this error if we haven't found one already, but keep looking for the
+ // previous |auth_error_| in case there's a match elsewhere in the set.
+ if (!error_changed) {
+ auth_error_ = error;
+ error_account_id_ = account_id;
+ error_changed = true;
+ }
+ }
+
+ if (!error_changed && prev_state != GoogleServiceAuthError::NONE) {
+ // No provider reported an error, so clear the error we have now.
+ auth_error_ = GoogleServiceAuthError::AuthErrorNone();
+ error_account_id_.clear();
+ error_changed = true;
+ }
+
+ if (error_changed) {
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnErrorChanged());
+ }
+}
+
+bool SigninErrorController::HasError() const {
+ return auth_error_.state() != GoogleServiceAuthError::NONE &&
+ auth_error_.state() != GoogleServiceAuthError::CONNECTION_FAILED;
+}
+
+void SigninErrorController::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void SigninErrorController::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
diff --git a/components/signin/core/signin_error_controller.h b/components/signin/core/signin_error_controller.h
new file mode 100644
index 0000000..c66963e
--- /dev/null
+++ b/components/signin/core/signin_error_controller.h
@@ -0,0 +1,79 @@
+// Copyright 2014 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.
+
+#ifndef COMPONENTS_SIGNIN_CORE_SIGNIN_ERROR_CONTROLLER_H_
+#define COMPONENTS_SIGNIN_CORE_SIGNIN_ERROR_CONTROLLER_H_
+
+#include <set>
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/observer_list.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+// Keep track of auth errors and expose them to observers in the UI. Services
+// that wish to expose auth errors to the user should register an
+// AuthStatusProvider to report their current authentication state, and should
+// invoke AuthStatusChanged() when their authentication state may have changed.
+class SigninErrorController {
+ public:
+ class AuthStatusProvider {
+ public:
+ AuthStatusProvider();
+ virtual ~AuthStatusProvider();
+
+ // Returns the account id with the status specified by GetAuthStatus().
+ virtual std::string GetAccountId() const = 0;
+
+ // API invoked by SigninErrorController to get the current auth status of
+ // the various signed in services.
+ virtual GoogleServiceAuthError GetAuthStatus() const = 0;
+ };
+
+ // The observer class for SigninErrorController lets the controller notify
+ // observers when an error arises or changes.
+ class Observer {
+ public:
+ virtual ~Observer() {}
+ virtual void OnErrorChanged() = 0;
+ };
+
+ SigninErrorController();
+ ~SigninErrorController();
+
+ // Adds a provider which the SigninErrorController object will start querying
+ // for auth status.
+ void AddProvider(const AuthStatusProvider* provider);
+
+ // Removes a provider previously added by SigninErrorController (generally
+ // only called in preparation for shutdown).
+ void RemoveProvider(const AuthStatusProvider* provider);
+
+ // Invoked when the auth status of an AuthStatusProvider has changed.
+ void AuthStatusChanged();
+
+ // True if there exists an error worth elevating to the user.
+ bool HasError() const;
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ const std::string& error_account_id() const { return error_account_id_; }
+ const GoogleServiceAuthError& auth_error() const { return auth_error_; }
+
+ private:
+ std::set<const AuthStatusProvider*> provider_set_;
+
+ // The account that generated the last auth error.
+ std::string error_account_id_;
+
+ // The auth error detected the last time AuthStatusChanged() was invoked (or
+ // NONE if AuthStatusChanged() has never been invoked).
+ GoogleServiceAuthError auth_error_;
+
+ ObserverList<Observer, false> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(SigninErrorController);
+};
+
+#endif // COMPONENTS_SIGNIN_CORE_SIGNIN_ERROR_CONTROLLER_H_