Move files from app to src and tests in root directory + adding more tests
parent
d7d743bdfa
commit
e13315eb92
|
@ -1,3 +1,7 @@
|
|||
|
||||
node_modules/
|
||||
|
||||
coverage/
|
||||
|
||||
*.swp
|
||||
|
||||
|
|
37
app/index.js
37
app/index.js
|
@ -1,37 +0,0 @@
|
|||
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var routes = require('./lib/routes');
|
||||
var ldap = require('ldapjs');
|
||||
var speakeasy = require('speakeasy');
|
||||
|
||||
var totpSecret = process.env.SECRET;
|
||||
var LDAP_URL = process.env.LDAP_URL || 'ldap://127.0.0.1:389';
|
||||
var USERS_DN = process.env.USERS_DN;
|
||||
var PORT = process.env.PORT || 80
|
||||
var JWT_SECRET = 'this is the secret';
|
||||
var EXPIRATION_TIME = process.env.EXPIRATION_TIME || '1h';
|
||||
|
||||
var ldap_client = ldap.createClient({
|
||||
url: LDAP_URL
|
||||
});
|
||||
|
||||
|
||||
var app = express();
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(__dirname + '/public_html'));
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.get ('/login', routes.login);
|
||||
app.post ('/login', routes.login);
|
||||
|
||||
app.get ('/logout', routes.logout);
|
||||
app.get ('/_auth', routes.auth);
|
||||
|
||||
app.listen(PORT, function(err) {
|
||||
console.log('Listening on %d...', PORT);
|
||||
});
|
||||
|
|
@ -2,9 +2,10 @@
|
|||
"name": "ldap-totp-nginx-auth",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "app/index.js",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "./node_modules/.bin/mocha",
|
||||
"coverage": "./node_modules/.bin/istanbul cover _mocha -- -R spec"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -29,6 +30,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^3.2.0",
|
||||
"request": "^2.79.0",
|
||||
"should": "^11.1.1",
|
||||
"sinon": "^1.17.6",
|
||||
"sinon-promise": "^0.1.3"
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
var server = require('./lib/server');
|
||||
|
||||
var ldap = require('ldapjs');
|
||||
|
||||
var config = {
|
||||
port: process.env.PORT || 8080
|
||||
totp_secret: process.env.TOTP_SECRET,
|
||||
ldap_url: process.env.LDAP_URL || 'ldap://127.0.0.1:389',
|
||||
ldap_users_dn: process.env.LDAP_USERS_DN,
|
||||
jwt_secret: process.env.JWT_SECRET,
|
||||
jwt_expiration_time: process.env.JWT_EXPIRATION_TIME || '1h'
|
||||
}
|
||||
|
||||
var ldap_client = ldap.createClient({
|
||||
url: config.ldap_url
|
||||
});
|
||||
|
||||
server.run(config, ldap_client);
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
|
||||
module.exports = {
|
||||
'authenticate': authenticate,
|
||||
'verify_authentication': verify_authentication
|
||||
'verify': verify_authentication
|
||||
}
|
||||
|
||||
var objectPath = require('object-path');
|
||||
var Jwt = require('./jwt');
|
||||
var ldap_checker = require('./ldap_checker');
|
||||
var totp_checker = require('./totp_checker');
|
||||
var replies = require('./replies');
|
||||
|
@ -13,38 +12,42 @@ var Q = require('q');
|
|||
var utils = require('./utils');
|
||||
|
||||
|
||||
function authenticate(req, res, args) {
|
||||
function authenticate(req, res) {
|
||||
var defer = Q.defer();
|
||||
var username = req.body.username;
|
||||
var password = req.body.password;
|
||||
var token = req.body.token;
|
||||
console.log('Start authentication');
|
||||
console.log('Start authentication of user %s', username);
|
||||
|
||||
if(!username || !password || !token) {
|
||||
replies.authentication_failed(res);
|
||||
return;
|
||||
}
|
||||
|
||||
var totp_promise = totp_checker.validate(args.totp_interface, token, args.totp_secret);
|
||||
var credentials_promise = ldap_checker.validate(args.ldap_interface, username, password, args.users_dn);
|
||||
var jwt_engine = req.app.get('jwt engine');
|
||||
var ldap_client = req.app.get('ldap client');
|
||||
var totp_engine = req.app.get('totp engine');
|
||||
var config = req.app.get('config');
|
||||
|
||||
var totp_promise = totp_checker.validate(totp_engine, token, config.totp_secret);
|
||||
var credentials_promise = ldap_checker.validate(ldap_client, username, password, config.ldap_users_dn);
|
||||
|
||||
Q.all([totp_promise, credentials_promise])
|
||||
.then(function() {
|
||||
var token = args.jwt.sign({ user: username }, args.jwt_expiration_time);
|
||||
res.cookie('access_token', token);
|
||||
res.redirect('/');
|
||||
var token = jwt_engine.sign({ user: username }, config.jwt_expiration_time);
|
||||
replies.authentication_succeeded(res, username, token);
|
||||
console.log('Authentication succeeded');
|
||||
defer.resolve();
|
||||
})
|
||||
.fail(function(err1, err2) {
|
||||
res.render('login');
|
||||
console.log('Authentication failed', err1, err2);
|
||||
replies.authentication_failed(res);
|
||||
defer.reject();
|
||||
});
|
||||
return defer.promise;
|
||||
}
|
||||
|
||||
function verify_authentication(req, res, args) {
|
||||
function verify_authentication(req, res) {
|
||||
console.log('Verify authentication');
|
||||
|
||||
if(!objectPath.has(req, 'cookies.access_token')) {
|
||||
|
@ -52,6 +55,6 @@ function verify_authentication(req, res, args) {
|
|||
}
|
||||
|
||||
var jsonWebToken = req.cookies['access_token'];
|
||||
return args.jwt.verify(jsonWebToken);
|
||||
return req.app.get('jwt engine').verify(jsonWebToken);
|
||||
}
|
||||
|
|
@ -11,19 +11,17 @@ function authentication_failed(res) {
|
|||
res.send('Authentication failed');
|
||||
}
|
||||
|
||||
function send_success(res, username, msg) {
|
||||
function authentication_succeeded(res, username, token) {
|
||||
console.log('Reply: authentication succeeded');
|
||||
res.status(200);
|
||||
res.set({ 'X-Remote-User': username });
|
||||
res.send(msg);
|
||||
}
|
||||
|
||||
function authentication_succeeded(res, username) {
|
||||
console.log('Reply: authentication succeeded');
|
||||
send_success(res, username, 'Authentication succeeded');
|
||||
res.send(token);
|
||||
}
|
||||
|
||||
function already_authenticated(res, username) {
|
||||
console.log('Reply: already authenticated');
|
||||
send_success(res, username, 'Authentication succeeded');
|
||||
res.status(204);
|
||||
res.set({ 'X-Remote-User': username });
|
||||
res.send();
|
||||
}
|
||||
|
|
@ -9,6 +9,15 @@ var authentication = require('./authentication');
|
|||
var replies = require('./replies');
|
||||
|
||||
function serveAuth(req, res) {
|
||||
if(req.method == 'POST') {
|
||||
serveAuthPost(req, res);
|
||||
}
|
||||
else {
|
||||
serveAuthGet(req, res);
|
||||
}
|
||||
}
|
||||
|
||||
function serveAuthGet(req, res) {
|
||||
authentication.verify(req, res)
|
||||
.then(function(user) {
|
||||
replies.already_authenticated(res, user);
|
||||
|
@ -19,15 +28,13 @@ function serveAuth(req, res) {
|
|||
});
|
||||
}
|
||||
|
||||
function serveLogin(req, res) {
|
||||
console.log('METHOD=%s', req.method);
|
||||
if(req.method == 'POST') {
|
||||
function serveAuthPost(req, res) {
|
||||
authentication.authenticate(req, res);
|
||||
}
|
||||
else {
|
||||
|
||||
function serveLogin(req, res) {
|
||||
res.render('login');
|
||||
}
|
||||
}
|
||||
|
||||
function serveLogout(req, res) {
|
||||
res.clearCookie('access_token');
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
module.exports = {
|
||||
run: run
|
||||
}
|
||||
|
||||
var routes = require('./routes');
|
||||
var Jwt = require('./jwt');
|
||||
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var speakeasy = require('speakeasy');
|
||||
|
||||
function run(config, ldap_client) {
|
||||
var app = express();
|
||||
app.set('views', './src/views');
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(__dirname + '/public_html'));
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.set('jwt engine', new Jwt(config.jwt_secret));
|
||||
app.set('ldap client', ldap_client);
|
||||
app.set('totp engine', speakeasy);
|
||||
app.set('config', config);
|
||||
|
||||
app.get ('/login', routes.login);
|
||||
app.get ('/logout', routes.logout);
|
||||
|
||||
app.get ('/_auth', routes.auth);
|
||||
app.post ('/_auth', routes.auth);
|
||||
|
||||
app.listen(config.port, function(err) {
|
||||
console.log('Listening on %d...', config.port);
|
||||
});
|
||||
}
|
|
@ -5,9 +5,9 @@ module.exports = {
|
|||
|
||||
var Q = require('q');
|
||||
|
||||
function validate(speakeasy, token, totp_secret) {
|
||||
function validate(totp_engine, token, totp_secret) {
|
||||
var defer = Q.defer();
|
||||
var real_token = speakeasy.totp({
|
||||
var real_token = totp_engine.totp({
|
||||
secret: totp_secret,
|
||||
encoding: 'base32'
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
var assert = require('assert');
|
||||
var authentication = require('../lib/authentication');
|
||||
var authentication = require('../src/lib/authentication');
|
||||
var create_res_mock = require('./res_mock');
|
||||
var sinon = require('sinon');
|
||||
var sinonPromise = require('sinon-promise');
|
||||
|
@ -17,6 +17,9 @@ function create_req_mock(token) {
|
|||
},
|
||||
cookies: {
|
||||
'access_token': 'cookie_token'
|
||||
},
|
||||
app: {
|
||||
get: sinon.stub()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +58,14 @@ function create_mocks() {
|
|||
totp_interface: totp_interface_mock
|
||||
}
|
||||
|
||||
req_mock.app.get.withArgs('ldap client').returns(args.ldap_interface);
|
||||
req_mock.app.get.withArgs('jwt engine').returns(args.jwt);
|
||||
req_mock.app.get.withArgs('totp engine').returns(args.totp_interface);
|
||||
req_mock.app.get.withArgs('config').returns({
|
||||
totp_secret: 'totp_secret',
|
||||
ldap_users_dn: 'ou=users,dc=example,dc=com'
|
||||
});
|
||||
|
||||
return {
|
||||
req: req_mock,
|
||||
res: res_mock,
|
||||
|
@ -70,11 +81,11 @@ describe('test jwt', function() {
|
|||
var jwt_token = 'jwt_token';
|
||||
var clock = sinon.useFakeTimers();
|
||||
var mocks = create_mocks();
|
||||
authentication.authenticate(mocks.req, mocks.res, mocks.args)
|
||||
authentication.authenticate(mocks.req, mocks.res)
|
||||
.then(function() {
|
||||
clock.restore();
|
||||
assert(mocks.res.cookie.calledWith('access_token', jwt_token));
|
||||
assert(mocks.res.redirect.calledWith('/'));
|
||||
assert(mocks.res.status.calledWith(200));
|
||||
assert(mocks.res.send.calledWith(jwt_token));
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
@ -83,7 +94,7 @@ describe('test jwt', function() {
|
|||
var clock = sinon.useFakeTimers();
|
||||
var mocks = create_mocks();
|
||||
mocks.totp.returns('wrong token');
|
||||
authentication.authenticate(mocks.req, mocks.res, mocks.args)
|
||||
authentication.authenticate(mocks.req, mocks.res)
|
||||
.fail(function(err) {
|
||||
clock.restore();
|
||||
done();
|
||||
|
@ -96,8 +107,11 @@ describe('test jwt', function() {
|
|||
it('should be already authenticated', function(done) {
|
||||
var mocks = create_mocks();
|
||||
var data = { user: 'username' };
|
||||
mocks.jwt.verify = sinon.promise().resolves(data);
|
||||
authentication.verify_authentication(mocks.req, mocks.res, mocks.args)
|
||||
mocks.req.app.get.withArgs('jwt engine').returns({
|
||||
verify: sinon.promise().resolves(data)
|
||||
});
|
||||
|
||||
authentication.verify(mocks.req, mocks.res)
|
||||
.then(function(actual_data) {
|
||||
assert.equal(actual_data, data);
|
||||
done();
|
||||
|
@ -107,8 +121,10 @@ describe('test jwt', function() {
|
|||
it('should not be already authenticated', function(done) {
|
||||
var mocks = create_mocks();
|
||||
var data = { user: 'username' };
|
||||
mocks.jwt.verify = sinon.promise().rejects('Error with JWT token');
|
||||
return authentication.verify_authentication(mocks.req, mocks.res, mocks.args)
|
||||
mocks.req.app.get.withArgs('jwt engine').returns({
|
||||
verify: sinon.promise().rejects('Error with JWT token')
|
||||
});
|
||||
return authentication.verify(mocks.req, mocks.res, mocks.args)
|
||||
.fail(function() {
|
||||
done();
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
var Jwt = require('../lib/jwt');
|
||||
var Jwt = require('../src/lib/jwt');
|
||||
var sinon = require('sinon');
|
||||
var sinonPromise = require('sinon-promise');
|
||||
sinonPromise(sinon);
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
var ldap_checker = require('../lib/ldap_checker');
|
||||
var ldap_checker = require('../src/lib/ldap_checker');
|
||||
var sinon = require('sinon');
|
||||
var sinonPromise = require('sinon-promise');
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
var replies = require('../lib/replies');
|
||||
var replies = require('../src/lib/replies');
|
||||
var assert = require('assert');
|
||||
var sinon = require('sinon');
|
||||
var sinonPromise = require('sinon-promise');
|
||||
|
@ -36,7 +36,7 @@ describe('test jwt', function() {
|
|||
|
||||
replies.already_authenticated(res_mock, username);
|
||||
|
||||
assert(res_mock.status.calledWith(200));
|
||||
assert(res_mock.status.calledWith(204));
|
||||
assert(res_mock.set.calledWith({'X-Remote-User': username }));
|
||||
});
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
|
||||
var request = require('request');
|
||||
var assert = require('assert');
|
||||
var server = require('../src/lib/server');
|
||||
var Jwt = require('../src/lib/jwt');
|
||||
var speakeasy = require('speakeasy');
|
||||
var sinon = require('sinon');
|
||||
|
||||
describe('test the server', function() {
|
||||
var jwt = new Jwt('jwt_secret');
|
||||
var ldap_client = {
|
||||
bind: sinon.mock()
|
||||
};
|
||||
|
||||
before(function() {
|
||||
var config = {
|
||||
port: 8080,
|
||||
totp_secret: 'totp_secret',
|
||||
ldap_url: 'ldap://127.0.0.1:389',
|
||||
ldap_users_dn: 'ou=users,dc=example,dc=com',
|
||||
jwt_secret: 'jwt_secret',
|
||||
jwt_expiration_time: '1h'
|
||||
};
|
||||
|
||||
// ldap_client.bind.yields(undefined);
|
||||
ldap_client.bind.withArgs('cn=test_ok,ou=users,dc=example,dc=com',
|
||||
'password').yields(undefined);
|
||||
// ldap_client.bind.withArgs('cn=test_nok,ou=users,dc=example,dc=com',
|
||||
// 'password').yields(undefined, 'error');
|
||||
server.run(config, ldap_client);
|
||||
});
|
||||
|
||||
it('should serve the login page', function(done) {
|
||||
request.get('http://localhost:8080/login')
|
||||
.on('response', function(response) {
|
||||
assert.equal(response.statusCode, 200);
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('should return status code 401 when user is not authenticated', function(done) {
|
||||
request.get('http://localhost:8080/_auth')
|
||||
.on('response', function(response) {
|
||||
assert.equal(response.statusCode, 401);
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('should return status code 204 when user is authenticated', function(done) {
|
||||
var j = request.jar();
|
||||
var r = request.defaults({jar: j});
|
||||
var token = jwt.sign({ user: 'test' }, '1h');
|
||||
var cookie = r.cookie('access_token=' + token);
|
||||
j.setCookie(cookie, 'http://localhost:8080/_auth');
|
||||
|
||||
r.get('http://localhost:8080/_auth')
|
||||
.on('response', function(response) {
|
||||
assert.equal(response.statusCode, 204);
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('should return the JWT token when authentication is successful', function(done) {
|
||||
var clock = sinon.useFakeTimers();
|
||||
var real_token = speakeasy.totp({
|
||||
secret: 'totp_secret',
|
||||
encoding: 'base32'
|
||||
});
|
||||
var expectedJwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidGVzdF9vayIsImlhdCI6MCwiZXhwIjozNjAwfQ.ihvaljGjO5h3iSO_h3PkNNSCYeePyB8Hr5lfVZZYyrQ';
|
||||
|
||||
request.post('http://localhost:8080/_auth', {
|
||||
form: {
|
||||
username: 'test_ok',
|
||||
password: 'password',
|
||||
token: real_token
|
||||
}
|
||||
},
|
||||
function (error, response, body) {
|
||||
if (!error && response.statusCode == 200) {
|
||||
assert.equal(body, expectedJwt);
|
||||
clock.restore();
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
var totp_checker = require('../lib/totp_checker');
|
||||
var totp_checker = require('../src/lib/totp_checker');
|
||||
var sinon = require('sinon');
|
||||
var sinonPromise = require('sinon-promise');
|
||||
sinonPromise(sinon);
|
Loading…
Reference in New Issue