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) { |