Skip to content

Commit 887b073

Browse files
authored
Merge pull request meld-cp#85 from meld-cp:meld-cp/issue63
Auto decrypt note if password known
2 parents 3113bb6 + 9e771e7 commit 887b073

File tree

8 files changed

+126
-71
lines changed

8 files changed

+126
-71
lines changed

.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{
22
"cSpell.words": [
3-
"decryptable"
3+
"decryptable",
4+
"encryptable",
5+
"Inplace"
46
]
57
}

src/features/feature-inplace-encrypt/FeatureInplaceEncrypt.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
241241
let defaultPassword = '';
242242
let defaultHint = selectionAnalysis.decryptable?.hint;
243243
if ( this.pluginSettings.rememberPassword ){
244-
const bestGuessPasswordAndHint = SessionPasswordService.getBestGuess( activeFile );
244+
const bestGuessPasswordAndHint = SessionPasswordService.get( activeFile );
245245
//console.debug({bestGuessPasswordAndHint});
246246

247247
defaultPassword = bestGuessPasswordAndHint.password;

src/features/feature-whole-note-encrypt/EncryptedFileContentView.ts

Lines changed: 60 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ export class EncryptedFileContentView extends TextFileView {
3131
//console.debug('EncryptedFileContentView.constructor', {leaf});
3232

3333
this.elActionIconLockNote = this.addAction( 'lock', 'Lock', () => this.actionLockFile() );
34-
3534
this.elActionChangePassword = this.addAction( 'key', 'Change Password', () => this.actionChangePassword() );
35+
this.elActionIconLockNote.hide();
36+
this.elActionChangePassword.hide();
3637

3738
this.contentEl.style.display = 'flex';
3839
this.contentEl.style.flexDirection = 'column';
@@ -42,6 +43,7 @@ export class EncryptedFileContentView extends TextFileView {
4243

4344
private actionLockFile(){
4445
this.encryptionPassword = '';
46+
SessionPasswordService.clearForFile( this.file );
4547
this.refreshView(EncryptedFileContentViewStateEnum.decryptNote);
4648
}
4749

@@ -72,7 +74,7 @@ export class EncryptedFileContentView extends TextFileView {
7274
super.onPaneMenu(menu,source);
7375
}
7476

75-
private createTitle( title:string ) : HTMLElement{
77+
private addTitle( title:string ) : HTMLElement{
7678
return this.contentEl.createDiv({
7779
text : `🔐 ${title} 🔐`,
7880
attr : {
@@ -93,9 +95,12 @@ export class EncryptedFileContentView extends TextFileView {
9395
return passwordMatch ? '' :'Password doesn\'t match';
9496
}
9597

96-
private createNewNoteView() : HTMLElement {
98+
private addNewNoteView() {
99+
100+
this.addTitle('This note will be encrypted');
101+
97102
//console.debug('createDecryptNoteView', { "hint": this.hint} );
98-
const container = this.createInputContainer();
103+
const container = this.addInputContainer();
99104

100105
new Setting(container)
101106
.setDesc('Please provide a password and hint to start editing this note.')
@@ -123,7 +128,7 @@ export class EncryptedFileContentView extends TextFileView {
123128
}
124129
}
125130

126-
const bestGuessPassAndHint = SessionPasswordService.getBestGuess( this.file );
131+
const bestGuessPassAndHint = SessionPasswordService.get( this.file );
127132
let password = bestGuessPassAndHint.password;
128133
let confirm = '';
129134
let hint = bestGuessPassAndHint.hint;
@@ -191,24 +196,21 @@ export class EncryptedFileContentView extends TextFileView {
191196
})
192197
;
193198

194-
return container;
195199
}
196200

201+
private addDecryptNoteView() {
202+
203+
this.addTitle('This note is encrypted');
197204

198-
private createDecryptNoteView() : HTMLElement {
199-
const container = this.createInputContainer();
200-
205+
const container = this.addInputContainer();
206+
201207
new Setting(container)
202208
.setDesc('Please provide a password to unlock this note.')
203209
;
204210

205-
const bestGuessPassAndHint = SessionPasswordService.getBestGuess( this.file );
206-
this.encryptionPassword = bestGuessPassAndHint.password;
207-
208211
UiHelper.buildPasswordSetting({
209212
container,
210213
name:'Password:',
211-
initialValue: this.encryptionPassword,
212214
autoFocus : true,
213215
placeholder: this.formatHint(this.hint),
214216
onChangeCallback: (value) => {
@@ -228,7 +230,21 @@ export class EncryptedFileContentView extends TextFileView {
228230
})
229231
;
230232

231-
return container;
233+
// try to decode and go to edit mode if password is known
234+
const bestGuessPassAndHint = SessionPasswordService.get( this.file );
235+
this.encryptionPassword = bestGuessPassAndHint.password;
236+
237+
this.decryptWithPassword( bestGuessPassAndHint.password )
238+
.then( decryptedText => {
239+
if ( decryptedText != null ){
240+
this.currentEditorText = decryptedText;
241+
this.refreshView( EncryptedFileContentViewStateEnum.editNote );
242+
new Notice('Decrypted using remembered password', 2000);
243+
}
244+
})
245+
;
246+
247+
232248
}
233249

234250
private async encodeAndSave( ){
@@ -251,36 +267,40 @@ export class EncryptedFileContentView extends TextFileView {
251267
}
252268
}
253269

254-
private createEditorView() : HTMLElement {
255-
//const container = this.contentEl.createEl('textarea');
270+
private addEditorView() {
271+
272+
this.elActionIconLockNote.show();
273+
this.elActionChangePassword.show();
274+
275+
this.addTitle('This note is encrypted');
276+
256277
const container = this.contentEl.createDiv();
257278
container.contentEditable = 'true';
258279
container.style.flexGrow = '1';
259280
container.style.alignSelf = 'stretch';
260281

261-
//container.value = this.currentEditorText
262282
container.innerText = this.currentEditorText;
263283
container.focus();
264284

265285
container.on('input', '*', async (ev, target) =>{
266-
//console.debug('editor input',{ev, target});
267-
//this.currentEditorText = container.value;
268286
this.currentEditorText = container.innerText;
269287
await this.encodeAndSave();
270288
});
271-
return container;
272289
}
273290

274-
private createInputContainer() : HTMLElement{
291+
private addInputContainer() : HTMLElement{
275292
return this.contentEl.createDiv( {
276293
'attr': {
277294
'style': 'width:100%; max-width:400px;'
278295
}
279296
} );
280297
}
281298

282-
private createChangePasswordView() : HTMLElement {
283-
const container = this.createInputContainer();
299+
private addChangePasswordView() {
300+
301+
this.addTitle('Change encrypted note password');
302+
303+
const container = this.addInputContainer();
284304

285305
let newPassword = '';
286306
let confirm = '';
@@ -300,6 +320,9 @@ export class EncryptedFileContentView extends TextFileView {
300320

301321
this.encodeAndSave();
302322
this.refreshView( EncryptedFileContentViewStateEnum.editNote );
323+
324+
SessionPasswordService.put( {password: newPassword, hint: newHint}, this.file );
325+
303326
new Notice('Password and Hint were changed');
304327
}
305328
}
@@ -378,8 +401,6 @@ export class EncryptedFileContentView extends TextFileView {
378401
;
379402
})
380403
;
381-
382-
return container;
383404
}
384405

385406
private formatHint( hint:string ): string{
@@ -406,39 +427,35 @@ export class EncryptedFileContentView extends TextFileView {
406427

407428
switch (this.currentView) {
408429
case EncryptedFileContentViewStateEnum.newNote:
409-
this.createTitle('This note will be encrypted');
410-
this.createNewNoteView();
430+
this.addNewNoteView();
411431
break;
412432

413433
case EncryptedFileContentViewStateEnum.decryptNote:
414-
this.createTitle('This note is encrypted');
415-
this.createDecryptNoteView();
434+
this.addDecryptNoteView();
416435
break;
417436

418437
case EncryptedFileContentViewStateEnum.editNote:
419-
this.elActionIconLockNote.show();
420-
this.elActionChangePassword.show();
421-
this.createTitle('This note is encrypted');
422-
this.createEditorView();
438+
this.addEditorView();
423439
break;
424440

425441
case EncryptedFileContentViewStateEnum.changePassword:
426-
this.createTitle('Change encrypted note password');
427-
this.createChangePasswordView();
442+
this.addChangePasswordView();
428443
break;
429444
}
430445

431446
}
432447

448+
async decryptWithPassword( pw: string ) : Promise<string | null>{
449+
if ( pw.length == 0 ){
450+
return null;
451+
}
452+
const fileData = JsonFileEncoding.decode( this.data );
453+
const decryptedText = await FileDataHelper.decrypt( fileData, pw );
454+
return decryptedText;
455+
}
456+
433457
async handleDecryptButtonClick() {
434-
const fileData = JsonFileEncoding.decode(this.data)
435-
436-
//console.debug('Decrypt button', fileData);
437-
438-
const decryptedText = await FileDataHelper.decrypt(
439-
fileData,
440-
this.encryptionPassword
441-
);
458+
const decryptedText = await this.decryptWithPassword( this.encryptionPassword );
442459

443460
if (decryptedText === null){
444461
new Notice('Decryption failed');

src/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,12 @@ export default class MeldEncrypt extends Plugin {
4646
}
4747

4848
async loadSettings() {
49+
4950
const DEFAULT_SETTINGS: IMeldEncryptPluginSettings = {
5051
confirmPassword: true,
5152
rememberPassword: true,
5253
rememberPasswordTimeout: 30,
54+
rememberPasswordLevel: SessionPasswordService.LevelFullPath,
5355

5456
featureWholeNoteEncrypt: {
5557
addRibbonIconToCreateNote: true,
@@ -72,6 +74,7 @@ export default class MeldEncrypt extends Plugin {
7274
? null
7375
: this.settings.rememberPasswordTimeout
7476
);
77+
SessionPasswordService.setLevel( this.settings.rememberPasswordLevel );
7578
}
7679

7780
async saveSettings() {

src/services/MemoryCache.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export class MemoryCache<T> {
3535
return Array.from( this.values.keys() );
3636
}
3737

38+
public removeKey( key: string ) : boolean{
39+
return this.values.delete( key );
40+
}
41+
3842
public clear() {
3943
this.values.clear();
4044
}

src/services/SessionPasswordService.ts

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ export class SessionPasswordService{
1010

1111
private static isActive = true;
1212

13-
private static blankPasswordAndHint : IPasswordAndHint = {password:'', hint:'' };
13+
private static blankPasswordAndHint : IPasswordAndHint = { password:'', hint:'' };
1414

1515
private static cache = new MemoryCache<IPasswordAndHint>();
1616

1717
private static baseMinutesToExpire = 0;
1818
private static expiryTime : number | null = null;
1919

20+
public static LevelFullPath = 'fullPath';
21+
public static LevelParentPath = 'parentPath';
22+
private static level = SessionPasswordService.LevelFullPath;
23+
2024
public static setActive( isActive: boolean) {
2125
SessionPasswordService.isActive = isActive;
2226
if (!SessionPasswordService.isActive){
@@ -33,6 +37,14 @@ export class SessionPasswordService{
3337
SessionPasswordService.updateExpiryTime();
3438
}
3539

40+
public static setLevel( level: string ) {
41+
if ( SessionPasswordService.level == level ){
42+
return;
43+
}
44+
SessionPasswordService.level = level;
45+
this.clear();
46+
}
47+
3648
public static updateExpiryTime() : void {
3749
if (
3850
SessionPasswordService.baseMinutesToExpire == 0
@@ -49,40 +61,33 @@ export class SessionPasswordService{
4961
return;
5062
}
5163

52-
this.cache.put(file.path, pw);
53-
this.cache.put(file.parent.path, pw)
54-
this.cache.put(file.basename, pw);
64+
const key = SessionPasswordService.getFileCacheKey( file );
65+
66+
this.cache.put( key, pw );
5567

5668
SessionPasswordService.updateExpiryTime();
5769
}
5870

59-
public static getExact( file : TFile ): IPasswordAndHint {
71+
public static get( file : TFile ): IPasswordAndHint {
6072
if (!SessionPasswordService.isActive){
6173
return SessionPasswordService.blankPasswordAndHint;
6274
}
75+
6376
this.clearIfExpired();
6477
SessionPasswordService.updateExpiryTime();
65-
return this.cache.get(file.path, SessionPasswordService.blankPasswordAndHint);
66-
}
6778

68-
public static getBestGuess( file : TFile ): IPasswordAndHint {
69-
if (!SessionPasswordService.isActive){
70-
return SessionPasswordService.blankPasswordAndHint;
71-
}
79+
const key = SessionPasswordService.getFileCacheKey( file );
80+
return this.cache.get( key, SessionPasswordService.blankPasswordAndHint );
81+
}
7282

73-
this.clearIfExpired();
74-
SessionPasswordService.updateExpiryTime();
83+
private static getFileCacheKey( file : TFile ) : string {
84+
switch (SessionPasswordService.level) {
85+
case SessionPasswordService.LevelParentPath:
86+
return file.parent.path;
7587

76-
const buestGuess = this.cache.getFirst(
77-
[
78-
file.path,
79-
file.parent.path,
80-
file.basename
81-
],
82-
SessionPasswordService.blankPasswordAndHint
83-
);
84-
85-
return buestGuess;
88+
default:
89+
return file.path;
90+
}
8691
}
8792

8893
private static clearIfExpired() : void{
@@ -95,6 +100,10 @@ export class SessionPasswordService{
95100
this.clear();
96101
}
97102

103+
public static clearForFile( file: TFile ) : void {
104+
const key = SessionPasswordService.getFileCacheKey( file );
105+
this.cache.removeKey( key );
106+
}
98107

99108
public static clear(): void{
100109
this.cache.clear();

src/settings/MeldEncryptPluginSettings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface IMeldEncryptPluginSettings {
55
confirmPassword: boolean;
66
rememberPassword: boolean;
77
rememberPasswordTimeout: number;
8+
rememberPasswordLevel: string;
89

910
featureWholeNoteEncrypt : IFeatureWholeNoteEncryptSettings;
1011
featureInplaceEncrypt : IFeatureInplaceEncryptSettings;

0 commit comments

Comments
 (0)