From 2667002b0df3f74a9566f8af7c57118372fdfc47 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 10 Jul 2021 09:57:25 -0700 Subject: [PATCH] Refactored JSON RPC validation library --- src/controllers/json-rpc/users/index.js | 2 +- src/controllers/json-rpc/validators.js | 16 +++- test/unit/json-rpc/a12-validators.unit.js | 112 ++++++++++++---------- test/unit/mocks/adapters/index.js | 8 +- 4 files changed, 84 insertions(+), 54 deletions(-) diff --git a/src/controllers/json-rpc/users/index.js b/src/controllers/json-rpc/users/index.js index ae56329..bfadedc 100644 --- a/src/controllers/json-rpc/users/index.js +++ b/src/controllers/json-rpc/users/index.js @@ -29,7 +29,7 @@ class UserRPC { // Encapsulate dependencies this.userLib = this.useCases.user this.jsonrpc = jsonrpc - this.validators = new Validators() + this.validators = new Validators(localConfig) this.rateLimit = new RateLimit() } diff --git a/src/controllers/json-rpc/validators.js b/src/controllers/json-rpc/validators.js index 7158dbf..50cd251 100644 --- a/src/controllers/json-rpc/validators.js +++ b/src/controllers/json-rpc/validators.js @@ -8,14 +8,22 @@ const jwt = require('jsonwebtoken') // Local libraries const config = require('../../../config') -const UserModel = require('../../adapters/localdb/models/users') +// const UserModel = require('../../adapters/localdb/models/users') class Validators { - constructor () { + constructor (localConfig = {}) { + // Dependency Injection. + this.adapters = localConfig.adapters + if (!this.adapters) { + throw new Error( + 'Instance of Adapters library required when instantiating JSON RPC Validators library.' + ) + } + // Encapsulate dependencies this.config = config - this.UserModel = UserModel this.jwt = jwt + this.UserModel = this.adapters.localdb.Users } // Returns if user passes a valid JWT token that resolves to a valid user. @@ -63,7 +71,7 @@ class Validators { if (!user) throw new Error('User not found!') // If this current user is an admin, then quietly exit. - if (user.type === 'admin') return + if (user.type === 'admin') return true // Throw an error if the JWT token does not match the targeted user. if (user._id.toString() !== targetUserId) { diff --git a/test/unit/json-rpc/a12-validators.unit.js b/test/unit/json-rpc/a12-validators.unit.js index 25b1138..bce6c39 100644 --- a/test/unit/json-rpc/a12-validators.unit.js +++ b/test/unit/json-rpc/a12-validators.unit.js @@ -6,7 +6,6 @@ // Public npm libraries const jsonrpc = require('jsonrpc-lite') -const mongoose = require('mongoose') const sinon = require('sinon') const assert = require('chai').assert const { v4: uid } = require('uuid') @@ -15,49 +14,34 @@ const { v4: uid } = require('uuid') process.env.SVC_ENV = 'test' // Local libraries -const config = require('../../../config') const Validators = require('../../../src/controllers/json-rpc/validators') -const UserLib = require('../../../src/use-cases/user') -const userLib = new UserLib() +const adapters = require('../mocks/adapters') describe('#validators', () => { - let testUser let uut let sandbox - before(async () => { - // Connect to the Mongo Database. - console.log(`Connecting to database: ${config.database}`) - mongoose.Promise = global.Promise - mongoose.set('useCreateIndex', true) // Stop deprecation warning. - await mongoose.connect(config.database, { - useUnifiedTopology: true, - useNewUrlParser: true - }) - - // Create a test user. - testUser = await userLib.createUser({ - email: 'test544@test.com', - name: 'tester544', - password: 'password' - }) - // console.log('testUser: ', testUser) - }) - beforeEach(() => { sandbox = sinon.createSandbox() - uut = new Validators() + uut = new Validators({ adapters }) }) afterEach(() => sandbox.restore()) - after(async () => { - // Delete the test user. - testUser = await userLib.getUser({ id: testUser.userData._id }) - await userLib.deleteUser(testUser) + describe('#constructor', () => { + it('should throw an error if adapters is not passed in.', () => { + try { + uut = new Validators() - mongoose.connection.close() + assert.fail('Unexpected code path') + } catch (err) { + assert.include( + err.message, + 'Instance of Adapters library required when instantiating JSON RPC Validators library.' + ) + } + }) }) describe('#ensureUser', () => { @@ -67,18 +51,20 @@ describe('#validators', () => { const id = uid() const userCall = jsonrpc.request(id, 'users', { endpoint: 'getAll', - apiToken: testUser.token + apiToken: 'fakeJWTToken' }) const jsonStr = JSON.stringify(userCall, null, 2) const rpcData = jsonrpc.parse(jsonStr) + // Mock external dependencies. + sandbox.stub(uut.jwt, 'verify').returns(true) + sandbox.stub(uut.UserModel, 'findById').resolves(true) + const user = await uut.ensureUser(rpcData) // console.log('user: ', user) - assert.property(user, 'type') - assert.property(user, '_id') - assert.property(user, 'email') - assert.property(user, 'name') + // For this test, we return a value of 'true' instead of actual user data. + assert.equal(user, true) }) it('should throw an error if JWT token is not included', async () => { @@ -129,13 +115,14 @@ describe('#validators', () => { try { // Force 'error not found' error sandbox.stub(uut.UserModel, 'findById').resolves(null) + sandbox.stub(uut.jwt, 'verify').returns(true) // Generate the parsed data that the main router would pass to this // endpoint. const id = uid() const userCall = jsonrpc.request(id, 'users', { endpoint: 'getAll', - apiToken: testUser.token + apiToken: 'fakeJWTToken' }) const jsonStr = JSON.stringify(userCall, null, 2) const rpcData = jsonrpc.parse(jsonStr) @@ -157,18 +144,21 @@ describe('#validators', () => { const id = uid() const userCall = jsonrpc.request(id, 'users', { endpoint: 'deleteUser', - apiToken: testUser.token, - userId: testUser.userData._id.toString() + apiToken: 'fakeJWTToken', + userId: 'abc123' }) const jsonStr = JSON.stringify(userCall, null, 2) const rpcData = jsonrpc.parse(jsonStr) - const user = await uut.ensureTargetUserOrAdmin(rpcData) + // Mock external dependencies. + sandbox.stub(uut.jwt, 'verify').returns(true) + sandbox.stub(uut.UserModel, 'findById').resolves({ _id: 'abc123' }) - assert.property(user, 'type') - assert.property(user, '_id') - assert.property(user, 'email') - assert.property(user, 'name') + const user = await uut.ensureTargetUserOrAdmin(rpcData) + // console.log('user: ', user) + + // Assert that the mocked data expected is returned. + assert.equal(user._id, 'abc123') }) it('should throw error if JWT token is not provided', async () => { @@ -198,7 +188,7 @@ describe('#validators', () => { const id = uid() const userCall = jsonrpc.request(id, 'users', { endpoint: 'deleteUser', - apiToken: testUser.token + apiToken: 'fakeJWTToken' }) const jsonStr = JSON.stringify(userCall, null, 2) const rpcData = jsonrpc.parse(jsonStr) @@ -223,7 +213,7 @@ describe('#validators', () => { const userCall = jsonrpc.request(id, 'users', { endpoint: 'deleteUser', apiToken: token, - userId: testUser.userData._id.toString() + userId: 'abc123' }) const jsonStr = JSON.stringify(userCall, null, 2) const rpcData = jsonrpc.parse(jsonStr) @@ -247,12 +237,15 @@ describe('#validators', () => { const id = uid() const userCall = jsonrpc.request(id, 'users', { endpoint: 'deleteUser', - apiToken: testUser.token, - userId: testUser.userData._id.toString() + apiToken: 'fakeJWTToken', + userId: 'abc123' }) const jsonStr = JSON.stringify(userCall, null, 2) const rpcData = jsonrpc.parse(jsonStr) + // Mock external dependencies. + sandbox.stub(uut.jwt, 'verify').returns(true) + await uut.ensureTargetUserOrAdmin(rpcData) assert.fail('Unexpected code path') @@ -262,6 +255,29 @@ describe('#validators', () => { } }) - // TODO: it should exit quietly if user is an admin. + it('should return true if user is an admin', async () => { + // Generate the parsed data that the main router would pass to this + // endpoint. + const id = uid() + const userCall = jsonrpc.request(id, 'users', { + endpoint: 'deleteUser', + apiToken: 'fakeJWTToken', + userId: 'abc123' + }) + const jsonStr = JSON.stringify(userCall, null, 2) + const rpcData = jsonrpc.parse(jsonStr) + + // Mock external dependencies. + sandbox.stub(uut.jwt, 'verify').returns(true) + sandbox + .stub(uut.UserModel, 'findById') + .resolves({ _id: 'abc123', type: 'admin' }) + + const user = await uut.ensureTargetUserOrAdmin(rpcData) + // console.log('user: ', user) + + // Assert that the mocked data expected is returned. + assert.equal(user, true) + }) }) }) diff --git a/test/unit/mocks/adapters/index.js b/test/unit/mocks/adapters/index.js index 4e728b1..4bd83dc 100644 --- a/test/unit/mocks/adapters/index.js +++ b/test/unit/mocks/adapters/index.js @@ -11,4 +11,10 @@ const ipfs = { } } -module.exports = { ipfs } +const localdb = { + Users: class Users { + static findById () {} + } +} + +module.exports = { ipfs, localdb }