Open and close ldap client after each operation to avoid issues with idle connections and ECONNRESET exceptions
parent
2242f0b9ce
commit
fd59044f5e
|
@ -5,7 +5,7 @@ port: 80
|
||||||
# Log level
|
# Log level
|
||||||
#
|
#
|
||||||
# Level of verbosity for logs
|
# Level of verbosity for logs
|
||||||
logs_level: info
|
logs_level: debug
|
||||||
|
|
||||||
# LDAP configuration
|
# LDAP configuration
|
||||||
#
|
#
|
||||||
|
|
|
@ -5,6 +5,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
This is a very important secret!<br/>
|
This is a very important secret!<br/>
|
||||||
Go back to <a href="https://home.test.local/">home page</a>.
|
Go back to <a href="https://home.test.local:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -19,6 +19,7 @@ const yamlContent = YAML.load(configurationFilepath);
|
||||||
|
|
||||||
const deps: GlobalDependencies = {
|
const deps: GlobalDependencies = {
|
||||||
u2f: require("u2f"),
|
u2f: require("u2f"),
|
||||||
|
dovehash: require("dovehash"),
|
||||||
nodemailer: require("nodemailer"),
|
nodemailer: require("nodemailer"),
|
||||||
ldapjs: require("ldapjs"),
|
ldapjs: require("ldapjs"),
|
||||||
session: require("express-session"),
|
session: require("express-session"),
|
||||||
|
|
|
@ -15,6 +15,14 @@ export class LdapBindError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class LdapError extends Error {
|
||||||
|
constructor(message?: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = "LdapError";
|
||||||
|
Object.setPrototypeOf(this, LdapError.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class IdentityError extends Error {
|
export class IdentityError extends Error {
|
||||||
constructor(message?: string) {
|
constructor(message?: string) {
|
||||||
super(message);
|
super(message);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import ConfigurationAdapter from "./ConfigurationAdapter";
|
||||||
import { TOTPValidator } from "./TOTPValidator";
|
import { TOTPValidator } from "./TOTPValidator";
|
||||||
import { TOTPGenerator } from "./TOTPGenerator";
|
import { TOTPGenerator } from "./TOTPGenerator";
|
||||||
import RestApi from "./RestApi";
|
import RestApi from "./RestApi";
|
||||||
import { LdapClient } from "./LdapClient";
|
import { Client } from "./ldap/Client";
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import ServerVariables = require("./ServerVariables");
|
import ServerVariables = require("./ServerVariables");
|
||||||
import SessionConfigurationBuilder from "./SessionConfigurationBuilder";
|
import SessionConfigurationBuilder from "./SessionConfigurationBuilder";
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import { LdapClient } from "./LdapClient";
|
import { Authenticator } from "./ldap/Authenticator";
|
||||||
|
import { PasswordUpdater } from "./ldap/PasswordUpdater";
|
||||||
|
import { EmailsRetriever } from "./ldap/EmailsRetriever";
|
||||||
|
|
||||||
import { TOTPValidator } from "./TOTPValidator";
|
import { TOTPValidator } from "./TOTPValidator";
|
||||||
import { TOTPGenerator } from "./TOTPGenerator";
|
import { TOTPGenerator } from "./TOTPGenerator";
|
||||||
import U2F = require("u2f");
|
import U2F = require("u2f");
|
||||||
|
@ -19,7 +22,9 @@ export const VARIABLES_KEY = "authelia-variables";
|
||||||
|
|
||||||
export interface ServerVariables {
|
export interface ServerVariables {
|
||||||
logger: typeof winston;
|
logger: typeof winston;
|
||||||
ldap: LdapClient;
|
ldapAuthenticator: Authenticator;
|
||||||
|
ldapPasswordUpdater: PasswordUpdater;
|
||||||
|
ldapEmailsRetriever: EmailsRetriever;
|
||||||
totpValidator: TOTPValidator;
|
totpValidator: TOTPValidator;
|
||||||
totpGenerator: TOTPGenerator;
|
totpGenerator: TOTPGenerator;
|
||||||
u2f: typeof U2F;
|
u2f: typeof U2F;
|
||||||
|
@ -41,7 +46,9 @@ export function fill(app: express.Application, config: Configuration.AppConfigur
|
||||||
const userDataStore = new UserDataStore(datastore_options, deps.nedb);
|
const userDataStore = new UserDataStore(datastore_options, deps.nedb);
|
||||||
const regulator = new AuthenticationRegulator(userDataStore, five_minutes);
|
const regulator = new AuthenticationRegulator(userDataStore, five_minutes);
|
||||||
const notifier = NotifierFactory.build(config.notifier, deps.nodemailer);
|
const notifier = NotifierFactory.build(config.notifier, deps.nodemailer);
|
||||||
const ldap = new LdapClient(config.ldap, deps.ldapjs, deps.winston);
|
const ldapAuthenticator = new Authenticator(config.ldap, deps.ldapjs, deps.winston);
|
||||||
|
const ldapPasswordUpdater = new PasswordUpdater(config.ldap, deps.ldapjs, deps.dovehash, deps.winston);
|
||||||
|
const ldapEmailsRetriever = new EmailsRetriever(config.ldap, deps.ldapjs, deps.winston);
|
||||||
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);
|
||||||
const totpGenerator = new TOTPGenerator(deps.speakeasy);
|
const totpGenerator = new TOTPGenerator(deps.speakeasy);
|
||||||
|
@ -49,7 +56,9 @@ export function fill(app: express.Application, config: Configuration.AppConfigur
|
||||||
const variables: ServerVariables = {
|
const variables: ServerVariables = {
|
||||||
accessController: accessController,
|
accessController: accessController,
|
||||||
config: config,
|
config: config,
|
||||||
ldap: ldap,
|
ldapAuthenticator: ldapAuthenticator,
|
||||||
|
ldapPasswordUpdater: ldapPasswordUpdater,
|
||||||
|
ldapEmailsRetriever: ldapEmailsRetriever,
|
||||||
logger: deps.winston,
|
logger: deps.winston,
|
||||||
notifier: notifier,
|
notifier: notifier,
|
||||||
regulator: regulator,
|
regulator: regulator,
|
||||||
|
@ -74,8 +83,16 @@ export function getNotifier(app: express.Application): INotifier {
|
||||||
return (app.get(VARIABLES_KEY) as ServerVariables).notifier;
|
return (app.get(VARIABLES_KEY) as ServerVariables).notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLdapClient(app: express.Application): LdapClient {
|
export function getLdapAuthenticator(app: express.Application): Authenticator {
|
||||||
return (app.get(VARIABLES_KEY) as ServerVariables).ldap;
|
return (app.get(VARIABLES_KEY) as ServerVariables).ldapAuthenticator;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLdapPasswordUpdater(app: express.Application): PasswordUpdater {
|
||||||
|
return (app.get(VARIABLES_KEY) as ServerVariables).ldapPasswordUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLdapEmailsRetriever(app: express.Application): EmailsRetriever {
|
||||||
|
return (app.get(VARIABLES_KEY) as ServerVariables).ldapEmailsRetriever;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConfiguration(app: express.Application): Configuration.AppConfiguration {
|
export function getConfiguration(app: express.Application): Configuration.AppConfiguration {
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import exceptions = require("../Exceptions");
|
||||||
|
import ldapjs = require("ldapjs");
|
||||||
|
import { Client, Attributes } from "./Client";
|
||||||
|
import { buildUserDN } from "./common";
|
||||||
|
|
||||||
|
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||||
|
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
|
||||||
|
|
||||||
|
|
||||||
|
export class Authenticator {
|
||||||
|
private options: LdapConfiguration;
|
||||||
|
private ldapjs: Ldapjs;
|
||||||
|
private logger: Winston;
|
||||||
|
|
||||||
|
constructor(options: LdapConfiguration, ldapjs: Ldapjs, logger: Winston) {
|
||||||
|
this.options = options;
|
||||||
|
this.ldapjs = ldapjs;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createClient(userDN: string, password: string): Client {
|
||||||
|
return new Client(userDN, password, this.options, this.ldapjs, undefined, this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate(username: string, password: string): BluebirdPromise<Attributes> {
|
||||||
|
const self = this;
|
||||||
|
const userDN = buildUserDN(username, this.options);
|
||||||
|
const userClient = this.createClient(userDN, password);
|
||||||
|
const adminClient = this.createClient(this.options.user, this.options.password);
|
||||||
|
let attributes: Attributes;
|
||||||
|
|
||||||
|
return userClient.open()
|
||||||
|
.then(function () {
|
||||||
|
return userClient.close();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return adminClient.open();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return adminClient.searchEmailsAndGroups(username);
|
||||||
|
})
|
||||||
|
.then(function (attr: Attributes) {
|
||||||
|
attributes = attr;
|
||||||
|
return adminClient.close();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return BluebirdPromise.resolve(attributes);
|
||||||
|
})
|
||||||
|
.error(function (err: Error) {
|
||||||
|
return BluebirdPromise.reject(new exceptions.LdapError(err.message));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,79 +1,65 @@
|
||||||
|
|
||||||
import util = require("util");
|
import util = require("util");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import exceptions = require("./Exceptions");
|
import exceptions = require("../Exceptions");
|
||||||
import Dovehash = require("dovehash");
|
|
||||||
import ldapjs = require("ldapjs");
|
import ldapjs = require("ldapjs");
|
||||||
|
import { buildUserDN } from "./common";
|
||||||
|
|
||||||
import { EventEmitter } from "events";
|
import { EventEmitter } from "events";
|
||||||
import { LdapConfiguration } from "./../../types/Configuration";
|
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||||
import { Ldapjs } from "../../types/Dependencies";
|
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
|
||||||
import { Winston } from "../../types/Dependencies";
|
|
||||||
|
|
||||||
interface SearchEntry {
|
interface SearchEntry {
|
||||||
object: any;
|
object: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LdapClient {
|
export interface Attributes {
|
||||||
private options: LdapConfiguration;
|
groups: string[];
|
||||||
|
emails: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Client {
|
||||||
|
private userDN: string;
|
||||||
|
private password: string;
|
||||||
|
private client: ldapjs.ClientAsync;
|
||||||
|
|
||||||
private ldapjs: Ldapjs;
|
private ldapjs: Ldapjs;
|
||||||
private logger: Winston;
|
private logger: Winston;
|
||||||
private adminClient: ldapjs.ClientAsync;
|
private dovehash: Dovehash;
|
||||||
|
private options: LdapConfiguration;
|
||||||
|
|
||||||
constructor(options: LdapConfiguration, ldapjs: Ldapjs, logger: Winston) {
|
constructor(userDN: string, password: string, options: LdapConfiguration, ldapjs: Ldapjs, dovehash: Dovehash, logger: Winston) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.ldapjs = ldapjs;
|
this.ldapjs = ldapjs;
|
||||||
|
this.dovehash = dovehash;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
this.userDN = userDN;
|
||||||
|
this.password = password;
|
||||||
|
|
||||||
this.connect();
|
const ldapClient = ldapjs.createClient({
|
||||||
}
|
|
||||||
|
|
||||||
private createClient(): ldapjs.ClientAsync {
|
|
||||||
const ldapClient = this.ldapjs.createClient({
|
|
||||||
url: this.options.url,
|
url: this.options.url,
|
||||||
reconnect: true
|
reconnect: true
|
||||||
});
|
});
|
||||||
|
|
||||||
ldapClient.on("error", function (err: Error) {
|
const clientLogger = (ldapClient as any).log;
|
||||||
console.error("LDAP Error:", err.message);
|
if (clientLogger) {
|
||||||
|
clientLogger.level("trace");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.client = BluebirdPromise.promisifyAll(ldapClient) as ldapjs.ClientAsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
open(): BluebirdPromise<void> {
|
||||||
|
this.logger.debug("LDAP: Bind user '%s'", this.userDN);
|
||||||
|
return this.client.bindAsync(this.userDN, this.password)
|
||||||
|
.error(function (err: Error) {
|
||||||
|
return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
|
||||||
});
|
});
|
||||||
|
|
||||||
return BluebirdPromise.promisifyAll(ldapClient) as ldapjs.ClientAsync;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(): BluebirdPromise<void> {
|
close(): BluebirdPromise<void> {
|
||||||
const userDN = this.options.user;
|
this.logger.debug("LDAP: Unbind user '%s'", this.userDN);
|
||||||
const password = this.options.password;
|
return this.client.unbindAsync()
|
||||||
|
|
||||||
this.adminClient = this.createClient();
|
|
||||||
return this.adminClient.bindAsync(userDN, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildUserDN(username: string): string {
|
|
||||||
let userNameAttribute = this.options.user_name_attribute;
|
|
||||||
// if not provided, default to cn
|
|
||||||
if (!userNameAttribute) userNameAttribute = "cn";
|
|
||||||
|
|
||||||
const additionalUserDN = this.options.additional_user_dn;
|
|
||||||
const base_dn = this.options.base_dn;
|
|
||||||
|
|
||||||
let userDN = util.format("%s=%s", userNameAttribute, username);
|
|
||||||
if (additionalUserDN) userDN += util.format(",%s", additionalUserDN);
|
|
||||||
userDN += util.format(",%s", base_dn);
|
|
||||||
return userDN;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkPassword(username: string, password: string): BluebirdPromise<void> {
|
|
||||||
const userDN = this.buildUserDN(username);
|
|
||||||
const that = this;
|
|
||||||
const ldapClient = this.createClient();
|
|
||||||
|
|
||||||
this.logger.debug("LDAP: Check password by binding user '%s'", userDN);
|
|
||||||
return ldapClient.bindAsync(userDN, password)
|
|
||||||
.then(function () {
|
|
||||||
that.logger.debug("LDAP: Unbind user '%s'", userDN);
|
|
||||||
return ldapClient.unbindAsync();
|
|
||||||
})
|
|
||||||
.error(function (err: Error) {
|
.error(function (err: Error) {
|
||||||
return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
|
return BluebirdPromise.reject(new exceptions.LdapBindError(err.message));
|
||||||
});
|
});
|
||||||
|
@ -83,7 +69,7 @@ export class LdapClient {
|
||||||
const that = this;
|
const that = this;
|
||||||
|
|
||||||
that.logger.debug("LDAP: Search for '%s' in '%s'", JSON.stringify(query), base);
|
that.logger.debug("LDAP: Search for '%s' in '%s'", JSON.stringify(query), base);
|
||||||
return that.adminClient.searchAsync(base, query)
|
return that.client.searchAsync(base, query)
|
||||||
.then(function (res: EventEmitter) {
|
.then(function (res: EventEmitter) {
|
||||||
const doc: SearchEntry[] = [];
|
const doc: SearchEntry[] = [];
|
||||||
|
|
||||||
|
@ -97,7 +83,7 @@ export class LdapClient {
|
||||||
reject(new exceptions.LdapSearchError(err.message));
|
reject(new exceptions.LdapSearchError(err.message));
|
||||||
});
|
});
|
||||||
res.on("end", function () {
|
res.on("end", function () {
|
||||||
that.logger.debug("LDAP: Result of search is '%s'.", JSON.stringify(doc));
|
that.logger.debug("LDAP: Search ended and results are '%s'.", JSON.stringify(doc));
|
||||||
resolve(doc);
|
resolve(doc);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -107,8 +93,9 @@ export class LdapClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
retrieveGroups(username: string): BluebirdPromise<string[]> {
|
private searchGroups(username: string): BluebirdPromise<string[]> {
|
||||||
const userDN = this.buildUserDN(username);
|
const that = this;
|
||||||
|
const userDN = buildUserDN(username, this.options);
|
||||||
const password = this.options.password;
|
const password = this.options.password;
|
||||||
|
|
||||||
let groupNameAttribute = this.options.group_name_attribute;
|
let groupNameAttribute = this.options.group_name_attribute;
|
||||||
|
@ -127,24 +114,22 @@ export class LdapClient {
|
||||||
filter: "member=" + userDN
|
filter: "member=" + userDN
|
||||||
};
|
};
|
||||||
|
|
||||||
const that = this;
|
|
||||||
this.logger.debug("LDAP: get groups of user %s", username);
|
|
||||||
const groups: string[] = [];
|
const groups: string[] = [];
|
||||||
return that.search(groupDN, query)
|
return that.search(groupDN, query)
|
||||||
.then(function (docs) {
|
.then(function (docs) {
|
||||||
for (let i = 0; i < docs.length; ++i) {
|
for (let i = 0; i < docs.length; ++i) {
|
||||||
groups.push(docs[i].cn);
|
groups.push(docs[i].cn);
|
||||||
}
|
}
|
||||||
that.logger.debug("LDAP: got groups '%s'", groups);
|
that.logger.debug("LDAP: groups of user %s are %s", username, groups);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return BluebirdPromise.resolve(groups);
|
return BluebirdPromise.resolve(groups);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
retrieveEmails(username: string): BluebirdPromise<string[]> {
|
searchEmails(username: string): BluebirdPromise<string[]> {
|
||||||
const that = this;
|
const that = this;
|
||||||
const user_dn = this.buildUserDN(username);
|
const userDN = buildUserDN(username, this.options);
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
scope: "base",
|
scope: "base",
|
||||||
|
@ -152,8 +137,7 @@ export class LdapClient {
|
||||||
attributes: ["mail"]
|
attributes: ["mail"]
|
||||||
};
|
};
|
||||||
|
|
||||||
this.logger.debug("LDAP: get emails of user '%s'", username);
|
return this.search(userDN, query)
|
||||||
return this.search(user_dn, query)
|
|
||||||
.then(function (docs) {
|
.then(function (docs) {
|
||||||
const emails = [];
|
const emails = [];
|
||||||
for (let i = 0; i < docs.length; ++i) {
|
for (let i = 0; i < docs.length; ++i) {
|
||||||
|
@ -163,29 +147,45 @@ export class LdapClient {
|
||||||
emails.concat(docs[i].mail);
|
emails.concat(docs[i].mail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
that.logger.debug("LDAP: got emails '%s'", emails);
|
that.logger.debug("LDAP: emails of user '%s' are %s", username, emails);
|
||||||
return BluebirdPromise.resolve(emails);
|
return BluebirdPromise.resolve(emails);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePassword(username: string, newPassword: string): BluebirdPromise<void> {
|
searchEmailsAndGroups(username: string): BluebirdPromise<Attributes> {
|
||||||
const user_dn = this.buildUserDN(username);
|
const that = this;
|
||||||
|
let retrievedEmails: string[], retrievedGroups: string[];
|
||||||
|
|
||||||
const encoded_password = Dovehash.encode("SSHA", newPassword);
|
return this.searchEmails(username)
|
||||||
|
.then(function (emails: string[]) {
|
||||||
|
retrievedEmails = emails;
|
||||||
|
return that.searchGroups(username);
|
||||||
|
})
|
||||||
|
.then(function (groups: string[]) {
|
||||||
|
retrievedGroups = groups;
|
||||||
|
return BluebirdPromise.resolve({
|
||||||
|
emails: retrievedEmails,
|
||||||
|
groups: retrievedGroups
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyPassword(username: string, newPassword: string): BluebirdPromise<void> {
|
||||||
|
const that = this;
|
||||||
|
const userDN = buildUserDN(username, this.options);
|
||||||
|
|
||||||
|
const encodedPassword = this.dovehash.encode("SSHA", newPassword);
|
||||||
const change = {
|
const change = {
|
||||||
operation: "replace",
|
operation: "replace",
|
||||||
modification: {
|
modification: {
|
||||||
userPassword: encoded_password
|
userPassword: encodedPassword
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const that = this;
|
|
||||||
this.logger.debug("LDAP: update password of user '%s'", username);
|
this.logger.debug("LDAP: update password of user '%s'", username);
|
||||||
|
return this.client.modifyAsync(userDN, change)
|
||||||
that.logger.debug("LDAP: modify password");
|
|
||||||
return that.adminClient.modifyAsync(user_dn, change)
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return that.adminClient.unbindAsync();
|
return that.client.unbindAsync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import exceptions = require("../Exceptions");
|
||||||
|
import ldapjs = require("ldapjs");
|
||||||
|
import { Client } from "./Client";
|
||||||
|
import { buildUserDN } from "./common";
|
||||||
|
|
||||||
|
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||||
|
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
|
||||||
|
|
||||||
|
|
||||||
|
export class EmailsRetriever {
|
||||||
|
private options: LdapConfiguration;
|
||||||
|
private ldapjs: Ldapjs;
|
||||||
|
private logger: Winston;
|
||||||
|
|
||||||
|
constructor(options: LdapConfiguration, ldapjs: Ldapjs, logger: Winston) {
|
||||||
|
this.options = options;
|
||||||
|
this.ldapjs = ldapjs;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createClient(userDN: string, password: string): Client {
|
||||||
|
return new Client(userDN, password, this.options, this.ldapjs, undefined, this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieve(username: string): BluebirdPromise<string[]> {
|
||||||
|
const userDN = buildUserDN(username, this.options);
|
||||||
|
const adminClient = this.createClient(this.options.user, this.options.password);
|
||||||
|
let emails: string[];
|
||||||
|
|
||||||
|
return adminClient.open()
|
||||||
|
.then(function () {
|
||||||
|
return adminClient.searchEmails(username);
|
||||||
|
})
|
||||||
|
.then(function (emails_: string[]) {
|
||||||
|
emails = emails_;
|
||||||
|
return adminClient.close();
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
return BluebirdPromise.resolve(emails);
|
||||||
|
})
|
||||||
|
.error(function (err: Error) {
|
||||||
|
return BluebirdPromise.reject(new exceptions.LdapError("Failed during password update: " + err.message));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import exceptions = require("../Exceptions");
|
||||||
|
import ldapjs = require("ldapjs");
|
||||||
|
import { Client } from "./Client";
|
||||||
|
import { buildUserDN } from "./common";
|
||||||
|
|
||||||
|
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||||
|
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
|
||||||
|
|
||||||
|
|
||||||
|
export class PasswordUpdater {
|
||||||
|
private options: LdapConfiguration;
|
||||||
|
private ldapjs: Ldapjs;
|
||||||
|
private logger: Winston;
|
||||||
|
private dovehash: Dovehash;
|
||||||
|
|
||||||
|
constructor(options: LdapConfiguration, ldapjs: Ldapjs, dovehash: Dovehash, logger: Winston) {
|
||||||
|
this.options = options;
|
||||||
|
this.ldapjs = ldapjs;
|
||||||
|
this.logger = logger;
|
||||||
|
this.dovehash = dovehash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createClient(userDN: string, password: string): Client {
|
||||||
|
return new Client(userDN, password, this.options, this.ldapjs, this.dovehash, this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePassword(username: string, newPassword: string): BluebirdPromise<void> {
|
||||||
|
const userDN = buildUserDN(username, this.options);
|
||||||
|
const adminClient = this.createClient(this.options.user, this.options.password);
|
||||||
|
|
||||||
|
return adminClient.open()
|
||||||
|
.then(function () {
|
||||||
|
return adminClient.modifyPassword(username, newPassword);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return adminClient.close();
|
||||||
|
})
|
||||||
|
.error(function (err: Error) {
|
||||||
|
return BluebirdPromise.reject(new exceptions.LdapError("Failed during password update: " + err.message));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import util = require("util");
|
||||||
|
|
||||||
|
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||||
|
|
||||||
|
|
||||||
|
export function buildUserDN(username: string, options: LdapConfiguration): string {
|
||||||
|
let userNameAttribute = options.user_name_attribute;
|
||||||
|
// if not provided, default to cn
|
||||||
|
if (!userNameAttribute) userNameAttribute = "cn";
|
||||||
|
|
||||||
|
const additionalUserDN = options.additional_user_dn;
|
||||||
|
const base_dn = options.base_dn;
|
||||||
|
|
||||||
|
let userDN = util.format("%s=%s", userNameAttribute, username);
|
||||||
|
if (additionalUserDN) userDN += util.format(",%s", additionalUserDN);
|
||||||
|
userDN += util.format(",%s", base_dn);
|
||||||
|
return userDN;
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ import BluebirdPromise = require("bluebird");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import { AccessController } from "../../access_control/AccessController";
|
import { AccessController } from "../../access_control/AccessController";
|
||||||
import { AuthenticationRegulator } from "../../AuthenticationRegulator";
|
import { AuthenticationRegulator } from "../../AuthenticationRegulator";
|
||||||
import { LdapClient } from "../../LdapClient";
|
import { Client, Attributes } from "../../ldap/Client";
|
||||||
import Endpoint = require("../../../endpoints");
|
import Endpoint = require("../../../endpoints");
|
||||||
import ErrorReplies = require("../../ErrorReplies");
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
import ServerVariables = require("../../ServerVariables");
|
import ServerVariables = require("../../ServerVariables");
|
||||||
|
@ -16,7 +16,7 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
||||||
const password: string = req.body.password;
|
const password: string = req.body.password;
|
||||||
|
|
||||||
const logger = ServerVariables.getLogger(req.app);
|
const logger = ServerVariables.getLogger(req.app);
|
||||||
const ldap = ServerVariables.getLdapClient(req.app);
|
const ldap = ServerVariables.getLdapAuthenticator(req.app);
|
||||||
const config = ServerVariables.getConfiguration(req.app);
|
const config = ServerVariables.getConfiguration(req.app);
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
|
@ -36,18 +36,15 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
||||||
return regulator.regulate(username)
|
return regulator.regulate(username)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
logger.info("1st factor: No regulation applied.");
|
logger.info("1st factor: No regulation applied.");
|
||||||
return ldap.checkPassword(username, password);
|
return ldap.authenticate(username, password);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function (attributes: Attributes) {
|
||||||
logger.info("1st factor: LDAP binding successful");
|
logger.info("1st factor: LDAP binding successful. Retrieved information about user are %s", JSON.stringify(attributes));
|
||||||
authSession.userid = username;
|
authSession.userid = username;
|
||||||
authSession.first_factor = true;
|
authSession.first_factor = true;
|
||||||
logger.debug("1st factor: Retrieve email from LDAP");
|
|
||||||
return BluebirdPromise.join(ldap.retrieveEmails(username), ldap.retrieveGroups(username));
|
const emails: string[] = attributes.emails;
|
||||||
})
|
const groups: string[] = attributes.groups;
|
||||||
.then(function (data: [string[], string[]]) {
|
|
||||||
const emails: string[] = data[0];
|
|
||||||
const groups: string[] = data[1];
|
|
||||||
|
|
||||||
if (!emails || emails.length <= 0) {
|
if (!emails || emails.length <= 0) {
|
||||||
const errMessage = "No emails found. The user should have at least one email address to reset password.";
|
const errMessage = "No emails found. The user should have at least one email address to reset password.";
|
||||||
|
@ -55,12 +52,12 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
||||||
return BluebirdPromise.reject(new Error(errMessage));
|
return BluebirdPromise.reject(new Error(errMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("1st factor: Retrieved email are %s", emails);
|
|
||||||
logger.debug("1st factor: Retrieved groups are %s", groups);
|
|
||||||
authSession.email = emails[0];
|
authSession.email = emails[0];
|
||||||
authSession.groups = groups;
|
authSession.groups = groups;
|
||||||
|
|
||||||
|
logger.debug("1st factor: Mark successful authentication to regulator.");
|
||||||
regulator.mark(username, true);
|
regulator.mark(username, true);
|
||||||
|
|
||||||
logger.debug("1st factor: Redirect to %s", Endpoint.SECOND_FACTOR_GET);
|
logger.debug("1st factor: Redirect to %s", Endpoint.SECOND_FACTOR_GET);
|
||||||
res.redirect(Endpoint.SECOND_FACTOR_GET);
|
res.redirect(Endpoint.SECOND_FACTOR_GET);
|
||||||
return BluebirdPromise.resolve();
|
return BluebirdPromise.resolve();
|
||||||
|
|
|
@ -11,10 +11,10 @@ import Constants = require("./../constants");
|
||||||
|
|
||||||
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
const logger = ServerVariables.getLogger(req.app);
|
const logger = ServerVariables.getLogger(req.app);
|
||||||
const ldap = ServerVariables.getLdapClient(req.app);
|
const ldapPasswordUpdater = ServerVariables.getLdapPasswordUpdater(req.app);
|
||||||
const authSession = AuthenticationSession.get(req);
|
const authSession = AuthenticationSession.get(req);
|
||||||
|
|
||||||
const new_password = objectPath.get<express.Request, string>(req, "body.password");
|
const newPassword = objectPath.get<express.Request, string>(req, "body.password");
|
||||||
|
|
||||||
const userid = authSession.identity_check.userid;
|
const userid = authSession.identity_check.userid;
|
||||||
const challenge = authSession.identity_check.challenge;
|
const challenge = authSession.identity_check.challenge;
|
||||||
|
@ -26,7 +26,7 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
||||||
|
|
||||||
logger.info("POST reset-password: User %s wants to reset his/her password", userid);
|
logger.info("POST reset-password: User %s wants to reset his/her password", userid);
|
||||||
|
|
||||||
return ldap.updatePassword(userid, new_password)
|
return ldapPasswordUpdater.updatePassword(userid, newPassword)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
logger.info("POST reset-password: Password reset for user '%s'", userid);
|
logger.info("POST reset-password: Password reset for user '%s'", userid);
|
||||||
AuthenticationSession.reset(req);
|
AuthenticationSession.reset(req);
|
||||||
|
|
|
@ -25,8 +25,8 @@ export default class PasswordResetHandler implements IdentityValidable {
|
||||||
if (!userid)
|
if (!userid)
|
||||||
return BluebirdPromise.reject(new exceptions.AccessDeniedError("No user id provided"));
|
return BluebirdPromise.reject(new exceptions.AccessDeniedError("No user id provided"));
|
||||||
|
|
||||||
const ldap = ServerVariables.getLdapClient(req.app);
|
const emailsRetriever = ServerVariables.getLdapEmailsRetriever(req.app);
|
||||||
return ldap.retrieveEmails(userid)
|
return emailsRetriever.retrieve(userid)
|
||||||
.then(function (emails: string[]) {
|
.then(function (emails: string[]) {
|
||||||
if (!emails && emails.length <= 0) throw new Error("No email found");
|
if (!emails && emails.length <= 0) throw new Error("No email found");
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@ import nedb = require("nedb");
|
||||||
import ldapjs = require("ldapjs");
|
import ldapjs = require("ldapjs");
|
||||||
import u2f = require("u2f");
|
import u2f = require("u2f");
|
||||||
import RedisSession = require("connect-redis");
|
import RedisSession = require("connect-redis");
|
||||||
|
import dovehash = require("dovehash");
|
||||||
|
|
||||||
|
export type Dovehash = typeof dovehash;
|
||||||
export type Nodemailer = typeof nodemailer;
|
export type Nodemailer = typeof nodemailer;
|
||||||
export type Speakeasy = typeof speakeasy;
|
export type Speakeasy = typeof speakeasy;
|
||||||
export type Winston = typeof winston;
|
export type Winston = typeof winston;
|
||||||
|
@ -18,6 +20,7 @@ export type ConnectRedis = typeof RedisSession;
|
||||||
|
|
||||||
export interface GlobalDependencies {
|
export interface GlobalDependencies {
|
||||||
u2f: U2f;
|
u2f: U2f;
|
||||||
|
dovehash: Dovehash;
|
||||||
nodemailer: Nodemailer;
|
nodemailer: Nodemailer;
|
||||||
ldapjs: Ldapjs;
|
ldapjs: Ldapjs;
|
||||||
session: Session;
|
session: Session;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { EventEmitter } from "events";
|
||||||
|
|
||||||
declare module "ldapjs" {
|
declare module "ldapjs" {
|
||||||
export interface ClientAsync {
|
export interface ClientAsync {
|
||||||
|
on(event: string, callback: (data?: any) => void): void;
|
||||||
bindAsync(username: string, password: string): BluebirdPromise<void>;
|
bindAsync(username: string, password: string): BluebirdPromise<void>;
|
||||||
unbindAsync(): BluebirdPromise<void>;
|
unbindAsync(): BluebirdPromise<void>;
|
||||||
searchAsync(base: string, query: ldapjs.SearchOptions): BluebirdPromise<EventEmitter>;
|
searchAsync(base: string, query: ldapjs.SearchOptions): BluebirdPromise<EventEmitter>;
|
||||||
|
|
|
@ -48,6 +48,8 @@ describe("test data persistence", function () {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ldapClient.bind.withArgs("cn=admin,dc=example,dc=com",
|
||||||
|
"password").yields();
|
||||||
ldapClient.bind.withArgs("cn=test_ok,ou=users,dc=example,dc=com",
|
ldapClient.bind.withArgs("cn=test_ok,ou=users,dc=example,dc=com",
|
||||||
"password").yields();
|
"password").yields();
|
||||||
ldapClient.bind.withArgs("cn=test_nok,ou=users,dc=example,dc=com",
|
ldapClient.bind.withArgs("cn=test_nok,ou=users,dc=example,dc=com",
|
||||||
|
@ -61,7 +63,7 @@ describe("test data persistence", function () {
|
||||||
ldap: {
|
ldap: {
|
||||||
url: "ldap://127.0.0.1:389",
|
url: "ldap://127.0.0.1:389",
|
||||||
base_dn: "ou=users,dc=example,dc=com",
|
base_dn: "ou=users,dc=example,dc=com",
|
||||||
user: "user",
|
user: "cn=admin,dc=example,dc=com",
|
||||||
password: "password"
|
password: "password"
|
||||||
},
|
},
|
||||||
session: {
|
session: {
|
||||||
|
@ -108,7 +110,8 @@ describe("test data persistence", function () {
|
||||||
winston: winston,
|
winston: winston,
|
||||||
ldapjs: ldap,
|
ldapjs: ldap,
|
||||||
speakeasy: speakeasy,
|
speakeasy: speakeasy,
|
||||||
ConnectRedis: sinon.spy()
|
ConnectRedis: sinon.spy(),
|
||||||
|
dovehash: sinon.spy()
|
||||||
};
|
};
|
||||||
|
|
||||||
const j1 = request.jar();
|
const j1 = request.jar();
|
||||||
|
|
|
@ -49,7 +49,7 @@ describe("test identity check process", function () {
|
||||||
req.app = {};
|
req.app = {};
|
||||||
const mocks = ServerVariablesMock.mock(req.app);
|
const mocks = ServerVariablesMock.mock(req.app);
|
||||||
mocks.logger = winston;
|
mocks.logger = winston;
|
||||||
mocks.userDataStore = userDataStore;
|
mocks.userDataStore = userDataStore as any;
|
||||||
mocks.notifier = notifier;
|
mocks.notifier = notifier;
|
||||||
|
|
||||||
app = express();
|
app = express();
|
||||||
|
|
|
@ -1,242 +0,0 @@
|
||||||
|
|
||||||
import LdapClient = require("../../../src/server/lib/LdapClient");
|
|
||||||
import { LdapConfiguration } from "../../../src/types/Configuration";
|
|
||||||
|
|
||||||
import sinon = require("sinon");
|
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import assert = require("assert");
|
|
||||||
import ldapjs = require("ldapjs");
|
|
||||||
import winston = require("winston");
|
|
||||||
import { EventEmitter } from "events";
|
|
||||||
|
|
||||||
import { LdapjsMock, LdapjsClientMock } from "./mocks/ldapjs";
|
|
||||||
|
|
||||||
|
|
||||||
describe("test ldap validation", function () {
|
|
||||||
let ldap: LdapClient.LdapClient;
|
|
||||||
let ldapClient: LdapjsClientMock;
|
|
||||||
let ldapjs: LdapjsMock;
|
|
||||||
let ldapConfig: LdapConfiguration;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
ldapClient = LdapjsClientMock();
|
|
||||||
ldapjs = LdapjsMock();
|
|
||||||
ldapjs.createClient.returns(ldapClient);
|
|
||||||
|
|
||||||
ldapConfig = {
|
|
||||||
url: "http://localhost:324",
|
|
||||||
user: "admin",
|
|
||||||
password: "password",
|
|
||||||
base_dn: "dc=example,dc=com",
|
|
||||||
additional_user_dn: "ou=users"
|
|
||||||
};
|
|
||||||
|
|
||||||
ldap = new LdapClient.LdapClient(ldapConfig, ldapjs, winston);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("test checking password", test_checking_password);
|
|
||||||
describe("test get emails from username", test_get_emails);
|
|
||||||
describe("test get groups from username", test_get_groups);
|
|
||||||
describe("test update password", test_update_password);
|
|
||||||
|
|
||||||
function test_checking_password() {
|
|
||||||
function test_check_password_internal() {
|
|
||||||
const username = "username";
|
|
||||||
const password = "password";
|
|
||||||
return ldap.checkPassword(username, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
it("should bind the user if good credentials provided", function () {
|
|
||||||
ldapClient.bind.yields();
|
|
||||||
ldapClient.unbind.yields();
|
|
||||||
return test_check_password_internal();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should bind the user with correct DN", function () {
|
|
||||||
ldapConfig.user_name_attribute = "uid";
|
|
||||||
const username = "user";
|
|
||||||
const password = "password";
|
|
||||||
ldapClient.bind.withArgs("uid=user,ou=users,dc=example,dc=com").yields();
|
|
||||||
ldapClient.unbind.yields();
|
|
||||||
return ldap.checkPassword(username, password);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should default to cn user search filter if no filter provided", function () {
|
|
||||||
const username = "user";
|
|
||||||
const password = "password";
|
|
||||||
ldapClient.bind.withArgs("cn=user,ou=users,dc=example,dc=com").yields();
|
|
||||||
ldapClient.unbind.yields();
|
|
||||||
return ldap.checkPassword(username, password);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not bind the user if wrong credentials provided", function () {
|
|
||||||
ldapClient.bind.yields("wrong credentials");
|
|
||||||
const promise = test_check_password_internal();
|
|
||||||
return promise.catch(function () {
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_get_emails() {
|
|
||||||
let res_emitter: any;
|
|
||||||
let expected_doc: any;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
expected_doc = {
|
|
||||||
object: {
|
|
||||||
mail: "user@example.com"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
res_emitter = {
|
|
||||||
on: sinon.spy(function (event: string, fn: (doc: any) => void) {
|
|
||||||
if (event != "error") fn(expected_doc);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should retrieve the email of an existing user", function () {
|
|
||||||
ldapClient.search.yields(undefined, res_emitter);
|
|
||||||
|
|
||||||
return ldap.retrieveEmails("user")
|
|
||||||
.then(function (emails) {
|
|
||||||
assert.deepEqual(emails, [expected_doc.object.mail]);
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should retrieve email for user with uid name attribute", function () {
|
|
||||||
ldapConfig.user_name_attribute = "uid";
|
|
||||||
ldapClient.search.withArgs("uid=username,ou=users,dc=example,dc=com").yields(undefined, res_emitter);
|
|
||||||
return ldap.retrieveEmails("username")
|
|
||||||
.then(function (emails) {
|
|
||||||
assert.deepEqual(emails, ["user@example.com"]);
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should fail on error with search method", function () {
|
|
||||||
const expected_doc = {
|
|
||||||
mail: ["user@example.com"]
|
|
||||||
};
|
|
||||||
ldapClient.search.yields("Error while searching mails");
|
|
||||||
|
|
||||||
return ldap.retrieveEmails("user")
|
|
||||||
.catch(function () {
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_get_groups() {
|
|
||||||
let res_emitter: any;
|
|
||||||
let expected_doc1: any, expected_doc2: any;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
expected_doc1 = {
|
|
||||||
object: {
|
|
||||||
cn: "group1"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
expected_doc2 = {
|
|
||||||
object: {
|
|
||||||
cn: "group2"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
res_emitter = {
|
|
||||||
on: sinon.spy(function (event: string, fn: (doc: any) => void) {
|
|
||||||
if (event != "error") fn(expected_doc1);
|
|
||||||
if (event != "error") fn(expected_doc2);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should retrieve the groups of an existing user", function () {
|
|
||||||
ldapClient.search.yields(undefined, res_emitter);
|
|
||||||
return ldap.retrieveGroups("user")
|
|
||||||
.then(function (groups) {
|
|
||||||
assert.deepEqual(groups, ["group1", "group2"]);
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should reduce the scope to additional_group_dn", function (done) {
|
|
||||||
ldapConfig.additional_group_dn = "ou=groups";
|
|
||||||
ldapClient.search.yields(undefined, res_emitter);
|
|
||||||
ldap.retrieveGroups("user")
|
|
||||||
.then(function() {
|
|
||||||
assert.equal(ldapClient.search.getCall(0).args[0], "ou=groups,dc=example,dc=com");
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should use default group_name_attr if not provided", function (done) {
|
|
||||||
ldapClient.search.yields(undefined, res_emitter);
|
|
||||||
ldap.retrieveGroups("user")
|
|
||||||
.then(function() {
|
|
||||||
assert.equal(ldapClient.search.getCall(0).args[0], "dc=example,dc=com");
|
|
||||||
assert.equal(ldapClient.search.getCall(0).args[1].filter, "member=cn=user,ou=users,dc=example,dc=com");
|
|
||||||
assert.deepEqual(ldapClient.search.getCall(0).args[1].attributes, ["cn"]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should fail on error with search method", function () {
|
|
||||||
ldapClient.search.yields("error");
|
|
||||||
return ldap.retrieveGroups("user")
|
|
||||||
.catch(function () {
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_update_password() {
|
|
||||||
it("should update the password successfully", function () {
|
|
||||||
const change = {
|
|
||||||
operation: "replace",
|
|
||||||
modification: {
|
|
||||||
userPassword: "new-password"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const userdn = "cn=user,ou=users,dc=example,dc=com";
|
|
||||||
|
|
||||||
ldapClient.bind.yields();
|
|
||||||
ldapClient.unbind.yields();
|
|
||||||
ldapClient.modify.yields();
|
|
||||||
|
|
||||||
return ldap.updatePassword("user", "new-password")
|
|
||||||
.then(function () {
|
|
||||||
assert.deepEqual(ldapClient.modify.getCall(0).args[0], userdn);
|
|
||||||
assert.deepEqual(ldapClient.modify.getCall(0).args[1].operation, change.operation);
|
|
||||||
|
|
||||||
const userPassword = ldapClient.modify.getCall(0).args[1].modification.userPassword;
|
|
||||||
assert(/{SSHA}/.test(userPassword));
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
})
|
|
||||||
.catch(function(err) { return BluebirdPromise.reject(new Error("It should fail")); });
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should fail when ldap throws an error", function () {
|
|
||||||
ldapClient.bind.yields(undefined);
|
|
||||||
ldapClient.modify.yields("Error");
|
|
||||||
|
|
||||||
return ldap.updatePassword("user", "new-password")
|
|
||||||
.catch(function () {
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should update password of user using particular user name attribute", function () {
|
|
||||||
ldapConfig.user_name_attribute = "uid";
|
|
||||||
|
|
||||||
ldapClient.bind.yields();
|
|
||||||
ldapClient.unbind.yields();
|
|
||||||
ldapClient.modify.withArgs("uid=username,ou=users,dc=example,dc=com").yields();
|
|
||||||
return ldap.updatePassword("username", "newpass");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import nedb = require("nedb");
|
import nedb = require("nedb");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
|
@ -16,17 +16,17 @@ import Server from "../../../src/server/lib/Server";
|
||||||
|
|
||||||
describe("test server configuration", function () {
|
describe("test server configuration", function () {
|
||||||
let deps: GlobalDependencies;
|
let deps: GlobalDependencies;
|
||||||
let sessionMock: sinon.SinonSpy;
|
let sessionMock: Sinon.SinonSpy;
|
||||||
|
|
||||||
before(function () {
|
before(function () {
|
||||||
const transporter = {
|
const transporter = {
|
||||||
sendMail: sinon.stub().yields()
|
sendMail: Sinon.stub().yields()
|
||||||
};
|
};
|
||||||
|
|
||||||
const createTransport = sinon.stub(nodemailer, "createTransport");
|
const createTransport = Sinon.stub(nodemailer, "createTransport");
|
||||||
createTransport.returns(transporter);
|
createTransport.returns(transporter);
|
||||||
|
|
||||||
sessionMock = sinon.spy(session);
|
sessionMock = Sinon.spy(session);
|
||||||
|
|
||||||
deps = {
|
deps = {
|
||||||
nodemailer: nodemailer,
|
nodemailer: nodemailer,
|
||||||
|
@ -35,15 +35,16 @@ describe("test server configuration", function () {
|
||||||
nedb: nedb,
|
nedb: nedb,
|
||||||
winston: winston,
|
winston: winston,
|
||||||
ldapjs: {
|
ldapjs: {
|
||||||
createClient: sinon.spy(function () {
|
createClient: Sinon.spy(function () {
|
||||||
return {
|
return {
|
||||||
on: sinon.spy(),
|
on: Sinon.spy(),
|
||||||
bind: sinon.spy(),
|
bind: Sinon.spy(),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
session: sessionMock as any,
|
session: sessionMock as any,
|
||||||
ConnectRedis: sinon.spy()
|
ConnectRedis: Sinon.spy(),
|
||||||
|
dovehash: Sinon.spy() as any
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { AppConfiguration } from "../../../src/types/Configuration";
|
||||||
import { GlobalDependencies } from "../../../src/types/Dependencies";
|
import { GlobalDependencies } from "../../../src/types/Dependencies";
|
||||||
import ExpressSession = require("express-session");
|
import ExpressSession = require("express-session");
|
||||||
import ConnectRedis = require("connect-redis");
|
import ConnectRedis = require("connect-redis");
|
||||||
import sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
|
||||||
describe("test session configuration builder", function () {
|
describe("test session configuration builder", function () {
|
||||||
|
@ -36,14 +36,15 @@ describe("test session configuration builder", function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
const deps: GlobalDependencies = {
|
const deps: GlobalDependencies = {
|
||||||
ConnectRedis: sinon.spy() as any,
|
ConnectRedis: Sinon.spy() as any,
|
||||||
ldapjs: sinon.spy() as any,
|
ldapjs: Sinon.spy() as any,
|
||||||
nedb: sinon.spy() as any,
|
nedb: Sinon.spy() as any,
|
||||||
nodemailer: sinon.spy() as any,
|
nodemailer: Sinon.spy() as any,
|
||||||
session: sinon.spy() as any,
|
session: Sinon.spy() as any,
|
||||||
speakeasy: sinon.spy() as any,
|
speakeasy: Sinon.spy() as any,
|
||||||
u2f: sinon.spy() as any,
|
u2f: Sinon.spy() as any,
|
||||||
winston: sinon.spy() as any
|
winston: Sinon.spy() as any,
|
||||||
|
dovehash: Sinon.spy() as any
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = SessionConfigurationBuilder.build(configuration, deps);
|
const options = SessionConfigurationBuilder.build(configuration, deps);
|
||||||
|
@ -94,17 +95,18 @@ describe("test session configuration builder", function () {
|
||||||
store_in_memory: true
|
store_in_memory: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const RedisStoreMock = sinon.spy();
|
const RedisStoreMock = Sinon.spy();
|
||||||
|
|
||||||
const deps: GlobalDependencies = {
|
const deps: GlobalDependencies = {
|
||||||
ConnectRedis: sinon.stub().returns(RedisStoreMock) as any,
|
ConnectRedis: Sinon.stub().returns(RedisStoreMock) as any,
|
||||||
ldapjs: sinon.spy() as any,
|
ldapjs: Sinon.spy() as any,
|
||||||
nedb: sinon.spy() as any,
|
nedb: Sinon.spy() as any,
|
||||||
nodemailer: sinon.spy() as any,
|
nodemailer: Sinon.spy() as any,
|
||||||
session: sinon.spy() as any,
|
session: Sinon.spy() as any,
|
||||||
speakeasy: sinon.spy() as any,
|
speakeasy: Sinon.spy() as any,
|
||||||
u2f: sinon.spy() as any,
|
u2f: Sinon.spy() as any,
|
||||||
winston: sinon.spy() as any
|
winston: Sinon.spy() as any,
|
||||||
|
dovehash: Sinon.spy() as any
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = SessionConfigurationBuilder.build(configuration, deps);
|
const options = SessionConfigurationBuilder.build(configuration, deps);
|
||||||
|
@ -118,10 +120,10 @@ describe("test session configuration builder", function () {
|
||||||
maxAge: 3600,
|
maxAge: 3600,
|
||||||
domain: "example.com"
|
domain: "example.com"
|
||||||
},
|
},
|
||||||
store: sinon.match.object as any
|
store: Sinon.match.object as any
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert((deps.ConnectRedis as sinon.SinonStub).calledWith(deps.session));
|
Assert((deps.ConnectRedis as Sinon.SinonStub).calledWith(deps.session));
|
||||||
Assert.equal(options.secret, expectedOptions.secret);
|
Assert.equal(options.secret, expectedOptions.secret);
|
||||||
Assert.equal(options.resave, expectedOptions.resave);
|
Assert.equal(options.resave, expectedOptions.resave);
|
||||||
Assert.equal(options.saveUninitialized, expectedOptions.saveUninitialized);
|
Assert.equal(options.saveUninitialized, expectedOptions.saveUninitialized);
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
|
||||||
|
import { Authenticator } from "../../../../src/server/lib/ldap/Authenticator";
|
||||||
|
import { LdapConfiguration } from "../../../../src/types/Configuration";
|
||||||
|
|
||||||
|
import sinon = require("sinon");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import assert = require("assert");
|
||||||
|
import ldapjs = require("ldapjs");
|
||||||
|
import winston = require("winston");
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
import { LdapjsMock, LdapjsClientMock } from "../mocks/ldapjs";
|
||||||
|
|
||||||
|
|
||||||
|
describe("test ldap authentication", function () {
|
||||||
|
let authenticator: Authenticator;
|
||||||
|
let ldapClient: LdapjsClientMock;
|
||||||
|
let ldapjs: LdapjsMock;
|
||||||
|
let ldapConfig: LdapConfiguration;
|
||||||
|
let adminUserDN: string;
|
||||||
|
let adminPassword: string;
|
||||||
|
|
||||||
|
function retrieveEmailsAndGroups(ldapClient: LdapjsClientMock) {
|
||||||
|
const email0 = {
|
||||||
|
object: {
|
||||||
|
mail: "user@example.com"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const email1 = {
|
||||||
|
object: {
|
||||||
|
mail: "user@example1.com"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const group0 = {
|
||||||
|
object: {
|
||||||
|
group: "group0"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const emailsEmitter = {
|
||||||
|
on: sinon.spy(function (event: string, fn: (doc: any) => void) {
|
||||||
|
if (event != "error") fn(email0);
|
||||||
|
if (event != "error") fn(email1);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupsEmitter = {
|
||||||
|
on: sinon.spy(function (event: string, fn: (doc: any) => void) {
|
||||||
|
if (event != "error") fn(group0);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
ldapClient.search.onCall(0).yields(undefined, emailsEmitter);
|
||||||
|
ldapClient.search.onCall(1).yields(undefined, groupsEmitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
ldapClient = LdapjsClientMock();
|
||||||
|
ldapjs = LdapjsMock();
|
||||||
|
ldapjs.createClient.returns(ldapClient);
|
||||||
|
|
||||||
|
// winston.level = "debug";
|
||||||
|
|
||||||
|
adminUserDN = "cn=admin,dc=example,dc=com";
|
||||||
|
adminPassword = "password";
|
||||||
|
|
||||||
|
ldapConfig = {
|
||||||
|
url: "http://localhost:324",
|
||||||
|
user: adminUserDN,
|
||||||
|
password: adminPassword,
|
||||||
|
base_dn: "dc=example,dc=com",
|
||||||
|
additional_user_dn: "ou=users"
|
||||||
|
};
|
||||||
|
|
||||||
|
authenticator = new Authenticator(ldapConfig, ldapjs, winston);
|
||||||
|
});
|
||||||
|
|
||||||
|
function test_check_password_internal() {
|
||||||
|
const username = "username";
|
||||||
|
const password = "password";
|
||||||
|
return authenticator.authenticate(username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("success", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
retrieveEmailsAndGroups(ldapClient);
|
||||||
|
ldapClient.bind.withArgs(adminUserDN, adminPassword).yields();
|
||||||
|
ldapClient.unbind.yields();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should bind the user if good credentials provided", function () {
|
||||||
|
ldapClient.bind.withArgs("cn=username,ou=users,dc=example,dc=com", "password").yields();
|
||||||
|
return test_check_password_internal();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should bind the user with correct DN", function () {
|
||||||
|
ldapConfig.user_name_attribute = "uid";
|
||||||
|
ldapClient.bind.withArgs("uid=username,ou=users,dc=example,dc=com", "password").yields();
|
||||||
|
return test_check_password_internal();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("failure", function () {
|
||||||
|
it("should not bind the user if wrong credentials provided", function () {
|
||||||
|
ldapClient.bind.yields("wrong credentials");
|
||||||
|
return test_check_password_internal()
|
||||||
|
.catch(function () {
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not bind the user if search of emails or group fails", function () {
|
||||||
|
ldapClient.bind.withArgs("cn=username,ou=users,dc=example,dc=com", "password").yields();
|
||||||
|
ldapClient.bind.withArgs(adminUserDN, adminPassword).yields();
|
||||||
|
ldapClient.unbind.yields();
|
||||||
|
ldapClient.search.yields("wrong credentials");
|
||||||
|
return test_check_password_internal()
|
||||||
|
.catch(function () {
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,94 @@
|
||||||
|
|
||||||
|
import { EmailsRetriever } from "../../../../src/server/lib/ldap/EmailsRetriever";
|
||||||
|
import { LdapConfiguration } from "../../../../src/types/Configuration";
|
||||||
|
|
||||||
|
import sinon = require("sinon");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import assert = require("assert");
|
||||||
|
import ldapjs = require("ldapjs");
|
||||||
|
import winston = require("winston");
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
import { LdapjsMock, LdapjsClientMock } from "../mocks/ldapjs";
|
||||||
|
|
||||||
|
|
||||||
|
describe("test emails retriever", function () {
|
||||||
|
let emailsRetriever: EmailsRetriever;
|
||||||
|
let ldapClient: LdapjsClientMock;
|
||||||
|
let ldapjs: LdapjsMock;
|
||||||
|
let ldapConfig: LdapConfiguration;
|
||||||
|
let adminUserDN: string;
|
||||||
|
let adminPassword: string;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
ldapClient = LdapjsClientMock();
|
||||||
|
ldapjs = LdapjsMock();
|
||||||
|
ldapjs.createClient.returns(ldapClient);
|
||||||
|
|
||||||
|
// winston.level = "debug";
|
||||||
|
|
||||||
|
adminUserDN = "cn=admin,dc=example,dc=com";
|
||||||
|
adminPassword = "password";
|
||||||
|
|
||||||
|
ldapConfig = {
|
||||||
|
url: "http://localhost:324",
|
||||||
|
user: adminUserDN,
|
||||||
|
password: adminPassword,
|
||||||
|
base_dn: "dc=example,dc=com",
|
||||||
|
additional_user_dn: "ou=users"
|
||||||
|
};
|
||||||
|
|
||||||
|
emailsRetriever = new EmailsRetriever(ldapConfig, ldapjs, winston);
|
||||||
|
});
|
||||||
|
|
||||||
|
function retrieveEmails(ldapClient: LdapjsClientMock) {
|
||||||
|
const email0 = {
|
||||||
|
object: {
|
||||||
|
mail: "user@example.com"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const email1 = {
|
||||||
|
object: {
|
||||||
|
mail: "user@example1.com"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const emailsEmitter = {
|
||||||
|
on: sinon.spy(function (event: string, fn: (doc: any) => void) {
|
||||||
|
if (event != "error") fn(email0);
|
||||||
|
if (event != "error") fn(email1);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
ldapClient.search.onCall(0).yields(undefined, emailsEmitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_emails_retrieval() {
|
||||||
|
const username = "username";
|
||||||
|
return emailsRetriever.retrieve(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("success", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
ldapClient.bind.withArgs(adminUserDN, adminPassword).yields();
|
||||||
|
ldapClient.unbind.yields();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should update the password successfully", function () {
|
||||||
|
retrieveEmails(ldapClient);
|
||||||
|
return test_emails_retrieval();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("failure", function () {
|
||||||
|
it("should fail retrieving emails when search operation fails", function () {
|
||||||
|
ldapClient.bind.withArgs(adminUserDN, adminPassword).yields();
|
||||||
|
ldapClient.search.yields("wrong credentials");
|
||||||
|
return test_emails_retrieval()
|
||||||
|
.catch(function () {
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,83 @@
|
||||||
|
|
||||||
|
import { PasswordUpdater } from "../../../../src/server/lib/ldap/PasswordUpdater";
|
||||||
|
import { LdapConfiguration } from "../../../../src/types/Configuration";
|
||||||
|
|
||||||
|
import sinon = require("sinon");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import assert = require("assert");
|
||||||
|
import ldapjs = require("ldapjs");
|
||||||
|
import winston = require("winston");
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
|
import { LdapjsMock, LdapjsClientMock } from "../mocks/ldapjs";
|
||||||
|
|
||||||
|
|
||||||
|
describe("test password update", function () {
|
||||||
|
let passwordUpdater: PasswordUpdater;
|
||||||
|
let ldapClient: LdapjsClientMock;
|
||||||
|
let ldapjs: LdapjsMock;
|
||||||
|
let ldapConfig: LdapConfiguration;
|
||||||
|
let adminUserDN: string;
|
||||||
|
let adminPassword: string;
|
||||||
|
let dovehash: any;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
ldapClient = LdapjsClientMock();
|
||||||
|
ldapjs = LdapjsMock();
|
||||||
|
ldapjs.createClient.returns(ldapClient);
|
||||||
|
|
||||||
|
// winston.level = "debug";
|
||||||
|
|
||||||
|
adminUserDN = "cn=admin,dc=example,dc=com";
|
||||||
|
adminPassword = "password";
|
||||||
|
|
||||||
|
ldapConfig = {
|
||||||
|
url: "http://localhost:324",
|
||||||
|
user: adminUserDN,
|
||||||
|
password: adminPassword,
|
||||||
|
base_dn: "dc=example,dc=com",
|
||||||
|
additional_user_dn: "ou=users"
|
||||||
|
};
|
||||||
|
|
||||||
|
dovehash = {
|
||||||
|
encode: sinon.stub()
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordUpdater = new PasswordUpdater(ldapConfig, ldapjs, dovehash, winston);
|
||||||
|
});
|
||||||
|
|
||||||
|
function test_update_password() {
|
||||||
|
const username = "username";
|
||||||
|
const newpassword = "newpassword";
|
||||||
|
return passwordUpdater.updatePassword(username, newpassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("success", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
ldapClient.bind.withArgs(adminUserDN, adminPassword).yields();
|
||||||
|
ldapClient.unbind.yields();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should update the password successfully", function () {
|
||||||
|
dovehash.encode.returns("{SSHA}AQmxaKfobGY9HSQa6aDYkAWOgPGNhGYn");
|
||||||
|
ldapClient.modify.withArgs("cn=username,ou=users,dc=example,dc=com", {
|
||||||
|
operation: "replace",
|
||||||
|
modification: {
|
||||||
|
userPassword: "{SSHA}AQmxaKfobGY9HSQa6aDYkAWOgPGNhGYn"
|
||||||
|
}
|
||||||
|
}).yields();
|
||||||
|
return test_update_password();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("failure", function () {
|
||||||
|
it("should fail updating password when modify operation fails", function () {
|
||||||
|
ldapClient.bind.withArgs(adminUserDN, adminPassword).yields();
|
||||||
|
ldapClient.modify.yields("wrong credentials");
|
||||||
|
return test_update_password()
|
||||||
|
.catch(function () {
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,20 +0,0 @@
|
||||||
|
|
||||||
import sinon = require("sinon");
|
|
||||||
|
|
||||||
export interface LdapClientMock {
|
|
||||||
checkPassword: sinon.SinonStub;
|
|
||||||
retrieveEmails: sinon.SinonStub;
|
|
||||||
retrieveGroups: sinon.SinonStub;
|
|
||||||
search: sinon.SinonStub;
|
|
||||||
updatePassword: sinon.SinonStub;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function LdapClientMock(): LdapClientMock {
|
|
||||||
return {
|
|
||||||
checkPassword: sinon.stub(),
|
|
||||||
retrieveEmails: sinon.stub(),
|
|
||||||
retrieveGroups: sinon.stub(),
|
|
||||||
search: sinon.stub(),
|
|
||||||
updatePassword: sinon.stub()
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -16,18 +16,20 @@ export interface ServerVariablesMock {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function mock(app: express.Application): ServerVariablesMock {
|
export function mock(app: express.Application): ServerVariables {
|
||||||
const mocks: ServerVariablesMock = {
|
const mocks: ServerVariables = {
|
||||||
accessController: sinon.stub(),
|
accessController: sinon.stub() as any,
|
||||||
config: sinon.stub(),
|
config: sinon.stub() as any,
|
||||||
ldap: sinon.stub(),
|
ldapAuthenticator: sinon.stub() as any,
|
||||||
logger: sinon.stub(),
|
ldapEmailsRetriever: sinon.stub() as any,
|
||||||
notifier: sinon.stub(),
|
ldapPasswordUpdater: sinon.stub() as any,
|
||||||
regulator: sinon.stub(),
|
logger: sinon.stub() as any,
|
||||||
totpGenerator: sinon.stub(),
|
notifier: sinon.stub() as any,
|
||||||
totpValidator: sinon.stub(),
|
regulator: sinon.stub() as any,
|
||||||
u2f: sinon.stub(),
|
totpGenerator: sinon.stub() as any,
|
||||||
userDataStore: sinon.stub()
|
totpValidator: sinon.stub() as any,
|
||||||
|
u2f: sinon.stub() as any,
|
||||||
|
userDataStore: sinon.stub() as any,
|
||||||
};
|
};
|
||||||
app.get = sinon.stub().withArgs(VARIABLES_KEY).returns(mocks);
|
app.get = sinon.stub().withArgs(VARIABLES_KEY).returns(mocks);
|
||||||
return mocks;
|
return mocks;
|
||||||
|
|
|
@ -11,9 +11,9 @@ import Endpoints = require("../../../../../src/server/endpoints");
|
||||||
|
|
||||||
import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator");
|
import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator");
|
||||||
import AccessControllerMock = require("../../mocks/AccessController");
|
import AccessControllerMock = require("../../mocks/AccessController");
|
||||||
import { LdapClientMock } from "../../mocks/LdapClient";
|
|
||||||
import ExpressMock = require("../../mocks/express");
|
import ExpressMock = require("../../mocks/express");
|
||||||
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
||||||
|
import { ServerVariables } from "../../../../../src/server/lib/ServerVariables";
|
||||||
|
|
||||||
describe("test the first factor validation route", function () {
|
describe("test the first factor validation route", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
|
@ -21,9 +21,9 @@ describe("test the first factor validation route", function () {
|
||||||
let emails: string[];
|
let emails: string[];
|
||||||
let groups: string[];
|
let groups: string[];
|
||||||
let configuration;
|
let configuration;
|
||||||
let ldapMock: LdapClientMock;
|
|
||||||
let regulator: AuthenticationRegulatorMock.AuthenticationRegulatorMock;
|
let regulator: AuthenticationRegulatorMock.AuthenticationRegulatorMock;
|
||||||
let accessController: AccessControllerMock.AccessControllerMock;
|
let accessController: AccessControllerMock.AccessControllerMock;
|
||||||
|
let serverVariables: ServerVariables;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
configuration = {
|
configuration = {
|
||||||
|
@ -36,8 +36,6 @@ describe("test the first factor validation route", function () {
|
||||||
emails = ["test_ok@example.com"];
|
emails = ["test_ok@example.com"];
|
||||||
groups = ["group1", "group2" ];
|
groups = ["group1", "group2" ];
|
||||||
|
|
||||||
ldapMock = LdapClientMock();
|
|
||||||
|
|
||||||
accessController = AccessControllerMock.AccessControllerMock();
|
accessController = AccessControllerMock.AccessControllerMock();
|
||||||
accessController.isDomainAllowedForUser.returns(true);
|
accessController.isDomainAllowedForUser.returns(true);
|
||||||
|
|
||||||
|
@ -61,19 +59,24 @@ describe("test the first factor validation route", function () {
|
||||||
|
|
||||||
AuthenticationSession.reset(req as any);
|
AuthenticationSession.reset(req as any);
|
||||||
|
|
||||||
const mocks = ServerVariablesMock.mock(req.app);
|
serverVariables = ServerVariablesMock.mock(req.app);
|
||||||
mocks.ldap = ldapMock;
|
serverVariables.ldapAuthenticator = {
|
||||||
mocks.config = configuration;
|
authenticate: sinon.stub()
|
||||||
mocks.logger = winston;
|
} as any;
|
||||||
mocks.regulator = regulator;
|
serverVariables.config = configuration as any;
|
||||||
mocks.accessController = accessController;
|
serverVariables.logger = winston as any;
|
||||||
|
serverVariables.regulator = regulator as any;
|
||||||
|
serverVariables.accessController = accessController as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should redirect client to second factor page", function () {
|
it("should redirect client to second factor page", function () {
|
||||||
ldapMock.checkPassword.withArgs("username").returns(BluebirdPromise.resolve());
|
(serverVariables.ldapAuthenticator as any).authenticate.withArgs("username", "password")
|
||||||
ldapMock.retrieveEmails.returns(BluebirdPromise.resolve(emails));
|
.returns(BluebirdPromise.resolve({
|
||||||
|
emails: emails,
|
||||||
|
groups: groups
|
||||||
|
}));
|
||||||
const authSession = AuthenticationSession.get(req as any);
|
const authSession = AuthenticationSession.get(req as any);
|
||||||
return FirstFactorPost.default(req as any, res as any)
|
return FirstFactorPost.default(req as any, res as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
@ -83,24 +86,28 @@ describe("test the first factor validation route", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should retrieve email from LDAP", function () {
|
it("should retrieve email from LDAP", function () {
|
||||||
ldapMock.checkPassword.returns(BluebirdPromise.resolve());
|
(serverVariables.ldapAuthenticator as any).authenticate.withArgs("username", "password")
|
||||||
ldapMock.retrieveEmails = sinon.stub().withArgs("username").returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }]));
|
.returns(BluebirdPromise.resolve([{ mail: ["test@example.com"] }]));
|
||||||
return FirstFactorPost.default(req as any, res as any);
|
return FirstFactorPost.default(req as any, res as any);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set first email address as user session variable", function () {
|
it("should set first email address as user session variable", function () {
|
||||||
const emails = ["test_ok@example.com"];
|
const emails = ["test_ok@example.com"];
|
||||||
const authSession = AuthenticationSession.get(req as any);
|
const authSession = AuthenticationSession.get(req as any);
|
||||||
ldapMock.checkPassword.returns(BluebirdPromise.resolve());
|
(serverVariables.ldapAuthenticator as any).authenticate.withArgs("username", "password")
|
||||||
ldapMock.retrieveEmails.returns(BluebirdPromise.resolve(emails));
|
.returns(BluebirdPromise.resolve({
|
||||||
|
emails: emails,
|
||||||
|
groups: groups
|
||||||
|
}));
|
||||||
return FirstFactorPost.default(req as any, res as any)
|
return FirstFactorPost.default(req as any, res as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
assert.equal("test_ok@example.com", authSession.email);
|
assert.equal("test_ok@example.com", authSession.email);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return status code 401 when LDAP binding throws", function () {
|
it("should return status code 401 when LDAP authenticator throws", function () {
|
||||||
ldapMock.checkPassword.returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials")));
|
(serverVariables.ldapAuthenticator as any).authenticate.withArgs("username", "password")
|
||||||
|
.returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials")));
|
||||||
return FirstFactorPost.default(req as any, res as any)
|
return FirstFactorPost.default(req as any, res as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
assert.equal(401, res.status.getCall(0).args[0]);
|
assert.equal(401, res.status.getCall(0).args[0]);
|
||||||
|
@ -108,15 +115,6 @@ describe("test the first factor validation route", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return status code 500 when LDAP search throws", function () {
|
|
||||||
ldapMock.checkPassword.returns(BluebirdPromise.resolve());
|
|
||||||
ldapMock.retrieveEmails.returns(BluebirdPromise.reject(new exceptions.LdapSearchError("error while retrieving emails")));
|
|
||||||
return FirstFactorPost.default(req as any, res as any)
|
|
||||||
.then(function () {
|
|
||||||
assert.equal(500, res.status.getCall(0).args[0]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return status code 403 when regulator rejects authentication", function () {
|
it("should return status code 403 when regulator rejects authentication", function () {
|
||||||
const err = new exceptions.AuthenticationRegulationError("Authentication regulation...");
|
const err = new exceptions.AuthenticationRegulationError("Authentication regulation...");
|
||||||
regulator.regulate.returns(BluebirdPromise.reject(err));
|
regulator.regulate.returns(BluebirdPromise.reject(err));
|
||||||
|
@ -126,17 +124,6 @@ describe("test the first factor validation route", function () {
|
||||||
assert.equal(1, res.send.callCount);
|
assert.equal(1, res.send.callCount);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail when admin user does not have rights to retrieve attribute mail", function () {
|
|
||||||
ldapMock.checkPassword.returns(BluebirdPromise.resolve());
|
|
||||||
ldapMock.retrieveEmails = sinon.stub().withArgs("username").returns(BluebirdPromise.resolve([]));
|
|
||||||
ldapMock.retrieveGroups = sinon.stub().withArgs("username").returns(BluebirdPromise.resolve(["group1"]));
|
|
||||||
return FirstFactorPost.default(req as any, res as any)
|
|
||||||
.then(function () {
|
|
||||||
assert.equal(500, res.status.getCall(0).args[0]);
|
|
||||||
assert.equal(1, res.send.callCount);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
|
|
||||||
import PasswordResetHandler from "../../../../../../src/server/lib/routes/password-reset/identity/PasswordResetHandler";
|
import PasswordResetHandler from "../../../../../../src/server/lib/routes/password-reset/identity/PasswordResetHandler";
|
||||||
import LdapClient = require("../../../../../../src/server/lib/LdapClient");
|
import PasswordUpdater = require("../../../../../../src/server/lib/ldap/PasswordUpdater");
|
||||||
|
import { ServerVariables } from "../../../../../../src/server/lib/ServerVariables";
|
||||||
import sinon = require("sinon");
|
import sinon = require("sinon");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
import ExpressMock = require("../../../mocks/express");
|
import ExpressMock = require("../../../mocks/express");
|
||||||
import { LdapClientMock } from "../../../mocks/LdapClient";
|
|
||||||
import { UserDataStore } from "../../../mocks/UserDataStore";
|
import { UserDataStore } from "../../../mocks/UserDataStore";
|
||||||
import ServerVariablesMock = require("../../../mocks/ServerVariablesMock");
|
import ServerVariablesMock = require("../../../mocks/ServerVariablesMock");
|
||||||
|
|
||||||
describe("test reset password identity check", function () {
|
describe("test reset password identity check", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let user_data_store: UserDataStore;
|
let userDataStore: UserDataStore;
|
||||||
let ldap_client: LdapClientMock;
|
|
||||||
let configuration: any;
|
let configuration: any;
|
||||||
|
let serverVariables: ServerVariables;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = {
|
req = {
|
||||||
|
@ -43,15 +43,15 @@ describe("test reset password identity check", function () {
|
||||||
inMemoryOnly: true
|
inMemoryOnly: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const mocks = ServerVariablesMock.mock(req.app);
|
serverVariables = ServerVariablesMock.mock(req.app);
|
||||||
|
|
||||||
|
|
||||||
user_data_store = UserDataStore();
|
userDataStore = UserDataStore();
|
||||||
user_data_store.set_u2f_meta.returns(BluebirdPromise.resolve({}));
|
userDataStore.set_u2f_meta.returns(BluebirdPromise.resolve({}));
|
||||||
user_data_store.get_u2f_meta.returns(BluebirdPromise.resolve({}));
|
userDataStore.get_u2f_meta.returns(BluebirdPromise.resolve({}));
|
||||||
user_data_store.issue_identity_check_token.returns(BluebirdPromise.resolve({}));
|
userDataStore.issue_identity_check_token.returns(BluebirdPromise.resolve({}));
|
||||||
user_data_store.consume_identity_check_token.returns(BluebirdPromise.resolve({}));
|
userDataStore.consume_identity_check_token.returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore = user_data_store;
|
serverVariables.userDataStore = userDataStore as any;
|
||||||
|
|
||||||
|
|
||||||
configuration = {
|
configuration = {
|
||||||
|
@ -61,11 +61,11 @@ describe("test reset password identity check", function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mocks.logger = winston;
|
serverVariables.logger = winston;
|
||||||
mocks.config = configuration;
|
serverVariables.config = configuration;
|
||||||
|
serverVariables.ldapEmailsRetriever = {
|
||||||
ldap_client = LdapClientMock();
|
retrieve: sinon.stub()
|
||||||
mocks.ldap = ldap_client;
|
} as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
});
|
});
|
||||||
|
@ -82,7 +82,7 @@ describe("test reset password identity check", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail if ldap fail", function (done) {
|
it("should fail if ldap fail", function (done) {
|
||||||
ldap_client.retrieveEmails.returns(BluebirdPromise.reject("Internal error"));
|
(serverVariables.ldapEmailsRetriever as any).retrieve.returns(BluebirdPromise.reject("Internal error"));
|
||||||
new PasswordResetHandler().preValidationInit(req as any)
|
new PasswordResetHandler().preValidationInit(req as any)
|
||||||
.catch(function (err: Error) {
|
.catch(function (err: Error) {
|
||||||
done();
|
done();
|
||||||
|
@ -91,16 +91,16 @@ describe("test reset password identity check", function () {
|
||||||
|
|
||||||
it("should perform a search in ldap to find email address", function (done) {
|
it("should perform a search in ldap to find email address", function (done) {
|
||||||
configuration.ldap.user_name_attribute = "uid";
|
configuration.ldap.user_name_attribute = "uid";
|
||||||
ldap_client.retrieveEmails.returns(BluebirdPromise.resolve([]));
|
(serverVariables.ldapEmailsRetriever as any).retrieve.returns(BluebirdPromise.resolve([]));
|
||||||
new PasswordResetHandler().preValidationInit(req as any)
|
new PasswordResetHandler().preValidationInit(req as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
assert.equal("user", ldap_client.retrieveEmails.getCall(0).args[0]);
|
assert.equal("user", (serverVariables.ldapEmailsRetriever as any).retrieve.getCall(0).args[0]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should returns identity when ldap replies", function (done) {
|
it("should returns identity when ldap replies", function (done) {
|
||||||
ldap_client.retrieveEmails.returns(BluebirdPromise.resolve(["test@example.com"]));
|
(serverVariables.ldapEmailsRetriever as any).retrieve.returns(BluebirdPromise.resolve(["test@example.com"]));
|
||||||
new PasswordResetHandler().preValidationInit(req as any)
|
new PasswordResetHandler().preValidationInit(req as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
done();
|
done();
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
|
|
||||||
import PasswordResetFormPost = require("../../../../../src/server/lib/routes/password-reset/form/post");
|
import PasswordResetFormPost = require("../../../../../src/server/lib/routes/password-reset/form/post");
|
||||||
import LdapClient = require("../../../../../src/server/lib/LdapClient");
|
import { PasswordUpdater } from "../../../../../src/server/lib/ldap/PasswordUpdater";
|
||||||
import AuthenticationSession = require("../../../../../src/server/lib/AuthenticationSession");
|
import AuthenticationSession = require("../../../../../src/server/lib/AuthenticationSession");
|
||||||
|
import { ServerVariables } from "../../../../../src/server/lib/ServerVariables";
|
||||||
import sinon = require("sinon");
|
import sinon = require("sinon");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
import ExpressMock = require("../../mocks/express");
|
import ExpressMock = require("../../mocks/express");
|
||||||
import { LdapClientMock } from "../../mocks/LdapClient";
|
|
||||||
import { UserDataStore } from "../../mocks/UserDataStore";
|
import { UserDataStore } from "../../mocks/UserDataStore";
|
||||||
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
||||||
|
|
||||||
describe("test reset password route", function () {
|
describe("test reset password route", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let user_data_store: UserDataStore;
|
let userDataStore: UserDataStore;
|
||||||
let ldapClient: LdapClientMock;
|
|
||||||
let configuration: any;
|
let configuration: any;
|
||||||
let authSession: AuthenticationSession.AuthenticationSession;
|
let authSession: AuthenticationSession.AuthenticationSession;
|
||||||
|
let serverVariables: ServerVariables;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = {
|
req = {
|
||||||
|
@ -45,13 +45,13 @@ describe("test reset password route", function () {
|
||||||
inMemoryOnly: true
|
inMemoryOnly: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const mocks = ServerVariablesMock.mock(req.app);
|
serverVariables = ServerVariablesMock.mock(req.app);
|
||||||
user_data_store = UserDataStore();
|
userDataStore = UserDataStore();
|
||||||
user_data_store.set_u2f_meta.returns(BluebirdPromise.resolve({}));
|
userDataStore.set_u2f_meta.returns(BluebirdPromise.resolve({}));
|
||||||
user_data_store.get_u2f_meta.returns(BluebirdPromise.resolve({}));
|
userDataStore.get_u2f_meta.returns(BluebirdPromise.resolve({}));
|
||||||
user_data_store.issue_identity_check_token.returns(BluebirdPromise.resolve({}));
|
userDataStore.issue_identity_check_token.returns(BluebirdPromise.resolve({}));
|
||||||
user_data_store.consume_identity_check_token.returns(BluebirdPromise.resolve({}));
|
userDataStore.consume_identity_check_token.returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore = user_data_store;
|
serverVariables.userDataStore = userDataStore as any;
|
||||||
|
|
||||||
|
|
||||||
configuration = {
|
configuration = {
|
||||||
|
@ -61,11 +61,12 @@ describe("test reset password route", function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mocks.logger = winston;
|
serverVariables.logger = winston;
|
||||||
mocks.config = configuration;
|
serverVariables.config = configuration;
|
||||||
|
|
||||||
ldapClient = LdapClientMock();
|
serverVariables.ldapPasswordUpdater = {
|
||||||
mocks.ldap = ldapClient;
|
updatePassword: sinon.stub()
|
||||||
|
} as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
});
|
});
|
||||||
|
@ -79,8 +80,7 @@ describe("test reset password route", function () {
|
||||||
req.body = {};
|
req.body = {};
|
||||||
req.body.password = "new-password";
|
req.body.password = "new-password";
|
||||||
|
|
||||||
ldapClient.updatePassword.returns(BluebirdPromise.resolve());
|
(serverVariables.ldapPasswordUpdater.updatePassword as sinon.SinonStub).returns(BluebirdPromise.resolve());
|
||||||
ldapClient.checkPassword.returns(BluebirdPromise.resolve());
|
|
||||||
return PasswordResetFormPost.default(req as any, res as any)
|
return PasswordResetFormPost.default(req as any, res as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
const authSession = AuthenticationSession.get(req as any);
|
const authSession = AuthenticationSession.get(req as any);
|
||||||
|
@ -111,8 +111,7 @@ describe("test reset password route", function () {
|
||||||
req.body = {};
|
req.body = {};
|
||||||
req.body.password = "new-password";
|
req.body.password = "new-password";
|
||||||
|
|
||||||
ldapClient.checkPassword.yields(undefined);
|
(serverVariables.ldapPasswordUpdater.updatePassword as sinon.SinonStub).returns(BluebirdPromise.reject("Internal error with LDAP"));
|
||||||
ldapClient.updatePassword.returns(BluebirdPromise.reject("Internal error with LDAP"));
|
|
||||||
res.send = sinon.spy(function () {
|
res.send = sinon.spy(function () {
|
||||||
assert.equal(res.status.getCall(0).args[0], 500);
|
assert.equal(res.status.getCall(0).args[0], 500);
|
||||||
done();
|
done();
|
||||||
|
|
|
@ -43,7 +43,7 @@ describe("test totp register", function () {
|
||||||
userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
userDataStore.set_totp_secret = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.set_totp_secret = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore = userDataStore;
|
mocks.userDataStore = userDataStore as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
});
|
});
|
||||||
|
|
|
@ -53,9 +53,9 @@ describe("test totp route", function () {
|
||||||
userDataStore.get_totp_secret.returns(BluebirdPromise.resolve(doc));
|
userDataStore.get_totp_secret.returns(BluebirdPromise.resolve(doc));
|
||||||
|
|
||||||
mocks.logger = winston;
|
mocks.logger = winston;
|
||||||
mocks.totpValidator = totpValidator;
|
mocks.totpValidator = totpValidator as any;
|
||||||
mocks.config = config;
|
mocks.config = config as any;
|
||||||
mocks.userDataStore = userDataStore;
|
mocks.userDataStore = userDataStore as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ describe("test register handler", function () {
|
||||||
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore = userDataStore;
|
mocks.userDataStore = userDataStore as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
res.send = sinon.spy();
|
res.send = sinon.spy();
|
||||||
|
|
|
@ -4,6 +4,7 @@ import BluebirdPromise = require("bluebird");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import U2FRegisterPost = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/register/post");
|
import U2FRegisterPost = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/register/post");
|
||||||
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
||||||
|
import { ServerVariables } from "../../../../../../../src/server/lib/ServerVariables";
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
|
|
||||||
import ExpressMock = require("../../../../mocks/express");
|
import ExpressMock = require("../../../../mocks/express");
|
||||||
|
@ -16,7 +17,7 @@ describe("test u2f routes: register", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||||
let mocks: ServerVariablesMock.ServerVariablesMock;
|
let mocks: ServerVariables;
|
||||||
let authSession: AuthenticationSession.AuthenticationSession;
|
let authSession: AuthenticationSession.AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
@ -46,7 +47,7 @@ describe("test u2f routes: register", function () {
|
||||||
userDataStore = UserDataStoreMock.UserDataStore();
|
userDataStore = UserDataStoreMock.UserDataStore();
|
||||||
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore = userDataStore;
|
mocks.userDataStore = userDataStore as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
res.send = sinon.spy();
|
res.send = sinon.spy();
|
||||||
|
|
|
@ -4,6 +4,7 @@ import BluebirdPromise = require("bluebird");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import U2FRegisterRequestGet = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/register_request/get");
|
import U2FRegisterRequestGet = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/register_request/get");
|
||||||
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
||||||
|
import { ServerVariables } from "../../../../../../../src/server/lib/ServerVariables";
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
|
|
||||||
import ExpressMock = require("../../../../mocks/express");
|
import ExpressMock = require("../../../../mocks/express");
|
||||||
|
@ -16,7 +17,7 @@ describe("test u2f routes: register_request", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||||
let mocks: ServerVariablesMock.ServerVariablesMock;
|
let mocks: ServerVariables;
|
||||||
let authSession: AuthenticationSession.AuthenticationSession;
|
let authSession: AuthenticationSession.AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
@ -46,7 +47,7 @@ describe("test u2f routes: register_request", function () {
|
||||||
userDataStore = UserDataStoreMock.UserDataStore();
|
userDataStore = UserDataStoreMock.UserDataStore();
|
||||||
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore = userDataStore;
|
mocks.userDataStore = userDataStore as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
res.send = sinon.spy();
|
res.send = sinon.spy();
|
||||||
|
|
|
@ -4,6 +4,7 @@ import BluebirdPromise = require("bluebird");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import U2FSignPost = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/sign/post");
|
import U2FSignPost = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/sign/post");
|
||||||
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
||||||
|
import { ServerVariables } from "../../../../../../../src/server/lib/ServerVariables";
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
|
|
||||||
import ExpressMock = require("../../../../mocks/express");
|
import ExpressMock = require("../../../../mocks/express");
|
||||||
|
@ -16,7 +17,7 @@ describe("test u2f routes: sign", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||||
let mocks: ServerVariablesMock.ServerVariablesMock;
|
let mocks: ServerVariables;
|
||||||
let authSession: AuthenticationSession.AuthenticationSession;
|
let authSession: AuthenticationSession.AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
@ -46,7 +47,7 @@ describe("test u2f routes: sign", function () {
|
||||||
userDataStore = UserDataStoreMock.UserDataStore();
|
userDataStore = UserDataStoreMock.UserDataStore();
|
||||||
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore = userDataStore;
|
mocks.userDataStore = userDataStore as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
res.send = sinon.spy();
|
res.send = sinon.spy();
|
||||||
|
|
|
@ -4,6 +4,7 @@ import BluebirdPromise = require("bluebird");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import U2FSignRequestGet = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/sign_request/get");
|
import U2FSignRequestGet = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/sign_request/get");
|
||||||
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
||||||
|
import { ServerVariables } from "../../../../../../../src/server/lib/ServerVariables";
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
|
|
||||||
import ExpressMock = require("../../../../mocks/express");
|
import ExpressMock = require("../../../../mocks/express");
|
||||||
|
@ -18,7 +19,7 @@ describe("test u2f routes: sign_request", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||||
let mocks: ServerVariablesMock.ServerVariablesMock;
|
let mocks: ServerVariables;
|
||||||
let authSession: AuthenticationSession.AuthenticationSession;
|
let authSession: AuthenticationSession.AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
@ -50,7 +51,7 @@ describe("test u2f routes: sign_request", function () {
|
||||||
userDataStore = UserDataStoreMock.UserDataStore();
|
userDataStore = UserDataStoreMock.UserDataStore();
|
||||||
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore = userDataStore;
|
mocks.userDataStore = userDataStore as any;
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
res.send = sinon.spy();
|
res.send = sinon.spy();
|
||||||
|
|
|
@ -27,9 +27,9 @@ describe("test authentication token verification", function () {
|
||||||
req.headers = {};
|
req.headers = {};
|
||||||
req.headers.host = "secret.example.com";
|
req.headers.host = "secret.example.com";
|
||||||
const mocks = ServerVariablesMock.mock(req.app);
|
const mocks = ServerVariablesMock.mock(req.app);
|
||||||
mocks.config = {};
|
mocks.config = {} as any;
|
||||||
mocks.logger = winston;
|
mocks.logger = winston;
|
||||||
mocks.accessController = accessController;
|
mocks.accessController = accessController as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be already authenticated", function (done) {
|
it("should be already authenticated", function (done) {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
import Server from "../../../../src/server/lib/Server";
|
import Server from "../../../../src/server/lib/Server";
|
||||||
import LdapClient = require("../../../../src/server/lib/LdapClient");
|
|
||||||
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import speakeasy = require("speakeasy");
|
import speakeasy = require("speakeasy");
|
||||||
import request = require("request");
|
import request = require("request");
|
||||||
|
@ -108,7 +106,8 @@ describe("Private pages of the server must not be accessible without session", f
|
||||||
session: ExpressSession,
|
session: ExpressSession,
|
||||||
winston: Winston,
|
winston: Winston,
|
||||||
speakeasy: speakeasy,
|
speakeasy: speakeasy,
|
||||||
ConnectRedis: Sinon.spy()
|
ConnectRedis: Sinon.spy(),
|
||||||
|
dovehash: Sinon.spy() as any
|
||||||
};
|
};
|
||||||
|
|
||||||
server = new Server();
|
server = new Server();
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
import Server from "../../../../src/server/lib/Server";
|
import Server from "../../../../src/server/lib/Server";
|
||||||
import LdapClient = require("../../../../src/server/lib/LdapClient");
|
|
||||||
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import speakeasy = require("speakeasy");
|
import speakeasy = require("speakeasy");
|
||||||
import Request = require("request");
|
import Request = require("request");
|
||||||
|
@ -108,7 +106,8 @@ describe("Public pages of the server must be accessible without session", functi
|
||||||
session: ExpressSession,
|
session: ExpressSession,
|
||||||
winston: Winston,
|
winston: Winston,
|
||||||
speakeasy: speakeasy,
|
speakeasy: speakeasy,
|
||||||
ConnectRedis: Sinon.spy()
|
ConnectRedis: Sinon.spy(),
|
||||||
|
dovehash: Sinon.spy() as any
|
||||||
};
|
};
|
||||||
|
|
||||||
server = new Server();
|
server = new Server();
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
import Server from "../../../../src/server/lib/Server";
|
import Server from "../../../../src/server/lib/Server";
|
||||||
import LdapClient = require("../../../../src/server/lib/LdapClient");
|
|
||||||
import { LdapjsClientMock } from "./../mocks/ldapjs";
|
import { LdapjsClientMock } from "./../mocks/ldapjs";
|
||||||
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
@ -92,7 +91,7 @@ describe("test the server", function () {
|
||||||
"password").yields();
|
"password").yields();
|
||||||
|
|
||||||
ldapClient.bind.withArgs("cn=test_nok,ou=users,dc=example,dc=com",
|
ldapClient.bind.withArgs("cn=test_nok,ou=users,dc=example,dc=com",
|
||||||
"password").yields("error");
|
"password").yields("Bad credentials");
|
||||||
|
|
||||||
ldapClient.unbind.yields();
|
ldapClient.unbind.yields();
|
||||||
ldapClient.modify.yields();
|
ldapClient.modify.yields();
|
||||||
|
@ -106,7 +105,10 @@ describe("test the server", function () {
|
||||||
session: ExpressSession,
|
session: ExpressSession,
|
||||||
winston: Winston,
|
winston: Winston,
|
||||||
speakeasy: speakeasy,
|
speakeasy: speakeasy,
|
||||||
ConnectRedis: Sinon.spy()
|
ConnectRedis: Sinon.spy(),
|
||||||
|
dovehash: {
|
||||||
|
encode: Sinon.stub().returns("abc")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
server = new Server();
|
server = new Server();
|
||||||
|
|
Loading…
Reference in New Issue