orgnote-api 0.8.3-beta.1 → 0.8.3-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api.ts +106 -0
- package/encryption/__tests__/__snapshots__/note-encryption.spec.ts.snap +127 -0
- package/encryption/__tests__/encryption-keys.ts +163 -0
- package/encryption/__tests__/encryption.spec.ts +165 -0
- package/encryption/__tests__/note-encryption.spec.ts +261 -0
- package/encryption/encryption.ts +172 -0
- package/encryption/index.ts +2 -0
- package/encryption/note-encryption.ts +133 -0
- package/index.ts +5 -0
- package/models/command.ts +44 -0
- package/models/completion.ts +30 -0
- package/models/default-commands.ts +40 -0
- package/models/editor.ts +27 -0
- package/models/encryption.ts +22 -0
- package/models/extension.ts +45 -0
- package/models/index.ts +11 -0
- package/models/modal.ts +12 -0
- package/models/note.ts +24 -0
- package/models/theme-variables.ts +194 -0
- package/models/vue-component.ts +3 -0
- package/models/widget-type.ts +5 -0
- package/models/widget.ts +59 -0
- package/package.json +1 -1
- package/remote-api/.openapi-generator/FILES +8 -0
- package/remote-api/.openapi-generator/VERSION +1 -0
- package/remote-api/.openapi-generator-ignore +23 -0
- package/remote-api/api.ts +2199 -0
- package/remote-api/base.ts +72 -0
- package/remote-api/common.ts +150 -0
- package/remote-api/configuration.ts +101 -0
- package/remote-api/git_push.sh +57 -0
- package/remote-api/index.ts +18 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { decryptNote, encryptNote } from '../note-encryption';
|
|
3
|
+
import { Note } from '../../models';
|
|
4
|
+
import {
|
|
5
|
+
armoredPublicKey,
|
|
6
|
+
armoredPrivateKey,
|
|
7
|
+
privateKeyPassphrase,
|
|
8
|
+
} from './encryption-keys';
|
|
9
|
+
|
|
10
|
+
test('Should encrypt note content via password', async () => {
|
|
11
|
+
const content = `#+ID: qweqwe
|
|
12
|
+
|
|
13
|
+
#+TITLE: Hello worlld
|
|
14
|
+
|
|
15
|
+
* Hello?`;
|
|
16
|
+
|
|
17
|
+
const note: Note = {
|
|
18
|
+
id: 'id',
|
|
19
|
+
encrypted: 'gpgPassword',
|
|
20
|
+
meta: {
|
|
21
|
+
title: 'My note title',
|
|
22
|
+
images: [],
|
|
23
|
+
published: false,
|
|
24
|
+
description: 'Awesome description',
|
|
25
|
+
},
|
|
26
|
+
content,
|
|
27
|
+
author: {
|
|
28
|
+
id: '1',
|
|
29
|
+
name: 'John Doe',
|
|
30
|
+
email: 'test@mail.com',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const encryptedNote = await encryptNote(note, {
|
|
35
|
+
type: 'gpgPassword',
|
|
36
|
+
password: '123',
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(encryptedNote.content.startsWith('-----BEGIN PGP MESSAGE-----')).toBe(
|
|
40
|
+
true
|
|
41
|
+
);
|
|
42
|
+
delete encryptedNote.content;
|
|
43
|
+
expect(encryptedNote).toMatchSnapshot();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('Should decrypt note content via password', async () => {
|
|
47
|
+
const note: Note = {
|
|
48
|
+
id: 'id',
|
|
49
|
+
encrypted: 'gpgPassword',
|
|
50
|
+
meta: {
|
|
51
|
+
title: 'My note title',
|
|
52
|
+
images: [],
|
|
53
|
+
published: false,
|
|
54
|
+
description: 'Awesome description',
|
|
55
|
+
},
|
|
56
|
+
content: `-----BEGIN PGP MESSAGE-----
|
|
57
|
+
|
|
58
|
+
wy4ECQMI/CCaKMJEqy/gyROJeRgW9I738dDFBltFlxIjhxrN7nQ6gkX4GgX6
|
|
59
|
+
zt3v0mABvaBJA7corlU8su21TpPApOs/+DMWpVlbI3Zer7QfQK1fSBoSTbCR
|
|
60
|
+
2k3g68Afayke2nLkDNkH62tdOPiTkx7bSlp1zL4uU440IM1g6dC72JkmtoTJ
|
|
61
|
+
5Bjlwt4ZhxFsh+c=
|
|
62
|
+
=csID
|
|
63
|
+
-----END PGP MESSAGE-----`,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const decryptedNote = await decryptNote(note, {
|
|
67
|
+
type: 'gpgPassword',
|
|
68
|
+
password: '123',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(decryptedNote).toMatchSnapshot();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('Should encrypt note via keys', async () => {
|
|
75
|
+
const content = `#+ID: qweqwe
|
|
76
|
+
#+TITLE: Hello worlld
|
|
77
|
+
|
|
78
|
+
* Hello?`;
|
|
79
|
+
|
|
80
|
+
const note: Note = {
|
|
81
|
+
id: 'id',
|
|
82
|
+
encrypted: 'gpgKeys',
|
|
83
|
+
meta: {
|
|
84
|
+
title: 'My note title for encryption via keys',
|
|
85
|
+
images: [],
|
|
86
|
+
published: false,
|
|
87
|
+
description: 'Awesome description',
|
|
88
|
+
},
|
|
89
|
+
content,
|
|
90
|
+
author: {
|
|
91
|
+
id: '1',
|
|
92
|
+
name: 'John Doe',
|
|
93
|
+
email: 'test@mail.com',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const encryptedNote = await encryptNote(note, {
|
|
98
|
+
type: 'gpgKeys',
|
|
99
|
+
publicKey: armoredPublicKey,
|
|
100
|
+
privateKey: armoredPrivateKey,
|
|
101
|
+
privateKeyPassphrase,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(encryptedNote.content.startsWith('-----BEGIN PGP MESSAGE-----')).toBe(
|
|
105
|
+
true
|
|
106
|
+
);
|
|
107
|
+
delete encryptedNote.content;
|
|
108
|
+
expect(encryptedNote).toMatchSnapshot();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('Should decrypt note via provided keys', async () => {
|
|
112
|
+
const note: Note = {
|
|
113
|
+
id: 'id',
|
|
114
|
+
encrypted: 'gpgKeys',
|
|
115
|
+
meta: {
|
|
116
|
+
title: 'My note title for decryption via keys',
|
|
117
|
+
images: [],
|
|
118
|
+
published: false,
|
|
119
|
+
description: 'Awesome description',
|
|
120
|
+
},
|
|
121
|
+
content: `-----BEGIN PGP MESSAGE-----
|
|
122
|
+
|
|
123
|
+
wcFMA/vryg+TTn0rAQ//TBFRjKmjRQoLhSrgeH+NbsZXbxvo7Ey4k+BQ9XA5
|
|
124
|
+
+CMpXH9uFUxsSaI5+McUSEt32VI17HRpXQDCL9nwaWqWOanMaRe0tXXhtox2
|
|
125
|
+
gfe2f/6zsge9ux+mXF2BG4z+V5T8XIOrfzxosVprdJHZHM3x7cW5USQ0t2i2
|
|
126
|
+
FiOUWxSZO1899J3yICpMvhDXvTLVZuKpSNQho5PyXSeZa83eN+uYkhgt9lsk
|
|
127
|
+
0KW88Nr435S6n6mVw/zpitXIgEKpkqh46mhG+1W3aC0lYx6j9lHm3bjtdb4r
|
|
128
|
+
2mtZbWKwfdBggEy8qEeiUeslvKd6uWtEccomzFgJkaqWVGknLmrBdHfztRIV
|
|
129
|
+
fbZhxHz+J3GFOIgXf/5+fv+zg0nTazgz1mDUfnTHw3+qcAyEJ0ADdyg9EZc+
|
|
130
|
+
bKbK0GwzMDPQeM+cCRDWUTiIa1ruyEETiwMdhWUDztF8XxY2o8jXPerZ0NhG
|
|
131
|
+
R8l+SvAdYQQXPfxEh9lA3thkyz/Vp72pW46lUeJHGSq/fS6KIdLHLP9Z2e1J
|
|
132
|
+
aCFpNMgyAN+BaXwnbLZfz6k5hV8awbRScSWQLEg69D9b287SFDPOYW7OZikn
|
|
133
|
+
CKXL7xyQ5LrWJZN9Z/UPGjy+PdEw1SBhyluW6DQ+Sz1j4K8USTLqY89EInDl
|
|
134
|
+
G7AxHGujR9UkUGyUvvc71XW7jEOogiDgn1ZMoj9Y58LSweUB8vfkY3VF84Fy
|
|
135
|
+
zGVvLFWnFbtnWuoQC4OOXv1F6ETdmEsSMEMWKzRLDGPyqNX7FhG3cejlEuiK
|
|
136
|
+
EsT5oQhz00RECmnR8mJkGmJhzWmmeoQvaucFqhTOZjvYl9ivuEMBZ2jtkdHn
|
|
137
|
+
R6UNcNZRpCbfnl7YoikqbGBgqDFYydFGHXKHDpYhcQYWJsMDUayzUiPtzmaE
|
|
138
|
+
tgfTgNNqgPxkLnWA99KYNU8DH+FwgaYBuw9dwdqwcjxSbZjHhCFGUfsqM+ik
|
|
139
|
+
O9gcoAwVO1usODlESU7OWuSF2tIv5DBG5rhlSyxBp4d4aWmaumTswAdojy2O
|
|
140
|
+
sM70ETbg0mC8niW9lNsgJp55oFmlksvzjUIit5rBEr55rtPcJSgakPpR2yvd
|
|
141
|
+
Q4XjybUmV0IdONMhk/OlqaBPtnA0RG+qTcm7oP1qH/m5zRA0ZplVQ5ylkQwr
|
|
142
|
+
LwGq5JQpJkgxxgLIrUbbtzYBShXSr5c1XXR0LIRiNgtb6s1s4mt+fbyExdJF
|
|
143
|
+
+ceuu+/xYrW/YTuEJpHxLiZ+aNPW5g5Y7Hbqu3hp3UL/kD44cc8JjZh18spX
|
|
144
|
+
p/ncojDhF2r2vhR+CndYcMkpGMV+t1pGKC8wlcFc7lb9GJASnqMGvhQmvIaG
|
|
145
|
+
gO/x7UjuTZSBW+kITHHZJDqYryKUv1j0CkHxIn9tWsYOpa1giOFtXX5v0AAM
|
|
146
|
+
AJR/s/beLlqwCsUdYnw1TkP/0u0ZK3RPio1nJ7S6ckPfsM7DqCWD8ILD8Cdr
|
|
147
|
+
cuzQrWaE30t5PXx53xBPO+6t5wKfDL35WHWG1Irmvz9UuT7tDS3IzwtF4ijF
|
|
148
|
+
PX6alTbxGnoHgZ4bG4J1wfpTNPppP1gJeVg67VqOypzdZi+SjofMWnFgRFmD
|
|
149
|
+
yEN8xpFUs7A9xryVZOosp9Sfe3IbBkO99sAQ7jV4EoMYk3/GKA==
|
|
150
|
+
=LjkG
|
|
151
|
+
-----END PGP MESSAGE-----`,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const decryptedNote = await decryptNote(note, {
|
|
155
|
+
type: 'gpgKeys',
|
|
156
|
+
publicKey: armoredPublicKey,
|
|
157
|
+
privateKey: armoredPrivateKey,
|
|
158
|
+
privateKeyPassphrase,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(decryptedNote).toMatchSnapshot();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('Should not encrypt public note', async () => {
|
|
165
|
+
const content = `#+ID: qweqwe
|
|
166
|
+
#+TITLE: Hello worlld
|
|
167
|
+
|
|
168
|
+
* Hello?`;
|
|
169
|
+
|
|
170
|
+
const note: Note = {
|
|
171
|
+
id: 'id',
|
|
172
|
+
encrypted: 'gpgPassword',
|
|
173
|
+
meta: {
|
|
174
|
+
title: 'My note title',
|
|
175
|
+
images: [],
|
|
176
|
+
published: true,
|
|
177
|
+
description: 'Awesome description',
|
|
178
|
+
},
|
|
179
|
+
content,
|
|
180
|
+
author: {
|
|
181
|
+
id: '1',
|
|
182
|
+
name: 'John Doe',
|
|
183
|
+
email: 'test@mail.com',
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const encryptedNote = await encryptNote(note, {
|
|
188
|
+
type: 'gpgPassword',
|
|
189
|
+
password: '123',
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(encryptedNote.content.startsWith('-----BEGIN PGP MESSAGE-----')).toBe(
|
|
193
|
+
false
|
|
194
|
+
);
|
|
195
|
+
expect(encryptedNote).toMatchSnapshot();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('Should encrypt note with empty encrypted property', async () => {
|
|
199
|
+
const content = `#+ID: qweqwe
|
|
200
|
+
#+TITLE: Hello worlld
|
|
201
|
+
|
|
202
|
+
* Hello?`;
|
|
203
|
+
|
|
204
|
+
const note: Note = {
|
|
205
|
+
id: 'id',
|
|
206
|
+
meta: {
|
|
207
|
+
title: 'My note title',
|
|
208
|
+
images: [],
|
|
209
|
+
published: false,
|
|
210
|
+
description: 'Awesome description',
|
|
211
|
+
},
|
|
212
|
+
content,
|
|
213
|
+
author: {
|
|
214
|
+
id: '1',
|
|
215
|
+
name: 'John Doe',
|
|
216
|
+
email: 'test@mail.com',
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const encryptedNote = await encryptNote(note, {
|
|
221
|
+
type: 'gpgPassword',
|
|
222
|
+
password: '123',
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(encryptedNote.content.startsWith('-----BEGIN PGP MESSAGE-----')).toBe(
|
|
226
|
+
true
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
delete encryptedNote.content;
|
|
230
|
+
expect(encryptedNote).toMatchSnapshot();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test('Should not decrypt note without provided encrypted type', async () => {
|
|
234
|
+
const content = `#+ID: qweqwe
|
|
235
|
+
#+TITLE: Hello worlld
|
|
236
|
+
|
|
237
|
+
* Hello?`;
|
|
238
|
+
const note: Note = {
|
|
239
|
+
id: 'id',
|
|
240
|
+
meta: {
|
|
241
|
+
title: 'My note title',
|
|
242
|
+
images: [],
|
|
243
|
+
published: false,
|
|
244
|
+
description: 'Awesome description',
|
|
245
|
+
},
|
|
246
|
+
encrypted: undefined,
|
|
247
|
+
content,
|
|
248
|
+
author: {
|
|
249
|
+
id: '1',
|
|
250
|
+
name: 'John Doe',
|
|
251
|
+
email: 'test@mail.com',
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const decryptedNote = await decryptNote(note, {
|
|
256
|
+
type: 'gpgPassword',
|
|
257
|
+
password: '123',
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
expect(decryptedNote).toMatchSnapshot();
|
|
261
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMessage,
|
|
3
|
+
decrypt,
|
|
4
|
+
decryptKey,
|
|
5
|
+
encrypt,
|
|
6
|
+
readKey,
|
|
7
|
+
readMessage,
|
|
8
|
+
readPrivateKey,
|
|
9
|
+
} from 'openpgp';
|
|
10
|
+
|
|
11
|
+
export class IncorrectOrMissingPrivateKeyPasswordError extends Error {}
|
|
12
|
+
export class ImpossibleToDecryptWithProvidedKeysError extends Error {}
|
|
13
|
+
export class IncorrectEncryptionPasswordError extends Error {}
|
|
14
|
+
export class NoKeysProvidedError extends Error {}
|
|
15
|
+
export class NoPasswordProvidedError extends Error {}
|
|
16
|
+
|
|
17
|
+
const noPrivateKeyPassphraseProvidedErrorMsg =
|
|
18
|
+
'Error: Signing key is not decrypted.';
|
|
19
|
+
const incorrectPrivateKeyPassphraseErrorMsg =
|
|
20
|
+
'Error decrypting private key: Incorrect key passphrase';
|
|
21
|
+
const decryptionKeyIsNotDecryptedErrorMsg =
|
|
22
|
+
'Error decrypting message: Decryption key is not decrypted.';
|
|
23
|
+
const corruptedPrivateKeyErrorMsg = 'Misformed armored text';
|
|
24
|
+
|
|
25
|
+
const decriptionFailedErrorMsg =
|
|
26
|
+
'Error decrypting message: Session key decryption failed.';
|
|
27
|
+
const incorrectEncryptionPasswordErrorMsg =
|
|
28
|
+
'Error decrypting message: Modification detected.';
|
|
29
|
+
|
|
30
|
+
const noSymmetricallyEncryptedSessionKeyErrorMsg =
|
|
31
|
+
'Error decrypting message: No symmetrically encrypted session key packet found.';
|
|
32
|
+
|
|
33
|
+
const armoredTextNotTypePrivateKeyErrorMsg =
|
|
34
|
+
'Armored text not of type private key';
|
|
35
|
+
|
|
36
|
+
const notPrivateKeyErrprMsg =
|
|
37
|
+
'Error decrypting message: No public key encrypted session key packet found.';
|
|
38
|
+
|
|
39
|
+
export const encryptViaKeys = withCustomErrors(_encryptViaKeys);
|
|
40
|
+
export const encryptViaPassword = withCustomErrors(_encryptViaPassword);
|
|
41
|
+
export const decryptViaPassword = withCustomErrors(_decryptViaPassword);
|
|
42
|
+
export const decryptViaKeys = withCustomErrors(_decryptViaKeys);
|
|
43
|
+
|
|
44
|
+
async function _encryptViaPassword(
|
|
45
|
+
text: string,
|
|
46
|
+
password: string
|
|
47
|
+
): Promise<string> {
|
|
48
|
+
const message = await createMessage({
|
|
49
|
+
text,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const encryptedMessage = await encrypt({
|
|
53
|
+
message,
|
|
54
|
+
format: 'armored',
|
|
55
|
+
passwords: [password],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return encryptedMessage.toString();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function _encryptViaKeys(
|
|
62
|
+
text: string,
|
|
63
|
+
armoredPublicKey: string,
|
|
64
|
+
armoredPrivateKey: string,
|
|
65
|
+
privateKeyPassphrase?: string
|
|
66
|
+
): Promise<string> {
|
|
67
|
+
const publicKey = await readKey({ armoredKey: armoredPublicKey });
|
|
68
|
+
|
|
69
|
+
const message = await createMessage({
|
|
70
|
+
text,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const encryptedPrivateKey = await readPrivateKey({
|
|
74
|
+
armoredKey: armoredPrivateKey,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const privateKey = privateKeyPassphrase
|
|
78
|
+
? await decryptKey({
|
|
79
|
+
privateKey: encryptedPrivateKey,
|
|
80
|
+
passphrase: privateKeyPassphrase,
|
|
81
|
+
})
|
|
82
|
+
: encryptedPrivateKey;
|
|
83
|
+
|
|
84
|
+
const encryptedMessage = await encrypt({
|
|
85
|
+
message,
|
|
86
|
+
format: 'armored',
|
|
87
|
+
encryptionKeys: publicKey,
|
|
88
|
+
signingKeys: privateKey,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return encryptedMessage.toString();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function _decryptViaPassword(
|
|
95
|
+
data: string,
|
|
96
|
+
password: string
|
|
97
|
+
): Promise<string> {
|
|
98
|
+
const message = await readMessage({ armoredMessage: data });
|
|
99
|
+
|
|
100
|
+
const { data: decryptedText } = await decrypt({
|
|
101
|
+
message,
|
|
102
|
+
passwords: password,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return decryptedText.toString();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function _decryptViaKeys(
|
|
109
|
+
data: string,
|
|
110
|
+
armoredPrivateKey: string,
|
|
111
|
+
privateKeyPassword?: string
|
|
112
|
+
): Promise<string> {
|
|
113
|
+
const encryptedPrivateKey = await readPrivateKey({
|
|
114
|
+
armoredKey: armoredPrivateKey,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const privateKey = privateKeyPassword
|
|
118
|
+
? await decryptKey({
|
|
119
|
+
privateKey: encryptedPrivateKey,
|
|
120
|
+
passphrase: privateKeyPassword,
|
|
121
|
+
})
|
|
122
|
+
: encryptedPrivateKey;
|
|
123
|
+
|
|
124
|
+
const message = await readMessage({ armoredMessage: data });
|
|
125
|
+
|
|
126
|
+
const { data: decryptedText } = await decrypt({
|
|
127
|
+
message,
|
|
128
|
+
decryptionKeys: privateKey,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return decryptedText.toString();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function withCustomErrors<P extends unknown[], T>(
|
|
135
|
+
fn: (...args: P) => Promise<T | never>
|
|
136
|
+
) {
|
|
137
|
+
return async (...args: P): Promise<T> => {
|
|
138
|
+
try {
|
|
139
|
+
return await fn(...args);
|
|
140
|
+
} catch (e: unknown) {
|
|
141
|
+
if (!(e instanceof Error)) {
|
|
142
|
+
throw e;
|
|
143
|
+
}
|
|
144
|
+
if (
|
|
145
|
+
[
|
|
146
|
+
noPrivateKeyPassphraseProvidedErrorMsg,
|
|
147
|
+
incorrectPrivateKeyPassphraseErrorMsg,
|
|
148
|
+
corruptedPrivateKeyErrorMsg,
|
|
149
|
+
decryptionKeyIsNotDecryptedErrorMsg,
|
|
150
|
+
armoredTextNotTypePrivateKeyErrorMsg,
|
|
151
|
+
].includes(e.message)
|
|
152
|
+
) {
|
|
153
|
+
throw new IncorrectOrMissingPrivateKeyPasswordError(e.message);
|
|
154
|
+
}
|
|
155
|
+
if (e.message === decriptionFailedErrorMsg) {
|
|
156
|
+
throw new ImpossibleToDecryptWithProvidedKeysError(e.message);
|
|
157
|
+
}
|
|
158
|
+
if (e.message === incorrectEncryptionPasswordErrorMsg) {
|
|
159
|
+
throw new IncorrectEncryptionPasswordError();
|
|
160
|
+
}
|
|
161
|
+
if (e.message === noSymmetricallyEncryptedSessionKeyErrorMsg) {
|
|
162
|
+
throw new NoKeysProvidedError();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (e.message === notPrivateKeyErrprMsg) {
|
|
166
|
+
throw new NoPasswordProvidedError();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
throw e;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ModelsPublicNote, ModelsPublicNoteEncryptedEnum } from '../remote-api';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
decryptViaKeys,
|
|
5
|
+
decryptViaPassword,
|
|
6
|
+
encryptViaKeys,
|
|
7
|
+
encryptViaPassword,
|
|
8
|
+
} from './encryption';
|
|
9
|
+
import { OrgNoteEncryption } from '../models/encryption';
|
|
10
|
+
import { parse, withMetaInfo } from 'org-mode-ast';
|
|
11
|
+
|
|
12
|
+
export type NoteEncryptedType = `${ModelsPublicNote['encrypted']}`;
|
|
13
|
+
export interface AbstractEncryptedNote {
|
|
14
|
+
content: string;
|
|
15
|
+
encrypted?: NoteEncryptedType;
|
|
16
|
+
meta: {
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
published?: boolean;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export async function encryptNote<T extends AbstractEncryptedNote>(
|
|
22
|
+
note: T,
|
|
23
|
+
encryptionParams: OrgNoteEncryption
|
|
24
|
+
): Promise<T> {
|
|
25
|
+
if (
|
|
26
|
+
!encryptionParams.type ||
|
|
27
|
+
encryptionParams.type === ModelsPublicNoteEncryptedEnum.Disabled ||
|
|
28
|
+
note.meta.published
|
|
29
|
+
) {
|
|
30
|
+
return note;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
note.meta = { id: note.meta.id, published: note.meta.published };
|
|
34
|
+
|
|
35
|
+
if (encryptionParams.type === ModelsPublicNoteEncryptedEnum.GpgKeys) {
|
|
36
|
+
return await encryptNoteViaKeys(
|
|
37
|
+
note,
|
|
38
|
+
encryptionParams.publicKey,
|
|
39
|
+
encryptionParams.privateKey,
|
|
40
|
+
encryptionParams.privateKeyPassphrase
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return await encryptNoteViaPassword(note, encryptionParams.password);
|
|
44
|
+
}
|
|
45
|
+
export async function encryptNoteViaPassword<T extends AbstractEncryptedNote>(
|
|
46
|
+
note: T,
|
|
47
|
+
password: string
|
|
48
|
+
): Promise<T> {
|
|
49
|
+
const content = await encryptViaPassword(note.content, password);
|
|
50
|
+
return {
|
|
51
|
+
...note,
|
|
52
|
+
content,
|
|
53
|
+
encrypted: ModelsPublicNoteEncryptedEnum.GpgPassword,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function encryptNoteViaKeys<T extends AbstractEncryptedNote>(
|
|
58
|
+
note: T,
|
|
59
|
+
publicKey: string,
|
|
60
|
+
privateKey: string,
|
|
61
|
+
privateKeyPassphrase?: string
|
|
62
|
+
): Promise<T> {
|
|
63
|
+
const content = await encryptViaKeys(
|
|
64
|
+
note.content,
|
|
65
|
+
publicKey,
|
|
66
|
+
privateKey,
|
|
67
|
+
privateKeyPassphrase
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
...note,
|
|
72
|
+
content,
|
|
73
|
+
encrypted: ModelsPublicNoteEncryptedEnum.GpgKeys,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function decryptNote<T extends AbstractEncryptedNote>(
|
|
78
|
+
note: T,
|
|
79
|
+
encryptionParams: OrgNoteEncryption
|
|
80
|
+
): Promise<T> {
|
|
81
|
+
if (
|
|
82
|
+
note.meta.published ||
|
|
83
|
+
!note.encrypted ||
|
|
84
|
+
!encryptionParams.type ||
|
|
85
|
+
encryptionParams.type === ModelsPublicNoteEncryptedEnum.Disabled
|
|
86
|
+
) {
|
|
87
|
+
return note;
|
|
88
|
+
}
|
|
89
|
+
const decryptedNote =
|
|
90
|
+
encryptionParams.type === ModelsPublicNoteEncryptedEnum.GpgKeys
|
|
91
|
+
? await decryptNoteViaKeys(
|
|
92
|
+
note,
|
|
93
|
+
encryptionParams.privateKey,
|
|
94
|
+
encryptionParams.privateKeyPassphrase
|
|
95
|
+
)
|
|
96
|
+
: await decryptNoteViaPassword(note, encryptionParams.password);
|
|
97
|
+
|
|
98
|
+
const parsed = withMetaInfo(parse(decryptedNote.content));
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
...decryptedNote,
|
|
102
|
+
meta: { ...decryptedNote.meta, published: parsed.meta.published },
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function decryptNoteViaPassword<T extends AbstractEncryptedNote>(
|
|
107
|
+
note: T,
|
|
108
|
+
password: string
|
|
109
|
+
): Promise<T> {
|
|
110
|
+
const content = await decryptViaPassword(note.content, password);
|
|
111
|
+
return {
|
|
112
|
+
...note,
|
|
113
|
+
content,
|
|
114
|
+
encrypted: ModelsPublicNoteEncryptedEnum.GpgPassword,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function decryptNoteViaKeys<T extends AbstractEncryptedNote>(
|
|
119
|
+
note: T,
|
|
120
|
+
privateKey: string,
|
|
121
|
+
privateKeyPassphrase?: string
|
|
122
|
+
): Promise<T> {
|
|
123
|
+
const content = await decryptViaKeys(
|
|
124
|
+
note.content,
|
|
125
|
+
privateKey,
|
|
126
|
+
privateKeyPassphrase
|
|
127
|
+
);
|
|
128
|
+
return {
|
|
129
|
+
...note,
|
|
130
|
+
content,
|
|
131
|
+
encrypted: ModelsPublicNoteEncryptedEnum.GpgKeys,
|
|
132
|
+
};
|
|
133
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DefaultCommands } from './default-commands';
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_KEYBINDING_GROUP = 'default';
|
|
4
|
+
|
|
5
|
+
export type CommandGroup =
|
|
6
|
+
| 'settings'
|
|
7
|
+
| 'editor'
|
|
8
|
+
| 'global'
|
|
9
|
+
| 'note-detail'
|
|
10
|
+
| 'completion'
|
|
11
|
+
| string;
|
|
12
|
+
|
|
13
|
+
export interface CommandHandlerParams<T = any> {
|
|
14
|
+
event?: KeyboardEvent;
|
|
15
|
+
data?: T;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CommandPreview {
|
|
20
|
+
description?: string;
|
|
21
|
+
command?: DefaultCommands | string;
|
|
22
|
+
title?: string | (() => string);
|
|
23
|
+
icon?: string | (() => string);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CommandMeta<T = any> extends Partial<CommandPreview> {
|
|
27
|
+
// TODO: add support for multiple key sequences
|
|
28
|
+
keySequence?: string | string[];
|
|
29
|
+
/* Where is this command available, default value is global */
|
|
30
|
+
group?: CommandGroup;
|
|
31
|
+
allowOnInput?: boolean;
|
|
32
|
+
ignorePrompt?: boolean;
|
|
33
|
+
/* When command is system command, it will not be shown for users */
|
|
34
|
+
system?: boolean;
|
|
35
|
+
available?: () => boolean;
|
|
36
|
+
context?: {
|
|
37
|
+
[key: string]: T;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Command<T = any> extends CommandMeta<T> {
|
|
42
|
+
/* arguments depend on the current scope */
|
|
43
|
+
handler: (params?: CommandHandlerParams) => unknown | Promise<unknown>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface CompletionCandidate<T = unknown> {
|
|
2
|
+
icon?: string | (() => string);
|
|
3
|
+
group?: string;
|
|
4
|
+
title?: string | (() => string);
|
|
5
|
+
description?: string;
|
|
6
|
+
command: string;
|
|
7
|
+
data: T;
|
|
8
|
+
/* Command handler could be used instead of command string */
|
|
9
|
+
commandHandler?: (data: T) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CompletionSearchResult<T = unknown> {
|
|
13
|
+
total?: number;
|
|
14
|
+
result: CompletionCandidate<T>[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type CandidateGetterFn<T = unknown> = (
|
|
18
|
+
filter: string,
|
|
19
|
+
limit?: number,
|
|
20
|
+
offset?: number
|
|
21
|
+
) => CompletionSearchResult<T> | Promise<CompletionSearchResult<T>>;
|
|
22
|
+
|
|
23
|
+
export interface CompletionConfigs<T = unknown> {
|
|
24
|
+
searchAutocompletions?: string[];
|
|
25
|
+
itemsGetter: CandidateGetterFn<T>;
|
|
26
|
+
placeholder?: string;
|
|
27
|
+
itemHeight?: string;
|
|
28
|
+
searchText?: string;
|
|
29
|
+
onClicked?: (candidate: CompletionCandidate<T>) => void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This are default commands. It's not a complete list
|
|
3
|
+
* Some of the commands are dynamically generated or
|
|
4
|
+
* can be added by user extensions.
|
|
5
|
+
*/
|
|
6
|
+
export enum DefaultCommands {
|
|
7
|
+
// Global commands
|
|
8
|
+
REPORT_BUG = 'report bug',
|
|
9
|
+
OPEN_DEBUG_INFO = 'open debug info',
|
|
10
|
+
SHOW_LOGS = 'show logs',
|
|
11
|
+
TOGGLE_SIDEBAR = 'toggle sidebar',
|
|
12
|
+
TOGGLE_FILE_MANAGER = 'toggle file manager',
|
|
13
|
+
CREATE_NOTE = 'create note',
|
|
14
|
+
PROJECT_INFO = 'project info',
|
|
15
|
+
|
|
16
|
+
// Completion commands
|
|
17
|
+
SEARCH = 'search',
|
|
18
|
+
TOGGLE_COMMANDS = 'toggle commands',
|
|
19
|
+
RESTORE_COMPLETION = 'restore last completion',
|
|
20
|
+
EXIT_COMMAND_EXECUTOR = 'exit command executor',
|
|
21
|
+
NEXT_CANDIDATE = 'next candidate',
|
|
22
|
+
PREV_CANDIDATE = 'previous candidate',
|
|
23
|
+
EXECUTE_CANDIDATE = 'execute candidate',
|
|
24
|
+
|
|
25
|
+
// Settings
|
|
26
|
+
SETTINGS = 'settings',
|
|
27
|
+
RESET_THEME = 'reset theme',
|
|
28
|
+
TOGGLE_DARK_MODE = 'toggle dark mode',
|
|
29
|
+
TOGGLE_DEBUG_MODE = 'toggle debug mode',
|
|
30
|
+
SELECT_THEME = 'select theme',
|
|
31
|
+
|
|
32
|
+
// Routing
|
|
33
|
+
OPEN_MY_NOTES = 'my notes',
|
|
34
|
+
OPEN_DASHBOARD = 'dashboard',
|
|
35
|
+
OPEN_PUBLIC_NOTE_LIST = 'public note list',
|
|
36
|
+
OPEN_NOTE_EDITOR = 'edit mode',
|
|
37
|
+
OPEN_NOTE_VIEWER = 'view mode',
|
|
38
|
+
OPEN_GRAPH = 'graph',
|
|
39
|
+
OPEN_EXTENSIONS = 'extensions',
|
|
40
|
+
}
|