1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "google_apis/gaia/oauth2_token_service_request.h"
6
7#include "base/bind.h"
8#include "base/memory/ref_counted.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/single_thread_task_runner.h"
11#include "base/thread_task_runner_handle.h"
12#include "google_apis/gaia/google_service_auth_error.h"
13#include "google_apis/gaia/oauth2_access_token_consumer.h"
14
15OAuth2TokenServiceRequest::TokenServiceProvider::TokenServiceProvider() {
16}
17
18OAuth2TokenServiceRequest::TokenServiceProvider::~TokenServiceProvider() {
19}
20
21// Core serves as the base class for OAuth2TokenService operations.  Each
22// operation should be modeled as a derived type.
23//
24// Core is used like this:
25//
26// 1. Constructed on owner thread.
27//
28// 2. Start() is called on owner thread, which calls StartOnTokenServiceThread()
29// on token service thread.
30//
31// 3. Request is executed.
32//
33// 4. Stop() is called on owner thread, which calls StopOnTokenServiceThread()
34// on token service thread.
35//
36// 5. Core is destroyed on owner thread.
37class OAuth2TokenServiceRequest::Core
38    : public base::NonThreadSafe,
39      public base::RefCountedThreadSafe<OAuth2TokenServiceRequest::Core> {
40 public:
41  // Note the thread where an instance of Core is constructed is referred to as
42  // the "owner thread" here.
43  Core(OAuth2TokenServiceRequest* owner,
44       const scoped_refptr<TokenServiceProvider>& provider);
45
46  // Starts the core.  Must be called on the owner thread.
47  void Start();
48
49  // Stops the core.  Must be called on the owner thread.
50  void Stop();
51
52  // Returns true if this object has been stopped.  Must be called on the owner
53  // thread.
54  bool IsStopped() const;
55
56 protected:
57  // Core must be destroyed on the owner thread.  If data members must be
58  // cleaned up or destroyed on the token service thread, do so in the
59  // StopOnTokenServiceThread method.
60  virtual ~Core();
61
62  // Called on the token service thread.
63  virtual void StartOnTokenServiceThread() = 0;
64
65  // Called on the token service thread.
66  virtual void StopOnTokenServiceThread() = 0;
67
68  base::SingleThreadTaskRunner* token_service_task_runner();
69  OAuth2TokenService* token_service();
70  OAuth2TokenServiceRequest* owner();
71
72 private:
73  friend class base::RefCountedThreadSafe<OAuth2TokenServiceRequest::Core>;
74
75  void DoNothing();
76
77  scoped_refptr<base::SingleThreadTaskRunner> token_service_task_runner_;
78  OAuth2TokenServiceRequest* owner_;
79
80  // Clear on owner thread.  OAuth2TokenServiceRequest promises to clear its
81  // last reference to TokenServiceProvider on the owner thread so the caller
82  // can ensure it is destroyed on the owner thread if desired.
83  scoped_refptr<TokenServiceProvider> provider_;
84
85  DISALLOW_COPY_AND_ASSIGN(Core);
86};
87
88OAuth2TokenServiceRequest::Core::Core(
89    OAuth2TokenServiceRequest* owner,
90    const scoped_refptr<TokenServiceProvider>& provider)
91    : owner_(owner), provider_(provider) {
92  DCHECK(owner_);
93  DCHECK(provider_.get());
94  token_service_task_runner_ = provider_->GetTokenServiceTaskRunner();
95  DCHECK(token_service_task_runner_.get());
96}
97
98OAuth2TokenServiceRequest::Core::~Core() {
99}
100
101void OAuth2TokenServiceRequest::Core::Start() {
102  DCHECK(CalledOnValidThread());
103  token_service_task_runner_->PostTask(
104      FROM_HERE,
105      base::Bind(&OAuth2TokenServiceRequest::Core::StartOnTokenServiceThread,
106                 this));
107}
108
109void OAuth2TokenServiceRequest::Core::Stop() {
110  DCHECK(CalledOnValidThread());
111  DCHECK(!IsStopped());
112
113  // Detaches |owner_| from this instance so |owner_| will be called back only
114  // if |Stop()| has never been called.
115  owner_ = NULL;
116
117  // We are stopping and will likely be destroyed soon.  Use a reply closure
118  // (DoNothing) to retain "this" and ensure we are destroyed in the owner
119  // thread, not the task runner thread.  PostTaskAndReply guarantees that the
120  // reply closure will execute after StopOnTokenServiceThread has completed.
121  token_service_task_runner_->PostTaskAndReply(
122      FROM_HERE,
123      base::Bind(&OAuth2TokenServiceRequest::Core::StopOnTokenServiceThread,
124                 this),
125      base::Bind(&OAuth2TokenServiceRequest::Core::DoNothing, this));
126}
127
128bool OAuth2TokenServiceRequest::Core::IsStopped() const {
129  DCHECK(CalledOnValidThread());
130  return owner_ == NULL;
131}
132
133base::SingleThreadTaskRunner*
134OAuth2TokenServiceRequest::Core::token_service_task_runner() {
135  return token_service_task_runner_.get();
136}
137
138OAuth2TokenService* OAuth2TokenServiceRequest::Core::token_service() {
139  DCHECK(token_service_task_runner_->BelongsToCurrentThread());
140  return provider_->GetTokenService();
141}
142
143OAuth2TokenServiceRequest* OAuth2TokenServiceRequest::Core::owner() {
144  DCHECK(CalledOnValidThread());
145  return owner_;
146}
147
148void OAuth2TokenServiceRequest::Core::DoNothing() {
149  DCHECK(CalledOnValidThread());
150}
151
152namespace {
153
154// An implementation of Core for getting an access token.
155class RequestCore : public OAuth2TokenServiceRequest::Core,
156                    public OAuth2TokenService::Consumer {
157 public:
158  RequestCore(OAuth2TokenServiceRequest* owner,
159              const scoped_refptr<
160                  OAuth2TokenServiceRequest::TokenServiceProvider>& provider,
161              OAuth2TokenService::Consumer* consumer,
162              const std::string& account_id,
163              const OAuth2TokenService::ScopeSet& scopes);
164
165  // OAuth2TokenService::Consumer.  Must be called on the token service thread.
166  virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
167                                 const std::string& access_token,
168                                 const base::Time& expiration_time) OVERRIDE;
169  virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
170                                 const GoogleServiceAuthError& error) OVERRIDE;
171
172 private:
173  friend class base::RefCountedThreadSafe<RequestCore>;
174
175  // Must be destroyed on the owner thread.
176  virtual ~RequestCore();
177
178  // Core implementation.
179  virtual void StartOnTokenServiceThread() OVERRIDE;
180  virtual void StopOnTokenServiceThread() OVERRIDE;
181
182  void InformOwnerOnGetTokenSuccess(std::string access_token,
183                                    base::Time expiration_time);
184  void InformOwnerOnGetTokenFailure(GoogleServiceAuthError error);
185
186  scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner_;
187  OAuth2TokenService::Consumer* const consumer_;
188  std::string account_id_;
189  OAuth2TokenService::ScopeSet scopes_;
190
191  // OAuth2TokenService request for fetching OAuth2 access token; it should be
192  // created, reset and accessed only on the token service thread.
193  scoped_ptr<OAuth2TokenService::Request> request_;
194
195  DISALLOW_COPY_AND_ASSIGN(RequestCore);
196};
197
198RequestCore::RequestCore(
199    OAuth2TokenServiceRequest* owner,
200    const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
201        provider,
202    OAuth2TokenService::Consumer* consumer,
203    const std::string& account_id,
204    const OAuth2TokenService::ScopeSet& scopes)
205    : OAuth2TokenServiceRequest::Core(owner, provider),
206      OAuth2TokenService::Consumer("oauth2_token_service"),
207      owner_task_runner_(base::ThreadTaskRunnerHandle::Get()),
208      consumer_(consumer),
209      account_id_(account_id),
210      scopes_(scopes) {
211  DCHECK(consumer_);
212  DCHECK(!account_id_.empty());
213  DCHECK(!scopes_.empty());
214}
215
216RequestCore::~RequestCore() {
217}
218
219void RequestCore::StartOnTokenServiceThread() {
220  DCHECK(token_service_task_runner()->BelongsToCurrentThread());
221  request_ = token_service()->StartRequest(account_id_, scopes_, this).Pass();
222}
223
224void RequestCore::StopOnTokenServiceThread() {
225  DCHECK(token_service_task_runner()->BelongsToCurrentThread());
226  request_.reset();
227}
228
229void RequestCore::OnGetTokenSuccess(const OAuth2TokenService::Request* request,
230                                    const std::string& access_token,
231                                    const base::Time& expiration_time) {
232  DCHECK(token_service_task_runner()->BelongsToCurrentThread());
233  DCHECK_EQ(request_.get(), request);
234  owner_task_runner_->PostTask(
235      FROM_HERE,
236      base::Bind(&RequestCore::InformOwnerOnGetTokenSuccess,
237                 this,
238                 access_token,
239                 expiration_time));
240  request_.reset();
241}
242
243void RequestCore::OnGetTokenFailure(const OAuth2TokenService::Request* request,
244                                    const GoogleServiceAuthError& error) {
245  DCHECK(token_service_task_runner()->BelongsToCurrentThread());
246  DCHECK_EQ(request_.get(), request);
247  owner_task_runner_->PostTask(
248      FROM_HERE,
249      base::Bind(&RequestCore::InformOwnerOnGetTokenFailure, this, error));
250  request_.reset();
251}
252
253void RequestCore::InformOwnerOnGetTokenSuccess(std::string access_token,
254                                               base::Time expiration_time) {
255  DCHECK(CalledOnValidThread());
256  if (!IsStopped()) {
257    consumer_->OnGetTokenSuccess(owner(), access_token, expiration_time);
258  }
259}
260
261void RequestCore::InformOwnerOnGetTokenFailure(GoogleServiceAuthError error) {
262  DCHECK(CalledOnValidThread());
263  if (!IsStopped()) {
264    consumer_->OnGetTokenFailure(owner(), error);
265  }
266}
267
268// An implementation of Core for invalidating an access token.
269class InvalidateCore : public OAuth2TokenServiceRequest::Core {
270 public:
271  InvalidateCore(OAuth2TokenServiceRequest* owner,
272                 const scoped_refptr<
273                     OAuth2TokenServiceRequest::TokenServiceProvider>& provider,
274                 const std::string& access_token,
275                 const std::string& account_id,
276                 const OAuth2TokenService::ScopeSet& scopes);
277
278 private:
279  friend class base::RefCountedThreadSafe<InvalidateCore>;
280
281  // Must be destroyed on the owner thread.
282  virtual ~InvalidateCore();
283
284  // Core implementation.
285  virtual void StartOnTokenServiceThread() OVERRIDE;
286  virtual void StopOnTokenServiceThread() OVERRIDE;
287
288  std::string access_token_;
289  std::string account_id_;
290  OAuth2TokenService::ScopeSet scopes_;
291
292  DISALLOW_COPY_AND_ASSIGN(InvalidateCore);
293};
294
295InvalidateCore::InvalidateCore(
296    OAuth2TokenServiceRequest* owner,
297    const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
298        provider,
299    const std::string& access_token,
300    const std::string& account_id,
301    const OAuth2TokenService::ScopeSet& scopes)
302    : OAuth2TokenServiceRequest::Core(owner, provider),
303      access_token_(access_token),
304      account_id_(account_id),
305      scopes_(scopes) {
306  DCHECK(!access_token_.empty());
307  DCHECK(!account_id_.empty());
308  DCHECK(!scopes.empty());
309}
310
311InvalidateCore::~InvalidateCore() {
312}
313
314void InvalidateCore::StartOnTokenServiceThread() {
315  DCHECK(token_service_task_runner()->BelongsToCurrentThread());
316  token_service()->InvalidateToken(account_id_, scopes_, access_token_);
317}
318
319void InvalidateCore::StopOnTokenServiceThread() {
320  DCHECK(token_service_task_runner()->BelongsToCurrentThread());
321  // Nothing to do.
322}
323
324}  // namespace
325
326// static
327scoped_ptr<OAuth2TokenServiceRequest> OAuth2TokenServiceRequest::CreateAndStart(
328    const scoped_refptr<TokenServiceProvider>& provider,
329    const std::string& account_id,
330    const OAuth2TokenService::ScopeSet& scopes,
331    OAuth2TokenService::Consumer* consumer) {
332  scoped_ptr<OAuth2TokenServiceRequest> request(
333      new OAuth2TokenServiceRequest(account_id));
334  scoped_refptr<Core> core(
335      new RequestCore(request.get(), provider, consumer, account_id, scopes));
336  request->StartWithCore(core);
337  return request.Pass();
338}
339
340// static
341void OAuth2TokenServiceRequest::InvalidateToken(
342    const scoped_refptr<TokenServiceProvider>& provider,
343    const std::string& account_id,
344    const OAuth2TokenService::ScopeSet& scopes,
345    const std::string& access_token) {
346  scoped_ptr<OAuth2TokenServiceRequest> request(
347      new OAuth2TokenServiceRequest(account_id));
348  scoped_refptr<Core> core(new InvalidateCore(
349      request.get(), provider, access_token, account_id, scopes));
350  request->StartWithCore(core);
351}
352
353OAuth2TokenServiceRequest::~OAuth2TokenServiceRequest() {
354  core_->Stop();
355}
356
357std::string OAuth2TokenServiceRequest::GetAccountId() const {
358  return account_id_;
359}
360
361OAuth2TokenServiceRequest::OAuth2TokenServiceRequest(
362    const std::string& account_id)
363    : account_id_(account_id) {
364  DCHECK(!account_id_.empty());
365}
366
367void OAuth2TokenServiceRequest::StartWithCore(const scoped_refptr<Core>& core) {
368  DCHECK(core.get());
369  core_ = core;
370  core_->Start();
371}
372