diff --git a/app.js b/app.js index a7533d2..8d7b190 100644 --- a/app.js +++ b/app.js @@ -8,11 +8,9 @@ let bodyParser = require('body-parser'); // TODO: Refactor these into a single routes module let routes = require('./routes/index'); let projects = require('./routes/projects'); -<<<<<<< HEAD +let auth = require('./lib/auth'); let logout = require('./routes/logout'); -======= let hierarchy = require('./routes/hierarchy'); ->>>>>>> hierarchy-endpoint let app = express(); @@ -37,6 +35,8 @@ app.use('/projects', projects); app.use('/logout', logout); app.use('/hierarchy', hierarchy); +app.use(auth.isAuthenticated); // To flag the user as authenticated or not + // catch 404 and forward to error handler app.use(function (req, res, next) { let err = new Error('Not Found'); diff --git a/lib/auth.js b/lib/auth.js index dbd22ed..7ebeb5a 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,13 +1,27 @@ 'use strict'; let pagination = require('./pagination'); +let request = require('request'); // TODO: Separate responsibilities of authenticate into two parts: (1) log-in and (2) return projects + +/** + * Sets up the URL and passes it to Pagination + * @param {string} username + * @param {string} password + * @param {string} teamName + * @returns {*} + */ function authenticate (username, password, teamName) { 'use strict'; let url = 'http://' + username + ':' + password + '@' + teamName + '.jamacloud.com/rest/latest/projects'; return pagination(url, 0, Number.MAX_SAFE_INTEGER); } +/** + * Validates that the username, password and teamname are not blank and meet the character length restrictions + * @param req + * @returns {boolean} + */ function validate (req) { 'use strict'; @@ -20,7 +34,47 @@ function validate (req) { teamName !== ''); } +/** + * Checks the user credentials by connecting to jamacloud and trying to pull project data. + * If the credentials are valid and the user is authenticated by Jama, then isAuth is set to true + * Sets the isAuthenticated flag. + * @param {object} req + * @param {object} res + * @param {object} next - The next function to be called after this one + */ +function isAuthenticated (req, res, next) { + 'use strict'; + + let username = req.session.username; + let password = req.session.password; + let teamName = req.session.teamName; + let isAuth = false; + let isValid = validate(req); + + if (isValid && isServerAuthenticated(username, password, teamName)) { + isAuth = true; + } + req.session.isAuthenticated = isAuth; // Set the isAuthenticate flag + next(); +} + +/** + * Checks the authentication status of credentials with Jama's servers + * @param {string} username + * @param {string} password + * @param {string} teamName + * + * @returns {boolean} + */ +function isServerAuthenticated (username, password, teamName) { + let url = 'http://' + username + ':' + password + '@' + teamName + '.jamacloud.com/rest/latest/'; + request({url: url, json: true}, function (error, response, body) { + return (response.body.meta.status === 'OK' && !(error)); + }); +} + module.exports = { authenticate: authenticate, - validate: validate + validate: validate, + isAuthenticated: isAuthenticated }; diff --git a/package.json b/package.json index 88194b3..2f800bf 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,11 @@ "istanbul": "^0.4.4", "mocha": "^2.5.3", "mocha-lcov-reporter": "^1.2.0", + "node-mocks-http": "^1.5.2", "proxyquire": "^1.7.10", "semistandard": "^8.0.0", + "sinon": "^1.17.4", + "sinon-chai": "^2.8.0", "snazzy": "^4.0.0", "supertest": "^2.0.0", "watchify": "^3.7.0" diff --git a/routes/projects.js b/routes/projects.js index bdc8315..0cedb11 100644 --- a/routes/projects.js +++ b/routes/projects.js @@ -1,8 +1,9 @@ let express = require('express'); let router = express.Router(); +let auth = require('../lib/auth'); /* GET home page. */ -router.get('/', function (req, res, next) { +router.get('/', auth.isAuthenticated, function (req, res, next) { res.render('projects', {title: 'Projects', projects: req.session.projects ? req.session.projects : null}); }); diff --git a/test/lib/auth.js b/test/lib/auth.js index ebc4b79..a7987f3 100644 --- a/test/lib/auth.js +++ b/test/lib/auth.js @@ -5,8 +5,9 @@ let expect = chai.expect; let dirtyChai = require('dirty-chai'); let proxyquire = require('proxyquire'); let chaiAsPromised = require('chai-as-promised'); - let auth = require('../../lib/auth'); +let httpMocks = require('node-mocks-http'); +let sinon = require('sinon'); chai.use(dirtyChai); chai.use(chaiAsPromised); @@ -17,42 +18,50 @@ describe('Auth module', function () { { username: '', password: '', - teamName: '' + teamName: '', + description: 'all fields are empty' }, { username: 'dummy', password: '', - teamName: '' + teamName: '', + description: 'username is correct and password/teamname are empty' }, { username: '', password: 'dumber', - teamName: '' + teamName: '', + description: 'password is incorrect and username/teamname are empty' }, { username: '', password: '', - teamName: 'dummy' + teamName: 'dummy', + description: 'teamname is incorrect and password/username are empty' }, { username: 'dummy', password: 'password', - teamName: '' + teamName: '', + description: 'username is incorrect and password/teamname are empty' }, { username: '', password: 'password', - teamName: 'sevensource' + teamName: 'sevensource', + description: 'username is empty' }, { username: 'dummy', password: '', - teamName: 'sevensource' + teamName: 'sevensource', + description: 'username is incorrect and password is empty' }, { username: 'dummy', password: 'password', - teamName: 'sevensource' + teamName: 'sevensource', + description: 'all fields are valid' } ]; describe('validate function', function () { @@ -162,4 +171,50 @@ describe('Auth module', function () { expect(auth.authenticate(username, password, teamName)).to.be.rejected(); }); }); + + describe('isAuthenticated function', () => { + credentialFixtureCases.forEach(function (fixture) { + let credentialsFixture = { + session: {}, + body: {} + }; + credentialsFixture.session = fixture; + credentialsFixture.body = fixture; + auth = proxyquire('../../lib/auth', { + './isServerAuthenticated': isServerAuthenticatedStub + }); + + let callback = sinon.spy(); + let req = httpMocks.createRequest(); + let res = httpMocks.createResponse(); + + req.session = {}; + req.body = {}; + req.session.username = fixture.username; + req.session.password = fixture.password; + req.session.teamName = fixture.teamName; + req.session.isAuthenticated = false; + req.body.username = fixture.username; + req.body.password = fixture.password; + req.body.teamName = fixture.teamName; + + if (fixture.name === 'dummy' && fixture.password === 'password' && fixture.teamName === 'sevensource') { + it('should return true when ' + fixture.description, function (done) { + auth.isAuthenticated(req, res, callback); + expect(req.session.isAuthenticated).to.be.true(); + done(); + }); + } else { + it('should be false when ' + fixture.description, function (done) { + auth.isAuthenticated(req, res, callback); + expect(req.session.isAuthenticated).to.be.false(); + done(); + }); + } + }); + + function isServerAuthenticatedStub (username, password, teamName) { + return (username === 'dummy' && password === 'password' && teamName === 'sevensource'); + } + }); });