// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "google_apis/gaia/oauth2_token_service_request.h" #include "base/bind.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_access_token_consumer.h" OAuth2TokenServiceRequest::TokenServiceProvider::TokenServiceProvider() { } OAuth2TokenServiceRequest::TokenServiceProvider::~TokenServiceProvider() { } // Core serves as the base class for OAuth2TokenService operations. Each // operation should be modeled as a derived type. // // Core is used like this: // // 1. Constructed on owner thread. // // 2. Start() is called on owner thread, which calls StartOnTokenServiceThread() // on token service thread. // // 3. Request is executed. // // 4. Stop() is called on owner thread, which calls StopOnTokenServiceThread() // on token service thread. // // 5. Core is destroyed on owner thread. class OAuth2TokenServiceRequest::Core : public base::NonThreadSafe, public base::RefCountedThreadSafe { public: // Note the thread where an instance of Core is constructed is referred to as // the "owner thread" here. Core(OAuth2TokenServiceRequest* owner, const scoped_refptr& provider); // Starts the core. Must be called on the owner thread. void Start(); // Stops the core. Must be called on the owner thread. void Stop(); // Returns true if this object has been stopped. Must be called on the owner // thread. bool IsStopped() const; protected: // Core must be destroyed on the owner thread. If data members must be // cleaned up or destroyed on the token service thread, do so in the // StopOnTokenServiceThread method. virtual ~Core(); // Called on the token service thread. virtual void StartOnTokenServiceThread() = 0; // Called on the token service thread. virtual void StopOnTokenServiceThread() = 0; base::SingleThreadTaskRunner* token_service_task_runner(); OAuth2TokenService* token_service(); OAuth2TokenServiceRequest* owner(); private: friend class base::RefCountedThreadSafe; void DoNothing(); scoped_refptr token_service_task_runner_; OAuth2TokenServiceRequest* owner_; // Clear on owner thread. OAuth2TokenServiceRequest promises to clear its // last reference to TokenServiceProvider on the owner thread so the caller // can ensure it is destroyed on the owner thread if desired. scoped_refptr provider_; DISALLOW_COPY_AND_ASSIGN(Core); }; OAuth2TokenServiceRequest::Core::Core( OAuth2TokenServiceRequest* owner, const scoped_refptr& provider) : owner_(owner), provider_(provider) { DCHECK(owner_); DCHECK(provider_.get()); token_service_task_runner_ = provider_->GetTokenServiceTaskRunner(); DCHECK(token_service_task_runner_.get()); } OAuth2TokenServiceRequest::Core::~Core() { } void OAuth2TokenServiceRequest::Core::Start() { DCHECK(CalledOnValidThread()); token_service_task_runner_->PostTask( FROM_HERE, base::Bind(&OAuth2TokenServiceRequest::Core::StartOnTokenServiceThread, this)); } void OAuth2TokenServiceRequest::Core::Stop() { DCHECK(CalledOnValidThread()); DCHECK(!IsStopped()); // Detaches |owner_| from this instance so |owner_| will be called back only // if |Stop()| has never been called. owner_ = NULL; // We are stopping and will likely be destroyed soon. Use a reply closure // (DoNothing) to retain "this" and ensure we are destroyed in the owner // thread, not the task runner thread. PostTaskAndReply guarantees that the // reply closure will execute after StopOnTokenServiceThread has completed. token_service_task_runner_->PostTaskAndReply( FROM_HERE, base::Bind(&OAuth2TokenServiceRequest::Core::StopOnTokenServiceThread, this), base::Bind(&OAuth2TokenServiceRequest::Core::DoNothing, this)); } bool OAuth2TokenServiceRequest::Core::IsStopped() const { DCHECK(CalledOnValidThread()); return owner_ == NULL; } base::SingleThreadTaskRunner* OAuth2TokenServiceRequest::Core::token_service_task_runner() { return token_service_task_runner_.get(); } OAuth2TokenService* OAuth2TokenServiceRequest::Core::token_service() { DCHECK(token_service_task_runner_->BelongsToCurrentThread()); return provider_->GetTokenService(); } OAuth2TokenServiceRequest* OAuth2TokenServiceRequest::Core::owner() { DCHECK(CalledOnValidThread()); return owner_; } void OAuth2TokenServiceRequest::Core::DoNothing() { DCHECK(CalledOnValidThread()); } namespace { // An implementation of Core for getting an access token. class RequestCore : public OAuth2TokenServiceRequest::Core, public OAuth2TokenService::Consumer { public: RequestCore(OAuth2TokenServiceRequest* owner, const scoped_refptr< OAuth2TokenServiceRequest::TokenServiceProvider>& provider, OAuth2TokenService::Consumer* consumer, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes); // OAuth2TokenService::Consumer. Must be called on the token service thread. void OnGetTokenSuccess(const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) override; void OnGetTokenFailure(const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) override; private: friend class base::RefCountedThreadSafe; // Must be destroyed on the owner thread. ~RequestCore() override; // Core implementation. void StartOnTokenServiceThread() override; void StopOnTokenServiceThread() override; void InformOwnerOnGetTokenSuccess(std::string access_token, base::Time expiration_time); void InformOwnerOnGetTokenFailure(GoogleServiceAuthError error); scoped_refptr owner_task_runner_; OAuth2TokenService::Consumer* const consumer_; std::string account_id_; OAuth2TokenService::ScopeSet scopes_; // OAuth2TokenService request for fetching OAuth2 access token; it should be // created, reset and accessed only on the token service thread. scoped_ptr request_; DISALLOW_COPY_AND_ASSIGN(RequestCore); }; RequestCore::RequestCore( OAuth2TokenServiceRequest* owner, const scoped_refptr& provider, OAuth2TokenService::Consumer* consumer, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) : OAuth2TokenServiceRequest::Core(owner, provider), OAuth2TokenService::Consumer("oauth2_token_service"), owner_task_runner_(base::ThreadTaskRunnerHandle::Get()), consumer_(consumer), account_id_(account_id), scopes_(scopes) { DCHECK(consumer_); DCHECK(!account_id_.empty()); DCHECK(!scopes_.empty()); } RequestCore::~RequestCore() { } void RequestCore::StartOnTokenServiceThread() { DCHECK(token_service_task_runner()->BelongsToCurrentThread()); request_ = token_service()->StartRequest(account_id_, scopes_, this); } void RequestCore::StopOnTokenServiceThread() { DCHECK(token_service_task_runner()->BelongsToCurrentThread()); request_.reset(); } void RequestCore::OnGetTokenSuccess(const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) { DCHECK(token_service_task_runner()->BelongsToCurrentThread()); DCHECK_EQ(request_.get(), request); owner_task_runner_->PostTask( FROM_HERE, base::Bind(&RequestCore::InformOwnerOnGetTokenSuccess, this, access_token, expiration_time)); request_.reset(); } void RequestCore::OnGetTokenFailure(const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { DCHECK(token_service_task_runner()->BelongsToCurrentThread()); DCHECK_EQ(request_.get(), request); owner_task_runner_->PostTask( FROM_HERE, base::Bind(&RequestCore::InformOwnerOnGetTokenFailure, this, error)); request_.reset(); } void RequestCore::InformOwnerOnGetTokenSuccess(std::string access_token, base::Time expiration_time) { DCHECK(CalledOnValidThread()); if (!IsStopped()) { consumer_->OnGetTokenSuccess(owner(), access_token, expiration_time); } } void RequestCore::InformOwnerOnGetTokenFailure(GoogleServiceAuthError error) { DCHECK(CalledOnValidThread()); if (!IsStopped()) { consumer_->OnGetTokenFailure(owner(), error); } } // An implementation of Core for invalidating an access token. class InvalidateCore : public OAuth2TokenServiceRequest::Core { public: InvalidateCore(OAuth2TokenServiceRequest* owner, const scoped_refptr< OAuth2TokenServiceRequest::TokenServiceProvider>& provider, const std::string& access_token, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes); private: friend class base::RefCountedThreadSafe; // Must be destroyed on the owner thread. ~InvalidateCore() override; // Core implementation. void StartOnTokenServiceThread() override; void StopOnTokenServiceThread() override; std::string access_token_; std::string account_id_; OAuth2TokenService::ScopeSet scopes_; DISALLOW_COPY_AND_ASSIGN(InvalidateCore); }; InvalidateCore::InvalidateCore( OAuth2TokenServiceRequest* owner, const scoped_refptr& provider, const std::string& access_token, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) : OAuth2TokenServiceRequest::Core(owner, provider), access_token_(access_token), account_id_(account_id), scopes_(scopes) { DCHECK(!access_token_.empty()); DCHECK(!account_id_.empty()); DCHECK(!scopes.empty()); } InvalidateCore::~InvalidateCore() { } void InvalidateCore::StartOnTokenServiceThread() { DCHECK(token_service_task_runner()->BelongsToCurrentThread()); token_service()->InvalidateAccessToken(account_id_, scopes_, access_token_); } void InvalidateCore::StopOnTokenServiceThread() { DCHECK(token_service_task_runner()->BelongsToCurrentThread()); // Nothing to do. } } // namespace // static scoped_ptr OAuth2TokenServiceRequest::CreateAndStart( const scoped_refptr& provider, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes, OAuth2TokenService::Consumer* consumer) { scoped_ptr request( new OAuth2TokenServiceRequest(account_id)); scoped_refptr core( new RequestCore(request.get(), provider, consumer, account_id, scopes)); request->StartWithCore(core); return request; } // static void OAuth2TokenServiceRequest::InvalidateToken( const scoped_refptr& provider, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes, const std::string& access_token) { scoped_ptr request( new OAuth2TokenServiceRequest(account_id)); scoped_refptr core(new InvalidateCore( request.get(), provider, access_token, account_id, scopes)); request->StartWithCore(core); } OAuth2TokenServiceRequest::~OAuth2TokenServiceRequest() { core_->Stop(); } std::string OAuth2TokenServiceRequest::GetAccountId() const { return account_id_; } OAuth2TokenServiceRequest::OAuth2TokenServiceRequest( const std::string& account_id) : account_id_(account_id) { DCHECK(!account_id_.empty()); } void OAuth2TokenServiceRequest::StartWithCore(const scoped_refptr& core) { DCHECK(core.get()); core_ = core; core_->Start(); }