Move exceptions to typescript
parent
ba80bbd219
commit
bada70cf64
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
import * as Promise from "bluebird";
|
import * as Promise from "bluebird";
|
||||||
|
import exceptions = require("./Exceptions");
|
||||||
const exceptions = require("./exceptions");
|
|
||||||
|
|
||||||
const REGULATION_TRACE_TYPE = "regulation";
|
const REGULATION_TRACE_TYPE = "regulation";
|
||||||
const MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE = 3;
|
const MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE = 3;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ export default class Server {
|
||||||
const five_minutes = 5 * 60;
|
const five_minutes = 5 * 60;
|
||||||
const data_store = new UserDataStore(datastore_options);
|
const data_store = new UserDataStore(datastore_options);
|
||||||
const regulator = new AuthenticationRegulator(data_store, five_minutes);
|
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 ldap = new Ldap(deps, config.ldap);
|
||||||
const accessController = new AccessController(config.access_control, deps.winston);
|
const accessController = new AccessController(config.access_control, deps.winston);
|
||||||
const totpValidator = new TOTPValidator(deps.speakeasy);
|
const totpValidator = new TOTPValidator(deps.speakeasy);
|
||||||
|
|
|
@ -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);
|
|
|
@ -3,7 +3,7 @@ var objectPath = require('object-path');
|
||||||
var randomstring = require('randomstring');
|
var randomstring = require('randomstring');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var exceptions = require('./exceptions');
|
var exceptions = require('./Exceptions');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var ejs = require('ejs');
|
var ejs = require('ejs');
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ module.exports = Ldap;
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
var exceptions = require('./exceptions');
|
var exceptions = require('./Exceptions');
|
||||||
var Dovehash = require('dovehash');
|
var Dovehash = require('dovehash');
|
||||||
|
|
||||||
function Ldap(deps, ldap_config) {
|
function Ldap(deps, ldap_config) {
|
||||||
|
@ -70,7 +70,7 @@ Ldap.prototype._search_in_ldap = function(base, query) {
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
reject(new exceptions.LdapSearchError(err));
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as BluebirdPromise from "bluebird";
|
||||||
import * as util from "util";
|
import * as util from "util";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { INotifier } from "./INotifier";
|
import { INotifier } from "./INotifier";
|
||||||
import { Identity } from "../Identity";
|
import { Identity } from "../../types/Identity";
|
||||||
|
|
||||||
import { FileSystemNotifierConfiguration } from "../Configuration";
|
import { FileSystemNotifierConfiguration } from "../Configuration";
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import * as fs from "fs";
|
||||||
import * as ejs from "ejs";
|
import * as ejs from "ejs";
|
||||||
import nodemailer = require("nodemailer");
|
import nodemailer = require("nodemailer");
|
||||||
|
|
||||||
import { NodemailerDependencies } from "../../types/Dependencies";
|
import { Nodemailer } from "../../types/Dependencies";
|
||||||
import { Identity } from "../Identity";
|
import { Identity } from "../../types/Identity";
|
||||||
import { INotifier } from "../notifiers/INotifier";
|
import { INotifier } from "../notifiers/INotifier";
|
||||||
import { GmailNotifierConfiguration } from "../Configuration";
|
import { GmailNotifierConfiguration } from "../Configuration";
|
||||||
|
|
||||||
|
@ -14,9 +14,9 @@ const email_template = fs.readFileSync(__dirname + "/../../resources/email-templ
|
||||||
export class GMailNotifier extends INotifier {
|
export class GMailNotifier extends INotifier {
|
||||||
private transporter: any;
|
private transporter: any;
|
||||||
|
|
||||||
constructor(options: GmailNotifierConfiguration, deps: NodemailerDependencies) {
|
constructor(options: GmailNotifierConfiguration, nodemailer: Nodemailer) {
|
||||||
super();
|
super();
|
||||||
const transporter = deps.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
service: "gmail",
|
service: "gmail",
|
||||||
auth: {
|
auth: {
|
||||||
user: options.username,
|
user: options.username,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
import * as BluebirdPromise from "bluebird";
|
import * as BluebirdPromise from "bluebird";
|
||||||
import { Identity } from "../Identity";
|
import { Identity } from "../../types/Identity";
|
||||||
|
|
||||||
export abstract class INotifier {
|
export abstract class INotifier {
|
||||||
abstract notify(identity: Identity, subject: string, link: string): BluebirdPromise<void>;
|
abstract notify(identity: Identity, subject: string, link: string): BluebirdPromise<void>;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
|
|
||||||
import { NotifierConfiguration } from "..//Configuration";
|
import { NotifierConfiguration } from "..//Configuration";
|
||||||
import { NotifierDependencies } from "../../types/Dependencies";
|
import { Nodemailer } from "../../types/Dependencies";
|
||||||
import { INotifier } from "./INotifier";
|
import { INotifier } from "./INotifier";
|
||||||
|
|
||||||
import { GMailNotifier } from "./GMailNotifier";
|
import { GMailNotifier } from "./GMailNotifier";
|
||||||
import { FileSystemNotifier } from "./FileSystemNotifier";
|
import { FileSystemNotifier } from "./FileSystemNotifier";
|
||||||
|
|
||||||
export class NotifierFactory {
|
export class NotifierFactory {
|
||||||
static build(options: NotifierConfiguration, deps: NotifierDependencies): INotifier {
|
static build(options: NotifierConfiguration, nodemailer: Nodemailer): INotifier {
|
||||||
if ("gmail" in options) {
|
if ("gmail" in options) {
|
||||||
return new GMailNotifier(options.gmail, deps.nodemailer);
|
return new GMailNotifier(options.gmail, nodemailer);
|
||||||
}
|
}
|
||||||
else if ("filesystem" in options) {
|
else if ("filesystem" in options) {
|
||||||
return new FileSystemNotifier(options.filesystem);
|
return new FileSystemNotifier(options.filesystem);
|
||||||
|
|
|
@ -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 second_factor = require('./routes/second_factor');
|
||||||
var reset_password = require('./routes/reset_password');
|
var reset_password = require('./routes/reset_password');
|
||||||
var verify = require('./routes/verify');
|
var verify = require('./routes/verify');
|
||||||
|
|
|
@ -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");
|
||||||
|
});
|
||||||
|
};
|
|
@ -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<access_control.length; ++i) {
|
|
||||||
var rule = access_control[i];
|
|
||||||
if('allowed_domains' in rule) {
|
|
||||||
if('group' in rule && groups.indexOf(rule['group']) >= 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');
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
var objectPath = require('object-path');
|
var objectPath = require('object-path');
|
||||||
var exceptions = require('../exceptions');
|
var exceptions = require('../Exceptions');
|
||||||
var CHALLENGE = 'reset-password';
|
var CHALLENGE = 'reset-password';
|
||||||
|
|
||||||
var icheck_interface = {
|
var icheck_interface = {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
module.exports = totp_fn;
|
module.exports = totp_fn;
|
||||||
|
|
||||||
var objectPath = require('object-path');
|
var objectPath = require('object-path');
|
||||||
var exceptions = require('../../../src/lib/exceptions');
|
var exceptions = require('../../../src/lib/Exceptions');
|
||||||
|
|
||||||
var UNAUTHORIZED_MESSAGE = 'Unauthorized access';
|
var UNAUTHORIZED_MESSAGE = 'Unauthorized access';
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import * as winston from "winston";
|
import winston = require("winston");
|
||||||
import * as speakeasy from "speakeasy";
|
import speakeasy = require("speakeasy");
|
||||||
import nodemailer = require("nodemailer");
|
import nodemailer = require("nodemailer");
|
||||||
import session = require("express-session");
|
import session = require("express-session");
|
||||||
import nedb = require("nedb");
|
import nedb = require("nedb");
|
||||||
|
import ldapjs = require("ldapjs");
|
||||||
|
|
||||||
export type Nodemailer = typeof nodemailer;
|
export type Nodemailer = typeof nodemailer;
|
||||||
export type Speakeasy = typeof speakeasy;
|
export type Speakeasy = typeof speakeasy;
|
||||||
|
@ -19,9 +20,3 @@ export interface GlobalDependencies {
|
||||||
speakeasy: Speakeasy;
|
speakeasy: Speakeasy;
|
||||||
nedb: Nedb;
|
nedb: Nedb;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodemailerDependencies = Nodemailer;
|
|
||||||
|
|
||||||
export interface NotifierDependencies {
|
|
||||||
nodemailer: Nodemailer;
|
|
||||||
}
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
|
||||||
import AuthenticationRegulator from "../../src/lib/AuthenticationRegulator";
|
import AuthenticationRegulator from "../../src/lib/AuthenticationRegulator";
|
||||||
import UserDataStore from "../../src/lib/UserDataStore";
|
import UserDataStore from "../../src/lib/UserDataStore";
|
||||||
import * as MockDate from "mockdate";
|
import MockDate = require("mockdate");
|
||||||
|
import exceptions = require("../../src/lib/Exceptions");
|
||||||
const exceptions = require("../../src/lib/exceptions");
|
|
||||||
|
|
||||||
describe("test authentication regulator", function() {
|
describe("test authentication regulator", function() {
|
||||||
it("should mark 2 authentication and regulate (resolve)", function() {
|
it("should mark 2 authentication and regulate (resolve)", function() {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
import sinon = require("sinon");
|
||||||
|
|
||||||
|
export = function () {
|
||||||
|
return {
|
||||||
|
isDomainAllowedForUser: sinon.stub()
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
import sinon = require("sinon");
|
||||||
|
|
||||||
|
export = function () {
|
||||||
|
return {
|
||||||
|
mark: sinon.stub(),
|
||||||
|
regulate: sinon.stub()
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
import sinon = require("sinon");
|
||||||
|
|
||||||
|
export = function () {
|
||||||
|
return {
|
||||||
|
bind: sinon.stub(),
|
||||||
|
get_emails: sinon.stub(),
|
||||||
|
get_groups: sinon.stub()
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
import sinon = require("sinon");
|
||||||
|
|
||||||
|
export = {
|
||||||
|
Response: function () {
|
||||||
|
return {
|
||||||
|
send: sinon.stub(),
|
||||||
|
status: sinon.stub()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
|
@ -9,14 +9,10 @@ import { NotifierFactory } from "../../../src/lib/notifiers/NotifierFactory";
|
||||||
import { GMailNotifier } from "../../../src/lib/notifiers/GMailNotifier";
|
import { GMailNotifier } from "../../../src/lib/notifiers/GMailNotifier";
|
||||||
import { FileSystemNotifier } from "../../../src/lib/notifiers/FileSystemNotifier";
|
import { FileSystemNotifier } from "../../../src/lib/notifiers/FileSystemNotifier";
|
||||||
|
|
||||||
import { NotifierDependencies } from "../../../src/types/Dependencies";
|
import nodemailerMock = require("../mocks/nodemailer");
|
||||||
|
|
||||||
|
|
||||||
describe("test notifier", function() {
|
describe("test notifier factory", function() {
|
||||||
const deps: NotifierDependencies = {
|
|
||||||
nodemailer: NodemailerMock
|
|
||||||
};
|
|
||||||
|
|
||||||
it("should build a Gmail Notifier", function() {
|
it("should build a Gmail Notifier", function() {
|
||||||
const options = {
|
const options = {
|
||||||
gmail: {
|
gmail: {
|
||||||
|
@ -24,7 +20,8 @@ describe("test notifier", function() {
|
||||||
password: "password"
|
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() {
|
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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var identity_check = require('../../src/lib/identity_check');
|
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 assert = require('assert');
|
||||||
var winston = require('winston');
|
var winston = require('winston');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('bluebird');
|
||||||
|
|
|
@ -122,7 +122,7 @@ describe('test ldap validation', function() {
|
||||||
var expected_doc = {};
|
var expected_doc = {};
|
||||||
expected_doc.mail = [];
|
expected_doc.mail = [];
|
||||||
expected_doc.mail.push('user@example.com');
|
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')
|
return ldap.get_emails('user')
|
||||||
.catch(function() {
|
.catch(function() {
|
||||||
|
|
Loading…
Reference in New Issue