diff options
author | davidroche@chromium.org <davidroche@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-18 06:02:18 +0000 |
---|---|---|
committer | davidroche@chromium.org <davidroche@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-18 06:02:18 +0000 |
commit | a28c45a97e2ae3e1d11f9b1b6ba1716c6b99302c (patch) | |
tree | bc21fb3353deb9c81f4715707fbee6aa58e67840 /chrome/browser/signin | |
parent | 29a093b64c932d9b7a61bab0906fe208de0e27c6 (diff) | |
download | chromium_src-a28c45a97e2ae3e1d11f9b1b6ba1716c6b99302c.zip chromium_src-a28c45a97e2ae3e1d11f9b1b6ba1716c6b99302c.tar.gz chromium_src-a28c45a97e2ae3e1d11f9b1b6ba1716c6b99302c.tar.bz2 |
Refactor OAuth2TokenService to have profile- and device-based implementations.
OAuth2TokenService was refactored into an abstract base class without
dependencies on Profile and the TokenService's OAuth2 login refresh token.
Added ProfileOAuth2TokenService as an implementation of OAuth2TokenService
that requires a Profile and the user's login refresh token from
TokenService. Migrated the existing OAuth2TokenServiceFactory and
OAuth2TokenServiceRequest to Profile* varients that use this new class.
Added DeviceOAuth2TokenService that serves up API access tokens for
enterprise enrolled devices (via a robot account with an any-api refresh
token). These tokens are global to all accounts on the device including
Public Accounts, and hence don't depend on a particular profile's login
credentials.
BUG=164606
Review URL: https://chromiumcodereview.appspot.com/12647008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194796 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/signin')
19 files changed, 1005 insertions, 647 deletions
diff --git a/chrome/browser/signin/android_profile_oauth2_token_service.cc b/chrome/browser/signin/android_profile_oauth2_token_service.cc new file mode 100644 index 0000000..698ee44 --- /dev/null +++ b/chrome/browser/signin/android_profile_oauth2_token_service.cc @@ -0,0 +1,62 @@ +// Copyright 2013 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 "chrome/browser/signin/android_profile_oauth2_token_service.h" + +#include "base/bind.h" +#include "chrome/browser/sync/profile_sync_service_android.h" +#include "content/public/browser/browser_thread.h" +#include "net/url_request/url_request_context_getter.h" + +AndroidProfileOAuth2TokenService::AndroidProfileOAuth2TokenService( + net::URLRequestContextGetter* getter) + : ProfileOAuth2TokenService(getter) { +} + +AndroidProfileOAuth2TokenService::~AndroidProfileOAuth2TokenService() { +} + +scoped_ptr<OAuth2TokenService::Request> + AndroidProfileOAuth2TokenService::StartRequest( + const OAuth2TokenService::ScopeSet& scopes, + OAuth2TokenService::Consumer* consumer) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + if (HasCacheEntry(scopes)) + return StartCacheLookupRequest(scopes, consumer); + + scoped_ptr<RequestImpl> request(new RequestImpl(consumer)); + DCHECK_EQ(scopes.size(), 1U); + std::vector<std::string> scope_list(scopes.begin(), scopes.end()); + ProfileSyncServiceAndroid* sync_service = + ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid(); + sync_service->FetchOAuth2Token( + scope_list.front(), + base::Bind(&OAuth2TokenService::InformConsumer, + request->AsWeakPtr())); + return request.PassAs<Request>(); +} + +void AndroidProfileOAuth2TokenService::InvalidateToken( + const ScopeSet& scopes, + const std::string& invalid_token) { + OAuth2TokenService::InvalidateToken(scopes, invalid_token); + + DCHECK_EQ(scopes.size(), 1U); + std::vector<std::string> scope_list(scopes.begin(), scopes.end()); + ProfileSyncServiceAndroid* sync_service = + ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid(); + sync_service->InvalidateOAuth2Token( + scope_list.front(), + invalid_token); +} + +bool AndroidProfileOAuth2TokenService::ShouldCacheForRefreshToken( + TokenService *token_service, + const std::string& refresh_token) { + // The parent class skips caching if the TokenService login token is stale, + // but on Android the user is always logged in to exactly one profile, so + // this concept doesn't exist and we can simply always cache. + return true; +} diff --git a/chrome/browser/signin/android_profile_oauth2_token_service.h b/chrome/browser/signin/android_profile_oauth2_token_service.h new file mode 100644 index 0000000..14556c03 --- /dev/null +++ b/chrome/browser/signin/android_profile_oauth2_token_service.h @@ -0,0 +1,51 @@ +// Copyright 2013 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 CHROME_BROWSER_SIGNIN_ANDROID_PROFILE_OAUTH2_TOKEN_SERVICE_H_ +#define CHROME_BROWSER_SIGNIN_ANDROID_PROFILE_OAUTH2_TOKEN_SERVICE_H_ + +#include <string> + +#include "chrome/browser/signin/profile_oauth2_token_service.h" + +namespace net { +class URLRequestContextGetter; +} + +class TokenService; + +// A specialization of ProfileOAuth2TokenService that will be returned by +// ProfileOAuth2TokenServiceFactory for OS_ANDROID. This instance uses +// native Android features to lookup OAuth2 tokens. +// +// See |ProfileOAuth2TokenService| for usage details. +// +// Note: requests should be started from the UI thread. To start a +// request from other thread, please use ProfileOAuth2TokenServiceRequest. +class AndroidProfileOAuth2TokenService : public ProfileOAuth2TokenService { + public: + // Start the OAuth2 access token for the given scopes using + // ProfileSyncServiceAndroid. + virtual scoped_ptr<OAuth2TokenService::Request> StartRequest( + const OAuth2TokenService::ScopeSet& scopes, + OAuth2TokenService::Consumer* consumer) OVERRIDE; + + virtual void InvalidateToken(const ScopeSet& scopes, + const std::string& invalid_token) OVERRIDE; + + protected: + friend class ProfileOAuth2TokenServiceFactory; + explicit AndroidProfileOAuth2TokenService( + net::URLRequestContextGetter* getter); + virtual ~AndroidProfileOAuth2TokenService(); + + // Takes injected TokenService for testing. + bool ShouldCacheForRefreshToken(TokenService *token_service, + const std::string& refresh_token); + + private: + DISALLOW_COPY_AND_ASSIGN(AndroidProfileOAuth2TokenService); +}; + +#endif // CHROME_BROWSER_SIGNIN_ANDROID_PROFILE_OAUTH2_TOKEN_SERVICE_H_ diff --git a/chrome/browser/signin/oauth2_token_service.cc b/chrome/browser/signin/oauth2_token_service.cc index 7886054..abddb4e 100644 --- a/chrome/browser/signin/oauth2_token_service.cc +++ b/chrome/browser/signin/oauth2_token_service.cc @@ -13,25 +13,12 @@ #include "base/stl_util.h" #include "base/time.h" #include "base/timer.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/signin/oauth2_token_service_factory.h" -#include "chrome/browser/signin/signin_manager.h" -#include "chrome/browser/signin/signin_manager_factory.h" -#include "chrome/browser/signin/token_service.h" -#include "chrome/browser/signin/token_service_factory.h" -#include "chrome/common/chrome_notification_types.h" #include "content/public/browser/browser_thread.h" -#include "content/public/browser/notification_details.h" -#include "content/public/browser/notification_source.h" -#include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.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" - -#if defined(OS_ANDROID) -#include "chrome/browser/sync/profile_sync_service_android.h" -#endif +#include "net/url_request/url_request_context_getter.h" namespace { @@ -49,26 +36,6 @@ int64 ComputeExponentialBackOffMilliseconds(int retry_num) { } // namespace -// Implements a cancelable |OAuth2TokenService::Request|, which should be -// operated on the UI thread. -class OAuth2TokenService::RequestImpl - : public base::SupportsWeakPtr<RequestImpl>, - public OAuth2TokenService::Request { - public: - // |consumer| is required to outlive this. - explicit RequestImpl(OAuth2TokenService::Consumer* consumer); - virtual ~RequestImpl(); - - // Informs |consumer_| that this request is completed. - void InformConsumer(const GoogleServiceAuthError& error, - const std::string& access_token, - const base::Time& expiration_date); - - private: - // |consumer_| to call back when this request completes. - OAuth2TokenService::Consumer* const consumer_; -}; - OAuth2TokenService::RequestImpl::RequestImpl( OAuth2TokenService::Consumer* consumer) : consumer_(consumer) { @@ -85,9 +52,9 @@ void OAuth2TokenService::RequestImpl::InformConsumer( const base::Time& expiration_date) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); if (error.state() == GoogleServiceAuthError::NONE) - consumer_-> OnGetTokenSuccess(this, access_token, expiration_date); + consumer_->OnGetTokenSuccess(this, access_token, expiration_date); else - consumer_-> OnGetTokenFailure(this, error); + consumer_->OnGetTokenFailure(this, error); } // Class that fetches OAuth2 access tokens for given scopes and refresh token. @@ -121,8 +88,8 @@ class OAuth2TokenService::Fetcher : public OAuth2AccessTokenConsumer { public: // Creates a Fetcher and starts fetching an OAuth2 access token for // |refresh_token| and |scopes| in the request context obtained by |getter|. - // |profile|'s OAuth2TokenService will be informed when fetching is done. - static Fetcher* CreateAndStart(Profile* profile, + // The given |oauth2_token_service| will be informed when fetching is done. + static Fetcher* CreateAndStart(OAuth2TokenService* oauth2_token_service, net::URLRequestContextGetter* getter, const std::string& refresh_token, const OAuth2TokenService::ScopeSet& scopes, @@ -145,7 +112,7 @@ class OAuth2TokenService::Fetcher : public OAuth2AccessTokenConsumer { virtual void OnGetTokenFailure(const GoogleServiceAuthError& error) OVERRIDE; private: - Fetcher(Profile* profile, + Fetcher(OAuth2TokenService* oauth2_token_service, net::URLRequestContextGetter* getter, const std::string& refresh_token, const OAuth2TokenService::ScopeSet& scopes, @@ -154,7 +121,11 @@ class OAuth2TokenService::Fetcher : public OAuth2AccessTokenConsumer { void InformWaitingRequests(); static bool ShouldRetry(const GoogleServiceAuthError& error); - Profile* const profile_; + // |oauth2_token_service_| remains valid for the life of this Fetcher, since + // this Fetcher is destructed in the dtor of the OAuth2TokenService or is + // scheduled for deletion at the end of OnGetTokenFailure/OnGetTokenSuccess + // (whichever comes first). + OAuth2TokenService* const oauth2_token_service_; scoped_refptr<net::URLRequestContextGetter> getter_; const std::string refresh_token_; const OAuth2TokenService::ScopeSet scopes_; @@ -176,30 +147,30 @@ class OAuth2TokenService::Fetcher : public OAuth2AccessTokenConsumer { // static OAuth2TokenService::Fetcher* OAuth2TokenService::Fetcher::CreateAndStart( - Profile* profile, + OAuth2TokenService* oauth2_token_service, net::URLRequestContextGetter* getter, const std::string& refresh_token, const OAuth2TokenService::ScopeSet& scopes, base::WeakPtr<RequestImpl> waiting_request) { OAuth2TokenService::Fetcher* fetcher = new Fetcher( - profile, getter, refresh_token, scopes, waiting_request); + oauth2_token_service, getter, refresh_token, scopes, waiting_request); fetcher->Start(); return fetcher; } OAuth2TokenService::Fetcher::Fetcher( - Profile* profile, + OAuth2TokenService* oauth2_token_service, net::URLRequestContextGetter* getter, const std::string& refresh_token, const OAuth2TokenService::ScopeSet& scopes, base::WeakPtr<RequestImpl> waiting_request) - : profile_(profile), + : oauth2_token_service_(oauth2_token_service), getter_(getter), refresh_token_(refresh_token), scopes_(scopes), retry_number_(0), error_(GoogleServiceAuthError::SERVICE_UNAVAILABLE) { - DCHECK(profile_); + DCHECK(oauth2_token_service_); DCHECK(getter_); DCHECK(refresh_token_.length()); waiting_requests_.push_back(waiting_request); @@ -226,24 +197,21 @@ void OAuth2TokenService::Fetcher::OnGetTokenSuccess( fetcher_.reset(); // Fetch completes. - error_ = GoogleServiceAuthError(GoogleServiceAuthError::NONE); + error_ = GoogleServiceAuthError::AuthErrorNone(); access_token_ = access_token; expiration_date_ = expiration_date; - // |oauth2_token_service| should not be NULL as this Fetcher is destructed in - // the dtor of the OAuth2TokenService that creates it if it is not scheduled - // to be destructed here and in OnGetTokenFailure(). - OAuth2TokenService* oauth2_token_service = - OAuth2TokenServiceFactory::GetForProfile(profile_); - DCHECK(oauth2_token_service); - - oauth2_token_service->RegisterCacheEntry(refresh_token_, - scopes_, - access_token_, - expiration_date_); + // Subclasses may override this method to skip caching in some cases, but + // we still inform all waiting Consumers of a successful token fetch below. + // This is intentional -- some consumers may need the token for cleanup + // tasks. https://chromiumcodereview.appspot.com/11312124/ + oauth2_token_service_->RegisterCacheEntry(refresh_token_, + scopes_, + access_token_, + expiration_date_); // Deregisters itself from the service to prevent more waiting requests to // be added when it calls back the waiting requests. - oauth2_token_service->OnFetchComplete(this); + oauth2_token_service_->OnFetchComplete(this); InformWaitingRequests(); MessageLoop::current()->DeleteSoon(FROM_HERE, this); } @@ -266,15 +234,9 @@ void OAuth2TokenService::Fetcher::OnGetTokenFailure( // Fetch completes. error_ = error; - // |oauth2_token_service| should not be NULL as this Fetcher is destructed in - // the dtor of the OAuth2TokenService that creates it if it is not scheduled - // to be destructed here and in OnGetTokenSuccess(). - OAuth2TokenService* oauth2_token_service = - OAuth2TokenServiceFactory::GetForProfile(profile_); - DCHECK(oauth2_token_service); // Deregisters itself from the service to prevent more waiting requests to be // added when it calls back the waiting requests. - oauth2_token_service->OnFetchComplete(this); + oauth2_token_service_->OnFetchComplete(this); InformWaitingRequests(); MessageLoop::current()->DeleteSoon(FROM_HERE, this); } @@ -325,44 +287,22 @@ OAuth2TokenService::Consumer::Consumer() { OAuth2TokenService::Consumer::~Consumer() { } -OAuth2TokenService::OAuth2TokenService() - : profile_(NULL), - last_auth_error_(GoogleServiceAuthError::NONE) { +OAuth2TokenService::OAuth2TokenService(net::URLRequestContextGetter* getter) + : request_context_getter_(getter) { } OAuth2TokenService::~OAuth2TokenService() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); // Release all the pending fetchers. STLDeleteContainerPairSecondPointers( pending_fetchers_.begin(), pending_fetchers_.end()); } -void OAuth2TokenService::Initialize(Profile* profile) { +bool OAuth2TokenService::RefreshTokenIsAvailable() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - - DCHECK(profile); - DCHECK(!profile_); - profile_ = profile; - getter_ = profile->GetRequestContext(); - content::Source<TokenService> token_service_source( - TokenServiceFactory::GetForProfile(profile)); - registrar_.Add(this, - chrome::NOTIFICATION_TOKENS_CLEARED, - token_service_source); - registrar_.Add(this, - chrome::NOTIFICATION_TOKEN_AVAILABLE, - token_service_source); - SigninManagerFactory::GetForProfile(profile_)->signin_global_error()-> - AddProvider(this); -} - -void OAuth2TokenService::Shutdown() { - if (profile_) { - SigninManagerFactory::GetForProfile(profile_)->signin_global_error()-> - RemoveProvider(this); - } + return !GetRefreshToken().empty(); } - // static void OAuth2TokenService::InformConsumer( base::WeakPtr<OAuth2TokenService::RequestImpl> request, @@ -382,53 +322,21 @@ scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest( scoped_ptr<RequestImpl> request(new RequestImpl(consumer)); -#if !defined(OS_ANDROID) - TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); - if (!token_service || !token_service->HasOAuthLoginToken()) { - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &OAuth2TokenService::InformConsumer, - request->AsWeakPtr(), - GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP), - std::string(), - base::Time())); - return request.PassAs<Request>(); - } -#endif - - const CacheEntry* cache_entry = GetCacheEntry(scopes); - if (cache_entry && cache_entry->access_token.length()) { - MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &OAuth2TokenService::InformConsumer, - request->AsWeakPtr(), - GoogleServiceAuthError(GoogleServiceAuthError::NONE), - cache_entry->access_token, - cache_entry->expiration_date)); - return request.PassAs<Request>(); - } - -#if defined(OS_ANDROID) - DCHECK_EQ(scopes.size(), 1U); - std::vector<std::string> scope_list(scopes.begin(), scopes.end()); - ProfileSyncServiceAndroid* sync_service = - ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid(); - sync_service->FetchOAuth2Token( - scope_list.front(), - base::Bind(&OAuth2TokenService::InformConsumer, - request->AsWeakPtr())); - return request.PassAs<Request>(); -#else - std::string refresh_token = token_service->GetOAuth2LoginRefreshToken(); - if (!refresh_token.length()) { + std::string refresh_token = GetRefreshToken(); + if (refresh_token.empty()) { MessageLoop::current()->PostTask(FROM_HERE, base::Bind( &OAuth2TokenService::InformConsumer, request->AsWeakPtr(), GoogleServiceAuthError( - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS), + GoogleServiceAuthError::USER_NOT_SIGNED_UP), std::string(), base::Time())); return request.PassAs<Request>(); } + if (HasCacheEntry(scopes)) + return StartCacheLookupRequest(scopes, consumer); + // Makes sure there is a pending fetcher for |scopes| and |refresh_token|. // Adds |request| to the waiting request list of this fetcher so |request| // will be called back when this fetcher finishes fetching. @@ -440,24 +348,31 @@ scoped_ptr<OAuth2TokenService::Request> OAuth2TokenService::StartRequest( return request.PassAs<Request>(); } pending_fetchers_[fetch_parameters] = Fetcher::CreateAndStart( - profile_, getter_, refresh_token, scopes, request->AsWeakPtr()); + this, request_context_getter_, refresh_token, scopes, + request->AsWeakPtr()); + return request.PassAs<Request>(); +} + +scoped_ptr<OAuth2TokenService::Request> + OAuth2TokenService::StartCacheLookupRequest( + const OAuth2TokenService::ScopeSet& scopes, + OAuth2TokenService::Consumer* consumer) { + CHECK(HasCacheEntry(scopes)); + const CacheEntry* cache_entry = GetCacheEntry(scopes); + scoped_ptr<RequestImpl> request(new RequestImpl(consumer)); + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( + &OAuth2TokenService::InformConsumer, + request->AsWeakPtr(), + GoogleServiceAuthError(GoogleServiceAuthError::NONE), + cache_entry->access_token, + cache_entry->expiration_date)); return request.PassAs<Request>(); -#endif // defined(OS_ANDROID) } void OAuth2TokenService::InvalidateToken(const ScopeSet& scopes, const std::string& invalid_token) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); RemoveCacheEntry(scopes, invalid_token); - -#if defined(OS_ANDROID) - DCHECK_EQ(scopes.size(), 1U); - std::vector<std::string> scope_list(scopes.begin(), scopes.end()); - ProfileSyncServiceAndroid* sync_service = - ProfileSyncServiceAndroid::GetProfileSyncServiceAndroid(); - sync_service->InvalidateOAuth2Token( - scope_list.front(), - invalid_token); -#endif } void OAuth2TokenService::OnFetchComplete(Fetcher* fetcher) { @@ -475,8 +390,7 @@ void OAuth2TokenService::OnFetchComplete(Fetcher* fetcher) { // (1) All the live Fetchers are created by this service. // This is because (1) all the live Fetchers are created by a live // service, as all the fetchers created by a service are destructed in the - // service's dtor, and (2) there is at most one live OAuth2TokenSevice for - // a given profile at a time. + // service's dtor. // // (2) All the uncompleted Fetchers created by this service are recorded in // |pending_fetchers_|. @@ -488,7 +402,7 @@ void OAuth2TokenService::OnFetchComplete(Fetcher* fetcher) { // // (3) Each of the Fetchers recorded in |pending_fetchers_| is mapped to its // refresh token and ScopeSet. This is guaranteed by Fetcher creation in - // method StartReuest(). + // method StartRequest(). // // When this method is called, |fetcher| is alive and uncompleted. // By (1), |fetcher| is created by this service. @@ -502,6 +416,12 @@ void OAuth2TokenService::OnFetchComplete(Fetcher* fetcher) { pending_fetchers_.erase(iter); } +bool OAuth2TokenService::HasCacheEntry( + const OAuth2TokenService::ScopeSet& scopes) { + const CacheEntry* cache_entry = GetCacheEntry(scopes); + return cache_entry && cache_entry->access_token.length(); +} + const OAuth2TokenService::CacheEntry* OAuth2TokenService::GetCacheEntry( const OAuth2TokenService::ScopeSet& scopes) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); @@ -535,56 +455,20 @@ void OAuth2TokenService::RegisterCacheEntry( const base::Time& expiration_date) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); -#if !defined(OS_ANDROID) - // Only register OAuth2 access tokens for the refresh token held by - // TokenService. - TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); - if (!token_service || - !token_service->HasOAuthLoginToken() || - token_service->GetOAuth2LoginRefreshToken().compare(refresh_token) != 0) { - DLOG(INFO) << - "Received a token with a refresh token not maintained by TokenService."; - return; - } -#endif - CacheEntry& token = token_cache_[scopes]; token.access_token = access_token; token.expiration_date = expiration_date; } -void OAuth2TokenService::Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) { - DCHECK(type == chrome::NOTIFICATION_TOKENS_CLEARED || - type == chrome::NOTIFICATION_TOKEN_AVAILABLE); - if (type == chrome::NOTIFICATION_TOKEN_AVAILABLE) { - TokenService::TokenAvailableDetails* tok_details = - content::Details<TokenService::TokenAvailableDetails>(details).ptr(); - if (tok_details->service() != GaiaConstants::kGaiaOAuth2LoginRefreshToken) - return; - } - // The GaiaConstants::kGaiaOAuth2LoginRefreshToken token is used to create - // OAuth2 access tokens. If this token either changes or is cleared, any - // available tokens must be invalidated. - token_cache_.clear(); - UpdateAuthError(GoogleServiceAuthError::AuthErrorNone()); -} - void OAuth2TokenService::UpdateAuthError(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) - return; + // Default implementation does nothing. +} - if (error.state() != last_auth_error_.state()) { - last_auth_error_ = error; - SigninManagerFactory::GetForProfile(profile_)->signin_global_error()-> - AuthStatusChanged(); - } +void OAuth2TokenService::ClearCache() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + token_cache_.clear(); } -GoogleServiceAuthError OAuth2TokenService::GetAuthStatus() const { - return last_auth_error_; +int OAuth2TokenService::cache_size_for_testing() const { + return token_cache_.size(); } diff --git a/chrome/browser/signin/oauth2_token_service.h b/chrome/browser/signin/oauth2_token_service.h index 71ddcb2..9260f41 100644 --- a/chrome/browser/signin/oauth2_token_service.h +++ b/chrome/browser/signin/oauth2_token_service.h @@ -9,23 +9,27 @@ #include <set> #include <string> +#include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time.h" -#include "chrome/browser/profiles/profile_keyed_service.h" -#include "chrome/browser/signin/signin_global_error.h" -#include "content/public/browser/notification_observer.h" -#include "content/public/browser/notification_registrar.h" -#include "net/url_request/url_request_context_getter.h" + +namespace base { +class Time; +} + +namespace net { +class URLRequestContextGetter; +} class GoogleServiceAuthError; -class OAuth2AccessTokenConsumer; -class Profile; -// OAuth2TokenService is a ProfileKeyedService that retrieves OAuth2 access -// tokens for a given set of scopes using the OAuth2 refresh token maintained by -// TokenService. All calls are expected from the UI thread. +// Abstract base class for a service that fetches and caches OAuth2 access +// tokens. Concrete subclasses should implement GetRefreshToken to return +// the appropriate refresh token. +// +// All calls are expected from the UI thread. // // To use this service, call StartRequest() with a given set of scopes and a // consumer of the request results. The consumer is required to outlive the @@ -43,12 +47,7 @@ class Profile; // // The caller of StartRequest() owns the returned request and is responsible to // delete the request even once the callback has been invoked. -// -// Note the request should be started from the UI thread. To start a request -// from other thread, please use OAuth2TokenServiceRequest. -class OAuth2TokenService : public content::NotificationObserver, - public SigninGlobalError::AuthStatusProvider, - public ProfileKeyedService { +class OAuth2TokenService { public: // Class representing a request that fetches an OAuth2 access token. class Request { @@ -76,58 +75,90 @@ class OAuth2TokenService : public content::NotificationObserver, // A set of scopes in OAuth2 authentication. typedef std::set<std::string> ScopeSet; - OAuth2TokenService(); + explicit OAuth2TokenService(net::URLRequestContextGetter* getter); virtual ~OAuth2TokenService(); - // Initializes this token service with the profile. - void Initialize(Profile* profile); - - // ProfileKeyedService implementation. - virtual void Shutdown() OVERRIDE; - - // Starts a request for an OAuth2 access token using the OAuth2 refresh token - // maintained by TokenService. The caller owns the returned Request. |scopes| - // is the set of scopes to get an access token for, |consumer| is the object - // that will be called back with results if the returned request is not - // deleted. - // Note the refresh token has been collected from TokenService when this - // method returns, and the request can continue even if TokenService clears - // its tokens after this method returns. This means that outstanding - // StartRequest actions will still complete even if the user signs out in the - // meantime. - virtual scoped_ptr<Request> StartRequest( - const ScopeSet& scopes, - OAuth2TokenService::Consumer* consumer); + // Checks in the cache for a valid access token, and if not found starts + // a request for an OAuth2 access token using the OAuth2 refresh token + // maintained by this instance. The caller owns the returned Request. + // |scopes| is the set of scopes to get an access token for, |consumer| is + // the object that will be called back with results if the returned request + // is not deleted. + virtual scoped_ptr<Request> StartRequest(const ScopeSet& scopes, + Consumer* consumer); + + // Returns true if a refresh token exists. If false, calls to + // |StartRequest| will result in a Consumer::OnGetTokenFailure callback. + bool RefreshTokenIsAvailable(); // Mark an OAuth2 access token as invalid. This should be done if the token // was received from this class, but was not accepted by the server (e.g., // the server returned 401 Unauthorized). The token will be removed from the // cache for the given scopes. - void InvalidateToken(const ScopeSet& scopes, - const std::string& invalid_token); - - // content::NotificationObserver - virtual void Observe(int type, - const content::NotificationSource& source, - const content::NotificationDetails& details) OVERRIDE; + virtual void InvalidateToken(const ScopeSet& scopes, + const std::string& invalid_token); + + // Return the current number of entries in the cache. + int cache_size_for_testing() const; + + protected: + // Subclasses should return the refresh token maintained. + // If no token is available, return an empty string. + virtual std::string GetRefreshToken() = 0; + + // Subclasses can override if they want to report errors to the user. + virtual void UpdateAuthError(const GoogleServiceAuthError& error); + + // Add a new entry to the cache. + // Subclasses can override if there are implementation-specific reasons + // that an access token should ever not be cached. + virtual void RegisterCacheEntry(const std::string& refresh_token, + const ScopeSet& scopes, + const std::string& access_token, + const base::Time& expiration_date); + + // Returns true if GetCacheEntry would return a valid cache entry for the + // given scopes. + bool HasCacheEntry(const ScopeSet& scopes); + + // Posts a task to fire the Consumer callback with the cached token. Must + // only be called if HasCacheEntry() returns true. + scoped_ptr<Request> StartCacheLookupRequest(const ScopeSet& scopes, + Consumer* consumer); + + // Clears the internal token cache. + void ClearCache(); + + // Implements a cancelable |OAuth2TokenService::Request|, which should be + // operated on the UI thread. + class RequestImpl : public base::SupportsWeakPtr<RequestImpl>, + public Request { + public: + // |consumer| is required to outlive this. + explicit RequestImpl(Consumer* consumer); + virtual ~RequestImpl(); + + // Informs |consumer_| that this request is completed. + void InformConsumer(const GoogleServiceAuthError& error, + const std::string& access_token, + const base::Time& expiration_date); + + private: + // |consumer_| to call back when this request completes. + Consumer* const consumer_; + }; - // SigninGlobalError::AuthStatusProvider implementation. - virtual GoogleServiceAuthError GetAuthStatus() const OVERRIDE; + // Informs the consumer of |request| fetch results. + static void InformConsumer(base::WeakPtr<RequestImpl> request, + const GoogleServiceAuthError& error, + const std::string& access_token, + const base::Time& expiration_date); private: // Class that fetches an OAuth2 access token for a given set of scopes and // OAuth2 refresh token. class Fetcher; friend class Fetcher; - // Implementation of Request. - class RequestImpl; - - // Informs the consumer of |request| fetch results. - static void InformConsumer( - base::WeakPtr<OAuth2TokenService::RequestImpl> request, - const GoogleServiceAuthError& error, - const std::string& access_token, - const base::Time& expiration_date); // Struct that contains the information of an OAuth2 access token. struct CacheEntry { @@ -141,12 +172,6 @@ class OAuth2TokenService : public content::NotificationObserver, // returned entry is done. const CacheEntry* GetCacheEntry(const ScopeSet& scopes); - // Registers a new access token in the cache if |refresh_token| is the one - // currently held by TokenService. - void RegisterCacheEntry(const std::string& refresh_token, - const ScopeSet& scopes, - const std::string& access_token, - const base::Time& expiration_date); // Removes an access token for the given set of scopes from the cache. // Returns true if the entry was removed, otherwise false. @@ -157,18 +182,8 @@ class OAuth2TokenService : public content::NotificationObserver, // Called when |fetcher| finishes fetching. void OnFetchComplete(Fetcher* fetcher); - // Updates the internal cache of the result from the most-recently-completed - // auth request (used for reporting errors to the user). - void UpdateAuthError(const GoogleServiceAuthError& error); - - // The profile with which this instance was initialized, or NULL. - Profile* profile_; - - // The auth status from the most-recently-completed request. - GoogleServiceAuthError last_auth_error_; - // Getter to use for fetchers. - scoped_refptr<net::URLRequestContextGetter> getter_; + scoped_refptr<net::URLRequestContextGetter> request_context_getter_; // The cache of currently valid tokens. typedef std::map<ScopeSet, CacheEntry> TokenCache; @@ -181,9 +196,6 @@ class OAuth2TokenService : public content::NotificationObserver, // token using these parameters. std::map<FetchParameters, Fetcher*> pending_fetchers_; - // Registrar for notifications from the TokenService. - content::NotificationRegistrar registrar_; - DISALLOW_COPY_AND_ASSIGN(OAuth2TokenService); }; diff --git a/chrome/browser/signin/oauth2_token_service_factory.cc b/chrome/browser/signin/oauth2_token_service_factory.cc deleted file mode 100644 index c0ca0be..0000000 --- a/chrome/browser/signin/oauth2_token_service_factory.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2012 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 "chrome/browser/signin/oauth2_token_service_factory.h" - -#include "chrome/browser/profiles/profile_dependency_manager.h" -#include "chrome/browser/signin/oauth2_token_service.h" -#include "chrome/browser/signin/signin_manager_factory.h" -#include "chrome/browser/signin/token_service_factory.h" - -OAuth2TokenServiceFactory::OAuth2TokenServiceFactory() - : ProfileKeyedServiceFactory("OAuth2TokenService", - ProfileDependencyManager::GetInstance()) { - DependsOn(SigninManagerFactory::GetInstance()); - DependsOn(TokenServiceFactory::GetInstance()); -} - -OAuth2TokenServiceFactory::~OAuth2TokenServiceFactory() { -} - -// static -OAuth2TokenService* OAuth2TokenServiceFactory::GetForProfile(Profile* profile) { - return static_cast<OAuth2TokenService*>( - GetInstance()->GetServiceForProfile(profile, true)); -} - -// static -OAuth2TokenServiceFactory* OAuth2TokenServiceFactory::GetInstance() { - return Singleton<OAuth2TokenServiceFactory>::get(); -} - -ProfileKeyedService* OAuth2TokenServiceFactory::BuildServiceInstanceFor( - Profile* profile) const { - OAuth2TokenService* service = new OAuth2TokenService(); - service->Initialize(profile); - return service; -} diff --git a/chrome/browser/signin/oauth2_token_service_factory.h b/chrome/browser/signin/oauth2_token_service_factory.h deleted file mode 100644 index 155f0e3..0000000 --- a/chrome/browser/signin/oauth2_token_service_factory.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2012 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 CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_FACTORY_H_ -#define CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_FACTORY_H_ - -#include "base/memory/singleton.h" -#include "chrome/browser/profiles/profile_keyed_service_factory.h" - -class OAuth2TokenService; -class Profile; - -// Singleton that owns all OAuth2TokenServices and associates them with -// Profiles. Listens for the Profile's destruction notification and cleans up -// the associated OAuth2TokenService. -class OAuth2TokenServiceFactory : public ProfileKeyedServiceFactory { - public: - // Returns the instance of OAuth2TokenService associated with this profile - // (creating one if none exists). Returns NULL if this profile cannot have a - // OAuth2TokenService (for example, if |profile| is incognito). - static OAuth2TokenService* GetForProfile(Profile* profile); - - // Returns an instance of the OAuth2TokenServiceFactory singleton. - static OAuth2TokenServiceFactory* GetInstance(); - - private: - friend struct DefaultSingletonTraits<OAuth2TokenServiceFactory>; - - OAuth2TokenServiceFactory(); - virtual ~OAuth2TokenServiceFactory(); - - // ProfileKeyedServiceFactory implementation. - virtual ProfileKeyedService* BuildServiceInstanceFor( - Profile* profile) const OVERRIDE; - - DISALLOW_COPY_AND_ASSIGN(OAuth2TokenServiceFactory); -}; - -#endif // CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_FACTORY_H_ diff --git a/chrome/browser/signin/oauth2_token_service_request.h b/chrome/browser/signin/oauth2_token_service_request.h deleted file mode 100644 index 8ab4b79..0000000 --- a/chrome/browser/signin/oauth2_token_service_request.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2012 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 CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_REQUEST_H_ -#define CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_REQUEST_H_ - -#include <set> -#include <string> - -#include "base/memory/ref_counted.h" -#include "base/threading/non_thread_safe.h" -#include "base/time.h" -#include "chrome/browser/signin/oauth2_token_service.h" -#include "google_apis/gaia/google_service_auth_error.h" - -class Profile; - -// OAuth2TokenServiceRequest represents a request to fetch an OAuth2 access -// token for a given set of |scopes| by calling |profile|'s -// OAuth2TokenService. A request can be created and started from any thread -// with an object |consumer| that will be called back on the same thread when -// fetching completes. -// If the request is destructed before |consumer| is called, |consumer| will -// never be called back. (Note the actual network activities are not canceled -// and the cache in OAuth2TokenService will be populated with the fetched -// results.) -class OAuth2TokenServiceRequest : public OAuth2TokenService::Request, - public base::NonThreadSafe { - public: - static OAuth2TokenServiceRequest* CreateAndStart( - Profile* profile, - const OAuth2TokenService::ScopeSet& scopes, - OAuth2TokenService::Consumer* consumer); - - virtual ~OAuth2TokenServiceRequest(); - - private: - class Core; - friend class Core; - - OAuth2TokenServiceRequest(Profile* profile, - const OAuth2TokenService::ScopeSet& scopes, - OAuth2TokenService::Consumer* consumer); - OAuth2TokenService::Consumer* const consumer_; - scoped_refptr<Core> core_; - - DISALLOW_COPY_AND_ASSIGN(OAuth2TokenServiceRequest); -}; - -#endif // CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_REQUEST_H_ diff --git a/chrome/browser/signin/oauth2_token_service_test_util.cc b/chrome/browser/signin/oauth2_token_service_test_util.cc new file mode 100644 index 0000000..dd03199 --- /dev/null +++ b/chrome/browser/signin/oauth2_token_service_test_util.cc @@ -0,0 +1,40 @@ +// Copyright 2013 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 "chrome/browser/signin/oauth2_token_service_test_util.h" + +const char kValidTokenResponse[] = + "{" + " \"access_token\": \"%s\"," + " \"expires_in\": %d," + " \"token_type\": \"Bearer\"" + "}"; + +std::string GetValidTokenResponse(std::string token, int expiration) { + return base::StringPrintf(kValidTokenResponse, token.c_str(), expiration); +} + +TestingOAuth2TokenServiceConsumer::TestingOAuth2TokenServiceConsumer() + : number_of_successful_tokens_(0), + last_error_(GoogleServiceAuthError::AuthErrorNone()), + number_of_errors_(0) { +} + +TestingOAuth2TokenServiceConsumer::~TestingOAuth2TokenServiceConsumer() { +} + +void TestingOAuth2TokenServiceConsumer::OnGetTokenSuccess( + const OAuth2TokenService::Request* request, + const std::string& token, + const base::Time& expiration_date) { + last_token_ = token; + ++number_of_successful_tokens_; +} + +void TestingOAuth2TokenServiceConsumer::OnGetTokenFailure( + const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) { + last_error_ = error; + ++number_of_errors_; +} diff --git a/chrome/browser/signin/oauth2_token_service_test_util.h b/chrome/browser/signin/oauth2_token_service_test_util.h new file mode 100644 index 0000000..f99fab3 --- /dev/null +++ b/chrome/browser/signin/oauth2_token_service_test_util.h @@ -0,0 +1,38 @@ +// Copyright 2013 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 CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_ +#define CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_ + +#include "chrome/browser/signin/oauth2_token_service.h" + +#include <string> + +#include "base/stringprintf.h" +#include "google_apis/gaia/google_service_auth_error.h" + +extern const char kValidTokenResponse[]; + +std::string GetValidTokenResponse(std::string token, int expiration); + +// A simple testing consumer. +class TestingOAuth2TokenServiceConsumer : public OAuth2TokenService::Consumer { + public: + TestingOAuth2TokenServiceConsumer(); + virtual ~TestingOAuth2TokenServiceConsumer(); + + // OAuth2TokenService::Consumer overrides. + virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, + const std::string& token, + const base::Time& expiration_date) OVERRIDE; + virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) OVERRIDE; + + std::string last_token_; + int number_of_successful_tokens_; + GoogleServiceAuthError last_error_; + int number_of_errors_; +}; + +#endif // CHROME_BROWSER_SIGNIN_OAUTH2_TOKEN_SERVICE_TEST_UTIL_H_ diff --git a/chrome/browser/signin/oauth2_token_service_unittest.cc b/chrome/browser/signin/oauth2_token_service_unittest.cc index 7095b43..8f96f13 100644 --- a/chrome/browser/signin/oauth2_token_service_unittest.cc +++ b/chrome/browser/signin/oauth2_token_service_unittest.cc @@ -2,108 +2,78 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <string> + #include "base/stringprintf.h" #include "chrome/browser/signin/oauth2_token_service.h" -#include "chrome/browser/signin/oauth2_token_service_factory.h" +#include "chrome/browser/signin/oauth2_token_service_test_util.h" #include "chrome/browser/signin/token_service_factory.h" #include "chrome/browser/signin/token_service_unittest.h" #include "chrome/common/chrome_notification_types.h" -#include "content/public/browser/browser_thread.h" +#include "chrome/test/base/testing_browser_process.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_access_token_consumer.h" #include "net/http/http_status_code.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_request_status.h" +#include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" using content::BrowserThread; -static const char kValidTokenResponse[] = - "{" - " \"access_token\": \"%s\"," - " \"expires_in\": %d," - " \"token_type\": \"Bearer\"" - "}"; - -std::string GetValidTokenResponse(std::string token, int expiration) { - return base::StringPrintf(kValidTokenResponse, token.c_str(), expiration); -} - -// A simple testing consumer. -class TestingOAuth2TokenServiceConsumer : public OAuth2TokenService::Consumer { - public: - TestingOAuth2TokenServiceConsumer() - : number_of_correct_tokens_(0), - last_error_(GoogleServiceAuthError::AuthErrorNone()), - number_of_errors_(0) {} - virtual ~TestingOAuth2TokenServiceConsumer() {} - - virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, - const std::string& token, - const base::Time& expiration_date) OVERRIDE { - last_token_ = token; - ++number_of_correct_tokens_; - } - - virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, - const GoogleServiceAuthError& error) OVERRIDE { - last_error_ = error; - ++number_of_errors_; - } - - std::string last_token_; - int number_of_correct_tokens_; - GoogleServiceAuthError last_error_; - int number_of_errors_; -}; - // A testing consumer that retries on error. class RetryingTestingOAuth2TokenServiceConsumer - : public OAuth2TokenService::Consumer { + : public TestingOAuth2TokenServiceConsumer { public: RetryingTestingOAuth2TokenServiceConsumer( OAuth2TokenService* oauth2_service) - : oauth2_service_(oauth2_service), - number_of_correct_tokens_(0), - last_error_(GoogleServiceAuthError::AuthErrorNone()), - number_of_errors_(0) {} + : oauth2_service_(oauth2_service) {} virtual ~RetryingTestingOAuth2TokenServiceConsumer() {} - virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, - const std::string& token, - const base::Time& expiration_date) OVERRIDE { - last_token_ = token; - ++number_of_correct_tokens_; - } - virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) OVERRIDE { - last_error_ = error; - ++number_of_errors_; + TestingOAuth2TokenServiceConsumer::OnGetTokenFailure(request, error); request_.reset(oauth2_service_->StartRequest( std::set<std::string>(), this).release()); } OAuth2TokenService* oauth2_service_; scoped_ptr<OAuth2TokenService::Request> request_; - std::string last_token_; - int number_of_correct_tokens_; - GoogleServiceAuthError last_error_; - int number_of_errors_; +}; + +class TestOAuth2TokenService : public OAuth2TokenService { + public: + explicit TestOAuth2TokenService(net::URLRequestContextGetter* getter) + : OAuth2TokenService(getter) { + } + + // For testing: set the refresh token to be used. + void set_refresh_token(const std::string& refresh_token) { + refresh_token_ = refresh_token; + } + + protected: + std::string GetRefreshToken() OVERRIDE { + return refresh_token_; + } + + private: + std::string refresh_token_; }; class OAuth2TokenServiceTest : public TokenServiceTestHarness { public: - OAuth2TokenServiceTest() {} + OAuth2TokenServiceTest() + : request_context_getter_(new net::TestURLRequestContextGetter( + message_loop_.message_loop_proxy())) { + } virtual void SetUp() OVERRIDE { TokenServiceTestHarness::SetUp(); io_thread_.reset(new content::TestBrowserThread(content::BrowserThread::IO, &message_loop_)); - service_->UpdateCredentials(credentials_); - profile_->CreateRequestContext(); - oauth2_service_ = OAuth2TokenServiceFactory::GetForProfile(profile_.get()); + oauth2_service_.reset(new TestOAuth2TokenService(request_context_getter_)); } virtual void TearDown() OVERRIDE { @@ -112,8 +82,9 @@ class OAuth2TokenServiceTest : public TokenServiceTestHarness { protected: scoped_ptr<content::TestBrowserThread> io_thread_; + scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_; net::TestURLFetcherFactory factory_; - OAuth2TokenService* oauth2_service_; + scoped_ptr<TestOAuth2TokenService> oauth2_service_; TestingOAuth2TokenServiceConsumer consumer_; }; @@ -122,44 +93,42 @@ TEST_F(OAuth2TokenServiceTest, NoOAuth2RefreshToken) { oauth2_service_->StartRequest(std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(1, consumer_.number_of_errors_); } TEST_F(OAuth2TokenServiceTest, FailureShouldNotRetry) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); - scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( - std::set<std::string>(), &consumer_)); + oauth2_service_->set_refresh_token("refreshToken"); + scoped_ptr<OAuth2TokenService::Request> request( + oauth2_service_->StartRequest(std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); EXPECT_TRUE(fetcher); fetcher->set_response_code(net::HTTP_UNAUTHORIZED); fetcher->SetResponseString(std::string()); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(1, consumer_.number_of_errors_); EXPECT_EQ(fetcher, factory_.GetFetcherByID(0)); } TEST_F(OAuth2TokenServiceTest, SuccessWithoutCaching) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); - scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( - std::set<std::string>(), &consumer_)); + oauth2_service_->set_refresh_token("refreshToken"); + scoped_ptr<OAuth2TokenService::Request> request( + oauth2_service_->StartRequest(std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); EXPECT_TRUE(fetcher); fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token", 3600)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token", consumer_.last_token_); } @@ -174,22 +143,21 @@ TEST_F(OAuth2TokenServiceTest, SuccessWithCaching) { std::set<std::string> scopes2; scopes2.insert("s3"); - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); + oauth2_service_->set_refresh_token("refreshToken"); // First request. scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( scopes1, &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); EXPECT_TRUE(fetcher); fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token", 3600)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token", consumer_.last_token_); @@ -201,7 +169,7 @@ TEST_F(OAuth2TokenServiceTest, SuccessWithCaching) { // No new network fetcher. EXPECT_EQ(fetcher, factory_.GetFetcherByID(0)); - EXPECT_EQ(2, consumer_.number_of_correct_tokens_); + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token", consumer_.last_token_); @@ -209,34 +177,33 @@ TEST_F(OAuth2TokenServiceTest, SuccessWithCaching) { scoped_ptr<OAuth2TokenService::Request> request3( oauth2_service_->StartRequest(scopes2, &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(2, consumer_.number_of_correct_tokens_); + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); fetcher = factory_.GetFetcherByID(0); EXPECT_TRUE(fetcher); fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token2", 3600)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(3, consumer_.number_of_correct_tokens_); + EXPECT_EQ(3, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token2", consumer_.last_token_); } TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndFailure) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); + oauth2_service_->set_refresh_token("refreshToken"); // First request. scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); EXPECT_TRUE(fetcher); fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token", 0)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token", consumer_.last_token_); @@ -244,7 +211,7 @@ TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndFailure) { scoped_ptr<OAuth2TokenService::Request> request2( oauth2_service_->StartRequest(std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); // Network failure. @@ -253,26 +220,25 @@ TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndFailure) { fetcher->set_response_code(net::HTTP_UNAUTHORIZED); fetcher->SetResponseString(std::string()); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(1, consumer_.number_of_errors_); } TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndSuccess) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); + oauth2_service_->set_refresh_token("refreshToken"); // First request. scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); EXPECT_TRUE(fetcher); fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token", 0)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token", consumer_.last_token_); @@ -280,7 +246,7 @@ TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndSuccess) { scoped_ptr<OAuth2TokenService::Request> request2( oauth2_service_->StartRequest(std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); fetcher = factory_.GetFetcherByID(0); @@ -288,19 +254,18 @@ TEST_F(OAuth2TokenServiceTest, SuccessAndExpirationAndSuccess) { fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("another token", 0)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(2, consumer_.number_of_correct_tokens_); + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("another token", consumer_.last_token_); } TEST_F(OAuth2TokenServiceTest, RequestDeletedBeforeCompletion) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); + oauth2_service_->set_refresh_token("refreshToken"); scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); EXPECT_TRUE(fetcher); @@ -310,13 +275,12 @@ TEST_F(OAuth2TokenServiceTest, RequestDeletedBeforeCompletion) { fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token", 3600)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); } TEST_F(OAuth2TokenServiceTest, RequestDeletedAfterCompletion) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); + oauth2_service_->set_refresh_token("refreshToken"); scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( std::set<std::string>(), &consumer_)); @@ -325,20 +289,19 @@ TEST_F(OAuth2TokenServiceTest, RequestDeletedAfterCompletion) { fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token", 3600)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token", consumer_.last_token_); request.reset(); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token", consumer_.last_token_); } TEST_F(OAuth2TokenServiceTest, MultipleRequestsForTheSameScopesWithOneDeleted) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); + oauth2_service_->set_refresh_token("refreshToken"); scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( std::set<std::string>(), &consumer_)); @@ -353,14 +316,13 @@ TEST_F(OAuth2TokenServiceTest, MultipleRequestsForTheSameScopesWithOneDeleted) { fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token", 3600)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); } -TEST_F(OAuth2TokenServiceTest, SuccessAndSignOutAndRequest) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); - +TEST_F(OAuth2TokenServiceTest, ClearedRefreshTokenFailsSubsequentRequests) { + // We have a valid refresh token; the first request is successful. + oauth2_service_->set_refresh_token("refreshToken"); scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); @@ -368,61 +330,22 @@ TEST_F(OAuth2TokenServiceTest, SuccessAndSignOutAndRequest) { fetcher->set_response_code(net::HTTP_OK); fetcher->SetResponseString(GetValidTokenResponse("token", 3600)); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("token", consumer_.last_token_); - // Signs out - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - std::string()); - service_->EraseTokensFromDB(); - + // The refresh token is no longer available; subsequent requests fail. + oauth2_service_->set_refresh_token(""); request = oauth2_service_->StartRequest(std::set<std::string>(), &consumer_); message_loop_.RunUntilIdle(); EXPECT_EQ(fetcher, factory_.GetFetcherByID(0)); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(1, consumer_.number_of_errors_); } -TEST_F(OAuth2TokenServiceTest, SuccessAndSignOutAndSignInAndSuccess) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); - std::set<std::string> scopes; - scopes.insert("s1"); - scopes.insert("s2"); - - scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( - scopes, &consumer_)); - message_loop_.RunUntilIdle(); - net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); - fetcher->set_response_code(net::HTTP_OK); - fetcher->SetResponseString(GetValidTokenResponse("token", 3600)); - fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); - EXPECT_EQ(0, consumer_.number_of_errors_); - EXPECT_EQ("token", consumer_.last_token_); - - // Signs out and signs in - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - std::string()); - service_->EraseTokensFromDB(); - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); - - request = oauth2_service_->StartRequest(scopes, &consumer_); - message_loop_.RunUntilIdle(); - fetcher = factory_.GetFetcherByID(0); - fetcher->set_response_code(net::HTTP_OK); - fetcher->SetResponseString(GetValidTokenResponse("another token", 3600)); - fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(2, consumer_.number_of_correct_tokens_); - EXPECT_EQ(0, consumer_.number_of_errors_); - EXPECT_EQ("another token", consumer_.last_token_); -} - -TEST_F(OAuth2TokenServiceTest, PendingAndSignOutAndSignInAndSuccess) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "first refreshToken"); +TEST_F(OAuth2TokenServiceTest, + ChangedRefreshTokenDoesNotAffectInFlightRequests) { + oauth2_service_->set_refresh_token("first refreshToken"); std::set<std::string> scopes; scopes.insert("s1"); scopes.insert("s2"); @@ -432,11 +355,11 @@ TEST_F(OAuth2TokenServiceTest, PendingAndSignOutAndSignInAndSuccess) { message_loop_.RunUntilIdle(); net::TestURLFetcher* fetcher1 = factory_.GetFetcherByID(0); - // Note |request| is still pending. - service_->EraseTokensFromDB(); - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "second refreshToken"); + // Note |request| is still pending when the refresh token changes. + oauth2_service_->set_refresh_token("second refreshToken"); + // A 2nd request (using the new refresh token) that occurs and completes + // while the 1st request is in flight is successful. TestingOAuth2TokenServiceConsumer consumer2; scoped_ptr<OAuth2TokenService::Request> request2( oauth2_service_->StartRequest(scopes, &consumer2)); @@ -446,41 +369,40 @@ TEST_F(OAuth2TokenServiceTest, PendingAndSignOutAndSignInAndSuccess) { fetcher2->set_response_code(net::HTTP_OK); fetcher2->SetResponseString(GetValidTokenResponse("second token", 3600)); fetcher2->delegate()->OnURLFetchComplete(fetcher2); - EXPECT_EQ(1, consumer2.number_of_correct_tokens_); + EXPECT_EQ(1, consumer2.number_of_successful_tokens_); EXPECT_EQ(0, consumer2.number_of_errors_); EXPECT_EQ("second token", consumer2.last_token_); fetcher1->set_response_code(net::HTTP_OK); fetcher1->SetResponseString(GetValidTokenResponse("first token", 3600)); fetcher1->delegate()->OnURLFetchComplete(fetcher1); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); EXPECT_EQ("first token", consumer_.last_token_); } TEST_F(OAuth2TokenServiceTest, ServiceShutDownBeforeFetchComplete) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); + oauth2_service_->set_refresh_token("refreshToken"); scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( std::set<std::string>(), &consumer_)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); - profile_.reset(); + // The destructor should cancel all in-flight fetchers. + oauth2_service_.reset(NULL); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(1, consumer_.number_of_errors_); } TEST_F(OAuth2TokenServiceTest, RetryingConsumer) { - service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, - "refreshToken"); - RetryingTestingOAuth2TokenServiceConsumer consumer(oauth2_service_); + oauth2_service_->set_refresh_token("refreshToken"); + RetryingTestingOAuth2TokenServiceConsumer consumer(oauth2_service_.get()); scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( std::set<std::string>(), &consumer)); message_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer.number_of_correct_tokens_); + EXPECT_EQ(0, consumer.number_of_successful_tokens_); EXPECT_EQ(0, consumer.number_of_errors_); net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); @@ -488,7 +410,7 @@ TEST_F(OAuth2TokenServiceTest, RetryingConsumer) { fetcher->set_response_code(net::HTTP_UNAUTHORIZED); fetcher->SetResponseString(std::string()); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(0, consumer.number_of_correct_tokens_); + EXPECT_EQ(0, consumer.number_of_successful_tokens_); EXPECT_EQ(1, consumer.number_of_errors_); fetcher = factory_.GetFetcherByID(0); @@ -496,6 +418,6 @@ TEST_F(OAuth2TokenServiceTest, RetryingConsumer) { fetcher->set_response_code(net::HTTP_UNAUTHORIZED); fetcher->SetResponseString(std::string()); fetcher->delegate()->OnURLFetchComplete(fetcher); - EXPECT_EQ(0, consumer.number_of_correct_tokens_); + EXPECT_EQ(0, consumer.number_of_successful_tokens_); EXPECT_EQ(2, consumer.number_of_errors_); } diff --git a/chrome/browser/signin/profile_oauth2_token_service.cc b/chrome/browser/signin/profile_oauth2_token_service.cc new file mode 100644 index 0000000..7d34b2d --- /dev/null +++ b/chrome/browser/signin/profile_oauth2_token_service.cc @@ -0,0 +1,131 @@ +// Copyright 2013 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 "chrome/browser/signin/profile_oauth2_token_service.h" + +#include "base/bind.h" +#include "base/message_loop.h" +#include "base/stl_util.h" +#include "base/time.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/signin_manager.h" +#include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/browser/signin/token_service.h" +#include "chrome/browser/signin/token_service_factory.h" +#include "chrome/common/chrome_notification_types.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_source.h" +#include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "net/url_request/url_request_context_getter.h" + +ProfileOAuth2TokenService::ProfileOAuth2TokenService( + net::URLRequestContextGetter* getter) + : OAuth2TokenService(getter), + profile_(NULL), + last_auth_error_(GoogleServiceAuthError::NONE) { +} + +ProfileOAuth2TokenService::~ProfileOAuth2TokenService() { +} + +void ProfileOAuth2TokenService::Initialize(Profile* profile) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + + DCHECK(profile); + DCHECK(!profile_); + profile_ = profile; + + content::Source<TokenService> token_service_source( + TokenServiceFactory::GetForProfile(profile)); + registrar_.Add(this, + chrome::NOTIFICATION_TOKENS_CLEARED, + token_service_source); + registrar_.Add(this, + chrome::NOTIFICATION_TOKEN_AVAILABLE, + token_service_source); + SigninManagerFactory::GetForProfile(profile_)->signin_global_error()-> + AddProvider(this); +} + +void ProfileOAuth2TokenService::Shutdown() { + if (profile_) { + SigninManagerFactory::GetForProfile(profile_)->signin_global_error()-> + RemoveProvider(this); + } +} + +std::string ProfileOAuth2TokenService::GetRefreshToken() { + TokenService* token_service = TokenServiceFactory::GetForProfile(profile_); + if (!token_service || !token_service->HasOAuthLoginToken()) { + return std::string(); + } + return token_service->GetOAuth2LoginRefreshToken(); +} + +void ProfileOAuth2TokenService::UpdateAuthError( + 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) + return; + + if (error.state() != last_auth_error_.state()) { + last_auth_error_ = error; + SigninManagerFactory::GetForProfile(profile_)->signin_global_error()-> + AuthStatusChanged(); + } +} + +void ProfileOAuth2TokenService::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK(type == chrome::NOTIFICATION_TOKENS_CLEARED || + type == chrome::NOTIFICATION_TOKEN_AVAILABLE); + if (type == chrome::NOTIFICATION_TOKEN_AVAILABLE) { + TokenService::TokenAvailableDetails* tok_details = + content::Details<TokenService::TokenAvailableDetails>(details).ptr(); + if (tok_details->service() != GaiaConstants::kGaiaOAuth2LoginRefreshToken) + return; + } + // The GaiaConstants::kGaiaOAuth2LoginRefreshToken token is used to create + // OAuth2 access tokens. If this token either changes or is cleared, any + // available tokens must be invalidated. + ClearCache(); + UpdateAuthError(GoogleServiceAuthError::AuthErrorNone()); +} + +GoogleServiceAuthError ProfileOAuth2TokenService::GetAuthStatus() const { + return last_auth_error_; +} + +void ProfileOAuth2TokenService::RegisterCacheEntry( + const std::string& refresh_token, + const ScopeSet& scopes, + const std::string& access_token, + const base::Time& expiration_date) { + if (ShouldCacheForRefreshToken(TokenServiceFactory::GetForProfile(profile_), + refresh_token)) { + OAuth2TokenService::RegisterCacheEntry(refresh_token, + scopes, + access_token, + expiration_date); + } +} + +bool ProfileOAuth2TokenService::ShouldCacheForRefreshToken( + TokenService *token_service, + const std::string& refresh_token) { + if (!token_service || + !token_service->HasOAuthLoginToken() || + token_service->GetOAuth2LoginRefreshToken().compare(refresh_token) != 0) { + DLOG(INFO) << + "Received a token with a refresh token not maintained by TokenService."; + return false; + } + return true; +} diff --git a/chrome/browser/signin/profile_oauth2_token_service.h b/chrome/browser/signin/profile_oauth2_token_service.h new file mode 100644 index 0000000..8804b6f --- /dev/null +++ b/chrome/browser/signin/profile_oauth2_token_service.h @@ -0,0 +1,101 @@ +// Copyright 2013 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 CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_H_ +#define CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_H_ + +#include <string> + +#include "base/gtest_prod_util.h" +#include "chrome/browser/profiles/profile_keyed_service.h" +#include "chrome/browser/signin/oauth2_token_service.h" +#include "chrome/browser/signin/signin_global_error.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +namespace net { +class URLRequestContextGetter; +} + +class GoogleServiceAuthError; +class Profile; +class TokenService; + +// ProfileOAuth2TokenService is a ProfileKeyedService that retrieves +// OAuth2 access tokens for a given set of scopes using the OAuth2 login +// refresh token maintained by TokenService. +// +// 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 content::NotificationObserver, + public SigninGlobalError::AuthStatusProvider, + public ProfileKeyedService { + public: + // content::NotificationObserver listening for TokenService updates. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // Initializes this token service with the profile. + virtual void Initialize(Profile* profile); + + // ProfileKeyedService implementation. + virtual void Shutdown() OVERRIDE; + + // SigninGlobalError::AuthStatusProvider implementation. + virtual GoogleServiceAuthError GetAuthStatus() const OVERRIDE; + + // Takes injected TokenService for testing. + bool ShouldCacheForRefreshToken(TokenService *token_service, + const std::string& refresh_token); + + protected: + friend class ProfileOAuth2TokenServiceFactory; + explicit ProfileOAuth2TokenService(net::URLRequestContextGetter* getter); + virtual ~ProfileOAuth2TokenService(); + + virtual std::string GetRefreshToken() 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 GoogleServiceAuthError& error) OVERRIDE; + + // Overridden to not cache tokens if the TokenService refresh token + // changes while a token fetch is in-flight. If the user logs out and + // logs back in with a different account, then any in-flight token + // fetches will be for the old account's refresh token. Therefore + // when they come back, they shouldn't be cached. + virtual void RegisterCacheEntry(const std::string& refresh_token, + const ScopeSet& scopes, + const std::string& access_token, + const base::Time& expiration_date) OVERRIDE; + + private: + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceTest, + StaleRefreshTokensNotCached); + FRIEND_TEST_ALL_PREFIXES(ProfileOAuth2TokenServiceTest, + TokenServiceUpdateClearsCache); + + // The profile with which this instance was initialized, or NULL. + Profile* profile_; + + // The auth status from the most-recently-completed request. + GoogleServiceAuthError last_auth_error_; + + // Registrar for notifications from the TokenService. + content::NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenService); +}; + +#endif // CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_H_ diff --git a/chrome/browser/signin/profile_oauth2_token_service_factory.cc b/chrome/browser/signin/profile_oauth2_token_service_factory.cc new file mode 100644 index 0000000..f9fb647 --- /dev/null +++ b/chrome/browser/signin/profile_oauth2_token_service_factory.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2012 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 "chrome/browser/signin/profile_oauth2_token_service_factory.h" + +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_dependency_manager.h" +#include "chrome/browser/signin/profile_oauth2_token_service.h" +#include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/browser/signin/token_service_factory.h" + +#if defined(OS_ANDROID) +#include "chrome/browser/signin/android_profile_oauth2_token_service.h" +#endif + +ProfileOAuth2TokenServiceFactory::ProfileOAuth2TokenServiceFactory() + : ProfileKeyedServiceFactory("ProfileOAuth2TokenService", + ProfileDependencyManager::GetInstance()) { + DependsOn(SigninManagerFactory::GetInstance()); + DependsOn(TokenServiceFactory::GetInstance()); +} + +ProfileOAuth2TokenServiceFactory::~ProfileOAuth2TokenServiceFactory() { +} + +// static +ProfileOAuth2TokenService* ProfileOAuth2TokenServiceFactory::GetForProfile( + Profile* profile) { + return static_cast<ProfileOAuth2TokenService*>( + GetInstance()->GetServiceForProfile(profile, true)); +} + +// static +ProfileOAuth2TokenServiceFactory* + ProfileOAuth2TokenServiceFactory::GetInstance() { + return Singleton<ProfileOAuth2TokenServiceFactory>::get(); +} + +ProfileKeyedService* ProfileOAuth2TokenServiceFactory::BuildServiceInstanceFor( + Profile* profile) const { + ProfileOAuth2TokenService* service; +#if defined(OS_ANDROID) + service = new AndroidProfileOAuth2TokenService(profile->GetRequestContext()); +#else + service = new ProfileOAuth2TokenService(profile->GetRequestContext()); +#endif + service->Initialize(profile); + return service; +} diff --git a/chrome/browser/signin/profile_oauth2_token_service_factory.h b/chrome/browser/signin/profile_oauth2_token_service_factory.h new file mode 100644 index 0000000..a63e6db --- /dev/null +++ b/chrome/browser/signin/profile_oauth2_token_service_factory.h @@ -0,0 +1,42 @@ +// Copyright 2012 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 CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "chrome/browser/profiles/profile_keyed_service_factory.h" + +class ProfileOAuth2TokenService; +class Profile; + +// Singleton that owns all ProfileOAuth2TokenServices and associates them with +// Profiles. Listens for the Profile's destruction notification and cleans up +// the associated ProfileOAuth2TokenService. +class ProfileOAuth2TokenServiceFactory : public ProfileKeyedServiceFactory { + public: + // Returns the instance of ProfileOAuth2TokenService associated with this + // profile (creating one if none exists). Returns NULL if this profile + // cannot have a ProfileOAuth2TokenService (for example, if |profile| is + // incognito). On Android, returns the AndroidProfileOAuth2TokenService + // specialization. + static ProfileOAuth2TokenService* GetForProfile(Profile* profile); + + // Returns an instance of the ProfileOAuth2TokenServiceFactory singleton. + static ProfileOAuth2TokenServiceFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<ProfileOAuth2TokenServiceFactory>; + + ProfileOAuth2TokenServiceFactory(); + virtual ~ProfileOAuth2TokenServiceFactory(); + + // ProfileKeyedServiceFactory implementation. + virtual ProfileKeyedService* BuildServiceInstanceFor( + Profile* profile) const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenServiceFactory); +}; + +#endif // CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_FACTORY_H_ diff --git a/chrome/browser/signin/oauth2_token_service_request.cc b/chrome/browser/signin/profile_oauth2_token_service_request.cc index aad3ca4..753c2e4 100644 --- a/chrome/browser/signin/oauth2_token_service_request.cc +++ b/chrome/browser/signin/profile_oauth2_token_service_request.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/signin/oauth2_token_service_request.h" +#include "chrome/browser/signin/profile_oauth2_token_service_request.h" #include "base/bind.h" #include "base/memory/ref_counted.h" @@ -10,20 +10,20 @@ #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/signin/oauth2_token_service.h" -#include "chrome/browser/signin/oauth2_token_service_factory.h" +#include "chrome/browser/signin/profile_oauth2_token_service.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "content/public/browser/browser_thread.h" #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_access_token_consumer.h" -class OAuth2TokenServiceRequest::Core - : public base::RefCountedThreadSafe<OAuth2TokenServiceRequest::Core>, +class ProfileOAuth2TokenServiceRequest::Core + : public base::RefCountedThreadSafe<ProfileOAuth2TokenServiceRequest::Core>, public OAuth2TokenService::Consumer { public: // Note the thread where an instance of Core is constructed is referred to as // the "owner thread" here. This will be the thread of |owner_task_runner_|. Core(Profile* profile, - OAuth2TokenServiceRequest* owner); + ProfileOAuth2TokenServiceRequest* owner); // Starts fetching an OAuth2 access token for |scopes|. It should be called // on the owner thread. void Start(const OAuth2TokenService::ScopeSet& scopes); @@ -39,7 +39,8 @@ class OAuth2TokenServiceRequest::Core const GoogleServiceAuthError& error) OVERRIDE; private: - friend class base::RefCountedThreadSafe<OAuth2TokenServiceRequest::Core>; + friend class + base::RefCountedThreadSafe<ProfileOAuth2TokenServiceRequest::Core>; // Note this can be destructed on the owner thread or on the UI thread, // depending on the reference count. @@ -62,7 +63,7 @@ class OAuth2TokenServiceRequest::Core // The object to call back when fetching completes. |owner_| should be // called back only on the owner thread. - OAuth2TokenServiceRequest* owner_; + ProfileOAuth2TokenServiceRequest* owner_; // Task runner on which |owner_| should be called back. scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner_; @@ -73,9 +74,9 @@ class OAuth2TokenServiceRequest::Core DISALLOW_COPY_AND_ASSIGN(Core); }; -OAuth2TokenServiceRequest::Core::Core( +ProfileOAuth2TokenServiceRequest::Core::Core( Profile* profile, - OAuth2TokenServiceRequest* owner) + ProfileOAuth2TokenServiceRequest* owner) : profile_(profile), owner_(owner), owner_task_runner_(base::ThreadTaskRunnerHandle::Get()) { @@ -83,10 +84,10 @@ OAuth2TokenServiceRequest::Core::Core( DCHECK(owner); } -OAuth2TokenServiceRequest::Core::~Core() { +ProfileOAuth2TokenServiceRequest::Core::~Core() { } -void OAuth2TokenServiceRequest::Core::Start( +void ProfileOAuth2TokenServiceRequest::Core::Start( const OAuth2TokenService::ScopeSet& scopes) { DCHECK(owner_task_runner_->BelongsToCurrentThread()); @@ -96,12 +97,12 @@ void OAuth2TokenServiceRequest::Core::Start( content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, - base::Bind(&OAuth2TokenServiceRequest::Core::StartOnUIThread, + base::Bind(&ProfileOAuth2TokenServiceRequest::Core::StartOnUIThread, this, scopes)); } } -void OAuth2TokenServiceRequest::Core::Stop() { +void ProfileOAuth2TokenServiceRequest::Core::Stop() { DCHECK(owner_task_runner_->BelongsToCurrentThread()); // Detaches |owner_| from this instance so |owner_| will be called back only @@ -113,49 +114,53 @@ void OAuth2TokenServiceRequest::Core::Stop() { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, - base::Bind(&OAuth2TokenServiceRequest::Core::StopOnUIThread, this)); + base::Bind(&ProfileOAuth2TokenServiceRequest::Core::StopOnUIThread, + this)); } } -void OAuth2TokenServiceRequest::Core::StopOnUIThread() { +void ProfileOAuth2TokenServiceRequest::Core::StopOnUIThread() { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); request_.reset(); } -void OAuth2TokenServiceRequest::Core::StartOnUIThread( +void ProfileOAuth2TokenServiceRequest::Core::StartOnUIThread( const OAuth2TokenService::ScopeSet& scopes) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - OAuth2TokenService* service = OAuth2TokenServiceFactory::GetForProfile( - profile_); + ProfileOAuth2TokenService* service = + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_); DCHECK(service); request_.reset(service->StartRequest(scopes, this).release()); } -void OAuth2TokenServiceRequest::Core::OnGetTokenSuccess( +void ProfileOAuth2TokenServiceRequest::Core::OnGetTokenSuccess( const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK_EQ(request_.get(), request); owner_task_runner_->PostTask(FROM_HERE, base::Bind( - &OAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenSuccess, this, - access_token, expiration_time)); + &ProfileOAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenSuccess, + this, + access_token, + expiration_time)); request_.reset(); } -void OAuth2TokenServiceRequest::Core::OnGetTokenFailure( +void ProfileOAuth2TokenServiceRequest::Core::OnGetTokenFailure( const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK_EQ(request_.get(), request); owner_task_runner_->PostTask(FROM_HERE, base::Bind( - &OAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenFailure, this, + &ProfileOAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenFailure, + this, error)); request_.reset(); } -void OAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenSuccess( +void ProfileOAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenSuccess( std::string access_token, base::Time expiration_time) { DCHECK(owner_task_runner_->BelongsToCurrentThread()); @@ -164,7 +169,7 @@ void OAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenSuccess( owner_->consumer_->OnGetTokenSuccess(owner_, access_token, expiration_time); } -void OAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenFailure( +void ProfileOAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenFailure( GoogleServiceAuthError error) { DCHECK(owner_task_runner_->BelongsToCurrentThread()); @@ -173,14 +178,15 @@ void OAuth2TokenServiceRequest::Core::InformOwnerOnGetTokenFailure( } // static -OAuth2TokenServiceRequest* OAuth2TokenServiceRequest::CreateAndStart( - Profile* profile, - const OAuth2TokenService::ScopeSet& scopes, - OAuth2TokenService::Consumer* consumer) { - return new OAuth2TokenServiceRequest(profile, scopes, consumer); +ProfileOAuth2TokenServiceRequest* + ProfileOAuth2TokenServiceRequest::CreateAndStart( + Profile* profile, + const OAuth2TokenService::ScopeSet& scopes, + OAuth2TokenService::Consumer* consumer) { + return new ProfileOAuth2TokenServiceRequest(profile, scopes, consumer); } -OAuth2TokenServiceRequest::OAuth2TokenServiceRequest( +ProfileOAuth2TokenServiceRequest::ProfileOAuth2TokenServiceRequest( Profile* profile, const OAuth2TokenService::ScopeSet& scopes, OAuth2TokenService::Consumer* consumer) @@ -189,7 +195,7 @@ OAuth2TokenServiceRequest::OAuth2TokenServiceRequest( core_->Start(scopes); } -OAuth2TokenServiceRequest::~OAuth2TokenServiceRequest() { +ProfileOAuth2TokenServiceRequest::~ProfileOAuth2TokenServiceRequest() { DCHECK(CalledOnValidThread()); core_->Stop(); } diff --git a/chrome/browser/signin/profile_oauth2_token_service_request.h b/chrome/browser/signin/profile_oauth2_token_service_request.h new file mode 100644 index 0000000..bcb1ff8 --- /dev/null +++ b/chrome/browser/signin/profile_oauth2_token_service_request.h @@ -0,0 +1,47 @@ +// Copyright 2012 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 CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_REQUEST_H_ +#define CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_REQUEST_H_ + +#include <set> +#include <string> + +#include "base/threading/non_thread_safe.h" +#include "chrome/browser/signin/oauth2_token_service.h" + +class Profile; + +// ProfileOAuth2TokenServiceRequest represents a request to fetch an +// OAuth2 access token for a given set of |scopes| by calling |profile|'s +// ProfileOAuth2TokenService. A request can be created and started from +// any thread with an object |consumer| that will be called back on the +// same thread when fetching completes. If the request is destructed +// before |consumer| is called, |consumer| will never be called back. (Note +// the actual network activities are not canceled and the cache in +// ProfileOAuth2TokenService will be populated with the fetched results.) +class ProfileOAuth2TokenServiceRequest : public OAuth2TokenService::Request, + public base::NonThreadSafe { + public: + static ProfileOAuth2TokenServiceRequest* CreateAndStart( + Profile* profile, + const OAuth2TokenService::ScopeSet& scopes, + OAuth2TokenService::Consumer* consumer); + + virtual ~ProfileOAuth2TokenServiceRequest(); + + private: + class Core; + friend class Core; + + ProfileOAuth2TokenServiceRequest(Profile* profile, + const OAuth2TokenService::ScopeSet& scopes, + OAuth2TokenService::Consumer* consumer); + OAuth2TokenService::Consumer* const consumer_; + scoped_refptr<Core> core_; + + DISALLOW_COPY_AND_ASSIGN(ProfileOAuth2TokenServiceRequest); +}; + +#endif // CHROME_BROWSER_SIGNIN_PROFILE_OAUTH2_TOKEN_SERVICE_REQUEST_H_ diff --git a/chrome/browser/signin/oauth2_token_service_request_unittest.cc b/chrome/browser/signin/profile_oauth2_token_service_request_unittest.cc index 83b92a1..184c7b2 100644 --- a/chrome/browser/signin/oauth2_token_service_request_unittest.cc +++ b/chrome/browser/signin/profile_oauth2_token_service_request_unittest.cc @@ -1,14 +1,15 @@ // Copyright 2012 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 "chrome/browser/signin/oauth2_token_service_request.h" +#include "chrome/browser/signin/profile_oauth2_token_service_request.h" #include <set> #include <string> #include <vector> #include "base/threading/thread.h" #include "chrome/browser/signin/oauth2_token_service.h" -#include "chrome/browser/signin/oauth2_token_service_factory.h" +#include "chrome/browser/signin/profile_oauth2_token_service.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/token_service_factory.h" #include "chrome/test/base/testing_profile.h" #include "content/public/test/test_browser_thread.h" @@ -29,13 +30,13 @@ class TestingOAuth2TokenServiceConsumer : public OAuth2TokenService::Consumer { const GoogleServiceAuthError& error) OVERRIDE; std::string last_token_; - int number_of_correct_tokens_; + int number_of_successful_tokens_; GoogleServiceAuthError last_error_; int number_of_errors_; }; TestingOAuth2TokenServiceConsumer::TestingOAuth2TokenServiceConsumer() - : number_of_correct_tokens_(0), + : number_of_successful_tokens_(0), last_error_(GoogleServiceAuthError::AuthErrorNone()), number_of_errors_(0) { } @@ -48,7 +49,7 @@ void TestingOAuth2TokenServiceConsumer::OnGetTokenSuccess( const std::string& token, const base::Time& expiration_date) { last_token_ = token; - ++number_of_correct_tokens_; + ++number_of_successful_tokens_; } void TestingOAuth2TokenServiceConsumer::OnGetTokenFailure( @@ -58,7 +59,7 @@ void TestingOAuth2TokenServiceConsumer::OnGetTokenFailure( ++number_of_errors_; } -class MockOAuth2TokenService : public OAuth2TokenService { +class MockProfileOAuth2TokenService : public ProfileOAuth2TokenService { public: class Request : public OAuth2TokenService::Request, public base::SupportsWeakPtr<Request> { @@ -77,8 +78,8 @@ class MockOAuth2TokenService : public OAuth2TokenService { base::Time expiration_date_; }; - MockOAuth2TokenService(); - virtual ~MockOAuth2TokenService(); + MockProfileOAuth2TokenService(); + virtual ~MockProfileOAuth2TokenService(); virtual scoped_ptr<OAuth2TokenService::Request> StartRequest( const std::set<std::string>& scopes, @@ -88,13 +89,13 @@ class MockOAuth2TokenService : public OAuth2TokenService { private: static void InformConsumer( - base::WeakPtr<MockOAuth2TokenService::Request> request); + base::WeakPtr<MockProfileOAuth2TokenService::Request> request); bool success_; std::string oauth2_access_token_; }; -MockOAuth2TokenService::Request::Request( +MockProfileOAuth2TokenService::Request::Request( OAuth2TokenService::Consumer* consumer, GoogleServiceAuthError error, std::string access_token) @@ -103,62 +104,64 @@ MockOAuth2TokenService::Request::Request( access_token_(access_token) { } -MockOAuth2TokenService::Request::~Request() { +MockProfileOAuth2TokenService::Request::~Request() { } -void MockOAuth2TokenService::Request::InformConsumer() const { +void MockProfileOAuth2TokenService::Request::InformConsumer() const { if (error_.state() == GoogleServiceAuthError::NONE) consumer_->OnGetTokenSuccess(this, access_token_, expiration_date_); else consumer_->OnGetTokenFailure(this, error_); } -MockOAuth2TokenService::MockOAuth2TokenService() - : success_(true), +MockProfileOAuth2TokenService::MockProfileOAuth2TokenService() + : ProfileOAuth2TokenService(NULL /* URLRequestContextGetter */), + success_(true), oauth2_access_token_(std::string("success token")) { } -MockOAuth2TokenService::~MockOAuth2TokenService() { +MockProfileOAuth2TokenService::~MockProfileOAuth2TokenService() { } -void MockOAuth2TokenService::SetExpectation(bool success, +void MockProfileOAuth2TokenService::SetExpectation(bool success, std::string oauth2_access_token) { success_ = success; oauth2_access_token_ = oauth2_access_token; } // static -void MockOAuth2TokenService::InformConsumer( - base::WeakPtr<MockOAuth2TokenService::Request> request) { +void MockProfileOAuth2TokenService::InformConsumer( + base::WeakPtr<MockProfileOAuth2TokenService::Request> request) { if (request) request->InformConsumer(); } -scoped_ptr<OAuth2TokenService::Request> MockOAuth2TokenService::StartRequest( - const std::set<std::string>& scopes, - OAuth2TokenService::Consumer* consumer) { +scoped_ptr<OAuth2TokenService::Request> + MockProfileOAuth2TokenService::StartRequest( + const std::set<std::string>& scopes, + OAuth2TokenService::Consumer* consumer) { scoped_ptr<Request> request; if (success_) { - request.reset(new MockOAuth2TokenService::Request( + request.reset(new MockProfileOAuth2TokenService::Request( consumer, GoogleServiceAuthError(GoogleServiceAuthError::NONE), oauth2_access_token_)); } else { - request.reset(new MockOAuth2TokenService::Request( + request.reset(new MockProfileOAuth2TokenService::Request( consumer, GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE), std::string())); } MessageLoop::current()->PostTask(FROM_HERE, base::Bind( - &MockOAuth2TokenService::InformConsumer, request->AsWeakPtr())); + &MockProfileOAuth2TokenService::InformConsumer, request->AsWeakPtr())); return request.PassAs<OAuth2TokenService::Request>(); } static ProfileKeyedService* CreateOAuth2TokenService(Profile* profile) { - return new MockOAuth2TokenService(); + return new MockProfileOAuth2TokenService(); } -class OAuth2TokenServiceRequestTest : public testing::Test { +class ProfileOAuth2TokenServiceRequestTest : public testing::Test { public: virtual void SetUp() OVERRIDE; @@ -168,70 +171,70 @@ class OAuth2TokenServiceRequestTest : public testing::Test { scoped_ptr<Profile> profile_; TestingOAuth2TokenServiceConsumer consumer_; - MockOAuth2TokenService* oauth2_service_; + MockProfileOAuth2TokenService* oauth2_service_; - scoped_ptr<OAuth2TokenServiceRequest> request_; + scoped_ptr<ProfileOAuth2TokenServiceRequest> request_; }; -void OAuth2TokenServiceRequestTest::SetUp() { +void ProfileOAuth2TokenServiceRequestTest::SetUp() { ui_thread_.reset(new content::TestBrowserThread(content::BrowserThread::UI, &ui_loop_)); profile_.reset(new TestingProfile()); - OAuth2TokenServiceFactory::GetInstance()->SetTestingFactory( + ProfileOAuth2TokenServiceFactory::GetInstance()->SetTestingFactory( profile_.get(), &CreateOAuth2TokenService); - oauth2_service_ = (MockOAuth2TokenService*) - OAuth2TokenServiceFactory::GetForProfile(profile_.get()); + oauth2_service_ = (MockProfileOAuth2TokenService*) + ProfileOAuth2TokenServiceFactory::GetForProfile(profile_.get()); } -TEST_F(OAuth2TokenServiceRequestTest, +TEST_F(ProfileOAuth2TokenServiceRequestTest, Failure) { oauth2_service_->SetExpectation(false, std::string()); - scoped_ptr<OAuth2TokenServiceRequest> request( - OAuth2TokenServiceRequest::CreateAndStart( + scoped_ptr<ProfileOAuth2TokenServiceRequest> request( + ProfileOAuth2TokenServiceRequest::CreateAndStart( profile_.get(), std::set<std::string>(), &consumer_)); ui_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(1, consumer_.number_of_errors_); } -TEST_F(OAuth2TokenServiceRequestTest, +TEST_F(ProfileOAuth2TokenServiceRequestTest, Success) { - scoped_ptr<OAuth2TokenServiceRequest> request( - OAuth2TokenServiceRequest::CreateAndStart( + scoped_ptr<ProfileOAuth2TokenServiceRequest> request( + ProfileOAuth2TokenServiceRequest::CreateAndStart( profile_.get(), std::set<std::string>(), &consumer_)); ui_loop_.RunUntilIdle(); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ("success token", consumer_.last_token_); EXPECT_EQ(0, consumer_.number_of_errors_); } -TEST_F(OAuth2TokenServiceRequestTest, +TEST_F(ProfileOAuth2TokenServiceRequestTest, RequestDeletionBeforeServiceComplete) { - scoped_ptr<OAuth2TokenServiceRequest> request( - OAuth2TokenServiceRequest::CreateAndStart( + scoped_ptr<ProfileOAuth2TokenServiceRequest> request( + ProfileOAuth2TokenServiceRequest::CreateAndStart( profile_.get(), std::set<std::string>(), &consumer_)); request.reset(); ui_loop_.RunUntilIdle(); - EXPECT_EQ(0, consumer_.number_of_correct_tokens_); + EXPECT_EQ(0, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); } -TEST_F(OAuth2TokenServiceRequestTest, +TEST_F(ProfileOAuth2TokenServiceRequestTest, RequestDeletionAfterServiceComplete) { - scoped_ptr<OAuth2TokenServiceRequest> request( - OAuth2TokenServiceRequest::CreateAndStart( + scoped_ptr<ProfileOAuth2TokenServiceRequest> request( + ProfileOAuth2TokenServiceRequest::CreateAndStart( profile_.get(), std::set<std::string>(), &consumer_)); ui_loop_.RunUntilIdle(); request.reset(); - EXPECT_EQ(1, consumer_.number_of_correct_tokens_); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); EXPECT_EQ(0, consumer_.number_of_errors_); } diff --git a/chrome/browser/signin/profile_oauth2_token_service_unittest.cc b/chrome/browser/signin/profile_oauth2_token_service_unittest.cc new file mode 100644 index 0000000..036a1c6 --- /dev/null +++ b/chrome/browser/signin/profile_oauth2_token_service_unittest.cc @@ -0,0 +1,97 @@ +// Copyright 2013 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 "chrome/browser/signin/oauth2_token_service.h" +#include "chrome/browser/signin/oauth2_token_service_test_util.h" +#include "chrome/browser/signin/profile_oauth2_token_service.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" +#include "chrome/browser/signin/token_service_unittest.h" +#include "content/public/browser/browser_thread.h" +#include "google_apis/gaia/gaia_constants.h" +#include "net/http/http_status_code.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::BrowserThread; + +class ProfileOAuth2TokenServiceTest : public TokenServiceTestHarness { + public: + ProfileOAuth2TokenServiceTest() {} + + virtual void SetUp() OVERRIDE { + TokenServiceTestHarness::SetUp(); + io_thread_.reset(new content::TestBrowserThread(content::BrowserThread::IO, + &message_loop_)); + service_->UpdateCredentials(credentials_); + profile_->CreateRequestContext(); + oauth2_service_ = ProfileOAuth2TokenServiceFactory::GetForProfile( + profile_.get()); + } + + virtual void TearDown() OVERRIDE { + TokenServiceTestHarness::TearDown(); + } + + protected: + scoped_ptr<content::TestBrowserThread> io_thread_; + net::TestURLFetcherFactory factory_; + ProfileOAuth2TokenService* oauth2_service_; + TestingOAuth2TokenServiceConsumer consumer_; +}; + +TEST_F(ProfileOAuth2TokenServiceTest, TokenServiceUpdateClearsCache) { + EXPECT_EQ(0, oauth2_service_->cache_size_for_testing()); + std::set<std::string> scope_list; + scope_list.insert("scope"); + service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, + "refreshToken"); + scoped_ptr<OAuth2TokenService::Request> request(oauth2_service_->StartRequest( + scope_list, &consumer_)); + message_loop_.RunUntilIdle(); + net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); + fetcher->set_response_code(net::HTTP_OK); + fetcher->SetResponseString(GetValidTokenResponse("token", 3600)); + fetcher->delegate()->OnURLFetchComplete(fetcher); + EXPECT_EQ(1, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("token", consumer_.last_token_); + EXPECT_EQ(1, oauth2_service_->cache_size_for_testing()); + + // Signs out and signs in + service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, + ""); + service_->EraseTokensFromDB(); + EXPECT_EQ(0, oauth2_service_->cache_size_for_testing()); + service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, + "refreshToken"); + + request = oauth2_service_->StartRequest(scope_list, &consumer_); + message_loop_.RunUntilIdle(); + fetcher = factory_.GetFetcherByID(0); + fetcher->set_response_code(net::HTTP_OK); + fetcher->SetResponseString(GetValidTokenResponse("another token", 3600)); + fetcher->delegate()->OnURLFetchComplete(fetcher); + EXPECT_EQ(2, consumer_.number_of_successful_tokens_); + EXPECT_EQ(0, consumer_.number_of_errors_); + EXPECT_EQ("another token", consumer_.last_token_); + EXPECT_EQ(1, oauth2_service_->cache_size_for_testing()); +} + +// Android doesn't use the current profile's TokenService login refresh token. +#if !defined(OS_ANDROID) +TEST_F(ProfileOAuth2TokenServiceTest, StaleRefreshTokensNotCached) { + EXPECT_FALSE(service_->HasOAuthLoginToken()); + EXPECT_FALSE(oauth2_service_->ShouldCacheForRefreshToken(service_, "T1")); + + service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, + "T1"); + EXPECT_TRUE(oauth2_service_->ShouldCacheForRefreshToken(service_, "T1")); + EXPECT_FALSE(oauth2_service_->ShouldCacheForRefreshToken(service_, "T2")); + + service_->IssueAuthTokenForTest(GaiaConstants::kGaiaOAuth2LoginRefreshToken, + "T2"); + EXPECT_TRUE(oauth2_service_->ShouldCacheForRefreshToken(service_, "T2")); + EXPECT_FALSE(oauth2_service_->ShouldCacheForRefreshToken(NULL, "T2")); +} +#endif diff --git a/chrome/browser/signin/token_service_unittest.cc b/chrome/browser/signin/token_service_unittest.cc index fbe83df..198952c 100644 --- a/chrome/browser/signin/token_service_unittest.cc +++ b/chrome/browser/signin/token_service_unittest.cc @@ -231,7 +231,8 @@ TEST_F(TokenServiceTest, OnTokenSuccessUpdate) { } TEST_F(TokenServiceTest, OnOAuth2LoginTokenSuccessUpdate) { - std::string service = GaiaConstants::kGaiaOAuth2LoginRefreshToken; + EXPECT_FALSE(service_->HasOAuthLoginToken()); + service_->OnClientOAuthSuccess( GaiaAuthConsumer::ClientOAuthResult("rt1", "at1", 3600)); EXPECT_TRUE(service_->HasOAuthLoginToken()); |