summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-10 20:22:50 +0000
committertim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-10 20:22:50 +0000
commit954b10803a3d37f4a079874d5a6c81d4956920c6 (patch)
tree2dfcc9628b0aba92e30116e7bae3e11897a2db87
parenta8ba6369d632006245574b26a2b4ee8cc21ca25f (diff)
downloadchromium_src-954b10803a3d37f4a079874d5a6c81d4956920c6.zip
chromium_src-954b10803a3d37f4a079874d5a6c81d4956920c6.tar.gz
chromium_src-954b10803a3d37f4a079874d5a6c81d4956920c6.tar.bz2
sync: bake 'explicit' passphrases into the nigori specifics, rather
than using a local-only preference. Tries to deal with migration from local pref, but some weird cases might require the nuclear option to totally clean things up, since we were erroneously setting the pref even when you would be in "gaia passphrase" mode. Fixes the "same passphrase as gaia password" bug for new users. BUG=62103, 60827, 59867 TEST=unit_tests, integration tests Review URL: http://codereview.chromium.org/4632001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65697 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/sync/engine/syncapi.cc64
-rw-r--r--chrome/browser/sync/engine/syncapi.h9
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.cc20
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.h11
-rw-r--r--chrome/browser/sync/profile_sync_service.cc52
-rw-r--r--chrome/browser/sync/profile_sync_service.h29
-rw-r--r--chrome/browser/sync/profile_sync_service_harness.cc24
-rw-r--r--chrome/browser/sync/profile_sync_service_harness.h18
-rw-r--r--chrome/browser/sync/profile_sync_service_password_unittest.cc2
-rw-r--r--chrome/browser/sync/protocol/nigori_specifics.proto3
-rw-r--r--chrome/browser/sync/resources/passphrase.html7
-rw-r--r--chrome/browser/sync/sync_setup_flow.cc26
-rw-r--r--chrome/browser/sync/sync_setup_flow.h3
-rw-r--r--chrome/browser/sync/sync_setup_wizard_unittest.cc6
-rw-r--r--chrome/test/live_sync/two_client_live_passwords_sync_test.cc14
-rwxr-xr-xnet/tools/testserver/chromiumsync.py14
16 files changed, 235 insertions, 67 deletions
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index b453a52..9d42312 100644
--- a/chrome/browser/sync/engine/syncapi.cc
+++ b/chrome/browser/sync/engine/syncapi.cc
@@ -976,7 +976,13 @@ class SyncManager::SyncInternal
// Tell the sync engine to start the syncing process.
void StartSyncing();
- void SetPassphrase(const std::string& passphrase);
+ // Whether or not the Nigori node is encrypted using an explicit passphrase.
+ bool IsUsingExplicitPassphrase();
+
+ // Try to set the current passphrase to |passphrase|, and record whether
+ // it is an explicit passphrase or implicitly using gaia in the Nigori
+ // node.
+ void SetPassphrase(const std::string& passphrase, bool is_explicit);
// Call periodically from a database-safe thread to persist recent changes
// to the syncapi model.
@@ -1173,6 +1179,12 @@ class SyncManager::SyncInternal
// decryption. Otherwise, the cryptographer is made ready (is_ready()).
void BootstrapEncryption(const std::string& restored_key_for_bootstrapping);
+ // Helper for migration to new nigori proto to set
+ // 'using_explicit_passphrase' in the NigoriSpecifics.
+ // TODO(tim): Bug 62103. Remove this after it has been pushed out to dev
+ // channel users.
+ void SetUsingExplicitPassphrasePrefForMigration();
+
// Checks for server reachabilty and requests a nudge.
void OnIPAddressChangedImpl();
@@ -1297,8 +1309,13 @@ void SyncManager::StartSyncing() {
data_->StartSyncing();
}
-void SyncManager::SetPassphrase(const std::string& passphrase) {
- data_->SetPassphrase(passphrase);
+void SyncManager::SetPassphrase(const std::string& passphrase,
+ bool is_explicit) {
+ data_->SetPassphrase(passphrase, is_explicit);
+}
+
+bool SyncManager::IsUsingExplicitPassphrase() {
+ return data_ && data_->IsUsingExplicitPassphrase();
}
bool SyncManager::RequestPause() {
@@ -1572,8 +1589,21 @@ void SyncManager::SyncInternal::RaiseAuthNeededEvent() {
}
}
+void SyncManager::SyncInternal::SetUsingExplicitPassphrasePrefForMigration() {
+ WriteTransaction trans(&share_);
+ WriteNode node(&trans);
+ if (!node.InitByTagLookup(kNigoriTag)) {
+ // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
+ NOTREACHED();
+ return;
+ }
+ sync_pb::NigoriSpecifics specifics(node.GetNigoriSpecifics());
+ specifics.set_using_explicit_passphrase(true);
+ node.SetNigoriSpecifics(specifics);
+}
+
void SyncManager::SyncInternal::SetPassphrase(
- const std::string& passphrase) {
+ const std::string& passphrase, bool is_explicit) {
Cryptographer* cryptographer = dir_manager()->cryptographer();
KeyParams params = {"localhost", "dummy", passphrase};
if (cryptographer->has_pending_keys()) {
@@ -1581,6 +1611,13 @@ void SyncManager::SyncInternal::SetPassphrase(
observer_->OnPassphraseRequired();
return;
}
+
+ // TODO(tim): If this is the first time the user has entered a passphrase
+ // since the protocol changed to store passphrase preferences in the cloud,
+ // make sure we update this preference. See bug 62103.
+ if (is_explicit)
+ SetUsingExplicitPassphrasePrefForMigration();
+
// Nudge the syncer so that passwords updates that were waiting for this
// passphrase get applied as soon as possible.
sync_manager_->RequestNudge();
@@ -1592,6 +1629,12 @@ void SyncManager::SyncInternal::SetPassphrase(
NOTREACHED();
return;
}
+
+ // Prevent an implicit SetPassphrase request from changing an explicitly
+ // set passphrase.
+ if (!is_explicit && node.GetNigoriSpecifics().using_explicit_passphrase())
+ return;
+
cryptographer->AddKey(params);
// TODO(tim): Bug 58231. It would be nice if SetPassphrase didn't require
@@ -1600,6 +1643,7 @@ void SyncManager::SyncInternal::SetPassphrase(
// safe to defer this work.
sync_pb::NigoriSpecifics specifics;
cryptographer->GetKeys(specifics.mutable_encrypted());
+ specifics.set_using_explicit_passphrase(is_explicit);
node.SetNigoriSpecifics(specifics);
ReEncryptEverything(&trans);
}
@@ -1609,6 +1653,18 @@ void SyncManager::SyncInternal::SetPassphrase(
observer_->OnPassphraseAccepted(bootstrap_token);
}
+bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() {
+ ReadTransaction trans(&share_);
+ ReadNode node(&trans);
+ if (!node.InitByTagLookup(kNigoriTag)) {
+ // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
+ NOTREACHED();
+ return false;
+ }
+
+ return node.GetNigoriSpecifics().using_explicit_passphrase();
+}
+
void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) {
// TODO(tim): bug 59242. We shouldn't lookup by data type and instead use
// a protocol flag or existence of an EncryptedData message, but for now,
diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h
index 9a3da9b..6ea7b63 100644
--- a/chrome/browser/sync/engine/syncapi.h
+++ b/chrome/browser/sync/engine/syncapi.h
@@ -815,7 +815,11 @@ class SyncManager {
// If the passphrase in invalid, OnPassphraseRequired will be fired.
// Calling this metdod again is the appropriate course of action to "retry"
// with a new passphrase.
- void SetPassphrase(const std::string& passphrase);
+ // |is_explicit| is true if the call is in response to the user explicitly
+ // setting a passphrase as opposed to implicitly (from the users' perspective)
+ // using their Google Account password. An implicit SetPassphrase will *not*
+ // *not* override an explicit passphrase set previously.
+ void SetPassphrase(const std::string& passphrase, bool is_explicit);
// Requests the syncer thread to pause. The observer's OnPause
// method will be called when the syncer thread is paused. Returns
@@ -852,6 +856,9 @@ class SyncManager {
Status::Summary GetStatusSummary() const;
Status GetDetailedStatus() const;
+ // Whether or not the Nigori node is encrypted using an explicit passphrase.
+ bool IsUsingExplicitPassphrase();
+
// Get the internal implementation for use by BaseTransaction, etc.
SyncInternal* GetImpl() const;
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index 7d833bd..342e989 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -158,6 +158,12 @@ bool SyncBackendHost::IsNigoriEnabled() const {
registrar_.routing_info.end();
}
+bool SyncBackendHost::IsUsingExplicitPassphrase() {
+ return IsNigoriEnabled() && syncapi_initialized_ &&
+ core_->syncapi()->InitialSyncEndedForAllEnabledTypes() &&
+ core_->syncapi()->IsUsingExplicitPassphrase();
+}
+
bool SyncBackendHost::IsCryptographerReady() const {
return syncapi_initialized_ &&
GetUserShareHandle()->dir_manager->cryptographer()->is_ready();
@@ -186,7 +192,8 @@ void SyncBackendHost::StartSyncingWithServer() {
NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing));
}
-void SyncBackendHost::SetPassphrase(const std::string& passphrase) {
+void SyncBackendHost::SetPassphrase(const std::string& passphrase,
+ bool is_explicit) {
if (!IsNigoriEnabled()) {
LOG(WARNING) << "Silently dropping SetPassphrase request.";
return;
@@ -195,7 +202,7 @@ void SyncBackendHost::SetPassphrase(const std::string& passphrase) {
// If encryption is enabled and we've got a SetPassphrase
core_thread_.message_loop()->PostTask(FROM_HERE,
NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoSetPassphrase,
- passphrase));
+ passphrase, is_explicit));
}
void SyncBackendHost::Shutdown(bool sync_disabled) {
@@ -385,6 +392,8 @@ void SyncBackendHost::Core::NotifyPassphraseRequired() {
void SyncBackendHost::Core::NotifyPassphraseAccepted(
const std::string& bootstrap_token) {
+ if (!host_)
+ return;
host_->PersistEncryptionBootstrapToken(bootstrap_token);
NotificationService::current()->Notify(
NotificationType::SYNC_PASSPHRASE_ACCEPTED,
@@ -393,6 +402,8 @@ void SyncBackendHost::Core::NotifyPassphraseAccepted(
}
void SyncBackendHost::Core::NotifyUpdatedToken(const std::string& token) {
+ if (!host_)
+ return;
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
TokenAvailableDetails details(GaiaConstants::kSyncService, token);
NotificationService::current()->Notify(
@@ -526,9 +537,10 @@ void SyncBackendHost::Core::DoStartSyncing() {
syncapi_->StartSyncing();
}
-void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase) {
+void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase,
+ bool is_explicit) {
DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
- syncapi_->SetPassphrase(passphrase);
+ syncapi_->SetPassphrase(passphrase, is_explicit);
}
UIModelWorker* SyncBackendHost::ui_worker() {
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index 26d060a..0b8f94e 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -124,7 +124,11 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
virtual void StartSyncingWithServer();
// Called on |frontend_loop_| to asynchronously set the passphrase.
- void SetPassphrase(const std::string& passphrase);
+ // |is_explicit| is true if the call is in response to the user explicitly
+ // setting a passphrase as opposed to implicitly (from the users' perspective)
+ // using their Google Account password. An implicit SetPassphrase will *not*
+ // *not* override an explicit passphrase set previously.
+ void SetPassphrase(const std::string& passphrase, bool is_explicit);
// Called on |frontend_loop_| to kick off shutdown.
// |sync_disabled| indicates if syncing is being disabled or not.
@@ -194,6 +198,9 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// Whether or not we are syncing encryption keys.
bool IsNigoriEnabled() const;
+ // Whether or not the Nigori node is encrypted using an explicit passphrase.
+ bool IsUsingExplicitPassphrase();
+
// True if the cryptographer has any keys available to attempt decryption.
// Could mean we've downloaded and loaded Nigori objects, or we bootstrapped
// using a token previously received.
@@ -282,7 +289,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// Called on our SyncBackendHost's |core_thread_| to set the passphrase
// on behalf of SyncBackendHost::SupplyPassphrase.
- void DoSetPassphrase(const std::string& passphrase);
+ void DoSetPassphrase(const std::string& passphrase, bool is_explicit);
// The shutdown order is a bit complicated:
// 1) From |core_thread_|, invoke the syncapi Shutdown call to do a final
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 7b210d0..c235ad5 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -75,7 +75,6 @@ ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory,
unrecoverable_error_detected_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)),
token_migrator_(NULL),
-
clear_server_data_state_(CLEAR_NOT_STARTED) {
DCHECK(factory);
DCHECK(profile);
@@ -97,6 +96,8 @@ ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory,
sync_service_url_ = GURL(kSyncServerUrl);
}
#endif
+
+ tried_implicit_gaia_remove_when_bug_62103_fixed_ = false;
}
ProfileSyncService::ProfileSyncService()
@@ -365,8 +366,6 @@ void ProfileSyncService::RegisterPreferences() {
enable_by_default);
pref_service->RegisterBooleanPref(prefs::kSyncManaged, false);
pref_service->RegisterStringPref(prefs::kEncryptionBootstrapToken, "");
- pref_service->RegisterBooleanPref(prefs::kSyncUsingSecondaryPassphrase,
- false);
}
void ProfileSyncService::ClearPreferences() {
@@ -374,7 +373,6 @@ void ProfileSyncService::ClearPreferences() {
pref_service->ClearPref(prefs::kSyncLastSyncedTime);
pref_service->ClearPref(prefs::kSyncHasSetupCompleted);
pref_service->ClearPref(prefs::kEncryptionBootstrapToken);
- pref_service->ClearPref(prefs::kSyncUsingSecondaryPassphrase);
// TODO(nick): The current behavior does not clear e.g. prefs::kSyncBookmarks.
// Is that really what we want?
@@ -923,12 +921,9 @@ void ProfileSyncService::GetRegisteredDataTypes(
}
bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
- return profile_->GetPrefs()->GetBoolean(prefs::kSyncUsingSecondaryPassphrase);
-}
-
-void ProfileSyncService::SetSecondaryPassphrase(const std::string& passphrase) {
- SetPassphrase(passphrase);
- profile_->GetPrefs()->SetBoolean(prefs::kSyncUsingSecondaryPassphrase, true);
+ return backend_.get() && backend_->IsUsingExplicitPassphrase() ||
+ (tried_implicit_gaia_remove_when_bug_62103_fixed_ &&
+ observed_passphrase_required_);
}
bool ProfileSyncService::IsCryptographerReady() const {
@@ -973,11 +968,13 @@ void ProfileSyncService::DeactivateDataType(
backend_->DeactivateDataType(data_type_controller, change_processor);
}
-void ProfileSyncService::SetPassphrase(const std::string& passphrase) {
+void ProfileSyncService::SetPassphrase(const std::string& passphrase,
+ bool is_explicit) {
if (ShouldPushChanges() || observed_passphrase_required_) {
- backend_->SetPassphrase(passphrase);
+ backend_->SetPassphrase(passphrase, is_explicit);
} else {
- cached_passphrase_ = passphrase;
+ cached_passphrase_.value = passphrase;
+ cached_passphrase_.is_explicit = is_explicit;
}
}
@@ -1003,10 +1000,11 @@ void ProfileSyncService::Observe(NotificationType type,
return;
}
- if (!cached_passphrase_.empty()) {
+ if (!cached_passphrase_.value.empty()) {
// Don't hold on to the passphrase in raw form longer than needed.
- SetPassphrase(cached_passphrase_);
- cached_passphrase_.clear();
+ SetPassphrase(cached_passphrase_.value,
+ cached_passphrase_.is_explicit);
+ cached_passphrase_ = CachedPassphrase();
}
// TODO(sync): Less wizard, more toast.
@@ -1021,9 +1019,10 @@ void ProfileSyncService::Observe(NotificationType type,
DCHECK(backend_->IsNigoriEnabled());
observed_passphrase_required_ = true;
- if (!cached_passphrase_.empty()) {
- SetPassphrase(cached_passphrase_);
- cached_passphrase_.clear();
+ if (!cached_passphrase_.value.empty()) {
+ SetPassphrase(cached_passphrase_.value,
+ cached_passphrase_.is_explicit);
+ cached_passphrase_ = CachedPassphrase();
break;
}
@@ -1068,12 +1067,15 @@ void ProfileSyncService::Observe(NotificationType type,
break;
}
case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: {
- PrefService* prefs = profile_->GetPrefs();
- if (!prefs->GetBoolean(prefs::kSyncUsingSecondaryPassphrase)) {
- const GoogleServiceSigninSuccessDetails* successful =
- (Details<const GoogleServiceSigninSuccessDetails>(details).ptr());
- SetPassphrase(successful->password);
- }
+ const GoogleServiceSigninSuccessDetails* successful =
+ (Details<const GoogleServiceSigninSuccessDetails>(details).ptr());
+ // We pass 'false' to SetPassphrase to denote that this is an implicit
+ // request and shouldn't override an explicit one. Thus, we either
+ // update the implicit passphrase (idempotent if the passphrase didn't
+ // actually change), or the user has an explicit passphrase set so this
+ // becomes a no-op.
+ tried_implicit_gaia_remove_when_bug_62103_fixed_ = true;
+ SetPassphrase(successful->password, false);
break;
}
case NotificationType::GOOGLE_SIGNIN_FAILED: {
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index 65f18f5..d4cb576 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -340,14 +340,15 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// Returns true if a secondary passphrase is being used.
virtual bool IsUsingSecondaryPassphrase() const;
- // Sets the secondary passphrase.
- virtual void SetSecondaryPassphrase(const std::string& passphrase);
-
// Sets the Cryptographer's passphrase, or caches it until that is possible.
// This will check asynchronously whether the passphrase is valid and notify
// ProfileSyncServiceObservers via the NotificationService when the outcome
// is known.
- virtual void SetPassphrase(const std::string& passphrase);
+ // |is_explicit| is true if the call is in response to the user explicitly
+ // setting a passphrase as opposed to implicitly (from the users' perspective)
+ // using their Google Account password. An implicit SetPassphrase will *not*
+ // *not* override an explicit passphrase set previously.
+ virtual void SetPassphrase(const std::string& passphrase, bool is_explicit);
// Returns whether processing changes is allowed. Check this before doing
// any model-modifying operations.
@@ -485,7 +486,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
NotificationRegistrar registrar_;
ScopedRunnableMethodFactory<ProfileSyncService>
- scoped_runnable_method_factory_;
+ scoped_runnable_method_factory_;
// The preference that controls whether sync is under control by configuration
// management.
@@ -502,13 +503,27 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// yet have a backend to send it to. This happens during initialization as
// we don't StartUp until we have a valid token, which happens after valid
// credentials were provided.
- std::string cached_passphrase_;
+ struct CachedPassphrase {
+ std::string value;
+ bool is_explicit;
+ CachedPassphrase() : is_explicit(false) {}
+ };
+ CachedPassphrase cached_passphrase_;
+
+ // TODO(tim): Remove this once new 'explicit passphrase' code flushes through
+ // dev channel. See bug 62103.
+ // To "migrate" early adopters of password sync on dev channel to the new
+ // model that stores their secondary passphrase preference in the cloud, we
+ // need some extra state since this cloud pref will be empty for all of them
+ // regardless of how they set up sync, and we can't trust
+ // kSyncUsingSecondaryPassphrase due to bugs in that implementation.
+ bool tried_implicit_gaia_remove_when_bug_62103_fixed_;
// Keep track of where we are in a server clear operation
ClearServerDataState clear_server_data_state_;
// Timeout for the clear data command. This timeout is a temporary hack
- // and is necessary becaue the nudge sync framework can drop nudges for
+ // and is necessary because the nudge sync framework can drop nudges for
// a wide variety of sync-related conditions (throttling, connections issues,
// syncer paused, etc.). It can only be removed correctly when the framework
// is reworked to allow one-shot commands like clearing server data.
diff --git a/chrome/browser/sync/profile_sync_service_harness.cc b/chrome/browser/sync/profile_sync_service_harness.cc
index 4750033..d86c286 100644
--- a/chrome/browser/sync/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/profile_sync_service_harness.cc
@@ -14,6 +14,7 @@
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
+#include "chrome/common/notification_source.h"
#include "chrome/common/pref_names.h"
// The default value for min_timestamp_needed_ when we're not in the
@@ -95,6 +96,7 @@ ProfileSyncServiceHarness::ProfileSyncServiceHarness(
min_timestamp_needed_(kMinTimestampNeededNone),
username_(username),
password_(password),
+ passphrase_acceptance_counter_(0),
id_(id) {
if (IsSyncAlreadySetup()) {
service_ = profile_->GetProfileSyncService();
@@ -132,6 +134,14 @@ bool ProfileSyncServiceHarness::SetupSync() {
return SetupSync(synced_datatypes);
}
+void ProfileSyncServiceHarness::StartObservingPassphraseAcceptance() {
+ // Prime the counter to account for the implicit set passphrase due to
+ // gaia login.
+ passphrase_acceptance_counter_--;
+ registrar_.Add(this, NotificationType::SYNC_PASSPHRASE_ACCEPTED,
+ Source<browser_sync::SyncBackendHost>(service_->backend()));
+}
+
bool ProfileSyncServiceHarness::SetupSync(
const syncable::ModelTypeSet& synced_datatypes) {
// Initialize the sync client's profile sync service object.
@@ -198,6 +208,7 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() {
if (service()->sync_initialized()) {
// The sync backend is initialized. Start waiting for the first sync
// cycle to complete.
+ StartObservingPassphraseAcceptance();
SignalStateCompleteWithNextState(WAITING_FOR_INITIAL_SYNC);
}
break;
@@ -251,7 +262,7 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() {
}
case WAITING_FOR_PASSPHRASE_ACCEPTED: {
LogClientInfo("WAITING_FOR_PASSPHRASE_ACCEPTED");
- if (!service()->observed_passphrase_required())
+ if (passphrase_acceptance_counter_ >= 0)
SignalStateCompleteWithNextState(FULLY_SYNCED);
break;
}
@@ -278,13 +289,21 @@ bool ProfileSyncServiceHarness::AwaitPassphraseAccepted() {
LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
return false;
}
- if (!service()->observed_passphrase_required())
+ passphrase_acceptance_counter_--;
+ if (passphrase_acceptance_counter_ >= 0)
return true;
wait_state_ = WAITING_FOR_PASSPHRASE_ACCEPTED;
return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs,
"Waiting for passphrase accepted.");
}
+void ProfileSyncServiceHarness::Observe(NotificationType type,
+ const NotificationSource& source, const NotificationDetails& details) {
+ DCHECK_EQ(NotificationType::SYNC_PASSPHRASE_ACCEPTED, type.value);
+ passphrase_acceptance_counter_++;
+ RunStateChangeMachine();
+}
+
bool ProfileSyncServiceHarness::AwaitSyncCycleCompletion(
const std::string& reason) {
LogClientInfo("AwaitSyncCycleCompletion");
@@ -412,6 +431,7 @@ bool ProfileSyncServiceHarness::IsSynced() {
// TODO(rsimha): Remove additional checks of snap->has_more_to_sync and
// snap->unsynced_count once http://crbug.com/48989 is fixed.
return (snap &&
+ snap->num_conflicting_updates == 0 && // We can decrypt everything.
ServiceIsPushingChanges() &&
GetStatus().notifications_enabled &&
!service()->backend()->HasUnsyncedItems() &&
diff --git a/chrome/browser/sync/profile_sync_service_harness.h b/chrome/browser/sync/profile_sync_service_harness.h
index a50bb8a..383c786 100644
--- a/chrome/browser/sync/profile_sync_service_harness.h
+++ b/chrome/browser/sync/profile_sync_service_harness.h
@@ -11,6 +11,8 @@
#include "base/time.h"
#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
using browser_sync::sessions::SyncSessionSnapshot;
@@ -21,7 +23,8 @@ class Profile;
// profile passed to it on construction and automates certain things like setup
// and authentication. It provides ways to "wait" adequate periods of time for
// several clients to get to the same state.
-class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
+class ProfileSyncServiceHarness : public ProfileSyncServiceObserver,
+ public NotificationObserver {
public:
ProfileSyncServiceHarness(Profile* profile,
const std::string& username,
@@ -54,6 +57,11 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
// ProfileSyncServiceObserver implementation.
virtual void OnStateChanged();
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
// Blocks the caller until this harness has completed a single sync cycle
// since the previous one. Returns true if a sync cycle has completed.
bool AwaitSyncCycleCompletion(const std::string& reason);
@@ -172,6 +180,8 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
// Returns the new value of |last_timestamp_|.
int64 GetUpdatedTimestamp();
+ void StartObservingPassphraseAcceptance();
+
WaitState wait_state_;
Profile* profile_;
@@ -191,6 +201,12 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
std::string username_;
std::string password_;
+ // A counter to track the number of await passphrase requests versus
+ // actual acceptances. Can go negative if #requests > #acceptances.
+ int passphrase_acceptance_counter_;
+
+ NotificationRegistrar registrar_;
+
// Client ID, used for logging purposes.
int id_;
diff --git a/chrome/browser/sync/profile_sync_service_password_unittest.cc b/chrome/browser/sync/profile_sync_service_password_unittest.cc
index 43ee1fb..dfb172b 100644
--- a/chrome/browser/sync/profile_sync_service_password_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc
@@ -198,7 +198,7 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest {
NotificationType(NotificationType::SYNC_CONFIGURE_DONE),
_,_)).
WillOnce(QuitUIMessageLoop());
- service_->SetPassphrase("foo");
+ service_->SetPassphrase("foo", false);
MessageLoop::current()->Run();
}
}
diff --git a/chrome/browser/sync/protocol/nigori_specifics.proto b/chrome/browser/sync/protocol/nigori_specifics.proto
index aa33ba6..e7458c6 100644
--- a/chrome/browser/sync/protocol/nigori_specifics.proto
+++ b/chrome/browser/sync/protocol/nigori_specifics.proto
@@ -28,6 +28,9 @@ message NigoriKeyBag {
// Properties of nigori sync object.
message NigoriSpecifics {
optional EncryptedData encrypted = 1;
+ // True if |encrypted| is encrypted using a passphrase
+ // explicitly set by the user.
+ optional bool using_explicit_passphrase = 2;
}
extend EntitySpecifics {
diff --git a/chrome/browser/sync/resources/passphrase.html b/chrome/browser/sync/resources/passphrase.html
index e18da44..4ea8317 100644
--- a/chrome/browser/sync/resources/passphrase.html
+++ b/chrome/browser/sync/resources/passphrase.html
@@ -52,8 +52,8 @@ html[os='mac'] input[type='submit'] {
</style>
<script src="chrome://resources/js/cr.js"></script>
-<script>
- var currentTab;
+<script>
+ var currentMode;
// Called once, when this html/js is loaded.
function setupPassphraseDialog(args) {
@@ -88,7 +88,8 @@ html[os='mac'] input[type='submit'] {
function sendPassphraseAndClose() {
var f = document.getElementById("passphraseForm");
- var result = JSON.stringify({"passphrase": f.passphrase.value});
+ var result = JSON.stringify({"passphrase": f.passphrase.value,
+ "mode": currentMode});
chrome.send("Passphrase", [result]);
}
</script>
diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc
index 08dcfa1..ff923da 100644
--- a/chrome/browser/sync/sync_setup_flow.cc
+++ b/chrome/browser/sync/sync_setup_flow.cc
@@ -68,13 +68,15 @@ static bool GetAuthData(const std::string& json,
return true;
}
-static bool GetPassphrase(const std::string& json, std::string* passphrase) {
+bool GetPassphrase(const std::string& json, std::string* passphrase,
+ std::string* mode) {
scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
return false;
DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
- return result->GetString("passphrase", passphrase);
+ return result->GetString("passphrase", passphrase) &&
+ result->GetString("mode", mode);
}
static bool GetConfiguration(const std::string& json,
@@ -194,14 +196,15 @@ void FlowHandler::HandlePassphraseEntry(const ListValue* args) {
return;
std::string passphrase;
- if (!GetPassphrase(json, &passphrase)) {
+ std::string mode;
+ if (!GetPassphrase(json, &passphrase, &mode)) {
// Couldn't understand what the page sent. Indicates a programming error.
NOTREACHED();
return;
}
DCHECK(flow_);
- flow_->OnPassphraseEntry(passphrase);
+ flow_->OnPassphraseEntry(passphrase, mode);
}
// Called by SyncSetupFlow::Advance.
@@ -654,6 +657,10 @@ void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
// we need to prompt them to enter one.
if (configuration.use_secondary_passphrase &&
!service_->IsUsingSecondaryPassphrase()) {
+ // TODO(tim): If we could download the Nigori node first before any other
+ // types, we could do that prior to showing the configure page so that we
+ // could pre-populate the 'Use an encryption passphrase' checkbox.
+ // http://crbug.com/60182
Advance(SyncSetupWizard::CREATE_PASSPHRASE);
return;
}
@@ -674,8 +681,9 @@ void SyncSetupFlow::OnConfigurationComplete() {
configuration_.secondary_passphrase.length() > 0);
if (configuration_.use_secondary_passphrase &&
- !service_->IsUsingSecondaryPassphrase())
- service_->SetSecondaryPassphrase(configuration_.secondary_passphrase);
+ !service_->IsUsingSecondaryPassphrase()) {
+ service_->SetPassphrase(configuration_.secondary_passphrase, true);
+ }
service_->OnUserChoseDatatypes(configuration_.sync_everything,
configuration_.data_types);
@@ -683,11 +691,13 @@ void SyncSetupFlow::OnConfigurationComplete() {
configuration_pending_ = false;
}
-void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase) {
+void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase,
+ const std::string& mode) {
if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE) {
- service_->SetSecondaryPassphrase(passphrase);
+ service_->SetPassphrase(passphrase, mode == std::string("enter"));
Advance(SyncSetupWizard::SETTING_UP);
} else if (configuration_pending_) {
+ DCHECK_EQ(SyncSetupWizard::CREATE_PASSPHRASE, current_state_);
configuration_.secondary_passphrase = passphrase;
OnConfigurationComplete();
}
diff --git a/chrome/browser/sync/sync_setup_flow.h b/chrome/browser/sync/sync_setup_flow.h
index 4b4b034..7d64402 100644
--- a/chrome/browser/sync/sync_setup_flow.h
+++ b/chrome/browser/sync/sync_setup_flow.h
@@ -115,7 +115,8 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
void OnUserConfigured(const SyncConfiguration& configuration);
- void OnPassphraseEntry(const std::string& passphrase);
+ void OnPassphraseEntry(const std::string& passphrase,
+ const std::string& mode);
void OnConfigurationComplete();
diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc
index ba432af..c8b733c 100644
--- a/chrome/browser/sync/sync_setup_wizard_unittest.cc
+++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc
@@ -59,7 +59,8 @@ class ProfileSyncServiceForWizardTest : public ProfileSyncService {
user_cancelled_dialog_ = true;
}
- virtual void SetSecondaryPassphrase(const std::string& passphrase) {
+ virtual void SetPassphrase(const std::string& passphrase,
+ bool is_explicit) {
passphrase_ = passphrase;
}
@@ -352,7 +353,8 @@ TEST_F(SyncSetupWizardTest, EnterPassphraseRequired) {
EXPECT_EQ(SyncSetupWizard::ENTER_PASSPHRASE,
test_window_->flow()->current_state_);
ListValue value;
- value.Append(new StringValue("{\"passphrase\":\"myPassphrase\"}"));
+ value.Append(new StringValue("{\"passphrase\":\"myPassphrase\","
+ "\"mode\":\"gaia\"}"));
test_window_->flow()->flow_handler_->HandlePassphraseEntry(&value);
EXPECT_EQ("myPassphrase", service_->passphrase_);
}
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 764ac52..3444e4e 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
@@ -68,12 +68,13 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, Race) {
}
// Marked as FAILS -- see http://crbug.com/59867.
-IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, FAILS_SetPassphrase) {
+IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, SetPassphrase) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
- GetClient(0)->service()->SetPassphrase(kValidPassphrase);
+ GetClient(0)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(0)->AwaitPassphraseAccepted();
GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1));
ASSERT_TRUE(GetClient(1)->service()->observed_passphrase_required());
- GetClient(1)->service()->SetPassphrase(kValidPassphrase);
+ GetClient(1)->service()->SetPassphrase(kValidPassphrase, true);
GetClient(1)->AwaitPassphraseAccepted();
ASSERT_FALSE(GetClient(1)->service()->observed_passphrase_required());
}
@@ -81,7 +82,8 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest, FAILS_SetPassphrase) {
IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest,
SetPassphraseAndAddPassword) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
- GetClient(0)->service()->SetPassphrase(kValidPassphrase);
+ GetClient(0)->service()->SetPassphrase(kValidPassphrase, true);
+ GetClient(0)->AwaitPassphraseAccepted();
PasswordForm form;
form.origin = GURL("http://www.google.com/");
@@ -95,10 +97,10 @@ IN_PROC_BROWSER_TEST_F(TwoClientLivePasswordsSyncTest,
ASSERT_EQ(1, GetClient(1)->GetLastSessionSnapshot()->
num_conflicting_updates);
- GetClient(1)->service()->SetPassphrase(kValidPassphrase);
- GetClient(1)->AwaitSyncCycleCompletion("Accept passphrase and decrypt.");
+ GetClient(1)->service()->SetPassphrase(kValidPassphrase, true);
GetClient(1)->AwaitPassphraseAccepted();
ASSERT_FALSE(GetClient(1)->service()->observed_passphrase_required());
+ GetClient(1)->AwaitSyncCycleCompletion("Accept passphrase and decrypt.");
ASSERT_EQ(0, GetClient(1)->GetLastSessionSnapshot()->
num_conflicting_updates);
}
diff --git a/net/tools/testserver/chromiumsync.py b/net/tools/testserver/chromiumsync.py
index 62d1bf7..b678238 100755
--- a/net/tools/testserver/chromiumsync.py
+++ b/net/tools/testserver/chromiumsync.py
@@ -487,6 +487,18 @@ class SyncDataModel(object):
# batch, even if that item was filtered out.
return (batch[-1].version, filtered, len(new_changes) - len(batch))
+ def _CopyOverImmutableFields(self, entry):
+ """Preserve immutable fields by copying pre-commit state.
+
+ Args:
+ entry: A sync entity from the client.
+ """
+ if entry.id_string in self._entries:
+ if self._entries[entry.id_string].HasField(
+ 'server_defined_unique_tag'):
+ entry.server_defined_unique_tag = (
+ self._entries[entry.id_string].server_defined_unique_tag)
+
def _CheckVersionForCommit(self, entry):
"""Perform an optimistic concurrency check on the version number.
@@ -599,6 +611,8 @@ class SyncDataModel(object):
if not self._CheckParentIdForCommit(entry):
return None
+ self._CopyOverImmutableFields(entry);
+
# At this point, the commit is definitely going to happen.
# Deletion works by storing a limited record for an entry, called a