Home | History | Annotate | Download | only in settings
      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 {
     30   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 
     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 
     59 DeviceOAuth2TokenService::~DeviceOAuth2TokenService() {
     60   FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
     61   FlushTokenSaveCallbacks(false);
     62 }
     63 
     64 // static
     65 void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) {
     66   registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken,
     67                                std::string());
     68 }
     69 
     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 
     89 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 
    106 std::string DeviceOAuth2TokenService::GetRobotAccountId() const {
    107   std::string result;
    108   CrosSettings::Get()->GetString(kServiceAccountIdentity, &result);
    109   return result;
    110 }
    111 
    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 
    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 
    130 void DeviceOAuth2TokenService::OnOAuthError() {
    131   gaia_oauth_client_.reset();
    132   state_ = STATE_TOKEN_INVALID;
    133   FlushPendingRequests(false, GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
    134 }
    135 
    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 
    145 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 
    167 net::URLRequestContextGetter* DeviceOAuth2TokenService::GetRequestContext() {
    168   return url_request_context_getter_.get();
    169 }
    170 
    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 
    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 
    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   if (!encrypted_refresh_token.empty()) {
    239     CryptohomeTokenEncryptor encryptor(system_salt_);
    240     refresh_token_ = encryptor.DecryptWithSystemSalt(encrypted_refresh_token);
    241     if (refresh_token_.empty()) {
    242       LOG(ERROR) << "Failed to decrypt refresh token.";
    243       state_ = STATE_NO_TOKEN;
    244       FireRefreshTokensLoaded();
    245       return;
    246     }
    247   }
    248 
    249   state_ = STATE_VALIDATION_PENDING;
    250 
    251   // If there are pending requests, start a validation.
    252   if (!pending_requests_.empty())
    253     StartValidation();
    254 
    255   // Announce the token.
    256   FireRefreshTokenAvailable(GetRobotAccountId());
    257   FireRefreshTokensLoaded();
    258 }
    259 
    260 void DeviceOAuth2TokenService::CheckRobotAccountId(
    261     const std::string& gaia_robot_id) {
    262   // Make sure the value returned by GetRobotAccountId has been validated
    263   // against current device settings.
    264   switch (CrosSettings::Get()->PrepareTrustedValues(
    265       base::Bind(&DeviceOAuth2TokenService::CheckRobotAccountId,
    266                  weak_ptr_factory_.GetWeakPtr(),
    267                  gaia_robot_id))) {
    268     case CrosSettingsProvider::TRUSTED:
    269       // All good, compare account ids below.
    270       break;
    271     case CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
    272       // The callback passed to PrepareTrustedValues above will trigger a
    273       // re-check eventually.
    274       return;
    275     case CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
    276       // There's no trusted account id, which is equivalent to no token present.
    277       LOG(WARNING) << "Device settings permanently untrusted.";
    278       state_ = STATE_NO_TOKEN;
    279       FlushPendingRequests(false, GoogleServiceAuthError::USER_NOT_SIGNED_UP);
    280       return;
    281   }
    282 
    283   std::string policy_robot_id = GetRobotAccountId();
    284   if (policy_robot_id == gaia_robot_id) {
    285     state_ = STATE_TOKEN_VALID;
    286     FlushPendingRequests(true, GoogleServiceAuthError::NONE);
    287   } else {
    288     if (gaia_robot_id.empty()) {
    289       LOG(WARNING) << "Device service account owner in policy is empty.";
    290     } else {
    291       LOG(WARNING) << "Device service account owner in policy does not match "
    292                    << "refresh token owner \"" << gaia_robot_id << "\".";
    293     }
    294     state_ = STATE_TOKEN_INVALID;
    295     FlushPendingRequests(false,
    296                          GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
    297   }
    298 }
    299 
    300 void DeviceOAuth2TokenService::EncryptAndSaveToken() {
    301   DCHECK_NE(state_, STATE_LOADING);
    302 
    303   CryptohomeTokenEncryptor encryptor(system_salt_);
    304   std::string encrypted_refresh_token =
    305       encryptor.EncryptWithSystemSalt(refresh_token_);
    306   bool result = true;
    307   if (encrypted_refresh_token.empty()) {
    308     LOG(ERROR) << "Failed to encrypt refresh token; save aborted.";
    309     result = false;
    310   } else {
    311     local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken,
    312                             encrypted_refresh_token);
    313   }
    314 
    315   FlushTokenSaveCallbacks(result);
    316 }
    317 
    318 void DeviceOAuth2TokenService::StartValidation() {
    319   DCHECK_EQ(state_, STATE_VALIDATION_PENDING);
    320   DCHECK(!gaia_oauth_client_);
    321 
    322   state_ = STATE_VALIDATION_STARTED;
    323 
    324   gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
    325       g_browser_process->system_request_context()));
    326 
    327   GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
    328   gaia::OAuthClientInfo client_info;
    329   client_info.client_id = gaia_urls->oauth2_chrome_client_id();
    330   client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
    331 
    332   gaia_oauth_client_->RefreshToken(
    333       client_info,
    334       refresh_token_,
    335       std::vector<std::string>(1, GaiaConstants::kOAuthWrapBridgeUserInfoScope),
    336       max_refresh_token_validation_retries_,
    337       this);
    338 }
    339 
    340 void DeviceOAuth2TokenService::FlushPendingRequests(
    341     bool token_is_valid,
    342     GoogleServiceAuthError::State error) {
    343   std::vector<PendingRequest*> requests;
    344   requests.swap(pending_requests_);
    345   for (std::vector<PendingRequest*>::iterator request(requests.begin());
    346        request != requests.end();
    347        ++request) {
    348     scoped_ptr<PendingRequest> scoped_request(*request);
    349     if (!scoped_request->request)
    350       continue;
    351 
    352     if (token_is_valid) {
    353       OAuth2TokenService::FetchOAuth2Token(
    354           scoped_request->request.get(),
    355           scoped_request->request->GetAccountId(),
    356           GetRequestContext(),
    357           scoped_request->client_id,
    358           scoped_request->client_secret,
    359           scoped_request->scopes);
    360     } else {
    361       FailRequest(scoped_request->request.get(), error);
    362     }
    363   }
    364 }
    365 
    366 void DeviceOAuth2TokenService::FlushTokenSaveCallbacks(bool result) {
    367   std::vector<StatusCallback> callbacks;
    368   callbacks.swap(token_save_callbacks_);
    369   for (std::vector<StatusCallback>::iterator callback(callbacks.begin());
    370        callback != callbacks.end();
    371        ++callback) {
    372     if (!callback->is_null())
    373       callback->Run(result);
    374   }
    375 }
    376 
    377 void DeviceOAuth2TokenService::FailRequest(
    378     RequestImpl* request,
    379     GoogleServiceAuthError::State error) {
    380   GoogleServiceAuthError auth_error(error);
    381   base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
    382       &RequestImpl::InformConsumer,
    383       request->AsWeakPtr(),
    384       auth_error,
    385       std::string(),
    386       base::Time()));
    387 }
    388 
    389 }  // namespace chromeos
    390