diff --git a/circle.yml b/circle.yml index b06971f..9d2a604 100644 --- a/circle.yml +++ b/circle.yml @@ -5,5 +5,6 @@ machine: dependencies: pre: + - npm install -g node-gyp - npm install -g bower babel - bower install diff --git a/package.json b/package.json index 6f4fc7c..e12dbc0 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "author": "jniles", "license": "MIT", "dependencies": { - "argon2": "^0.10.1", + "bcrypt": "^0.8.5", "bluebird": "^3.3.3", "body-parser": "^1.15.0", "compression": "^1.6.1", diff --git a/server/controllers/auth.js b/server/controllers/auth.js index 956f36a..af609ef 100644 --- a/server/controllers/auth.js +++ b/server/controllers/auth.js @@ -8,11 +8,15 @@ * @todo - migrate this to using passportjs and JSON Web Tokens based on the invitation email. */ +import bcrypt from 'bcrypt'; +import Promise from 'bluebird'; import db from '../lib/db'; import { Unauthorized, Forbidden, NotFound } from '../lib/errors'; -import argon from 'argon2'; import logger from '../lib/logger'; +// create async functions in the bcrypt library +Promise.promisifyAll(bcrypt); + /** * Basic password-based authentication for users that are registered with the system. Passwords * are stored securely using the Argon2 password hashing library for NodeJS, and verified using @@ -35,8 +39,8 @@ export async function login(req, res, next) { throw new Unauthorized(`Bad username and password combination for ${req.body.username}`); } - // passwords are hashed with argon and random salt. Use argon to verify correctness. - const bool = await argon.verify(user.password, req.body.password); + // passwords are hashed with bcrypt, using a salt. We can verify them with a simple hash + const bool = await bcrypt.compareAsync(req.body.password, user.password); if (!bool) { throw new Unauthorized(`Bad username and password combination for ${req.body.username}`); diff --git a/server/controllers/users.js b/server/controllers/users.js index 32df298..ac14749 100644 --- a/server/controllers/users.js +++ b/server/controllers/users.js @@ -10,16 +10,19 @@ */ import express from 'express'; +import Promise from 'bluebird'; import path from 'path'; import workerpool from 'workerpool'; import uuid from 'node-uuid'; -import argon from 'argon2'; +import bcrypt from 'bcrypt'; import db from '../lib/db'; import mailer from '../lib/mailer'; import logger from '../lib/logger'; import { NotFound } from '../lib/errors'; +Promise.promisifyAll(bcrypt); + // default to 5 seconds of timeout const pool = workerpool.pool(path.join(__dirname, '../lib/RSAWorker.js')); @@ -79,11 +82,11 @@ export async function create(req, res, next) { logger.verbose(`Found invitation for ${invitation.email}. Hashing password...`); - // generate a cryptographically secure salt using the argon2 library - const salt = await argon.generateSalt(); + // generate a cryptographically secure salt using bcrypt + const salt = await bcrypt.genSaltAsync(13); - // hash the user's password argon2 before verifying - const hash = await argon.hash(data.password, salt); + // hash the user's password with bcrypt + const hash = await bcrypt.hashAsync(data.password, salt); logger.verbose(`Password hash: ${hash}.`); logger.verbose(`Creating user: ${data.username}.`); diff --git a/server/lib/db/data.sql b/server/lib/db/data.sql index bc7b1ff..862da94 100644 --- a/server/lib/db/data.sql +++ b/server/lib/db/data.sql @@ -48,6 +48,6 @@ INSERT INTO subproject (projectid, label) VALUES (1, "Empowerment"), (1, "Atelier/Conference"); - +-- password is 'password' hashed with bcrypt INSERT INTO user (username, displayname, email, password, roleid, hidden, projectid, signatureid) VALUES -("admin", "Adminstrator", "developers@imaworldhealth.org", "$argon2i$m=4096,t=3,p=1$B4cjObbiYdN1IHXLFhq2Mg$RR5DMmvjU3bBY328K/SkTuC0yER7kD1tVN3AN7mfY9E", 1, 1, 1, 1); +("admin", "Adminstrator", "developers@imaworldhealth.org", "$2a$10$RjH4nNRIR4A4uw.iBIhPMexfiRuJDIG1lF4lwp3wYabYzavBj5qL.", 1, 1, 1, 1); diff --git a/server/server.js b/server/server.js index b3015fb..241c887 100644 --- a/server/server.js +++ b/server/server.js @@ -26,6 +26,8 @@ import { handler } from './lib/errors'; /** create the server */ const server = express(); +server.use((req, res, next) => { console.log(req.ip, req.path); next(); }); + /** (pre authentication) */ server.use(middleware); @@ -33,7 +35,7 @@ server.post('/auth/basic', auth.login); server.post('/auth/logout', auth.logout); // make sure so unauthorized requests can get through -// server.use(auth.gateway); +server.use(auth.gateway); // bind controllers server.use(controllers); diff --git a/test/api/auth/auth.js b/test/api/auth/auth.js index b58769c..7b727c6 100755 --- a/test/api/auth/auth.js +++ b/test/api/auth/auth.js @@ -7,15 +7,20 @@ import request from 'supertest-as-promised'; import {} from './_env'; import * as helpers from '../../helpers/helpers'; -let agent = null; +/** the url for this test suite */ const url = '/auth/basic'; /** - * Before the test suite, start the server and connect the database. Also - * sets up the agent for sharing cookie information. + * Before the test suite, start the server and connect the database. In this case, we wish to + * avoid sharing cookies, so we create a new request using the server exported from the helpers + * module. */ test.before(async t => { - agent = await helpers.setup(); + try { + await helpers.setup(); + } catch (e) { + throw e; + } }); test('auth:failure:username', async t => { @@ -26,7 +31,7 @@ test('auth:failure:username', async t => { password: 'password', }; - const res = await agent.post(url).send(user); + const res = await request(helpers.app).post(url).send(user); const description = `Bad username and password combination for ${user.username}`; @@ -43,7 +48,7 @@ test('auth:failure:password', async t => { password: 'password1', // the real password is 'password' }; - const res = await agent.post(url).send(user); + const res = await request(helpers.app).post(url).send(user); const description = `Bad username and password combination for ${user.username}`; @@ -60,7 +65,7 @@ test('auth:failure:both', async t => { password: 'garbage', }; - const res = await agent.post(url).send(user); + const res = await request(helpers.app).post(url).send(user); const description = `Bad username and password combination for ${user.username}`; @@ -69,6 +74,14 @@ test('auth:failure:both', async t => { t.is(res.body.description, description); }); +test('auth:forbidden', async t => { + t.plan(1); + + // useing vouchers api just for kicks + const res = await request(helpers.app).get('/vouchers'); + t.is(res.status, 403); +}); + test('auth:success', async t => { t.plan(5); @@ -77,7 +90,7 @@ test('auth:success', async t => { password: 'password', }; - const res = await agent.post(url).send(user); + const res = await request(helpers.app).post(url).send(user); t.is(res.status, 200); t.is(res.body.username, 'admin'); diff --git a/test/helpers/_env.js b/test/helpers/_env.js index b277dd9..ede9b72 100755 --- a/test/helpers/_env.js +++ b/test/helpers/_env.js @@ -4,3 +4,4 @@ process.env.SESS_SECRET = 'S4meT3stS3cr3t'; process.env.LOG_LEVEL = 'debug'; process.env.MAILGUN_DOMAIN = 'mg.somedomain.com'; process.env.MAILGUN_KEY = 'key-somerandomhash'; +process.env.KEY = 'WengeTestKey'; diff --git a/test/helpers/helpers.js b/test/helpers/helpers.js index 78e159a..cd68dfa 100755 --- a/test/helpers/helpers.js +++ b/test/helpers/helpers.js @@ -47,24 +47,28 @@ async function database(server) { } /** - * Logs the agent into the application as needed. + * Setups up tests by initializing a server and database connection. */ -export async function login(agent) { +export async function setup() { + const agent = request.agent(app); + const user = { username: 'admin', password: 'password', }; - const res = await agent.post('/auth/login').send(user); - return agent; -} - -/** - * Setups up tests by initializing a server and database connection. - */ -export async function setup() { - await database(app); + try { + await database(app); + console.log('before'); + await agent.post('/auth/basic').send(user); + console.log('after'); + } catch (e) { + throw e; + } // return the agent for usage in subsequent tests - return request.agent(app); + return agent; } + +/** re-export app for consumption in auth */ +export { app };