From 04c6579f2cc54c08c7eb816db5150e1155631434 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Tue, 28 May 2019 18:10:21 +0100 Subject: [PATCH] All: Fix: Fix issue with revisions being needlessly created when decrypting notes --- CliClient/tests/synchronizer.js | 36 +++++++++++++++++++ ReactNativeClient/android/app/build.gradle | 4 +-- ReactNativeClient/lib/models/BaseItem.js | 3 +- ReactNativeClient/lib/models/ItemChange.js | 1 + .../lib/services/RevisionService.js | 3 +- 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/CliClient/tests/synchronizer.js b/CliClient/tests/synchronizer.js index ba205ae67..3d1f68274 100644 --- a/CliClient/tests/synchronizer.js +++ b/CliClient/tests/synchronizer.js @@ -1385,4 +1385,40 @@ describe('Synchronizer', function() { expect(!!resource.encryption_blob_encrypted).toBe(false); })); + it('should not create revisions when item is modified as a result of decryption', asyncTest(async () => { + // Handle this scenario: + // - C1 creates note + // - C1 never changes it + // - E2EE is enabled + // - C1 sync + // - More than one week later (as defined by oldNoteCutOffDate_), C2 sync + // - C2 enters master password and note gets decrypted + // + // Technically at this point the note is modified (from encrypted to non-encrypted) and thus a ItemChange + // object is created. The note is also older than oldNoteCutOffDate. However, this should not lead to the + // creation of a revision because that change was not the result of a user action. + // I guess that's the general rule - changes that come from user actions should result in revisions, + // while automated changes (sync, decryption) should not. + + const dateInPast = revisionService().oldNoteCutOffDate_() - 1000; + + const note1 = await Note.save({ title: 'ma note', updated_time: dateInPast, created_time: dateInPast }, { autoTimestamp: false }); + const masterKey = await loadEncryptionMasterKey(); + await encryptionService().enableEncryption(masterKey, '123456'); + await encryptionService().loadMasterKeysFromSettings(); + await synchronizer().start(); + + await switchClient(2); + + await synchronizer().start(); + + Setting.setObjectKey('encryption.passwordCache', masterKey.id, '123456'); + await encryptionService().loadMasterKeysFromSettings(); + await decryptionWorker().start(); + + await revisionService().collectRevisions(); + + expect((await Revision.all()).length).toBe(0); + })); + }); diff --git a/ReactNativeClient/android/app/build.gradle b/ReactNativeClient/android/app/build.gradle index 1ed263c86..7f251cf80 100644 --- a/ReactNativeClient/android/app/build.gradle +++ b/ReactNativeClient/android/app/build.gradle @@ -90,8 +90,8 @@ android { applicationId "net.cozic.joplin" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 2097491 - versionName "1.0.255" + versionCode 2097494 + versionName "1.0.258" ndk { abiFilters "armeabi-v7a", "x86" } diff --git a/ReactNativeClient/lib/models/BaseItem.js b/ReactNativeClient/lib/models/BaseItem.js index 3618e8254..5fd542af4 100644 --- a/ReactNativeClient/lib/models/BaseItem.js +++ b/ReactNativeClient/lib/models/BaseItem.js @@ -1,6 +1,7 @@ const BaseModel = require('lib/BaseModel.js'); const { Database } = require('lib/database.js'); const Setting = require('lib/models/Setting.js'); +const ItemChange = require('lib/models/ItemChange.js'); const JoplinError = require('lib/JoplinError.js'); const { time } = require('lib/time-utils.js'); const { sprintf } = require('sprintf-js'); @@ -355,7 +356,7 @@ class BaseItem extends BaseModel { plainItem.updated_time = item.updated_time; plainItem.encryption_cipher_text = ''; plainItem.encryption_applied = 0; - return ItemClass.save(plainItem, { autoTimestamp: false }); + return ItemClass.save(plainItem, { autoTimestamp: false, changeSource: ItemChange.SOURCE_DECRYPTION }); } static async unserialize(content) { diff --git a/ReactNativeClient/lib/models/ItemChange.js b/ReactNativeClient/lib/models/ItemChange.js index 656a1dacd..31eadcfe4 100644 --- a/ReactNativeClient/lib/models/ItemChange.js +++ b/ReactNativeClient/lib/models/ItemChange.js @@ -66,5 +66,6 @@ ItemChange.TYPE_DELETE = 3; ItemChange.SOURCE_UNSPECIFIED = 1; ItemChange.SOURCE_SYNC = 2; +ItemChange.SOURCE_DECRYPTION = 2; module.exports = ItemChange; \ No newline at end of file diff --git a/ReactNativeClient/lib/services/RevisionService.js b/ReactNativeClient/lib/services/RevisionService.js index 6321e7d30..d47a51c88 100644 --- a/ReactNativeClient/lib/services/RevisionService.js +++ b/ReactNativeClient/lib/services/RevisionService.js @@ -118,10 +118,11 @@ class RevisionService extends BaseService { FROM item_changes WHERE item_type = ? AND source != ? + AND source != ? AND id > ? ORDER BY id ASC LIMIT 10 - `, [BaseModel.TYPE_NOTE, ItemChange.SOURCE_SYNC, Setting.value('revisionService.lastProcessedChangeId')]); + `, [BaseModel.TYPE_NOTE, ItemChange.SOURCE_SYNC, ItemChange.SOURCE_DECRYPTION, Setting.value('revisionService.lastProcessedChangeId')]); if (!changes.length) break;