• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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/chromeos/settings/device_oauth2_token_service.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "base/bind.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/prefs/pref_registry_simple.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/values.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chromeos/settings/cros_settings.h"
18 #include "chrome/browser/chromeos/settings/token_encryptor.h"
19 #include "chrome/common/pref_names.h"
20 #include "chromeos/cryptohome/system_salt_getter.h"
21 #include "google_apis/gaia/gaia_constants.h"
22 #include "google_apis/gaia/gaia_urls.h"
23 #include "google_apis/gaia/google_service_auth_error.h"
24 #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
25 #include "policy/proto/device_management_backend.pb.h"
26 
27 namespace chromeos {
28 
29 struct DeviceOAuth2TokenService::PendingRequest {
PendingRequestchromeos::DeviceOAuth2TokenService::PendingRequest30   PendingRequest(const base::WeakPtr<RequestImpl>& request,
31                  const std::string& client_id,
32                  const std::string& client_secret,
33                  const ScopeSet& scopes)
34       : request(request),
35         client_id(client_id),
36         client_secret(client_secret),
37         scopes(scopes) {}
38 
39   const base::WeakPtr<RequestImpl> request;
40   const std::string client_id;
41   const std::string client_secret;
42   const ScopeSet scopes;
43 };
44 
DeviceOAuth2TokenService(net::URLRequestContextGetter * getter,PrefService * local_state)45 DeviceOAuth2TokenService::DeviceOAuth2TokenService(
46     net::URLRequestContextGetter* getter,
47     PrefService* local_state)
48     : url_request_context_getter_(getter),
49       local_state_(local_state),
50       state_(STATE_LOADING),
51       max_refresh_token_validation_retries_(3),
52       weak_ptr_factory_(this) {
53   // Pull in the system salt.
54   SystemSaltGetter::Get()->GetSystemSalt(
55       base::Bind(&DeviceOAuth2TokenService::DidGetSystemSalt,
56                  weak_ptr_factory_.GetWeakPtr()));
57 }
58 
~DeviceOAuth2TokenService()59 DeviceOAuth2TokenService::~DeviceOAuth2TokenService() {
60   FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
61   FlushTokenSaveCallbacks(false);
62 }
63 
64 // static
RegisterPrefs(PrefRegistrySimple * registry)65 void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) {
66   registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken,
67                                std::string());
68 }
69 
SetAndSaveRefreshToken(const std::string & refresh_token,const StatusCallback & result_callback)70 void DeviceOAuth2TokenService::SetAndSaveRefreshToken(
71     const std::string& refresh_token,
72     const StatusCallback& result_callback) {
73   FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
74 
75   bool waiting_for_salt = state_ == STATE_LOADING;
76   refresh_token_ = refresh_token;
77   state_ = STATE_VALIDATION_PENDING;
78   FireRefreshTokenAvailable(GetRobotAccountId());
79 
80   token_save_callbacks_.push_back(result_callback);
81   if (!waiting_for_salt) {
82     if (system_salt_.empty())
83       FlushTokenSaveCallbacks(false);
84     else
85       EncryptAndSaveToken();
86   }
87 }
88 
RefreshTokenIsAvailable(const std::string & account_id) const89 bool DeviceOAuth2TokenService::RefreshTokenIsAvailable(
90     const std::string& account_id) const {
91   switch (state_) {
92     case STATE_NO_TOKEN:
93     case STATE_TOKEN_INVALID:
94       return false;
95     case STATE_LOADING:
96     case STATE_VALIDATION_PENDING:
97     case STATE_VALIDATION_STARTED:
98     case STATE_TOKEN_VALID:
99       return account_id == GetRobotAccountId();
100   }
101 
102   NOTREACHED() << "Unhandled state " << state_;
103   return false;
104 }
105 
GetRobotAccountId() const106 std::string DeviceOAuth2TokenService::GetRobotAccountId() const {
107   std::string result;
108   CrosSettings::Get()->GetString(kServiceAccountIdentity, &result);
109   return result;
110 }
111 
OnRefreshTokenResponse(const std::string & access_token,int expires_in_seconds)112 void DeviceOAuth2TokenService::OnRefreshTokenResponse(
113     const std::string& access_token,
114     int expires_in_seconds) {
115   gaia_oauth_client_->GetTokenInfo(
116       access_token,
117       max_refresh_token_validation_retries_,
118       this);
119 }
120 
OnGetTokenInfoResponse(scoped_ptr<base::DictionaryValue> token_info)121 void DeviceOAuth2TokenService::OnGetTokenInfoResponse(
122     scoped_ptr<base::DictionaryValue> token_info) {
123   std::string gaia_robot_id;
124   token_info->GetString("email", &gaia_robot_id);
125   gaia_oauth_client_.reset();
126 
127   CheckRobotAccountId(gaia_robot_id);
128 }
129 
OnOAuthError()130 void DeviceOAuth2TokenService::OnOAuthError() {
131   gaia_oauth_client_.reset();
132   state_ = STATE_TOKEN_INVALID;
133   FlushPendingRequests(false, GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
134 }
135 
OnNetworkError(int response_code)136 void DeviceOAuth2TokenService::OnNetworkError(int response_code) {
137   gaia_oauth_client_.reset();
138 
139   // Go back to pending validation state. That'll allow a retry on subsequent
140   // token minting requests.
141   state_ = STATE_VALIDATION_PENDING;
142   FlushPendingRequests(false, GoogleServiceAuthError::CONNECTION_FAILED);
143 }
144 
GetRefreshToken(const std::string & account_id) const145 std::string DeviceOAuth2TokenService::GetRefreshToken(
146     const std::string& account_id) const {
147   switch (state_) {
148     case STATE_LOADING:
149     case STATE_NO_TOKEN:
150     case STATE_TOKEN_INVALID:
151       // This shouldn't happen: GetRefreshToken() is only called for actual
152       // token minting operations. In above states, requests are either queued
153       // or short-circuited to signal error immediately, so no actual token
154       // minting via OAuth2TokenService::FetchOAuth2Token should be triggered.
155       NOTREACHED();
156       return std::string();
157     case STATE_VALIDATION_PENDING:
158     case STATE_VALIDATION_STARTED:
159     case STATE_TOKEN_VALID:
160       return refresh_token_;
161   }
162 
163   NOTREACHED() << "Unhandled state " << state_;
164   return std::string();
165 }
166 
GetRequestContext()167 net::URLRequestContextGetter* DeviceOAuth2TokenService::GetRequestContext() {
168   return url_request_context_getter_.get();
169 }
170 
FetchOAuth2Token(RequestImpl * request,const std::string & account_id,net::URLRequestContextGetter * getter,const std::string & client_id,const std::string & client_secret,const ScopeSet & scopes)171 void DeviceOAuth2TokenService::FetchOAuth2Token(
172     RequestImpl* request,
173     const std::string& account_id,
174     net::URLRequestContextGetter* getter,
175     const std::string& client_id,
176     const std::string& client_secret,
177     const ScopeSet& scopes) {
178   switch (state_) {
179     case STATE_VALIDATION_PENDING:
180       // If this is the first request for a token, start validation.
181       StartValidation();
182       // fall through.
183     case STATE_LOADING:
184     case STATE_VALIDATION_STARTED:
185       // Add a pending request that will be satisfied once validation completes.
186       pending_requests_.push_back(new PendingRequest(
187           request->AsWeakPtr(), client_id, client_secret, scopes));
188       return;
189     case STATE_NO_TOKEN:
190       FailRequest(request, GoogleServiceAuthError::USER_NOT_SIGNED_UP);
191       return;
192     case STATE_TOKEN_INVALID:
193       FailRequest(request, GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
194       return;
195     case STATE_TOKEN_VALID:
196       // Pass through to OAuth2TokenService to satisfy the request.
197       OAuth2TokenService::FetchOAuth2Token(
198           request, account_id, getter, client_id, client_secret, scopes);
199       return;
200   }
201 
202   NOTREACHED() << "Unexpected state " << state_;
203 }
204 
CreateAccessTokenFetcher(const std::string & account_id,net::URLRequestContextGetter * getter,OAuth2AccessTokenConsumer * consumer)205 OAuth2AccessTokenFetcher* DeviceOAuth2TokenService::CreateAccessTokenFetcher(
206     const std::string& account_id,
207     net::URLRequestContextGetter* getter,
208     OAuth2AccessTokenConsumer* consumer) {
209   std::string refresh_token = GetRefreshToken(account_id);
210   DCHECK(!refresh_token.empty());
211   return new OAuth2AccessTokenFetcherImpl(consumer, getter, refresh_token);
212 }
213 
214 
DidGetSystemSalt(const std::string & system_salt)215 void DeviceOAuth2TokenService::DidGetSystemSalt(
216     const std::string& system_salt) {
217   system_salt_ = system_salt;
218 
219   // Bail out if system salt is not available.
220   if (system_salt_.empty()) {
221     LOG(ERROR) << "Failed to get system salt.";
222     FlushTokenSaveCallbacks(false);
223     state_ = STATE_NO_TOKEN;
224     FireRefreshTokensLoaded();
225     return;
226   }
227 
228   // If the token has been set meanwhile, write it to |local_state_|.
229   if (!refresh_token_.empty()) {
230     EncryptAndSaveToken();
231     FireRefreshTokensLoaded();
232     return;
233   }
234 
235   // Otherwise, load the refresh token from |local_state_|.
236   std::string encrypted_refresh_token =
237       local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken);
238   CryptohomeTokenEncryptor encryptor(system_salt_);
239   refresh_token_ = encryptor.DecryptWithSystemSalt(encrypted_refresh_token);
240   if (!encrypted_refresh_token.empty() && refresh_token_.empty()) {
241     LOG(ERROR) << "Failed to decrypt refresh token.";
242     state_ = STATE_NO_TOKEN;
243     FireRefreshTokensLoaded();
244     return;
245   }
246 
247   state_ = STATE_VALIDATION_PENDING;
248 
249   // If there are pending requests, start a validation.
250   if (!pending_requests_.empty())
251     StartValidation();
252 
253   // Announce the token.
254   FireRefreshTokenAvailable(GetRobotAccountId());
255   FireRefreshTokensLoaded();
256 }
257 
CheckRobotAccountId(const std::string & gaia_robot_id)258 void DeviceOAuth2TokenService::CheckRobotAccountId(
259     const std::string& gaia_robot_id) {
260   // Make sure the value returned by GetRobotAccountId has been validated
261   // against current device settings.
262   switch (CrosSettings::Get()->PrepareTrustedValues(
263       base::Bind(&DeviceOAuth2TokenService::CheckRobotAccountId,
264                  weak_ptr_factory_.GetWeakPtr(),
265                  gaia_robot_id))) {
266     case CrosSettingsProvider::TRUSTED:
267       // All good, compare account ids below.
268       break;
269     case CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
270       // The callback passed to PrepareTrustedValues above will trigger a
271       // re-check eventually.
272       return;
273     case CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
274       // There's no trusted account id, which is equivalent to no token present.
275       LOG(WARNING) << "Device settings permanently untrusted.";
276       state_ = STATE_NO_TOKEN;
277       FlushPendingRequests(false, GoogleServiceAuthError::USER_NOT_SIGNED_UP);
278       return;
279   }
280 
281   std::string policy_robot_id = GetRobotAccountId();
282   if (policy_robot_id == gaia_robot_id) {
283     state_ = STATE_TOKEN_VALID;
284     FlushPendingRequests(true, GoogleServiceAuthError::NONE);
285   } else {
286     if (gaia_robot_id.empty()) {
287       LOG(WARNING) << "Device service account owner in policy is empty.";
288     } else {
289       LOG(WARNING) << "Device service account owner in policy does not match "
290                    << "refresh token owner \"" << gaia_robot_id << "\".";
291     }
292     state_ = STATE_TOKEN_INVALID;
293     FlushPendingRequests(false,
294                          GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
295   }
296 }
297 
EncryptAndSaveToken()298 void DeviceOAuth2TokenService::EncryptAndSaveToken() {
299   DCHECK_NE(state_, STATE_LOADING);
300 
301   CryptohomeTokenEncryptor encryptor(system_salt_);
302   std::string encrypted_refresh_token =
303       encryptor.EncryptWithSystemSalt(refresh_token_);
304   bool result = true;
305   if (encrypted_refresh_token.empty()) {
306     LOG(ERROR) << "Failed to encrypt refresh token; save aborted.";
307     result = false;
308   } else {
309     local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken,
310                             encrypted_refresh_token);
311   }
312 
313   FlushTokenSaveCallbacks(result);
314 }
315 
StartValidation()316 void DeviceOAuth2TokenService::StartValidation() {
317   DCHECK_EQ(state_, STATE_VALIDATION_PENDING);
318   DCHECK(!gaia_oauth_client_);
319 
320   state_ = STATE_VALIDATION_STARTED;
321 
322   gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
323       g_browser_process->system_request_context()));
324 
325   GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
326   gaia::OAuthClientInfo client_info;
327   client_info.client_id = gaia_urls->oauth2_chrome_client_id();
328   client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
329 
330   gaia_oauth_client_->RefreshToken(
331       client_info,
332       refresh_token_,
333       std::vector<std::string>(1, GaiaConstants::kOAuthWrapBridgeUserInfoScope),
334       max_refresh_token_validation_retries_,
335       this);
336 }
337 
FlushPendingRequests(bool token_is_valid,GoogleServiceAuthError::State error)338 void DeviceOAuth2TokenService::FlushPendingRequests(
339     bool token_is_valid,
340     GoogleServiceAuthError::State error) {
341   std::vector<PendingRequest*> requests;
342   requests.swap(pending_requests_);
343   for (std::vector<PendingRequest*>::iterator request(requests.begin());
344        request != requests.end();
345        ++request) {
346     scoped_ptr<PendingRequest> scoped_request(*request);
347     if (!scoped_request->request)
348       continue;
349 
350     if (token_is_valid) {
351       OAuth2TokenService::FetchOAuth2Token(
352           scoped_request->request.get(),
353           scoped_request->request->GetAccountId(),
354           GetRequestContext(),
355           scoped_request->client_id,
356           scoped_request->client_secret,
357           scoped_request->scopes);
358     } else {
359       FailRequest(scoped_request->request.get(), error);
360     }
361   }
362 }
363 
FlushTokenSaveCallbacks(bool result)364 void DeviceOAuth2TokenService::FlushTokenSaveCallbacks(bool result) {
365   std::vector<StatusCallback> callbacks;
366   callbacks.swap(token_save_callbacks_);
367   for (std::vector<StatusCallback>::iterator callback(callbacks.begin());
368        callback != callbacks.end();
369        ++callback) {
370     if (!callback->is_null())
371       callback->Run(result);
372   }
373 }
374 
FailRequest(RequestImpl * request,GoogleServiceAuthError::State error)375 void DeviceOAuth2TokenService::FailRequest(
376     RequestImpl* request,
377     GoogleServiceAuthError::State error) {
378   GoogleServiceAuthError auth_error(error);
379   base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
380       &RequestImpl::InformConsumer,
381       request->AsWeakPtr(),
382       auth_error,
383       std::string(),
384       base::Time()));
385 }
386 
387 }  // namespace chromeos
388