feat(storage-manager): add en-/decryption stub (WIP) and restructure
This commit is contained in:
parent
50e4212224
commit
54d852f308
@ -1,6 +1,7 @@
|
||||
/* global global:writable */
|
||||
|
||||
import * as semver from 'semver';
|
||||
import sodium from 'sodium-javascript';
|
||||
|
||||
import { HttpClient } from '../../services/http-client/http-client';
|
||||
|
||||
@ -45,7 +46,7 @@ export class StorageManager {
|
||||
throw new Error('Cannot setup StorageManager without window or global');
|
||||
|
||||
if (this._options.encryption) {
|
||||
Object.values(LOCATION).forEach((location) => {
|
||||
[LOCATION.LOCAL, LOCATION.SESSION].forEach((location) => {
|
||||
const encryption = this._options.encryption.all || this._options.encryption[location];
|
||||
if (encryption) this._requestStorageKey({ location: location, encryption: encryption });
|
||||
});
|
||||
@ -65,15 +66,15 @@ export class StorageManager {
|
||||
|
||||
switch (location) {
|
||||
case LOCATION.LOCAL: {
|
||||
this._saveToLocalStorage({ ...this._getFromLocalStorage(), [key]: value });
|
||||
this._saveToLocalStorage(this._updateStorage(this._getFromLocalStorage(options), { [key]: value }, LOCATION.LOCAL, options));
|
||||
break;
|
||||
}
|
||||
case LOCATION.SESSION: {
|
||||
this._saveToSessionStorage({ ...this._getFromSessionStorage(), [key]: value });
|
||||
this._saveToSessionStorage(this._updateStorage(this._getFromSessionStorage(options), { [key]: value }, LOCATION.SESSION, options));
|
||||
break;
|
||||
}
|
||||
case LOCATION.WINDOW: {
|
||||
this._saveToWindow({ ...this._getFromLocalStorage(), [key]: value });
|
||||
this._saveToWindow({ ...this._getFromWindow(), [key]: value });
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -94,11 +95,11 @@ export class StorageManager {
|
||||
|
||||
switch (location) {
|
||||
case LOCATION.LOCAL: {
|
||||
val = this._getFromLocalStorage()[key];
|
||||
val = this._getFromLocalStorage(options)[key];
|
||||
break;
|
||||
}
|
||||
case LOCATION.SESSION: {
|
||||
val = this._getFromSessionStorage()[key];
|
||||
val = this._getFromSessionStorage(options)[key];
|
||||
break;
|
||||
}
|
||||
case LOCATION.WINDOW: {
|
||||
@ -125,14 +126,14 @@ export class StorageManager {
|
||||
for (const location of locations) {
|
||||
switch (location) {
|
||||
case LOCATION.LOCAL: {
|
||||
let val = this._getFromLocalStorage();
|
||||
let val = this._getFromLocalStorage(options);
|
||||
|
||||
delete val[key];
|
||||
|
||||
return this._saveToLocalStorage(val);
|
||||
}
|
||||
case LOCATION.SESSION: {
|
||||
let val = this._getFromSessionStorage();
|
||||
let val = this._getFromSessionStorage(options);
|
||||
|
||||
delete val[key];
|
||||
|
||||
@ -174,7 +175,7 @@ export class StorageManager {
|
||||
}
|
||||
|
||||
|
||||
_getFromLocalStorage() {
|
||||
_getFromLocalStorage(options=this._options) {
|
||||
let state;
|
||||
|
||||
try {
|
||||
@ -190,10 +191,10 @@ export class StorageManager {
|
||||
}
|
||||
|
||||
if ('state' in state)
|
||||
return state.state;
|
||||
return this._getFromStorage(state.state, LOCATION.LOCAL, options);
|
||||
else {
|
||||
delete state.version;
|
||||
return state;
|
||||
return this._getFromStorage(state, LOCATION.LOCAL, options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,12 +225,12 @@ export class StorageManager {
|
||||
if (!this._global.App.Storage)
|
||||
this._global.App.Storage = {};
|
||||
|
||||
return this._global.App.Storage[this.namespace] || {};
|
||||
return this._global.App.Storage;
|
||||
}
|
||||
|
||||
_saveToWindow(value) {
|
||||
if (!this._global || !this._global.App) {
|
||||
throw new Error('StorageManager._saveToWindow called when window.App is not available');
|
||||
return console.error('StorageManager._saveToWindow called when window.App is not available');
|
||||
}
|
||||
|
||||
if (!value)
|
||||
@ -243,7 +244,7 @@ export class StorageManager {
|
||||
|
||||
_clearWindow() {
|
||||
if (!this._global || !this._global.App) {
|
||||
throw new Error('StorageManager._saveToWindow called when window.App is not available');
|
||||
return console.error('StorageManager._saveToWindow called when window.App is not available');
|
||||
}
|
||||
|
||||
if (this._global.App.Storage) {
|
||||
@ -252,7 +253,8 @@ export class StorageManager {
|
||||
}
|
||||
|
||||
|
||||
_getFromSessionStorage() {
|
||||
_getFromSessionStorage(options=this._options) {
|
||||
console.log('_getFromSessionStorage with args', options);
|
||||
let state;
|
||||
|
||||
try {
|
||||
@ -268,10 +270,10 @@ export class StorageManager {
|
||||
}
|
||||
|
||||
if ('state' in state)
|
||||
return state.state;
|
||||
return this._getFromStorage(state.state, LOCATION.SESSION, options);
|
||||
else {
|
||||
delete state.version;
|
||||
return state;
|
||||
return this._getFromStorage(state, LOCATION.SESSION, options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,6 +296,26 @@ export class StorageManager {
|
||||
window.sessionStorage.removeItem(this.namespace);
|
||||
}
|
||||
|
||||
|
||||
_getFromStorage(storage, location, options=this._options) {
|
||||
const encryption = options.encryption && (options.encryption.all || options.encryption[location]);
|
||||
if (encryption && storage.encryption) {
|
||||
return { ...storage, ...JSON.parse(decrypt(storage.encryption.ciphertext, encryption.key) || '{}') };
|
||||
} else {
|
||||
return storage;
|
||||
}
|
||||
}
|
||||
|
||||
_updateStorage(storage, update, location, options=this._options) {
|
||||
const encryption = options.encryption && (options.encryption.all || options.encryption[location]);
|
||||
if (encryption && storage.encryption) {
|
||||
const updatedDecryptedStorage = { ...JSON.parse(decrypt(storage.encryption.ciphertext, this._encryptionKey[location]) || '{}'), ...update };
|
||||
return { ...storage, encryption: { ...storage.encryption, ciphertext: encrypt(JSON.stringify(updatedDecryptedStorage), this._encryptionKey[location]) } };
|
||||
} else {
|
||||
return { ...storage, ...update };
|
||||
}
|
||||
}
|
||||
|
||||
_requestStorageKey(options=this._options) {
|
||||
if (!(options && options.location && options.encryption))
|
||||
throw new Error('Storage Manager cannot request storage key with unsupported options!');
|
||||
@ -301,7 +323,7 @@ export class StorageManager {
|
||||
const requestBody = {
|
||||
type : options.encryption,
|
||||
length : 42,
|
||||
...this.load('encryption', options),
|
||||
...this.load('encryption', { ...options, encryption: false }),
|
||||
};
|
||||
|
||||
this._global.App.httpClient.post({
|
||||
@ -318,9 +340,50 @@ export class StorageManager {
|
||||
if (response.salt !== requestBody.salt || response.timestamp !== requestBody.timestamp) {
|
||||
this.clear(options);
|
||||
}
|
||||
this.save('encryption', { salt: response.salt, timestamp: response.timestamp }, options);
|
||||
this.save('encryption', { salt: response.salt, timestamp: response.timestamp }, { ...options, encryption: false });
|
||||
this._encryptionKey[options.location] = response.key;
|
||||
}).catch(console.error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// TODO debug unnecessary calls of encrypt
|
||||
function encrypt(plaintext, key) {
|
||||
console.log('args encrypt', plaintext, key);
|
||||
|
||||
if (!plaintext) return '';
|
||||
if (!key) throw new Error('Cannot encrypt plaintext without a valid key!');
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
// TODO use const if possible
|
||||
let plaintextB = Buffer.from(plaintext);
|
||||
let cipherB = Buffer.alloc(plaintextB.length + sodium.crypto_secretbox_MACBYTES);
|
||||
let nonceB = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES);
|
||||
let keyB = Buffer.from(key);
|
||||
/* eslint-enable no-undef */
|
||||
|
||||
sodium.crypto_secretbox_easy(cipherB, plaintextB, nonceB, keyB);
|
||||
|
||||
return cipherB.toString('base64');
|
||||
}
|
||||
|
||||
// TODO debug unnecessary calls of decrypt
|
||||
function decrypt(ciphertext, key) {
|
||||
console.log('args decrypt', ciphertext, key);
|
||||
|
||||
if (!ciphertext) return '';
|
||||
if (!key) throw new Error('Cannot decrypt ciphertext without a valid key!');
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
// TODO use const if possible
|
||||
let cipherB = Buffer.from(ciphertext);
|
||||
let plaintextB = Buffer.alloc(cipherB.length - sodium.crypto_secretbox_MACBYTES);
|
||||
let nonceB = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES);
|
||||
let keyB = Buffer.from(key);
|
||||
/* eslint-enable no-undef */
|
||||
|
||||
sodium.crypto_secretbox_open_easy(plaintextB, cipherB, nonceB, keyB);
|
||||
|
||||
return plaintextB.toString();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user