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