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
TokenServiceProvider()15 OAuth2TokenServiceRequest::TokenServiceProvider::TokenServiceProvider() {
16 }
17
~TokenServiceProvider()18 OAuth2TokenServiceRequest::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.
37 class 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
Core(OAuth2TokenServiceRequest * owner,const scoped_refptr<TokenServiceProvider> & provider)88 OAuth2TokenServiceRequest::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
~Core()98 OAuth2TokenServiceRequest::Core::~Core() {
99 }
100
Start()101 void 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
Stop()109 void 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
IsStopped() const128 bool OAuth2TokenServiceRequest::Core::IsStopped() const {
129 DCHECK(CalledOnValidThread());
130 return owner_ == NULL;
131 }
132
133 base::SingleThreadTaskRunner*
token_service_task_runner()134 OAuth2TokenServiceRequest::Core::token_service_task_runner() {
135 return token_service_task_runner_.get();
136 }
137
token_service()138 OAuth2TokenService* OAuth2TokenServiceRequest::Core::token_service() {
139 DCHECK(token_service_task_runner_->BelongsToCurrentThread());
140 return provider_->GetTokenService();
141 }
142
owner()143 OAuth2TokenServiceRequest* OAuth2TokenServiceRequest::Core::owner() {
144 DCHECK(CalledOnValidThread());
145 return owner_;
146 }
147
DoNothing()148 void OAuth2TokenServiceRequest::Core::DoNothing() {
149 DCHECK(CalledOnValidThread());
150 }
151
152 namespace {
153
154 // An implementation of Core for getting an access token.
155 class 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
RequestCore(OAuth2TokenServiceRequest * owner,const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider> & provider,OAuth2TokenService::Consumer * consumer,const std::string & account_id,const OAuth2TokenService::ScopeSet & scopes)198 RequestCore::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
~RequestCore()216 RequestCore::~RequestCore() {
217 }
218
StartOnTokenServiceThread()219 void RequestCore::StartOnTokenServiceThread() {
220 DCHECK(token_service_task_runner()->BelongsToCurrentThread());
221 request_ = token_service()->StartRequest(account_id_, scopes_, this).Pass();
222 }
223
StopOnTokenServiceThread()224 void RequestCore::StopOnTokenServiceThread() {
225 DCHECK(token_service_task_runner()->BelongsToCurrentThread());
226 request_.reset();
227 }
228
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)229 void 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
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)243 void 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
InformOwnerOnGetTokenSuccess(std::string access_token,base::Time expiration_time)253 void 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
InformOwnerOnGetTokenFailure(GoogleServiceAuthError error)261 void 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.
269 class 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
InvalidateCore(OAuth2TokenServiceRequest * owner,const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider> & provider,const std::string & access_token,const std::string & account_id,const OAuth2TokenService::ScopeSet & scopes)295 InvalidateCore::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
~InvalidateCore()311 InvalidateCore::~InvalidateCore() {
312 }
313
StartOnTokenServiceThread()314 void InvalidateCore::StartOnTokenServiceThread() {
315 DCHECK(token_service_task_runner()->BelongsToCurrentThread());
316 token_service()->InvalidateToken(account_id_, scopes_, access_token_);
317 }
318
StopOnTokenServiceThread()319 void InvalidateCore::StopOnTokenServiceThread() {
320 DCHECK(token_service_task_runner()->BelongsToCurrentThread());
321 // Nothing to do.
322 }
323
324 } // namespace
325
326 // static
CreateAndStart(const scoped_refptr<TokenServiceProvider> & provider,const std::string & account_id,const OAuth2TokenService::ScopeSet & scopes,OAuth2TokenService::Consumer * consumer)327 scoped_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
InvalidateToken(const scoped_refptr<TokenServiceProvider> & provider,const std::string & account_id,const OAuth2TokenService::ScopeSet & scopes,const std::string & access_token)341 void 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
~OAuth2TokenServiceRequest()353 OAuth2TokenServiceRequest::~OAuth2TokenServiceRequest() {
354 core_->Stop();
355 }
356
GetAccountId() const357 std::string OAuth2TokenServiceRequest::GetAccountId() const {
358 return account_id_;
359 }
360
OAuth2TokenServiceRequest(const std::string & account_id)361 OAuth2TokenServiceRequest::OAuth2TokenServiceRequest(
362 const std::string& account_id)
363 : account_id_(account_id) {
364 DCHECK(!account_id_.empty());
365 }
366
StartWithCore(const scoped_refptr<Core> & core)367 void OAuth2TokenServiceRequest::StartWithCore(const scoped_refptr<Core>& core) {
368 DCHECK(core.get());
369 core_ = core;
370 core_->Start();
371 }
372