diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-05 17:15:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-04-11 07:47:18 +0000 |
commit | 7324afb043a0b1e623d8e8eb906cdc53bdeb4685 (patch) | |
tree | a3fe2d74ea9c9e142c390dac4ca0e219382ace46 /chromium/components/signin | |
parent | 6a4cabb866f66d4128a97cdc6d9d08ce074f1247 (diff) | |
download | qtwebengine-chromium-7324afb043a0b1e623d8e8eb906cdc53bdeb4685.tar.gz |
BASELINE: Update Chromium to 58.0.3029.54
Change-Id: I67f57065a7afdc8e4614adb5c0230281428df4d1
Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'chromium/components/signin')
27 files changed, 811 insertions, 374 deletions
diff --git a/chromium/components/signin/core/browser/BUILD.gn b/chromium/components/signin/core/browser/BUILD.gn index a381719d0ba..5265026b314 100644 --- a/chromium/components/signin/core/browser/BUILD.gn +++ b/chromium/components/signin/core/browser/BUILD.gn @@ -10,6 +10,8 @@ static_library("browser") { sources = [ "about_signin_internals.cc", "about_signin_internals.h", + "access_token_fetcher.cc", + "access_token_fetcher.h", "account_fetcher_service.cc", "account_fetcher_service.h", "account_info.cc", @@ -30,8 +32,6 @@ static_library("browser") { "child_account_info_fetcher_android.h", "child_account_info_fetcher_impl.cc", "child_account_info_fetcher_impl.h", - "device_activity_fetcher.cc", - "device_activity_fetcher.h", "gaia_cookie_manager_service.cc", "gaia_cookie_manager_service.h", "profile_identity_provider.cc", @@ -83,6 +83,7 @@ static_library("browser") { "//base:i18n", "//components/content_settings/core/browser", "//components/content_settings/core/common", + "//components/data_use_measurement/core", "//components/google/core/browser", "//components/invalidation/public", "//components/keyed_service/core", @@ -146,6 +147,7 @@ static_library("test_support") { source_set("unit_tests") { testonly = true sources = [ + "access_token_fetcher_unittest.cc", "account_info_unittest.cc", "account_investigator_unittest.cc", "account_tracker_service_unittest.cc", @@ -160,6 +162,7 @@ source_set("unit_tests") { deps = [ ":test_support", + "//base/test:test_support", "//components/content_settings/core/browser", "//components/os_crypt:test_support", "//components/pref_registry:pref_registry", diff --git a/chromium/components/signin/core/browser/DEPS b/chromium/components/signin/core/browser/DEPS index 2619fdcfa73..8ca3732081c 100644 --- a/chromium/components/signin/core/browser/DEPS +++ b/chromium/components/signin/core/browser/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+components/data_use_measurement/core", "+components/invalidation/public", "+components/metrics", "+google/cacheinvalidation", diff --git a/chromium/components/signin/core/browser/about_signin_internals.cc b/chromium/components/signin/core/browser/about_signin_internals.cc index ec21740ccb8..d925e375482 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.cc +++ b/chromium/components/signin/core/browser/about_signin_internals.cc @@ -28,6 +28,7 @@ #include "components/signin/core/browser/signin_manager.h" #include "components/signin/core/common/profile_management_switches.h" #include "components/signin/core/common/signin_switches.h" +#include "google_apis/gaia/oauth2_token_service_delegate.h" #include "net/base/backoff_entry.h" using base::Time; @@ -88,6 +89,30 @@ std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) { return std::string(); } +std::string TokenServiceLoadCredentialsStateToLabel( + OAuth2TokenServiceDelegate::LoadCredentialsState state) { + switch (state) { + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_UNKNOWN: + return "Unknown"; + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_NOT_STARTED: + return "Load credentials not started"; + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_IN_PROGRESS: + return "Load credentials in progress"; + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_SUCCESS: + return "Load credentials finished with success"; + case OAuth2TokenServiceDelegate::LOAD_CREDENTIALS_FINISHED_WITH_DB_ERRORS: + return "Load credentials failed with database errors"; + case OAuth2TokenServiceDelegate:: + LOAD_CREDENTIALS_FINISHED_WITH_DECRYPT_ERRORS: + return "Load credentials failed with decrypt errors"; + case OAuth2TokenServiceDelegate:: + LOAD_CREDENTIALS_FINISHED_WITH_UNKNOWN_ERRORS: + return "Load credentials failed with unknown errors"; + } + NOTREACHED(); + return std::string(); +} + #if !defined (OS_CHROMEOS) std::string SigninStatusFieldToLabel(TimedSigninStatusField field) { switch (field) { @@ -334,6 +359,10 @@ void AboutSigninInternals::OnFetchAccessTokenComplete( NotifyObservers(); } +void AboutSigninInternals::OnRefreshTokensLoaded() { + NotifyObservers(); +} + void AboutSigninInternals::OnTokenRemoved( const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) { @@ -514,6 +543,10 @@ AboutSigninInternals::SigninStatus::ToValue( switches::IsEnableAccountConsistency() == true ? "On" : "Off"); AddSectionEntry(basic_info, "Signin Status", signin_manager->IsAuthenticated() ? "Signed In" : "Not Signed In"); + OAuth2TokenServiceDelegate::LoadCredentialsState load_tokens_state = + token_service->GetDelegate()->GetLoadCredentialsState(); + AddSectionEntry(basic_info, "TokenService Status", + TokenServiceLoadCredentialsStateToLabel(load_tokens_state)); // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is // fixed. diff --git a/chromium/components/signin/core/browser/about_signin_internals.h b/chromium/components/signin/core/browser/about_signin_internals.h index 3caeb6ebd3e..8e42feab409 100644 --- a/chromium/components/signin/core/browser/about_signin_internals.h +++ b/chromium/components/signin/core/browser/about_signin_internals.h @@ -37,6 +37,7 @@ using TimedSigninStatusValue = std::pair<std::string, std::string>; class AboutSigninInternals : public KeyedService, public signin_internals_util::SigninDiagnosticsObserver, + public OAuth2TokenService::Observer, public OAuth2TokenService::DiagnosticsObserver, public GaiaCookieManagerService::Observer, SigninManagerBase::Observer, @@ -184,6 +185,9 @@ class AboutSigninInternals void OnTokenRemoved(const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) override; + // OAuth2TokenServiceDelegate::Observer implementations. + void OnRefreshTokensLoaded() override; + // SigninManagerBase::Observer implementations. void GoogleSigninFailed(const GoogleServiceAuthError& error) override; void GoogleSigninSucceeded(const std::string& account_id, diff --git a/chromium/components/signin/core/browser/access_token_fetcher.cc b/chromium/components/signin/core/browser/access_token_fetcher.cc new file mode 100644 index 00000000000..d301d3708da --- /dev/null +++ b/chromium/components/signin/core/browser/access_token_fetcher.cc @@ -0,0 +1,169 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/core/browser/access_token_fetcher.h" + +#include <utility> + +#include "base/logging.h" + +AccessTokenFetcher::AccessTokenFetcher( + const std::string& oauth_consumer_name, + SigninManagerBase* signin_manager, + OAuth2TokenService* token_service, + const OAuth2TokenService::ScopeSet& scopes, + TokenCallback callback) + : OAuth2TokenService::Consumer(oauth_consumer_name), + signin_manager_(signin_manager), + token_service_(token_service), + scopes_(scopes), + callback_(std::move(callback)), + waiting_for_sign_in_(false), + waiting_for_refresh_token_(false), + access_token_retried_(false) { + Start(); +} + +AccessTokenFetcher::~AccessTokenFetcher() { + if (waiting_for_sign_in_) { + signin_manager_->RemoveObserver(this); + } + if (waiting_for_refresh_token_) { + token_service_->RemoveObserver(this); + } +} + +void AccessTokenFetcher::Start() { + if (signin_manager_->IsAuthenticated()) { + // Already signed in: Make sure we have a refresh token, then get the access + // token. + WaitForRefreshToken(); + return; + } + + // Not signed in: Wait for a sign-in to complete (to get the refresh token), + // then get the access token. + DCHECK(!waiting_for_sign_in_); + waiting_for_sign_in_ = true; + signin_manager_->AddObserver(this); +} + +void AccessTokenFetcher::WaitForRefreshToken() { + DCHECK(signin_manager_->IsAuthenticated()); + DCHECK(!waiting_for_refresh_token_); + + if (token_service_->RefreshTokenIsAvailable( + signin_manager_->GetAuthenticatedAccountId())) { + // Already have refresh token: Get the access token directly. + StartAccessTokenRequest(); + return; + } + + // Signed in, but refresh token isn't there yet: Wait for the refresh + // token to be loaded, then get the access token. + waiting_for_refresh_token_ = true; + token_service_->AddObserver(this); +} + +void AccessTokenFetcher::StartAccessTokenRequest() { + // Note: We might get here even in cases where we know that there's no refresh + // token. We're requesting an access token anyway, so that the token service + // will generate an appropriate error code that we can return to the client. + DCHECK(!access_token_request_); + access_token_request_ = token_service_->StartRequest( + signin_manager_->GetAuthenticatedAccountId(), scopes_, this); +} + +void AccessTokenFetcher::GoogleSigninSucceeded(const std::string& account_id, + const std::string& username, + const std::string& password) { + DCHECK(waiting_for_sign_in_); + DCHECK(!waiting_for_refresh_token_); + DCHECK(signin_manager_->IsAuthenticated()); + waiting_for_sign_in_ = false; + signin_manager_->RemoveObserver(this); + + WaitForRefreshToken(); +} + +void AccessTokenFetcher::GoogleSigninFailed( + const GoogleServiceAuthError& error) { + DCHECK(waiting_for_sign_in_); + DCHECK(!waiting_for_refresh_token_); + waiting_for_sign_in_ = false; + signin_manager_->RemoveObserver(this); + + std::move(callback_).Run(error, std::string()); +} + +void AccessTokenFetcher::OnRefreshTokenAvailable( + const std::string& account_id) { + DCHECK(waiting_for_refresh_token_); + DCHECK(!waiting_for_sign_in_); + + // Only react on tokens for the account the user has signed in with. + if (account_id != signin_manager_->GetAuthenticatedAccountId()) { + return; + } + + waiting_for_refresh_token_ = false; + token_service_->RemoveObserver(this); + StartAccessTokenRequest(); +} + +void AccessTokenFetcher::OnRefreshTokensLoaded() { + DCHECK(waiting_for_refresh_token_); + DCHECK(!waiting_for_sign_in_); + DCHECK(!access_token_request_); + + // All refresh tokens were loaded, but we didn't get one for the account we + // care about. We probably won't get one any time soon. + // Attempt to fetch an access token anyway, so that the token service will + // provide us with an appropriate error code. + waiting_for_refresh_token_ = false; + token_service_->RemoveObserver(this); + StartAccessTokenRequest(); +} + +void AccessTokenFetcher::OnGetTokenSuccess( + const OAuth2TokenService::Request* request, + const std::string& access_token, + const base::Time& expiration_time) { + DCHECK_EQ(request, access_token_request_.get()); + std::unique_ptr<OAuth2TokenService::Request> request_deleter( + std::move(access_token_request_)); + + std::move(callback_).Run(GoogleServiceAuthError::AuthErrorNone(), + access_token); +} + +void AccessTokenFetcher::OnGetTokenFailure( + const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) { + DCHECK_EQ(request, access_token_request_.get()); + std::unique_ptr<OAuth2TokenService::Request> request_deleter( + std::move(access_token_request_)); + + // There is a special case for Android that RefreshTokenIsAvailable and + // StartRequest are called to pre-fetch the account image and name before + // sign-in. In that case, our ongoing access token request gets cancelled. + // Moreover, OnRefreshTokenAvailable might happen after startup when the + // credentials are changed/updated. + // To handle these cases, we retry a canceled request once. + // However, a request may also get cancelled for legitimate reasons, e.g. + // because the user signed out. In those cases, there's no point in retrying, + // so only retry if there (still) is a valid refresh token. + // NOTE: Maybe we should retry for all transient errors here, so that clients + // don't have to. + if (!access_token_retried_ && + error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED && + token_service_->RefreshTokenIsAvailable( + signin_manager_->GetAuthenticatedAccountId())) { + access_token_retried_ = true; + StartAccessTokenRequest(); + return; + } + + std::move(callback_).Run(error, std::string()); +} diff --git a/chromium/components/signin/core/browser/access_token_fetcher.h b/chromium/components/signin/core/browser/access_token_fetcher.h new file mode 100644 index 00000000000..2963ab8b797 --- /dev/null +++ b/chromium/components/signin/core/browser/access_token_fetcher.h @@ -0,0 +1,85 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_ACCESS_TOKEN_FETCHER_H_ +#define COMPONENTS_SIGNIN_CORE_BROWSER_ACCESS_TOKEN_FETCHER_H_ + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "components/signin/core/browser/signin_manager_base.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "google_apis/gaia/oauth2_token_service.h" + +// Helper class to ease the task of obtaining an OAuth2 access token for the +// authenticated account. This handles various special cases, e.g. when the +// refresh token isn't loaded yet (during startup), or when there is some +// transient error. +// May only be used on the UI thread. +class AccessTokenFetcher : public SigninManagerBase::Observer, + public OAuth2TokenService::Observer, + public OAuth2TokenService::Consumer { + public: + // Callback for when a request completes (successful or not). On successful + // requests, |error| is NONE and |access_token| contains the obtained OAuth2 + // access token. On failed requests, |error| contains the actual error and + // |access_token| is empty. + using TokenCallback = + base::OnceCallback<void(const GoogleServiceAuthError& error, + const std::string& access_token)>; + + // Instantiates a fetcher and immediately starts the process of obtaining an + // OAuth2 access token for the given |scopes|. The |callback| is called once + // the request completes (successful or not). If the AccessTokenFetcher is + // destroyed before the process completes, the callback is not called. + AccessTokenFetcher(const std::string& oauth_consumer_name, + SigninManagerBase* signin_manager, + OAuth2TokenService* token_service, + const OAuth2TokenService::ScopeSet& scopes, + TokenCallback callback); + + ~AccessTokenFetcher() override; + + private: + void Start(); + + void WaitForRefreshToken(); + void StartAccessTokenRequest(); + + // SigninManagerBase::Observer implementation. + void GoogleSigninSucceeded(const std::string& account_id, + const std::string& username, + const std::string& password) override; + void GoogleSigninFailed(const GoogleServiceAuthError& error) override; + + // OAuth2TokenService::Observer implementation. + void OnRefreshTokenAvailable(const std::string& account_id) override; + void OnRefreshTokensLoaded() override; + + // OAuth2TokenService::Consumer implementation. + void OnGetTokenSuccess(const OAuth2TokenService::Request* request, + const std::string& access_token, + const base::Time& expiration_time) override; + void OnGetTokenFailure(const OAuth2TokenService::Request* request, + const GoogleServiceAuthError& error) override; + + SigninManagerBase* signin_manager_; + OAuth2TokenService* token_service_; + OAuth2TokenService::ScopeSet scopes_; + TokenCallback callback_; + + bool waiting_for_sign_in_; + bool waiting_for_refresh_token_; + + std::unique_ptr<OAuth2TokenService::Request> access_token_request_; + + // When a token request gets canceled, we want to retry once. + bool access_token_retried_; + + DISALLOW_COPY_AND_ASSIGN(AccessTokenFetcher); +}; + +#endif // COMPONENTS_SIGNIN_CORE_BROWSER_ACCESS_TOKEN_FETCHER_H_ diff --git a/chromium/components/signin/core/browser/access_token_fetcher_unittest.cc b/chromium/components/signin/core/browser/access_token_fetcher_unittest.cc new file mode 100644 index 00000000000..331468b0e15 --- /dev/null +++ b/chromium/components/signin/core/browser/access_token_fetcher_unittest.cc @@ -0,0 +1,390 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/signin/core/browser/access_token_fetcher.h" + +#include <memory> +#include <utility> + +#include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/test/mock_callback.h" +#include "components/prefs/testing_pref_service.h" +#include "components/signin/core/browser/account_tracker_service.h" +#include "components/signin/core/browser/fake_profile_oauth2_token_service.h" +#include "components/signin/core/browser/fake_signin_manager.h" +#include "components/signin/core/browser/test_signin_client.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gmock_mutant.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::MockCallback; +using sync_preferences::TestingPrefServiceSyncable; +using testing::CallbackToFunctor; +using testing::InvokeWithoutArgs; +using testing::StrictMock; + +#if defined(OS_CHROMEOS) +// ChromeOS doesn't have SigninManager. +using SigninManagerForTest = FakeSigninManagerBase; +#else +using SigninManagerForTest = FakeSigninManager; +#endif // OS_CHROMEOS + +class AccessTokenFetcherTest : public testing::Test { + public: + using TestTokenCallback = + StrictMock<MockCallback<AccessTokenFetcher::TokenCallback>>; + + AccessTokenFetcherTest() : signin_client_(&pref_service_) { + AccountTrackerService::RegisterPrefs(pref_service_.registry()); +#if defined(OS_CHROMEOS) + SigninManagerBase::RegisterProfilePrefs(pref_service_.registry()); + SigninManagerBase::RegisterPrefs(pref_service_.registry()); +#else + SigninManager::RegisterProfilePrefs(pref_service_.registry()); + SigninManager::RegisterPrefs(pref_service_.registry()); +#endif // OS_CHROMEOS + + account_tracker_ = base::MakeUnique<AccountTrackerService>(); + account_tracker_->Initialize(&signin_client_); + +#if defined(OS_CHROMEOS) + signin_manager_ = base::MakeUnique<FakeSigninManagerBase>( + &signin_client_, account_tracker_.get()); +#else + signin_manager_ = base::MakeUnique<FakeSigninManager>( + &signin_client_, &token_service_, account_tracker_.get(), + /*cookie_manager_service=*/nullptr); +#endif // OS_CHROMEOS + } + + ~AccessTokenFetcherTest() override {} + + std::unique_ptr<AccessTokenFetcher> CreateFetcher( + AccessTokenFetcher::TokenCallback callback) { + std::set<std::string> scopes{"scope"}; + return base::MakeUnique<AccessTokenFetcher>( + "test_consumer", signin_manager_.get(), &token_service_, scopes, + std::move(callback)); + } + + FakeProfileOAuth2TokenService* token_service() { return &token_service_; } + SigninManagerForTest* signin_manager() { return signin_manager_.get(); } + + void SignIn(const std::string& account) { +#if defined(OS_CHROMEOS) + signin_manager_->SignIn(account); +#else + signin_manager_->SignIn(account, "user", "pass"); +#endif // OS_CHROMEOS + } + + private: + TestingPrefServiceSyncable pref_service_; + TestSigninClient signin_client_; + FakeProfileOAuth2TokenService token_service_; + + std::unique_ptr<AccountTrackerService> account_tracker_; + std::unique_ptr<SigninManagerForTest> signin_manager_; +}; + +TEST_F(AccessTokenFetcherTest, ShouldReturnAccessToken) { + TestTokenCallback callback; + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher(callback.Get()); + + // Once the access token request is fulfilled, we should get called back with + // the access token. + EXPECT_CALL(callback, + Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); + token_service()->IssueAllTokensForAccount( + "account", "access token", + base::Time::Now() + base::TimeDelta::FromHours(1)); +} + +TEST_F(AccessTokenFetcherTest, ShouldNotReplyIfDestroyed) { + TestTokenCallback callback; + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher(callback.Get()); + + // Destroy the fetcher before the access token request is fulfilled. + fetcher.reset(); + + // Now fulfilling the access token request should have no effect. + token_service()->IssueAllTokensForAccount( + "account", "access token", + base::Time::Now() + base::TimeDelta::FromHours(1)); +} + +TEST_F(AccessTokenFetcherTest, ShouldNotReturnWhenSignedOut) { + TestTokenCallback callback; + + // Signed out -> the fetcher should wait for a sign-in which never happens + // in this test, so we shouldn't get called back. + auto fetcher = CreateFetcher(callback.Get()); +} + +// Tests related to waiting for sign-in don't apply on ChromeOS (it doesn't have +// that concept). +#if !defined(OS_CHROMEOS) + +TEST_F(AccessTokenFetcherTest, ShouldWaitForSignIn) { + TestTokenCallback callback; + + // Not signed in, so this should wait for a sign-in to complete. + auto fetcher = CreateFetcher(callback.Get()); + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Once the access token request is fulfilled, we should get called back with + // the access token. + EXPECT_CALL(callback, + Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); + token_service()->IssueAllTokensForAccount( + "account", "access token", + base::Time::Now() + base::TimeDelta::FromHours(1)); +} + +TEST_F(AccessTokenFetcherTest, ShouldWaitForSignInInProgress) { + TestTokenCallback callback; + + signin_manager()->set_auth_in_progress("account"); + + // A sign-in is currently in progress, so this should wait for the sign-in to + // complete. + auto fetcher = CreateFetcher(callback.Get()); + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Once the access token request is fulfilled, we should get called back with + // the access token. + EXPECT_CALL(callback, + Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); + token_service()->IssueAllTokensForAccount( + "account", "access token", + base::Time::Now() + base::TimeDelta::FromHours(1)); +} + +TEST_F(AccessTokenFetcherTest, ShouldWaitForFailedSignIn) { + TestTokenCallback callback; + + signin_manager()->set_auth_in_progress("account"); + + // A sign-in is currently in progress, so this should wait for the sign-in to + // complete. + auto fetcher = CreateFetcher(callback.Get()); + + // The fetcher should detect the failed sign-in and call us with an empty + // access token. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED), + std::string())); + + signin_manager()->FailSignin( + GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); +} + +#endif // !OS_CHROMEOS + +TEST_F(AccessTokenFetcherTest, ShouldWaitForRefreshToken) { + TestTokenCallback callback; + + SignIn("account"); + + // Signed in, but there is no refresh token -> we should not get called back + // (yet). + auto fetcher = CreateFetcher(callback.Get()); + + // Getting a refresh token should result in a request for an access token. + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Once the access token request is fulfilled, we should get called back with + // the access token. + EXPECT_CALL(callback, + Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); + token_service()->IssueAllTokensForAccount( + "account", "access token", + base::Time::Now() + base::TimeDelta::FromHours(1)); +} + +TEST_F(AccessTokenFetcherTest, ShouldIgnoreRefreshTokensForOtherAccounts) { + TestTokenCallback callback; + + // Signed-in to "account", but there's only a refresh token for a different + // account. + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account 2", "refresh"); + + // The fetcher should wait for the correct refresh token. + auto fetcher = CreateFetcher(callback.Get()); + + // A refresh token for yet another account shouldn't matter either. + token_service()->GetDelegate()->UpdateCredentials("account 3", "refresh"); +} + +TEST_F(AccessTokenFetcherTest, ShouldReturnWhenNoRefreshTokenAvailable) { + TestTokenCallback callback; + + SignIn("account"); + + // Signed in, but there is no refresh token -> we should not get called back + // (yet). + auto fetcher = CreateFetcher(callback.Get()); + + // Getting a refresh token for some other account should have no effect. + token_service()->GetDelegate()->UpdateCredentials("different account", + "refresh token"); + + // The OAuth2TokenService posts a task to the current thread when we try to + // get an access token for an account without a refresh token, so we need a + // MessageLoop in this test to not crash. + base::MessageLoop message_loop; + + // When all refresh tokens have been loaded by the token service, but the one + // for our account wasn't among them, we should get called back with an empty + // access token. + EXPECT_CALL(callback, Run(testing::_, std::string())); + token_service()->GetDelegate()->LoadCredentials("account doesn't matter"); + + // Wait for the task posted by OAuth2TokenService to run. + base::RunLoop().RunUntilIdle(); +} + +TEST_F(AccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequest) { + TestTokenCallback callback; + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher(callback.Get()); + + // A canceled access token request should get retried once. + token_service()->IssueErrorForAllPendingRequestsForAccount( + "account", + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + + // Once the access token request is fulfilled, we should get called back with + // the access token. + EXPECT_CALL(callback, + Run(GoogleServiceAuthError::AuthErrorNone(), "access token")); + token_service()->IssueAllTokensForAccount( + "account", "access token", + base::Time::Now() + base::TimeDelta::FromHours(1)); +} + +TEST_F(AccessTokenFetcherTest, ShouldRetryCanceledAccessTokenRequestOnlyOnce) { + TestTokenCallback callback; + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher(callback.Get()); + + // A canceled access token request should get retried once. + token_service()->IssueErrorForAllPendingRequestsForAccount( + "account", + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); + + // On the second failure, we should get called back with an empty access + // token. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + std::string())); + token_service()->IssueErrorForAllPendingRequestsForAccount( + "account", + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); +} + +#if !defined(OS_CHROMEOS) + +TEST_F(AccessTokenFetcherTest, + ShouldNotRetryCanceledAccessTokenRequestIfSignedOut) { + TestTokenCallback callback; + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher(callback.Get()); + + // Simulate the user signing out while the access token request is pending. + // In this case, the pending request gets canceled, and the fetcher should + // *not* retry. + signin_manager()->ForceSignOut(); + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + std::string())); + token_service()->IssueErrorForAllPendingRequestsForAccount( + "account", + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); +} + +#endif // !OS_CHROMEOS + +TEST_F(AccessTokenFetcherTest, + ShouldNotRetryCanceledAccessTokenRequestIfRefreshTokenRevoked) { + TestTokenCallback callback; + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher(callback.Get()); + + // Simulate the refresh token getting invalidated. In this case, pending + // access token requests get canceled, and the fetcher should *not* retry. + token_service()->GetDelegate()->RevokeCredentials("account"); + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED), + std::string())); + token_service()->IssueErrorForAllPendingRequestsForAccount( + "account", + GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED)); +} + +TEST_F(AccessTokenFetcherTest, ShouldNotRetryFailedAccessTokenRequest) { + TestTokenCallback callback; + + SignIn("account"); + token_service()->GetDelegate()->UpdateCredentials("account", "refresh token"); + + // Signed in and refresh token already exists, so this should result in a + // request for an access token. + auto fetcher = CreateFetcher(callback.Get()); + + // An access token failure other than "canceled" should not be retried; we + // should immediately get called back with an empty access token. + EXPECT_CALL( + callback, + Run(GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE), + std::string())); + token_service()->IssueErrorForAllPendingRequestsForAccount( + "account", + GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); +} diff --git a/chromium/components/signin/core/browser/account_tracker_service.cc b/chromium/components/signin/core/browser/account_tracker_service.cc index efe9e6e36c7..dd3f339588f 100644 --- a/chromium/components/signin/core/browser/account_tracker_service.cc +++ b/chromium/components/signin/core/browser/account_tracker_service.cc @@ -238,7 +238,7 @@ void AccountTrackerService::SetAccountStateFromUserInfo( state.info.picture_url = kNoPictureURLFound; } } - if (state.info.IsValid()) + if (!state.info.gaia.empty()) NotifyAccountUpdated(state); SaveToPrefs(state); } @@ -250,7 +250,7 @@ void AccountTrackerService::SetIsChildAccount(const std::string& account_id, if (state.info.is_child_account == is_child_account) return; state.info.is_child_account = is_child_account; - if (state.info.IsValid()) + if (!state.info.gaia.empty()) NotifyAccountUpdated(state); SaveToPrefs(state); } @@ -361,7 +361,7 @@ void AccountTrackerService::LoadFromPrefs() { if (dict->GetBoolean(kAccountChildAccountStatusPath, &is_child_account)) state.info.is_child_account = is_child_account; - if (state.info.IsValid()) + if (!state.info.gaia.empty()) NotifyAccountUpdated(state); } } @@ -496,11 +496,9 @@ std::string AccountTrackerService::SeedAccountInfo(AccountInfo info) { AccountState& state = accounts_[info.account_id]; // Update the missing fields in |state.info| with |info|. if (state.info.UpdateWith(info)) { - if (state.info.IsValid()) { - // Notify only if the account info is fully updated, as it is equivalent - // to having the account fully fetched. + if (!state.info.gaia.empty()) NotifyAccountUpdated(state); - } + SaveToPrefs(state); } return info.account_id; diff --git a/chromium/components/signin/core/browser/account_tracker_service_unittest.cc b/chromium/components/signin/core/browser/account_tracker_service_unittest.cc index b6847009f33..9fae742506b 100644 --- a/chromium/components/signin/core/browser/account_tracker_service_unittest.cc +++ b/chromium/components/signin/core/browser/account_tracker_service_unittest.cc @@ -678,13 +678,13 @@ TEST_F(AccountTrackerServiceTest, SeedAccountInfoFull) { info.full_name = AccountIdToFullName("alpha"); info.account_id = account_tracker()->SeedAccountInfo(info); - // Validate that seeding an unexisting account works and doesn't send a - // notification if the info isn't full. + // Validate that seeding an unexisting account works and sends a notification. AccountInfo stored_info = account_tracker()->GetAccountInfo(info.account_id); EXPECT_EQ(info.gaia, stored_info.gaia); EXPECT_EQ(info.email, stored_info.email); EXPECT_EQ(info.full_name, stored_info.full_name); - EXPECT_TRUE(observer.CheckEvents()); + EXPECT_TRUE( + observer.CheckEvents(TrackingEvent(UPDATED, info.account_id, info.gaia))); // Validate that seeding new full informations to an existing account works // and sends a notification. diff --git a/chromium/components/signin/core/browser/device_activity_fetcher.cc b/chromium/components/signin/core/browser/device_activity_fetcher.cc deleted file mode 100644 index 39b7433c8f5..00000000000 --- a/chromium/components/signin/core/browser/device_activity_fetcher.cc +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/signin/core/browser/device_activity_fetcher.h" - -#include "base/strings/stringprintf.h" -#include "components/signin/core/browser/signin_client.h" -#include "google_apis/gaia/gaia_auth_fetcher.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 "net/http/http_status_code.h" -#include "net/url_request/url_fetcher.h" - -namespace { - -// TODO(mlerman,anthonyvd): Replace the three parameters following with the -// necessary parameters for calling the ListDevices endpoint, once live. -// crbug.com/463611 for details! -const char kSyncListDevicesScope[] = - "https://www.googleapis.com/auth/chromesynclistdevices"; -const char kChromeDomain[] = "http://www.chrome.com"; -const char kListDevicesEndpoint[] = "http://127.0.0.1"; -// Template for optional authorization header when using an OAuth access token. -const char kAuthorizationHeader[] = "Authorization: Bearer %s"; - -// In case of an error while fetching using the GaiaAuthFetcher, retry with -// exponential backoff. Try up to 9 times within 10 minutes. -const net::BackoffEntry::Policy kBackoffPolicy = { - // Number of initial errors (in sequence) to ignore before applying - // exponential back-off rules. - 0, - // Initial delay for exponential backoff in ms. - 1000, - // Factor by which the waiting time will be multiplied. - 2, - // Fuzzing percentage. ex: 10% will spread requests randomly - // between 90%-100% of the calculated time. - 0.1, // 10% - // Maximum amount of time we are willing to delay our request in ms. - 1000 * 60 * 15, // 15 minutes. - // Time to keep an entry from being discarded even when it - // has no significant state, -1 to never discard. - -1, - // Don't use initial delay unless the last request was an error. - false, -}; - -const int kMaxFetcherRetries = 9; - -} // namespace - -DeviceActivityFetcher::DeviceActivityFetcher( - SigninClient* signin_client, - DeviceActivityFetcher::Observer* observer) - : fetcher_backoff_(&kBackoffPolicy), - fetcher_retries_(0), - signin_client_(signin_client), - observer_(observer) { -} - -DeviceActivityFetcher::~DeviceActivityFetcher() { -} - -void DeviceActivityFetcher::Start() { - fetcher_retries_ = 0; - login_hint_ = std::string(); - StartFetchingListIdpSessions(); -} - -void DeviceActivityFetcher::Stop() { - if (gaia_auth_fetcher_) - gaia_auth_fetcher_->CancelRequest(); - if (url_fetcher_) - url_fetcher_.reset(); -} - -void DeviceActivityFetcher::StartFetchingListIdpSessions() { - gaia_auth_fetcher_.reset(signin_client_->CreateGaiaAuthFetcher( - this, GaiaConstants::kChromeSource, - signin_client_->GetURLRequestContext())); - gaia_auth_fetcher_->StartListIDPSessions(kSyncListDevicesScope, - kChromeDomain); -} - -void DeviceActivityFetcher::StartFetchingGetTokenResponse() { - gaia_auth_fetcher_.reset(signin_client_->CreateGaiaAuthFetcher( - this, GaiaConstants::kChromeSource, - signin_client_->GetURLRequestContext())); - gaia_auth_fetcher_->StartGetTokenResponse(kSyncListDevicesScope, - kChromeDomain, login_hint_); -} - -void DeviceActivityFetcher::StartFetchingListDevices() { - // Call the Sync Endpoint. - url_fetcher_ = net::URLFetcher::Create(GURL(kListDevicesEndpoint), - net::URLFetcher::GET, this); - url_fetcher_->SetRequestContext(signin_client_->GetURLRequestContext()); - if (!access_token_.empty()) { - url_fetcher_->SetExtraRequestHeaders( - base::StringPrintf(kAuthorizationHeader, access_token_.c_str())); - } - url_fetcher_->Start(); -} - -void DeviceActivityFetcher::OnURLFetchComplete(const net::URLFetcher* source) { - // TODO(mlerman): Uncomment the code below once we have a working proto. - // Work done under crbug.com/463611 - - // std::string response_string; - // ListDevices list_devices; - - // if (!source->GetStatus().is_success()) { - // VLOG(1) << "Failed to fetch listdevices response. Retrying."; - // if (++fetcher_retries_ < kMaxFetcherRetries) { - // fetcher_backoff_.InformOfRequest(false); - // fetcher_timer_.Start( - // FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), this, - // &DeviceActivityFetcher::StartFetchingListDevices); - // return; - // } else { - // observer_->OnFetchDeviceActivityFailure(); - // return; - // } - // } - - // net::HttpStatusCode response_status = static_cast<net::HttpStatusCode>( - // source->GetResponseCode()); - // if (response_status == net::HTTP_BAD_REQUEST || - // response_status == net::HTTP_UNAUTHORIZED) { - // // BAD_REQUEST indicates that the request was malformed. - // // UNAUTHORIZED indicates that security token didn't match the id. - // VLOG(1) << "No point retrying the checkin with status: " - // << response_status << ". Checkin failed."; - // CheckinRequestStatus status = response_status == net::HTTP_BAD_REQUEST ? - // HTTP_BAD_REQUEST : HTTP_UNAUTHORIZED; - // RecordCheckinStatusAndReportUMA(status, recorder_, false); - // callback_.Run(response_proto); - // return; - // } - - // if (response_status != net::HTTP_OK || - // !source->GetResponseAsString(&response_string) || - // !list_devices.ParseFromString(response_string)) { - // LOG(ERROR) << "Failed to get list devices response. HTTP Status: " - // << response_status; - // if (++fetcher_retries_ < kMaxFetcherRetries) { - // fetcher_backoff_.InformOfRequest(false); - // fetcher_timer_.Start( - // FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), this, - // &DeviceActivityFetcher::StartFetchingListDevices); - // return; - // } else { - // observer_->OnFetchDeviceActivityFailure(); - // return; - // } - // } - - std::vector<DeviceActivity> devices; - // TODO(mlerman): Fill |devices| from the proto in |source|. - - // Call this last as OnFetchDeviceActivitySuccess will delete |this|. - observer_->OnFetchDeviceActivitySuccess(devices); -} - -void DeviceActivityFetcher::OnListIdpSessionsSuccess( - const std::string& login_hint) { - fetcher_backoff_.InformOfRequest(true); - login_hint_ = login_hint; - access_token_ = std::string(); - StartFetchingGetTokenResponse(); -} - -void DeviceActivityFetcher::OnListIdpSessionsError( - const GoogleServiceAuthError& error) { - if (++fetcher_retries_ < kMaxFetcherRetries && error.IsTransientError()) { - fetcher_backoff_.InformOfRequest(false); - fetcher_timer_.Start(FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), - this, - &DeviceActivityFetcher::StartFetchingListIdpSessions); - return; - } - observer_->OnFetchDeviceActivityFailure(); -} - -void DeviceActivityFetcher::OnGetTokenResponseSuccess( - const ClientOAuthResult& result) { - fetcher_backoff_.InformOfRequest(true); - access_token_ = result.access_token; - StartFetchingListDevices(); -} - -void DeviceActivityFetcher::OnGetTokenResponseError( - const GoogleServiceAuthError& error) { - if (++fetcher_retries_ < kMaxFetcherRetries && error.IsTransientError()) { - fetcher_backoff_.InformOfRequest(false); - fetcher_timer_.Start(FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), - this, - &DeviceActivityFetcher::StartFetchingGetTokenResponse); - return; - } - observer_->OnFetchDeviceActivityFailure(); -} diff --git a/chromium/components/signin/core/browser/device_activity_fetcher.h b/chromium/components/signin/core/browser/device_activity_fetcher.h deleted file mode 100644 index f91e7dfc672..00000000000 --- a/chromium/components/signin/core/browser/device_activity_fetcher.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_DEVICE_ACTVITIY_FETCHER_H_ -#define COMPONENTS_SIGNIN_CORE_BROWSER_DEVICE_ACTVITIY_FETCHER_H_ - -#include <memory> -#include <string> -#include <vector> - -#include "base/macros.h" -#include "base/time/time.h" -#include "base/timer/timer.h" -#include "google_apis/gaia/gaia_auth_consumer.h" -#include "net/base/backoff_entry.h" -#include "net/url_request/url_fetcher_delegate.h" - -class GaiaAuthFetcher; -class SigninClient; - -namespace net { -class URLFetcher; -} - -class DeviceActivityFetcher : public GaiaAuthConsumer, - public net::URLFetcherDelegate { - public: - struct DeviceActivity { - base::Time last_active; - std::string name; - }; - - class Observer { - public: - virtual void OnFetchDeviceActivitySuccess( - const std::vector<DeviceActivityFetcher::DeviceActivity>& devices) = 0; - virtual void OnFetchDeviceActivityFailure() {} - }; - - DeviceActivityFetcher(SigninClient* signin_client, - DeviceActivityFetcher::Observer* observer); - ~DeviceActivityFetcher() override; - - void Start(); - void Stop(); - - // GaiaAuthConsumer: - void OnListIdpSessionsSuccess(const std::string& login_hint) override; - void OnListIdpSessionsError(const GoogleServiceAuthError& error) override; - void OnGetTokenResponseSuccess(const ClientOAuthResult& result) override; - void OnGetTokenResponseError(const GoogleServiceAuthError& error) override; - - // net::URLFetcherDelegate: - void OnURLFetchComplete(const net::URLFetcher* source) override; - - private: - void StartFetchingListIdpSessions(); - void StartFetchingGetTokenResponse(); - void StartFetchingListDevices(); - - // Gaia fetcher used for acquiring an access token. - std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_; - // URL Fetcher used for calling List Devices. - std::unique_ptr<net::URLFetcher> url_fetcher_; - - // If either fetcher fails, retry with exponential backoff. - net::BackoffEntry fetcher_backoff_; - base::OneShotTimer fetcher_timer_; - int fetcher_retries_; - - std::string access_token_; - std::string login_hint_; - - SigninClient* signin_client_; // Weak pointer. - Observer* observer_; - - DISALLOW_COPY_AND_ASSIGN(DeviceActivityFetcher); -}; - -#endif // COMPONENTS_SIGNIN_CORE_BROWSER_DEVICE_ACTVITIY_FETCHER_H_ diff --git a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc index 876e8063f5c..c15a1c3510a 100644 --- a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc +++ b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/location.h" +#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "google_apis/gaia/fake_oauth2_token_service_delegate.h" @@ -19,11 +20,11 @@ FakeProfileOAuth2TokenService::PendingRequest::~PendingRequest() {} FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService() : FakeProfileOAuth2TokenService( - new FakeOAuth2TokenServiceDelegate(nullptr)) {} + base::MakeUnique<FakeOAuth2TokenServiceDelegate>(nullptr)) {} FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService( - OAuth2TokenServiceDelegate* delegate) - : ProfileOAuth2TokenService(delegate), + std::unique_ptr<OAuth2TokenServiceDelegate> delegate) + : ProfileOAuth2TokenService(std::move(delegate)), auto_post_fetch_response_on_message_loop_(false), weak_ptr_factory_(this) {} diff --git a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h index 043fc9a7cdb..f2301fdbd80 100644 --- a/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h +++ b/chromium/components/signin/core/browser/fake_profile_oauth2_token_service.h @@ -48,7 +48,8 @@ class FakeProfileOAuth2TokenService : public ProfileOAuth2TokenService { }; FakeProfileOAuth2TokenService(); - explicit FakeProfileOAuth2TokenService(OAuth2TokenServiceDelegate* delegate); + explicit FakeProfileOAuth2TokenService( + std::unique_ptr<OAuth2TokenServiceDelegate> delegate); ~FakeProfileOAuth2TokenService() override; // Gets a list of active requests (can be used by tests to validate that the diff --git a/chromium/components/signin/core/browser/fake_signin_manager.h b/chromium/components/signin/core/browser/fake_signin_manager.h index 83b2728e189..5ba63b81e53 100644 --- a/chromium/components/signin/core/browser/fake_signin_manager.h +++ b/chromium/components/signin/core/browser/fake_signin_manager.h @@ -17,7 +17,6 @@ // SigninManagerForTesting to ensure that the right type for their platform is // used. -// Overrides InitTokenService to do-nothing in tests. class FakeSigninManagerBase : public SigninManagerBase { public: FakeSigninManagerBase(SigninClient* client, diff --git a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc b/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc index 3d14c265528..28f30f1d2ff 100644 --- a/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc +++ b/chromium/components/signin/core/browser/gaia_cookie_manager_service.cc @@ -15,6 +15,7 @@ #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "base/values.h" +#include "components/data_use_measurement/core/data_use_user_data.h" #include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/signin_metrics.h" #include "google_apis/gaia/gaia_auth_fetcher.h" @@ -62,9 +63,6 @@ const int kMaxFetcherRetries = 8; // accounts have changed in the content-area. const char* kGaiaCookieName = "APISID"; -// String format appended to GAIA fetcher source if request context has changed. -const char* kRequestContextChangedTag = "__changed_%d__"; - enum GaiaCookieRequestType { ADD_ACCOUNT, LOG_OUT_ALL_ACCOUNTS, @@ -72,11 +70,6 @@ enum GaiaCookieRequestType { LIST_ACCOUNTS }; -void AppendRequestContextChangedTagIfNeeded(std::string* source, int changes) { - if (changes != 0) - base::StringAppendF(source, kRequestContextChangedTag, changes); -} - } // namespace GaiaCookieManagerService::GaiaCookieRequest::GaiaCookieRequest( @@ -223,6 +216,8 @@ GaiaCookieManagerService::ExternalCcResultFetcher::CreateFetcher( fetcher->SetRequestContext(helper_->request_context()); fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES); + data_use_measurement::DataUseUserData::AttachToFetcher( + fetcher.get(), data_use_measurement::DataUseUserData::SIGNIN); // Fetchers are sometimes cancelled because a network change was detected, // especially at startup and after sign-in on ChromeOS. @@ -402,7 +397,7 @@ void GaiaCookieManagerService::ForceOnCookieChangedProcessing() { std::unique_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create( google_url, kGaiaCookieName, std::string(), "." + google_url.host(), std::string(), base::Time(), base::Time(), false, false, - net::CookieSameSite::DEFAULT_MODE, false, net::COOKIE_PRIORITY_DEFAULT)); + net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT)); OnCookieChanged(*cookie, net::CookieStore::ChangeCause::UNKNOWN_DELETION); } @@ -473,19 +468,12 @@ void GaiaCookieManagerService::CancelAll() { std::string GaiaCookieManagerService::GetSourceForRequest( const GaiaCookieManagerService::GaiaCookieRequest& request) { - std::string source = request.source().empty() ? source_ : request.source(); - AppendRequestContextChangedTagIfNeeded( - &source, - signin_client_->number_of_request_context_pointer_changes()); - return source; + return request.source().empty() ? GetDefaultSourceForRequest() : + request.source(); } std::string GaiaCookieManagerService::GetDefaultSourceForRequest() { - std::string source = source_; - AppendRequestContextChangedTagIfNeeded( - &source, - signin_client_->number_of_request_context_pointer_changes()); - return source; + return source_; } void GaiaCookieManagerService::OnCookieChanged( @@ -506,17 +494,20 @@ void GaiaCookieManagerService::OnCookieChanged( case net::CookieStore::ChangeCause::INSERTED: source += "INSERTED"; break; - case net::CookieStore::ChangeCause::EXPLICIT_DELETE: - source += "EXPLICIT_DELETE"; + case net::CookieStore::ChangeCause::EXPLICIT: + source += "EXPLICIT"; + break; + case net::CookieStore::ChangeCause::EXPLICIT_DELETE_BETWEEN: + source += "EXPLICIT_DELETE_BETWEEN"; break; - case net::CookieStore::ChangeCause::EXPLICIT_DUPLICATE_IN_BACKING_STORE: - source += "EXPLICIT_DUPLICATE_IN_BACKING_STORE"; + case net::CookieStore::ChangeCause::EXPLICIT_DELETE_PREDICATE: + source += "EXPLICIT_DELETE_PREDICATE"; break; - case net::CookieStore::ChangeCause::EXPLICIT_DONT_RECORD: - source += "EXPLICIT_DONT_RECORD"; + case net::CookieStore::ChangeCause::EXPLICIT_DELETE_SINGLE: + source += "EXPLICIT_DELETE_SINGLE"; break; - case net::CookieStore::ChangeCause::EXPLICIT_LAST_ENTRY: - source += "EXPLICIT_LAST_ENTRY"; + case net::CookieStore::ChangeCause::EXPLICIT_DELETE_CANONICAL: + source += "EXPLICIT_DELETE_CANONICAL"; break; case net::CookieStore::ChangeCause::UNKNOWN_DELETION: source += "UNKNOWN_DELETION"; diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service.cc b/chromium/components/signin/core/browser/profile_oauth2_token_service.cc index b7fd8d01da3..7e8eb2a22c9 100644 --- a/chromium/components/signin/core/browser/profile_oauth2_token_service.cc +++ b/chromium/components/signin/core/browser/profile_oauth2_token_service.cc @@ -4,21 +4,9 @@ #include "components/signin/core/browser/profile_oauth2_token_service.h" -void ProfileOAuth2TokenService::OnRefreshTokenAvailable( - const std::string& account_id) { - CancelRequestsForAccount(account_id); - ClearCacheForAccount(account_id); -} - -void ProfileOAuth2TokenService::OnRefreshTokenRevoked( - const std::string& account_id) { - CancelRequestsForAccount(account_id); - ClearCacheForAccount(account_id); -} - ProfileOAuth2TokenService::ProfileOAuth2TokenService( - OAuth2TokenServiceDelegate* delegate) - : OAuth2TokenService(delegate) { + std::unique_ptr<OAuth2TokenServiceDelegate> delegate) + : OAuth2TokenService(std::move(delegate)) { AddObserver(this); } @@ -50,3 +38,15 @@ void ProfileOAuth2TokenService::RevokeCredentials( const net::BackoffEntry* ProfileOAuth2TokenService::GetDelegateBackoffEntry() { return GetDelegate()->BackoffEntry(); } + +void ProfileOAuth2TokenService::OnRefreshTokenAvailable( + const std::string& account_id) { + CancelRequestsForAccount(account_id); + ClearCacheForAccount(account_id); +} + +void ProfileOAuth2TokenService::OnRefreshTokenRevoked( + const std::string& account_id) { + CancelRequestsForAccount(account_id); + ClearCacheForAccount(account_id); +} diff --git a/chromium/components/signin/core/browser/profile_oauth2_token_service.h b/chromium/components/signin/core/browser/profile_oauth2_token_service.h index 199c7909ebc..fadf1417bd1 100644 --- a/chromium/components/signin/core/browser/profile_oauth2_token_service.h +++ b/chromium/components/signin/core/browser/profile_oauth2_token_service.h @@ -32,7 +32,8 @@ class ProfileOAuth2TokenService : public OAuth2TokenService, public OAuth2TokenService::Observer, public KeyedService { public: - ProfileOAuth2TokenService(OAuth2TokenServiceDelegate* delegate); + ProfileOAuth2TokenService( + std::unique_ptr<OAuth2TokenServiceDelegate> delegate); ~ProfileOAuth2TokenService() override; // KeyedService implementation. diff --git a/chromium/components/signin/core/browser/signin_client.cc b/chromium/components/signin/core/browser/signin_client.cc index f9018c85900..9473e0bb59c 100644 --- a/chromium/components/signin/core/browser/signin_client.cc +++ b/chromium/components/signin/core/browser/signin_client.cc @@ -36,10 +36,6 @@ void SigninClient::PreSignOut(const base::Callback<void()>& sign_out) { sign_out.Run(); } -int SigninClient::number_of_request_context_pointer_changes() const { - return 0; -} - void SigninClient::SignOut() { GetPrefs()->ClearPref(prefs::kGoogleServicesSigninScopedDeviceId); OnSignedOut(); diff --git a/chromium/components/signin/core/browser/signin_client.h b/chromium/components/signin/core/browser/signin_client.h index 43d90bc9d69..a4910c154df 100644 --- a/chromium/components/signin/core/browser/signin_client.h +++ b/chromium/components/signin/core/browser/signin_client.h @@ -126,10 +126,6 @@ class SigninClient : public KeyedService { // Called once the credentials has been copied to another SigninManager. virtual void AfterCredentialsCopied() {} - // Used do debug channel id binding problem in chrome. Returns the number of - // times the request context changed unexpectedly. - virtual int number_of_request_context_pointer_changes() const; - protected: // Returns device id that is scoped to single signin. // Stores the ID in the kGoogleServicesSigninScopedDeviceId pref. diff --git a/chromium/components/signin/core/browser/signin_header_helper.cc b/chromium/components/signin/core/browser/signin_header_helper.cc index 0d192ea5668..668d25d4492 100644 --- a/chromium/components/signin/core/browser/signin_header_helper.cc +++ b/chromium/components/signin/core/browser/signin_header_helper.cc @@ -164,8 +164,8 @@ bool SettingsAllowSigninCookies( GURL gaia_url = GaiaUrls::GetInstance()->gaia_url(); GURL google_url = GaiaUrls::GetInstance()->google_url(); return cookie_settings && - cookie_settings->IsSettingCookieAllowed(gaia_url, gaia_url) && - cookie_settings->IsSettingCookieAllowed(google_url, google_url); + cookie_settings->IsCookieAccessAllowed(gaia_url, gaia_url) && + cookie_settings->IsCookieAccessAllowed(google_url, google_url); } std::string BuildMirrorRequestCookieIfPossible( diff --git a/chromium/components/signin/core/browser/signin_internals_util.h b/chromium/components/signin/core/browser/signin_internals_util.h index 3dda94c9af2..03e5c6fa8db 100644 --- a/chromium/components/signin/core/browser/signin_internals_util.h +++ b/chromium/components/signin/core/browser/signin_internals_util.h @@ -69,13 +69,7 @@ class SigninDiagnosticsObserver { // Credentials and signin related changes. virtual void NotifySigninValueChanged(const TimedSigninStatusField& field, const std::string& value) {} - // OAuth tokens related changes. - virtual void NotifyTokenReceivedSuccess(const std::string& token_name, - const std::string& token, - bool update_time) {} - virtual void NotifyTokenReceivedFailure(const std::string& token_name, - const std::string& error) {} - virtual void NotifyClearStoredToken(const std::string& token_name) {}}; +}; // Gets the first 6 hex characters of the SHA256 hash of the passed in string. // These are enough to perform equality checks across a single users tokens, diff --git a/chromium/components/signin/core/browser/signin_manager.cc b/chromium/components/signin/core/browser/signin_manager.cc index 2e3a7044510..27fce040ba6 100644 --- a/chromium/components/signin/core/browser/signin_manager.cc +++ b/chromium/components/signin/core/browser/signin_manager.cc @@ -409,6 +409,9 @@ void SigninManager::PostSignedIn() { } void SigninManager::OnAccountUpdated(const AccountInfo& info) { + if (!info.IsValid()) + return; + user_info_fetched_by_account_tracker_ = true; PostSignedIn(); } diff --git a/chromium/components/signin/core/browser/signin_manager_base.cc b/chromium/components/signin/core/browser/signin_manager_base.cc index 68d68b477b4..b36d21d0f56 100644 --- a/chromium/components/signin/core/browser/signin_manager_base.cc +++ b/chromium/components/signin/core/browser/signin_manager_base.cc @@ -216,6 +216,10 @@ void SigninManagerBase::SetAuthenticatedAccountId( account_id); client_->GetPrefs()->SetString(prefs::kGoogleServicesLastUsername, info.email); + + // Commit authenticated account info immediately so that it does not get lost + // if Chrome crashes before the next commit interval. + client_->GetPrefs()->CommitPendingWrite(); } bool SigninManagerBase::IsAuthenticated() const { diff --git a/chromium/components/signin/core/browser/webdata/token_service_table.cc b/chromium/components/signin/core/browser/webdata/token_service_table.cc index d1ccb08da29..8c9075c9e5a 100644 --- a/chromium/components/signin/core/browser/webdata/token_service_table.cc +++ b/chromium/components/signin/core/browser/webdata/token_service_table.cc @@ -8,6 +8,7 @@ #include <string> #include "base/logging.h" +#include "base/metrics/histogram_macros.h" #include "components/os_crypt/os_crypt.h" #include "components/webdata/common/web_database.h" #include "sql/statement.h" @@ -21,6 +22,14 @@ WebDatabaseTable::TypeKey GetKey() { return reinterpret_cast<void*>(&table_key); } +// Entries in the |Signin.TokenTable.ReadTokenFromDBResult| histogram. +enum ReadOneTokenResult { + READ_ONE_TOKEN_SUCCESS, + READ_ONE_TOKEN_DB_SUCCESS_DECRYPT_FAILED, + READ_ONE_TOKEN_DB_FAILED_BAD_ENTRY, + READ_ONE_TOKEN_MAX_VALUE +}; + } // namespace TokenServiceTable* TokenServiceTable::FromWebDatabase(WebDatabase* db) { @@ -88,15 +97,23 @@ bool TokenServiceTable::SetTokenForService(const std::string& service, return s.Run(); } -bool TokenServiceTable::GetAllTokens( +TokenServiceTable::Result TokenServiceTable::GetAllTokens( std::map<std::string, std::string>* tokens) { sql::Statement s(db_->GetUniqueStatement( "SELECT service, encrypted_token FROM token_service")); - if (!s.is_valid()) - return false; + UMA_HISTOGRAM_BOOLEAN("Signin.TokenTable.GetAllTokensSqlStatementValidity", + s.is_valid()); + + if (!s.is_valid()) { + LOG(ERROR) << "Failed to load tokens (invalid SQL statement)."; + return TOKEN_DB_RESULT_SQL_INVALID_STATEMENT; + } + Result read_all_tokens_result = TOKEN_DB_RESULT_SUCCESS; while (s.Step()) { + ReadOneTokenResult read_token_result = READ_ONE_TOKEN_MAX_VALUE; + std::string encrypted_token; std::string decrypted_token; std::string service; @@ -104,12 +121,25 @@ bool TokenServiceTable::GetAllTokens( bool entry_ok = !service.empty() && s.ColumnBlobAsString(1, &encrypted_token); if (entry_ok) { - OSCrypt::DecryptString(encrypted_token, &decrypted_token); - (*tokens)[service] = decrypted_token; + if (OSCrypt::DecryptString(encrypted_token, &decrypted_token)) { + (*tokens)[service] = decrypted_token; + read_token_result = READ_ONE_TOKEN_SUCCESS; + } else { + // Chrome relies on native APIs to encrypt and decrypt the tokens which + // may fail (see http://crbug.com/686485). + LOG(ERROR) << "Failed to decrypt token for service " << service; + read_token_result = READ_ONE_TOKEN_DB_SUCCESS_DECRYPT_FAILED; + read_all_tokens_result = TOKEN_DB_RESULT_DECRYPT_ERROR; + } } else { - NOTREACHED(); - return false; + LOG(ERROR) << "Bad token entry for service " << service; + read_token_result = READ_ONE_TOKEN_DB_FAILED_BAD_ENTRY; + read_all_tokens_result = TOKEN_DB_RESULT_BAD_ENTRY; } + DCHECK_LT(read_token_result, READ_ONE_TOKEN_MAX_VALUE); + UMA_HISTOGRAM_ENUMERATION("Signin.TokenTable.ReadTokenFromDBResult", + read_token_result, + READ_ONE_TOKEN_MAX_VALUE); } - return true; + return read_all_tokens_result; } diff --git a/chromium/components/signin/core/browser/webdata/token_service_table.h b/chromium/components/signin/core/browser/webdata/token_service_table.h index 70cf20e93dc..b6d750f0ce5 100644 --- a/chromium/components/signin/core/browser/webdata/token_service_table.h +++ b/chromium/components/signin/core/browser/webdata/token_service_table.h @@ -16,6 +16,13 @@ class WebDatabase; class TokenServiceTable : public WebDatabaseTable { public: + enum Result { + TOKEN_DB_RESULT_SQL_INVALID_STATEMENT, + TOKEN_DB_RESULT_BAD_ENTRY, + TOKEN_DB_RESULT_DECRYPT_ERROR, + TOKEN_DB_RESULT_SUCCESS + }; + TokenServiceTable() {} ~TokenServiceTable() override {} @@ -36,7 +43,7 @@ class TokenServiceTable : public WebDatabaseTable { // Retrieves all tokens previously set with SetTokenForService. // Returns true if there were tokens and we decrypted them, // false if there was a failure somehow - bool GetAllTokens(std::map<std::string, std::string>* tokens); + Result GetAllTokens(std::map<std::string, std::string>* tokens); // Store a token in the token_service table. Stored encrypted. May cause // a mac keychain popup. diff --git a/chromium/components/signin/core/browser/webdata/token_web_data.cc b/chromium/components/signin/core/browser/webdata/token_web_data.cc index 0c6425529c0..d8c405f0605 100644 --- a/chromium/components/signin/core/browser/webdata/token_web_data.cc +++ b/chromium/components/signin/core/browser/webdata/token_web_data.cc @@ -47,10 +47,10 @@ class TokenWebDataBackend } std::unique_ptr<WDTypedResult> GetAllTokens(WebDatabase* db) { - std::map<std::string, std::string> map; - TokenServiceTable::FromWebDatabase(db)->GetAllTokens(&map); - return base::MakeUnique<WDResult<std::map<std::string, std::string>>>( - TOKEN_RESULT, map); + TokenResult result; + result.db_result = + TokenServiceTable::FromWebDatabase(db)->GetAllTokens(&result.tokens); + return base::MakeUnique<WDResult<TokenResult>>(TOKEN_RESULT, result); } protected: @@ -62,6 +62,11 @@ class TokenWebDataBackend friend class base::DeleteHelper<TokenWebDataBackend>; }; +TokenResult::TokenResult() + : db_result(TokenServiceTable::TOKEN_DB_RESULT_SQL_INVALID_STATEMENT) {} +TokenResult::TokenResult(const TokenResult& other) = default; +TokenResult::~TokenResult(){}; + TokenWebData::TokenWebData( scoped_refptr<WebDatabaseService> wdbs, scoped_refptr<base::SingleThreadTaskRunner> ui_thread, diff --git a/chromium/components/signin/core/browser/webdata/token_web_data.h b/chromium/components/signin/core/browser/webdata/token_web_data.h index ceec9cdcc86..f47f371c252 100644 --- a/chromium/components/signin/core/browser/webdata/token_web_data.h +++ b/chromium/components/signin/core/browser/webdata/token_web_data.h @@ -18,6 +18,7 @@ #include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "components/signin/core/browser/webdata/token_service_table.h" #include "components/webdata/common/web_data_results.h" #include "components/webdata/common/web_data_service_base.h" #include "components/webdata/common/web_data_service_consumer.h" @@ -31,6 +32,16 @@ class TokenWebDataBackend; class WebDatabaseService; class WebDataServiceConsumer; +// The result of a get tokens operation. +struct TokenResult { + TokenResult(); + TokenResult(const TokenResult& other); + ~TokenResult(); + + TokenServiceTable::Result db_result; + std::map<std::string, std::string> tokens; +}; + // TokenWebData is a data repository for storage of authentication tokens. class TokenWebData : public WebDataServiceBase { |