Move Authentication validator and routes to typescript
parent
c98c07832d
commit
fad23ff3be
|
@ -1,39 +0,0 @@
|
||||||
|
|
||||||
var first_factor = require('./routes/FirstFactor');
|
|
||||||
var second_factor = require('./routes/second_factor');
|
|
||||||
var reset_password = require('./routes/reset_password');
|
|
||||||
var verify = require('./routes/verify');
|
|
||||||
var u2f_register_handler = require('./routes/u2f_register_handler');
|
|
||||||
var totp_register = require('./routes/totp_register');
|
|
||||||
var objectPath = require('object-path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
login: serveLogin,
|
|
||||||
logout: serveLogout,
|
|
||||||
verify: verify,
|
|
||||||
first_factor: first_factor,
|
|
||||||
second_factor: second_factor,
|
|
||||||
reset_password: reset_password,
|
|
||||||
u2f_register: u2f_register_handler,
|
|
||||||
totp_register: totp_register,
|
|
||||||
}
|
|
||||||
|
|
||||||
function serveLogin(req, res) {
|
|
||||||
if(!(objectPath.has(req, 'session.auth_session'))) {
|
|
||||||
req.session.auth_session = {};
|
|
||||||
req.session.auth_session.first_factor = false;
|
|
||||||
req.session.auth_session.second_factor = false;
|
|
||||||
}
|
|
||||||
res.render('login');
|
|
||||||
}
|
|
||||||
|
|
||||||
function serveLogout(req, res) {
|
|
||||||
var redirect_param = req.query.redirect;
|
|
||||||
var redirect_url = redirect_param || '/';
|
|
||||||
req.session.auth_session = {
|
|
||||||
first_factor: false,
|
|
||||||
second_factor: false
|
|
||||||
}
|
|
||||||
res.redirect(redirect_url);
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
import FirstFactor = require("./routes/FirstFactor");
|
||||||
|
import second_factor = require("./routes/second_factor");
|
||||||
|
import reset_password = require("./routes/reset_password");
|
||||||
|
import AuthenticationValidator = require("./routes/AuthenticationValidator");
|
||||||
|
import u2f_register_handler = require("./routes/u2f_register_handler");
|
||||||
|
import totp_register = require("./routes/totp_register");
|
||||||
|
import objectPath = require("object-path");
|
||||||
|
|
||||||
|
import express = require("express");
|
||||||
|
|
||||||
|
export = {
|
||||||
|
login: serveLogin,
|
||||||
|
logout: serveLogout,
|
||||||
|
verify: AuthenticationValidator,
|
||||||
|
first_factor: FirstFactor,
|
||||||
|
second_factor: second_factor,
|
||||||
|
reset_password: reset_password,
|
||||||
|
u2f_register: u2f_register_handler,
|
||||||
|
totp_register: totp_register,
|
||||||
|
};
|
||||||
|
|
||||||
|
function serveLogin(req: express.Request, res: express.Response) {
|
||||||
|
if (!(objectPath.has(req, "session.auth_session"))) {
|
||||||
|
req.session.auth_session = {};
|
||||||
|
req.session.auth_session.first_factor = false;
|
||||||
|
req.session.auth_session.second_factor = false;
|
||||||
|
}
|
||||||
|
res.render("login");
|
||||||
|
}
|
||||||
|
|
||||||
|
function serveLogout(req: express.Request, res: express.Response) {
|
||||||
|
const redirect_param = req.query.redirect;
|
||||||
|
const redirect_url = redirect_param || "/";
|
||||||
|
req.session.auth_session = {
|
||||||
|
first_factor: false,
|
||||||
|
second_factor: false
|
||||||
|
};
|
||||||
|
res.redirect(redirect_url);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
import objectPath = require("object-path");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import express = require("express");
|
||||||
|
|
||||||
|
function verify_filter(req: express.Request, res: express.Response) {
|
||||||
|
const logger = req.app.get("logger");
|
||||||
|
|
||||||
|
if (!objectPath.has(req, "session.auth_session"))
|
||||||
|
return BluebirdPromise.reject("No auth_session variable");
|
||||||
|
|
||||||
|
if (!objectPath.has(req, "session.auth_session.first_factor"))
|
||||||
|
return BluebirdPromise.reject("No first factor variable");
|
||||||
|
|
||||||
|
if (!objectPath.has(req, "session.auth_session.second_factor"))
|
||||||
|
return BluebirdPromise.reject("No second factor variable");
|
||||||
|
|
||||||
|
if (!objectPath.has(req, "session.auth_session.userid"))
|
||||||
|
return BluebirdPromise.reject("No userid variable");
|
||||||
|
|
||||||
|
const host = objectPath.get<express.Request, string>(req, "headers.host");
|
||||||
|
const domain = host.split(":")[0];
|
||||||
|
|
||||||
|
if (!req.session.auth_session.first_factor ||
|
||||||
|
!req.session.auth_session.second_factor)
|
||||||
|
return BluebirdPromise.reject("First or second factor not validated");
|
||||||
|
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
export = function(req: express.Request, res: express.Response) {
|
||||||
|
verify_filter(req, res)
|
||||||
|
.then(function () {
|
||||||
|
res.status(204);
|
||||||
|
res.send();
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
req.app.get("logger").error(err);
|
||||||
|
res.status(401);
|
||||||
|
res.send();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
|
|
||||||
module.exports = verify;
|
|
||||||
|
|
||||||
var objectPath = require('object-path');
|
|
||||||
var BluebirdPromise = require('bluebird');
|
|
||||||
|
|
||||||
function verify_filter(req, res) {
|
|
||||||
var logger = req.app.get('logger');
|
|
||||||
|
|
||||||
if(!objectPath.has(req, 'session.auth_session'))
|
|
||||||
return BluebirdPromise.reject('No auth_session variable');
|
|
||||||
|
|
||||||
if(!objectPath.has(req, 'session.auth_session.first_factor'))
|
|
||||||
return BluebirdPromise.reject('No first factor variable');
|
|
||||||
|
|
||||||
if(!objectPath.has(req, 'session.auth_session.second_factor'))
|
|
||||||
return BluebirdPromise.reject('No second factor variable');
|
|
||||||
|
|
||||||
if(!objectPath.has(req, 'session.auth_session.userid'))
|
|
||||||
return BluebirdPromise.reject('No userid variable');
|
|
||||||
|
|
||||||
var host = objectPath.get(req, 'headers.host');
|
|
||||||
var domain = host.split(':')[0];
|
|
||||||
|
|
||||||
if(!req.session.auth_session.first_factor ||
|
|
||||||
!req.session.auth_session.second_factor)
|
|
||||||
return BluebirdPromise.reject('First or second factor not validated');
|
|
||||||
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
function verify(req, res) {
|
|
||||||
verify_filter(req, res)
|
|
||||||
.then(function() {
|
|
||||||
res.status(204);
|
|
||||||
res.send();
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
req.app.get('logger').error(err);
|
|
||||||
res.status(401);
|
|
||||||
res.send();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +1,51 @@
|
||||||
|
|
||||||
import sinon = require("sinon");
|
import sinon = require("sinon");
|
||||||
|
import express = require("express");
|
||||||
|
|
||||||
export interface RequestMock {
|
export interface RequestMock {
|
||||||
app?: any;
|
app?: any;
|
||||||
body?: any;
|
body?: any;
|
||||||
session?: any;
|
session?: any;
|
||||||
headers?: any;
|
headers?: any;
|
||||||
|
get?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ResponseMock {
|
export interface ResponseMock {
|
||||||
send: sinon.SinonStub | sinon.SinonSpy;
|
send: sinon.SinonStub | sinon.SinonSpy;
|
||||||
|
sendStatus: sinon.SinonStub;
|
||||||
|
sendFile: sinon.SinonStub;
|
||||||
|
sendfile: sinon.SinonStub;
|
||||||
status: sinon.SinonStub;
|
status: sinon.SinonStub;
|
||||||
json: sinon.SinonStub;
|
json: sinon.SinonStub;
|
||||||
|
links: sinon.SinonStub;
|
||||||
|
jsonp: sinon.SinonStub;
|
||||||
|
download: sinon.SinonStub;
|
||||||
|
contentType: sinon.SinonStub;
|
||||||
|
type: sinon.SinonStub;
|
||||||
|
format: sinon.SinonStub;
|
||||||
|
attachment: sinon.SinonStub;
|
||||||
|
set: sinon.SinonStub;
|
||||||
|
header: sinon.SinonStub;
|
||||||
|
headersSent: boolean;
|
||||||
|
get: sinon.SinonStub;
|
||||||
|
clearCookie: sinon.SinonStub;
|
||||||
|
cookie: sinon.SinonStub;
|
||||||
|
location: sinon.SinonStub;
|
||||||
|
redirect: sinon.SinonStub;
|
||||||
|
render: sinon.SinonStub;
|
||||||
|
locals: sinon.SinonStub;
|
||||||
|
charset: string;
|
||||||
|
vary: sinon.SinonStub;
|
||||||
|
app: any;
|
||||||
|
write: sinon.SinonStub;
|
||||||
|
writeContinue: sinon.SinonStub;
|
||||||
|
writeHead: sinon.SinonStub;
|
||||||
|
statusCode: number;
|
||||||
|
statusMessage: string;
|
||||||
|
setHeader: sinon.SinonStub;
|
||||||
|
setTimeout: sinon.SinonStub;
|
||||||
|
sendDate: boolean;
|
||||||
|
getHeader: sinon.SinonStub;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RequestMock(): RequestMock {
|
export function RequestMock(): RequestMock {
|
||||||
|
@ -25,6 +59,38 @@ export function ResponseMock(): ResponseMock {
|
||||||
return {
|
return {
|
||||||
send: sinon.stub(),
|
send: sinon.stub(),
|
||||||
status: sinon.stub(),
|
status: sinon.stub(),
|
||||||
json: sinon.stub()
|
json: sinon.stub(),
|
||||||
|
sendStatus: sinon.stub(),
|
||||||
|
links: sinon.stub(),
|
||||||
|
jsonp: sinon.stub(),
|
||||||
|
sendFile: sinon.stub(),
|
||||||
|
sendfile: sinon.stub(),
|
||||||
|
download: sinon.stub(),
|
||||||
|
contentType: sinon.stub(),
|
||||||
|
type: sinon.stub(),
|
||||||
|
format: sinon.stub(),
|
||||||
|
attachment: sinon.stub(),
|
||||||
|
set: sinon.stub(),
|
||||||
|
header: sinon.stub(),
|
||||||
|
headersSent: true,
|
||||||
|
get: sinon.stub(),
|
||||||
|
clearCookie: sinon.stub(),
|
||||||
|
cookie: sinon.stub(),
|
||||||
|
location: sinon.stub(),
|
||||||
|
redirect: sinon.stub(),
|
||||||
|
render: sinon.stub(),
|
||||||
|
locals: sinon.stub(),
|
||||||
|
charset: "utf-8",
|
||||||
|
vary: sinon.stub(),
|
||||||
|
app: sinon.stub(),
|
||||||
|
write: sinon.stub(),
|
||||||
|
writeContinue: sinon.stub(),
|
||||||
|
writeHead: sinon.stub(),
|
||||||
|
statusCode: 200,
|
||||||
|
statusMessage: "message",
|
||||||
|
setHeader: sinon.stub(),
|
||||||
|
setTimeout: sinon.stub(),
|
||||||
|
sendDate: true,
|
||||||
|
getHeader: sinon.stub()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
|
||||||
|
import assert = require("assert");
|
||||||
|
import AuthenticationValidator = require("../../../src/lib/routes/AuthenticationValidator");
|
||||||
|
import sinon = require("sinon");
|
||||||
|
import winston = require("winston");
|
||||||
|
|
||||||
|
import express = require("express");
|
||||||
|
|
||||||
|
import ExpressMock = require("../mocks/express");
|
||||||
|
import AccessControllerMock = require("../mocks/AccessController");
|
||||||
|
|
||||||
|
describe("test authentication token verification", function () {
|
||||||
|
let req: ExpressMock.RequestMock;
|
||||||
|
let res: ExpressMock.ResponseMock;
|
||||||
|
let accessController: AccessControllerMock.AccessControllerMock;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
accessController = AccessControllerMock.AccessControllerMock();
|
||||||
|
accessController.isDomainAllowedForUser.returns(true);
|
||||||
|
|
||||||
|
req = ExpressMock.RequestMock();
|
||||||
|
res = ExpressMock.ResponseMock();
|
||||||
|
req.headers = {};
|
||||||
|
req.headers.host = "secret.example.com";
|
||||||
|
req.app.get = sinon.stub();
|
||||||
|
req.app.get.withArgs("config").returns({});
|
||||||
|
req.app.get.withArgs("logger").returns(winston);
|
||||||
|
req.app.get.withArgs("access controller").returns(accessController);
|
||||||
|
});
|
||||||
|
|
||||||
|
interface AuthenticationSession {
|
||||||
|
first_factor?: boolean;
|
||||||
|
second_factor?: boolean;
|
||||||
|
userid?: string;
|
||||||
|
groups?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should be already authenticated", function (done) {
|
||||||
|
req.session = {};
|
||||||
|
req.session.auth_session = {
|
||||||
|
first_factor: true,
|
||||||
|
second_factor: true,
|
||||||
|
userid: "myuser",
|
||||||
|
} as AuthenticationSession;
|
||||||
|
|
||||||
|
res.send = sinon.spy(function () {
|
||||||
|
assert.equal(204, res.status.getCall(0).args[0]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
AuthenticationValidator(req as express.Request, res as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given different cases of session", function () {
|
||||||
|
function test_session(auth_session: AuthenticationSession, status_code: number) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
req.session = {};
|
||||||
|
req.session.auth_session = auth_session;
|
||||||
|
|
||||||
|
res.send = sinon.spy(function () {
|
||||||
|
assert.equal(status_code, res.status.getCall(0).args[0]);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
AuthenticationValidator(req as express.Request, res as any);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_unauthorized(auth_session: AuthenticationSession) {
|
||||||
|
return test_session(auth_session, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_authorized(auth_session: AuthenticationSession) {
|
||||||
|
return test_session(auth_session, 204);
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should not be authenticated when second factor is missing", function () {
|
||||||
|
return test_unauthorized({
|
||||||
|
userid: "user",
|
||||||
|
first_factor: true,
|
||||||
|
second_factor: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not be authenticated when first factor is missing", function () {
|
||||||
|
return test_unauthorized({ first_factor: false, second_factor: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not be authenticated when userid is missing", function () {
|
||||||
|
return test_unauthorized({
|
||||||
|
first_factor: true,
|
||||||
|
second_factor: true,
|
||||||
|
groups: ["mygroup"],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not be authenticated when first and second factor are missing", function () {
|
||||||
|
return test_unauthorized({ first_factor: false, second_factor: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not be authenticated when session has not be initiated", function () {
|
||||||
|
return test_unauthorized(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not be authenticated when session is partially initialized", function () {
|
||||||
|
return test_unauthorized({ first_factor: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
|
|
||||||
var assert = require('assert');
|
|
||||||
var verify = require('../../../src/lib/routes/verify');
|
|
||||||
var sinon = require('sinon');
|
|
||||||
var winston = require('winston');
|
|
||||||
|
|
||||||
describe('test authentication token verification', function() {
|
|
||||||
var req, res;
|
|
||||||
var config_mock;
|
|
||||||
var acl_matcher;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
acl_matcher = {
|
|
||||||
is_domain_allowed: sinon.stub().returns(true)
|
|
||||||
};
|
|
||||||
var access_control = {
|
|
||||||
matcher: acl_matcher
|
|
||||||
}
|
|
||||||
config_mock = {};
|
|
||||||
req = {};
|
|
||||||
res = {};
|
|
||||||
req.headers = {};
|
|
||||||
req.headers.host = 'secret.example.com';
|
|
||||||
req.app = {};
|
|
||||||
req.app.get = sinon.stub();
|
|
||||||
req.app.get.withArgs('config').returns(config_mock);
|
|
||||||
req.app.get.withArgs('logger').returns(winston);
|
|
||||||
req.app.get.withArgs('access control').returns(access_control);
|
|
||||||
res.status = sinon.spy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be already authenticated', function(done) {
|
|
||||||
req.session = {};
|
|
||||||
req.session.auth_session = {
|
|
||||||
first_factor: true,
|
|
||||||
second_factor: true,
|
|
||||||
userid: 'myuser',
|
|
||||||
allowed_domains: ['*']
|
|
||||||
};
|
|
||||||
|
|
||||||
res.send = sinon.spy(function() {
|
|
||||||
assert.equal(204, res.status.getCall(0).args[0]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
verify(req, res);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('given different cases of session', function() {
|
|
||||||
function test_session(auth_session, status_code) {
|
|
||||||
return new Promise(function(resolve, reject) {
|
|
||||||
req.session = {};
|
|
||||||
req.session.auth_session = auth_session;
|
|
||||||
|
|
||||||
res.send = sinon.spy(function() {
|
|
||||||
assert.equal(status_code, res.status.getCall(0).args[0]);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
verify(req, res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_unauthorized(auth_session) {
|
|
||||||
return test_session(auth_session, 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_authorized(auth_session) {
|
|
||||||
return test_session(auth_session, 204);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should not be authenticated when second factor is missing', function() {
|
|
||||||
return test_unauthorized({ first_factor: true, second_factor: false });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be authenticated when first factor is missing', function() {
|
|
||||||
return test_unauthorized({ first_factor: false, second_factor: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be authenticated when userid is missing', function() {
|
|
||||||
return test_unauthorized({
|
|
||||||
first_factor: true,
|
|
||||||
second_factor: true,
|
|
||||||
group: 'mygroup',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be authenticated when first and second factor are missing', function() {
|
|
||||||
return test_unauthorized({ first_factor: false, second_factor: false });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be authenticated when session has not be initiated', function() {
|
|
||||||
return test_unauthorized(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not be authenticated when session is partially initialized', function() {
|
|
||||||
return test_unauthorized({ first_factor: true });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
Loading…
Reference in New Issue