summaryrefslogtreecommitdiffstats
path: root/sync/engine/model_type_sync_worker_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sync/engine/model_type_sync_worker_impl.cc')
-rw-r--r--sync/engine/model_type_sync_worker_impl.cc209
1 files changed, 188 insertions, 21 deletions
diff --git a/sync/engine/model_type_sync_worker_impl.cc b/sync/engine/model_type_sync_worker_impl.cc
index a5402c2..a58400f4 100644
--- a/sync/engine/model_type_sync_worker_impl.cc
+++ b/sync/engine/model_type_sync_worker_impl.cc
@@ -13,6 +13,7 @@
#include "sync/engine/model_type_sync_proxy.h"
#include "sync/engine/non_blocking_type_commit_contribution.h"
#include "sync/syncable/syncable_util.h"
+#include "sync/util/cryptographer.h"
#include "sync/util/time.h"
namespace syncer {
@@ -20,11 +21,14 @@ namespace syncer {
ModelTypeSyncWorkerImpl::ModelTypeSyncWorkerImpl(
ModelType type,
const DataTypeState& initial_state,
+ const UpdateResponseDataList& saved_pending_updates,
+ CryptographerProvider* cryptographer_provider,
NudgeHandler* nudge_handler,
scoped_ptr<ModelTypeSyncProxy> type_sync_proxy)
: type_(type),
data_type_state_(initial_state),
type_sync_proxy_(type_sync_proxy.Pass()),
+ cryptographer_provider_(cryptographer_provider),
nudge_handler_(nudge_handler),
entities_deleter_(&entities_),
weak_ptr_factory_(this) {
@@ -32,6 +36,18 @@ ModelTypeSyncWorkerImpl::ModelTypeSyncWorkerImpl(
if (!data_type_state_.initial_sync_done) {
nudge_handler_->NudgeForInitialDownload(type_);
}
+
+ for (UpdateResponseDataList::const_iterator it =
+ saved_pending_updates.begin();
+ it != saved_pending_updates.end();
+ ++it) {
+ EntityTracker* entity_tracker = EntityTracker::FromServerUpdate(
+ it->id, it->client_tag_hash, it->response_version);
+ entity_tracker->ReceivePendingUpdate(*it);
+ entities_.insert(std::make_pair(it->client_tag_hash, entity_tracker));
+ }
+
+ TryDecryptPendingUpdates();
}
ModelTypeSyncWorkerImpl::~ModelTypeSyncWorkerImpl() {
@@ -42,6 +58,33 @@ ModelType ModelTypeSyncWorkerImpl::GetModelType() const {
return type_;
}
+bool ModelTypeSyncWorkerImpl::IsEncryptionRequired() const {
+ return !data_type_state_.encryption_key_name.empty();
+}
+
+void ModelTypeSyncWorkerImpl::SetEncryptionKeyName(const std::string& name) {
+ if (data_type_state_.encryption_key_name == name)
+ return;
+
+ data_type_state_.encryption_key_name = name;
+
+ // Pretend to send an update. This will cause the TypeSyncProxy to notice
+ // the new encryption key and take appropriate action.
+ type_sync_proxy_->OnUpdateReceived(
+ data_type_state_, UpdateResponseDataList(), UpdateResponseDataList());
+}
+
+void ModelTypeSyncWorkerImpl::OnCryptographerStateChanged() {
+ TryDecryptPendingUpdates();
+
+ ScopedCryptographerRef scoped_cryptographer_ref;
+ cryptographer_provider_->InitScopedCryptographerRef(
+ &scoped_cryptographer_ref);
+ Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
+ if (CanCommitItems(cryptographer))
+ nudge_handler_->NudgeForCommit(type_);
+}
+
// UpdateHandler implementation.
void ModelTypeSyncWorkerImpl::GetDownloadProgress(
sync_pb::DataTypeProgressMarker* progress_marker) const {
@@ -66,7 +109,14 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse(
data_type_state_.type_context = mutated_context;
data_type_state_.progress_marker = progress_marker;
+ ScopedCryptographerRef scoped_cryptographer_ref;
+ cryptographer_provider_->InitScopedCryptographerRef(
+ &scoped_cryptographer_ref);
+ Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
+ DCHECK(cryptographer);
+
UpdateResponseDataList response_datas;
+ UpdateResponseDataList pending_updates;
for (SyncEntityList::const_iterator update_it = applicable_updates.begin();
update_it != applicable_updates.end();
@@ -86,16 +136,17 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse(
const std::string& client_tag_hash =
update_entity->client_defined_unique_tag();
DCHECK(!client_tag_hash.empty());
+
+ EntityTracker* entity_tracker = NULL;
EntityMap::const_iterator map_it = entities_.find(client_tag_hash);
if (map_it == entities_.end()) {
- EntityTracker* entity =
+ entity_tracker =
EntityTracker::FromServerUpdate(update_entity->id_string(),
client_tag_hash,
update_entity->version());
- entities_.insert(std::make_pair(client_tag_hash, entity));
+ entities_.insert(std::make_pair(client_tag_hash, entity_tracker));
} else {
- EntityTracker* entity = map_it->second;
- entity->ReceiveUpdate(update_entity->version());
+ entity_tracker = map_it->second;
}
// Prepare the message for the model thread.
@@ -107,14 +158,39 @@ SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse(
response_data.mtime = ProtoTimeToTime(update_entity->mtime());
response_data.non_unique_name = update_entity->name();
response_data.deleted = update_entity->deleted();
- response_data.specifics = update_entity->specifics();
- response_datas.push_back(response_data);
+ const sync_pb::EntitySpecifics& specifics = update_entity->specifics();
+
+ if (!specifics.has_encrypted()) {
+ // No encryption.
+ entity_tracker->ReceiveUpdate(update_entity->version());
+ response_data.specifics = specifics;
+ response_datas.push_back(response_data);
+ } else if (specifics.has_encrypted() &&
+ cryptographer->CanDecrypt(specifics.encrypted())) {
+ // Encrypted, but we know the key.
+ if (DecryptSpecifics(
+ cryptographer, specifics, &response_data.specifics)) {
+ entity_tracker->ReceiveUpdate(update_entity->version());
+ response_data.encryption_key_name = specifics.encrypted().key_name();
+ response_datas.push_back(response_data);
+ }
+ } else if (specifics.has_encrypted() &&
+ !cryptographer->CanDecrypt(specifics.encrypted())) {
+ // Can't decrypt right now. Ask the entity tracker to handle it.
+ response_data.specifics = specifics;
+ if (entity_tracker->ReceivePendingUpdate(response_data)) {
+ // Send to the model thread for safe-keeping across restarts if the
+ // tracker decides the update is worth keeping.
+ pending_updates.push_back(response_data);
+ }
+ }
}
}
// Forward these updates to the model thread so it can do the rest.
- type_sync_proxy_->OnUpdateReceived(data_type_state_, response_datas);
+ type_sync_proxy_->OnUpdateReceived(
+ data_type_state_, response_datas, pending_updates);
return SYNCER_OK;
}
@@ -128,8 +204,8 @@ void ModelTypeSyncWorkerImpl::ApplyUpdates(sessions::StatusController* status) {
if (!data_type_state_.initial_sync_done) {
data_type_state_.initial_sync_done = true;
- UpdateResponseDataList empty_update_list;
- type_sync_proxy_->OnUpdateReceived(data_type_state_, empty_update_list);
+ type_sync_proxy_->OnUpdateReceived(
+ data_type_state_, UpdateResponseDataList(), UpdateResponseDataList());
}
}
@@ -144,7 +220,7 @@ void ModelTypeSyncWorkerImpl::EnqueueForCommit(
const CommitRequestDataList& list) {
DCHECK(CalledOnValidThread());
- DCHECK(CanCommitItems())
+ DCHECK(IsTypeInitialized())
<< "Asked to commit items before type was initialized. "
<< "ModelType is: " << ModelTypeToString(type_);
@@ -153,6 +229,13 @@ void ModelTypeSyncWorkerImpl::EnqueueForCommit(
++it) {
StorePendingCommit(*it);
}
+
+ ScopedCryptographerRef scoped_cryptographer_ref;
+ cryptographer_provider_->InitScopedCryptographerRef(
+ &scoped_cryptographer_ref);
+ Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
+ if (CanCommitItems(cryptographer))
+ nudge_handler_->NudgeForCommit(type_);
}
// CommitContributor implementation.
@@ -164,7 +247,12 @@ scoped_ptr<CommitContribution> ModelTypeSyncWorkerImpl::GetContribution(
std::vector<int64> sequence_numbers;
google::protobuf::RepeatedPtrField<sync_pb::SyncEntity> commit_entities;
- if (!CanCommitItems())
+ ScopedCryptographerRef scoped_cryptographer_ref;
+ cryptographer_provider_->InitScopedCryptographerRef(
+ &scoped_cryptographer_ref);
+ Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
+
+ if (!CanCommitItems(cryptographer))
return scoped_ptr<CommitContribution>();
// TODO(rlarocque): Avoid iterating here.
@@ -177,7 +265,7 @@ scoped_ptr<CommitContribution> ModelTypeSyncWorkerImpl::GetContribution(
int64 sequence_number = -1;
entity->PrepareCommitProto(commit_entity, &sequence_number);
- HelpInitializeCommitEntity(commit_entity);
+ HelpInitializeCommitEntity(cryptographer, commit_entity);
sequence_numbers.push_back(sequence_number);
space_remaining--;
@@ -222,9 +310,6 @@ void ModelTypeSyncWorkerImpl::StorePendingCommit(
request.deleted,
request.specifics);
}
-
- if (CanCommitItems())
- nudge_handler_->NudgeForCommit(type_);
}
void ModelTypeSyncWorkerImpl::OnCommitResponse(
@@ -260,14 +345,30 @@ base::WeakPtr<ModelTypeSyncWorkerImpl> ModelTypeSyncWorkerImpl::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
-bool ModelTypeSyncWorkerImpl::CanCommitItems() const {
- // We can't commit anything until we know the type's parent node.
- // We'll get it in the first update response.
+bool ModelTypeSyncWorkerImpl::IsTypeInitialized() const {
return !data_type_state_.type_root_id.empty() &&
data_type_state_.initial_sync_done;
}
+bool ModelTypeSyncWorkerImpl::CanCommitItems(
+ Cryptographer* cryptographer) const {
+ // We can't commit anything until we know the type's parent node.
+ // We'll get it in the first update response.
+ if (!IsTypeInitialized())
+ return false;
+
+ // Don't commit if we should be encrypting but don't have the required keys.
+ if (IsEncryptionRequired() && (!cryptographer || !cryptographer->is_ready() ||
+ cryptographer->GetDefaultNigoriKeyName() !=
+ data_type_state_.encryption_key_name)) {
+ return false;
+ }
+
+ return true;
+}
+
void ModelTypeSyncWorkerImpl::HelpInitializeCommitEntity(
+ Cryptographer* cryptographer,
sync_pb::SyncEntity* sync_entity) {
// Initial commits need our help to generate a client ID.
if (!sync_entity->has_id_string()) {
@@ -277,14 +378,80 @@ void ModelTypeSyncWorkerImpl::HelpInitializeCommitEntity(
base::StringPrintf("%s-%" PRId64, ModelTypeToString(type_), id));
}
+ // Encrypt the specifics and hide the title if necessary.
+ if (IsEncryptionRequired()) {
+ sync_pb::EntitySpecifics encrypted_specifics;
+ cryptographer->Encrypt(sync_entity->specifics(),
+ encrypted_specifics.mutable_encrypted());
+ sync_entity->mutable_specifics()->CopyFrom(encrypted_specifics);
+ sync_entity->set_name("encrypted");
+ }
+
// Always include enough specifics to identify the type. Do this even in
// deletion requests, where the specifics are otherwise invalid.
- if (!sync_entity->has_specifics()) {
- AddDefaultFieldValue(type_, sync_entity->mutable_specifics());
- }
+ AddDefaultFieldValue(type_, sync_entity->mutable_specifics());
// We're always responsible for the parent ID.
sync_entity->set_parent_id_string(data_type_state_.type_root_id);
}
+void ModelTypeSyncWorkerImpl::TryDecryptPendingUpdates() {
+ UpdateResponseDataList response_datas;
+
+ ScopedCryptographerRef scoped_cryptographer_ref;
+ cryptographer_provider_->InitScopedCryptographerRef(
+ &scoped_cryptographer_ref);
+ Cryptographer* cryptographer = scoped_cryptographer_ref.Get();
+ DCHECK(cryptographer);
+
+ for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
+ ++it) {
+ if (it->second->HasPendingUpdate()) {
+ const UpdateResponseData& saved_pending = it->second->GetPendingUpdate();
+
+ // We assume all pending updates are encrypted items for which we
+ // don't have the key.
+ DCHECK(saved_pending.specifics.has_encrypted());
+
+ if (cryptographer->CanDecrypt(saved_pending.specifics.encrypted())) {
+ UpdateResponseData decrypted_response = saved_pending;
+ if (DecryptSpecifics(cryptographer,
+ saved_pending.specifics,
+ &decrypted_response.specifics)) {
+ decrypted_response.encryption_key_name =
+ saved_pending.specifics.encrypted().key_name();
+ response_datas.push_back(decrypted_response);
+
+ it->second->ClearPendingUpdate();
+ }
+ }
+ }
+ }
+
+ if (!response_datas.empty()) {
+ type_sync_proxy_->OnUpdateReceived(
+ data_type_state_, response_datas, UpdateResponseDataList());
+ }
+}
+
+bool ModelTypeSyncWorkerImpl::DecryptSpecifics(
+ Cryptographer* cryptographer,
+ const sync_pb::EntitySpecifics& in,
+ sync_pb::EntitySpecifics* out) {
+ DCHECK(in.has_encrypted());
+ DCHECK(cryptographer->CanDecrypt(in.encrypted()));
+
+ std::string plaintext;
+ plaintext = cryptographer->DecryptToString(in.encrypted());
+ if (plaintext.empty()) {
+ LOG(ERROR) << "Failed to decrypt a decryptable entity";
+ return false;
+ }
+ if (!out->ParseFromString(plaintext)) {
+ LOG(ERROR) << "Failed to parse decrypted entity";
+ return false;
+ }
+ return true;
+}
+
} // namespace syncer