diff --git a/lib/io.js b/lib/io.js index 84914ae..5f37ab4 100644 --- a/lib/io.js +++ b/lib/io.js @@ -47,7 +47,7 @@ module.exports = (encPath, _password, _algorithm) => { .then(() => JSON.stringify(inputJson, null, 2)) .then((input) => doCrypto(input, false)) .then((output) => fse.writeFileAsync(actualPath, output)) - .catch((e) => { + .catch(/* istanbul ignore next */ (e) => { debug('failed to save:'); debug(e); throw e; diff --git a/lib/main.js b/lib/main.js index 3a92c19..584de40 100644 --- a/lib/main.js +++ b/lib/main.js @@ -25,7 +25,7 @@ module.exports = (encPath, password, algorithm) => { .then((data) => { return operate.getPassword(data, service, account); }) - .catch((e) => { + .catch(/* istanbul ignore next */(e) => { debug(e); return null; }); @@ -47,7 +47,7 @@ module.exports = (encPath, password, algorithm) => { .then((_success) => { success = _success; }) .then(() => success && ioImpl.save(data)) .then(() => success) - .catch(() => false); + .catch(/* istanbul ignore next */() => false); corePromise = newPromise; return newPromise; @@ -65,7 +65,7 @@ module.exports = (encPath, password, algorithm) => { .then((_success) => { success = _success; }) .then(() => success && ioImpl.save(data)) .then(() => success) - .catch(() => false); + .catch(/* istanbul ignore next */() => false); corePromise = newPromise; return newPromise; @@ -84,7 +84,7 @@ module.exports = (encPath, password, algorithm) => { .then((_success) => { success = _success; }) .then(() => success && ioImpl.save(data)) .then(() => success) - .catch(() => false); + .catch(/* istanbul ignore next */() => false); corePromise = newPromise; return newPromise; @@ -95,7 +95,7 @@ module.exports = (encPath, password, algorithm) => { .then(() => guard('service', service)) .then(() => ioImpl.load()) .then((data) => operate.findPassword(data, service)) - .catch(() => null); + .catch(/* istanbul ignore next */() => null); corePromise = newPromise; return newPromise; diff --git a/lib/operateOnData.js b/lib/operateOnData.js index f454ed3..d7eeff6 100644 --- a/lib/operateOnData.js +++ b/lib/operateOnData.js @@ -5,7 +5,7 @@ const debug = require('debug')('secure-storage:operateOnData'); module.exports.getPassword = (data, service, account) => { debug('getting password: %s %s %s', JSON.stringify(data), service, account); try { - return data[service][account]; + return data[service][account] || null; } catch (e) { return null; @@ -33,13 +33,11 @@ module.exports.deletePassword = (data, service, account) => { let password = false; try { - if (data[service]) { - if (data[service][account]) { - password = data[service][account]; - delete data[service][account]; // eslint-disable-line no-param-reassign - if (!Object.keys(data[service])) { - delete data[service]; // eslint-disable-line no-param-reassign - } + if (data[service][account]) { + password = data[service][account]; + delete data[service][account]; // eslint-disable-line no-param-reassign + if (!Object.keys(data[service]).length) { + delete data[service]; // eslint-disable-line no-param-reassign } } } diff --git a/package.json b/package.json index 8055e0e..b7e33ac 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "secure-storage", - "version": "0.2.0", + "version": "0.2.1", "description": "", "main": "lib/main.js", "scripts": { "lint": "eslint lib spec/spec.js", - "test": "mocha spec/spec.js" + "test": "nyc mocha spec/spec.js" }, "author": "Maximiliano Korp { }; // disabled linter for this as another test will use it -const decryptWithOpenSSL = (inputFile, algo) => { // eslint-disable-line no-unused-vars - const cmd = [ - 'openssl', - algo, - '-e', - '-in', - inputFile, - '-nosalt' - ].join(' '); - return cp.execSync(cmd); -}; +// const decryptWithOpenSSL = (inputFile, algo) => { // eslint-disable-line no-unused-vars +// const cmd = [ +// 'openssl', +// algo, +// '-e', +// '-in', +// inputFile, +// '-nosalt' +// ].join(' '); +// return cp.execSync(cmd); +// }; describe(`secure-storage (Using password: ${password})`, () => { beforeEach(() => { @@ -194,4 +194,101 @@ describe(`secure-storage (Using password: ${password})`, () => { expect(pass).toEqual('c'); }); }); + + it('can find a password', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return Promise.all([ + ss.setPassword('serv1', 'acct1', 'condo'), + ss.setPassword('serv1', 'acct2', 'hondo'), + ss.setPassword('serv2', 'acct1111', 'janefondo') + ]) + .then(() => + ss.findPassword('serv1') + .then((pass) => expect(['condo', 'hondo'].includes(pass)).toEqual(true)) + .then(() => ss.findPassword('serv2')) + .then((pass) => expect(['janefondo'].includes(pass)).toEqual(true)) + ); + }); + + it('gets null when getting a non-existing password', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return ss.getPassword('serv1', 'acct1') + .then((pass) => expect(pass).toEqual(null)); + }); + + it('gets null when finding a non-existing password', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return ss.findPassword('serv1', 'acct1') + .then((pass) => expect(pass).toEqual(null)); + }); + + it('can replace an existing password', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return ss.setPassword('serv1', 'acct1', 'condo') + .then(() => ss.replacePassword('serv1', 'acct1', 'hondo')) + .then(() => ss.getPassword('serv1', 'acct1')) + .then((pass) => expect(pass).toEqual('hondo')); + }); + + it('can replace a non-existing password', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return ss.setPassword('serv1', 'acct1', 'condo') + .then(() => ss.replacePassword('serv1', 'acct2', 'hondo')) + .then(() => ss.getPassword('serv1', 'acct1')) + .then((pass) => expect(pass).toEqual('condo')) + .then(() => ss.getPassword('serv1', 'acct2')) + .then((pass) => expect(pass).toEqual('hondo')); + }); + + it('does not replace an existing password when using setPassword', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return ss.setPassword('serv1', 'acct1', 'condo') + .then(() => ss.setPassword('serv1', 'acct1', 'hondo')) + .then(() => ss.getPassword('serv1', 'acct1')) + .then((pass) => expect(pass).toEqual('condo')); + }); + + it('returns when getting a non existing password', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return ss.setPassword('serv1', 'acct1', 'condo') + .then(() => ss.getPassword('poyo', 'fundido')) + .then((pass) => expect(pass).toEqual(null)); + }); + + it('can delete a password', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return ss.setPassword('serv1', 'acct1', 'condo') + .then(() => ss.getPassword('serv1', 'acct1')) + .then((pass) => expect(pass).toEqual('condo')) + .then(() => ss.deletePassword('serv1', 'acct1')) + .then((pass) => expect(pass).toEqual('condo')) + .then(() => ss.getPassword('serv1', 'acct1')) + .then((pass) => expect(pass).toEqual(null)) + .then(() => ss.deletePassword('serv2', 'cactusAccount')) + .then((pass) => expect(pass).toEqual(false)); + }); + + it('can properly guard', () => { + const filePath = path.join(__dirname, 'tmp', 'secure.enc'); + const algo = Object.keys(algos)[0]; + const ss = secureStorage(filePath, password, algo); + return ss.setPassword('serv1', 'acct1') + .then((set) => expect(set).toEqual(false)); + }); });