Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / chromeos / settings / device_oauth2_token_service_delegate.cc
blobe561af8f45d8ec64fd038193583ef5b62541f838
1 // Copyright 2015 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.
5 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h"
7 #include <string>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/prefs/pref_registry_simple.h"
15 #include "base/prefs/pref_service.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"
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"
25 #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
26 #include "policy/proto/device_management_backend.pb.h"
28 namespace chromeos {
30 void DeviceOAuth2TokenServiceDelegate::OnServiceAccountIdentityChanged() {
31 if (!GetRobotAccountId().empty() && !refresh_token_.empty())
32 FireRefreshTokenAvailable(GetRobotAccountId());
35 DeviceOAuth2TokenServiceDelegate::DeviceOAuth2TokenServiceDelegate(
36 net::URLRequestContextGetter* getter,
37 PrefService* local_state)
38 : url_request_context_getter_(getter),
39 local_state_(local_state),
40 state_(STATE_LOADING),
41 max_refresh_token_validation_retries_(3),
42 validation_requested_(false),
43 validation_status_delegate_(nullptr),
44 service_account_identity_subscription_(
45 CrosSettings::Get()
46 ->AddSettingsObserver(
47 kServiceAccountIdentity,
48 base::Bind(&DeviceOAuth2TokenServiceDelegate::
49 OnServiceAccountIdentityChanged,
50 base::Unretained(this)))
51 .Pass()),
52 weak_ptr_factory_(this) {
53 // Pull in the system salt.
54 SystemSaltGetter::Get()->GetSystemSalt(
55 base::Bind(&DeviceOAuth2TokenServiceDelegate::DidGetSystemSalt,
56 weak_ptr_factory_.GetWeakPtr()));
59 DeviceOAuth2TokenServiceDelegate::~DeviceOAuth2TokenServiceDelegate() {
60 FlushTokenSaveCallbacks(false);
63 void DeviceOAuth2TokenServiceDelegate::SetAndSaveRefreshToken(
64 const std::string& refresh_token,
65 const StatusCallback& result_callback) {
66 ReportServiceError(GoogleServiceAuthError::REQUEST_CANCELED);
68 bool waiting_for_salt = state_ == STATE_LOADING;
69 refresh_token_ = refresh_token;
70 state_ = STATE_VALIDATION_PENDING;
72 // If the robot account ID is not available yet, do not announce the token. It
73 // will be done from OnServiceAccountIdentityChanged() once the robot account
74 // ID becomes available as well.
75 if (!GetRobotAccountId().empty())
76 FireRefreshTokenAvailable(GetRobotAccountId());
78 token_save_callbacks_.push_back(result_callback);
79 if (!waiting_for_salt) {
80 if (system_salt_.empty())
81 FlushTokenSaveCallbacks(false);
82 else
83 EncryptAndSaveToken();
87 bool DeviceOAuth2TokenServiceDelegate::RefreshTokenIsAvailable(
88 const std::string& account_id) const {
89 switch (state_) {
90 case STATE_NO_TOKEN:
91 case STATE_TOKEN_INVALID:
92 return false;
93 case STATE_LOADING:
94 case STATE_VALIDATION_PENDING:
95 case STATE_VALIDATION_STARTED:
96 case STATE_TOKEN_VALID:
97 return account_id == GetRobotAccountId();
100 NOTREACHED() << "Unhandled state " << state_;
101 return false;
104 std::string DeviceOAuth2TokenServiceDelegate::GetRobotAccountId() const {
105 std::string result;
106 CrosSettings::Get()->GetString(kServiceAccountIdentity, &result);
107 return result;
110 void DeviceOAuth2TokenServiceDelegate::OnRefreshTokenResponse(
111 const std::string& access_token,
112 int expires_in_seconds) {
113 gaia_oauth_client_->GetTokenInfo(access_token,
114 max_refresh_token_validation_retries_, this);
117 void DeviceOAuth2TokenServiceDelegate::OnGetTokenInfoResponse(
118 scoped_ptr<base::DictionaryValue> token_info) {
119 std::string gaia_robot_id;
120 token_info->GetString("email", &gaia_robot_id);
121 gaia_oauth_client_.reset();
123 CheckRobotAccountId(gaia_robot_id);
126 void DeviceOAuth2TokenServiceDelegate::OnOAuthError() {
127 gaia_oauth_client_.reset();
128 state_ = STATE_TOKEN_INVALID;
129 ReportServiceError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
132 void DeviceOAuth2TokenServiceDelegate::OnNetworkError(int response_code) {
133 gaia_oauth_client_.reset();
135 // Go back to pending validation state. That'll allow a retry on subsequent
136 // token minting requests.
137 state_ = STATE_VALIDATION_PENDING;
138 ReportServiceError(GoogleServiceAuthError::CONNECTION_FAILED);
141 std::string DeviceOAuth2TokenServiceDelegate::GetRefreshToken(
142 const std::string& account_id) const {
143 switch (state_) {
144 case STATE_LOADING:
145 case STATE_NO_TOKEN:
146 case STATE_TOKEN_INVALID:
147 // This shouldn't happen: GetRefreshToken() is only called for actual
148 // token minting operations. In above states, requests are either queued
149 // or short-circuited to signal error immediately, so no actual token
150 // minting via OAuth2TokenService::FetchOAuth2Token should be triggered.
151 NOTREACHED();
152 return std::string();
153 case STATE_VALIDATION_PENDING:
154 case STATE_VALIDATION_STARTED:
155 case STATE_TOKEN_VALID:
156 return refresh_token_;
159 NOTREACHED() << "Unhandled state " << state_;
160 return std::string();
163 net::URLRequestContextGetter*
164 DeviceOAuth2TokenServiceDelegate::GetRequestContext() const {
165 return url_request_context_getter_.get();
168 OAuth2AccessTokenFetcher*
169 DeviceOAuth2TokenServiceDelegate::CreateAccessTokenFetcher(
170 const std::string& account_id,
171 net::URLRequestContextGetter* getter,
172 OAuth2AccessTokenConsumer* consumer) {
173 std::string refresh_token = GetRefreshToken(account_id);
174 DCHECK(!refresh_token.empty());
175 return new OAuth2AccessTokenFetcherImpl(consumer, getter, refresh_token);
178 void DeviceOAuth2TokenServiceDelegate::DidGetSystemSalt(
179 const std::string& system_salt) {
180 system_salt_ = system_salt;
182 // Bail out if system salt is not available.
183 if (system_salt_.empty()) {
184 LOG(ERROR) << "Failed to get system salt.";
185 FlushTokenSaveCallbacks(false);
186 state_ = STATE_NO_TOKEN;
187 FireRefreshTokensLoaded();
188 return;
191 // If the token has been set meanwhile, write it to |local_state_|.
192 if (!refresh_token_.empty()) {
193 EncryptAndSaveToken();
194 FireRefreshTokensLoaded();
195 return;
198 // Otherwise, load the refresh token from |local_state_|.
199 std::string encrypted_refresh_token =
200 local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken);
201 if (!encrypted_refresh_token.empty()) {
202 CryptohomeTokenEncryptor encryptor(system_salt_);
203 refresh_token_ = encryptor.DecryptWithSystemSalt(encrypted_refresh_token);
204 if (refresh_token_.empty()) {
205 LOG(ERROR) << "Failed to decrypt refresh token.";
206 state_ = STATE_NO_TOKEN;
207 FireRefreshTokensLoaded();
208 return;
212 state_ = STATE_VALIDATION_PENDING;
214 // If there are pending requests, start a validation.
215 if (validation_requested_)
216 StartValidation();
218 // Announce the token.
219 FireRefreshTokenAvailable(GetRobotAccountId());
220 FireRefreshTokensLoaded();
223 void DeviceOAuth2TokenServiceDelegate::CheckRobotAccountId(
224 const std::string& gaia_robot_id) {
225 // Make sure the value returned by GetRobotAccountId has been validated
226 // against current device settings.
227 switch (CrosSettings::Get()->PrepareTrustedValues(
228 base::Bind(&DeviceOAuth2TokenServiceDelegate::CheckRobotAccountId,
229 weak_ptr_factory_.GetWeakPtr(), gaia_robot_id))) {
230 case CrosSettingsProvider::TRUSTED:
231 // All good, compare account ids below.
232 break;
233 case CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
234 // The callback passed to PrepareTrustedValues above will trigger a
235 // re-check eventually.
236 return;
237 case CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
238 // There's no trusted account id, which is equivalent to no token present.
239 LOG(WARNING) << "Device settings permanently untrusted.";
240 state_ = STATE_NO_TOKEN;
241 ReportServiceError(GoogleServiceAuthError::USER_NOT_SIGNED_UP);
242 return;
245 std::string policy_robot_id = GetRobotAccountId();
246 if (policy_robot_id == gaia_robot_id) {
247 state_ = STATE_TOKEN_VALID;
248 ReportServiceError(GoogleServiceAuthError::NONE);
249 } else {
250 if (gaia_robot_id.empty()) {
251 LOG(WARNING) << "Device service account owner in policy is empty.";
252 } else {
253 LOG(WARNING) << "Device service account owner in policy does not match "
254 << "refresh token owner \"" << gaia_robot_id << "\".";
256 state_ = STATE_TOKEN_INVALID;
257 ReportServiceError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
261 void DeviceOAuth2TokenServiceDelegate::EncryptAndSaveToken() {
262 DCHECK_NE(state_, STATE_LOADING);
264 CryptohomeTokenEncryptor encryptor(system_salt_);
265 std::string encrypted_refresh_token =
266 encryptor.EncryptWithSystemSalt(refresh_token_);
267 bool result = true;
268 if (encrypted_refresh_token.empty()) {
269 LOG(ERROR) << "Failed to encrypt refresh token; save aborted.";
270 result = false;
271 } else {
272 local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken,
273 encrypted_refresh_token);
276 FlushTokenSaveCallbacks(result);
279 void DeviceOAuth2TokenServiceDelegate::StartValidation() {
280 DCHECK_EQ(state_, STATE_VALIDATION_PENDING);
281 DCHECK(!gaia_oauth_client_);
283 state_ = STATE_VALIDATION_STARTED;
285 gaia_oauth_client_.reset(
286 new gaia::GaiaOAuthClient(g_browser_process->system_request_context()));
288 GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
289 gaia::OAuthClientInfo client_info;
290 client_info.client_id = gaia_urls->oauth2_chrome_client_id();
291 client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
293 gaia_oauth_client_->RefreshToken(
294 client_info, refresh_token_,
295 std::vector<std::string>(1, GaiaConstants::kOAuthWrapBridgeUserInfoScope),
296 max_refresh_token_validation_retries_, this);
299 void DeviceOAuth2TokenServiceDelegate::FlushTokenSaveCallbacks(bool result) {
300 std::vector<StatusCallback> callbacks;
301 callbacks.swap(token_save_callbacks_);
302 for (std::vector<StatusCallback>::iterator callback(callbacks.begin());
303 callback != callbacks.end(); ++callback) {
304 if (!callback->is_null())
305 callback->Run(result);
309 void DeviceOAuth2TokenServiceDelegate::RequestValidation() {
310 validation_requested_ = true;
313 void DeviceOAuth2TokenServiceDelegate::SetValidationStatusDelegate(
314 ValidationStatusDelegate* delegate) {
315 validation_status_delegate_ = delegate;
318 void DeviceOAuth2TokenServiceDelegate::ReportServiceError(
319 GoogleServiceAuthError::State error) {
320 if (validation_status_delegate_) {
321 validation_status_delegate_->OnValidationCompleted(error);
325 } // namespace chromeos