OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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/signin/oauth2_token_service.h" |
| 6 |
| 7 #include "base/callback.h" |
| 8 #include "base/time.h" |
| 9 #include "chrome/browser/profiles/profile.h" |
| 10 #include "chrome/browser/signin/oauth2_token_service_factory.h" |
| 11 #include "chrome/browser/signin/token_service.h" |
| 12 #include "chrome/browser/signin/token_service_factory.h" |
| 13 #include "chrome/common/chrome_notification_types.h" |
| 14 #include "content/public/browser/browser_thread.h" |
| 15 #include "content/public/browser/notification_details.h" |
| 16 #include "content/public/browser/notification_source.h" |
| 17 #include "google_apis/gaia/gaia_constants.h" |
| 18 #include "google_apis/gaia/gaia_urls.h" |
| 19 #include "google_apis/gaia/google_service_auth_error.h" |
| 20 #include "google_apis/gaia/oauth2_access_token_consumer.h" |
| 21 #include "google_apis/gaia/oauth2_access_token_fetcher.h" |
| 22 |
| 23 using content::BrowserThread; |
| 24 |
| 25 // Data structure used to communicate between the thread the request is made on |
| 26 // and the UI thread where the TokenService is accessible. |
| 27 struct Tokens { |
| 28 Tokens() : error(GoogleServiceAuthError::None()) {} |
| 29 GoogleServiceAuthError error; |
| 30 std::string refresh_token; |
| 31 std::string access_token; |
| 32 base::Time expiration_date; |
| 33 }; |
| 34 |
| 35 // Callback from the UI thread to the thread the request is made on. |
| 36 typedef base::Callback<void(Tokens)> RequestCallbackType; |
| 37 |
| 38 class OAuth2TokenService::RequestImpl : public CancelableRequestConsumer, |
| 39 public OAuth2TokenService::Request, |
| 40 public OAuth2AccessTokenConsumer { |
| 41 public: |
| 42 RequestImpl(Profile* profile, |
| 43 net::URLRequestContextGetter* getter, |
| 44 const std::set<std::string>& scopes, |
| 45 OAuth2AccessTokenConsumer* consumer); |
| 46 |
| 47 virtual ~RequestImpl(); |
| 48 |
| 49 // OAuth2AccessTokenConsumer implemenation; |
| 50 virtual void OnGetTokenSuccess(const std::string& access_token, |
| 51 const base::Time& expiration_date) OVERRIDE; |
| 52 virtual void OnGetTokenFailure(const GoogleServiceAuthError& error) OVERRIDE; |
| 53 |
| 54 private: |
| 55 friend class OAuth2TokenService; |
| 56 |
| 57 // This method is called on the UI thread so that it can access the profile |
| 58 // services and retrieve the tokens needed to solve or execute the request. |
| 59 static void GetTokens(CancelableRequest<RequestCallbackType>* request, |
| 60 Profile* profile, |
| 61 std::set<std::string> scopes); |
| 62 |
| 63 // Register an entry in the cache of the OAuth2TokenService associated to |
| 64 // |profile|. |
| 65 static void RegisterCacheEntry(Profile* profile, |
| 66 const std::set<std::string>& scopes, |
| 67 const std::string& access_token, |
| 68 const base::Time& expiration_date); |
| 69 |
| 70 // Callback on the request thread with the tokens needed to do the request. |
| 71 void FetchWithTokens(Tokens tokens); |
| 72 |
| 73 Profile* profile_; |
| 74 scoped_refptr<net::URLRequestContextGetter> getter_; |
| 75 std::set<std::string> scopes_; |
| 76 OAuth2AccessTokenConsumer* consumer_; |
| 77 scoped_ptr<OAuth2AccessTokenFetcher> fetcher_; |
| 78 |
| 79 DISALLOW_COPY_AND_ASSIGN(RequestImpl); |
| 80 }; |
| 81 |
| 82 OAuth2TokenService::OAuth2TokenService() { |
| 83 } |
| 84 |
| 85 OAuth2TokenService::~OAuth2TokenService() { |
| 86 } |
| 87 |
| 88 void OAuth2TokenService::Initialize(Profile* profile) { |
| 89 DCHECK(profile); |
| 90 profile_ = profile; |
| 91 getter_ = profile->GetRequestContext(); |
| 92 content::Source<TokenService> token_service_source( |
| 93 TokenServiceFactory::GetForProfile(profile)); |
| 94 registrar_.Add(this, |
| 95 chrome::NOTIFICATION_TOKENS_CLEARED, |
| 96 token_service_source); |
| 97 registrar_.Add(this, |
| 98 chrome::NOTIFICATION_TOKEN_AVAILABLE, |
| 99 token_service_source); |
| 100 registrar_.Add(this, |
| 101 chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, |
| 102 token_service_source); |
| 103 } |
| 104 |
| 105 OAuth2TokenService::Request* OAuth2TokenService::StartRequest( |
| 106 const std::vector<std::string>& scopes, |
| 107 OAuth2AccessTokenConsumer* consumer) { |
| 108 std::set<std::string> scopes_set(scopes.begin(), scopes.end()); |
| 109 RequestImpl* request = |
| 110 new RequestImpl(profile_, getter_, scopes_set, consumer); |
| 111 scoped_refptr<CancelableRequest<RequestCallbackType> > cancellable_request( |
| 112 new CancelableRequest<RequestCallbackType>( |
| 113 base::Bind(&RequestImpl::FetchWithTokens, |
| 114 base::Unretained(request)))); |
| 115 AddRequest(cancellable_request, request); |
| 116 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| 117 RequestImpl::GetTokens(cancellable_request, profile_, scopes_set); |
| 118 } else { |
| 119 BrowserThread::PostTask(BrowserThread::UI, |
| 120 FROM_HERE, |
| 121 base::Bind(&RequestImpl::GetTokens, |
| 122 cancellable_request, |
| 123 profile_, |
| 124 scopes_set)); |
| 125 } |
| 126 return request; |
| 127 } |
| 128 |
| 129 void OAuth2TokenService::ClearCache() { |
| 130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 131 tokens_cache_.clear(); |
| 132 } |
| 133 |
| 134 OAuth2TokenService::CacheEntry const* OAuth2TokenService::GetCacheEntry( |
| 135 const std::set<std::string>& scopes) { |
| 136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 137 TokensCache::iterator token_iterator = tokens_cache_.find(scopes); |
| 138 if (token_iterator == tokens_cache_.end()) |
| 139 return NULL; |
| 140 if (token_iterator->second.expiration_date <= base::Time::Now()) { |
| 141 tokens_cache_.erase(token_iterator); |
| 142 return NULL; |
| 143 } |
| 144 return &token_iterator->second; |
| 145 } |
| 146 |
| 147 void OAuth2TokenService::RegisterCacheEntry( |
| 148 const std::set<std::string>& scopes, |
| 149 const std::string& access_token, |
| 150 const base::Time& expiration_date) { |
| 151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 152 CacheEntry& token = tokens_cache_[scopes]; |
| 153 token.access_token = access_token; |
| 154 token.expiration_date = expiration_date; |
| 155 } |
| 156 |
| 157 void OAuth2TokenService::Observe(int type, |
| 158 const content::NotificationSource& source, |
| 159 const content::NotificationDetails& details) { |
| 160 DCHECK(type == chrome::NOTIFICATION_TOKENS_CLEARED || |
| 161 type == chrome::NOTIFICATION_TOKEN_AVAILABLE || |
| 162 type == chrome::NOTIFICATION_TOKEN_LOADING_FINISHED); |
| 163 if (type == chrome::NOTIFICATION_TOKEN_AVAILABLE) { |
| 164 TokenService::TokenAvailableDetails* tok_details = |
| 165 content::Details<TokenService::TokenAvailableDetails>(details).ptr(); |
| 166 if (tok_details->service() != GaiaConstants::kGaiaOAuth2LoginRefreshToken) |
| 167 return; |
| 168 } |
| 169 // The GaiaConstants::kGaiaOAuth2LoginRefreshToken token is used to create |
| 170 // OAuth2 access tokens. If this token either changes or is cleared, any |
| 171 // available tokens must be invalidated. |
| 172 ClearCache(); |
| 173 } |
| 174 |
| 175 OAuth2TokenService::RequestImpl::RequestImpl( |
| 176 Profile* profile, |
| 177 net::URLRequestContextGetter* getter, |
| 178 const std::set<std::string>& scopes, |
| 179 OAuth2AccessTokenConsumer* consumer) |
| 180 : profile_(profile), |
| 181 getter_(getter), |
| 182 scopes_(scopes), |
| 183 consumer_(consumer) { |
| 184 } |
| 185 |
| 186 OAuth2TokenService::RequestImpl::~RequestImpl() { |
| 187 } |
| 188 |
| 189 // static |
| 190 void OAuth2TokenService::RequestImpl::GetTokens( |
| 191 CancelableRequest<RequestCallbackType>* request, |
| 192 Profile* profile, |
| 193 std::set<std::string> scopes) { |
| 194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 195 if (request->canceled()) |
| 196 return; |
| 197 Tokens tokens; |
| 198 TokenService* token_service = TokenServiceFactory::GetForProfile(profile); |
| 199 if (!token_service || !token_service->HasOAuthLoginToken()) { |
| 200 tokens.error = |
| 201 GoogleServiceAuthError(GoogleServiceAuthError::USER_NOT_SIGNED_UP); |
| 202 } else { |
| 203 OAuth2TokenService* oauth2_token_service = |
| 204 OAuth2TokenServiceFactory::GetForProfile(profile); |
| 205 DCHECK(oauth2_token_service); |
| 206 CacheEntry const* cache_entry = oauth2_token_service->GetCacheEntry(scopes); |
| 207 if (cache_entry) { |
| 208 tokens.access_token = cache_entry->access_token; |
| 209 tokens.expiration_date = cache_entry->expiration_date; |
| 210 } else { |
| 211 tokens.refresh_token = token_service->GetOAuth2LoginRefreshToken(); |
| 212 } |
| 213 } |
| 214 request->ForwardResult(tokens); |
| 215 } |
| 216 |
| 217 // static |
| 218 void OAuth2TokenService::RequestImpl::RegisterCacheEntry( |
| 219 Profile* profile, |
| 220 const std::set<std::string>& scopes, |
| 221 const std::string& access_token, |
| 222 const base::Time& expiration_date) { |
| 223 OAuth2TokenService* oauth2_token_service = |
| 224 OAuth2TokenServiceFactory::GetForProfile(profile); |
| 225 if (!oauth2_token_service) |
| 226 return; |
| 227 oauth2_token_service->RegisterCacheEntry(scopes, |
| 228 access_token, |
| 229 expiration_date); |
| 230 } |
| 231 |
| 232 void OAuth2TokenService::RequestImpl::OnGetTokenSuccess( |
| 233 const std::string& access_token, |
| 234 const base::Time& expiration_date) { |
| 235 fetcher_.reset(); |
| 236 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| 237 OAuth2TokenService* oauth2_token_service = |
| 238 OAuth2TokenServiceFactory::GetForProfile(profile_); |
| 239 if (oauth2_token_service) { |
| 240 oauth2_token_service->RegisterCacheEntry(scopes_, |
| 241 access_token, |
| 242 expiration_date); |
| 243 } |
| 244 } else { |
| 245 BrowserThread::PostTask( |
| 246 BrowserThread::UI, |
| 247 FROM_HERE, |
| 248 base::Bind(&OAuth2TokenService::RequestImpl::RegisterCacheEntry, |
| 249 base::Unretained(profile_), |
| 250 scopes_, |
| 251 access_token, |
| 252 expiration_date)); |
| 253 } |
| 254 consumer_->OnGetTokenSuccess(access_token, expiration_date); |
| 255 } |
| 256 |
| 257 void OAuth2TokenService::RequestImpl::OnGetTokenFailure( |
| 258 const GoogleServiceAuthError& error) { |
| 259 fetcher_.reset(); |
| 260 consumer_->OnGetTokenFailure(error); |
| 261 } |
| 262 |
| 263 void OAuth2TokenService::RequestImpl::FetchWithTokens(Tokens tokens) { |
| 264 if (tokens.error.state() != GoogleServiceAuthError::NONE) { |
| 265 consumer_->OnGetTokenFailure(tokens.error); |
| 266 return; |
| 267 } |
| 268 if (tokens.access_token.length()) { |
| 269 consumer_->OnGetTokenSuccess(tokens.access_token, tokens.expiration_date); |
| 270 return; |
| 271 } |
| 272 DCHECK(tokens.refresh_token.length()); |
| 273 fetcher_.reset(new OAuth2AccessTokenFetcher(this, getter_)); |
| 274 fetcher_->Start(GaiaUrls::GetInstance()->oauth2_chrome_client_id(), |
| 275 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), |
| 276 tokens.refresh_token, |
| 277 std::vector<std::string>(scopes_.begin(), scopes_.end())); |
| 278 } |
OLD | NEW |