| Index: chrome/browser/chromeos/settings/device_oauth2_token_service.cc
|
| diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
|
| index 1dc6bde934c3f50fbc53cff2d0e33b5f09c50b46..fb3490e9daa4fa9c4e87e3be924a61fdeea02aac 100644
|
| --- a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
|
| +++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
|
| @@ -12,18 +12,9 @@
|
| #include "base/memory/weak_ptr.h"
|
| #include "base/message_loop/message_loop.h"
|
| #include "base/prefs/pref_registry_simple.h"
|
| -#include "base/prefs/pref_service.h"
|
| -#include "base/values.h"
|
| -#include "chrome/browser/browser_process.h"
|
| -#include "chrome/browser/chromeos/settings/token_encryptor.h"
|
| +#include "base/time/time.h"
|
| #include "chrome/common/pref_names.h"
|
| -#include "chromeos/cryptohome/system_salt_getter.h"
|
| -#include "chromeos/settings/cros_settings_names.h"
|
| -#include "google_apis/gaia/gaia_constants.h"
|
| -#include "google_apis/gaia/gaia_urls.h"
|
| #include "google_apis/gaia/google_service_auth_error.h"
|
| -#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
|
| -#include "policy/proto/device_management_backend.pb.h"
|
|
|
| namespace chromeos {
|
|
|
| @@ -43,34 +34,24 @@ struct DeviceOAuth2TokenService::PendingRequest {
|
| const ScopeSet scopes;
|
| };
|
|
|
| -void DeviceOAuth2TokenService::OnServiceAccountIdentityChanged() {
|
| - if (!GetRobotAccountId().empty() && !refresh_token_.empty())
|
| - FireRefreshTokenAvailable(GetRobotAccountId());
|
| +void DeviceOAuth2TokenService::OnValidationCompleted(
|
| + GoogleServiceAuthError::State error) {
|
| + if (error == GoogleServiceAuthError::NONE)
|
| + FlushPendingRequests(true, GoogleServiceAuthError::NONE);
|
| + else
|
| + FlushPendingRequests(false, error);
|
| }
|
|
|
| DeviceOAuth2TokenService::DeviceOAuth2TokenService(
|
| - net::URLRequestContextGetter* getter,
|
| - PrefService* local_state)
|
| - : url_request_context_getter_(getter),
|
| - local_state_(local_state),
|
| - state_(STATE_LOADING),
|
| - max_refresh_token_validation_retries_(3),
|
| - service_account_identity_subscription_(
|
| - CrosSettings::Get()->AddSettingsObserver(
|
| - kServiceAccountIdentity,
|
| - base::Bind(
|
| - &DeviceOAuth2TokenService::OnServiceAccountIdentityChanged,
|
| - base::Unretained(this))).Pass()),
|
| - weak_ptr_factory_(this) {
|
| - // Pull in the system salt.
|
| - SystemSaltGetter::Get()->GetSystemSalt(
|
| - base::Bind(&DeviceOAuth2TokenService::DidGetSystemSalt,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| + DeviceOAuth2TokenServiceDelegate* delegate)
|
| + : OAuth2TokenService(delegate),
|
| + delegate_(static_cast<DeviceOAuth2TokenServiceDelegate*>(delegate)) {
|
| + delegate_->SetValidationStatusDelegate(this);
|
| }
|
|
|
| DeviceOAuth2TokenService::~DeviceOAuth2TokenService() {
|
| + delegate_->SetValidationStatusDelegate(nullptr);
|
| FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
|
| - FlushTokenSaveCallbacks(false);
|
| }
|
|
|
| // static
|
| @@ -82,107 +63,11 @@ void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) {
|
| void DeviceOAuth2TokenService::SetAndSaveRefreshToken(
|
| const std::string& refresh_token,
|
| const StatusCallback& result_callback) {
|
| - FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
|
| -
|
| - bool waiting_for_salt = state_ == STATE_LOADING;
|
| - refresh_token_ = refresh_token;
|
| - state_ = STATE_VALIDATION_PENDING;
|
| -
|
| - // If the robot account ID is not available yet, do not announce the token. It
|
| - // will be done from OnServiceAccountIdentityChanged() once the robot account
|
| - // ID becomes available as well.
|
| - if (!GetRobotAccountId().empty())
|
| - FireRefreshTokenAvailable(GetRobotAccountId());
|
| -
|
| - token_save_callbacks_.push_back(result_callback);
|
| - if (!waiting_for_salt) {
|
| - if (system_salt_.empty())
|
| - FlushTokenSaveCallbacks(false);
|
| - else
|
| - EncryptAndSaveToken();
|
| - }
|
| -}
|
| -
|
| -bool DeviceOAuth2TokenService::RefreshTokenIsAvailable(
|
| - const std::string& account_id) const {
|
| - switch (state_) {
|
| - case STATE_NO_TOKEN:
|
| - case STATE_TOKEN_INVALID:
|
| - return false;
|
| - case STATE_LOADING:
|
| - case STATE_VALIDATION_PENDING:
|
| - case STATE_VALIDATION_STARTED:
|
| - case STATE_TOKEN_VALID:
|
| - return account_id == GetRobotAccountId();
|
| - }
|
| -
|
| - NOTREACHED() << "Unhandled state " << state_;
|
| - return false;
|
| + delegate_->SetAndSaveRefreshToken(refresh_token, result_callback);
|
| }
|
|
|
| std::string DeviceOAuth2TokenService::GetRobotAccountId() const {
|
| - std::string result;
|
| - CrosSettings::Get()->GetString(kServiceAccountIdentity, &result);
|
| - return result;
|
| -}
|
| -
|
| -void DeviceOAuth2TokenService::OnRefreshTokenResponse(
|
| - const std::string& access_token,
|
| - int expires_in_seconds) {
|
| - gaia_oauth_client_->GetTokenInfo(
|
| - access_token,
|
| - max_refresh_token_validation_retries_,
|
| - this);
|
| -}
|
| -
|
| -void DeviceOAuth2TokenService::OnGetTokenInfoResponse(
|
| - scoped_ptr<base::DictionaryValue> token_info) {
|
| - std::string gaia_robot_id;
|
| - token_info->GetString("email", &gaia_robot_id);
|
| - gaia_oauth_client_.reset();
|
| -
|
| - CheckRobotAccountId(gaia_robot_id);
|
| -}
|
| -
|
| -void DeviceOAuth2TokenService::OnOAuthError() {
|
| - gaia_oauth_client_.reset();
|
| - state_ = STATE_TOKEN_INVALID;
|
| - FlushPendingRequests(false, GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
|
| -}
|
| -
|
| -void DeviceOAuth2TokenService::OnNetworkError(int response_code) {
|
| - gaia_oauth_client_.reset();
|
| -
|
| - // Go back to pending validation state. That'll allow a retry on subsequent
|
| - // token minting requests.
|
| - state_ = STATE_VALIDATION_PENDING;
|
| - FlushPendingRequests(false, GoogleServiceAuthError::CONNECTION_FAILED);
|
| -}
|
| -
|
| -std::string DeviceOAuth2TokenService::GetRefreshToken(
|
| - const std::string& account_id) const {
|
| - switch (state_) {
|
| - case STATE_LOADING:
|
| - case STATE_NO_TOKEN:
|
| - case STATE_TOKEN_INVALID:
|
| - // This shouldn't happen: GetRefreshToken() is only called for actual
|
| - // token minting operations. In above states, requests are either queued
|
| - // or short-circuited to signal error immediately, so no actual token
|
| - // minting via OAuth2TokenService::FetchOAuth2Token should be triggered.
|
| - NOTREACHED();
|
| - return std::string();
|
| - case STATE_VALIDATION_PENDING:
|
| - case STATE_VALIDATION_STARTED:
|
| - case STATE_TOKEN_VALID:
|
| - return refresh_token_;
|
| - }
|
| -
|
| - NOTREACHED() << "Unhandled state " << state_;
|
| - return std::string();
|
| -}
|
| -
|
| -net::URLRequestContextGetter* DeviceOAuth2TokenService::GetRequestContext() {
|
| - return url_request_context_getter_.get();
|
| + return delegate_->GetRobotAccountId();
|
| }
|
|
|
| void DeviceOAuth2TokenService::FetchOAuth2Token(
|
| @@ -192,166 +77,32 @@ void DeviceOAuth2TokenService::FetchOAuth2Token(
|
| const std::string& client_id,
|
| const std::string& client_secret,
|
| const ScopeSet& scopes) {
|
| - switch (state_) {
|
| - case STATE_VALIDATION_PENDING:
|
| + switch (delegate_->state_) {
|
| + case DeviceOAuth2TokenServiceDelegate::STATE_VALIDATION_PENDING:
|
| // If this is the first request for a token, start validation.
|
| - StartValidation();
|
| + delegate_->StartValidation();
|
| // fall through.
|
| - case STATE_LOADING:
|
| - case STATE_VALIDATION_STARTED:
|
| + case DeviceOAuth2TokenServiceDelegate::STATE_LOADING:
|
| + case DeviceOAuth2TokenServiceDelegate::STATE_VALIDATION_STARTED:
|
| // Add a pending request that will be satisfied once validation completes.
|
| pending_requests_.push_back(new PendingRequest(
|
| request->AsWeakPtr(), client_id, client_secret, scopes));
|
| + delegate_->RequestValidation();
|
| return;
|
| - case STATE_NO_TOKEN:
|
| + case DeviceOAuth2TokenServiceDelegate::STATE_NO_TOKEN:
|
| FailRequest(request, GoogleServiceAuthError::USER_NOT_SIGNED_UP);
|
| return;
|
| - case STATE_TOKEN_INVALID:
|
| + case DeviceOAuth2TokenServiceDelegate::STATE_TOKEN_INVALID:
|
| FailRequest(request, GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
|
| return;
|
| - case STATE_TOKEN_VALID:
|
| + case DeviceOAuth2TokenServiceDelegate::STATE_TOKEN_VALID:
|
| // Pass through to OAuth2TokenService to satisfy the request.
|
| OAuth2TokenService::FetchOAuth2Token(
|
| request, account_id, getter, client_id, client_secret, scopes);
|
| return;
|
| }
|
|
|
| - NOTREACHED() << "Unexpected state " << state_;
|
| -}
|
| -
|
| -OAuth2AccessTokenFetcher* DeviceOAuth2TokenService::CreateAccessTokenFetcher(
|
| - const std::string& account_id,
|
| - net::URLRequestContextGetter* getter,
|
| - OAuth2AccessTokenConsumer* consumer) {
|
| - std::string refresh_token = GetRefreshToken(account_id);
|
| - DCHECK(!refresh_token.empty());
|
| - return new OAuth2AccessTokenFetcherImpl(consumer, getter, refresh_token);
|
| -}
|
| -
|
| -
|
| -void DeviceOAuth2TokenService::DidGetSystemSalt(
|
| - const std::string& system_salt) {
|
| - system_salt_ = system_salt;
|
| -
|
| - // Bail out if system salt is not available.
|
| - if (system_salt_.empty()) {
|
| - LOG(ERROR) << "Failed to get system salt.";
|
| - FlushTokenSaveCallbacks(false);
|
| - state_ = STATE_NO_TOKEN;
|
| - FireRefreshTokensLoaded();
|
| - return;
|
| - }
|
| -
|
| - // If the token has been set meanwhile, write it to |local_state_|.
|
| - if (!refresh_token_.empty()) {
|
| - EncryptAndSaveToken();
|
| - FireRefreshTokensLoaded();
|
| - return;
|
| - }
|
| -
|
| - // Otherwise, load the refresh token from |local_state_|.
|
| - std::string encrypted_refresh_token =
|
| - local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken);
|
| - if (!encrypted_refresh_token.empty()) {
|
| - CryptohomeTokenEncryptor encryptor(system_salt_);
|
| - refresh_token_ = encryptor.DecryptWithSystemSalt(encrypted_refresh_token);
|
| - if (refresh_token_.empty()) {
|
| - LOG(ERROR) << "Failed to decrypt refresh token.";
|
| - state_ = STATE_NO_TOKEN;
|
| - FireRefreshTokensLoaded();
|
| - return;
|
| - }
|
| - }
|
| -
|
| - state_ = STATE_VALIDATION_PENDING;
|
| -
|
| - // If there are pending requests, start a validation.
|
| - if (!pending_requests_.empty())
|
| - StartValidation();
|
| -
|
| - // Announce the token.
|
| - FireRefreshTokenAvailable(GetRobotAccountId());
|
| - FireRefreshTokensLoaded();
|
| -}
|
| -
|
| -void DeviceOAuth2TokenService::CheckRobotAccountId(
|
| - const std::string& gaia_robot_id) {
|
| - // Make sure the value returned by GetRobotAccountId has been validated
|
| - // against current device settings.
|
| - switch (CrosSettings::Get()->PrepareTrustedValues(base::Bind(
|
| - &DeviceOAuth2TokenService::CheckRobotAccountId,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - gaia_robot_id))) {
|
| - case CrosSettingsProvider::TRUSTED:
|
| - // All good, compare account ids below.
|
| - break;
|
| - case CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
|
| - // The callback passed to PrepareTrustedValues above will trigger a
|
| - // re-check eventually.
|
| - return;
|
| - case CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
|
| - // There's no trusted account id, which is equivalent to no token present.
|
| - LOG(WARNING) << "Device settings permanently untrusted.";
|
| - state_ = STATE_NO_TOKEN;
|
| - FlushPendingRequests(false, GoogleServiceAuthError::USER_NOT_SIGNED_UP);
|
| - return;
|
| - }
|
| -
|
| - std::string policy_robot_id = GetRobotAccountId();
|
| - if (policy_robot_id == gaia_robot_id) {
|
| - state_ = STATE_TOKEN_VALID;
|
| - FlushPendingRequests(true, GoogleServiceAuthError::NONE);
|
| - } else {
|
| - if (gaia_robot_id.empty()) {
|
| - LOG(WARNING) << "Device service account owner in policy is empty.";
|
| - } else {
|
| - LOG(WARNING) << "Device service account owner in policy does not match "
|
| - << "refresh token owner \"" << gaia_robot_id << "\".";
|
| - }
|
| - state_ = STATE_TOKEN_INVALID;
|
| - FlushPendingRequests(false,
|
| - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
|
| - }
|
| -}
|
| -
|
| -void DeviceOAuth2TokenService::EncryptAndSaveToken() {
|
| - DCHECK_NE(state_, STATE_LOADING);
|
| -
|
| - CryptohomeTokenEncryptor encryptor(system_salt_);
|
| - std::string encrypted_refresh_token =
|
| - encryptor.EncryptWithSystemSalt(refresh_token_);
|
| - bool result = true;
|
| - if (encrypted_refresh_token.empty()) {
|
| - LOG(ERROR) << "Failed to encrypt refresh token; save aborted.";
|
| - result = false;
|
| - } else {
|
| - local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken,
|
| - encrypted_refresh_token);
|
| - }
|
| -
|
| - FlushTokenSaveCallbacks(result);
|
| -}
|
| -
|
| -void DeviceOAuth2TokenService::StartValidation() {
|
| - DCHECK_EQ(state_, STATE_VALIDATION_PENDING);
|
| - DCHECK(!gaia_oauth_client_);
|
| -
|
| - state_ = STATE_VALIDATION_STARTED;
|
| -
|
| - gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
|
| - g_browser_process->system_request_context()));
|
| -
|
| - GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
|
| - gaia::OAuthClientInfo client_info;
|
| - client_info.client_id = gaia_urls->oauth2_chrome_client_id();
|
| - client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
|
| -
|
| - gaia_oauth_client_->RefreshToken(
|
| - client_info,
|
| - refresh_token_,
|
| - std::vector<std::string>(1, GaiaConstants::kOAuthWrapBridgeUserInfoScope),
|
| - max_refresh_token_validation_retries_,
|
| - this);
|
| + NOTREACHED() << "Unexpected state " << delegate_->state_;
|
| }
|
|
|
| void DeviceOAuth2TokenService::FlushPendingRequests(
|
| @@ -370,27 +121,14 @@ void DeviceOAuth2TokenService::FlushPendingRequests(
|
| OAuth2TokenService::FetchOAuth2Token(
|
| scoped_request->request.get(),
|
| scoped_request->request->GetAccountId(),
|
| - GetRequestContext(),
|
| - scoped_request->client_id,
|
| - scoped_request->client_secret,
|
| - scoped_request->scopes);
|
| + delegate_->GetRequestContext(), scoped_request->client_id,
|
| + scoped_request->client_secret, scoped_request->scopes);
|
| } else {
|
| FailRequest(scoped_request->request.get(), error);
|
| }
|
| }
|
| }
|
|
|
| -void DeviceOAuth2TokenService::FlushTokenSaveCallbacks(bool result) {
|
| - std::vector<StatusCallback> callbacks;
|
| - callbacks.swap(token_save_callbacks_);
|
| - for (std::vector<StatusCallback>::iterator callback(callbacks.begin());
|
| - callback != callbacks.end();
|
| - ++callback) {
|
| - if (!callback->is_null())
|
| - callback->Run(result);
|
| - }
|
| -}
|
| -
|
| void DeviceOAuth2TokenService::FailRequest(
|
| RequestImpl* request,
|
| GoogleServiceAuthError::State error) {
|
|
|