diff options
author | rsimha@chromium.org <rsimha@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-02 19:47:43 +0000 |
---|---|---|
committer | rsimha@chromium.org <rsimha@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-02 19:47:43 +0000 |
commit | 43c48fb9f34da682482635dae6ebf1b3dcdae560 (patch) | |
tree | 7fbbcf193aaac9e248513fbf96a3264679e047e3 | |
parent | 7c4e90901e7f3b641672f90a9f8080320dbfceec (diff) | |
download | chromium_src-43c48fb9f34da682482635dae6ebf1b3dcdae560.zip chromium_src-43c48fb9f34da682482635dae6ebf1b3dcdae560.tar.gz chromium_src-43c48fb9f34da682482635dae6ebf1b3dcdae560.tar.bz2 |
Refactor sync passphrase setup flow and fix passphrase tests
The passphrase sync integration tests are in a state of disrepair due to
several recent changes to the sync implementation. In particular,
passphrase sync uses two similar methods called OnPassphraseRequired
and OnPassphraseFailed to differentiate between cases where a passphrase is
required for a first attempt at decryption and cases where decryption with a
cached passphrase failed and the user needs to be prompted.
This patch refactors the "for_decryption" boolean flag into an enum called
PassphraseRequiredReason, thereby eliminating the need for OnPassphraseFailed.
It also fixes the tests and updates the test framework to account for
scenarios in which a client is waiting for a passphrase that has been
set by one of its partners, and enables it to exit early when forward
progress is impossible without a call to SetPassphrase.
BUG=78840, 80180, 81018
TEST=sync_integration_tests
Review URL: http://codereview.chromium.org/6902101
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83764 0039d316-1c4b-4281-b951-d872f2087c98
21 files changed, 241 insertions, 165 deletions
diff --git a/chrome/browser/resources/sync_internals/chrome_sync.js b/chrome/browser/resources/sync_internals/chrome_sync.js index 0340f63..4b452742 100644 --- a/chrome/browser/resources/sync_internals/chrome_sync.js +++ b/chrome/browser/resources/sync_internals/chrome_sync.js @@ -180,8 +180,8 @@ function onUpdatedToken(token) { chrome.sync.onUpdatedToken.dispatch_(token); } -function onPassphraseRequired(forDecryption) { - chrome.sync.onPassphraseRequired.dispatch_(forDecryption); +function onPassphraseRequired(reason) { + chrome.sync.onPassphraseRequired.dispatch_(reason); } function onPassphraseAccepted(bootstrapToken) { diff --git a/chrome/browser/resources/sync_internals/sync_log.js b/chrome/browser/resources/sync_internals/sync_log.js index 704fd5a..35a6f2f 100644 --- a/chrome/browser/resources/sync_internals/sync_log.js +++ b/chrome/browser/resources/sync_internals/sync_log.js @@ -75,9 +75,9 @@ cr.define('chrome.sync', function() { }); }); - chrome.sync.onPassphraseRequired.addListener(function (forDecryption) { + chrome.sync.onPassphraseRequired.addListener(function (reason) { self.log_('manager', 'onPassphraseRequired', { - forDecryption: forDecryption + reason: reason }); }); diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index 102c49c0..43748a1 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -171,6 +171,24 @@ static void ServerNameToSyncAPIName(const std::string& server_name, } } +// Helper function that converts a PassphraseRequiredReason value to a string. +std::string PassphraseRequiredReasonToString( + PassphraseRequiredReason reason) { + switch (reason) { + case REASON_PASSPHRASE_NOT_REQUIRED: + return "REASON_PASSPHRASE_NOT_REQUIRED"; + case REASON_ENCRYPTION: + return "REASON_ENCRYPTION"; + case REASON_DECRYPTION: + return "REASON_DECRYPTION"; + case REASON_SET_PASSPHRASE_FAILED: + return "REASON_SET_PASSPHRASE_FAILED"; + default: + NOTREACHED(); + return "INVALID_REASON"; + } +} + UserShare::UserShare() {} UserShare::~UserShare() {} @@ -1790,7 +1808,7 @@ void SyncManager::SyncInternal::BootstrapEncryption( } else { cryptographer->SetPendingKeys(nigori.encrypted()); FOR_EACH_OBSERVER(SyncManager::Observer, observers_, - OnPassphraseRequired(true)); + OnPassphraseRequired(sync_api::REASON_DECRYPTION)); } } } @@ -1952,7 +1970,7 @@ void SyncManager::SyncInternal::SetPassphrase( if (!cryptographer->DecryptPendingKeys(params)) { VLOG(1) << "Passphrase failed to decrypt pending keys."; FOR_EACH_OBSERVER(SyncManager::Observer, observers_, - OnPassphraseFailed()); + OnPassphraseRequired(sync_api::REASON_SET_PASSPHRASE_FAILED)); return; } @@ -2494,10 +2512,10 @@ void SyncManager::SyncInternal::OnSyncEngineEvent( // yet, prompt the user for a passphrase. if (cryptographer->has_pending_keys()) { FOR_EACH_OBSERVER(SyncManager::Observer, observers_, - OnPassphraseRequired(true)); + OnPassphraseRequired(sync_api::REASON_DECRYPTION)); } else if (!cryptographer->is_ready()) { FOR_EACH_OBSERVER(SyncManager::Observer, observers_, - OnPassphraseRequired(false)); + OnPassphraseRequired(sync_api::REASON_ENCRYPTION)); } else { FOR_EACH_OBSERVER(SyncManager::Observer, observers_, OnEncryptionComplete(encrypted_types)); diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h index ff74f19..75c4c2a 100644 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -107,6 +107,25 @@ class HttpPostProviderFactory; class SyncManager; class WriteTransaction; +// Reasons due to which browser_sync::Cryptographer might require a passphrase. +enum PassphraseRequiredReason { + REASON_PASSPHRASE_NOT_REQUIRED = 0, // Initial value. + REASON_ENCRYPTION = 1, // The cryptographer requires a + // passphrase for its first attempt at + // encryption. Happens only during + // migration or upgrade. + REASON_DECRYPTION = 2, // The cryptographer requires a + // passphrase for its first attempt at + // decryption. + REASON_SET_PASSPHRASE_FAILED = 3, // The cryptographer requires a new + // passphrase because its attempt at + // decryption with the cached passphrase + // was unsuccessful. +}; + +// Returns the string representation of a PassphraseRequiredReason value. +std::string PassphraseRequiredReasonToString(PassphraseRequiredReason reason); + // A UserShare encapsulates the syncable pieces that represent an authenticated // user and their data (share). // This encompasses all pieces required to build transaction objects on the @@ -779,18 +798,13 @@ class SyncManager { virtual void OnUpdatedToken(const std::string& token) = 0; // Called when user interaction is required to obtain a valid passphrase. - // If the passphrase is required to decrypt something that has - // already been encrypted (and thus has to match the existing key), - // |for_decryption| will be true. If the passphrase is needed for - // encryption, |for_decryption| will be false. - virtual void OnPassphraseRequired(bool for_decryption) = 0; - - // Called only by SyncInternal::SetPassphrase to indiciate that an attempted - // passphrase failed to decrypt pending keys. This is different from - // OnPassphraseRequired in that it denotes we finished an attempt to set - // a passphrase. OnPassphraseRequired means we have data we could not - // decrypt yet, and can come from numerous places. - virtual void OnPassphraseFailed() = 0; + // - If the passphrase is required for encryption, |reason| will be + // REASON_ENCRYPTION. + // - If the passphrase is required for the decryption of data that has + // already been encrypted, |reason| will be REASON_DECRYPTION. + // - If the passphrase is required because decryption failed, and a new + // passphrase is required, |reason| will be REASON_SET_PASSPHRASE_FAILED. + virtual void OnPassphraseRequired(PassphraseRequiredReason reason) = 0; // Called when the passphrase provided by the user has been accepted and is // now used to encrypt sync data. |bootstrap_token| is an opaque base64 diff --git a/chrome/browser/sync/engine/syncapi_unittest.cc b/chrome/browser/sync/engine/syncapi_unittest.cc index 15d509f..465a642 100644 --- a/chrome/browser/sync/engine/syncapi_unittest.cc +++ b/chrome/browser/sync/engine/syncapi_unittest.cc @@ -600,8 +600,8 @@ class SyncManagerObserverMock : public SyncManager::Observer { void(const SyncSessionSnapshot*)); // NOLINT MOCK_METHOD0(OnInitializationComplete, void()); // NOLINT MOCK_METHOD1(OnAuthError, void(const GoogleServiceAuthError&)); // NOLINT - MOCK_METHOD1(OnPassphraseRequired, void(bool)); // NOLINT - MOCK_METHOD0(OnPassphraseFailed, void()); // NOLINT + MOCK_METHOD1(OnPassphraseRequired, + void(sync_api::PassphraseRequiredReason)); // NOLINT MOCK_METHOD1(OnPassphraseAccepted, void(const std::string&)); // NOLINT MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc index ed56a2e..688a4fa 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -528,30 +528,24 @@ bool SyncBackendHost::RequestClearServerData() { SyncBackendHost::Core::~Core() { } -void SyncBackendHost::Core::NotifyPassphraseRequired(bool for_decryption) { +void SyncBackendHost::Core::NotifyPassphraseRequired( + sync_api::PassphraseRequiredReason reason) { if (!host_ || !host_->frontend_) return; DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); + // When setting a passphrase fails, unset our waiting flag. + if (reason == sync_api::REASON_SET_PASSPHRASE_FAILED) + processing_passphrase_ = false; + if (processing_passphrase_) { VLOG(1) << "Core received OnPassphraseRequired while processing a " << "passphrase. Silently dropping."; return; } - host_->frontend_->OnPassphraseRequired(for_decryption); -} - -void SyncBackendHost::Core::NotifyPassphraseFailed() { - if (!host_ || !host_->frontend_) - return; - DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); - - // When a passphrase fails, we just unset our waiting flag and trigger a - // OnPassphraseRequired(true). - processing_passphrase_ = false; - host_->frontend_->OnPassphraseRequired(true); + host_->frontend_->OnPassphraseRequired(reason); } void SyncBackendHost::Core::NotifyPassphraseAccepted( @@ -972,14 +966,10 @@ void SyncBackendHost::Core::OnAuthError(const AuthError& auth_error) { auth_error)); } -void SyncBackendHost::Core::OnPassphraseRequired(bool for_decryption) { - host_->frontend_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &Core::NotifyPassphraseRequired, for_decryption)); -} - -void SyncBackendHost::Core::OnPassphraseFailed() { +void SyncBackendHost::Core::OnPassphraseRequired( + sync_api::PassphraseRequiredReason reason) { host_->frontend_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &Core::NotifyPassphraseFailed)); + NewRunnableMethod(this, &Core::NotifyPassphraseRequired, reason)); } void SyncBackendHost::Core::OnPassphraseAccepted( diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h index b5c2b70..007e106 100644 --- a/chrome/browser/sync/glue/sync_backend_host.h +++ b/chrome/browser/sync/glue/sync_backend_host.h @@ -79,13 +79,12 @@ class SyncFrontend { virtual void OnClearServerDataSucceeded() = 0; virtual void OnClearServerDataFailed() = 0; - // The syncer requires a passphrase to decrypt sensitive - // updates. This is called when the first sensitive data type is - // setup by the user as well as anytime any the passphrase is - // changed in another synced client. if - // |passphrase_required_for_decryption| is false, the passphrase is - // required only for encryption. - virtual void OnPassphraseRequired(bool for_decryption) = 0; + // The syncer requires a passphrase to decrypt sensitive updates. This is + // called when the first sensitive data type is setup by the user and anytime + // the passphrase is changed by another synced client. |reason| denotes why + // the passphrase was required. + virtual void OnPassphraseRequired( + sync_api::PassphraseRequiredReason reason) = 0; // Called when the passphrase provided by the user is // accepted. After this is called, updates to sensitive nodes are @@ -277,8 +276,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { const sessions::SyncSessionSnapshot* snapshot); virtual void OnInitializationComplete(); virtual void OnAuthError(const GoogleServiceAuthError& auth_error); - virtual void OnPassphraseRequired(bool for_decryption); - virtual void OnPassphraseFailed(); + virtual void OnPassphraseRequired( + sync_api::PassphraseRequiredReason reason); virtual void OnPassphraseAccepted(const std::string& bootstrap_token); virtual void OnStopSyncingPermanently(); virtual void OnUpdatedToken(const std::string& token); @@ -356,7 +355,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { void DoSetPassphrase(const std::string& passphrase, bool is_explicit); // Getter/setter for whether we are waiting on SetPassphrase to process a - // passphrase. Set by SetPassphrase, cleared by OnPassphraseFailed or + // passphrase. Set by SetPassphrase, cleared by OnPassphraseRequired or // OnPassphraseAccepted. bool processing_passphrase() const; void set_processing_passphrase(); @@ -450,14 +449,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { const GoogleServiceAuthError& new_auth_error); // Invoked when a passphrase is required to decrypt a set of Nigori keys, - // or for encrypting. If the reason is decryption, |for_decryption| will - // be true. - void NotifyPassphraseRequired(bool for_decryption); - - // Invoked when the syncer attempts to set a passphrase but fails to decrypt - // the cryptographer's pending keys. This tells the profile sync service - // that a new passphrase is required. - void NotifyPassphraseFailed(); + // or for encrypting. |reason| denotes why the passhrase was required. + void NotifyPassphraseRequired(sync_api::PassphraseRequiredReason reason); // Invoked when the passphrase provided by the user has been accepted. void NotifyPassphraseAccepted(const std::string& bootstrap_token); diff --git a/chrome/browser/sync/glue/sync_backend_host_unittest.cc b/chrome/browser/sync/glue/sync_backend_host_unittest.cc index b33977c..71d3974 100644 --- a/chrome/browser/sync/glue/sync_backend_host_unittest.cc +++ b/chrome/browser/sync/glue/sync_backend_host_unittest.cc @@ -36,7 +36,7 @@ class MockSyncFrontend : public SyncFrontend { MOCK_METHOD0(OnStopSyncingPermanently, void()); MOCK_METHOD0(OnClearServerDataSucceeded, void()); MOCK_METHOD0(OnClearServerDataFailed, void()); - MOCK_METHOD1(OnPassphraseRequired, void(bool)); + MOCK_METHOD1(OnPassphraseRequired, void(sync_api::PassphraseRequiredReason)); MOCK_METHOD0(OnPassphraseAccepted, void()); MOCK_METHOD1(OnEncryptionComplete, void(const syncable::ModelTypeSet&)); MOCK_METHOD1(OnMigrationNeededForTypes, void(const syncable::ModelTypeSet&)); diff --git a/chrome/browser/sync/js_sync_manager_observer.cc b/chrome/browser/sync/js_sync_manager_observer.cc index ef9f34d..a86178a 100644 --- a/chrome/browser/sync/js_sync_manager_observer.cc +++ b/chrome/browser/sync/js_sync_manager_observer.cc @@ -71,17 +71,16 @@ void JsSyncManagerObserver::OnUpdatedToken(const std::string& token) { JsArgList(return_args), NULL); } -void JsSyncManagerObserver::OnPassphraseRequired(bool for_decryption) { +void JsSyncManagerObserver::OnPassphraseRequired( + sync_api::PassphraseRequiredReason reason) { ListValue return_args; - return_args.Append(Value::CreateBooleanValue(for_decryption)); + + return_args.Append(Value::CreateStringValue( + sync_api::PassphraseRequiredReasonToString(reason))); parent_router_->RouteJsEvent("onPassphraseRequired", JsArgList(return_args), NULL); } -void JsSyncManagerObserver::OnPassphraseFailed() { - parent_router_->RouteJsEvent("onPassphraseFailed", JsArgList(), NULL); -} - void JsSyncManagerObserver::OnPassphraseAccepted( const std::string& bootstrap_token) { ListValue return_args; diff --git a/chrome/browser/sync/js_sync_manager_observer.h b/chrome/browser/sync/js_sync_manager_observer.h index 9436f52..597b3a8 100644 --- a/chrome/browser/sync/js_sync_manager_observer.h +++ b/chrome/browser/sync/js_sync_manager_observer.h @@ -34,8 +34,7 @@ class JsSyncManagerObserver : public sync_api::SyncManager::Observer { const sessions::SyncSessionSnapshot* snapshot); virtual void OnAuthError(const GoogleServiceAuthError& auth_error); virtual void OnUpdatedToken(const std::string& token); - virtual void OnPassphraseRequired(bool for_decryption); - virtual void OnPassphraseFailed(); + virtual void OnPassphraseRequired(sync_api::PassphraseRequiredReason reason); virtual void OnPassphraseAccepted(const std::string& bootstrap_token); virtual void OnEncryptionComplete( const syncable::ModelTypeSet& encrypted_types); diff --git a/chrome/browser/sync/js_sync_manager_observer_unittest.cc b/chrome/browser/sync/js_sync_manager_observer_unittest.cc index 3631963..d90c243 100644 --- a/chrome/browser/sync/js_sync_manager_observer_unittest.cc +++ b/chrome/browser/sync/js_sync_manager_observer_unittest.cc @@ -37,10 +37,6 @@ TEST_F(JsSyncManagerObserverTest, NoArgNotifiations) { RouteJsEvent("onInitializationComplete", HasArgs(JsArgList()), NULL)); EXPECT_CALL(mock_router_, - RouteJsEvent("onPassphraseFailed", - HasArgs(JsArgList()), NULL)); - - EXPECT_CALL(mock_router_, RouteJsEvent("onStopSyncingPermanently", HasArgs(JsArgList()), NULL)); EXPECT_CALL(mock_router_, @@ -51,7 +47,6 @@ TEST_F(JsSyncManagerObserverTest, NoArgNotifiations) { HasArgs(JsArgList()), NULL)); sync_manager_observer_.OnInitializationComplete(); - sync_manager_observer_.OnPassphraseFailed(); sync_manager_observer_.OnStopSyncingPermanently(); sync_manager_observer_.OnClearServerDataSucceeded(); sync_manager_observer_.OnClearServerDataFailed(); @@ -116,19 +111,43 @@ TEST_F(JsSyncManagerObserverTest, OnAuthError) { TEST_F(JsSyncManagerObserverTest, OnPassphraseRequired) { InSequence dummy; - ListValue true_args, false_args; - true_args.Append(Value::CreateBooleanValue(true)); - false_args.Append(Value::CreateBooleanValue(false)); + ListValue reason_passphrase_not_required_args; + ListValue reason_encryption_args; + ListValue reason_decryption_args; + ListValue reason_set_passphrase_failed_args; + + reason_passphrase_not_required_args.Append(Value::CreateStringValue( + sync_api::PassphraseRequiredReasonToString( + sync_api::REASON_PASSPHRASE_NOT_REQUIRED))); + reason_encryption_args.Append(Value::CreateStringValue( + sync_api::PassphraseRequiredReasonToString(sync_api::REASON_ENCRYPTION))); + reason_decryption_args.Append(Value::CreateStringValue( + sync_api::PassphraseRequiredReasonToString(sync_api::REASON_DECRYPTION))); + reason_set_passphrase_failed_args.Append(Value::CreateStringValue( + sync_api::PassphraseRequiredReasonToString( + sync_api::REASON_SET_PASSPHRASE_FAILED))); EXPECT_CALL(mock_router_, RouteJsEvent("onPassphraseRequired", - HasArgsAsList(false_args), NULL)); + HasArgsAsList(reason_passphrase_not_required_args), + NULL)); EXPECT_CALL(mock_router_, RouteJsEvent("onPassphraseRequired", - HasArgsAsList(true_args), NULL)); - - sync_manager_observer_.OnPassphraseRequired(false); - sync_manager_observer_.OnPassphraseRequired(true); + HasArgsAsList(reason_encryption_args), NULL)); + EXPECT_CALL(mock_router_, + RouteJsEvent("onPassphraseRequired", + HasArgsAsList(reason_decryption_args), NULL)); + EXPECT_CALL(mock_router_, + RouteJsEvent("onPassphraseRequired", + HasArgsAsList(reason_set_passphrase_failed_args), + NULL)); + + sync_manager_observer_.OnPassphraseRequired( + sync_api::REASON_PASSPHRASE_NOT_REQUIRED); + sync_manager_observer_.OnPassphraseRequired(sync_api::REASON_ENCRYPTION); + sync_manager_observer_.OnPassphraseRequired(sync_api::REASON_DECRYPTION); + sync_manager_observer_.OnPassphraseRequired( + sync_api::REASON_SET_PASSPHRASE_FAILED); } TEST_F(JsSyncManagerObserverTest, SensitiveNotifiations) { diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index d6347d8..db2fd10 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -68,8 +68,7 @@ ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory, Profile* profile, const std::string& cros_user) : last_auth_error_(AuthError::None()), - observed_passphrase_required_(false), - passphrase_required_for_decryption_(false), + passphrase_required_reason_(sync_api::REASON_PASSPHRASE_NOT_REQUIRED), passphrase_migration_in_progress_(false), factory_(factory), profile_(profile), @@ -379,7 +378,7 @@ void ProfileSyncService::Shutdown(bool sync_disabled) { // Clear various flags. is_auth_in_progress_ = false; backend_initialized_ = false; - observed_passphrase_required_ = false; + passphrase_required_reason_ = sync_api::REASON_PASSPHRASE_NOT_REQUIRED; last_attempted_user_email_.clear(); last_auth_error_ = GoogleServiceAuthError::None(); } @@ -592,7 +591,8 @@ void ProfileSyncService::OnClearServerDataSucceeded() { } } -void ProfileSyncService::OnPassphraseRequired(bool for_decryption) { +void ProfileSyncService::OnPassphraseRequired( + sync_api::PassphraseRequiredReason reason) { DCHECK(backend_.get()); DCHECK(backend_->IsNigoriEnabled()); @@ -602,8 +602,8 @@ void ProfileSyncService::OnPassphraseRequired(bool for_decryption) { // backend. The task might not have executed yet. return; } - observed_passphrase_required_ = true; - passphrase_required_for_decryption_ = for_decryption; + + passphrase_required_reason_ = reason; if (!cached_passphrase_.value.empty()) { SetPassphrase(cached_passphrase_.value, @@ -613,16 +613,19 @@ void ProfileSyncService::OnPassphraseRequired(bool for_decryption) { return; } - // We will skip the passphrase prompt and suppress the warning - // if the passphrase is needed for decryption but the user is - // not syncing an encrypted data type on this machine. - // Otherwise we prompt. - if (!IsEncryptedDatatypeEnabled() && for_decryption) { + // We will skip the passphrase prompt and suppress the warning if the + // passphrase is needed for decryption but the user is not syncing an + // encrypted data type on this machine. Otherwise we prompt. + if (!IsEncryptedDatatypeEnabled() && + (reason == sync_api::REASON_DECRYPTION || + reason == sync_api::REASON_SET_PASSPHRASE_FAILED)) { OnPassphraseAccepted(); return; } - if (WizardIsVisible() && for_decryption) { + if (WizardIsVisible() && + (reason == sync_api::REASON_DECRYPTION || + reason == sync_api::REASON_SET_PASSPHRASE_FAILED)) { wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE); } @@ -634,9 +637,11 @@ void ProfileSyncService::OnPassphraseAccepted() { // this time. syncable::ModelTypeSet types; GetPreferredDataTypes(&types); - // Reset "passphrase_required" flag before configuring the DataTypeManager + + // Reset passphrase_required_reason_ before configuring the DataTypeManager // since we know we no longer require the passphrase. - observed_passphrase_required_ = false; + passphrase_required_reason_ = sync_api::REASON_PASSPHRASE_NOT_REQUIRED; + if (data_type_manager_.get()) data_type_manager_->Configure(types); @@ -684,7 +689,7 @@ void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) { } void ProfileSyncService::ShowErrorUI(gfx::NativeWindow parent_window) { - if (observed_passphrase_required()) { + if (ObservedPassphraseRequired()) { if (IsUsingSecondaryPassphrase()) PromptForExistingPassphrase(parent_window); else @@ -940,7 +945,7 @@ void ProfileSyncService::GetRegisteredDataTypes( bool ProfileSyncService::IsUsingSecondaryPassphrase() const { return backend_.get() && (backend_->IsUsingExplicitPassphrase() || (tried_implicit_gaia_remove_when_bug_62103_fixed_ && - observed_passphrase_required_)); + ObservedPassphraseRequired())); } bool ProfileSyncService::IsCryptographerReady( @@ -981,18 +986,20 @@ void ProfileSyncService::ConfigureDataTypeManager() { encrypted_types_.clear(); if (types.count(syncable::PASSWORDS) > 0) encrypted_types_.insert(syncable::PASSWORDS); - if (observed_passphrase_required_ && passphrase_required_for_decryption_) { + if (passphrase_required_reason_ == sync_api::REASON_DECRYPTION || + passphrase_required_reason_ == sync_api::REASON_SET_PASSPHRASE_FAILED) { if (IsEncryptedDatatypeEnabled()) { // We need a passphrase still. Prompt the user for a passphrase, and // DataTypeManager::Configure() will get called once the passphrase is // accepted. - OnPassphraseRequired(true); + OnPassphraseRequired(passphrase_required_reason_); return; } else { // We've been informed that a passphrase is required for decryption, but - // now there are no encrypted data types enabled, so clear the flag - // (NotifyObservers() will be called when configuration completes). - observed_passphrase_required_ = false; + // now there are no encrypted data types enabled, so change the value of + // passphrase_required_reason_ to its default value. (NotifyObservers() + // will be called when configuration completes). + passphrase_required_reason_ = sync_api::REASON_PASSPHRASE_NOT_REQUIRED; } } data_type_manager_->Configure(types); @@ -1098,7 +1105,7 @@ void ProfileSyncService::DeactivateDataType( void ProfileSyncService::SetPassphrase(const std::string& passphrase, bool is_explicit, bool is_creation) { - if (ShouldPushChanges() || observed_passphrase_required_) { + if (ShouldPushChanges() || ObservedPassphraseRequired()) { backend_->SetPassphrase(passphrase, is_explicit); } else { cached_passphrase_.value = passphrase; @@ -1154,9 +1161,10 @@ void ProfileSyncService::Observe(NotificationType type, // We should never get in a state where we have no encrypted datatypes // enabled, and yet we still think we require a passphrase. - DCHECK(!(observed_passphrase_required_ && - passphrase_required_for_decryption_ && - !IsEncryptedDatatypeEnabled())); + DCHECK(passphrase_required_reason_ == + sync_api::REASON_PASSPHRASE_NOT_REQUIRED || + passphrase_required_reason_ == sync_api::REASON_ENCRYPTION || + IsEncryptedDatatypeEnabled()); // TODO(sync): Less wizard, more toast. wizard_.Step(SyncSetupWizard::DONE); @@ -1202,7 +1210,9 @@ void ProfileSyncService::Observe(NotificationType type, // If this signin was to initiate a passphrase migration (on the // first computer, thus not for decryption), continue the migration. if (passphrase_migration_in_progress_ && - !passphrase_required_for_decryption_) { + passphrase_required_reason_ != sync_api::REASON_DECRYPTION && + passphrase_required_reason_ != + sync_api::REASON_SET_PASSPHRASE_FAILED) { wizard_.Step(SyncSetupWizard::PASSPHRASE_MIGRATION); passphrase_migration_in_progress_ = false; } diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h index 05aa410..a0e1cb8 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -195,7 +195,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend, virtual void OnClearServerDataFailed(); virtual void OnClearServerDataTimeout(); virtual void OnClearServerDataSucceeded(); - virtual void OnPassphraseRequired(bool for_decryption); + virtual void OnPassphraseRequired(sync_api::PassphraseRequiredReason reason); virtual void OnPassphraseAccepted(); virtual void OnEncryptionComplete( const syncable::ModelTypeSet& encrypted_types); @@ -282,12 +282,14 @@ class ProfileSyncService : public browser_sync::SyncFrontend, return is_auth_in_progress_; } - bool observed_passphrase_required() const { - return observed_passphrase_required_; + // Returns true if OnPassphraseRequired has been called for any reason. + bool ObservedPassphraseRequired() const { + return passphrase_required_reason_ != + sync_api::REASON_PASSPHRASE_NOT_REQUIRED; } - bool passphrase_required_for_decryption() const { - return passphrase_required_for_decryption_; + sync_api::PassphraseRequiredReason passphrase_required_reason() const { + return passphrase_required_reason_; } // Returns a user-friendly string form of last synced time (in minutes). @@ -509,13 +511,10 @@ class ProfileSyncService : public browser_sync::SyncFrontend, // Cache of the last name the client attempted to authenticate. std::string last_attempted_user_email_; - // Whether we have seen a SYNC_PASSPHRASE_REQUIRED since initializing the - // backend, telling us that it is safe to send a passphrase down ASAP. - bool observed_passphrase_required_; - // Was the last SYNC_PASSPHRASE_REQUIRED notification sent because it - // was required for decryption? - bool passphrase_required_for_decryption_; + // was required for encryption, decryption with a cached passphrase, or + // because a new passphrase is required? + sync_api::PassphraseRequiredReason passphrase_required_reason_; // Is the user in a passphrase migration? bool passphrase_migration_in_progress_; diff --git a/chrome/browser/sync/profile_sync_service_harness.cc b/chrome/browser/sync/profile_sync_service_harness.cc index f8ab166..60529f2 100644 --- a/chrome/browser/sync/profile_sync_service_harness.cc +++ b/chrome/browser/sync/profile_sync_service_harness.cc @@ -134,11 +134,12 @@ bool ProfileSyncServiceHarness::SetupSync() { synced_datatypes.insert(syncable::ModelTypeFromInt(i)); } bool result = SetupSync(synced_datatypes); - VLOG(0) << "Client " << id_ << ": Set up sync completed with result " - << result; if (result == false) { - std::string pss_status = GetServiceStatus(); - VLOG(0) << pss_status; + std::string status = GetServiceStatus(); + LOG(ERROR) << "Client " << id_ << ": SetupSync failed. Syncer status:\n" + << status; + } else { + VLOG(1) << "Client " << id_ << ": SetupSync successful."; } return result; } @@ -183,6 +184,12 @@ bool ProfileSyncServiceHarness::SetupSync( return false; } + if (wait_state_ == SET_PASSPHRASE_FAILED) { + // A passphrase is required for decryption. Sync cannot proceed until + // SetPassphrase is called. + return false; + } + // Indicate to the browser that sync setup is complete. service()->SetSyncSetupCompleted(); @@ -215,21 +222,37 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() { if (IsSynced()) { // The first sync cycle is now complete. We can start running tests. SignalStateCompleteWithNextState(FULLY_SYNCED); + break; + } + if (service()->passphrase_required_reason() == + sync_api::REASON_SET_PASSPHRASE_FAILED) { + // A passphrase is required for decryption and we don't have it. Do not + // wait any more. + SignalStateCompleteWithNextState(SET_PASSPHRASE_FAILED); + break; } break; } case WAITING_FOR_SYNC_TO_FINISH: { LogClientInfo("WAITING_FOR_SYNC_TO_FINISH"); - if (!IsSynced()) { - // The client is not yet fully synced. Continue waiting. - if (!GetStatus().server_reachable) { - // The client cannot reach the sync server because the network is - // disabled. There is no need to wait anymore. - SignalStateCompleteWithNextState(SERVER_UNREACHABLE); - } + if (IsSynced()) { + // The sync cycle we were waiting for is complete. + SignalStateCompleteWithNextState(FULLY_SYNCED); + break; + } + if (service()->passphrase_required_reason() == + sync_api::REASON_SET_PASSPHRASE_FAILED) { + // A passphrase is required for decryption and we don't have it. Do not + // wait any more. + SignalStateCompleteWithNextState(SET_PASSPHRASE_FAILED); + break; + } + if (!GetStatus().server_reachable) { + // The client cannot reach the sync server because the network is + // disabled. There is no need to wait anymore. + SignalStateCompleteWithNextState(SERVER_UNREACHABLE); break; } - SignalStateCompleteWithNextState(FULLY_SYNCED); break; } case WAITING_FOR_UPDATES: { @@ -248,7 +271,7 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() { case WAITING_FOR_PASSPHRASE_ACCEPTED: { LogClientInfo("WAITING_FOR_PASSPHRASE_ACCEPTED"); if (service()->ShouldPushChanges() && - !service()->observed_passphrase_required()) { + !service()->ObservedPassphraseRequired()) { // The passphrase has been accepted, and sync has been restarted. SignalStateCompleteWithNextState(FULLY_SYNCED); } @@ -279,6 +302,12 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() { } break; } + case SET_PASSPHRASE_FAILED: { + // A passphrase is required for decryption. There is nothing the sync + // client can do until SetPassphrase() is called. + LogClientInfo("SET_PASSPHRASE_FAILED"); + break; + } case FULLY_SYNCED: { // The client is online and fully synced. There is nothing to do. LogClientInfo("FULLY_SYNCED"); @@ -308,10 +337,8 @@ bool ProfileSyncServiceHarness::AwaitPassphraseAccepted() { return false; } - // TODO(atwilson): After ProfileSyncService::OnPassphraseAccepted() is - // fixed, add an extra check to make sure that the value of - // service()->observed_passphrase_required() is false. - if (service()->ShouldPushChanges()) { + if (service()->ShouldPushChanges() && + !service()->ObservedPassphraseRequired()) { // Passphrase is already accepted; don't wait. return true; } @@ -620,8 +647,7 @@ std::string ProfileSyncServiceHarness::GetUpdatedTimestamp( } void ProfileSyncServiceHarness::LogClientInfo(const std::string& message) { - // TODO(lipalani): Change VLOG(0) to VLOG(1) - // http://crbug.com/80706 + // TODO(lipalani): Change VLOG(0) to VLOG(1) -- See http://crbug.com/80706. if (service()) { const SyncSessionSnapshot* snap = GetLastSessionSnapshot(); if (snap) { @@ -634,7 +660,7 @@ void ProfileSyncServiceHarness::LogClientInfo(const std::string& message) { << ", has_unsynced_items: " << service()->HasUnsyncedItems() << ", observed_passphrase_required: " - << service()->observed_passphrase_required() + << service()->ObservedPassphraseRequired() << ", notifications_enabled: " << GetStatus().notifications_enabled << ", service_is_pushing_changes: " << ServiceIsPushingChanges() diff --git a/chrome/browser/sync/profile_sync_service_harness.h b/chrome/browser/sync/profile_sync_service_harness.h index 16dc81f..af1d83c 100644 --- a/chrome/browser/sync/profile_sync_service_harness.h +++ b/chrome/browser/sync/profile_sync_service_harness.h @@ -172,6 +172,9 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver { // full sync cycle is not expected to occur. WAITING_FOR_SYNC_CONFIGURATION, + // The sync client needs a passphrase in order to decrypt data. + SET_PASSPHRASE_FAILED, + // The sync client cannot reach the server. SERVER_UNREACHABLE, @@ -212,16 +215,21 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver { // for a particular datatype. std::string GetUpdatedTimestamp(syncable::ModelType model_type); - // Gets the status from |service_| in pretty printable form. + // Gets detailed status from |service_| in pretty-printable form. std::string GetServiceStatus(); // When in WAITING_FOR_ENCRYPTION state, we check to see if this type is now // encrypted to determine if we're done. syncable::ModelType waiting_for_encryption_type_; + // The WaitState in which the sync client currently is. Helps determine what + // action to take when RunStateChangeMachine() is called. WaitState wait_state_; + // Sync profile associated with this sync client. Profile* profile_; + + // ProfileSyncService object associated with |profile_|. ProfileSyncService* service_; // The harness of the client whose update progress marker we're expecting diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc index 72ef033..e0970c0 100644 --- a/chrome/browser/sync/sync_setup_wizard_unittest.cc +++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc @@ -11,6 +11,7 @@ #include "base/stl_util-inl.h" #include "base/utf_string_conversions.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/profile_sync_factory_mock.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/sync_setup_flow.h" @@ -78,8 +79,8 @@ class ProfileSyncServiceForWizardTest : public ProfileSyncService { last_auth_error_ = error; } - void set_passphrase_required(bool required) { - observed_passphrase_required_ = required; + void SetPassphraseRequiredReason(sync_api::PassphraseRequiredReason reason) { + passphrase_required_reason_ = reason; } void ResetTestStats() { @@ -337,7 +338,7 @@ TEST_F(SyncSetupWizardTest, DISABLED_EnterPassphraseRequired) { wizard_->Step(SyncSetupWizard::GAIA_SUCCESS); wizard_->Step(SyncSetupWizard::CONFIGURE); wizard_->Step(SyncSetupWizard::SETTING_UP); - service_->set_passphrase_required(true); + service_->SetPassphraseRequiredReason(sync_api::REASON_ENCRYPTION); wizard_->Step(SyncSetupWizard::ENTER_PASSPHRASE); EXPECT_EQ(SyncSetupWizard::ENTER_PASSPHRASE, test_window_->flow()->current_state_); diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc index cba3c7c..8a1625f 100644 --- a/chrome/browser/sync/sync_ui_util.cc +++ b/chrome/browser/sync/sync_ui_util.cc @@ -103,7 +103,7 @@ MessageType GetStatusInfo(ProfileSyncService* service, // Either show auth error information with a link to re-login, auth in prog, // or note that everything is OK with the last synced time. - if (status.authenticated && !service->observed_passphrase_required()) { + if (status.authenticated && !service->ObservedPassphraseRequired()) { // Everything is peachy. if (status_label) { status_label->assign(GetSyncedStateStatusLabel(service)); @@ -115,8 +115,11 @@ MessageType GetStatusInfo(ProfileSyncService* service, l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL)); } result_type = PRE_SYNCED; - } else if (service->observed_passphrase_required()) { - if (service->passphrase_required_for_decryption()) { + } else if (service->ObservedPassphraseRequired()) { + if (service->passphrase_required_reason() == + sync_api::REASON_DECRYPTION || + service->passphrase_required_reason() == + sync_api::REASON_SET_PASSPHRASE_FAILED) { // NOT first machine. // Show a link ("needs attention"), but still indicate the // current synced status. Return SYNC_PROMO so that @@ -187,8 +190,8 @@ MessageType GetStatusInfoForNewTabPage(ProfileSyncService* service, DCHECK(link_label); if (service->HasSyncSetupCompleted() && - service->observed_passphrase_required()) { - if (!service->passphrase_required_for_decryption()) { + service->ObservedPassphraseRequired()) { + if (service->passphrase_required_reason() == sync_api::REASON_ENCRYPTION) { // First machine migrating to passwords. Show as a promotion. if (status_label && link_label) { status_label->assign( diff --git a/chrome/browser/sync/test_profile_sync_service.cc b/chrome/browser/sync/test_profile_sync_service.cc index 7d5be85..f4a0e78 100644 --- a/chrome/browser/sync/test_profile_sync_service.cc +++ b/chrome/browser/sync/test_profile_sync_service.cc @@ -227,7 +227,7 @@ void TestProfileSyncService::OnBackendInitialized() { ProfileSyncService::OnBackendInitialized(); if (send_passphrase_required) - OnPassphraseRequired(true); + OnPassphraseRequired(sync_api::REASON_DECRYPTION); // TODO(akalin): Figure out a better way to do this. if (synchronous_backend_initialization_) { diff --git a/chrome/test/live_sync/live_passwords_sync_test.cc b/chrome/test/live_sync/live_passwords_sync_test.cc index abdc2d8..b56f56e 100644 --- a/chrome/test/live_sync/live_passwords_sync_test.cc +++ b/chrome/test/live_sync/live_passwords_sync_test.cc @@ -15,7 +15,7 @@ using webkit_glue::PasswordForm; -const std::string kFakeSignonRealm = "http://fake-domain.google.com/"; +const std::string kFakeSignonRealm = "http://fake-signon-realm.google.com/"; // We use a WaitableEvent to wait on AddLogin instead of running the UI message // loop because of a restriction that prevents a DB thread from initiating a diff --git a/chrome/test/live_sync/single_client_live_passwords_sync_test.cc b/chrome/test/live_sync/single_client_live_passwords_sync_test.cc index 149763d..ba56e02 100644 --- a/chrome/test/live_sync/single_client_live_passwords_sync_test.cc +++ b/chrome/test/live_sync/single_client_live_passwords_sync_test.cc @@ -8,8 +8,7 @@ using webkit_glue::PasswordForm; -// TODO(rsimha): See http://crbug.com/78840. -IN_PROC_BROWSER_TEST_F(SingleClientLivePasswordsSyncTest, FLAKY_Sanity) { +IN_PROC_BROWSER_TEST_F(SingleClientLivePasswordsSyncTest, Sanity) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; PasswordForm form = CreateTestPasswordForm(0); diff --git a/chrome/test/live_sync/two_client_live_passwords_sync_test.cc b/chrome/test/live_sync/two_client_live_passwords_sync_test.cc index 810741a..bff966b 100644 --- a/chrome/test/live_sync/two_client_live_passwords_sync_test.cc +++ b/chrome/test/live_sync/two_client_live_passwords_sync_test.cc @@ -12,8 +12,7 @@ using webkit_glue::PasswordForm; static const char* kValidPassphrase = "passphrase!"; -// TODO(rsimha): See http://crbug.com/78840. -IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, FLAKY_Add) { +IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, Add) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; @@ -36,8 +35,7 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, FLAKY_Add) { ASSERT_TRUE(ContainsSamePasswordForms(verifier_forms, forms1)); } -// TODO(rsimha): See http://crbug.com/78840. -IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, FLAKY_Race) { +IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, Race) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; PasswordForm form0 = CreateTestPasswordForm(0); @@ -60,8 +58,7 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, FLAKY_Race) { ASSERT_TRUE(ContainsSamePasswordForms(forms0, forms1)); } -// TODO(rsimha): See http://crbug.com/78840. -IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, FLAKY_SetPassphrase) { +IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, SetPassphrase) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; SetPassphrase(0, kValidPassphrase, true); @@ -70,11 +67,11 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, FLAKY_SetPassphrase) { SetPassphrase(1, kValidPassphrase, false); ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted()); + ASSERT_TRUE(GetClient(1)->AwaitSyncCycleCompletion("Set passphrase.")); } -// TODO(rsimha): See http://crbug.com/78840. IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, - FLAKY_SetPassphraseAndAddPassword) { + SetPassphraseAndAddPassword) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; SetPassphrase(0, kValidPassphrase, true); @@ -98,9 +95,8 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, ASSERT_EQ(1U, forms1.size()); } -// TODO(rsimha): See http://crbug.com/78840. IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, - FLAKY_SetPassphraseAndThenSetupSync) { + SetPassphraseAndThenSetupSync) { ASSERT_TRUE(SetupClients()) << "SetupClients() failed."; ASSERT_TRUE(GetClient(0)->SetupSync()); @@ -108,14 +104,14 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, ASSERT_TRUE(GetClient(0)->AwaitPassphraseAccepted()); ASSERT_TRUE(GetClient(0)->AwaitSyncCycleCompletion("Initial sync.")); + ASSERT_FALSE(GetClient(1)->SetupSync()); SetPassphrase(1, kValidPassphrase, false); - ASSERT_TRUE(GetClient(1)->SetupSync()); ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted()); + ASSERT_TRUE(GetClient(1)->AwaitSyncCycleCompletion("Initial sync.")); } -// TODO(rsimha): See http://crbug.com/78840. IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, - FLAKY_SetPassphraseTwice) { + SetPassphraseTwice) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; SetPassphrase(0, kValidPassphrase, true); @@ -124,7 +120,9 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, SetPassphrase(1, kValidPassphrase, false); ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted()); + ASSERT_TRUE(GetClient(1)->AwaitSyncCycleCompletion("Set passphrase.")); SetPassphrase(1, kValidPassphrase, false); ASSERT_TRUE(GetClient(1)->AwaitPassphraseAccepted()); + ASSERT_TRUE(GetClient(1)->AwaitSyncCycleCompletion("Set passphrase again.")); } |