blob: 22ef1d68789e52adac6b962b09fd4f9c297b39dd [file] [log] [blame]
// 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 <memory>
#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/sequenced_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::RefCountedDeleteOnSequence<OAuth2TokenServiceRequest::Core> {
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<TokenServiceProvider>& 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;
const base::SingleThreadTaskRunner* token_service_task_runner() const {
return token_service_task_runner_.get();
}
OAuth2TokenService* token_service();
OAuth2TokenServiceRequest* owner();
SEQUENCE_CHECKER(sequence_checker_);
private:
friend class base::RefCountedDeleteOnSequence<
OAuth2TokenServiceRequest::Core>;
friend class base::DeleteHelper<OAuth2TokenServiceRequest::Core>;
scoped_refptr<base::SingleThreadTaskRunner> 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<TokenServiceProvider> provider_;
DISALLOW_COPY_AND_ASSIGN(Core);
};
OAuth2TokenServiceRequest::Core::Core(
OAuth2TokenServiceRequest* owner,
const scoped_refptr<TokenServiceProvider>& provider)
: RefCountedDeleteOnSequence<OAuth2TokenServiceRequest::Core>(
base::SequencedTaskRunnerHandle::Get()),
owner_(owner),
provider_(provider) {
DCHECK(owner_);
DCHECK(provider_.get());
token_service_task_runner_ = provider_->GetTokenServiceTaskRunner();
DCHECK(token_service_task_runner());
}
OAuth2TokenServiceRequest::Core::~Core() {
}
void OAuth2TokenServiceRequest::Core::Start() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
token_service_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&OAuth2TokenServiceRequest::Core::StartOnTokenServiceThread, this));
}
void OAuth2TokenServiceRequest::Core::Stop() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!IsStopped());
// Detaches |owner_| from this instance so |owner_| will be called back only
// if |Stop()| has never been called.
owner_ = nullptr;
// Stop on the token service thread. RefCountedDeleteOnSequence ensures we
// will be destroyed on the owner thread.
token_service_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&OAuth2TokenServiceRequest::Core::StopOnTokenServiceThread,
this));
}
bool OAuth2TokenServiceRequest::Core::IsStopped() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return owner_ == nullptr;
}
OAuth2TokenService* OAuth2TokenServiceRequest::Core::token_service() {
DCHECK(token_service_task_runner()->RunsTasksInCurrentSequence());
return provider_->GetTokenService();
}
OAuth2TokenServiceRequest* OAuth2TokenServiceRequest::Core::owner() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return owner_;
}
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 OAuth2AccessTokenConsumer::TokenResponse& token_response) override;
void OnGetTokenFailure(const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) override;
private:
friend class base::RefCountedThreadSafe<RequestCore>;
// Must be destroyed on the owner thread.
~RequestCore() override;
// Core implementation.
void StartOnTokenServiceThread() override;
void StopOnTokenServiceThread() override;
void InformOwnerOnGetTokenSuccess(
const OAuth2AccessTokenConsumer::TokenResponse& token_response);
void InformOwnerOnGetTokenFailure(GoogleServiceAuthError error);
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.
std::unique_ptr<OAuth2TokenService::Request> request_;
DISALLOW_COPY_AND_ASSIGN(RequestCore);
};
RequestCore::RequestCore(
OAuth2TokenServiceRequest* owner,
const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
provider,
OAuth2TokenService::Consumer* consumer,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes)
: OAuth2TokenServiceRequest::Core(owner, provider),
OAuth2TokenService::Consumer("oauth2_token_service"),
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()->RunsTasksInCurrentSequence());
request_ = token_service()->StartRequest(account_id_, scopes_, this);
}
void RequestCore::StopOnTokenServiceThread() {
DCHECK(token_service_task_runner()->RunsTasksInCurrentSequence());
request_.reset();
}
void RequestCore::OnGetTokenSuccess(
const OAuth2TokenService::Request* request,
const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
DCHECK(token_service_task_runner()->RunsTasksInCurrentSequence());
DCHECK_EQ(request_.get(), request);
owning_task_runner()->PostTask(
FROM_HERE, base::BindOnce(&RequestCore::InformOwnerOnGetTokenSuccess,
this, token_response));
request_.reset();
}
void RequestCore::OnGetTokenFailure(const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
DCHECK(token_service_task_runner()->RunsTasksInCurrentSequence());
DCHECK_EQ(request_.get(), request);
owning_task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&RequestCore::InformOwnerOnGetTokenFailure, this, error));
request_.reset();
}
void RequestCore::InformOwnerOnGetTokenSuccess(
const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!IsStopped()) {
consumer_->OnGetTokenSuccess(owner(), token_response);
}
}
void RequestCore::InformOwnerOnGetTokenFailure(GoogleServiceAuthError error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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<InvalidateCore>;
// 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<OAuth2TokenServiceRequest::TokenServiceProvider>&
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()->RunsTasksInCurrentSequence());
token_service()->InvalidateAccessToken(account_id_, scopes_, access_token_);
}
void InvalidateCore::StopOnTokenServiceThread() {
DCHECK(token_service_task_runner()->RunsTasksInCurrentSequence());
// Nothing to do.
}
} // namespace
// static
std::unique_ptr<OAuth2TokenServiceRequest>
OAuth2TokenServiceRequest::CreateAndStart(
const scoped_refptr<TokenServiceProvider>& provider,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes,
OAuth2TokenService::Consumer* consumer) {
std::unique_ptr<OAuth2TokenServiceRequest> request(
new OAuth2TokenServiceRequest(account_id));
scoped_refptr<Core> core(
new RequestCore(request.get(), provider, consumer, account_id, scopes));
request->StartWithCore(core);
return request;
}
// static
void OAuth2TokenServiceRequest::InvalidateToken(
const scoped_refptr<TokenServiceProvider>& provider,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes,
const std::string& access_token) {
std::unique_ptr<OAuth2TokenServiceRequest> request(
new OAuth2TokenServiceRequest(account_id));
scoped_refptr<Core> core(new InvalidateCore(
request.get(), provider, access_token, account_id, scopes));
request->StartWithCore(core);
}
OAuth2TokenServiceRequest::~OAuth2TokenServiceRequest() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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>& core) {
DCHECK(core.get());
core_ = core;
core_->Start();
}