diff options
author | blundell@chromium.org <blundell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-19 18:07:40 +0000 |
---|---|---|
committer | blundell@chromium.org <blundell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-03-19 18:07:40 +0000 |
commit | 835e98e142061272a6c46dfc73626ba3f8f64caa (patch) | |
tree | c4a3e81a4ffbd16659b009191068d5afd081a171 /components/signin | |
parent | ee786bf8adf20631adab484690f44b3af0fd5783 (diff) | |
download | chromium_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/DEPS | 4 | ||||
-rw-r--r-- | components/signin/core/mutable_profile_oauth2_token_service.cc | 384 | ||||
-rw-r--r-- | components/signin/core/mutable_profile_oauth2_token_service.h | 144 | ||||
-rw-r--r-- | components/signin/core/profile_oauth2_token_service.cc | 65 | ||||
-rw-r--r-- | components/signin/core/profile_oauth2_token_service.h | 104 | ||||
-rw-r--r-- | components/signin/core/signin_error_controller.cc | 106 | ||||
-rw-r--r-- | components/signin/core/signin_error_controller.h | 79 |
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_ |