Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(94)

Side by Side Diff: chrome/browser/chromeos/settings/device_oauth2_token_service.cc

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

Powered by Google App Engine
This is Rietveld 408576698