From bada70cf64bba8146387c8dd9df7685cd1509411 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Sat, 20 May 2017 22:55:37 +0200 Subject: [PATCH] Move exceptions to typescript --- src/lib/AuthenticationRegulator.ts | 3 +- src/lib/Exceptions.ts | 49 +++++++ src/lib/Server.ts | 2 +- src/lib/exceptions.js | 45 ------ src/lib/identity_check.js | 2 +- src/lib/ldap.js | 4 +- src/lib/notifiers/FileSystemNotifier.ts | 2 +- src/lib/notifiers/GMailNotifier.ts | 8 +- src/lib/notifiers/INotifier.ts | 2 +- src/lib/notifiers/NotifierFactory.ts | 6 +- src/lib/routes.js | 2 +- src/lib/routes/FirstFactor.ts | 76 ++++++++++ src/lib/routes/first_factor.js | 95 ------------ src/lib/routes/reset_password.js | 2 +- src/lib/routes/totp.js | 2 +- src/types/Dependencies.ts | 11 +- src/{lib => types}/Identity.ts | 0 test/unitary/AuthenticationRegulator.test.ts | 5 +- test/unitary/mocks/AccessController.ts | 8 ++ test/unitary/mocks/AuthenticationRegulator.ts | 9 ++ test/unitary/mocks/Ldap.ts | 10 ++ test/unitary/mocks/express.ts | 11 ++ .../unitary/notifiers/NotifierFactory.test.ts | 13 +- test/unitary/routes/FirstFactor.test.ts | 136 ++++++++++++++++++ test/unitary/routes/test_first_factor.js | 136 ------------------ test/unitary/test_identity_check.js | 2 +- test/unitary/test_ldap.js | 2 +- 27 files changed, 328 insertions(+), 315 deletions(-) create mode 100644 src/lib/Exceptions.ts delete mode 100644 src/lib/exceptions.js create mode 100644 src/lib/routes/FirstFactor.ts delete mode 100644 src/lib/routes/first_factor.js rename src/{lib => types}/Identity.ts (100%) create mode 100644 test/unitary/mocks/AccessController.ts create mode 100644 test/unitary/mocks/AuthenticationRegulator.ts create mode 100644 test/unitary/mocks/Ldap.ts create mode 100644 test/unitary/mocks/express.ts create mode 100644 test/unitary/routes/FirstFactor.test.ts delete mode 100644 test/unitary/routes/test_first_factor.js diff --git a/src/lib/AuthenticationRegulator.ts b/src/lib/AuthenticationRegulator.ts index e55d860a6..89b62892a 100644 --- a/src/lib/AuthenticationRegulator.ts +++ b/src/lib/AuthenticationRegulator.ts @@ -1,7 +1,6 @@ import * as Promise from "bluebird"; - -const exceptions = require("./exceptions"); +import exceptions = require("./Exceptions"); const REGULATION_TRACE_TYPE = "regulation"; const MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE = 3; diff --git a/src/lib/Exceptions.ts b/src/lib/Exceptions.ts new file mode 100644 index 000000000..0902e5dd9 --- /dev/null +++ b/src/lib/Exceptions.ts @@ -0,0 +1,49 @@ + + +export class LdapSeachError extends Error { + constructor(message?: string) { + super(message); + this.name = "LdapSeachError"; + Object.setPrototypeOf(this, LdapSeachError.prototype); + } +} + +export class LdapBindError extends Error { + constructor(message?: string) { + super(message); + this.name = "LdapBindError"; + Object.setPrototypeOf(this, LdapBindError.prototype); + } +} + +export class IdentityError extends Error { + constructor(message?: string) { + super(message); + this.name = "IdentityError"; + Object.setPrototypeOf(this, IdentityError.prototype); + } +} + +export class AccessDeniedError extends Error { + constructor(message?: string) { + super(message); + this.name = "AccessDeniedError"; + Object.setPrototypeOf(this, AccessDeniedError.prototype); + } +} + +export class AuthenticationRegulationError extends Error { + constructor(message?: string) { + super(message); + this.name = "AuthenticationRegulationError"; + Object.setPrototypeOf(this, AuthenticationRegulationError.prototype); + } +} + +export class InvalidTOTPError extends Error { + constructor(message?: string) { + super(message); + this.name = "InvalidTOTPError"; + Object.setPrototypeOf(this, InvalidTOTPError.prototype); + } +} diff --git a/src/lib/Server.ts b/src/lib/Server.ts index 1e8e8a957..4e81fa1bb 100644 --- a/src/lib/Server.ts +++ b/src/lib/Server.ts @@ -57,7 +57,7 @@ export default class Server { const five_minutes = 5 * 60; const data_store = new UserDataStore(datastore_options); const regulator = new AuthenticationRegulator(data_store, five_minutes); - const notifier = NotifierFactory.build(config.notifier, deps); + const notifier = NotifierFactory.build(config.notifier, deps.nodemailer); const ldap = new Ldap(deps, config.ldap); const accessController = new AccessController(config.access_control, deps.winston); const totpValidator = new TOTPValidator(deps.speakeasy); diff --git a/src/lib/exceptions.js b/src/lib/exceptions.js deleted file mode 100644 index 411816ee0..000000000 --- a/src/lib/exceptions.js +++ /dev/null @@ -1,45 +0,0 @@ - -module.exports = { - LdapSearchError: LdapSearchError, - LdapBindError: LdapBindError, - IdentityError: IdentityError, - AccessDeniedError: AccessDeniedError, - AuthenticationRegulationError: AuthenticationRegulationError, - InvalidTOTPError: InvalidTOTPError, -} - -function LdapSearchError(message) { - this.name = "LdapSearchError"; - this.message = (message || ""); -} -LdapSearchError.prototype = Object.create(Error.prototype); - -function LdapBindError(message) { - this.name = "LdapBindError"; - this.message = (message || ""); -} -LdapBindError.prototype = Object.create(Error.prototype); - -function IdentityError(message) { - this.name = "IdentityError"; - this.message = (message || ""); -} -IdentityError.prototype = Object.create(Error.prototype); - -function AccessDeniedError(message) { - this.name = "AccessDeniedError"; - this.message = (message || ""); -} -AccessDeniedError.prototype = Object.create(Error.prototype); - -function AuthenticationRegulationError(message) { - this.name = "AuthenticationRegulationError"; - this.message = (message || ""); -} -AuthenticationRegulationError.prototype = Object.create(Error.prototype); - -function InvalidTOTPError(message) { - this.name = "InvalidTOTPError"; - this.message = (message || ""); -} -InvalidTOTPError.prototype = Object.create(Error.prototype); diff --git a/src/lib/identity_check.js b/src/lib/identity_check.js index 0b03509a6..72fd603af 100644 --- a/src/lib/identity_check.js +++ b/src/lib/identity_check.js @@ -3,7 +3,7 @@ var objectPath = require('object-path'); var randomstring = require('randomstring'); var Promise = require('bluebird'); var util = require('util'); -var exceptions = require('./exceptions'); +var exceptions = require('./Exceptions'); var fs = require('fs'); var ejs = require('ejs'); diff --git a/src/lib/ldap.js b/src/lib/ldap.js index 0007dffcd..cc473e987 100644 --- a/src/lib/ldap.js +++ b/src/lib/ldap.js @@ -3,7 +3,7 @@ module.exports = Ldap; var util = require('util'); var Promise = require('bluebird'); -var exceptions = require('./exceptions'); +var exceptions = require('./Exceptions'); var Dovehash = require('dovehash'); function Ldap(deps, ldap_config) { @@ -70,7 +70,7 @@ Ldap.prototype._search_in_ldap = function(base, query) { }); }) .catch(function(err) { - reject(new exceptions.LdapSearchError(err)); + reject(err); }); }); } diff --git a/src/lib/notifiers/FileSystemNotifier.ts b/src/lib/notifiers/FileSystemNotifier.ts index 10b62f8c1..2ab95531d 100644 --- a/src/lib/notifiers/FileSystemNotifier.ts +++ b/src/lib/notifiers/FileSystemNotifier.ts @@ -3,7 +3,7 @@ import * as BluebirdPromise from "bluebird"; import * as util from "util"; import * as fs from "fs"; import { INotifier } from "./INotifier"; -import { Identity } from "../Identity"; +import { Identity } from "../../types/Identity"; import { FileSystemNotifierConfiguration } from "../Configuration"; diff --git a/src/lib/notifiers/GMailNotifier.ts b/src/lib/notifiers/GMailNotifier.ts index 949ecf0c0..1cd11a3ca 100644 --- a/src/lib/notifiers/GMailNotifier.ts +++ b/src/lib/notifiers/GMailNotifier.ts @@ -4,8 +4,8 @@ import * as fs from "fs"; import * as ejs from "ejs"; import nodemailer = require("nodemailer"); -import { NodemailerDependencies } from "../../types/Dependencies"; -import { Identity } from "../Identity"; +import { Nodemailer } from "../../types/Dependencies"; +import { Identity } from "../../types/Identity"; import { INotifier } from "../notifiers/INotifier"; import { GmailNotifierConfiguration } from "../Configuration"; @@ -14,9 +14,9 @@ const email_template = fs.readFileSync(__dirname + "/../../resources/email-templ export class GMailNotifier extends INotifier { private transporter: any; - constructor(options: GmailNotifierConfiguration, deps: NodemailerDependencies) { + constructor(options: GmailNotifierConfiguration, nodemailer: Nodemailer) { super(); - const transporter = deps.createTransport({ + const transporter = nodemailer.createTransport({ service: "gmail", auth: { user: options.username, diff --git a/src/lib/notifiers/INotifier.ts b/src/lib/notifiers/INotifier.ts index 84e94b3d5..e413047af 100644 --- a/src/lib/notifiers/INotifier.ts +++ b/src/lib/notifiers/INotifier.ts @@ -1,6 +1,6 @@ import * as BluebirdPromise from "bluebird"; -import { Identity } from "../Identity"; +import { Identity } from "../../types/Identity"; export abstract class INotifier { abstract notify(identity: Identity, subject: string, link: string): BluebirdPromise; diff --git a/src/lib/notifiers/NotifierFactory.ts b/src/lib/notifiers/NotifierFactory.ts index 96f631f63..56986fdd8 100644 --- a/src/lib/notifiers/NotifierFactory.ts +++ b/src/lib/notifiers/NotifierFactory.ts @@ -1,15 +1,15 @@ import { NotifierConfiguration } from "..//Configuration"; -import { NotifierDependencies } from "../../types/Dependencies"; +import { Nodemailer } from "../../types/Dependencies"; import { INotifier } from "./INotifier"; import { GMailNotifier } from "./GMailNotifier"; import { FileSystemNotifier } from "./FileSystemNotifier"; export class NotifierFactory { - static build(options: NotifierConfiguration, deps: NotifierDependencies): INotifier { + static build(options: NotifierConfiguration, nodemailer: Nodemailer): INotifier { if ("gmail" in options) { - return new GMailNotifier(options.gmail, deps.nodemailer); + return new GMailNotifier(options.gmail, nodemailer); } else if ("filesystem" in options) { return new FileSystemNotifier(options.filesystem); diff --git a/src/lib/routes.js b/src/lib/routes.js index 31655d4ce..b2c9f3b28 100644 --- a/src/lib/routes.js +++ b/src/lib/routes.js @@ -1,5 +1,5 @@ -var first_factor = require('./routes/first_factor'); +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'); diff --git a/src/lib/routes/FirstFactor.ts b/src/lib/routes/FirstFactor.ts new file mode 100644 index 000000000..9b24afe01 --- /dev/null +++ b/src/lib/routes/FirstFactor.ts @@ -0,0 +1,76 @@ + +import exceptions = require("../Exceptions"); +import objectPath = require("object-path"); +import Promise = require("bluebird"); +import express = require("express"); + +export = function(req: express.Request, res: express.Response) { + const username = req.body.username; + const password = req.body.password; + if (!username || !password) { + res.status(401); + res.send(); + return; + } + + const logger = req.app.get("logger"); + const ldap = req.app.get("ldap"); + const config = req.app.get("config"); + const regulator = req.app.get("authentication regulator"); + const accessController = req.app.get("access controller"); + + logger.info("1st factor: Starting authentication of user \"%s\"", username); + logger.debug("1st factor: Start bind operation against LDAP"); + logger.debug("1st factor: username=%s", username); + + regulator.regulate(username) + .then(function() { + return ldap.bind(username, password); + }) + .then(function() { + objectPath.set(req, "session.auth_session.userid", username); + objectPath.set(req, "session.auth_session.first_factor", true); + logger.info("1st factor: LDAP binding successful"); + logger.debug("1st factor: Retrieve email from LDAP"); + return Promise.join(ldap.get_emails(username), ldap.get_groups(username)); + }) + .then(function(data: string[2]) { + const emails = data[0]; + const groups = data[1]; + + if (!emails && emails.length <= 0) throw new Error("No email found"); + logger.debug("1st factor: Retrieved email are %s", emails); + objectPath.set(req, "session.auth_session.email", emails[0]); + + const isAllowed = accessController.isDomainAllowedForUser(username, groups); + if (!isAllowed) throw new Error("User not allowed to visit this domain"); + + regulator.mark(username, true); + res.status(204); + res.send(); + }) + .catch(exceptions.LdapSeachError, function(err: Error) { + logger.error("1st factor: Unable to retrieve email from LDAP", err); + res.status(500); + res.send(); + }) + .catch(exceptions.LdapBindError, function(err: Error) { + logger.error("1st factor: LDAP binding failed"); + logger.debug("1st factor: LDAP binding failed due to ", err); + regulator.mark(username, false); + res.status(401); + res.send("Bad credentials"); + }) + .catch(exceptions.AuthenticationRegulationError, function(err: Error) { + logger.error("1st factor: the regulator rejected the authentication of user %s", username); + logger.debug("1st factor: authentication rejected due to %s", err); + res.status(403); + res.send("Access has been restricted for a few minutes..."); + }) + .catch(function(err: Error) { + console.log(err.stack); + logger.error("1st factor: Unhandled error %s", err); + res.status(500); + res.send("Internal error"); + }); +}; diff --git a/src/lib/routes/first_factor.js b/src/lib/routes/first_factor.js deleted file mode 100644 index 32af80ff1..000000000 --- a/src/lib/routes/first_factor.js +++ /dev/null @@ -1,95 +0,0 @@ - -module.exports = first_factor; - -var exceptions = require('../exceptions'); -var objectPath = require('object-path'); -var Promise = require('bluebird'); - -function get_allowed_domains(access_control, username, groups) { - var allowed_domains = []; - - for(var i = 0; i= 0) { - var domains = rule.allowed_domains; - allowed_domains = allowed_domains.concat(domains); - } - else if('user' in rule && username == rule['user']) { - var domains = rule.allowed_domains; - allowed_domains = allowed_domains.concat(domains); - } - } - } - return allowed_domains; -} - -function first_factor(req, res) { - var username = req.body.username; - var password = req.body.password; - if(!username || !password) { - res.status(401); - res.send(); - return; - } - - var logger = req.app.get('logger'); - var ldap = req.app.get('ldap'); - var config = req.app.get('config'); - var regulator = req.app.get('authentication regulator'); - var accessController = req.app.get('access controller'); - - logger.info('1st factor: Starting authentication of user "%s"', username); - logger.debug('1st factor: Start bind operation against LDAP'); - logger.debug('1st factor: username=%s', username); - - regulator.regulate(username) - .then(function() { - return ldap.bind(username, password); - }) - .then(function() { - objectPath.set(req, 'session.auth_session.userid', username); - objectPath.set(req, 'session.auth_session.first_factor', true); - logger.info('1st factor: LDAP binding successful'); - logger.debug('1st factor: Retrieve email from LDAP'); - return Promise.join(ldap.get_emails(username), ldap.get_groups(username)); - }) - .then(function(data) { - var emails = data[0]; - var groups = data[1]; - var allowed_domains; - - if(!emails && emails.length <= 0) throw new Error('No email found'); - logger.debug('1st factor: Retrieved email are %s', emails); - objectPath.set(req, 'session.auth_session.email', emails[0]); - - allowed_domains = accessController.isDomainAllowedForUser(username, groups); - - regulator.mark(username, true); - res.status(204); - res.send(); - }) - .catch(exceptions.LdapSearchError, function(err) { - logger.error('1st factor: Unable to retrieve email from LDAP', err); - res.status(500); - res.send(); - }) - .catch(exceptions.LdapBindError, function(err) { - logger.error('1st factor: LDAP binding failed'); - logger.debug('1st factor: LDAP binding failed due to ', err); - regulator.mark(username, false); - res.status(401); - res.send('Bad credentials'); - }) - .catch(exceptions.AuthenticationRegulationError, function(err) { - logger.error('1st factor: the regulator rejected the authentication of user %s', username); - logger.debug('1st factor: authentication rejected due to %s', err); - res.status(403); - res.send('Access has been restricted for a few minutes...'); - }) - .catch(function(err) { - logger.error('1st factor: Unhandled error %s', err); - res.status(500); - res.send('Internal error'); - }); -} diff --git a/src/lib/routes/reset_password.js b/src/lib/routes/reset_password.js index dd26bf5c9..c635993c7 100644 --- a/src/lib/routes/reset_password.js +++ b/src/lib/routes/reset_password.js @@ -1,7 +1,7 @@ var Promise = require('bluebird'); var objectPath = require('object-path'); -var exceptions = require('../exceptions'); +var exceptions = require('../Exceptions'); var CHALLENGE = 'reset-password'; var icheck_interface = { diff --git a/src/lib/routes/totp.js b/src/lib/routes/totp.js index 9fdfb93ae..dc5c57216 100644 --- a/src/lib/routes/totp.js +++ b/src/lib/routes/totp.js @@ -2,7 +2,7 @@ module.exports = totp_fn; var objectPath = require('object-path'); -var exceptions = require('../../../src/lib/exceptions'); +var exceptions = require('../../../src/lib/Exceptions'); var UNAUTHORIZED_MESSAGE = 'Unauthorized access'; diff --git a/src/types/Dependencies.ts b/src/types/Dependencies.ts index 9d7de3668..78c7e1505 100644 --- a/src/types/Dependencies.ts +++ b/src/types/Dependencies.ts @@ -1,8 +1,9 @@ -import * as winston from "winston"; -import * as speakeasy from "speakeasy"; +import winston = require("winston"); +import speakeasy = require("speakeasy"); import nodemailer = require("nodemailer"); import session = require("express-session"); import nedb = require("nedb"); +import ldapjs = require("ldapjs"); export type Nodemailer = typeof nodemailer; export type Speakeasy = typeof speakeasy; @@ -18,10 +19,4 @@ export interface GlobalDependencies { winston: Winston; speakeasy: Speakeasy; nedb: Nedb; -} - -export type NodemailerDependencies = Nodemailer; - -export interface NotifierDependencies { - nodemailer: Nodemailer; } \ No newline at end of file diff --git a/src/lib/Identity.ts b/src/types/Identity.ts similarity index 100% rename from src/lib/Identity.ts rename to src/types/Identity.ts diff --git a/test/unitary/AuthenticationRegulator.test.ts b/test/unitary/AuthenticationRegulator.test.ts index 50a739d26..88707aff9 100644 --- a/test/unitary/AuthenticationRegulator.test.ts +++ b/test/unitary/AuthenticationRegulator.test.ts @@ -1,9 +1,8 @@ import AuthenticationRegulator from "../../src/lib/AuthenticationRegulator"; import UserDataStore from "../../src/lib/UserDataStore"; -import * as MockDate from "mockdate"; - -const exceptions = require("../../src/lib/exceptions"); +import MockDate = require("mockdate"); +import exceptions = require("../../src/lib/Exceptions"); describe("test authentication regulator", function() { it("should mark 2 authentication and regulate (resolve)", function() { diff --git a/test/unitary/mocks/AccessController.ts b/test/unitary/mocks/AccessController.ts new file mode 100644 index 000000000..a0c978534 --- /dev/null +++ b/test/unitary/mocks/AccessController.ts @@ -0,0 +1,8 @@ + +import sinon = require("sinon"); + +export = function () { + return { + isDomainAllowedForUser: sinon.stub() + }; +}; diff --git a/test/unitary/mocks/AuthenticationRegulator.ts b/test/unitary/mocks/AuthenticationRegulator.ts new file mode 100644 index 000000000..d4464c456 --- /dev/null +++ b/test/unitary/mocks/AuthenticationRegulator.ts @@ -0,0 +1,9 @@ + +import sinon = require("sinon"); + +export = function () { + return { + mark: sinon.stub(), + regulate: sinon.stub() + }; +}; diff --git a/test/unitary/mocks/Ldap.ts b/test/unitary/mocks/Ldap.ts new file mode 100644 index 000000000..a44846c96 --- /dev/null +++ b/test/unitary/mocks/Ldap.ts @@ -0,0 +1,10 @@ + +import sinon = require("sinon"); + +export = function () { + return { + bind: sinon.stub(), + get_emails: sinon.stub(), + get_groups: sinon.stub() + }; +}; diff --git a/test/unitary/mocks/express.ts b/test/unitary/mocks/express.ts new file mode 100644 index 000000000..1f6712bca --- /dev/null +++ b/test/unitary/mocks/express.ts @@ -0,0 +1,11 @@ + +import sinon = require("sinon"); + +export = { + Response: function () { + return { + send: sinon.stub(), + status: sinon.stub() + }; + } +}; \ No newline at end of file diff --git a/test/unitary/notifiers/NotifierFactory.test.ts b/test/unitary/notifiers/NotifierFactory.test.ts index b0233d70b..ff6200c0e 100644 --- a/test/unitary/notifiers/NotifierFactory.test.ts +++ b/test/unitary/notifiers/NotifierFactory.test.ts @@ -9,14 +9,10 @@ import { NotifierFactory } from "../../../src/lib/notifiers/NotifierFactory"; import { GMailNotifier } from "../../../src/lib/notifiers/GMailNotifier"; import { FileSystemNotifier } from "../../../src/lib/notifiers/FileSystemNotifier"; -import { NotifierDependencies } from "../../../src/types/Dependencies"; +import nodemailerMock = require("../mocks/nodemailer"); -describe("test notifier", function() { - const deps: NotifierDependencies = { - nodemailer: NodemailerMock - }; - +describe("test notifier factory", function() { it("should build a Gmail Notifier", function() { const options = { gmail: { @@ -24,7 +20,8 @@ describe("test notifier", function() { password: "password" } }; - assert(NotifierFactory.build(options, deps) instanceof GMailNotifier); + nodemailerMock.createTransport.returns(sinon.spy()); + assert(NotifierFactory.build(options, nodemailerMock) instanceof GMailNotifier); }); it("should build a FS Notifier", function() { @@ -34,6 +31,6 @@ describe("test notifier", function() { } }; - assert(NotifierFactory.build(options, deps) instanceof FileSystemNotifier); + assert(NotifierFactory.build(options, nodemailerMock) instanceof FileSystemNotifier); }); }); diff --git a/test/unitary/routes/FirstFactor.test.ts b/test/unitary/routes/FirstFactor.test.ts new file mode 100644 index 000000000..f949a151f --- /dev/null +++ b/test/unitary/routes/FirstFactor.test.ts @@ -0,0 +1,136 @@ + +import sinon = require("sinon"); +import BluebirdPromise = require("bluebird"); +import assert = require("assert"); +import winston = require("winston"); + +import FirstFactor = require("../../../src/lib/routes/FirstFactor"); +import exceptions = require("../../../src/lib/Exceptions"); +import AuthenticationRegulatorMock = require("../mocks/AuthenticationRegulator"); +import AccessControllerMock = require("../mocks/AccessController"); +import LdapMock = require("../mocks/Ldap"); +import ExpressMock = require("../mocks/express"); + +describe("test the first factor validation route", function() { + let req: any; + let res: any; + let emails: string[]; + let groups: string[]; + let configuration; + let ldapMock: any; + let regulator: any; + let accessController: any; + + beforeEach(function() { + configuration = { + ldap: { + base_dn: "ou=users,dc=example,dc=com", + user_name_attribute: "uid" + } + }; + + emails = [ "test_ok@example.com" ]; + groups = [ "group1", "group2" ]; + + ldapMock = LdapMock(); + + accessController = AccessControllerMock(); + accessController.isDomainAllowedForUser.returns(true); + + regulator = AuthenticationRegulatorMock(); + regulator.regulate.returns(BluebirdPromise.resolve()); + regulator.mark.returns(BluebirdPromise.resolve()); + + const app_get = sinon.stub(); + app_get.withArgs("ldap").returns(ldapMock); + app_get.withArgs("configuration").returns(configuration); + app_get.withArgs("logger").returns(winston); + app_get.withArgs("authentication regulator").returns(regulator); + app_get.withArgs("access controller").returns(accessController); + + req = { + app: { + get: app_get + }, + body: { + username: "username", + password: "password" + }, + session: { + auth_session: { + FirstFactor: false, + second_factor: false + } + } + }; + res = ExpressMock.Response(); + }); + + it("should return status code 204 when LDAP binding succeeds", function() { + return new Promise(function(resolve, reject) { + res.send = sinon.spy(function() { + assert.equal("username", req.session.auth_session.userid); + assert.equal(204, res.status.getCall(0).args[0]); + resolve(); + }); + ldapMock.bind.withArgs("username").returns(BluebirdPromise.resolve()); + ldapMock.get_emails.returns(BluebirdPromise.resolve(emails)); + FirstFactor(req, res); + }); + }); + + it("should retrieve email from LDAP", function(done) { + res.send = sinon.spy(function() { done(); }); + ldapMock.bind.returns(BluebirdPromise.resolve()); + ldapMock.get_emails = sinon.stub().withArgs("usernam").returns(BluebirdPromise.resolve([{mail: ["test@example.com"] }])); + FirstFactor(req, res); + }); + + it("should set email as session variables", function() { + return new Promise(function(resolve, reject) { + res.send = sinon.spy(function() { + assert.equal("test_ok@example.com", req.session.auth_session.email); + resolve(); + }); + const emails = [ "test_ok@example.com" ]; + ldapMock.bind.returns(BluebirdPromise.resolve()); + ldapMock.get_emails.returns(BluebirdPromise.resolve(emails)); + FirstFactor(req, res); + }); + }); + + it("should return status code 401 when LDAP binding throws", function(done) { + res.send = sinon.spy(function() { + assert.equal(401, res.status.getCall(0).args[0]); + assert.equal(regulator.mark.getCall(0).args[0], "username"); + done(); + }); + ldapMock.bind.throws(new exceptions.LdapBindError("Bad credentials")); + FirstFactor(req, res); + }); + + it("should return status code 500 when LDAP search throws", function(done) { + res.send = sinon.spy(function() { + assert.equal(500, res.status.getCall(0).args[0]); + done(); + }); + ldapMock.bind.returns(BluebirdPromise.resolve()); + ldapMock.get_emails.throws(new exceptions.LdapSeachError("error while retrieving emails")); + FirstFactor(req, res); + }); + + it("should return status code 403 when regulator rejects authentication", function(done) { + const err = new exceptions.AuthenticationRegulationError("Authentication regulation..."); + regulator.regulate.returns(BluebirdPromise.reject(err)); + + res.send = sinon.spy(function() { + assert.equal(403, res.status.getCall(0).args[0]); + done(); + }); + ldapMock.bind.returns(BluebirdPromise.resolve()); + ldapMock.get_emails.returns(BluebirdPromise.resolve()); + FirstFactor(req, res); + }); +}); + + diff --git a/test/unitary/routes/test_first_factor.js b/test/unitary/routes/test_first_factor.js deleted file mode 100644 index fffb8cabc..000000000 --- a/test/unitary/routes/test_first_factor.js +++ /dev/null @@ -1,136 +0,0 @@ - -var sinon = require('sinon'); -var Promise = require('bluebird'); -var assert = require('assert'); -var winston = require('winston'); -var first_factor = require('../../../src/lib/routes/first_factor'); -var exceptions = require('../../../src/lib/exceptions'); -var Ldap = require('../../../src/lib/ldap'); - -describe('test the first factor validation route', function() { - var req, res; - var ldap_interface_mock; - var emails; - var search_res_ok; - var regulator; - var access_controller; - var config; - - beforeEach(function() { - ldap_interface_mock = sinon.createStubInstance(Ldap); - config = { - ldap: { - base_dn: 'ou=users,dc=example,dc=com', - user_name_attribute: 'uid' - } - } - - emails = [ 'test_ok@example.com' ]; - groups = [ 'group1', 'group2' ]; - - regulator = {}; - regulator.mark = sinon.stub(); - regulator.regulate = sinon.stub(); - - regulator.mark.returns(Promise.resolve()); - regulator.regulate.returns(Promise.resolve()); - - access_controller = { - isDomainAllowedForUser: sinon.stub().returns(true) - }; - - var app_get = sinon.stub(); - app_get.withArgs('ldap').returns(ldap_interface_mock); - app_get.withArgs('config').returns(config); - app_get.withArgs('logger').returns(winston); - app_get.withArgs('authentication regulator').returns(regulator); - app_get.withArgs('access controller').returns(access_controller); - - req = { - app: { - get: app_get - }, - body: { - username: 'username', - password: 'password' - }, - session: { - auth_session: { - first_factor: false, - second_factor: false - } - } - } - res = { - send: sinon.spy(), - status: sinon.spy() - } - }); - - it('should return status code 204 when LDAP binding succeeds', function() { - return new Promise(function(resolve, reject) { - res.send = sinon.spy(function(data) { - assert.equal('username', req.session.auth_session.userid); - assert.equal(204, res.status.getCall(0).args[0]); - resolve(); - }); - ldap_interface_mock.bind.withArgs('username').returns(Promise.resolve()); - ldap_interface_mock.get_emails.returns(Promise.resolve(emails)); - first_factor(req, res); - }); - }); - - it('should retrieve email from LDAP', function(done) { - res.send = sinon.spy(function(data) { done(); }); - ldap_interface_mock.bind.returns(Promise.resolve()); - ldap_interface_mock.get_emails = sinon.stub().withArgs('usernam').returns(Promise.resolve([{mail: ['test@example.com'] }])); - first_factor(req, res); - }); - - it('should set email as session variables', function() { - return new Promise(function(resolve, reject) { - res.send = sinon.spy(function(data) { - assert.equal('test_ok@example.com', req.session.auth_session.email); - resolve(); - }); - var emails = [ 'test_ok@example.com' ]; - ldap_interface_mock.bind.returns(Promise.resolve()); - ldap_interface_mock.get_emails.returns(Promise.resolve(emails)); - first_factor(req, res); - }); - }); - - it('should return status code 401 when LDAP binding throws', function(done) { - res.send = sinon.spy(function(data) { - assert.equal(401, res.status.getCall(0).args[0]); - assert.equal(regulator.mark.getCall(0).args[0], 'username'); - done(); - }); - ldap_interface_mock.bind.throws(new exceptions.LdapBindError('Bad credentials')); - first_factor(req, res); - }); - - it('should return status code 500 when LDAP search throws', function(done) { - res.send = sinon.spy(function(data) { - assert.equal(500, res.status.getCall(0).args[0]); - done(); - }); - ldap_interface_mock.bind.returns(Promise.resolve()); - ldap_interface_mock.get_emails.throws(new exceptions.LdapSearchError('err')); - first_factor(req, res); - }); - - it('should return status code 403 when regulator rejects authentication', function(done) { - var err = new exceptions.AuthenticationRegulationError(); - regulator.regulate.returns(Promise.reject(err)); - res.send = sinon.spy(function(data) { - assert.equal(403, res.status.getCall(0).args[0]); - done(); - }); - ldap_interface_mock.bind.returns(Promise.resolve()); - ldap_interface_mock.get_emails.returns(Promise.resolve()); - first_factor(req, res); - }); -}); - - diff --git a/test/unitary/test_identity_check.js b/test/unitary/test_identity_check.js index 52e890dd1..fde553c1e 100644 --- a/test/unitary/test_identity_check.js +++ b/test/unitary/test_identity_check.js @@ -1,7 +1,7 @@ var sinon = require('sinon'); var identity_check = require('../../src/lib/identity_check'); -var exceptions = require('../../src/lib/exceptions'); +var exceptions = require('../../src/lib/Exceptions'); var assert = require('assert'); var winston = require('winston'); var Promise = require('bluebird'); diff --git a/test/unitary/test_ldap.js b/test/unitary/test_ldap.js index c7fff8f61..080857005 100644 --- a/test/unitary/test_ldap.js +++ b/test/unitary/test_ldap.js @@ -122,7 +122,7 @@ describe('test ldap validation', function() { var expected_doc = {}; expected_doc.mail = []; expected_doc.mail.push('user@example.com'); - ldap_client.search.yields('error'); + ldap_client.search.yields('Error while searching mails'); return ldap.get_emails('user') .catch(function() {