fix(tests): Increased coverage of REST API validators

This commit is contained in:
Chris Troutner
2022-09-17 14:18:16 -07:00
parent d266bcc54b
commit d9377ea78b
15 changed files with 534 additions and 259 deletions
+8
View File
@@ -0,0 +1,8 @@
[
{
"srcDir": "",
"destDir": "",
"files": "**/*.js",
"command": "npm run lint"
}
]
+1 -1
View File
@@ -27,7 +27,7 @@ import AdminLib from '../src/adapters/admin.js'
import errorMiddleware from '../src/controllers/rest-api/middleware/error.js' import errorMiddleware from '../src/controllers/rest-api/middleware/error.js'
import wlogger from '../src/adapters/wlogger.js' import wlogger from '../src/adapters/wlogger.js'
import Controllers from '../src/controllers/index.js' import Controllers from '../src/controllers/index.js'
import applyPassportMods from '../config/passport.js' import { applyPassportMods } from '../config/passport.js'
class Server { class Server {
constructor () { constructor () {
+6 -1
View File
@@ -5,9 +5,14 @@
/* eslint no-unneeded-ternary:0 */ /* eslint no-unneeded-ternary:0 */
// Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
// Get the version from the package.json file. // Get the version from the package.json file.
import { readFileSync } from 'fs' import { readFileSync } from 'fs'
const pkgInfo = JSON.parse(readFileSync('package.json')) const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const pkgInfo = JSON.parse(readFileSync(`${__dirname.toString()}/../../package.json`))
const version = pkgInfo.version const version = pkgInfo.version
+2 -1
View File
@@ -5,11 +5,12 @@ import production from './env/production.js'
import test from './env/test.js' import test from './env/test.js'
const env = process.env.SVC_ENV || 'development' const env = process.env.SVC_ENV || 'development'
console.log(`Loading config for this environment: ${env}`)
let config = development let config = development
if (env === 'test') { if (env === 'test') {
config = test config = test
} else if (env === 'production') { } else if (env === 'prod') {
config = production config = production
} }
+4 -1
View File
@@ -49,8 +49,11 @@ function applyPassportMods (passport) {
passportCallback passportCallback
) )
) )
return true
} }
// For testing // For testing
// export default { passport, passportCallback }; // export default { passport, passportCallback };
export default applyPassportMods // export default applyPassportMods
export { applyPassportMods, passportCallback }
+39 -151
View File
@@ -12,7 +12,7 @@
"axios": "0.27.2", "axios": "0.27.2",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"glob": "7.1.6", "glob": "7.1.6",
"ipfs-coord-esm": "9.1.2", "ipfs-coord-esm": "../ipfs-coord-esm/",
"ipfs-http-client": "58.0.0", "ipfs-http-client": "58.0.0",
"jsonrpc-lite": "2.2.0", "jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "8.5.1",
@@ -51,6 +51,28 @@
"uuid": "8.3.2" "uuid": "8.3.2"
} }
}, },
"../ipfs-coord-esm": {
"version": "9.0.0",
"license": "MIT",
"dependencies": {
"bch-encrypt-lib": "2.0.0",
"uuid": "9.0.0"
},
"devDependencies": {
"@istanbuljs/esm-loader-hook": "0.2.0",
"chai": "4.3.6",
"cross-env": "7.0.3",
"lodash.clonedeep": "4.5.0",
"minimal-slp-wallet": "5.0.3",
"mocha": "10.0.0",
"nyc": "15.1.0",
"sinon": "14.0.0",
"standard": "17.0.0"
},
"peerDependencies": {
"ipfs-http-client": ">= 58.0.0"
}
},
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.18.6", "version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
@@ -1489,11 +1511,6 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/aes-js": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz",
"integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ=="
},
"node_modules/agent-base": { "node_modules/agent-base": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -2053,15 +2070,6 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/bch-encrypt-lib": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bch-encrypt-lib/-/bch-encrypt-lib-2.0.0.tgz",
"integrity": "sha512-EAfcw2vzcKMkdO004tfLmQEEpGZA9QeIgzG9sbxDweFiGr2BP6/06G5SIJdvsJhcAd9OS9Rw5bLlPAy25kX8HQ==",
"dependencies": {
"eccrypto-js": "^5.3.0",
"wif": "^2.0.6"
}
},
"node_modules/bchaddrjs-slp": { "node_modules/bchaddrjs-slp": {
"version": "0.2.5", "version": "0.2.5",
"resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz", "resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz",
@@ -3550,28 +3558,6 @@
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"dev": true "dev": true
}, },
"node_modules/eccrypto-js": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/eccrypto-js/-/eccrypto-js-5.4.0.tgz",
"integrity": "sha512-W4xBr0UANgpkSIu2FBHG5wjhvOR4L19HwAUm9xiA4c3bzB9gGbH8+E9hSMTNF+QbmEi+TDvt373JHKA6NnW38A==",
"dependencies": {
"aes-js": "3.1.2",
"enc-utils": "2.1.0",
"hash.js": "1.1.7",
"js-sha3": "0.8.0",
"pbkdf2": "^3.0.17",
"randombytes": "2.1.0",
"secp256k1": "3.8.0"
}
},
"node_modules/eccrypto-js/node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/ecdsa-sig-formatter": { "node_modules/ecdsa-sig-formatter": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -3648,16 +3634,6 @@
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
}, },
"node_modules/enc-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/enc-utils/-/enc-utils-2.1.0.tgz",
"integrity": "sha512-VD0eunGDyzhojePzkORWDnW88gi6tIeGb5Z6QVHugux6mMAPiXyw94fb/7WdDQEWhKMSoYRyzFFUebCqeH20PA==",
"dependencies": {
"bn.js": "4.11.8",
"is-typedarray": "1.0.0",
"typedarray-to-buffer": "3.1.5"
}
},
"node_modules/encodeurl": { "node_modules/encodeurl": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -6072,24 +6048,8 @@
} }
}, },
"node_modules/ipfs-coord-esm": { "node_modules/ipfs-coord-esm": {
"version": "9.1.2", "resolved": "../ipfs-coord-esm",
"resolved": "https://registry.npmjs.org/ipfs-coord-esm/-/ipfs-coord-esm-9.1.2.tgz", "link": true
"integrity": "sha512-tZZMtBS7TTatDJD2gWdu9EOPt5E2kxwSRr5GMeRKRPnP4z1Z4QTpP94nmDhcDDABpdD4IseVQjcwUED5fd+IgQ==",
"dependencies": {
"bch-encrypt-lib": "2.0.0",
"uuid": "9.0.0"
},
"peerDependencies": {
"ipfs-http-client": ">= 58.0.0"
}
},
"node_modules/ipfs-coord-esm/node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "dist/bin/uuid"
}
}, },
"node_modules/ipfs-http-client": { "node_modules/ipfs-http-client": {
"version": "58.0.0", "version": "58.0.0",
@@ -6645,7 +6605,8 @@
"node_modules/is-typedarray": { "node_modules/is-typedarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true
}, },
"node_modules/is-unicode-supported": { "node_modules/is-unicode-supported": {
"version": "0.1.0", "version": "0.1.0",
@@ -6880,11 +6841,6 @@
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
}, },
"node_modules/js-sha3": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
"integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -14153,14 +14109,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"dependencies": {
"is-typedarray": "^1.0.0"
}
},
"node_modules/typeforce": { "node_modules/typeforce": {
"version": "1.18.0", "version": "1.18.0",
"resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
@@ -16068,11 +16016,6 @@
"dev": true, "dev": true,
"requires": {} "requires": {}
}, },
"aes-js": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz",
"integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ=="
},
"agent-base": { "agent-base": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -16485,15 +16428,6 @@
} }
} }
}, },
"bch-encrypt-lib": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bch-encrypt-lib/-/bch-encrypt-lib-2.0.0.tgz",
"integrity": "sha512-EAfcw2vzcKMkdO004tfLmQEEpGZA9QeIgzG9sbxDweFiGr2BP6/06G5SIJdvsJhcAd9OS9Rw5bLlPAy25kX8HQ==",
"requires": {
"eccrypto-js": "^5.3.0",
"wif": "^2.0.6"
}
},
"bchaddrjs-slp": { "bchaddrjs-slp": {
"version": "0.2.5", "version": "0.2.5",
"resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz", "resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz",
@@ -17669,30 +17603,6 @@
} }
} }
}, },
"eccrypto-js": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/eccrypto-js/-/eccrypto-js-5.4.0.tgz",
"integrity": "sha512-W4xBr0UANgpkSIu2FBHG5wjhvOR4L19HwAUm9xiA4c3bzB9gGbH8+E9hSMTNF+QbmEi+TDvt373JHKA6NnW38A==",
"requires": {
"aes-js": "3.1.2",
"enc-utils": "2.1.0",
"hash.js": "1.1.7",
"js-sha3": "0.8.0",
"pbkdf2": "^3.0.17",
"randombytes": "2.1.0",
"secp256k1": "3.8.0"
},
"dependencies": {
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"requires": {
"safe-buffer": "^5.1.0"
}
}
}
},
"ecdsa-sig-formatter": { "ecdsa-sig-formatter": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
@@ -17765,16 +17675,6 @@
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
}, },
"enc-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/enc-utils/-/enc-utils-2.1.0.tgz",
"integrity": "sha512-VD0eunGDyzhojePzkORWDnW88gi6tIeGb5Z6QVHugux6mMAPiXyw94fb/7WdDQEWhKMSoYRyzFFUebCqeH20PA==",
"requires": {
"bn.js": "4.11.8",
"is-typedarray": "1.0.0",
"typedarray-to-buffer": "3.1.5"
}
},
"encodeurl": { "encodeurl": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -19425,19 +19325,19 @@
"integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==" "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q=="
}, },
"ipfs-coord-esm": { "ipfs-coord-esm": {
"version": "9.1.2", "version": "file:../ipfs-coord-esm",
"resolved": "https://registry.npmjs.org/ipfs-coord-esm/-/ipfs-coord-esm-9.1.2.tgz",
"integrity": "sha512-tZZMtBS7TTatDJD2gWdu9EOPt5E2kxwSRr5GMeRKRPnP4z1Z4QTpP94nmDhcDDABpdD4IseVQjcwUED5fd+IgQ==",
"requires": { "requires": {
"@istanbuljs/esm-loader-hook": "0.2.0",
"bch-encrypt-lib": "2.0.0", "bch-encrypt-lib": "2.0.0",
"chai": "4.3.6",
"cross-env": "7.0.3",
"lodash.clonedeep": "4.5.0",
"minimal-slp-wallet": "5.0.3",
"mocha": "10.0.0",
"nyc": "15.1.0",
"sinon": "14.0.0",
"standard": "17.0.0",
"uuid": "9.0.0" "uuid": "9.0.0"
},
"dependencies": {
"uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
}
} }
}, },
"ipfs-http-client": { "ipfs-http-client": {
@@ -19832,7 +19732,8 @@
"is-typedarray": { "is-typedarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true
}, },
"is-unicode-supported": { "is-unicode-supported": {
"version": "0.1.0", "version": "0.1.0",
@@ -20021,11 +19922,6 @@
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
}, },
"js-sha3": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
"integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
},
"js-tokens": { "js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -25451,14 +25347,6 @@
"mime-types": "~2.1.24" "mime-types": "~2.1.24"
} }
}, },
"typedarray-to-buffer": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"requires": {
"is-typedarray": "^1.0.0"
}
},
"typeforce": { "typeforce": {
"version": "1.18.0", "version": "1.18.0",
"resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
+1 -1
View File
@@ -27,7 +27,7 @@
"axios": "0.27.2", "axios": "0.27.2",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"glob": "7.1.6", "glob": "7.1.6",
"ipfs-coord-esm": "9.1.2", "ipfs-coord-esm": "../ipfs-coord-esm/",
"ipfs-http-client": "58.0.0", "ipfs-http-client": "58.0.0",
"jsonrpc-lite": "2.2.0", "jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "8.5.1",
+1 -1
View File
@@ -25,9 +25,9 @@ import JsonFiles from '../adapters/json-files.js'
// Hack to get __dirname back. // Hack to get __dirname back.
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/ // https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url' import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const jsonFiles = new JsonFiles() const jsonFiles = new JsonFiles()
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
const JSON_FILE = `system-user-${config.env}.json` const JSON_FILE = `system-user-${config.env}.json`
const JSON_PATH = `${__dirname.toString()}/../../config/${JSON_FILE}` const JSON_PATH = `${__dirname.toString()}/../../config/${JSON_FILE}`
@@ -1,5 +1,17 @@
/* /*
REST API validator middleware. REST API validator middleware.
These are a series of functions that ensure the user making a REST API
matches a user in the database (or not). In can do fine-grain user control
such as telling the difference between an admin, a normal user, and an
anonymous user.
This middleware is used to gatekeep access to different REST API resources.
CT 9/17/22:
This library was lightly refactored to make it work with the new unit tests.
This not and the commented code below can be deleted once it is verified that
this refactor did not result in any breaking changes.
*/ */
import User from '../../../adapters/localdb/models/users.js' import User from '../../../adapters/localdb/models/users.js'
@@ -21,12 +33,10 @@ class Validators {
async ensureUser (ctx, next) { async ensureUser (ctx, next) {
try { try {
// console.log(`getToken: ${typeof (getToken)}`)
const token = _this.getToken(ctx) const token = _this.getToken(ctx)
if (!token) { if (!token) {
// console.log(`Err: Token not provided.`) throw new Error('Token could not be retrieved from header')
ctx.throw(401)
} }
let decoded = null let decoded = null
@@ -35,20 +45,23 @@ class Validators {
// console.log(`config: ${JSON.stringify(config, null, 2)}`) // console.log(`config: ${JSON.stringify(config, null, 2)}`)
decoded = _this.jwt.verify(token, config.token) decoded = _this.jwt.verify(token, config.token)
} catch (err) { } catch (err) {
// console.log(`Err: Token could not be decoded: ${err}`) throw new Error('Could not verify JWT')
ctx.throw(401)
} }
ctx.state.user = await _this.User.findById(decoded.id, '-password') ctx.state.user = await _this.User.findById(decoded.id, '-password')
if (!ctx.state.user) { if (!ctx.state.user) {
// console.log(`Err: Could not find user.`) // console.log('Err: Could not find user.')
ctx.throw(401) throw new Error('Could not find user')
} }
// return next() // return next()
return true return true
} catch (error) { } catch (error) {
ctx.throw(401) // console.log('Ensure user error: ', error)
// console.log('ctx: ', ctx)
ctx.status = 401
ctx.throw(401, error.message)
} }
} }
@@ -61,7 +74,8 @@ class Validators {
if (!token) { if (!token) {
// console.log(`Err: Token not provided.`) // console.log(`Err: Token not provided.`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Token could not be retrieved from header')
} }
let decoded = null let decoded = null
@@ -71,22 +85,26 @@ class Validators {
decoded = _this.jwt.verify(token, config.token) decoded = _this.jwt.verify(token, config.token)
} catch (err) { } catch (err) {
// console.log(`Err: Token could not be decoded: ${err}`) // console.log(`Err: Token could not be decoded: ${err}`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Could not verify JWT')
} }
ctx.state.user = await _this.User.findById(decoded.id, '-password') ctx.state.user = await _this.User.findById(decoded.id, '-password')
if (!ctx.state.user) { if (!ctx.state.user) {
// console.log(`Err: Could not find user.`) // console.log(`Err: Could not find user.`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Could not find user')
} }
if (ctx.state.user.type !== 'admin') { if (ctx.state.user.type !== 'admin') {
ctx.throw(401, 'not admin') // ctx.throw(401, 'not admin')
throw new Error('User is not an admin')
} }
// return next() // return next()
return true return true
} catch (error) { } catch (error) {
ctx.status = 401
ctx.throw(401, error.message) ctx.throw(401, error.message)
} }
} }
@@ -102,7 +120,8 @@ class Validators {
if (!token) { if (!token) {
// console.log(`Err: Token not provided.`) // console.log(`Err: Token not provided.`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Token could not be retrieved from header')
} }
// The user ID targeted in this API call. // The user ID targeted in this API call.
@@ -115,14 +134,16 @@ class Validators {
// console.log(`config: ${JSON.stringify(config, null, 2)}`) // console.log(`config: ${JSON.stringify(config, null, 2)}`)
decoded = _this.jwt.verify(token, config.token) decoded = _this.jwt.verify(token, config.token)
} catch (err) { } catch (err) {
// console.log(`Err: Token could not be decoded: ${err}`) console.log(`Err: Token could not be decoded: ${err}`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Could not verify JWT')
} }
ctx.state.user = await _this.User.findById(decoded.id, '-password') ctx.state.user = await _this.User.findById(decoded.id, '-password')
if (!ctx.state.user) { if (!ctx.state.user) {
// console.log(`Err: Could not find user.`) // console.log(`Err: Could not find user.`)
ctx.throw(401) // ctx.throw(401)
throw new Error('Could not find user')
} }
// console.log('ctx.state.user: ', ctx.state.user) // console.log('ctx.state.user: ', ctx.state.user)
@@ -136,7 +157,8 @@ class Validators {
// If they don't match, then the calling user better be an admin. // If they don't match, then the calling user better be an admin.
if (ctx.state.user.type !== 'admin') { if (ctx.state.user.type !== 'admin') {
ctx.throw(401, 'not admin') // ctx.throw(401, 'not admin')
throw new Error('User is not an admin')
} else { } else {
wlogger.verbose("It's ok. The user is an admin.") wlogger.verbose("It's ok. The user is an admin.")
} }
@@ -145,6 +167,8 @@ class Validators {
// return next() // return next()
return true return true
} catch (error) { } catch (error) {
// console.log('Error in ensureTargetUserOrAdmin(): ', error)
ctx.status = 401
ctx.throw(401, error.message) ctx.throw(401, error.message)
} }
} }
+2 -2
View File
@@ -36,7 +36,7 @@ class UserRESTControllerLib {
* @apiGroup REST Users * @apiGroup REST Users
* *
* @apiExample Example usage: * @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "user": { "email": "email@format.com", "name": "my name", "password": "secretpasas" } }' localhost:5001/users * curl -H "Content-Type: application/json" -X POST -d '{ "user": { "email": "email@format.com", "name": "my name", "password": "secretpasas" } }' localhost:5010/users
* *
* @apiParam {Object} user User object (required) * @apiParam {Object} user User object (required)
* @apiParam {String} user.email Email * @apiParam {String} user.email Email
@@ -136,7 +136,7 @@ class UserRESTControllerLib {
* @apiGroup REST Users * @apiGroup REST Users
* *
* @apiExample Example usage: * @apiExample Example usage:
* curl -H "Content-Type: application/json" -X GET localhost:5000/users/56bd1da600a526986cf65c80 * curl -H "Content-Type: application/json" -X GET localhost:5010/users/56bd1da600a526986cf65c80
* *
* @apiSuccess {Object} users User object * @apiSuccess {Object} users User object
* @apiSuccess {ObjectId} users._id User id * @apiSuccess {ObjectId} users._id User id
@@ -0,0 +1,306 @@
/*
Unit tests for the REST API middleware that validates users.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local libraries
import Validators from '../../../../../src/controllers/rest-api/middleware/validators.js'
import { context as mockContext } from '../../../../unit/mocks/ctx-mock.js'
describe('#Validators', () => {
let uut
let ctx
let sandbox
beforeEach(() => {
uut = new Validators()
// Mock the context object.
ctx = mockContext()
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#getToken', () => {
it('should return null if no header is provided', () => {
const result = uut.getToken(ctx)
assert.equal(result, null)
})
it('should return null if header is not in two parts', () => {
ctx.request.header.authorization = 'Bearer'
const result = uut.getToken(ctx)
assert.equal(result, null)
})
it('should return null if first part of header does not container the word bearer', () => {
ctx.request.header.authorization = 'some thing'
const result = uut.getToken(ctx)
assert.equal(result, null)
})
it('should return the JWT token from the header', () => {
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzMjNiNTUwNzgxYWYzNTc4YzI0ZmU5YiIsImlhdCI6MTY2MzQ0NDczNCwiZXhwIjoxNjYzNTMxMTM0fQ.BY5sOfXc4z5axS98CdTfyqnO9y2wijOlwnv52rcvxHA'
ctx.request.header.authorization = `Bearer ${jwt}`
const result = uut.getToken(ctx)
assert.equal(result, jwt)
})
})
describe('#ensureUser', () => {
it('should throw error if token is not provided', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns()
await uut.ensureUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if token can not be verified', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').throws(new Error('test error'))
await uut.ensureUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user can not be found in database', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves(false)
await uut.ensureUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should return true if the user is verified', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ user: 'alice' })
const result = await uut.ensureUser(ctx)
assert.equal(result, true)
})
})
describe('#ensureUser', () => {
it('should throw error if token is not provided', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns()
await uut.ensureAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if token can not be verified', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').throws(new Error('test error'))
await uut.ensureAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user can not be found in database', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves(false)
await uut.ensureAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user is not an admin', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'user' })
await uut.ensureAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should return true if the user is an admin', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'admin' })
const result = await uut.ensureAdmin(ctx)
assert.equal(result, true)
})
})
describe('#ensureTargetUserOrAdmin', () => {
it('should throw error if token is not provided', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns()
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if token can not be verified', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').throws(new Error('test error'))
ctx.params = {
id: '456'
}
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user can not be found in database', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves(false)
ctx.params = {
id: '456'
}
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error if user is not an admin', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'user' })
ctx.params = {
id: '456'
}
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should throw error is user is not admin or target user', async () => {
try {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'user', _id: '123' })
ctx.params = {
id: '456'
}
await uut.ensureTargetUserOrAdmin(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(ctx.status, 401)
}
})
it('should return true if the user is an admin', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'admin', _id: '123' })
ctx.params = {
id: '456'
}
const result = await uut.ensureTargetUserOrAdmin(ctx)
assert.equal(result, true)
})
it('should return true if the user is the target user', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut, 'getToken').returns('fake-jwt')
sandbox.stub(uut.jwt, 'verify').returns({})
sandbox.stub(uut.User, 'findById').resolves({ type: 'user', _id: '123' })
ctx.params = {
id: '123'
}
const result = await uut.ensureTargetUserOrAdmin(ctx)
assert.equal(result, true)
})
})
})
@@ -4,12 +4,10 @@
// Public npm libraries // Public npm libraries
import { assert } from 'chai' import { assert } from 'chai'
import sinon from 'sinon' import sinon from 'sinon'
// Local support libraries // Local support libraries
import adapters from '../../../mocks/adapters/index.js' import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js' import UseCasesMock from '../../../mocks/use-cases/index.js'
import UserController from '../../../../../src/controllers/rest-api/users/controller.js' import UserController from '../../../../../src/controllers/rest-api/users/controller.js'
+30 -2
View File
@@ -2,6 +2,8 @@
Unit tests for the config directory Unit tests for the config directory
*/ */
import { assert } from 'chai'
let currentEnv let currentEnv
describe('#config', () => { describe('#config', () => {
@@ -15,8 +17,34 @@ describe('#config', () => {
process.env.SVC_ENV = currentEnv process.env.SVC_ENV = currentEnv
}) })
it('Should return development environment config', () => { it('Should return development environment config by default', async () => {
// import config from '../../../config/index.js' const importedConfig = await import('../../../config/index.js')
const config = importedConfig.default
// console.log('config: ', config) // console.log('config: ', config)
assert.equal(config.env, 'dev')
})
it('Should return test environment config', async () => {
// Hack to dynamically import a library multiple times:
// https://github.com/denoland/deno/issues/6946
process.env.SVC_ENV = 'test'
const importedConfig2 = await import('../../../config/index.js?foo=bar1')
const config = importedConfig2.default
// console.log('config: ', config)
assert.equal(config.env, 'test')
})
it('Should return test environment config', async () => {
process.env.SVC_ENV = 'prod'
const importedConfig3 = await import('../../../config/index.js?foo=bar2')
const config = importedConfig3.default
// console.log('config: ', config)
assert.equal(config.env, 'prod')
}) })
}) })
+93 -78
View File
@@ -1,78 +1,93 @@
// /* /*
// Unit tests for the passport library. Unit tests for the passport library.
// */ */
//
// // Public npm libraries // Public npm libraries
// // const assert = require('chai').assert import { assert } from 'chai'
// import sinon from 'sinon'; import sinon from 'sinon'
// import passport from 'koa-passport'
// // Local libraries
// import User from '../../../src/adapters/localdb/models/users.js'; // Local libraries
// import User from '../../../src/adapters/localdb/models/users.js'
// import { passport, passportCallback } from '../../../config/passport.js';
// import adaptersMock from '../mocks/adapters/index.js'; import { applyPassportMods, passportCallback } from '../../../config/passport.js'
// import adaptersMock from '../mocks/adapters/index.js'
// describe('#passport', () => {
// let sandbox describe('#passport', () => {
// let id let sandbox
// let done let id
// let done
// beforeEach(() => {
// sandbox = sinon.createSandbox() beforeEach(() => {
// sandbox = sinon.createSandbox()
// id = 'abc123'
// done = () => {} id = 'abc123'
// }) done = () => {}
// })
// afterEach(() => sandbox.restore())
// afterEach(() => sandbox.restore())
// describe('#serializeUser', () => {
// it('should serialize a user', () => { describe('#passportCallback', () => {
// const user = { it('should return if user is found', () => {
// id: 'abc123' // Mock Users model.
// } sandbox.stub(User, 'findOne').resolves({ id })
// const done = () => {}
// passportCallback(id, 'password', done)
// passport.serializeUser(user, done) })
// })
// }) it('should return if password is validated', () => {
// // Mock Users model.
// describe('#deserializeUser', () => { sandbox.stub(User, 'findOne').resolves(new adaptersMock.localdb.Users())
// it('should deserialize a user', () => {
// // Mock Users model. passportCallback(id, 'password', done)
// sandbox.stub(User, 'findById').resolves({ id }) })
//
// passport.deserializeUser(id, done) it('should catch a high-level error', () => {
// }) // Force an error
// sandbox.stub(User, 'findOne').rejects(new Error('test error'))
// it('should catch and handle errors', () => {
// // Force an error passportCallback(id, 'password', done)
// sandbox.stub(User, 'findById').rejects(new Error('test error')) })
// })
// passport.deserializeUser(id, done)
// }) describe('#applyPassportMods', () => {
// }) it('should apply modifications to default passport behavior', () => {
// const result = applyPassportMods(passport)
// describe('#passportCallback', () => {
// it('should return if user is found', () => { assert.equal(result, true)
// // Mock Users model. })
// sandbox.stub(User, 'findOne').resolves({ id }) })
//
// passportCallback(id, 'password', done) describe('#serializeUser', () => {
// }) it('should serialize a user', () => {
// const user = {
// it('should return if password is validated', () => { id: 'abc123'
// // Mock Users model. }
// sandbox.stub(User, 'findOne').resolves(new adaptersMock.localdb.Users()) const done = () => {}
//
// passportCallback(id, 'password', done) applyPassportMods(passport)
// })
// passport.serializeUser(user, done)
// it('should catch a high-level error', () => { })
// // Force an error })
// sandbox.stub(User, 'findOne').rejects(new Error('test error'))
// describe('#deserializeUser', () => {
// passportCallback(id, 'password', done) it('should deserialize a user', () => {
// }) // Mock Users model.
// }) sandbox.stub(User, 'findById').resolves({ id })
// })
applyPassportMods(passport)
passport.deserializeUser(id, done)
})
it('should catch and handle errors', () => {
// Force an error
sandbox.stub(User, 'findById').rejects(new Error('test error'))
applyPassportMods(passport)
passport.deserializeUser(id, done)
})
})
})
-1
View File
@@ -4,7 +4,6 @@
// Public npm libraries // Public npm libraries
import { assert } from 'chai' import { assert } from 'chai'
import sinon from 'sinon' import sinon from 'sinon'
// Local libraries // Local libraries