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 "chrome/browser/sync/profile_sync_auth_provider.h"
6
7#include "base/bind.h"
8#include "base/location.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "base/single_thread_task_runner.h"
11#include "components/signin/core/browser/profile_oauth2_token_service.h"
12#include "google_apis/gaia/gaia_constants.h"
13
14// This is thin proxy class for forwarding calls between sync and UI threads.
15// The main purpose is to hide the fact that ProfileSyncAuthProvider and
16// SyncThreadProxy have independent lifetimes. If ProfileSyncAuthProvider is
17// destroyed first calls to RequestAccessToken will never complete. This is fine
18// since sync thread is not blocked and is in the process of shutdown anyway.
19class ProfileSyncAuthProvider::SyncThreadProxy
20    : public syncer::SyncAuthProvider,
21      public base::NonThreadSafe {
22 public:
23  SyncThreadProxy(
24      base::WeakPtr<ProfileSyncAuthProvider> provider_impl,
25      scoped_refptr<base::SingleThreadTaskRunner> provider_task_runner);
26
27  // syncer::SyncAuthProvider implementation.
28  virtual void RequestAccessToken(
29      const RequestTokenCallback& callback) OVERRIDE;
30  virtual void InvalidateAccessToken(const std::string& token) OVERRIDE;
31
32 private:
33  base::WeakPtr<ProfileSyncAuthProvider> provider_impl_;
34  scoped_refptr<base::SingleThreadTaskRunner> provider_task_runner_;
35
36  DISALLOW_COPY_AND_ASSIGN(SyncThreadProxy);
37};
38
39ProfileSyncAuthProvider::SyncThreadProxy::SyncThreadProxy(
40    base::WeakPtr<ProfileSyncAuthProvider> provider_impl,
41    scoped_refptr<base::SingleThreadTaskRunner> provider_task_runner)
42    : provider_impl_(provider_impl),
43      provider_task_runner_(provider_task_runner) {
44  // SyncThreadProxy is created on UI thread but used on sync thread.
45  // Detach NonThreadSafe from UI thread so that it can reattach to sync thread
46  // on first invocation.
47  DetachFromThread();
48}
49
50void ProfileSyncAuthProvider::SyncThreadProxy::RequestAccessToken(
51    const RequestTokenCallback& callback) {
52  DCHECK(CalledOnValidThread());
53  provider_task_runner_->PostTask(
54      FROM_HERE,
55      base::Bind(&ProfileSyncAuthProvider::RequestAccessToken,
56                 provider_impl_,
57                 callback,
58                 base::MessageLoopProxy::current()));
59}
60
61void ProfileSyncAuthProvider::SyncThreadProxy::InvalidateAccessToken(
62    const std::string& token) {
63  DCHECK(CalledOnValidThread());
64  provider_task_runner_->PostTask(
65      FROM_HERE,
66      base::Bind(&ProfileSyncAuthProvider::InvalidateAccessToken,
67                 provider_impl_,
68                 token));
69}
70
71ProfileSyncAuthProvider::ProfileSyncAuthProvider(
72    ProfileOAuth2TokenService* token_service,
73    const std::string& account_id,
74    const std::string& scope)
75    : OAuth2TokenService::Consumer("sync_auth_provider"),
76      token_service_(token_service),
77      account_id_(account_id),
78      weak_factory_(this) {
79  oauth2_scope_.insert(scope);
80}
81
82ProfileSyncAuthProvider::~ProfileSyncAuthProvider() {
83  DCHECK(CalledOnValidThread());
84}
85
86void ProfileSyncAuthProvider::RequestAccessToken(
87    const syncer::SyncAuthProvider::RequestTokenCallback& callback,
88    scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
89  DCHECK(CalledOnValidThread());
90  if (access_token_request_ != NULL) {
91    // If there is already pending request report it as cancelled.
92    GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
93    RespondToTokenRequest(error, std::string());
94  }
95  request_token_callback_ = callback;
96  callback_task_runner_ = task_runner;
97  access_token_request_ =
98      token_service_->StartRequest(account_id_, oauth2_scope_, this);
99}
100
101void ProfileSyncAuthProvider::OnGetTokenSuccess(
102    const OAuth2TokenService::Request* request,
103    const std::string& access_token,
104    const base::Time& expiration_time) {
105  DCHECK_EQ(access_token_request_, request);
106  RespondToTokenRequest(GoogleServiceAuthError::AuthErrorNone(), access_token);
107}
108
109void ProfileSyncAuthProvider::OnGetTokenFailure(
110    const OAuth2TokenService::Request* request,
111    const GoogleServiceAuthError& error) {
112  DCHECK_EQ(access_token_request_, request);
113  RespondToTokenRequest(error, std::string());
114}
115
116void ProfileSyncAuthProvider::RespondToTokenRequest(
117    const GoogleServiceAuthError& error,
118    const std::string& token) {
119  DCHECK(CalledOnValidThread());
120  callback_task_runner_->PostTask(
121      FROM_HERE, base::Bind(request_token_callback_, error, token));
122  access_token_request_.reset();
123  request_token_callback_.Reset();
124  callback_task_runner_ = NULL;
125}
126
127void ProfileSyncAuthProvider::InvalidateAccessToken(const std::string& token) {
128  DCHECK(CalledOnValidThread());
129  token_service_->InvalidateToken(account_id_, oauth2_scope_, token);
130}
131
132scoped_ptr<syncer::SyncAuthProvider>
133ProfileSyncAuthProvider::CreateProviderForSyncThread() {
134  DCHECK(CalledOnValidThread());
135  scoped_ptr<syncer::SyncAuthProvider> auth_provider(new SyncThreadProxy(
136      weak_factory_.GetWeakPtr(), base::MessageLoopProxy::current()));
137  return auth_provider.Pass();
138}
139