| OLD | NEW |
| 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> |
| 8 #include <vector> |
| 9 |
| 7 #include "base/prefs/pref_registry_simple.h" | 10 #include "base/prefs/pref_registry_simple.h" |
| 8 #include "base/prefs/pref_service.h" | 11 #include "base/prefs/pref_service.h" |
| 12 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" |
| 14 #include "chrome/browser/policy/browser_policy_connector.h" |
| 15 #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" |
| 9 #include "chrome/common/pref_names.h" | 16 #include "chrome/common/pref_names.h" |
| 10 #include "chromeos/cryptohome/cryptohome_library.h" | 17 #include "chromeos/cryptohome/cryptohome_library.h" |
| 11 #include "content/public/browser/browser_thread.h" | 18 #include "content/public/browser/browser_thread.h" |
| 19 #include "google_apis/gaia/gaia_urls.h" |
| 20 #include "google_apis/gaia/google_service_auth_error.h" |
| 12 | 21 |
| 13 namespace chromeos { | 22 namespace chromeos { |
| 14 | 23 |
| 24 // A wrapper for the consumer passed to StartRequest, which doesn't call |
| 25 // through to the target Consumer unless the refresh token validation is |
| 26 // complete. |
| 27 class DeviceOAuth2TokenService::ValidatingConsumer |
| 28 : public OAuth2TokenService::Consumer, |
| 29 public gaia::GaiaOAuthClient::Delegate { |
| 30 public: |
| 31 explicit ValidatingConsumer(DeviceOAuth2TokenService* token_service, |
| 32 Consumer* consumer); |
| 33 virtual ~ValidatingConsumer(); |
| 34 |
| 35 void StartValidation(); |
| 36 |
| 37 // OAuth2TokenService::Consumer |
| 38 virtual void OnGetTokenSuccess( |
| 39 const Request* request, |
| 40 const std::string& access_token, |
| 41 const base::Time& expiration_time) OVERRIDE; |
| 42 virtual void OnGetTokenFailure( |
| 43 const Request* request, |
| 44 const GoogleServiceAuthError& error) OVERRIDE; |
| 45 |
| 46 // gaia::GaiaOAuthClient::Delegate implementation. |
| 47 virtual void OnRefreshTokenResponse(const std::string& access_token, |
| 48 int expires_in_seconds) OVERRIDE; |
| 49 virtual void OnGetTokenInfoResponse(scoped_ptr<DictionaryValue> token_info) |
| 50 OVERRIDE; |
| 51 virtual void OnOAuthError() OVERRIDE; |
| 52 virtual void OnNetworkError(int response_code) OVERRIDE; |
| 53 |
| 54 private: |
| 55 void RefreshTokenIsValid(bool is_valid); |
| 56 void InformConsumer(); |
| 57 |
| 58 DeviceOAuth2TokenService* token_service_; |
| 59 Consumer* consumer_; |
| 60 scoped_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_; |
| 61 |
| 62 // We don't know which will complete first: the validation or the token |
| 63 // minting. So, we need to cache the results so the final callback can |
| 64 // take action. |
| 65 |
| 66 // RefreshTokenValidationConsumer results |
| 67 bool token_validation_done_; |
| 68 bool token_is_valid_; |
| 69 |
| 70 // OAuth2TokenService::Consumer results |
| 71 const Request* request_; |
| 72 std::string access_token_; |
| 73 base::Time expiration_time_; |
| 74 scoped_ptr<GoogleServiceAuthError> error_; |
| 75 }; |
| 76 |
| 77 DeviceOAuth2TokenService::ValidatingConsumer::ValidatingConsumer( |
| 78 DeviceOAuth2TokenService* token_service, |
| 79 Consumer* consumer) |
| 80 : token_service_(token_service), |
| 81 consumer_(consumer), |
| 82 token_validation_done_(false), |
| 83 token_is_valid_(false), |
| 84 request_(NULL) { |
| 85 } |
| 86 |
| 87 DeviceOAuth2TokenService::ValidatingConsumer::~ValidatingConsumer() { |
| 88 } |
| 89 |
| 90 void DeviceOAuth2TokenService::ValidatingConsumer::StartValidation() { |
| 91 DCHECK(!gaia_oauth_client_); |
| 92 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient( |
| 93 g_browser_process->system_request_context())); |
| 94 |
| 95 GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); |
| 96 gaia::OAuthClientInfo client_info; |
| 97 client_info.client_id = gaia_urls->oauth2_chrome_client_id(); |
| 98 client_info.client_secret = gaia_urls->oauth2_chrome_client_secret(); |
| 99 |
| 100 gaia_oauth_client_->RefreshToken( |
| 101 client_info, |
| 102 token_service_->GetRefreshToken(), |
| 103 std::vector<std::string>(1, gaia_urls->oauth2_token_info_url()), |
| 104 token_service_->max_refresh_token_validation_retries_, |
| 105 this); |
| 106 } |
| 107 |
| 108 void DeviceOAuth2TokenService::ValidatingConsumer::OnRefreshTokenResponse( |
| 109 const std::string& access_token, |
| 110 int expires_in_seconds) { |
| 111 gaia_oauth_client_->GetTokenInfo( |
| 112 access_token, |
| 113 token_service_->max_refresh_token_validation_retries_, |
| 114 this); |
| 115 } |
| 116 |
| 117 void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenInfoResponse( |
| 118 scoped_ptr<DictionaryValue> token_info) { |
| 119 std::string gaia_robot_id; |
| 120 token_info->GetString("issued_to", &gaia_robot_id); |
| 121 |
| 122 std::string policy_robot_id = token_service_->GetRobotAccountId(); |
| 123 |
| 124 if (policy_robot_id == gaia_robot_id) { |
| 125 RefreshTokenIsValid(true); |
| 126 } else { |
| 127 if (gaia_robot_id.empty()) { |
| 128 LOG(WARNING) << "Device service account owner in policy is empty."; |
| 129 } else { |
| 130 LOG(INFO) << "Device service account owner in policy does not match " |
| 131 << "refresh token."; |
| 132 } |
| 133 RefreshTokenIsValid(false); |
| 134 } |
| 135 } |
| 136 |
| 137 void DeviceOAuth2TokenService::ValidatingConsumer::OnOAuthError() { |
| 138 RefreshTokenIsValid(false); |
| 139 } |
| 140 |
| 141 void DeviceOAuth2TokenService::ValidatingConsumer::OnNetworkError( |
| 142 int response_code) { |
| 143 RefreshTokenIsValid(false); |
| 144 } |
| 145 |
| 146 void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenSuccess( |
| 147 const Request* request, |
| 148 const std::string& access_token, |
| 149 const base::Time& expiration_time) { |
| 150 request_ = request; |
| 151 access_token_ = access_token; |
| 152 expiration_time_ = expiration_time; |
| 153 if (token_validation_done_) |
| 154 InformConsumer(); |
| 155 } |
| 156 |
| 157 void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenFailure( |
| 158 const Request* request, |
| 159 const GoogleServiceAuthError& error) { |
| 160 request_ = request; |
| 161 error_.reset(new GoogleServiceAuthError(error.state())); |
| 162 if (token_validation_done_) |
| 163 InformConsumer(); |
| 164 } |
| 165 |
| 166 void DeviceOAuth2TokenService::ValidatingConsumer::RefreshTokenIsValid( |
| 167 bool is_valid) { |
| 168 token_validation_done_ = true; |
| 169 token_is_valid_ = is_valid; |
| 170 // If we have a request pointer, then the minting is complete. |
| 171 if (request_) |
| 172 InformConsumer(); |
| 173 } |
| 174 |
| 175 void DeviceOAuth2TokenService::ValidatingConsumer::InformConsumer() { |
| 176 DCHECK(request_); |
| 177 DCHECK(token_validation_done_); |
| 178 if (!token_is_valid_) { |
| 179 consumer_->OnGetTokenFailure(request_, GoogleServiceAuthError( |
| 180 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); |
| 181 } else if (error_) { |
| 182 consumer_->OnGetTokenFailure(request_, *error_.get()); |
| 183 } else { |
| 184 consumer_->OnGetTokenSuccess(request_, access_token_, expiration_time_); |
| 185 } |
| 186 token_service_->OnValidationComplete(this, token_is_valid_); |
| 187 } |
| 188 |
| 15 DeviceOAuth2TokenService::DeviceOAuth2TokenService( | 189 DeviceOAuth2TokenService::DeviceOAuth2TokenService( |
| 16 net::URLRequestContextGetter* getter, | 190 net::URLRequestContextGetter* getter, |
| 17 PrefService* local_state) | 191 PrefService* local_state) |
| 18 : OAuth2TokenService(getter), | 192 : OAuth2TokenService(getter), |
| 193 refresh_token_is_valid_(false), |
| 194 max_refresh_token_validation_retries_(3), |
| 195 pending_validators_(new std::set<ValidatingConsumer*>()), |
| 19 local_state_(local_state) { | 196 local_state_(local_state) { |
| 20 } | 197 } |
| 21 | 198 |
| 22 DeviceOAuth2TokenService::~DeviceOAuth2TokenService() { | 199 DeviceOAuth2TokenService::~DeviceOAuth2TokenService() { |
| 200 STLDeleteElements(pending_validators_.get()); |
| 201 } |
| 202 |
| 203 // TODO(davidroche): if the caller deletes the returned Request while |
| 204 // the fetches are in-flight, the OAuth2TokenService class won't call |
| 205 // back into the ValidatingConsumer and we'll end up with stale values |
| 206 // in pending_validators_ until this object is deleted. Probably not a |
| 207 // big deal, but it should be resolved by returning a Request that this |
| 208 // object owns. |
| 209 scoped_ptr<OAuth2TokenService::Request> DeviceOAuth2TokenService::StartRequest( |
| 210 const OAuth2TokenService::ScopeSet& scopes, |
| 211 OAuth2TokenService::Consumer* consumer) { |
| 212 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 213 |
| 214 if (refresh_token_is_valid_) { |
| 215 return OAuth2TokenService::StartRequest(scopes, consumer).Pass(); |
| 216 } else { |
| 217 ValidatingConsumer* validating_consumer = new ValidatingConsumer(this, |
| 218 consumer); |
| 219 pending_validators_->insert(validating_consumer); |
| 220 |
| 221 validating_consumer->StartValidation(); |
| 222 return OAuth2TokenService::StartRequest(scopes, validating_consumer).Pass(); |
| 223 } |
| 224 } |
| 225 |
| 226 void DeviceOAuth2TokenService::OnValidationComplete( |
| 227 ValidatingConsumer* validator, |
| 228 bool refresh_token_is_valid) { |
| 229 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 230 refresh_token_is_valid_ = refresh_token_is_valid; |
| 231 std::set<ValidatingConsumer*>::iterator iter = pending_validators_->find( |
| 232 validator); |
| 233 if (iter != pending_validators_->end()) |
| 234 pending_validators_->erase(iter); |
| 235 else |
| 236 LOG(ERROR) << "OnValidationComplete called for unknown validator"; |
| 23 } | 237 } |
| 24 | 238 |
| 25 // static | 239 // static |
| 26 void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) { | 240 void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) { |
| 27 registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken, | 241 registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken, |
| 28 std::string()); | 242 std::string()); |
| 29 } | 243 } |
| 30 | 244 |
| 31 void DeviceOAuth2TokenService::SetAndSaveRefreshToken( | 245 void DeviceOAuth2TokenService::SetAndSaveRefreshToken( |
| 32 const std::string& refresh_token) { | 246 const std::string& refresh_token) { |
| 33 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | 247 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 34 std::string encrypted_refresh_token = | 248 std::string encrypted_refresh_token = |
| 35 CryptohomeLibrary::Get()->EncryptWithSystemSalt(refresh_token); | 249 CryptohomeLibrary::Get()->EncryptWithSystemSalt(refresh_token); |
| 36 | 250 |
| 37 local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken, | 251 local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken, |
| 38 encrypted_refresh_token); | 252 encrypted_refresh_token); |
| 39 } | 253 } |
| 40 | 254 |
| 41 std::string DeviceOAuth2TokenService::GetRefreshToken() { | 255 std::string DeviceOAuth2TokenService::GetRefreshToken() { |
| 42 if (refresh_token_.empty()) { | 256 if (refresh_token_.empty()) { |
| 43 std::string encrypted_refresh_token = | 257 std::string encrypted_refresh_token = |
| 44 local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken); | 258 local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken); |
| 45 | 259 |
| 46 refresh_token_ = CryptohomeLibrary::Get()->DecryptWithSystemSalt( | 260 refresh_token_ = CryptohomeLibrary::Get()->DecryptWithSystemSalt( |
| 47 encrypted_refresh_token); | 261 encrypted_refresh_token); |
| 48 } | 262 } |
| 49 return refresh_token_; | 263 return refresh_token_; |
| 50 } | 264 } |
| 51 | 265 |
| 266 std::string DeviceOAuth2TokenService::GetRobotAccountId() { |
| 267 policy::BrowserPolicyConnector* connector = |
| 268 g_browser_process->browser_policy_connector(); |
| 269 if (connector) { |
| 270 return connector->GetDeviceCloudPolicyManager()->GetRobotAccountId(); |
| 271 } |
| 272 return std::string(); |
| 273 } |
| 274 |
| 52 } // namespace chromeos | 275 } // namespace chromeos |
| OLD | NEW |