diff options
author | Marius David Wieschollek <passwords.public@mdns.eu> | 2020-12-23 00:53:56 +0300 |
---|---|---|
committer | Marius David Wieschollek <passwords.public@mdns.eu> | 2020-12-23 00:53:56 +0300 |
commit | d8e2ab26c60ed6ffee324711720d6b51ea70f718 (patch) | |
tree | 5fd548f6b9e2295bc8a457a2196409f0c6a5fc4c /src | |
parent | a4f90d861516e5b7cc1779f6a6ad563585c85d98 (diff) |
Improve encryption handling
Throw errors when encryption not ready on decrypt
Allow session renew
Signed-off-by: Marius David Wieschollek <passwords.public@mdns.eu>
Diffstat (limited to 'src')
-rw-r--r-- | src/Authorization/SessionAuthorization.js | 1 | ||||
-rw-r--r-- | src/Client/BasicPasswordsClient.js | 49 | ||||
-rw-r--r-- | src/Encryption/CSEv1Encryption.js | 26 | ||||
-rw-r--r-- | src/Exception/Encryption/EncryptionNotEnabledError.js | 5 | ||||
-rw-r--r-- | src/Exception/Encryption/InvalidObjectTypeError.js | 5 | ||||
-rw-r--r-- | src/Exception/Encryption/UnsupportedEncryptionTypeError.js | 27 | ||||
-rw-r--r-- | src/Exception/EncryptionTypeNotSupported.js | 29 | ||||
-rw-r--r-- | src/Repositories/AbstractRepository.js | 44 | ||||
-rw-r--r-- | src/main.js | 7 |
9 files changed, 109 insertions, 84 deletions
diff --git a/src/Authorization/SessionAuthorization.js b/src/Authorization/SessionAuthorization.js index 9a84ef1..8b36bf1 100644 --- a/src/Authorization/SessionAuthorization.js +++ b/src/Authorization/SessionAuthorization.js @@ -1,4 +1,3 @@ -import PWDv1Challenge from './Challenge/PWDv1Challenge'; import CSEv1Keychain from '../Encryption/Keychain/CSEv1Keychain'; import AbstractToken from './Token/AbstractToken'; diff --git a/src/Client/BasicPasswordsClient.js b/src/Client/BasicPasswordsClient.js index d5f1a84..f313211 100644 --- a/src/Client/BasicPasswordsClient.js +++ b/src/Client/BasicPasswordsClient.js @@ -14,10 +14,11 @@ export default class BasicPasswordsClient { this._classes.setInstance('model.server', server); this._classes.setInstance('api', this); this._classes.setInstance('client', this); + this._classes.setInstance('classes', this._classes); this._server = server; - this._session = this._classes.getInstance('model.session', server.getUser(), server.getToken()); this._events = this._classes.getInstance('event.event'); + this.renewSession(); } /** @@ -79,7 +80,7 @@ export default class BasicPasswordsClient { */ getRequest() { /** @type {ApiRequest} **/ - let request = this.getClass('network.request', this, this._server.getApiUrl(), this.getSession()); + let request = this._classes.getClass('network.request', this, this._server.getApiUrl(), this.getSession()); if(this._config.userAgent !== null) { request.setUserAgent(this._config.userAgent); } @@ -91,9 +92,23 @@ export default class BasicPasswordsClient { * @returns {Session} */ getSession() { - return this._session - .setUser(this._server.getUser()) - .setToken(this._server.getToken()); + return this + ._session + .setUser(this._server.getUser()) + .setToken(this._server.getToken()); + } + + /** + * Replaces the session with a blank one + * + * @returns {Session} + */ + renewSession() { + this._session = this._classes.getClass('model.session', this._server.getUser(), this._server.getToken()); + this._classes.setInstance('session', this._session); + this._classes.setInstance('model.session', this._session); + this._classes.setInstance('authorization.session', this._classes.getClass('authorization.session')); + return this._session; } /** @@ -101,7 +116,7 @@ export default class BasicPasswordsClient { * @returns {SessionAuthorization} */ getSessionAuthorization() { - return this.getInstance('authorization.session'); + return this._classes.getInstance('authorization.session'); } /** @@ -109,7 +124,7 @@ export default class BasicPasswordsClient { * @returns {PasswordRepository} */ getPasswordRepository() { - return this.getInstance('repository.password'); + return this._classes.getInstance('repository.password'); } /** @@ -117,7 +132,7 @@ export default class BasicPasswordsClient { * @returns {FolderRepository} */ getFolderRepository() { - return this.getInstance('repository.folder'); + return this._classes.getInstance('repository.folder'); } /** @@ -125,7 +140,7 @@ export default class BasicPasswordsClient { * @returns {TagRepository} */ getTagRepository() { - return this.getInstance('repository.tag'); + return this._classes.getInstance('repository.tag'); } /** @@ -133,7 +148,7 @@ export default class BasicPasswordsClient { * @returns {CSEv1Encryption} */ getCseV1Encryption() { - return this.getInstance('encryption.csev1'); + return this._classes.getInstance('encryption.csev1'); } /** @@ -147,16 +162,16 @@ export default class BasicPasswordsClient { } if(mode === 'none') { - return this.getInstance('encryption.none'); + return this._classes.getInstance('encryption.none'); } if(mode === 'csev1') { - return this.getInstance('encryption.csev1'); + return this._classes.getInstance('encryption.csev1'); } - let csev1 = this.getInstance('encryption.csev1'); + let csev1 = this._classes.getInstance('encryption.csev1'); if(csev1.enabled()) return csev1; - return this.getInstance('encryption.none'); + return this._classes.getInstance('encryption.none'); } /** @@ -165,7 +180,7 @@ export default class BasicPasswordsClient { * @return {*} */ getInstance(...parameters) { - return this._classes.getInstance(...parameters) + return this._classes.getInstance(...parameters); } /** @@ -174,7 +189,7 @@ export default class BasicPasswordsClient { * @return {*} */ setInstance(...parameters) { - return this._classes.setInstance(...parameters) + return this._classes.setInstance(...parameters); } /** @@ -183,7 +198,7 @@ export default class BasicPasswordsClient { * @return {*} */ getClass(...parameters) { - return this._classes.getClass(...parameters) + return this._classes.getClass(...parameters); } /** diff --git a/src/Encryption/CSEv1Encryption.js b/src/Encryption/CSEv1Encryption.js index 9beb88c..efe772b 100644 --- a/src/Encryption/CSEv1Encryption.js +++ b/src/Encryption/CSEv1Encryption.js @@ -1,17 +1,20 @@ import sodium from 'libsodium-wrappers'; -import BooleanState from '../State/BooleanState'; export default class CSEv1Encryption { - constructor() { + /** + * @param {BasicClassLoader} classLoader + */ + constructor(classLoader) { this.fields = { password: ['url', 'label', 'notes', 'password', 'username', 'customFields'], folder : ['label'], tag : ['label', 'color'] }; - this._enabled = new BooleanState(false); - this._ready = new BooleanState(false); + this._enabled = classLoader.getClass('state.boolean', false); + this._ready = classLoader.getClass('state.boolean', false); this._keychain = null; + this._classLoader = classLoader; sodium.ready.then(() => {this._ready.set(true);}); } @@ -22,6 +25,7 @@ export default class CSEv1Encryption { */ async ready() { await this._ready.awaitTrue() && await this._enabled.awaitTrue(); + return true; } /** @@ -29,7 +33,7 @@ export default class CSEv1Encryption { * @returns {Boolean} */ enabled() { - this._ready.get() && this._enabled.get(); + return this._ready.get() && this._enabled.get(); } /** @@ -40,9 +44,8 @@ export default class CSEv1Encryption { * @returns {Object} */ async encrypt(object, type) { - // TODO custom errors here - if(!this.fields.hasOwnProperty(type)) throw new Error('Invalid object type'); - await this.ready(); + if(!this.fields.hasOwnProperty(type)) throw this._classLoader.getClass('exception.encryption.object'); + if(!this.enabled()) throw this._classLoader.getClass('exception.encryption.enabled'); let fields = this.fields[type], key = this._keychain.getCurrentKey(); @@ -69,10 +72,9 @@ export default class CSEv1Encryption { * @returns {Object} */ async decrypt(object, type) { - // TODO custom errors here - if(!this.fields.hasOwnProperty(type)) throw new Error('Invalid object type'); - if(object.cseType !== 'CSEv1r1') throw new Error('Unsupported encryption type'); - await this.ready(); + if(!this.fields.hasOwnProperty(type)) throw this._classLoader.getClass('exception.encryption.object'); + if(object.cseType !== 'CSEv1r1') throw this._classLoader.getClass('exception.encryption.unsupported', object, 'CSEv1r1'); + if(!this.enabled()) throw this._classLoader.getClass('exception.encryption.enabled'); let fields = this.fields[type], key = this._keychain.getKey(object.cseKey); diff --git a/src/Exception/Encryption/EncryptionNotEnabledError.js b/src/Exception/Encryption/EncryptionNotEnabledError.js new file mode 100644 index 0000000..b31461c --- /dev/null +++ b/src/Exception/Encryption/EncryptionNotEnabledError.js @@ -0,0 +1,5 @@ +export default class EncryptionNotEnabledError extends Error { + constructor() { + super(`CSE Encryption not enabled or ready`); + } +}
\ No newline at end of file diff --git a/src/Exception/Encryption/InvalidObjectTypeError.js b/src/Exception/Encryption/InvalidObjectTypeError.js new file mode 100644 index 0000000..de0d77b --- /dev/null +++ b/src/Exception/Encryption/InvalidObjectTypeError.js @@ -0,0 +1,5 @@ +export default class InvalidObjectTypeError extends Error { + constructor(type) { + super(`Invalid Object Type "${type}"`); + } +}
\ No newline at end of file diff --git a/src/Exception/Encryption/UnsupportedEncryptionTypeError.js b/src/Exception/Encryption/UnsupportedEncryptionTypeError.js new file mode 100644 index 0000000..adef944 --- /dev/null +++ b/src/Exception/Encryption/UnsupportedEncryptionTypeError.js @@ -0,0 +1,27 @@ +export default class UnsupportedEncryptionTypeError extends Error { + + /** + * @returns {String} + */ + get supportedTypes() { + return this._supportedTypes; + } + + /** + * @returns {Object} + */ + get object() { + return this._object; + } + + constructor(object, supportedTypes) { + if(Array.isArray(supportedTypes)) { + supportedTypes = supportedTypes.join(', '); + } + + super(`Unsupported encryption type "${object.cseType}" in ${object.id}. Supported types are ${supportedTypes}.`); + + this._object = object; + this._supportedTypes = supportedTypes; + } +}
\ No newline at end of file diff --git a/src/Exception/EncryptionTypeNotSupported.js b/src/Exception/EncryptionTypeNotSupported.js deleted file mode 100644 index aa1824d..0000000 --- a/src/Exception/EncryptionTypeNotSupported.js +++ /dev/null @@ -1,29 +0,0 @@ -export default class EncryptionTypeNotSupported extends Error { - - /** - * - * @return {String} - */ - get objectId() { - return this._objectId; - } - - /** - * - * @return {String} - */ - get cseType() { - return this._cseType; - } - - /** - * - * @param {String} objectId - * @param {String} cseType - */ - constructor(objectId, cseType) { - super(`The encryption type ${cseType} used for ${objectId} is not supported by this client`); - this._objectId = objectId; - this._cseType = cseType; - } -}
\ No newline at end of file diff --git a/src/Repositories/AbstractRepository.js b/src/Repositories/AbstractRepository.js index ce14382..b947299 100644 --- a/src/Repositories/AbstractRepository.js +++ b/src/Repositories/AbstractRepository.js @@ -199,26 +199,30 @@ export default class AbstractRepository { * @return {Promise<AbstractRevisionModel[]>} * @private */ - async _dataToModels(data, detailLevel) { - let promises = [], - models = []; - - for(let element of data) { - promises.push(new Promise((resolve) => { - this._dataToModel(element, detailLevel) - .then((model) => { - models.push(model); - resolve(); - }) - .catch((e) => { - console.error(e, element); - }); - })); - } + _dataToModels(data, detailLevel) { + return new Promise(async (resolve, reject) => { + let promises = [], + models = []; + + for(let element of data) { + promises.push( + new Promise( + (resolve) => { + this._dataToModel(element, detailLevel) + .then((model) => { + models.push(model); + resolve(); + }) + .catch(reject); + } + ) + ); + } - await Promise.all(promises); + await Promise.all(promises); - return models; + resolve(models); + }); } /** @@ -241,12 +245,12 @@ export default class AbstractRepository { if(typeof detailLevel === 'string') { detailLevel = detailLevel.trim().split('+'); } - if(detailLevel===null || detailLevel.length === 0) return this.DEFAULT_DETAIL_LEVEL; + if(detailLevel === null || detailLevel.length === 0) return this.DEFAULT_DETAIL_LEVEL; for(let level of detailLevel) { if(this.AVAILABLE_DETAIL_LEVELS.indexOf(level) === -1) { // @TODO custom error - throw new Error('Unknown detail level '+level); + throw new Error('Unknown detail level ' + level); } } diff --git a/src/main.js b/src/main.js index 60a01ee..4f19add 100644 --- a/src/main.js +++ b/src/main.js @@ -12,10 +12,8 @@ import EnhancedTag from "./Model/Tag/EnhancedTag"; import Tag from "./Model/Tag/Tag"; import Server from "./Model/Server/Server"; -/** @deprecated **/ -const Api = PasswordsClient; - export default PasswordsClient; + export { PasswordsClient, BasicPasswordsClient, @@ -29,6 +27,5 @@ export { Password, Folder, Tag, - Server, - Api + Server }; |