Commit 8c0f6ea8 authored by Mohamed Amir Yosef's avatar Mohamed Amir Yosef Committed by Commit Bot

[Sync:USS] Handle new encryption requirements for bookmarks updates

Bug: 516866
Change-Id: I8f1fce1850629fef4f7d2eb81adc57a6f74429ea
Reviewed-on: https://chromium-review.googlesource.com/c/1292879
Commit-Queue: Mohamed Amir Yosef <mamir@chromium.org>
Reviewed-by: default avatarMikel Astiz <mastiz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#601904}
parent 250d12a4
......@@ -1987,6 +1987,38 @@ IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
ASSERT_TRUE(BookmarksMatchChecker().Wait());
}
// Enable enccryption and then trigger the server side creation of Synced
// Bookmarks. Ensure both clients remain syncing afterwards. Add bookmarks to
// the synced bookmarks folder and ensure both clients receive the bookmark.
IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
CreateSyncedBookmarksWithSingleClientEnableEncryption) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(AllModelsMatchVerifier());
// Enable the encryption on Client 0.
ASSERT_TRUE(EnableEncryption(0));
ASSERT_TRUE(GetClient(0)->AwaitMutualSyncCycleCompletion(GetClient(1)));
ASSERT_TRUE(BookmarksMatchChecker().Wait());
fake_server_->InjectEntity(syncer::PersistentPermanentEntity::CreateNew(
syncer::BOOKMARKS, "synced_bookmarks", "Synced Bookmarks",
"google_chrome_bookmarks"));
ASSERT_TRUE(BookmarksMatchChecker().Wait());
// Add a bookmark on Client 0 and ensure it syncs over. This will also trigger
// both clients downloading the new Synced Bookmarks folder.
ASSERT_NE(nullptr, AddURL(0, "Google", GURL("http://www.google.com")));
ASSERT_TRUE(BookmarksMatchChecker().Wait());
// Now add a bookmark within the Synced Bookmarks folder and ensure it syncs
// over.
const BookmarkNode* synced_bookmarks = GetSyncedBookmarksNode(0);
ASSERT_TRUE(synced_bookmarks);
ASSERT_NE(nullptr, AddURL(0, synced_bookmarks, 0, "Google2",
GURL("http://www.google2.com")));
ASSERT_TRUE(BookmarksMatchChecker().Wait());
}
IN_PROC_BROWSER_TEST_P(TwoClientBookmarksSyncTestIncludingUssTests,
BookmarkAllNodesRemovedEvent) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
......
......@@ -179,7 +179,10 @@ void BookmarkModelTypeProcessor::OnUpdateReceived(
bookmark_model_, bookmark_undo_service_, bookmark_model_observer_.get());
BookmarkRemoteUpdatesHandler updates_handler(
bookmark_model_, favicon_service_, bookmark_tracker_.get());
updates_handler.Process(updates);
const bool got_new_encryption_requirements =
bookmark_tracker_->model_type_state().encryption_key_name() !=
model_type_state.encryption_key_name();
updates_handler.Process(updates, got_new_encryption_requirements);
bookmark_tracker_->set_model_type_state(
std::make_unique<sync_pb::ModelTypeState>(model_type_state));
// Schedule save just in case one is needed.
......
......@@ -7,6 +7,7 @@
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include "base/metrics/histogram_macros.h"
......@@ -131,7 +132,13 @@ BookmarkRemoteUpdatesHandler::BookmarkRemoteUpdatesHandler(
}
void BookmarkRemoteUpdatesHandler::Process(
const syncer::UpdateResponseDataList& updates) {
const syncer::UpdateResponseDataList& updates,
bool got_new_encryption_requirements) {
// If new encryption requirements come from the server, the entities that are
// in |updates| will be recorded here so they can be ignored during the
// re-encryption phase at the end.
std::unordered_set<std::string> entities_with_up_to_date_encryption;
for (const syncer::UpdateResponseData* update : ReorderUpdates(&updates)) {
const syncer::EntityData& update_entity = update->entity.value();
// Only non deletions and non premanent node should have valid specifics and
......@@ -182,26 +189,64 @@ void BookmarkRemoteUpdatesHandler::Process(
tracked_entity = bookmark_tracker_->GetEntityForSyncId(update_entity.id);
}
// TODO(crbug.com/516866): Handle the case of conflict as a result of
// re-encryption request.
if (tracked_entity && tracked_entity->IsUnsynced()) {
ProcessConflict(*update, tracked_entity);
continue;
}
if (update_entity.is_deleted()) {
} else if (update_entity.is_deleted()) {
ProcessDelete(update_entity, tracked_entity);
// If the local entity has been deleted, no need to check for out of date
// encryption. Therefore, we can go ahead and process the next update.
continue;
}
if (!tracked_entity) {
} else if (!tracked_entity) {
ProcessCreate(*update);
continue;
// Because the Synced Bookmarks node can be created server side, it's
// possible it'll arrive at the client as a creation. No need to check
// encryption for permanent folders.
if (update_entity.server_defined_unique_tag == kMobileBookmarksTag) {
continue;
}
} else {
// Ignore changes to the permanent nodes (e.g. bookmarks bar). We only
// care about their children.
if (bookmark_model_->is_permanent_node(tracked_entity->bookmark_node())) {
continue;
}
ProcessUpdate(*update, tracked_entity);
}
// Ignore changes to the permanent nodes (e.g. bookmarks bar). We only care
// about their children.
if (bookmark_model_->is_permanent_node(tracked_entity->bookmark_node())) {
continue;
// If the received entity has out of date encryption, we schedule another
// commit to fix it.
if (bookmark_tracker_->model_type_state().encryption_key_name() !=
update->encryption_key_name) {
DVLOG(2) << "Bookmarks: Requesting re-encrypt commit "
<< update->encryption_key_name << " -> "
<< bookmark_tracker_->model_type_state().encryption_key_name();
bookmark_tracker_->IncrementSequenceNumber(update_entity.id);
}
if (got_new_encryption_requirements) {
entities_with_up_to_date_encryption.insert(update_entity.id);
}
}
// Recommit entities with out of date encryption.
if (got_new_encryption_requirements) {
std::vector<const SyncedBookmarkTracker::Entity*> all_entities =
bookmark_tracker_->GetAllEntities();
for (const SyncedBookmarkTracker::Entity* entity : all_entities) {
// No need to recommit tombstones and permanent nodes.
if (entity->metadata()->is_deleted()) {
continue;
}
DCHECK(entity->bookmark_node());
if (entity->bookmark_node()->is_permanent_node()) {
continue;
}
if (entities_with_up_to_date_encryption.count(
entity->metadata()->server_id()) != 0) {
continue;
}
bookmark_tracker_->IncrementSequenceNumber(
entity->metadata()->server_id());
}
ProcessUpdate(*update, tracked_entity);
}
}
......@@ -421,6 +466,9 @@ void BookmarkRemoteUpdatesHandler::ProcessConflict(
const syncer::UpdateResponseData& update,
const SyncedBookmarkTracker::Entity* tracked_entity) {
const syncer::EntityData& update_entity = update.entity.value();
// TODO(crbug.com/516866): Handle the case of conflict as a result of
// re-encryption request.
// TODO(crbug.com/516866): Add basic unit test for this function.
// Can only conflict with existing nodes.
......@@ -481,7 +529,7 @@ void BookmarkRemoteUpdatesHandler::ProcessConflict(
const bookmarks::BookmarkNode* new_parent =
new_parent_entity->bookmark_node();
// |new_parent| would be null if the parent has been deleted locally and not
// committed yet. Deletions are excuted recusively, so a parent deletions
// committed yet. Deletions are executed recursively, so a parent deletions
// entails child deletion, and if this child has been updated on another
// client, this would cause conflict.
if (!new_parent) {
......
......@@ -33,8 +33,12 @@ class BookmarkRemoteUpdatesHandler {
favicon::FaviconService* favicon_service,
SyncedBookmarkTracker* bookmark_tracker);
// Processes the updates received from the sync server in |updates| and
// updates the |bookmark_model_| and |bookmark_tracker_| accordingly.
void Process(const syncer::UpdateResponseDataList& updates);
// updates the |bookmark_model_| and |bookmark_tracker_| accordingly. If
// |got_new_encryption_requirements| is true, it recommits all tracked
// entities except those in |updates| which should use the latest encryption
// key and hence don't need recommitting.
void Process(const syncer::UpdateResponseDataList& updates,
bool got_new_encryption_requirements);
// Public for testing.
static std::vector<const syncer::UpdateResponseData*> ReorderUpdatesForTest(
......
......@@ -297,6 +297,16 @@ bool SyncedBookmarkTracker::HasLocalChanges() const {
return false;
}
std::vector<const SyncedBookmarkTracker::Entity*>
SyncedBookmarkTracker::GetAllEntities() const {
std::vector<const SyncedBookmarkTracker::Entity*> entities;
for (const std::pair<const std::string, std::unique_ptr<Entity>>& pair :
sync_id_to_entities_map_) {
entities.push_back(pair.second.get());
}
return entities;
}
std::vector<const SyncedBookmarkTracker::Entity*>
SyncedBookmarkTracker::GetEntitiesWithLocalChanges(size_t max_entries) const {
std::vector<const SyncedBookmarkTracker::Entity*> entities_with_local_changes;
......
......@@ -157,6 +157,8 @@ class SyncedBookmarkTracker {
model_type_state_ = std::move(model_type_state);
}
std::vector<const Entity*> GetAllEntities() const;
std::vector<const Entity*> GetEntitiesWithLocalChanges(
size_t max_entries) const;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment