1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Cli: Ask for master password when encryption or decryption fails

This commit is contained in:
Laurent Cozic 2021-11-22 12:46:57 +00:00
parent 0e11273c45
commit c19e59f5da
5 changed files with 52 additions and 23 deletions

View File

@ -78,6 +78,9 @@ packages/app-cli/app/command-e2ee.js.map
packages/app-cli/app/command-settingschema.d.ts
packages/app-cli/app/command-settingschema.js
packages/app-cli/app/command-settingschema.js.map
packages/app-cli/app/command-sync.d.ts
packages/app-cli/app/command-sync.js
packages/app-cli/app/command-sync.js.map
packages/app-cli/app/command-testing.d.ts
packages/app-cli/app/command-testing.js
packages/app-cli/app/command-testing.js.map

3
.gitignore vendored
View File

@ -61,6 +61,9 @@ packages/app-cli/app/command-e2ee.js.map
packages/app-cli/app/command-settingschema.d.ts
packages/app-cli/app/command-settingschema.js
packages/app-cli/app/command-settingschema.js.map
packages/app-cli/app/command-sync.d.ts
packages/app-cli/app/command-sync.js
packages/app-cli/app/command-sync.js.map
packages/app-cli/app/command-testing.d.ts
packages/app-cli/app/command-testing.js
packages/app-cli/app/command-testing.js.map

View File

@ -6,8 +6,8 @@ import BaseItem from '@joplin/lib/models/BaseItem';
import Setting from '@joplin/lib/models/Setting';
import shim from '@joplin/lib/shim';
import * as pathUtils from '@joplin/lib/path-utils';
import { getEncryptionEnabled } from '@joplin/lib/services/synchronizer/syncInfoUtils';
import { generateMasterKeyAndEnableEncryption, loadMasterKeysFromSettings, setupAndDisableEncryption } from '@joplin/lib/services/e2ee/utils';
import { getEncryptionEnabled, localSyncInfo } from '@joplin/lib/services/synchronizer/syncInfoUtils';
import { generateMasterKeyAndEnableEncryption, loadMasterKeysFromSettings, masterPasswordIsValid, setupAndDisableEncryption } from '@joplin/lib/services/e2ee/utils';
const imageType = require('image-type');
const readChunk = require('read-chunk');
@ -40,6 +40,13 @@ class Command extends BaseCommand {
this.stdout(_('Operation cancelled'));
return false;
}
const masterKey = localSyncInfo().masterKeys.find(mk => mk.id === masterKeyId);
if (!(await masterPasswordIsValid(password, masterKey))) {
this.stdout(_('Invalid password'));
return false;
}
Setting.setObjectValue('encryption.passwordCache', masterKeyId, password);
await loadMasterKeysFromSettings(EncryptionService.instance());
return true;

View File

@ -1,25 +1,24 @@
import { _ } from '@joplin/lib/locale';
import Setting from '@joplin/lib/models/Setting';
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
import MigrationHandler from '@joplin/lib/services/synchronizer/MigrationHandler';
import ResourceFetcher from '@joplin/lib/services/ResourceFetcher';
import Synchronizer from '@joplin/lib/Synchronizer';
import { masterKeysWithoutPassword } from '@joplin/lib/services/e2ee/utils';
const { BaseCommand } = require('./base-command.js');
const { app } = require('./app.js');
const { _ } = require('@joplin/lib/locale');
const { OneDriveApiNodeUtils } = require('@joplin/lib/onedrive-api-node-utils.js');
const Setting = require('@joplin/lib/models/Setting').default;
const ResourceFetcher = require('@joplin/lib/services/ResourceFetcher').default;
const Synchronizer = require('@joplin/lib/Synchronizer').default;
const { reg } = require('@joplin/lib/registry.js');
const { cliUtils } = require('./cli-utils.js');
const md5 = require('md5');
const locker = require('proper-lockfile');
const fs = require('fs-extra');
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry').default;
const MigrationHandler = require('@joplin/lib/services/synchronizer/MigrationHandler').default;
class Command extends BaseCommand {
constructor() {
super();
this.syncTargetId_ = null;
this.releaseLockFn_ = null;
this.oneDriveApiUtils_ = null;
}
private syncTargetId_: number = null;
private releaseLockFn_: Function = null;
private oneDriveApiUtils_: any = null;
usage() {
return 'sync';
@ -37,9 +36,9 @@ class Command extends BaseCommand {
];
}
static lockFile(filePath) {
static lockFile(filePath: string): Promise<Function> {
return new Promise((resolve, reject) => {
locker.lock(filePath, { stale: 1000 * 60 * 5 }, (error, release) => {
locker.lock(filePath, { stale: 1000 * 60 * 5 }, (error: any, release: any) => {
if (error) {
reject(error);
return;
@ -50,9 +49,9 @@ class Command extends BaseCommand {
});
}
static isLocked(filePath) {
static isLocked(filePath: string) {
return new Promise((resolve, reject) => {
locker.check(filePath, (error, isLocked) => {
locker.check(filePath, (error: any, isLocked: boolean) => {
if (error) {
reject(error);
return;
@ -71,7 +70,7 @@ class Command extends BaseCommand {
// OneDrive
this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api());
const auth = await this.oneDriveApiUtils_.oauthDance({
log: (...s) => {
log: (...s: any[]) => {
return this.stdout(...s);
},
});
@ -118,7 +117,7 @@ class Command extends BaseCommand {
return !!this.oneDriveApiUtils_;
}
async action(args) {
async action(args: any) {
this.releaseLockFn_ = null;
// Lock is unique per profile/database
@ -166,12 +165,12 @@ class Command extends BaseCommand {
const sync = await syncTarget.synchronizer();
const options = {
onProgress: report => {
const options: any = {
onProgress: (report: any) => {
const lines = Synchronizer.reportToLines(report);
if (lines.length) cliUtils.redraw(lines.join(' '));
},
onMessage: msg => {
onMessage: (msg: string) => {
cliUtils.redrawDone();
this.stdout(msg);
},
@ -238,6 +237,9 @@ class Command extends BaseCommand {
await ResourceFetcher.instance().waitForAllFinished();
}
const noPasswordMkIds = await masterKeysWithoutPassword();
if (noPasswordMkIds.length) this.stdout(`/!\\ ${_('Your password is needed to decrypt some of your data. Type `:e2ee decrypt` to set it.')}`);
await app().refreshCurrentFolder();
} catch (error) {
cleanUp();

View File

@ -340,3 +340,17 @@ export async function masterPasswordIsValid(masterPassword: string, activeMaster
// compare to whatever they've entered earlier.
return Setting.value('encryption.masterPassword') === masterPassword;
}
export async function masterKeysWithoutPassword(): Promise<string[]> {
const syncInfo = localSyncInfo();
const passwordCache = Setting.value('encryption.passwordCache');
const output: string[] = [];
for (const mk of syncInfo.masterKeys) {
if (!masterKeyEnabled(mk)) continue;
const password = await findMasterKeyPassword(EncryptionService.instance(), mk, passwordCache);
if (!password) output.push(mk.id);
}
return output;
}