Add Mongo as scalable and resilient storage backend

This commit is contained in:
Clement Michaud 2017-07-19 21:06:12 +02:00
parent b0e3038aa6
commit e45ac39c8f
113 changed files with 2056 additions and 1200 deletions

View File

@ -77,9 +77,13 @@ session:
host: redis host: redis
port: 6379 port: 6379
# The directory where the DB files will be saved storage:
store_directory: /var/lib/authelia/store # The directory where the DB files will be saved
# local: /var/lib/authelia/store
# Settings to connect to mongo server
mongo:
url: mongodb://mongo/authelia
# Notifications are sent to users when they require a password reset, a u2f # Notifications are sent to users when they require a password reset, a u2f
# registration or a TOTP registration. # registration or a TOTP registration.

View File

@ -0,0 +1,6 @@
version: '2'
services:
mongo:
image: mongo:3.4
networks:
- example-network

View File

@ -1,6 +1,6 @@
version: '2' version: '2'
services: services:
redis: redis:
image: redis image: redis:4.0-alpine
networks: networks:
- example-network - example-network

View File

@ -24,7 +24,6 @@
"title": "Authelia API documentation" "title": "Authelia API documentation"
}, },
"dependencies": { "dependencies": {
"@types/cors": "^2.8.1",
"bluebird": "^3.4.7", "bluebird": "^3.4.7",
"body-parser": "^1.15.2", "body-parser": "^1.15.2",
"connect-redis": "^3.3.0", "connect-redis": "^3.3.0",
@ -33,6 +32,7 @@
"express": "^4.14.0", "express": "^4.14.0",
"express-session": "^1.14.2", "express-session": "^1.14.2",
"ldapjs": "^1.0.1", "ldapjs": "^1.0.1",
"mongodb": "^2.2.30",
"nedb": "^1.8.0", "nedb": "^1.8.0",
"nodemailer": "^4.0.1", "nodemailer": "^4.0.1",
"object-path": "^0.11.3", "object-path": "^0.11.3",
@ -47,6 +47,7 @@
"@types/bluebird": "^3.5.4", "@types/bluebird": "^3.5.4",
"@types/body-parser": "^1.16.3", "@types/body-parser": "^1.16.3",
"@types/connect-redis": "0.0.6", "@types/connect-redis": "0.0.6",
"@types/cors": "^2.8.1",
"@types/ejs": "^2.3.33", "@types/ejs": "^2.3.33",
"@types/express": "^4.0.35", "@types/express": "^4.0.35",
"@types/express-session": "0.0.32", "@types/express-session": "0.0.32",
@ -55,6 +56,7 @@
"@types/ldapjs": "^1.0.0", "@types/ldapjs": "^1.0.0",
"@types/mocha": "^2.2.41", "@types/mocha": "^2.2.41",
"@types/mockdate": "^2.0.0", "@types/mockdate": "^2.0.0",
"@types/mongodb": "^2.2.7",
"@types/nedb": "^1.8.3", "@types/nedb": "^1.8.3",
"@types/nodemailer": "^1.3.32", "@types/nodemailer": "^1.3.32",
"@types/object-path": "^0.9.28", "@types/object-path": "^0.9.28",
@ -89,7 +91,7 @@
"query-string": "^4.3.4", "query-string": "^4.3.4",
"request": "^2.81.0", "request": "^2.81.0",
"should": "^11.1.1", "should": "^11.1.1",
"sinon": "^1.17.6", "sinon": "^2.3.8",
"sinon-promise": "^0.1.3", "sinon-promise": "^0.1.3",
"tmp": "0.0.31", "tmp": "0.0.31",
"ts-node": "^3.0.4", "ts-node": "^3.0.4",
@ -107,7 +109,8 @@
"doc", "doc",
"src/types", "src/types",
"dist", "dist",
"test" "test",
"src/**/*.d.ts"
], ],
"extension": [ "extension": [
".ts" ".ts"

View File

@ -2,4 +2,12 @@
set -e set -e
docker-compose -f docker-compose.base.yml -f docker-compose.yml -f docker-compose.dev.yml -f example/redis/docker-compose.yml -f example/nginx/docker-compose.yml -f example/ldap/docker-compose.yml -f test/integration/docker-compose.yml $* docker-compose \
-f docker-compose.base.yml \
-f docker-compose.yml \
-f docker-compose.dev.yml \
-f example/mongo/docker-compose.yml \
-f example/redis/docker-compose.yml \
-f example/nginx/docker-compose.yml \
-f example/ldap/docker-compose.yml \
-f test/integration/docker-compose.yml $*

View File

@ -2,4 +2,11 @@
set -e set -e
docker-compose -f docker-compose.base.yml -f docker-compose.yml -f example/redis/docker-compose.yml -f example/nginx/docker-compose.yml -f example/ldap/docker-compose.yml -f test/integration/docker-compose.yml $* docker-compose \
-f docker-compose.base.yml \
-f docker-compose.yml \
-f example/mongo/docker-compose.yml \
-f example/redis/docker-compose.yml \
-f example/nginx/docker-compose.yml \
-f example/ldap/docker-compose.yml \
-f test/integration/docker-compose.yml $*

View File

@ -3,4 +3,4 @@
DC_SCRIPT=./scripts/example/dc-example.sh DC_SCRIPT=./scripts/example/dc-example.sh
$DC_SCRIPT build $DC_SCRIPT build
$DC_SCRIPT up -d redis openldap authelia nginx $DC_SCRIPT up -d mongo redis openldap authelia nginx

View File

@ -1,9 +1,10 @@
#!/bin/bash #!/bin/bash
DC_SCRIPT=./scripts/example/dc-example.sh DC_SCRIPT=./scripts/example/dc-example.sh
EXPECTED_SERVICES_COUNT=6
start_services() { start_services() {
$DC_SCRIPT up -d redis openldap authelia nginx nginx-tests $DC_SCRIPT up -d mongo redis openldap authelia nginx nginx-tests
sleep 3 sleep 3
} }
@ -44,7 +45,7 @@ run_integration_tests() {
$DC_SCRIPT logs authelia $DC_SCRIPT logs authelia
echo "Check number of services" echo "Check number of services"
expect_services_count 5 expect_services_count $EXPECTED_SERVICES_COUNT
echo "Run integration tests..." echo "Run integration tests..."
$DC_SCRIPT run --rm integration-tests $DC_SCRIPT run --rm integration-tests
@ -56,7 +57,7 @@ run_integration_tests() {
run_system_tests() { run_system_tests() {
echo "Start services..." echo "Start services..."
start_services start_services
expect_services_count 5 expect_services_count $EXPECTED_SERVICES_COUNT
./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/system ./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/system
shut_services shut_services
@ -67,7 +68,7 @@ run_other_tests() {
npm install --only=dev npm install --only=dev
./node_modules/.bin/grunt build-dist ./node_modules/.bin/grunt build-dist
./scripts/example/deploy-example.sh ./scripts/example/deploy-example.sh
expect_services_count 4 expect_services_count 5
} }

View File

@ -1,39 +1,36 @@
import * as BluebirdPromise from "bluebird"; import * as BluebirdPromise from "bluebird";
import exceptions = require("./Exceptions"); import exceptions = require("./Exceptions");
import { UserDataStore } from "./storage/UserDataStore";
import {AuthenticationTraceDocument} from "./storage/AuthenticationTraceDocument";
const REGULATION_TRACE_TYPE = "regulation";
const MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE = 3; const MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE = 3;
interface DatedDocument {
date: Date;
}
export class AuthenticationRegulator { export class AuthenticationRegulator {
private _user_data_store: any; private userDataStore: UserDataStore;
private _lock_time_in_seconds: number; private lockTimeInSeconds: number;
constructor(user_data_store: any, lock_time_in_seconds: number) { constructor(userDataStore: any, lockTimeInSeconds: number) {
this._user_data_store = user_data_store; this.userDataStore = userDataStore;
this._lock_time_in_seconds = lock_time_in_seconds; this.lockTimeInSeconds = lockTimeInSeconds;
} }
// Mark authentication // Mark authentication
mark(userid: string, is_success: boolean): BluebirdPromise<void> { mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise<void> {
return this._user_data_store.save_authentication_trace(userid, REGULATION_TRACE_TYPE, is_success); return this.userDataStore.saveAuthenticationTrace(userId, isAuthenticationSuccessful);
} }
regulate(userid: string): BluebirdPromise<void> { regulate(userId: string): BluebirdPromise<void> {
return this._user_data_store.get_last_authentication_traces(userid, REGULATION_TRACE_TYPE, false, 3) return this.userDataStore.retrieveLatestAuthenticationTraces(userId, false, 3)
.then((docs: Array<DatedDocument>) => { .then((docs: AuthenticationTraceDocument[]) => {
if (docs.length < MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE) { if (docs.length < MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE) {
// less than the max authorized number of authentication in time range, thus authorizing access // less than the max authorized number of authentication in time range, thus authorizing access
return BluebirdPromise.resolve(); return BluebirdPromise.resolve();
} }
const oldest_doc = docs[MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE - 1]; const oldestDocument = docs[MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE - 1];
const no_lock_min_date = new Date(new Date().getTime() - this._lock_time_in_seconds * 1000); const noLockMinDate = new Date(new Date().getTime() - this.lockTimeInSeconds * 1000);
if (oldest_doc.date > no_lock_min_date) { if (oldestDocument.date > noLockMinDate) {
throw new exceptions.AuthenticationRegulationError("Max number of authentication. Please retry in few minutes."); throw new exceptions.AuthenticationRegulationError("Max number of authentication. Please retry in few minutes.");
} }

View File

@ -6,15 +6,15 @@ import util = require("util");
import Exceptions = require("./Exceptions"); import Exceptions = require("./Exceptions");
import fs = require("fs"); import fs = require("fs");
import ejs = require("ejs"); import ejs = require("ejs");
import UserDataStore from "./UserDataStore"; import { IUserDataStore } from "./storage/IUserDataStore";
import { Winston } from "../../types/Dependencies"; import { Winston } from "../../types/Dependencies";
import express = require("express"); import express = require("express");
import ErrorReplies = require("./ErrorReplies"); import ErrorReplies = require("./ErrorReplies");
import ServerVariables = require("./ServerVariables"); import { ServerVariablesHandler } from "./ServerVariablesHandler";
import AuthenticationSession = require("./AuthenticationSession"); import AuthenticationSession = require("./AuthenticationSession");
import Identity = require("../../types/Identity"); import Identity = require("../../types/Identity");
import { IdentityValidationRequestContent } from "./UserDataStore"; import { IdentityValidationDocument } from "./storage/IdentityValidationDocument";
const filePath = __dirname + "/../resources/email-template.ejs"; const filePath = __dirname + "/../resources/email-template.ejs";
const email_template = fs.readFileSync(filePath, "utf8"); const email_template = fs.readFileSync(filePath, "utf8");
@ -33,21 +33,21 @@ export interface IdentityValidable {
mailSubject(): string; mailSubject(): string;
} }
function issue_token(userid: string, content: Object, userDataStore: UserDataStore, logger: Winston): BluebirdPromise<string> { function createAndSaveToken(userid: string, challenge: string, userDataStore: IUserDataStore, logger: Winston): BluebirdPromise<string> {
const five_minutes = 4 * 60 * 1000; const five_minutes = 4 * 60 * 1000;
const token = randomstring.generate({ length: 64 }); const token = randomstring.generate({ length: 64 });
const that = this; const that = this;
logger.debug("identity_check: issue identity token %s for 5 minutes", token); logger.debug("identity_check: issue identity token %s for 5 minutes", token);
return userDataStore.issue_identity_check_token(userid, token, content, five_minutes) return userDataStore.produceIdentityValidationToken(userid, token, challenge, five_minutes)
.then(function () { .then(function () {
return BluebirdPromise.resolve(token); return BluebirdPromise.resolve(token);
}); });
} }
function consume_token(token: string, userDataStore: UserDataStore, logger: Winston): BluebirdPromise<IdentityValidationRequestContent> { function consumeToken(token: string, challenge: string, userDataStore: IUserDataStore, logger: Winston): BluebirdPromise<IdentityValidationDocument> {
logger.debug("identity_check: consume token %s", token); logger.debug("identity_check: consume token %s", token);
return userDataStore.consume_identity_check_token(token); return userDataStore.consumeIdentityValidationToken(token, challenge);
} }
export function register(app: express.Application, pre_validation_endpoint: string, post_validation_endpoint: string, handler: IdentityValidable) { export function register(app: express.Application, pre_validation_endpoint: string, post_validation_endpoint: string, handler: IdentityValidable) {
@ -63,8 +63,8 @@ function checkIdentityToken(req: express.Request, identityToken: string): Bluebi
export function get_finish_validation(handler: IdentityValidable): express.RequestHandler { export function get_finish_validation(handler: IdentityValidable): express.RequestHandler {
return function (req: express.Request, res: express.Response): BluebirdPromise<void> { return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const userDataStore = ServerVariables.getUserDataStore(req.app); const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
const identityToken = objectPath.get<express.Request, string>(req, "query.identity_token"); const identityToken = objectPath.get<express.Request, string>(req, "query.identity_token");
@ -75,12 +75,12 @@ export function get_finish_validation(handler: IdentityValidable): express.Reque
return handler.postValidationInit(req); return handler.postValidationInit(req);
}) })
.then(function () { .then(function () {
return consume_token(identityToken, userDataStore, logger); return consumeToken(identityToken, handler.challenge(), userDataStore, logger);
}) })
.then(function (content: IdentityValidationRequestContent) { .then(function (doc: IdentityValidationDocument) {
authSession.identity_check = { authSession.identity_check = {
challenge: handler.challenge(), challenge: handler.challenge(),
userid: content.userid userid: doc.userId
}; };
handler.postValidationResponse(req, res); handler.postValidationResponse(req, res);
return BluebirdPromise.resolve(); return BluebirdPromise.resolve();
@ -94,9 +94,9 @@ export function get_finish_validation(handler: IdentityValidable): express.Reque
export function get_start_validation(handler: IdentityValidable, postValidationEndpoint: string): express.RequestHandler { export function get_start_validation(handler: IdentityValidable, postValidationEndpoint: string): express.RequestHandler {
return function (req: express.Request, res: express.Response): BluebirdPromise<void> { return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const notifier = ServerVariables.getNotifier(req.app); const notifier = ServerVariablesHandler.getNotifier(req.app);
const userDataStore = ServerVariables.getUserDataStore(req.app); const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
let identity: Identity.Identity; let identity: Identity.Identity;
logger.info("Identity Validation: Start identity validation"); logger.info("Identity Validation: Start identity validation");
@ -104,13 +104,13 @@ export function get_start_validation(handler: IdentityValidable, postValidationE
.then(function (id: Identity.Identity) { .then(function (id: Identity.Identity) {
logger.debug("Identity Validation: retrieved identity is %s", JSON.stringify(id)); logger.debug("Identity Validation: retrieved identity is %s", JSON.stringify(id));
identity = id; identity = id;
const email_address = objectPath.get<Identity.Identity, string>(identity, "email"); const email = identity.email;
const userid = objectPath.get<Identity.Identity, string>(identity, "userid"); const userid = identity.userid;
if (!(email_address && userid)) if (!(email && userid))
return BluebirdPromise.reject(new Exceptions.IdentityError("Missing user id or email address")); return BluebirdPromise.reject(new Exceptions.IdentityError("Missing user id or email address"));
return issue_token(userid, undefined, userDataStore, logger); return createAndSaveToken(userid, handler.challenge(), userDataStore, logger);
}) })
.then(function (token: string) { .then(function (token: string) {
const host = req.get("Host"); const host = req.get("Host");

View File

@ -1,6 +1,6 @@
import express = require("express"); import express = require("express");
import UserDataStore from "./UserDataStore"; import { UserDataStore } from "./storage/UserDataStore";
import { Winston } from "../../types/Dependencies"; import { Winston } from "../../types/Dependencies";
import FirstFactorGet = require("./routes/firstfactor/get"); import FirstFactorGet = require("./routes/firstfactor/get");
@ -34,7 +34,7 @@ import Error404Get = require("./routes/error/404/get");
import Endpoints = require("../endpoints"); import Endpoints = require("../endpoints");
export default class RestApi { export class RestApi {
static setup(app: express.Application): void { static setup(app: express.Application): void {
app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default); app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default);
app.get(Endpoints.SECOND_FACTOR_GET, SecondFactorGet.default); app.get(Endpoints.SECOND_FACTOR_GET, SecondFactorGet.default);

View File

@ -1,43 +1,54 @@
import BluebirdPromise = require("bluebird");
import { AccessController } from "./access_control/AccessController"; import { AccessController } from "./access_control/AccessController";
import { UserConfiguration } from "./../../types/Configuration"; import { AppConfiguration, UserConfiguration } from "./configuration/Configuration";
import { GlobalDependencies } from "../../types/Dependencies"; import { GlobalDependencies } from "../../types/Dependencies";
import { AuthenticationRegulator } from "./AuthenticationRegulator"; import { AuthenticationRegulator } from "./AuthenticationRegulator";
import UserDataStore from "./UserDataStore"; import { UserDataStore } from "./storage/UserDataStore";
import ConfigurationAdapter from "./ConfigurationAdapter"; import { ConfigurationAdapter } from "./configuration/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 { Client } from "./ldap/Client"; import { Client } from "./ldap/Client";
import BluebirdPromise = require("bluebird"); import { ServerVariablesHandler } from "./ServerVariablesHandler";
import ServerVariables = require("./ServerVariables"); import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder";
import SessionConfigurationBuilder from "./SessionConfigurationBuilder";
import * as Express from "express"; import * as Express from "express";
import * as BodyParser from "body-parser"; import * as BodyParser from "body-parser";
import * as Path from "path"; import * as Path from "path";
import * as http from "http"; import * as http from "http";
// Constants
const TRUST_PROXY = "trust proxy";
const VIEWS = "views";
const VIEW_ENGINE = "view engine";
const PUG = "pug";
export default class Server { export default class Server {
private httpServer: http.Server; private httpServer: http.Server;
start(yamlConfiguration: UserConfiguration, deps: GlobalDependencies): BluebirdPromise<void> { private setupExpressApplication(config: AppConfiguration, app: Express.Application, deps: GlobalDependencies): void {
const config = ConfigurationAdapter.adapt(yamlConfiguration);
const viewsDirectory = Path.resolve(__dirname, "../views"); const viewsDirectory = Path.resolve(__dirname, "../views");
const publicHtmlDirectory = Path.resolve(__dirname, "../public_html"); const publicHtmlDirectory = Path.resolve(__dirname, "../public_html");
const expressSessionOptions = SessionConfigurationBuilder.build(config, deps); const expressSessionOptions = SessionConfigurationBuilder.build(config, deps);
const app = Express();
app.use(Express.static(publicHtmlDirectory)); app.use(Express.static(publicHtmlDirectory));
app.use(BodyParser.urlencoded({ extended: false })); app.use(BodyParser.urlencoded({ extended: false }));
app.use(BodyParser.json()); app.use(BodyParser.json());
app.use(deps.session(expressSessionOptions)); app.use(deps.session(expressSessionOptions));
app.set("trust proxy", 1); app.set(TRUST_PROXY, 1);
app.set("views", viewsDirectory); app.set(VIEWS, viewsDirectory);
app.set("view engine", "pug"); app.set(VIEW_ENGINE, PUG);
RestApi.setup(app);
}
private transformConfiguration(yamlConfiguration: UserConfiguration, deps: GlobalDependencies): AppConfiguration {
const config = ConfigurationAdapter.adapt(yamlConfiguration);
// by default the level of logs is info // by default the level of logs is info
deps.winston.level = config.logs_level; deps.winston.level = config.logs_level;
@ -45,18 +56,33 @@ export default class Server {
deps.winston.debug("Content of YAML configuration file is %s", JSON.stringify(yamlConfiguration, undefined, 2)); deps.winston.debug("Content of YAML configuration file is %s", JSON.stringify(yamlConfiguration, undefined, 2));
deps.winston.debug("Authelia configuration is %s", JSON.stringify(config, undefined, 2)); deps.winston.debug("Authelia configuration is %s", JSON.stringify(config, undefined, 2));
return config;
}
ServerVariables.fill(app, config, deps); private setup(config: AppConfiguration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise<void> {
RestApi.setup(app); this.setupExpressApplication(config, app, deps);
return ServerVariablesHandler.initialize(app, config, deps);
}
private startServer(app: Express.Application, port: number) {
return new BluebirdPromise<void>((resolve, reject) => { return new BluebirdPromise<void>((resolve, reject) => {
this.httpServer = app.listen(config.port, function (err: string) { this.httpServer = app.listen(port, function (err: string) {
console.log("Listening on %d...", config.port); console.log("Listening on %d...", port);
resolve(); resolve();
}); });
}); });
} }
start(yamlConfiguration: UserConfiguration, deps: GlobalDependencies): BluebirdPromise<void> {
const that = this;
const app = Express();
const config = this.transformConfiguration(yamlConfiguration, deps);
return this.setup(config, app, deps)
.then(function () {
return that.startServer(app, config.port);
});
}
stop() { stop() {
this.httpServer.close(); this.httpServer.close();
} }

View File

@ -1,120 +0,0 @@
import winston = require("winston");
import { Authenticator } from "./ldap/Authenticator";
import { PasswordUpdater } from "./ldap/PasswordUpdater";
import { EmailsRetriever } from "./ldap/EmailsRetriever";
import { TOTPValidator } from "./TOTPValidator";
import { TOTPGenerator } from "./TOTPGenerator";
import U2F = require("u2f");
import UserDataStore from "./UserDataStore";
import { INotifier } from "./notifiers/INotifier";
import { AuthenticationRegulator } from "./AuthenticationRegulator";
import Configuration = require("../../types/Configuration");
import { AccessController } from "./access_control/AccessController";
import { NotifierFactory } from "./notifiers/NotifierFactory";
import { GlobalDependencies } from "../../types/Dependencies";
import express = require("express");
export const VARIABLES_KEY = "authelia-variables";
export interface ServerVariables {
logger: typeof winston;
ldapAuthenticator: Authenticator;
ldapPasswordUpdater: PasswordUpdater;
ldapEmailsRetriever: EmailsRetriever;
totpValidator: TOTPValidator;
totpGenerator: TOTPGenerator;
u2f: typeof U2F;
userDataStore: UserDataStore;
notifier: INotifier;
regulator: AuthenticationRegulator;
config: Configuration.AppConfiguration;
accessController: AccessController;
}
export function fill(app: express.Application, config: Configuration.AppConfiguration, deps: GlobalDependencies) {
const five_minutes = 5 * 60;
const datastore_options = {
directory: config.store_directory,
inMemory: config.store_in_memory
};
const userDataStore = new UserDataStore(datastore_options, deps.nedb);
const regulator = new AuthenticationRegulator(userDataStore, five_minutes);
const notifier = NotifierFactory.build(config.notifier, deps.nodemailer);
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 totpValidator = new TOTPValidator(deps.speakeasy);
const totpGenerator = new TOTPGenerator(deps.speakeasy);
const variables: ServerVariables = {
accessController: accessController,
config: config,
ldapAuthenticator: ldapAuthenticator,
ldapPasswordUpdater: ldapPasswordUpdater,
ldapEmailsRetriever: ldapEmailsRetriever,
logger: deps.winston,
notifier: notifier,
regulator: regulator,
totpGenerator: totpGenerator,
totpValidator: totpValidator,
u2f: deps.u2f,
userDataStore: userDataStore
};
app.set(VARIABLES_KEY, variables);
}
export function getLogger(app: express.Application): typeof winston {
return (app.get(VARIABLES_KEY) as ServerVariables).logger;
}
export function getUserDataStore(app: express.Application): UserDataStore {
return (app.get(VARIABLES_KEY) as ServerVariables).userDataStore;
}
export function getNotifier(app: express.Application): INotifier {
return (app.get(VARIABLES_KEY) as ServerVariables).notifier;
}
export function getLdapAuthenticator(app: express.Application): Authenticator {
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 {
return (app.get(VARIABLES_KEY) as ServerVariables).config;
}
export function getAuthenticationRegulator(app: express.Application): AuthenticationRegulator {
return (app.get(VARIABLES_KEY) as ServerVariables).regulator;
}
export function getAccessController(app: express.Application): AccessController {
return (app.get(VARIABLES_KEY) as ServerVariables).accessController;
}
export function getTOTPGenerator(app: express.Application): TOTPGenerator {
return (app.get(VARIABLES_KEY) as ServerVariables).totpGenerator;
}
export function getTOTPValidator(app: express.Application): TOTPValidator {
return (app.get(VARIABLES_KEY) as ServerVariables).totpValidator;
}
export function getU2F(app: express.Application): typeof U2F {
return (app.get(VARIABLES_KEY) as ServerVariables).u2f;
}

View File

@ -0,0 +1,151 @@
import winston = require("winston");
import BluebirdPromise = require("bluebird");
import { Authenticator } from "./ldap/Authenticator";
import { PasswordUpdater } from "./ldap/PasswordUpdater";
import { EmailsRetriever } from "./ldap/EmailsRetriever";
import { TOTPValidator } from "./TOTPValidator";
import { TOTPGenerator } from "./TOTPGenerator";
import U2F = require("u2f");
import { IUserDataStore } from "./storage/IUserDataStore";
import { UserDataStore } from "./storage/UserDataStore";
import { INotifier } from "./notifiers/INotifier";
import { AuthenticationRegulator } from "./AuthenticationRegulator";
import Configuration = require("./configuration/Configuration");
import { AccessController } from "./access_control/AccessController";
import { NotifierFactory } from "./notifiers/NotifierFactory";
import { CollectionFactoryFactory } from "./storage/CollectionFactoryFactory";
import { ICollectionFactory } from "./storage/ICollectionFactory";
import { MongoCollectionFactory } from "./storage/mongo/MongoCollectionFactory";
import { MongoConnectorFactory } from "./connectors/mongo/MongoConnectorFactory";
import { IMongoClient } from "./connectors/mongo/IMongoClient";
import { GlobalDependencies } from "../../types/Dependencies";
import express = require("express");
export const VARIABLES_KEY = "authelia-variables";
export interface ServerVariables {
logger: typeof winston;
ldapAuthenticator: Authenticator;
ldapPasswordUpdater: PasswordUpdater;
ldapEmailsRetriever: EmailsRetriever;
totpValidator: TOTPValidator;
totpGenerator: TOTPGenerator;
u2f: typeof U2F;
userDataStore: IUserDataStore;
notifier: INotifier;
regulator: AuthenticationRegulator;
config: Configuration.AppConfiguration;
accessController: AccessController;
}
class UserDataStoreFactory {
static create(config: Configuration.AppConfiguration): BluebirdPromise<UserDataStore> {
if (config.storage.local) {
const nedbOptions = {
directory: config.storage.local.path,
inMemory: config.storage.local.in_memory
};
const collectionFactory = CollectionFactoryFactory.createNedb(nedbOptions);
return BluebirdPromise.resolve(new UserDataStore(collectionFactory));
}
else if (config.storage.mongo) {
const mongoConnectorFactory = new MongoConnectorFactory();
const mongoConnector = mongoConnectorFactory.create(config.storage.mongo.url);
return mongoConnector.connect()
.then(function (client: IMongoClient) {
const collectionFactory = CollectionFactoryFactory.createMongo(client);
return BluebirdPromise.resolve(new UserDataStore(collectionFactory));
});
}
return BluebirdPromise.reject(new Error("Storage backend incorrectly configured."));
}
}
export class ServerVariablesHandler {
static initialize(app: express.Application, config: Configuration.AppConfiguration, deps: GlobalDependencies): BluebirdPromise<void> {
const five_minutes = 5 * 60;
const notifier = NotifierFactory.build(config.notifier, deps.nodemailer);
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 totpValidator = new TOTPValidator(deps.speakeasy);
const totpGenerator = new TOTPGenerator(deps.speakeasy);
return UserDataStoreFactory.create(config)
.then(function (userDataStore: UserDataStore) {
const regulator = new AuthenticationRegulator(userDataStore, five_minutes);
const variables: ServerVariables = {
accessController: accessController,
config: config,
ldapAuthenticator: ldapAuthenticator,
ldapPasswordUpdater: ldapPasswordUpdater,
ldapEmailsRetriever: ldapEmailsRetriever,
logger: deps.winston,
notifier: notifier,
regulator: regulator,
totpGenerator: totpGenerator,
totpValidator: totpValidator,
u2f: deps.u2f,
userDataStore: userDataStore
};
app.set(VARIABLES_KEY, variables);
});
}
static getLogger(app: express.Application): typeof winston {
return (app.get(VARIABLES_KEY) as ServerVariables).logger;
}
static getUserDataStore(app: express.Application): IUserDataStore {
return (app.get(VARIABLES_KEY) as ServerVariables).userDataStore;
}
static getNotifier(app: express.Application): INotifier {
return (app.get(VARIABLES_KEY) as ServerVariables).notifier;
}
static getLdapAuthenticator(app: express.Application): Authenticator {
return (app.get(VARIABLES_KEY) as ServerVariables).ldapAuthenticator;
}
static getLdapPasswordUpdater(app: express.Application): PasswordUpdater {
return (app.get(VARIABLES_KEY) as ServerVariables).ldapPasswordUpdater;
}
static getLdapEmailsRetriever(app: express.Application): EmailsRetriever {
return (app.get(VARIABLES_KEY) as ServerVariables).ldapEmailsRetriever;
}
static getConfiguration(app: express.Application): Configuration.AppConfiguration {
return (app.get(VARIABLES_KEY) as ServerVariables).config;
}
static getAuthenticationRegulator(app: express.Application): AuthenticationRegulator {
return (app.get(VARIABLES_KEY) as ServerVariables).regulator;
}
static getAccessController(app: express.Application): AccessController {
return (app.get(VARIABLES_KEY) as ServerVariables).accessController;
}
static getTOTPGenerator(app: express.Application): TOTPGenerator {
return (app.get(VARIABLES_KEY) as ServerVariables).totpGenerator;
}
static getTOTPValidator(app: express.Application): TOTPValidator {
return (app.get(VARIABLES_KEY) as ServerVariables).totpValidator;
}
static getU2F(app: express.Application): typeof U2F {
return (app.get(VARIABLES_KEY) as ServerVariables).u2f;
}
}

View File

@ -1,37 +0,0 @@
import ExpressSession = require("express-session");
import { AppConfiguration } from "../../types/Configuration";
import { GlobalDependencies } from "../../types/Dependencies";
export default class SessionConfigurationBuilder {
static build(configuration: AppConfiguration, deps: GlobalDependencies): ExpressSession.SessionOptions {
const sessionOptions: ExpressSession.SessionOptions = {
secret: configuration.session.secret,
resave: false,
saveUninitialized: true,
cookie: {
secure: false,
maxAge: configuration.session.expiration,
domain: configuration.session.domain
},
};
if (configuration.session.redis) {
let redisOptions;
if (configuration.session.redis.host
&& configuration.session.redis.port) {
redisOptions = {
host: configuration.session.redis.host,
port: configuration.session.redis.port
};
}
if (redisOptions) {
const RedisStore = deps.ConnectRedis(deps.session);
sessionOptions.store = new RedisStore(redisOptions);
}
}
return sessionOptions;
}
}

View File

@ -1,190 +0,0 @@
import * as BluebirdPromise from "bluebird";
import * as path from "path";
import { NedbAsync } from "nedb";
import { TOTPSecret } from "../../types/TOTPSecret";
import { Nedb } from "../../types/Dependencies";
import u2f = require("u2f");
// Constants
const U2F_META_COLLECTION_NAME = "u2f_meta";
const IDENTITY_CHECK_TOKENS_COLLECTION_NAME = "identity_check_tokens";
const AUTHENTICATION_TRACES_COLLECTION_NAME = "authentication_traces";
const TOTP_SECRETS_COLLECTION_NAME = "totp_secrets";
export interface TOTPSecretDocument {
userid: string;
secret: TOTPSecret;
}
export interface U2FRegistrationDocument {
keyHandle: string;
publicKey: string;
userId: string;
appId: string;
}
export interface Options {
inMemoryOnly?: boolean;
directory?: string;
}
export interface IdentityValidationRequestContent {
userid: string;
data: string;
}
export interface IdentityValidationRequestDocument {
userid: string;
token: string;
content: IdentityValidationRequestContent;
max_date: Date;
}
interface U2FRegistrationFilter {
userId: string;
appId: string;
}
// Source
export default class UserDataStore {
private _u2f_meta_collection: NedbAsync;
private _identity_check_tokens_collection: NedbAsync;
private _authentication_traces_collection: NedbAsync;
private _totp_secret_collection: NedbAsync;
private nedb: Nedb;
constructor(options: Options, nedb: Nedb) {
this.nedb = nedb;
this._u2f_meta_collection = this.create_collection(U2F_META_COLLECTION_NAME, options);
this._identity_check_tokens_collection =
this.create_collection(IDENTITY_CHECK_TOKENS_COLLECTION_NAME, options);
this._authentication_traces_collection =
this.create_collection(AUTHENTICATION_TRACES_COLLECTION_NAME, options);
this._totp_secret_collection =
this.create_collection(TOTP_SECRETS_COLLECTION_NAME, options);
}
set_u2f_meta(userId: string, appId: string, keyHandle: string, publicKey: string): BluebirdPromise<any> {
const newDocument: U2FRegistrationDocument = {
userId: userId,
appId: appId,
keyHandle: keyHandle,
publicKey: publicKey
};
const filter: U2FRegistrationFilter = {
userId: userId,
appId: appId
};
return this._u2f_meta_collection.updateAsync(filter, newDocument, { upsert: true });
}
get_u2f_meta(userId: string, appId: string): BluebirdPromise<U2FRegistrationDocument> {
const filter: U2FRegistrationFilter = {
userId: userId,
appId: appId
};
return this._u2f_meta_collection.findOneAsync(filter);
}
save_authentication_trace(userid: string, type: string, is_success: boolean) {
const newDocument = {
userid: userid,
date: new Date(),
is_success: is_success,
type: type
};
return this._authentication_traces_collection.insertAsync(newDocument);
}
get_last_authentication_traces(userid: string, type: string, is_success: boolean, count: number): BluebirdPromise<any> {
const q = {
userid: userid,
type: type,
is_success: is_success
};
const query = this._authentication_traces_collection.find(q)
.sort({ date: -1 }).limit(count);
const query_promisified = BluebirdPromise.promisify(query.exec, { context: query });
return query_promisified();
}
issue_identity_check_token(userid: string, token: string, data: string | object, max_age: number): BluebirdPromise<any> {
const newDocument = {
userid: userid,
token: token,
content: {
userid: userid,
data: data
},
max_date: new Date(new Date().getTime() + max_age)
};
return this._identity_check_tokens_collection.insertAsync(newDocument);
}
consume_identity_check_token(token: string): BluebirdPromise<IdentityValidationRequestContent> {
const query = {
token: token
};
return this._identity_check_tokens_collection.findOneAsync(query)
.then(function (doc) {
if (!doc) {
return BluebirdPromise.reject(new Error("Registration token does not exist"));
}
const max_date = doc.max_date;
const current_date = new Date();
if (current_date > max_date)
return BluebirdPromise.reject(new Error("Registration token is not valid anymore"));
return BluebirdPromise.resolve(doc.content);
})
.then((content) => {
return BluebirdPromise.join(this._identity_check_tokens_collection.removeAsync(query),
BluebirdPromise.resolve(content));
})
.then((v) => {
return BluebirdPromise.resolve(v[1]);
});
}
set_totp_secret(userid: string, secret: TOTPSecret): BluebirdPromise<any> {
const doc = {
userid: userid,
secret: secret
};
const query = {
userid: userid
};
return this._totp_secret_collection.updateAsync(query, doc, { upsert: true });
}
get_totp_secret(userid: string): BluebirdPromise<TOTPSecretDocument> {
const query = {
userid: userid
};
return this._totp_secret_collection.findOneAsync(query);
}
private create_collection(name: string, options: any): NedbAsync {
const datastore_options = {
inMemoryOnly: options.inMemoryOnly || false,
autoload: true,
filename: ""
};
if (options.directory)
datastore_options.filename = path.resolve(options.directory, name);
return BluebirdPromise.promisifyAll(new this.nedb(datastore_options)) as NedbAsync;
}
}

View File

@ -1,5 +1,5 @@
import { ACLConfiguration } from "../../../types/Configuration"; import { ACLConfiguration } from "../configuration/Configuration";
import PatternBuilder from "./PatternBuilder"; import PatternBuilder from "./PatternBuilder";
import { Winston } from "../../../types/Dependencies"; import { Winston } from "../../../types/Dependencies";

View File

@ -1,6 +1,6 @@
import { Winston } from "../../../types/Dependencies"; import { Winston } from "../../../types/Dependencies";
import { ACLConfiguration, ACLGroupsRules, ACLUsersRules, ACLDefaultRules } from "../../../types/Configuration"; import { ACLConfiguration, ACLGroupsRules, ACLUsersRules, ACLDefaultRules } from "../configuration/Configuration";
import objectPath = require("object-path"); import objectPath = require("object-path");
export default class AccessControlPatternBuilder { export default class AccessControlPatternBuilder {

View File

@ -50,12 +50,26 @@ export interface NotifierConfiguration {
filesystem?: FileSystemNotifierConfiguration; filesystem?: FileSystemNotifierConfiguration;
} }
export interface MongoStorageConfiguration {
url: string;
}
export interface LocalStorageConfiguration {
path?: string;
in_memory?: boolean;
}
export interface StorageConfiguration {
local?: LocalStorageConfiguration;
mongo?: MongoStorageConfiguration;
}
export interface UserConfiguration { export interface UserConfiguration {
port?: number; port?: number;
logs_level?: string; logs_level?: string;
ldap: LdapConfiguration; ldap: LdapConfiguration;
session: SessionCookieConfiguration; session: SessionCookieConfiguration;
store_directory?: string; storage: StorageConfiguration;
notifier: NotifierConfiguration; notifier: NotifierConfiguration;
access_control?: ACLConfiguration; access_control?: ACLConfiguration;
} }
@ -65,8 +79,7 @@ export interface AppConfiguration {
logs_level: string; logs_level: string;
ldap: LdapConfiguration; ldap: LdapConfiguration;
session: SessionCookieConfiguration; session: SessionCookieConfiguration;
store_in_memory?: boolean; storage: StorageConfiguration;
store_directory?: string;
notifier: NotifierConfiguration; notifier: NotifierConfiguration;
access_control?: ACLConfiguration; access_control?: ACLConfiguration;
} }

View File

@ -1,6 +1,10 @@
import * as ObjectPath from "object-path"; import * as ObjectPath from "object-path";
import { AppConfiguration, UserConfiguration, NotifierConfiguration, ACLConfiguration, LdapConfiguration, SessionRedisOptions } from "./../../types/Configuration"; import {
AppConfiguration, UserConfiguration, NotifierConfiguration,
ACLConfiguration, LdapConfiguration, SessionRedisOptions,
MongoStorageConfiguration, LocalStorageConfiguration
} from "./Configuration";
const LDAP_URL_ENV_VARIABLE = "LDAP_URL"; const LDAP_URL_ENV_VARIABLE = "LDAP_URL";
@ -34,14 +38,17 @@ function adaptFromUserConfiguration(userConfiguration: UserConfiguration): AppCo
expiration: get_optional<number>(userConfiguration, "session.expiration", 3600000), // in ms expiration: get_optional<number>(userConfiguration, "session.expiration", 3600000), // in ms
redis: ObjectPath.get<object, SessionRedisOptions>(userConfiguration, "session.redis") redis: ObjectPath.get<object, SessionRedisOptions>(userConfiguration, "session.redis")
}, },
store_directory: get_optional<string>(userConfiguration, "store_directory", undefined), storage: {
local: get_optional<LocalStorageConfiguration>(userConfiguration, "storage.local", undefined),
mongo: get_optional<MongoStorageConfiguration>(userConfiguration, "storage.mongo", undefined)
},
logs_level: get_optional<string>(userConfiguration, "logs_level", "info"), logs_level: get_optional<string>(userConfiguration, "logs_level", "info"),
notifier: ObjectPath.get<object, NotifierConfiguration>(userConfiguration, "notifier"), notifier: ObjectPath.get<object, NotifierConfiguration>(userConfiguration, "notifier"),
access_control: ObjectPath.get<object, ACLConfiguration>(userConfiguration, "access_control") access_control: ObjectPath.get<object, ACLConfiguration>(userConfiguration, "access_control")
}; };
} }
export default class ConfigurationAdapter { export class ConfigurationAdapter {
static adapt(userConfiguration: UserConfiguration): AppConfiguration { static adapt(userConfiguration: UserConfiguration): AppConfiguration {
const appConfiguration = adaptFromUserConfiguration(userConfiguration); const appConfiguration = adaptFromUserConfiguration(userConfiguration);

View File

@ -0,0 +1,37 @@
import ExpressSession = require("express-session");
import { AppConfiguration } from "./Configuration";
import { GlobalDependencies } from "../../../types/Dependencies";
export class SessionConfigurationBuilder {
static build(configuration: AppConfiguration, deps: GlobalDependencies): ExpressSession.SessionOptions {
const sessionOptions: ExpressSession.SessionOptions = {
secret: configuration.session.secret,
resave: false,
saveUninitialized: true,
cookie: {
secure: false,
maxAge: configuration.session.expiration,
domain: configuration.session.domain
},
};
if (configuration.session.redis) {
let redisOptions;
if (configuration.session.redis.host
&& configuration.session.redis.port) {
redisOptions = {
host: configuration.session.redis.host,
port: configuration.session.redis.port
};
}
if (redisOptions) {
const RedisStore = deps.ConnectRedis(deps.session);
sessionOptions.store = new RedisStore(redisOptions);
}
}
return sessionOptions;
}
}

View File

@ -0,0 +1,5 @@
import MongoDB = require("mongodb");
export interface IMongoClient {
collection(name: string): MongoDB.Collection;
}

View File

@ -0,0 +1,6 @@
import BluebirdPromise = require("bluebird");
import { IMongoClient } from "./IMongoClient";
export interface IMongoConnector {
connect(): BluebirdPromise<IMongoClient>;
}

View File

@ -0,0 +1,5 @@
import { IMongoConnector } from "./IMongoConnector";
export interface IMongoConnectorFactory {
create(url: string): IMongoConnector;
}

View File

@ -0,0 +1,15 @@
import MongoDB = require("mongodb");
import { IMongoClient } from "./IMongoClient";
export class MongoClient implements IMongoClient {
private db: MongoDB.Db;
constructor(db: MongoDB.Db) {
this.db = db;
}
collection(name: string): MongoDB.Collection {
return this.db.collection(name);
}
}

View File

@ -0,0 +1,22 @@
import MongoDB = require("mongodb");
import BluebirdPromise = require("bluebird");
import { IMongoClient } from "./IMongoClient";
import { IMongoConnector } from "./IMongoConnector";
import { MongoClient } from "./MongoClient";
export class MongoConnector implements IMongoConnector {
private url: string;
constructor(url: string) {
this.url = url;
}
connect(): BluebirdPromise<IMongoClient> {
const connectAsync = BluebirdPromise.promisify(MongoDB.MongoClient.connect);
return connectAsync(this.url)
.then(function (db: MongoDB.Db) {
return BluebirdPromise.resolve(new MongoClient(db));
});
}
}

View File

@ -0,0 +1,12 @@
import BluebirdPromise = require("bluebird");
import { IMongoConnectorFactory } from "./IMongoConnectorFactory";
import { IMongoConnector } from "./IMongoConnector";
import { MongoConnector } from "./MongoConnector";
import MongoDB = require("mongodb");
export class MongoConnectorFactory implements IMongoConnectorFactory {
create(url: string): IMongoConnector {
return new MongoConnector(url);
}
}

View File

@ -4,7 +4,7 @@ import ldapjs = require("ldapjs");
import { Client, Attributes } from "./Client"; import { Client, Attributes } from "./Client";
import { buildUserDN } from "./common"; import { buildUserDN } from "./common";
import { LdapConfiguration } from "./../../../types/Configuration"; import { LdapConfiguration } from "../configuration/Configuration";
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies"; import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";

View File

@ -6,7 +6,7 @@ import ldapjs = require("ldapjs");
import { buildUserDN } from "./common"; import { buildUserDN } from "./common";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import { LdapConfiguration } from "./../../../types/Configuration"; import { LdapConfiguration } from "../configuration/Configuration";
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies"; import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
interface SearchEntry { interface SearchEntry {

View File

@ -4,7 +4,7 @@ import ldapjs = require("ldapjs");
import { Client } from "./Client"; import { Client } from "./Client";
import { buildUserDN } from "./common"; import { buildUserDN } from "./common";
import { LdapConfiguration } from "./../../../types/Configuration"; import { LdapConfiguration } from "../configuration/Configuration";
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies"; import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";

View File

@ -4,7 +4,7 @@ import ldapjs = require("ldapjs");
import { Client } from "./Client"; import { Client } from "./Client";
import { buildUserDN } from "./common"; import { buildUserDN } from "./common";
import { LdapConfiguration } from "./../../../types/Configuration"; import { LdapConfiguration } from "../configuration/Configuration";
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies"; import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";

View File

@ -1,6 +1,6 @@
import util = require("util"); import util = require("util");
import { LdapConfiguration } from "./../../../types/Configuration"; import { LdapConfiguration } from "../configuration/Configuration";
export function buildUserDN(username: string, options: LdapConfiguration): string { export function buildUserDN(username: string, options: LdapConfiguration): string {

View File

@ -5,13 +5,12 @@ import * as fs from "fs";
import { INotifier } from "./INotifier"; import { INotifier } from "./INotifier";
import { Identity } from "../../../types/Identity"; import { Identity } from "../../../types/Identity";
import { FileSystemNotifierConfiguration } from "../../../types/Configuration"; import { FileSystemNotifierConfiguration } from "../configuration/Configuration";
export class FileSystemNotifier extends INotifier { export class FileSystemNotifier implements INotifier {
private filename: string; private filename: string;
constructor(options: FileSystemNotifierConfiguration) { constructor(options: FileSystemNotifierConfiguration) {
super();
this.filename = options.filename; this.filename = options.filename;
} }

View File

@ -7,16 +7,15 @@ import nodemailer = require("nodemailer");
import { Nodemailer } from "../../../types/Dependencies"; import { Nodemailer } from "../../../types/Dependencies";
import { Identity } from "../../../types/Identity"; import { Identity } from "../../../types/Identity";
import { INotifier } from "../notifiers/INotifier"; import { INotifier } from "../notifiers/INotifier";
import { GmailNotifierConfiguration } from "../../../types/Configuration"; import { GmailNotifierConfiguration } from "../configuration/Configuration";
import path = require("path"); import path = require("path");
const email_template = fs.readFileSync(path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8"); const email_template = fs.readFileSync(path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8");
export class GMailNotifier extends INotifier { export class GMailNotifier implements INotifier {
private transporter: any; private transporter: any;
constructor(options: GmailNotifierConfiguration, nodemailer: Nodemailer) { constructor(options: GmailNotifierConfiguration, nodemailer: Nodemailer) {
super();
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
service: "gmail", service: "gmail",
auth: { auth: {

View File

@ -0,0 +1,7 @@
import * as BluebirdPromise from "bluebird";
import { Identity } from "../../../types/Identity";
export interface INotifier {
notify(identity: Identity, subject: string, link: string): BluebirdPromise<void>;
}

View File

@ -1,7 +0,0 @@
import * as BluebirdPromise from "bluebird";
import { Identity } from "../../../types/Identity";
export abstract class INotifier {
abstract notify(identity: Identity, subject: string, link: string): BluebirdPromise<void>;
}

View File

@ -1,5 +1,5 @@
import { NotifierConfiguration } from "../../../types/Configuration"; import { NotifierConfiguration } from "../configuration/Configuration";
import { Nodemailer } from "../../../types/Dependencies"; import { Nodemailer } from "../../../types/Dependencies";
import { INotifier } from "./INotifier"; import { INotifier } from "./INotifier";

View File

@ -5,14 +5,14 @@ import FirstFactorValidator = require("../FirstFactorValidator");
import Exceptions = require("../Exceptions"); import Exceptions = require("../Exceptions");
import ErrorReplies = require("../ErrorReplies"); import ErrorReplies = require("../ErrorReplies");
import objectPath = require("object-path"); import objectPath = require("object-path");
import ServerVariables = require("../ServerVariables"); import { ServerVariablesHandler } from "../ServerVariablesHandler";
import AuthenticationSession = require("../AuthenticationSession"); import AuthenticationSession = require("../AuthenticationSession");
type Handler = (req: express.Request, res: express.Response) => BluebirdPromise<void>; type Handler = (req: express.Request, res: express.Response) => BluebirdPromise<void>;
export default function (callback: Handler): Handler { export default function (callback: Handler): Handler {
return function (req: express.Request, res: express.Response): BluebirdPromise<void> { return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
logger.debug("AuthSession is %s", JSON.stringify(authSession)); logger.debug("AuthSession is %s", JSON.stringify(authSession));

View File

@ -1,7 +1,8 @@
import BluebirdPromise = require("bluebird");
import express = require("express"); import express = require("express");
export default function (req: express.Request, res: express.Response) { export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
res.render("errors/401"); res.render("errors/401");
return BluebirdPromise.resolve();
} }

View File

@ -1,7 +1,8 @@
import BluebirdPromise = require("bluebird");
import express = require("express"); import express = require("express");
export default function (req: express.Request, res: express.Response) { export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
res.render("errors/403"); res.render("errors/403");
return BluebirdPromise.resolve();
} }

View File

@ -1,7 +1,8 @@
import BluebirdPromise = require("bluebird");
import express = require("express"); import express = require("express");
export default function (req: express.Request, res: express.Response) { export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
res.render("errors/404"); res.render("errors/404");
return BluebirdPromise.resolve();
} }

View File

@ -4,10 +4,10 @@ import objectPath = require("object-path");
import winston = require("winston"); import winston = require("winston");
import Endpoints = require("../../../endpoints"); import Endpoints = require("../../../endpoints");
import AuthenticationValidator = require("../../AuthenticationValidator"); import AuthenticationValidator = require("../../AuthenticationValidator");
import ServerVariables = require("../../ServerVariables"); import { ServerVariablesHandler } from "../../ServerVariablesHandler";
export default function (req: express.Request, res: express.Response) { export default function (req: express.Request, res: express.Response) {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
logger.debug("First factor: headers are %s", JSON.stringify(req.headers)); logger.debug("First factor: headers are %s", JSON.stringify(req.headers));

View File

@ -8,16 +8,16 @@ import { AuthenticationRegulator } from "../../AuthenticationRegulator";
import { Client, Attributes } from "../../ldap/Client"; 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 { ServerVariablesHandler } from "../../ServerVariablesHandler";
import AuthenticationSession = require("../../AuthenticationSession"); import AuthenticationSession = require("../../AuthenticationSession");
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> { export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
const username: string = req.body.username; const username: string = req.body.username;
const password: string = req.body.password; const password: string = req.body.password;
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const ldap = ServerVariables.getLdapAuthenticator(req.app); const ldap = ServerVariablesHandler.getLdapAuthenticator(req.app);
const config = ServerVariables.getConfiguration(req.app); const config = ServerVariablesHandler.getConfiguration(req.app);
if (!username || !password) { if (!username || !password) {
const err = new Error("No username or password"); const err = new Error("No username or password");
@ -25,8 +25,8 @@ export default function (req: express.Request, res: express.Response): BluebirdP
return BluebirdPromise.reject(err); return BluebirdPromise.reject(err);
} }
const regulator = ServerVariables.getAuthenticationRegulator(req.app); const regulator = ServerVariablesHandler.getAuthenticationRegulator(req.app);
const accessController = ServerVariables.getAccessController(req.app); const accessController = ServerVariablesHandler.getAccessController(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
logger.info("1st factor: Starting authentication of user \"%s\"", username); logger.info("1st factor: Starting authentication of user \"%s\"", username);

View File

@ -3,15 +3,15 @@ import express = require("express");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
import objectPath = require("object-path"); import objectPath = require("object-path");
import exceptions = require("../../../Exceptions"); import exceptions = require("../../../Exceptions");
import ServerVariables = require("../../../ServerVariables"); import { ServerVariablesHandler } from "../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../AuthenticationSession"); import AuthenticationSession = require("../../../AuthenticationSession");
import ErrorReplies = require("../../../ErrorReplies"); import ErrorReplies = require("../../../ErrorReplies");
import Constants = require("./../constants"); 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 = ServerVariablesHandler.getLogger(req.app);
const ldapPasswordUpdater = ServerVariables.getLdapPasswordUpdater(req.app); const ldapPasswordUpdater = ServerVariablesHandler.getLdapPasswordUpdater(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
const newPassword = objectPath.get<express.Request, string>(req, "body.password"); const newPassword = objectPath.get<express.Request, string>(req, "body.password");

View File

@ -8,7 +8,7 @@ import { IdentityValidable } from "../../../IdentityCheckMiddleware";
import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate"; import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate";
import Constants = require("../constants"); import Constants = require("../constants");
import { Winston } from "winston"; import { Winston } from "winston";
import ServerVariables = require("../../../ServerVariables"); import { ServerVariablesHandler } from "../../../ServerVariablesHandler";
export const TEMPLATE_NAME = "password-reset-form"; export const TEMPLATE_NAME = "password-reset-form";
@ -18,14 +18,14 @@ export default class PasswordResetHandler implements IdentityValidable {
} }
preValidationInit(req: express.Request): BluebirdPromise<Identity> { preValidationInit(req: express.Request): BluebirdPromise<Identity> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const userid: string = objectPath.get<express.Request, string>(req, "query.userid"); const userid: string = objectPath.get<express.Request, string>(req, "query.userid");
logger.debug("Reset Password: user '%s' requested a password reset", userid); logger.debug("Reset Password: user '%s' requested a password reset", userid);
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 emailsRetriever = ServerVariables.getLdapEmailsRetriever(req.app); const emailsRetriever = ServerVariablesHandler.getLdapEmailsRetriever(req.app);
return emailsRetriever.retrieve(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");

View File

@ -3,7 +3,7 @@ import express = require("express");
import objectPath = require("object-path"); import objectPath = require("object-path");
import winston = require("winston"); import winston = require("winston");
import Endpoints = require("../../../endpoints"); import Endpoints = require("../../../endpoints");
import ServerVariables = require("../../ServerVariables"); import { ServerVariablesHandler } from "../../ServerVariablesHandler";
import AuthenticationSession = require("../../AuthenticationSession"); import AuthenticationSession = require("../../AuthenticationSession");
export default function (req: express.Request, res: express.Response) { export default function (req: express.Request, res: express.Response) {

View File

@ -9,7 +9,7 @@ import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationT
import Constants = require("../constants"); import Constants = require("../constants");
import Endpoints = require("../../../../../endpoints"); import Endpoints = require("../../../../../endpoints");
import ErrorReplies = require("../../../../ErrorReplies"); import ErrorReplies = require("../../../../ErrorReplies");
import ServerVariables = require("../../../../ServerVariables"); import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession"); import AuthenticationSession = require("../../../../AuthenticationSession");
import FirstFactorValidator = require("../../../../FirstFactorValidator"); import FirstFactorValidator = require("../../../../FirstFactorValidator");
@ -53,7 +53,7 @@ export default class RegistrationHandler implements IdentityValidable {
} }
postValidationResponse(req: express.Request, res: express.Response) { postValidationResponse(req: express.Request, res: express.Response) {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
const userid = authSession.identity_check.userid; const userid = authSession.identity_check.userid;
@ -65,12 +65,12 @@ export default class RegistrationHandler implements IdentityValidable {
return; return;
} }
const userDataStore = ServerVariables.getUserDataStore(req.app); const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const totpGenerator = ServerVariables.getTOTPGenerator(req.app); const totpGenerator = ServerVariablesHandler.getTOTPGenerator(req.app);
const secret = totpGenerator.generate(); const secret = totpGenerator.generate();
logger.debug("POST new-totp-secret: save the TOTP secret in DB"); logger.debug("POST new-totp-secret: save the TOTP secret in DB");
userDataStore.set_totp_secret(userid, secret) userDataStore.saveTOTPSecret(userid, secret)
.then(function () { .then(function () {
objectPath.set(req, "session", undefined); objectPath.set(req, "session", undefined);

View File

@ -2,13 +2,13 @@
import exceptions = require("../../../../Exceptions"); import exceptions = require("../../../../Exceptions");
import objectPath = require("object-path"); import objectPath = require("object-path");
import express = require("express"); import express = require("express");
import { TOTPSecretDocument } from "../../../../UserDataStore"; import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument";
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
import FirstFactorBlocker from "../../../FirstFactorBlocker"; import FirstFactorBlocker from "../../../FirstFactorBlocker";
import Endpoints = require("../../../../../endpoints"); import Endpoints = require("../../../../../endpoints");
import redirect from "../../redirect"; import redirect from "../../redirect";
import ErrorReplies = require("../../../../ErrorReplies"); import ErrorReplies = require("../../../../ErrorReplies");
import ServerVariables = require("./../../../../ServerVariables"); import { ServerVariablesHandler } from "./../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession"); import AuthenticationSession = require("../../../../AuthenticationSession");
const UNAUTHORIZED_MESSAGE = "Unauthorized access"; const UNAUTHORIZED_MESSAGE = "Unauthorized access";
@ -16,17 +16,17 @@ const UNAUTHORIZED_MESSAGE = "Unauthorized access";
export default FirstFactorBlocker(handler); export default FirstFactorBlocker(handler);
export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> { export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
const userid = authSession.userid; const userid = authSession.userid;
logger.info("POST 2ndfactor totp: Initiate TOTP validation for user %s", userid); logger.info("POST 2ndfactor totp: Initiate TOTP validation for user %s", userid);
const token = req.body.token; const token = req.body.token;
const totpValidator = ServerVariables.getTOTPValidator(req.app); const totpValidator = ServerVariablesHandler.getTOTPValidator(req.app);
const userDataStore = ServerVariables.getUserDataStore(req.app); const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
logger.debug("POST 2ndfactor totp: Fetching secret for user %s", userid); logger.debug("POST 2ndfactor totp: Fetching secret for user %s", userid);
return userDataStore.get_totp_secret(userid) return userDataStore.retrieveTOTPSecret(userid)
.then(function (doc: TOTPSecretDocument) { .then(function (doc: TOTPSecretDocument) {
logger.debug("POST 2ndfactor totp: TOTP secret is %s", JSON.stringify(doc)); logger.debug("POST 2ndfactor totp: TOTP secret is %s", JSON.stringify(doc));
return totpValidator.validate(token, doc.secret.base32); return totpValidator.validate(token, doc.secret.base32);

View File

@ -1,15 +1,16 @@
import UserDataStore from "../../../../UserDataStore"; import { UserDataStore } from "../../../../storage/UserDataStore";
import objectPath = require("object-path"); import objectPath = require("object-path");
import u2f_common = require("../U2FCommon"); import u2f_common = require("../U2FCommon");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
import express = require("express"); import express = require("express");
import U2f = require("u2f"); import U2f = require("u2f");
import { U2FRegistration } from "../../../../../../types/U2FRegistration";
import FirstFactorBlocker from "../../../FirstFactorBlocker"; import FirstFactorBlocker from "../../../FirstFactorBlocker";
import redirect from "../../redirect"; import redirect from "../../redirect";
import ErrorReplies = require("../../../../ErrorReplies"); import ErrorReplies = require("../../../../ErrorReplies");
import ServerVariables = require("../../../../ServerVariables"); import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession"); import AuthenticationSession = require("../../../../AuthenticationSession");
@ -34,11 +35,11 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
} }
const userDataStore = ServerVariables.getUserDataStore(req.app); const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const u2f = ServerVariables.getU2F(req.app); const u2f = ServerVariablesHandler.getU2F(req.app);
const userid = authSession.userid; const userid = authSession.userid;
const appid = u2f_common.extract_app_id(req); const appid = u2f_common.extract_app_id(req);
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const registrationResponse: U2f.RegistrationData = req.body; const registrationResponse: U2f.RegistrationData = req.body;
@ -54,7 +55,11 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult; const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult;
logger.info("U2F register: Store regisutration and reply"); logger.info("U2F register: Store regisutration and reply");
logger.debug("U2F register: registration = %s", JSON.stringify(registrationResult)); logger.debug("U2F register: registration = %s", JSON.stringify(registrationResult));
return userDataStore.set_u2f_meta(userid, appid, registrationResult.keyHandle, registrationResult.publicKey); const registration: U2FRegistration = {
keyHandle: registrationResult.keyHandle,
publicKey: registrationResult.publicKey
};
return userDataStore.saveU2FRegistration(userid, appid, registration);
}) })
.then(function () { .then(function () {
authSession.identity_check = undefined; authSession.identity_check = undefined;

View File

@ -1,5 +1,5 @@
import UserDataStore from "../../../../UserDataStore"; import { UserDataStore } from "../../../../storage/UserDataStore";
import objectPath = require("object-path"); import objectPath = require("object-path");
import u2f_common = require("../U2FCommon"); import u2f_common = require("../U2FCommon");
@ -8,13 +8,13 @@ import express = require("express");
import U2f = require("u2f"); import U2f = require("u2f");
import FirstFactorBlocker from "../../../FirstFactorBlocker"; import FirstFactorBlocker from "../../../FirstFactorBlocker";
import ErrorReplies = require("../../../../ErrorReplies"); import ErrorReplies = require("../../../../ErrorReplies");
import ServerVariables = require("../../../../ServerVariables"); import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession"); import AuthenticationSession = require("../../../../AuthenticationSession");
export default FirstFactorBlocker(handler); export default FirstFactorBlocker(handler);
function handler(req: express.Request, res: express.Response): BluebirdPromise<void> { function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
if (!authSession.identity_check if (!authSession.identity_check
@ -24,7 +24,7 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
return; return;
} }
const u2f = ServerVariables.getU2F(req.app); const u2f = ServerVariablesHandler.getU2F(req.app);
const appid: string = u2f_common.extract_app_id(req); const appid: string = u2f_common.extract_app_id(req);
logger.debug("U2F register_request: headers=%s", JSON.stringify(req.headers)); logger.debug("U2F register_request: headers=%s", JSON.stringify(req.headers));

View File

@ -3,22 +3,22 @@ import objectPath = require("object-path");
import u2f_common = require("../U2FCommon"); import u2f_common = require("../U2FCommon");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
import express = require("express"); import express = require("express");
import UserDataStore, { U2FRegistrationDocument } from "../../../../UserDataStore"; import { UserDataStore } from "../../../../storage/UserDataStore";
import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument";
import { Winston } from "../../../../../../types/Dependencies"; import { Winston } from "../../../../../../types/Dependencies";
import U2f = require("u2f"); import U2f = require("u2f");
import exceptions = require("../../../../Exceptions"); import exceptions = require("../../../../Exceptions");
import FirstFactorBlocker from "../../../FirstFactorBlocker"; import FirstFactorBlocker from "../../../FirstFactorBlocker";
import redirect from "../../redirect"; import redirect from "../../redirect";
import ErrorReplies = require("../../../../ErrorReplies"); import ErrorReplies = require("../../../../ErrorReplies");
import ServerVariables = require("../../../../ServerVariables"); import {  ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession"); import AuthenticationSession = require("../../../../AuthenticationSession");
export default FirstFactorBlocker(handler); export default FirstFactorBlocker(handler);
export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> { export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const userDataStore = ServerVariables.getUserDataStore(req.app); const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
if (!authSession.sign_request) { if (!authSession.sign_request) {
@ -29,14 +29,14 @@ export function handler(req: express.Request, res: express.Response): BluebirdPr
const userid = authSession.userid; const userid = authSession.userid;
const appid = u2f_common.extract_app_id(req); const appid = u2f_common.extract_app_id(req);
return userDataStore.get_u2f_meta(userid, appid) return userDataStore.retrieveU2FRegistration(userid, appid)
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<U2f.SignatureResult | U2f.Error> { .then(function (doc: U2FRegistrationDocument): BluebirdPromise<U2f.SignatureResult | U2f.Error> {
const appid = u2f_common.extract_app_id(req); const appId = u2f_common.extract_app_id(req);
const u2f = ServerVariables.getU2F(req.app); const u2f = ServerVariablesHandler.getU2F(req.app);
const signRequest = authSession.sign_request; const signRequest = authSession.sign_request;
const signData: U2f.SignatureData = req.body; const signData: U2f.SignatureData = req.body;
logger.info("U2F sign: Finish authentication"); logger.info("U2F sign: Finish authentication");
return BluebirdPromise.resolve(u2f.checkSignature(signRequest, signData, doc.publicKey)); return BluebirdPromise.resolve(u2f.checkSignature(signRequest, signData, doc.registration.publicKey));
}) })
.then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise<void> { .then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise<void> {
if (objectPath.has(result, "errorCode")) if (objectPath.has(result, "errorCode"))

View File

@ -4,39 +4,39 @@ import U2f = require("u2f");
import u2f_common = require("../../../secondfactor/u2f/U2FCommon"); import u2f_common = require("../../../secondfactor/u2f/U2FCommon");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
import express = require("express"); import express = require("express");
import UserDataStore, { U2FRegistrationDocument } from "../../../../UserDataStore"; import { UserDataStore } from "../../../../storage/UserDataStore";
import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument";
import { Winston } from "../../../../../../types/Dependencies"; import { Winston } from "../../../../../../types/Dependencies";
import exceptions = require("../../../../Exceptions"); import exceptions = require("../../../../Exceptions");
import { SignMessage } from "./SignMessage"; import { SignMessage } from "./SignMessage";
import FirstFactorBlocker from "../../../FirstFactorBlocker"; import FirstFactorBlocker from "../../../FirstFactorBlocker";
import ErrorReplies = require("../../../../ErrorReplies"); import ErrorReplies = require("../../../../ErrorReplies");
import ServerVariables = require("../../../../ServerVariables"); import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession"); import AuthenticationSession = require("../../../../AuthenticationSession");
export default FirstFactorBlocker(handler); export default FirstFactorBlocker(handler);
export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> { export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const userDataStore = ServerVariables.getUserDataStore(req.app); const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
const userid = authSession.userid; const userId = authSession.userid;
const appid = u2f_common.extract_app_id(req); const appId = u2f_common.extract_app_id(req);
return userDataStore.get_u2f_meta(userid, appid) return userDataStore.retrieveU2FRegistration(userId, appId)
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<SignMessage> { .then(function (doc: U2FRegistrationDocument): BluebirdPromise<SignMessage> {
if (!doc) if (!doc)
return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration found")); return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration found"));
const u2f = ServerVariables.getU2F(req.app); const u2f = ServerVariablesHandler.getU2F(req.app);
const appId: string = u2f_common.extract_app_id(req); const appId: string = u2f_common.extract_app_id(req);
logger.info("U2F sign_request: Start authentication to app %s", appId); logger.info("U2F sign_request: Start authentication to app %s", appId);
logger.debug("U2F sign_request: appId=%s, keyHandle=%s", appId, JSON.stringify(doc.keyHandle)); logger.debug("U2F sign_request: appId=%s, keyHandle=%s", appId, JSON.stringify(doc.registration.keyHandle));
const request = u2f.request(appId, doc.keyHandle); const request = u2f.request(appId, doc.registration.keyHandle);
const authenticationMessage: SignMessage = { const authenticationMessage: SignMessage = {
request: request, request: request,
keyHandle: doc.keyHandle keyHandle: doc.registration.keyHandle
}; };
return BluebirdPromise.resolve(authenticationMessage); return BluebirdPromise.resolve(authenticationMessage);
}) })

View File

@ -8,12 +8,12 @@ import exceptions = require("../../Exceptions");
import winston = require("winston"); import winston = require("winston");
import AuthenticationValidator = require("../../AuthenticationValidator"); import AuthenticationValidator = require("../../AuthenticationValidator");
import ErrorReplies = require("../../ErrorReplies"); import ErrorReplies = require("../../ErrorReplies");
import ServerVariables = require("../../ServerVariables"); import { ServerVariablesHandler } from "../../ServerVariablesHandler";
import AuthenticationSession = require("../../AuthenticationSession"); import AuthenticationSession = require("../../AuthenticationSession");
function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> { function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
const accessController = ServerVariables.getAccessController(req.app); const accessController = ServerVariablesHandler.getAccessController(req.app);
const authSession = AuthenticationSession.get(req); const authSession = AuthenticationSession.get(req);
logger.debug("Verify: headers are %s", JSON.stringify(req.headers)); logger.debug("Verify: headers are %s", JSON.stringify(req.headers));
@ -40,7 +40,7 @@ function verify_filter(req: express.Request, res: express.Response): BluebirdPro
} }
export default function (req: express.Request, res: express.Response) { export default function (req: express.Request, res: express.Response) {
const logger = ServerVariables.getLogger(req.app); const logger = ServerVariablesHandler.getLogger(req.app);
verify_filter(req, res) verify_filter(req, res)
.then(function () { .then(function () {
res.status(204); res.status(204);

View File

@ -0,0 +1,6 @@
export interface AuthenticationTraceDocument {
userId: string;
date: Date;
isAuthenticationSuccessful: boolean;
}

View File

@ -0,0 +1,15 @@
import { ICollectionFactory } from "./ICollectionFactory";
import { NedbCollectionFactory, NedbOptions } from "./nedb/NedbCollectionFactory";
import { MongoCollectionFactory } from "./mongo/MongoCollectionFactory";
import { IMongoClient } from "../connectors/mongo/IMongoClient";
export class CollectionFactoryFactory {
static createNedb(options: NedbOptions): ICollectionFactory {
return new NedbCollectionFactory(options);
}
static createMongo(client: IMongoClient): ICollectionFactory {
return new MongoCollectionFactory(client);
}
}

11
src/server/lib/storage/ICollection.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
/* istanbul ignore next */
import BluebirdPromise = require("bluebird");
/* istanbul ignore next */
export interface ICollection {
find(query: any, sortKeys: any, count: number): BluebirdPromise<any>;
findOne(query: any): BluebirdPromise<any>;
update(query: any, updateQuery: any, options?: any): BluebirdPromise<any>;
remove(query: any): BluebirdPromise<any>;
insert(document: any): BluebirdPromise<any>;
}

View File

@ -0,0 +1,6 @@
import { ICollection } from "./ICollection";
export interface ICollectionFactory {
build(collectionName: string): ICollection;
}

View File

@ -0,0 +1,21 @@
import BluebirdPromise = require("bluebird");
import { TOTPSecretDocument } from "./TOTPSecretDocument";
import { U2FRegistrationDocument } from "./U2FRegistrationDocument";
import { U2FRegistration } from "../../../types/U2FRegistration";
import { TOTPSecret } from "../../../types/TOTPSecret";
import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument";
import { IdentityValidationDocument } from "./IdentityValidationDocument";
export interface IUserDataStore {
saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise<void>;
retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise<U2FRegistrationDocument>;
saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise<void>;
retrieveLatestAuthenticationTraces(userId: string, isAuthenticationSuccessful: boolean, count: number): BluebirdPromise<AuthenticationTraceDocument[]>;
produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise<any>;
consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise<IdentityValidationDocument>;
saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise<void>;
retrieveTOTPSecret(userId: string): BluebirdPromise<TOTPSecretDocument>;
}

View File

@ -0,0 +1,7 @@
export interface IdentityValidationDocument {
userId: string;
token: string;
challenge: string;
maxDate: Date;
}

View File

@ -0,0 +1,6 @@
import { TOTPSecret } from "../../../types/TOTPSecret";
export interface TOTPSecretDocument {
userid: string;
secret: TOTPSecret;
}

View File

@ -0,0 +1,8 @@
import { U2FRegistration } from "../../../types/U2FRegistration";
export interface U2FRegistrationDocument {
userId: string;
appId: string;
registration: U2FRegistration;
}

View File

@ -0,0 +1,144 @@
import * as BluebirdPromise from "bluebird";
import * as path from "path";
import { IUserDataStore } from "./IUserDataStore";
import { ICollection } from "./ICollection";
import { ICollectionFactory } from "./ICollectionFactory";
import { TOTPSecretDocument } from "./TOTPSecretDocument";
import { U2FRegistrationDocument } from "./U2FRegistrationDocument";
import { U2FRegistration } from "../../../types/U2FRegistration";
import { TOTPSecret } from "../../../types/TOTPSecret";
import { AuthenticationTraceDocument } from "./AuthenticationTraceDocument";
import { IdentityValidationDocument } from "./IdentityValidationDocument";
// Constants
const IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME = "identity_validation_tokens";
const AUTHENTICATION_TRACES_COLLECTION_NAME = "authentication_traces";
const U2F_REGISTRATIONS_COLLECTION_NAME = "u2f_registrations";
const TOTP_SECRETS_COLLECTION_NAME = "totp_secrets";
export interface U2FRegistrationKey {
userId: string;
appId: string;
}
// Source
export class UserDataStore implements IUserDataStore {
private u2fSecretCollection: ICollection;
private identityCheckTokensCollection: ICollection;
private authenticationTracesCollection: ICollection;
private totpSecretCollection: ICollection;
private collectionFactory: ICollectionFactory;
constructor(collectionFactory: ICollectionFactory) {
this.collectionFactory = collectionFactory;
this.u2fSecretCollection = this.collectionFactory.build(U2F_REGISTRATIONS_COLLECTION_NAME);
this.identityCheckTokensCollection = this.collectionFactory.build(IDENTITY_VALIDATION_TOKENS_COLLECTION_NAME);
this.authenticationTracesCollection = this.collectionFactory.build(AUTHENTICATION_TRACES_COLLECTION_NAME);
this.totpSecretCollection = this.collectionFactory.build(TOTP_SECRETS_COLLECTION_NAME);
}
saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise<void> {
const newDocument: U2FRegistrationDocument = {
userId: userId,
appId: appId,
registration: registration
};
const filter: U2FRegistrationKey = {
userId: userId,
appId: appId
};
return this.u2fSecretCollection.update(filter, newDocument, { upsert: true });
}
retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise<U2FRegistrationDocument> {
const filter: U2FRegistrationKey = {
userId: userId,
appId: appId
};
return this.u2fSecretCollection.findOne(filter);
}
saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise<void> {
const newDocument: AuthenticationTraceDocument = {
userId: userId,
date: new Date(),
isAuthenticationSuccessful: isAuthenticationSuccessful,
};
return this.authenticationTracesCollection.insert(newDocument);
}
retrieveLatestAuthenticationTraces(userId: string, isAuthenticationSuccessful: boolean, count: number): BluebirdPromise<AuthenticationTraceDocument[]> {
const q = {
userId: userId,
isAuthenticationSuccessful: isAuthenticationSuccessful
};
return this.authenticationTracesCollection.find(q, { date: -1 }, count);
}
produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise<any> {
const newDocument: IdentityValidationDocument = {
userId: userId,
token: token,
challenge: challenge,
maxDate: new Date(new Date().getTime() + maxAge)
};
return this.identityCheckTokensCollection.insert(newDocument);
}
consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise<IdentityValidationDocument> {
const that = this;
const filter = {
token: token,
challenge: challenge
};
let identityValidationDocument: IdentityValidationDocument;
return this.identityCheckTokensCollection.findOne(filter)
.then(function (doc: IdentityValidationDocument) {
if (!doc) {
return BluebirdPromise.reject(new Error("Registration token does not exist"));
}
identityValidationDocument = doc;
const current_date = new Date();
if (current_date > doc.maxDate)
return BluebirdPromise.reject(new Error("Registration token is not valid anymore"));
return that.identityCheckTokensCollection.remove(filter);
})
.then(() => {
return BluebirdPromise.resolve(identityValidationDocument);
});
}
saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise<void> {
const doc = {
userId: userId,
secret: secret
};
const filter = {
userId: userId
};
return this.totpSecretCollection.update(filter, doc, { upsert: true });
}
retrieveTOTPSecret(userId: string): BluebirdPromise<TOTPSecretDocument> {
const filter = {
userId: userId
};
return this.totpSecretCollection.findOne(filter);
}
}

View File

@ -0,0 +1,44 @@
import BluebirdPromise = require("bluebird");
import { ICollection } from "../ICollection";
import MongoDB = require("mongodb");
export class MongoCollection implements ICollection {
private collection: MongoDB.Collection;
constructor(collection: MongoDB.Collection) {
this.collection = collection;
}
find(query: any, sortKeys?: any, count?: number): BluebirdPromise<any> {
const q = this.collection.find(query).sort(sortKeys).limit(count);
const toArrayAsync = BluebirdPromise.promisify(q.toArray, { context: q });
return toArrayAsync();
}
findOne(query: any): BluebirdPromise<any> {
const findOneAsync = BluebirdPromise.promisify<any, any>(this.collection.findOne, { context: this.collection });
return findOneAsync(query);
}
update(query: any, updateQuery: any, options?: any): BluebirdPromise<any> {
const updateAsync = BluebirdPromise.promisify<any, any, any, any>(this.collection.update, { context: this.collection });
return updateAsync(query, updateQuery, options);
}
remove(query: any): BluebirdPromise<any> {
const removeAsync = BluebirdPromise.promisify<any, any>(this.collection.remove, { context: this.collection });
return removeAsync(query);
}
insert(document: any): BluebirdPromise<any> {
const insertAsync = BluebirdPromise.promisify<any, any>(this.collection.insert, { context: this.collection });
return insertAsync(document);
}
count(query: any): BluebirdPromise<number> {
const countAsync = BluebirdPromise.promisify<any, any>(this.collection.count, { context: this.collection });
return countAsync(query);
}
}

View File

@ -0,0 +1,19 @@
import BluebirdPromise = require("bluebird");
import { ICollection } from "../ICollection";
import { ICollectionFactory } from "../ICollectionFactory";
import { MongoCollection } from "./MongoCollection";
import path = require("path");
import MongoDB = require("mongodb");
import { IMongoClient } from "../../connectors/mongo/IMongoClient";
export class MongoCollectionFactory implements ICollectionFactory {
private mongoClient: IMongoClient;
constructor(mongoClient: IMongoClient) {
this.mongoClient = mongoClient;
}
build(collectionName: string): ICollection {
return new MongoCollection(this.mongoClient.collection(collectionName));
}
}

View File

@ -0,0 +1,38 @@
import BluebirdPromise = require("bluebird");
import { ICollection } from "../ICollection";
import Nedb = require("nedb");
import { NedbAsync } from "nedb";
export class NedbCollection implements ICollection {
private collection: NedbAsync;
constructor(options: Nedb.DataStoreOptions) {
this.collection = BluebirdPromise.promisifyAll(new Nedb(options)) as NedbAsync;
}
find(query: any, sortKeys?: any, count?: number): BluebirdPromise<any> {
const q = this.collection.find(query).sort(sortKeys).limit(count);
return BluebirdPromise.promisify(q.exec, { context: q })();
}
findOne(query: any): BluebirdPromise<any> {
return this.collection.findOneAsync(query);
}
update(query: any, updateQuery: any, options?: any): BluebirdPromise<any> {
return this.collection.updateAsync(query, updateQuery, options);
}
remove(query: any): BluebirdPromise<any> {
return this.collection.removeAsync(query);
}
insert(document: any): BluebirdPromise<any> {
return this.collection.insertAsync(document);
}
count(query: any): BluebirdPromise<number> {
return this.collection.countAsync(query);
}
}

View File

@ -0,0 +1,28 @@
import { ICollection } from "../ICollection";
import { ICollectionFactory } from "../ICollectionFactory";
import { NedbCollection } from "./NedbCollection";
import path = require("path");
import Nedb = require("nedb");
export interface NedbOptions {
inMemoryOnly?: boolean;
directory?: string;
}
export class NedbCollectionFactory implements ICollectionFactory {
private options: NedbOptions;
constructor(options: NedbOptions) {
this.options = options;
}
build(collectionName: string): ICollection {
const datastoreOptions = {
inMemoryOnly: this.options.inMemoryOnly || false,
autoload: true,
filename: (this.options.directory) ? path.resolve(this.options.directory, collectionName) : undefined
};
return new NedbCollection(datastoreOptions);
}
}

View File

@ -0,0 +1,5 @@
export interface U2FRegistration {
keyHandle: string;
publicKey: string;
}

View File

@ -5,8 +5,9 @@ declare module "nedb" {
export class NedbAsync extends Nedb { export class NedbAsync extends Nedb {
constructor(pathOrOptions?: string | Nedb.DataStoreOptions); constructor(pathOrOptions?: string | Nedb.DataStoreOptions);
updateAsync(query: any, updateQuery: any, options?: Nedb.UpdateOptions): BluebirdPromise<any>; updateAsync(query: any, updateQuery: any, options?: Nedb.UpdateOptions): BluebirdPromise<any>;
findOneAsync(query: any): BluebirdPromise<any>; findOneAsync<T>(query: any): BluebirdPromise<T>;
insertAsync<T>(newDoc: T): BluebirdPromise<any>; insertAsync<T>(newDoc: T): BluebirdPromise<any>;
removeAsync(query: any): BluebirdPromise<any>; removeAsync(query: any): BluebirdPromise<any>;
countAsync(query: any): BluebirdPromise<number>
} }
} }

View File

@ -1,72 +1,119 @@
import { AuthenticationRegulator } from "../../../src/server/lib/AuthenticationRegulator"; import Sinon = require("sinon");
import UserDataStore from "../../../src/server/lib/UserDataStore"; import BluebirdPromise = require("bluebird");
import { AuthenticationRegulator } from "../../../src/server/lib/AuthenticationRegulator";
import { UserDataStore } from "../../../src/server/lib/storage/UserDataStore";
import MockDate = require("mockdate"); import MockDate = require("mockdate");
import exceptions = require("../../../src/server/lib/Exceptions"); import exceptions = require("../../../src/server/lib/Exceptions");
import nedb = require("nedb"); import { CollectionStub } from "./mocks/storage/CollectionStub";
import { CollectionFactoryStub } from "./mocks/storage/CollectionFactoryStub";
describe("test authentication regulator", function() { describe("test authentication regulator", function () {
it("should mark 2 authentication and regulate (resolve)", function() { let collectionFactory: CollectionFactoryStub;
const options = { let collection: CollectionStub;
inMemoryOnly: true
}; beforeEach(function () {
const data_store = new UserDataStore(options, nedb); collectionFactory = new CollectionFactoryStub();
const regulator = new AuthenticationRegulator(data_store, 10); collection = new CollectionStub();
const user = "user";
collectionFactory.buildStub.returns(collection);
});
it("should mark 2 authentication and regulate", function () {
const user = "USER";
collection.insertStub.returns(BluebirdPromise.resolve());
collection.findStub.returns(BluebirdPromise.resolve([{
userId: user,
date: new Date(),
isAuthenticationSuccessful: false
}, {
userId: user,
date: new Date(),
isAuthenticationSuccessful: true
}]));
const dataStore = new UserDataStore(collectionFactory);
const regulator = new AuthenticationRegulator(dataStore, 10);
return regulator.mark(user, false) return regulator.mark(user, false)
.then(function() { .then(function () {
return regulator.mark(user, true); return regulator.mark(user, true);
}) })
.then(function() { .then(function () {
return regulator.regulate(user); return regulator.regulate(user);
}); });
}); });
it("should mark 3 authentications and regulate (reject)", function(done) { it("should mark 3 authentications and regulate (reject)", function (done) {
const options = { const user = "USER";
inMemoryOnly: true collection.insertStub.returns(BluebirdPromise.resolve());
}; collection.findStub.returns(BluebirdPromise.resolve([{
const data_store = new UserDataStore(options, nedb); userId: user,
const regulator = new AuthenticationRegulator(data_store, 10); date: new Date(),
const user = "user"; isAuthenticationSuccessful: false
}, {
userId: user,
date: new Date(),
isAuthenticationSuccessful: false
}, {
userId: user,
date: new Date(),
isAuthenticationSuccessful: false
}]));
const dataStore = new UserDataStore(collectionFactory);
const regulator = new AuthenticationRegulator(dataStore, 10);
regulator.mark(user, false) regulator.mark(user, false)
.then(function() { .then(function () {
return regulator.mark(user, false); return regulator.mark(user, false);
}) })
.then(function() { .then(function () {
return regulator.mark(user, false); return regulator.mark(user, false);
}) })
.then(function() { .then(function () {
return regulator.regulate(user); return regulator.regulate(user);
}) })
.catch(exceptions.AuthenticationRegulationError, function() { .catch(exceptions.AuthenticationRegulationError, function () {
done(); done();
}); });
}); });
it("should mark 3 authentications and regulate (resolve)", function(done) { it("should mark 3 authentications separated by a lot of time and allow access to user", function (done) {
const options = { const user = "USER";
inMemoryOnly: true collection.insertStub.returns(BluebirdPromise.resolve());
}; collection.findStub.returns(BluebirdPromise.resolve([{
const data_store = new UserDataStore(options, nedb); userId: user,
date: new Date("1/2/2000 06:00:15"),
isAuthenticationSuccessful: false
}, {
userId: user,
date: new Date("1/2/2000 00:00:15"),
isAuthenticationSuccessful: false
}, {
userId: user,
date: new Date("1/2/2000 00:00:00"),
isAuthenticationSuccessful: false
}]));
const data_store = new UserDataStore(collectionFactory);
const regulator = new AuthenticationRegulator(data_store, 10); const regulator = new AuthenticationRegulator(data_store, 10);
const user = "user";
MockDate.set("1/2/2000 00:00:00"); MockDate.set("1/2/2000 00:00:00");
regulator.mark(user, false) regulator.mark(user, false)
.then(function() { .then(function () {
MockDate.set("1/2/2000 00:00:15"); MockDate.set("1/2/2000 00:00:15");
return regulator.mark(user, false); return regulator.mark(user, false);
}) })
.then(function() { .then(function () {
MockDate.set("1/2/2000 06:00:15");
return regulator.mark(user, false); return regulator.mark(user, false);
}) })
.then(function() { .then(function () {
return regulator.regulate(user); return regulator.regulate(user);
}) })
.then(function() { .then(function () {
done(); done();
}); });
}); });

View File

@ -1,6 +1,6 @@
import * as Assert from "assert"; import * as Assert from "assert";
import { UserConfiguration } from "../../../src/types/Configuration"; import { UserConfiguration } from "../../../src/server/lib/configuration/Configuration";
import ConfigurationAdapter from "../../../src/server/lib/ConfigurationAdapter"; import { ConfigurationAdapter } from "../../../src/server/lib/configuration/ConfigurationAdapter";
describe("test config adapter", function() { describe("test config adapter", function() {
function build_yaml_config(): UserConfiguration { function build_yaml_config(): UserConfiguration {
@ -17,7 +17,11 @@ describe("test config adapter", function() {
secret: "secret", secret: "secret",
max_age: 40000 max_age: 40000
}, },
store_directory: "/mydirectory", storage: {
local: {
path: "/mydirectory"
}
},
logs_level: "debug", logs_level: "debug",
notifier: { notifier: {
gmail: { gmail: {

View File

@ -3,7 +3,7 @@ import * as BluebirdPromise from "bluebird";
import * as request from "request"; import * as request from "request";
import Server from "../../../src/server/lib/Server"; import Server from "../../../src/server/lib/Server";
import { UserConfiguration } from "../../../src/types/Configuration"; import { UserConfiguration } from "../../../src/server/lib/configuration/Configuration";
import { GlobalDependencies } from "../../../src/types/Dependencies"; import { GlobalDependencies } from "../../../src/types/Dependencies";
import * as tmp from "tmp"; import * as tmp from "tmp";
import U2FMock = require("./mocks/u2f"); import U2FMock = require("./mocks/u2f");
@ -70,7 +70,11 @@ describe("test data persistence", function () {
secret: "session_secret", secret: "session_secret",
expiration: 50000, expiration: 50000,
}, },
store_directory: tmpDir.name, storage: {
local: {
path: tmpDir.name
}
},
notifier: { notifier: {
gmail: { gmail: {
username: "user@example.com", username: "user@example.com",

View File

@ -2,24 +2,24 @@
import sinon = require("sinon"); import sinon = require("sinon");
import IdentityValidator = require("../../../src/server/lib/IdentityCheckMiddleware"); import IdentityValidator = require("../../../src/server/lib/IdentityCheckMiddleware");
import AuthenticationSession = require("../../../src/server/lib/AuthenticationSession"); import AuthenticationSession = require("../../../src/server/lib/AuthenticationSession");
import { UserDataStore } from "../../../src/server/lib/storage/UserDataStore";
import exceptions = require("../../../src/server/lib/Exceptions"); import exceptions = require("../../../src/server/lib/Exceptions");
import assert = require("assert"); import assert = require("assert");
import winston = require("winston");
import Promise = require("bluebird"); import Promise = require("bluebird");
import express = require("express"); import express = require("express");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
import ExpressMock = require("./mocks/express"); import ExpressMock = require("./mocks/express");
import UserDataStoreMock = require("./mocks/UserDataStore");
import NotifierMock = require("./mocks/Notifier"); import NotifierMock = require("./mocks/Notifier");
import IdentityValidatorMock = require("./mocks/IdentityValidator"); import IdentityValidatorMock = require("./mocks/IdentityValidator");
import ServerVariablesMock = require("./mocks/ServerVariablesMock"); import ServerVariablesMock = require("./mocks/ServerVariablesMock");
describe("test identity check process", function () { describe("test identity check process", function () {
let mocks: ServerVariablesMock.ServerVariablesMock;
let req: ExpressMock.RequestMock; let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock; let res: ExpressMock.ResponseMock;
let userDataStore: UserDataStoreMock.UserDataStore;
let notifier: NotifierMock.NotifierMock; let notifier: NotifierMock.NotifierMock;
let app: express.Application; let app: express.Application;
let app_get: sinon.SinonStub; let app_get: sinon.SinonStub;
@ -32,12 +32,6 @@ describe("test identity check process", function () {
identityValidable = IdentityValidatorMock.IdentityValidableMock(); identityValidable = IdentityValidatorMock.IdentityValidableMock();
userDataStore = UserDataStoreMock.UserDataStore();
userDataStore.issue_identity_check_token = sinon.stub();
userDataStore.issue_identity_check_token.returns(Promise.resolve());
userDataStore.consume_identity_check_token = sinon.stub();
userDataStore.consume_identity_check_token.returns(Promise.resolve({ userid: "user" }));
notifier = NotifierMock.NotifierMock(); notifier = NotifierMock.NotifierMock();
notifier.notify = sinon.stub().returns(Promise.resolve()); notifier.notify = sinon.stub().returns(Promise.resolve());
@ -47,11 +41,13 @@ describe("test identity check process", function () {
req.query = {}; req.query = {};
req.app = {}; req.app = {};
const mocks = ServerVariablesMock.mock(req.app);
mocks.logger = winston; mocks = ServerVariablesMock.mock(req.app);
mocks.userDataStore = userDataStore as any;
mocks.notifier = notifier; mocks.notifier = notifier;
mocks.userDataStore.produceIdentityValidationTokenStub.returns(Promise.resolve());
mocks.userDataStore.consumeIdentityValidationTokenStub.returns(Promise.resolve({ userId: "user" }));
app = express(); app = express();
app_get = sinon.stub(app, "get"); app_get = sinon.stub(app, "get");
app_post = sinon.stub(app, "post"); app_post = sinon.stub(app, "post");
@ -116,9 +112,9 @@ describe("test identity check process", function () {
return callback(req as any, res as any, undefined) return callback(req as any, res as any, undefined)
.then(function () { .then(function () {
assert(notifier.notify.calledOnce); assert(notifier.notify.calledOnce);
assert(userDataStore.issue_identity_check_token.calledOnce); assert(mocks.userDataStore.produceIdentityValidationTokenStub.calledOnce);
assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[0], "user"); assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[0], "user");
assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[3], 240000); assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[3], 240000);
}); });
}); });
} }
@ -145,8 +141,7 @@ describe("test identity check process", function () {
it("should return 500 if identity_token is provided but invalid", function () { it("should return 500 if identity_token is provided but invalid", function () {
req.query.identity_token = "token"; req.query.identity_token = "token";
userDataStore.consume_identity_check_token mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.reject(new Error("Invalid token")));
.returns(BluebirdPromise.reject(new Error("Invalid token")));
const callback = IdentityValidator.get_finish_validation(identityValidable); const callback = IdentityValidator.get_finish_validation(identityValidable);
return callback(req as any, res as any, undefined) return callback(req as any, res as any, undefined)

View File

@ -9,7 +9,7 @@ import u2f = require("u2f");
import nodemailer = require("nodemailer"); import nodemailer = require("nodemailer");
import session = require("express-session"); import session = require("express-session");
import { AppConfiguration, UserConfiguration } from "../../../src/types/Configuration"; import { AppConfiguration, UserConfiguration } from "../../../src/server/lib/configuration/Configuration";
import { GlobalDependencies, Nodemailer } from "../../../src/types/Dependencies"; import { GlobalDependencies, Nodemailer } from "../../../src/types/Dependencies";
import Server from "../../../src/server/lib/Server"; import Server from "../../../src/server/lib/Server";
@ -50,7 +50,7 @@ describe("test server configuration", function () {
it("should set cookie scope to domain set in the config", function () { it("should set cookie scope to domain set in the config", function () {
const config = { const config: UserConfiguration = {
session: { session: {
domain: "example.com", domain: "example.com",
secret: "secret" secret: "secret"
@ -58,15 +58,21 @@ describe("test server configuration", function () {
ldap: { ldap: {
url: "http://ldap", url: "http://ldap",
user: "user", user: "user",
password: "password" password: "password",
base_dn: "dc=example,dc=com"
}, },
notifier: { notifier: {
gmail: { gmail: {
username: "user@example.com", username: "user@example.com",
password: "password" password: "password"
} }
},
storage: {
local: {
in_memory: true
} }
} as UserConfiguration; }
};
const server = new Server(); const server = new Server();
server.start(config, deps); server.start(config, deps);

View File

@ -1,6 +1,7 @@
import SessionConfigurationBuilder from "../../../src/server/lib/SessionConfigurationBuilder"; import { SessionConfigurationBuilder } from "../../../src/server/lib/configuration/SessionConfigurationBuilder";
import { AppConfiguration } from "../../../src/types/Configuration"; import { AppConfiguration } from "../../../src/server/lib/configuration/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");
@ -32,7 +33,11 @@ describe("test session configuration builder", function () {
expiration: 3600, expiration: 3600,
secret: "secret" secret: "secret"
}, },
store_in_memory: true storage: {
local: {
in_memory: true
}
}
}; };
const deps: GlobalDependencies = { const deps: GlobalDependencies = {
@ -92,7 +97,11 @@ describe("test session configuration builder", function () {
port: 6379 port: 6379
} }
}, },
store_in_memory: true storage: {
local: {
in_memory: true
}
}
}; };
const RedisStoreMock = Sinon.spy(); const RedisStoreMock = Sinon.spy();

View File

@ -1,205 +0,0 @@
import UserDataStore from "../../../src/server/lib/UserDataStore";
import { U2FRegistrationDocument, Options } from "../../../src/server/lib/UserDataStore";
import nedb = require("nedb");
import assert = require("assert");
import Promise = require("bluebird");
import sinon = require("sinon");
import MockDate = require("mockdate");
describe("test user data store", () => {
let options: Options;
beforeEach(function () {
options = {
inMemoryOnly: true
};
});
describe("test u2f meta", () => {
it("should save a u2f meta", function () {
const data_store = new UserDataStore(options, nedb);
const userid = "user";
const app_id = "https://localhost";
const keyHandle = "keyhandle";
const publicKey = "publicKey";
return data_store.set_u2f_meta(userid, app_id, keyHandle, publicKey)
.then(function (numUpdated) {
assert.equal(1, numUpdated);
return Promise.resolve();
});
});
it("should retrieve no u2f meta", function () {
const options = {
inMemoryOnly: true
};
const data_store = new UserDataStore(options, nedb);
const userid = "user";
const app_id = "https://localhost";
const meta = {
publicKey: "pbk"
};
return data_store.get_u2f_meta(userid, app_id)
.then(function (doc) {
assert.equal(undefined, doc);
return Promise.resolve();
});
});
it("should insert and retrieve a u2f meta", function () {
const options = {
inMemoryOnly: true
};
const data_store = new UserDataStore(options, nedb);
const userid = "user";
const app_id = "https://localhost";
const keyHandle = "keyHandle";
const publicKey = "publicKey";
return data_store.set_u2f_meta(userid, app_id, keyHandle, publicKey)
.then(function (numUpdated: number) {
assert.equal(1, numUpdated);
return data_store.get_u2f_meta(userid, app_id);
})
.then(function (doc: U2FRegistrationDocument) {
assert.deepEqual(keyHandle, doc.keyHandle);
assert.deepEqual(publicKey, doc.publicKey);
assert.deepEqual(userid, doc.userId);
assert.deepEqual(app_id, doc.appId);
assert("_id" in doc);
return Promise.resolve();
});
});
});
describe("test u2f registration token", () => {
it("should save u2f registration token", function () {
const data_store = new UserDataStore(options, nedb);
const userid = "user";
const token = "token";
const max_age = 60;
const content = "abc";
return data_store.issue_identity_check_token(userid, token, content, max_age)
.then(function (document) {
assert.equal(document.userid, userid);
assert.equal(document.token, token);
assert.deepEqual(document.content, { userid: "user", data: content });
assert("max_date" in document);
assert("_id" in document);
return Promise.resolve();
})
.catch(function (err) {
console.error(err);
return Promise.reject(err);
});
});
it("should save u2f registration token and consume it", function (done) {
const data_store = new UserDataStore(options, nedb);
const userid = "user";
const token = "token";
const max_age = 50;
data_store.issue_identity_check_token(userid, token, {}, max_age)
.then(function (document) {
return data_store.consume_identity_check_token(token);
})
.then(function () {
done();
})
.catch(function (err) {
console.error(err);
});
});
it("should not be able to consume registration token twice", function (done) {
const data_store = new UserDataStore(options, nedb);
const userid = "user";
const token = "token";
const max_age = 50;
data_store.issue_identity_check_token(userid, token, {}, max_age)
.then(function (document) {
return data_store.consume_identity_check_token(token);
})
.then(function (document) {
return data_store.consume_identity_check_token(token);
})
.catch(function (err) {
console.error(err);
done();
});
});
it("should fail when token does not exist", function () {
const data_store = new UserDataStore(options, nedb);
const token = "token";
return data_store.consume_identity_check_token(token)
.then(function (document) {
return Promise.reject("Error while checking token");
})
.catch(function (err) {
return Promise.resolve(err);
});
});
it("should fail when token expired", function (done) {
const data_store = new UserDataStore(options, nedb);
const userid = "user";
const token = "token";
const max_age = 60;
MockDate.set("1/1/2000");
data_store.issue_identity_check_token(userid, token, {}, max_age)
.then(function () {
MockDate.set("1/2/2000");
return data_store.consume_identity_check_token(token);
})
.catch(function (err) {
MockDate.reset();
done();
});
});
it("should save the userid and some data with the token", function (done) {
const data_store = new UserDataStore(options, nedb);
const userid = "user";
const token = "token";
const max_age = 60;
MockDate.set("1/1/2000");
const data = "abc";
data_store.issue_identity_check_token(userid, token, data, max_age)
.then(function () {
return data_store.consume_identity_check_token(token);
})
.then(function (content) {
const expected_content = {
userid: "user",
data: "abc"
};
assert.deepEqual(content, expected_content);
done();
});
});
});
});

View File

@ -2,7 +2,7 @@
import assert = require("assert"); import assert = require("assert");
import winston = require("winston"); import winston = require("winston");
import { AccessController } from "../../../../src/server/lib/access_control/AccessController"; import { AccessController } from "../../../../src/server/lib/access_control/AccessController";
import { ACLConfiguration } from "../../../../src/types/Configuration"; import { ACLConfiguration } from "../../../../src/server/lib/configuration/Configuration";
describe("test access control manager", function () { describe("test access control manager", function () {
let accessController: AccessController; let accessController: AccessController;

View File

@ -3,7 +3,7 @@ import assert = require("assert");
import winston = require("winston"); import winston = require("winston");
import PatternBuilder from "../../../../src/server/lib/access_control/PatternBuilder"; import PatternBuilder from "../../../../src/server/lib/access_control/PatternBuilder";
import { ACLConfiguration } from "../../../../src/types/Configuration"; import { ACLConfiguration } from "../../../../src/server/lib/configuration/Configuration";
describe("test access control manager", function () { describe("test access control manager", function () {
describe("test access control pattern builder when no configuration is provided", () => { describe("test access control pattern builder when no configuration is provided", () => {

View File

@ -0,0 +1,38 @@
import Assert = require("assert");
import Sinon = require("sinon");
import MongoDB = require("mongodb");
import { MongoClient } from "../../../../../src/server/lib/connectors/mongo/MongoClient";
describe("MongoClient", function () {
let mongoClientConnectStub: Sinon.SinonStub;
let mongoDatabase: any;
let mongoDatabaseCollectionStub: Sinon.SinonStub;
describe("collection", function () {
before(function () {
mongoDatabaseCollectionStub = Sinon.stub();
mongoDatabase = {
collection: mongoDatabaseCollectionStub
};
mongoClientConnectStub = Sinon.stub(MongoDB.MongoClient, "connect");
mongoClientConnectStub.yields(undefined, mongoDatabase);
});
after(function () {
mongoClientConnectStub.restore();
});
it("should create a collection", function () {
const COLLECTION_NAME = "mycollection";
const client = new MongoClient(mongoDatabase);
mongoDatabaseCollectionStub.returns({});
const collection = client.collection(COLLECTION_NAME);
Assert(collection);
Assert(mongoDatabaseCollectionStub.calledWith(COLLECTION_NAME ));
});
});
});

View File

@ -0,0 +1,45 @@
import Assert = require("assert");
import Sinon = require("sinon");
import MongoDB = require("mongodb");
import BluebirdPromise = require("bluebird");
import { IMongoClient } from "../../../../../src/server/lib/connectors/mongo/IMongoClient";
import { MongoConnector } from "../../../../../src/server/lib/connectors/mongo/MongoConnector";
describe("MongoConnector", function () {
let mongoClientConnectStub: Sinon.SinonStub;
describe("create", function () {
before(function () {
mongoClientConnectStub = Sinon.stub(MongoDB.MongoClient, "connect");
});
after(function() {
mongoClientConnectStub.restore();
});
it("should create a connector", function () {
mongoClientConnectStub.yields(undefined);
const url = "mongodb://test.url";
const connector = new MongoConnector(url);
return connector.connect()
.then(function (client: IMongoClient) {
Assert(client);
Assert(mongoClientConnectStub.calledWith(url));
});
});
it("should fail creating a connector", function () {
mongoClientConnectStub.yields(new Error("Error while creating mongo client"));
const url = "mongodb://test.url";
const connector = new MongoConnector(url);
return connector.connect()
.then(function () { return BluebirdPromise.reject(new Error("It should not be here")); })
.error(function (client: IMongoClient) {
Assert(client);
Assert(mongoClientConnectStub.calledWith(url));
return BluebirdPromise.resolve();
});
});
});
});

View File

@ -0,0 +1,13 @@
import Assert = require("assert");
import { MongoConnectorFactory } from "../../../../../src/server/lib/connectors/mongo/MongoConnectorFactory";
describe("MongoConnectorFactory", function () {
describe("create", function () {
it("should create a connector", function () {
const factory = new MongoConnectorFactory();
const connector = factory.create("mongodb://test.url");
Assert(connector);
});
});
});

View File

@ -1,6 +1,6 @@
import { Authenticator } from "../../../../src/server/lib/ldap/Authenticator"; import { Authenticator } from "../../../../src/server/lib/ldap/Authenticator";
import { LdapConfiguration } from "../../../../src/types/Configuration"; import { LdapConfiguration } from "../../../../src/server/lib/configuration/Configuration";
import sinon = require("sinon"); import sinon = require("sinon");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");

View File

@ -1,6 +1,6 @@
import { EmailsRetriever } from "../../../../src/server/lib/ldap/EmailsRetriever"; import { EmailsRetriever } from "../../../../src/server/lib/ldap/EmailsRetriever";
import { LdapConfiguration } from "../../../../src/types/Configuration"; import { LdapConfiguration } from "../../../../src/server/lib/configuration/Configuration";
import sinon = require("sinon"); import sinon = require("sinon");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");

View File

@ -1,6 +1,6 @@
import { PasswordUpdater } from "../../../../src/server/lib/ldap/PasswordUpdater"; import { PasswordUpdater } from "../../../../src/server/lib/ldap/PasswordUpdater";
import { LdapConfiguration } from "../../../../src/types/Configuration"; import { LdapConfiguration } from "../../../../src/server/lib/configuration/Configuration";
import sinon = require("sinon"); import sinon = require("sinon");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");

View File

@ -1,14 +1,18 @@
import sinon = require("sinon"); import sinon = require("sinon");
import express = require("express"); import express = require("express");
import {  ServerVariables, VARIABLES_KEY }  from "../../../../src/server/lib/ServerVariables"; import winston = require("winston");
import { UserDataStoreStub } from "./storage/UserDataStoreStub";
import { ServerVariables, VARIABLES_KEY }  from "../../../../src/server/lib/ServerVariablesHandler";
export interface ServerVariablesMock { export interface ServerVariablesMock {
logger: any; logger: any;
ldap: any; ldapAuthenticator: any;
ldapEmailsRetriever: any;
ldapPasswordUpdater: any;
totpValidator: any; totpValidator: any;
totpGenerator: any; totpGenerator: any;
u2f: any; u2f: any;
userDataStore: any; userDataStore: UserDataStoreStub;
notifier: any; notifier: any;
regulator: any; regulator: any;
config: any; config: any;
@ -16,20 +20,20 @@ export interface ServerVariablesMock {
} }
export function mock(app: express.Application): ServerVariables { export function mock(app: express.Application): ServerVariablesMock {
const mocks: ServerVariables = { const mocks: ServerVariablesMock = {
accessController: sinon.stub() as any, accessController: sinon.stub(),
config: sinon.stub() as any, config: sinon.stub(),
ldapAuthenticator: sinon.stub() as any, ldapAuthenticator: sinon.stub() as any,
ldapEmailsRetriever: sinon.stub() as any, ldapEmailsRetriever: sinon.stub() as any,
ldapPasswordUpdater: sinon.stub() as any, ldapPasswordUpdater: sinon.stub() as any,
logger: sinon.stub() as any, logger: winston,
notifier: sinon.stub() as any, notifier: sinon.stub(),
regulator: sinon.stub() as any, regulator: sinon.stub(),
totpGenerator: sinon.stub() as any, totpGenerator: sinon.stub(),
totpValidator: sinon.stub() as any, totpValidator: sinon.stub(),
u2f: sinon.stub() as any, u2f: sinon.stub(),
userDataStore: sinon.stub() as any, userDataStore: new UserDataStoreStub()
}; };
app.get = sinon.stub().withArgs(VARIABLES_KEY).returns(mocks); app.get = sinon.stub().withArgs(VARIABLES_KEY).returns(mocks);
return mocks; return mocks;

View File

@ -1,22 +0,0 @@
import sinon = require("sinon");
export interface UserDataStore {
set_u2f_meta: sinon.SinonStub;
get_u2f_meta: sinon.SinonStub;
issue_identity_check_token: sinon.SinonStub;
consume_identity_check_token: sinon.SinonStub;
get_totp_secret: sinon.SinonStub;
set_totp_secret: sinon.SinonStub;
}
export function UserDataStore(): UserDataStore {
return {
set_u2f_meta: sinon.stub(),
get_u2f_meta: sinon.stub(),
issue_identity_check_token: sinon.stub(),
consume_identity_check_token: sinon.stub(),
get_totp_secret: sinon.stub(),
set_totp_secret: sinon.stub()
};
}

View File

@ -0,0 +1,15 @@
import Sinon = require("sinon");
import MongoDB = require("mongodb");
import { IMongoClient } from "../../../../../../src/server/lib/connectors/mongo/IMongoClient";
export class MongoClientStub implements IMongoClient {
public collectionStub: Sinon.SinonStub;
constructor() {
this.collectionStub = Sinon.stub();
}
collection(name: string): MongoDB.Collection {
return this.collectionStub(name);
}
}

View File

@ -0,0 +1,16 @@
import BluebirdPromise = require("bluebird");
import Sinon = require("sinon");
import { ICollection } from "../../../../../src/server/lib/storage/ICollection";
import { ICollectionFactory } from "../../../../../src/server/lib/storage/ICollectionFactory";
export class CollectionFactoryStub implements ICollectionFactory {
buildStub: Sinon.SinonStub;
constructor() {
this.buildStub = Sinon.stub();
}
build(collectionName: string): ICollection {
return this.buildStub(collectionName);
}
}

View File

@ -0,0 +1,39 @@
import BluebirdPromise = require("bluebird");
import Sinon = require("sinon");
import { ICollection } from "../../../../../src/server/lib/storage/ICollection";
export class CollectionStub implements ICollection {
findStub: Sinon.SinonStub;
findOneStub: Sinon.SinonStub;
updateStub: Sinon.SinonStub;
removeStub: Sinon.SinonStub;
insertStub: Sinon.SinonStub;
constructor() {
this.findStub = Sinon.stub();
this.findOneStub = Sinon.stub();
this.updateStub = Sinon.stub();
this.removeStub = Sinon.stub();
this.insertStub = Sinon.stub();
}
find(filter: any, sortKeys: any, count: number): BluebirdPromise<any> {
return this.findStub(filter, sortKeys, count);
}
findOne(filter: any): BluebirdPromise<any> {
return this.findOneStub(filter);
}
update(filter: any, document: any, options: any): BluebirdPromise<any> {
return this.updateStub(filter, document, options);
}
remove(filter: any): BluebirdPromise<any> {
return this.removeStub(filter);
}
insert(document: any): BluebirdPromise<any> {
return this.insertStub(document);
}
}

View File

@ -0,0 +1,65 @@
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import { TOTPSecretDocument } from "../../../../../src/server/lib/storage/TOTPSecretDocument";
import { U2FRegistrationDocument } from "../../../../../src/server/lib/storage/U2FRegistrationDocument";
import { U2FRegistration } from "../../../../../src/types/U2FRegistration";
import { TOTPSecret } from "../../../../../src/types/TOTPSecret";
import { AuthenticationTraceDocument } from "../../../../../src/server/lib/storage/AuthenticationTraceDocument";
import { IdentityValidationDocument } from "../../../../../src/server/lib/storage/IdentityValidationDocument";
import { IUserDataStore } from "../../../../../src/server/lib/storage/IUserDataStore";
export class UserDataStoreStub implements IUserDataStore {
saveU2FRegistrationStub: Sinon.SinonStub;
retrieveU2FRegistrationStub: Sinon.SinonStub;
saveAuthenticationTraceStub: Sinon.SinonStub;
retrieveLatestAuthenticationTracesStub: Sinon.SinonStub;
produceIdentityValidationTokenStub: Sinon.SinonStub;
consumeIdentityValidationTokenStub: Sinon.SinonStub;
saveTOTPSecretStub: Sinon.SinonStub;
retrieveTOTPSecretStub: Sinon.SinonStub;
constructor() {
this.saveU2FRegistrationStub = Sinon.stub();
this.retrieveU2FRegistrationStub = Sinon.stub();
this.saveAuthenticationTraceStub = Sinon.stub();
this.retrieveLatestAuthenticationTracesStub = Sinon.stub();
this.produceIdentityValidationTokenStub = Sinon.stub();
this.consumeIdentityValidationTokenStub = Sinon.stub();
this.saveTOTPSecretStub = Sinon.stub();
this.retrieveTOTPSecretStub = Sinon.stub();
}
saveU2FRegistration(userId: string, appId: string, registration: U2FRegistration): BluebirdPromise<void> {
return this.saveU2FRegistrationStub(userId, appId, registration);
}
retrieveU2FRegistration(userId: string, appId: string): BluebirdPromise<U2FRegistrationDocument> {
return this.retrieveU2FRegistrationStub(userId, appId);
}
saveAuthenticationTrace(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise<void> {
return this.saveAuthenticationTraceStub(userId, isAuthenticationSuccessful);
}
retrieveLatestAuthenticationTraces(userId: string, isAuthenticationSuccessful: boolean, count: number): BluebirdPromise<AuthenticationTraceDocument[]> {
return this.retrieveLatestAuthenticationTracesStub(userId, isAuthenticationSuccessful, count);
}
produceIdentityValidationToken(userId: string, token: string, challenge: string, maxAge: number): BluebirdPromise<any> {
return this.produceIdentityValidationTokenStub(userId, token, challenge, maxAge);
}
consumeIdentityValidationToken(token: string, challenge: string): BluebirdPromise<IdentityValidationDocument> {
return this.consumeIdentityValidationTokenStub(token, challenge);
}
saveTOTPSecret(userId: string, secret: TOTPSecret): BluebirdPromise<void> {
return this.saveTOTPSecretStub(userId, secret);
}
retrieveTOTPSecret(userId: string): BluebirdPromise<TOTPSecretDocument> {
return this.retrieveTOTPSecretStub(userId);
}
}

View File

@ -0,0 +1,19 @@
import Sinon = require("sinon");
import Express = require("express");
import Assert = require("assert");
import Get401 from "../../../../../../src/server/lib/routes/error/401/get";
describe("Server error 401", function () {
it("should render the page", function () {
const req = {} as Express.Request;
const res = {
render: Sinon.stub()
};
return Get401(req, res as any)
.then(function () {
Assert(res.render.calledOnce);
Assert(res.render.calledWith("errors/401"));
});
});
});

View File

@ -0,0 +1,19 @@
import Sinon = require("sinon");
import Express = require("express");
import Assert = require("assert");
import Get403 from "../../../../../../src/server/lib/routes/error/403/get";
describe("Server error 403", function () {
it("should render the page", function () {
const req = {} as Express.Request;
const res = {
render: Sinon.stub()
};
return Get403(req, res as any)
.then(function () {
Assert(res.render.calledOnce);
Assert(res.render.calledWith("errors/403"));
});
});
});

View File

@ -0,0 +1,19 @@
import Sinon = require("sinon");
import Express = require("express");
import Assert = require("assert");
import Get404 from "../../../../../../src/server/lib/routes/error/404/get";
describe("Server error 404", function () {
it("should render the page", function () {
const req = {} as Express.Request;
const res = {
render: Sinon.stub()
};
return Get404(req, res as any)
.then(function () {
Assert(res.render.calledOnce);
Assert(res.render.calledWith("errors/404"));
});
});
});

View File

@ -13,7 +13,7 @@ import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulato
import AccessControllerMock = require("../../mocks/AccessController"); import AccessControllerMock = require("../../mocks/AccessController");
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"; import { ServerVariables } from "../../../../../src/server/lib/ServerVariablesHandler";
describe("test the first factor validation route", function () { describe("test the first factor validation route", function () {
let req: ExpressMock.RequestMock; let req: ExpressMock.RequestMock;

View File

@ -1,22 +1,21 @@
import PasswordResetHandler from "../../../../../../src/server/lib/routes/password-reset/identity/PasswordResetHandler"; import PasswordResetHandler from "../../../../../../src/server/lib/routes/password-reset/identity/PasswordResetHandler";
import PasswordUpdater = require("../../../../../../src/server/lib/ldap/PasswordUpdater"); import PasswordUpdater = require("../../../../../../src/server/lib/ldap/PasswordUpdater");
import { ServerVariables } from "../../../../../../src/server/lib/ServerVariables"; import { ServerVariablesHandler } from "../../../../../../src/server/lib/ServerVariablesHandler";
import sinon = require("sinon"); import { UserDataStore } from "../../../../../../src/server/lib/storage/UserDataStore";
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 { 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 userDataStore: UserDataStore;
let configuration: any; let configuration: any;
let serverVariables: ServerVariables; let serverVariables: ServerVariablesMock.ServerVariablesMock;
beforeEach(function () { beforeEach(function () {
req = { req = {
@ -24,7 +23,7 @@ describe("test reset password identity check", function () {
userid: "user" userid: "user"
}, },
app: { app: {
get: sinon.stub() get: Sinon.stub()
}, },
session: { session: {
auth_session: { auth_session: {
@ -45,14 +44,10 @@ describe("test reset password identity check", function () {
serverVariables = ServerVariablesMock.mock(req.app); serverVariables = ServerVariablesMock.mock(req.app);
serverVariables.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore = UserDataStore(); serverVariables.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.set_u2f_meta.returns(BluebirdPromise.resolve({})); serverVariables.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
userDataStore.get_u2f_meta.returns(BluebirdPromise.resolve({})); serverVariables.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
userDataStore.issue_identity_check_token.returns(BluebirdPromise.resolve({}));
userDataStore.consume_identity_check_token.returns(BluebirdPromise.resolve({}));
serverVariables.userDataStore = userDataStore as any;
configuration = { configuration = {
ldap: { ldap: {
@ -64,9 +59,8 @@ describe("test reset password identity check", function () {
serverVariables.logger = winston; serverVariables.logger = winston;
serverVariables.config = configuration; serverVariables.config = configuration;
serverVariables.ldapEmailsRetriever = { serverVariables.ldapEmailsRetriever = {
retrieve: sinon.stub() retrieve: Sinon.stub()
} as any; } as any;
res = ExpressMock.ResponseMock(); res = ExpressMock.ResponseMock();
}); });

View File

@ -2,23 +2,22 @@
import PasswordResetFormPost = require("../../../../../src/server/lib/routes/password-reset/form/post"); import PasswordResetFormPost = require("../../../../../src/server/lib/routes/password-reset/form/post");
import { PasswordUpdater } from "../../../../../src/server/lib/ldap/PasswordUpdater"; 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 { ServerVariablesHandler } from "../../../../../src/server/lib/ServerVariablesHandler";
import sinon = require("sinon"); import { UserDataStore } from "../../../../../src/server/lib/storage/UserDataStore";
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 { 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 userDataStore: UserDataStore;
let configuration: any; let configuration: any;
let authSession: AuthenticationSession.AuthenticationSession; let authSession: AuthenticationSession.AuthenticationSession;
let serverVariables: ServerVariables; let serverVariables: ServerVariablesMock.ServerVariablesMock;
beforeEach(function () { beforeEach(function () {
req = { req = {
@ -26,7 +25,7 @@ describe("test reset password route", function () {
userid: "user" userid: "user"
}, },
app: { app: {
get: sinon.stub() get: Sinon.stub()
}, },
session: {}, session: {},
headers: { headers: {
@ -46,13 +45,10 @@ describe("test reset password route", function () {
}; };
serverVariables = ServerVariablesMock.mock(req.app); serverVariables = ServerVariablesMock.mock(req.app);
userDataStore = UserDataStore(); serverVariables.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.set_u2f_meta.returns(BluebirdPromise.resolve({})); serverVariables.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.get_u2f_meta.returns(BluebirdPromise.resolve({})); serverVariables.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
userDataStore.issue_identity_check_token.returns(BluebirdPromise.resolve({})); serverVariables.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
userDataStore.consume_identity_check_token.returns(BluebirdPromise.resolve({}));
serverVariables.userDataStore = userDataStore as any;
configuration = { configuration = {
ldap: { ldap: {
@ -65,7 +61,7 @@ describe("test reset password route", function () {
serverVariables.config = configuration; serverVariables.config = configuration;
serverVariables.ldapPasswordUpdater = { serverVariables.ldapPasswordUpdater = {
updatePassword: sinon.stub() updatePassword: Sinon.stub()
} as any; } as any;
res = ExpressMock.ResponseMock(); res = ExpressMock.ResponseMock();
@ -96,7 +92,7 @@ describe("test reset password route", function () {
userid: "user", userid: "user",
challenge: undefined challenge: undefined
}; };
res.send = sinon.spy(function () { res.send = Sinon.spy(function () {
assert.equal(res.status.getCall(0).args[0], 403); assert.equal(res.status.getCall(0).args[0], 403);
done(); done();
}); });
@ -111,8 +107,8 @@ describe("test reset password route", function () {
req.body = {}; req.body = {};
req.body.password = "new-password"; req.body.password = "new-password";
(serverVariables.ldapPasswordUpdater.updatePassword as sinon.SinonStub).returns(BluebirdPromise.reject("Internal error with LDAP")); (serverVariables.ldapPasswordUpdater.updatePassword as Sinon.SinonStub).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();
}); });

View File

@ -1,19 +1,18 @@
import sinon = require("sinon"); import Sinon = require("sinon");
import winston = require("winston"); import winston = require("winston");
import RegistrationHandler from "../../../../../../../src/server/lib/routes/secondfactor/totp/identity/RegistrationHandler"; import RegistrationHandler from "../../../../../../../src/server/lib/routes/secondfactor/totp/identity/RegistrationHandler";
import { Identity } from "../../../../../../../src/types/Identity"; import { Identity } from "../../../../../../../src/types/Identity";
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession"); import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
import { UserDataStore } from "../../../../../../../src/server/lib/storage/UserDataStore";
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 UserDataStoreMock = require("../../../../mocks/UserDataStore");
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
describe("test totp register", function () { describe("test totp register", function () {
let req: ExpressMock.RequestMock; let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock; let res: ExpressMock.ResponseMock;
let userDataStore: UserDataStoreMock.UserDataStore;
const registrationHandler: RegistrationHandler = new RegistrationHandler(); const registrationHandler: RegistrationHandler = new RegistrationHandler();
let authSession: AuthenticationSession.AuthenticationSession; let authSession: AuthenticationSession.AuthenticationSession;
@ -37,13 +36,11 @@ describe("test totp register", function () {
inMemoryOnly: true inMemoryOnly: true
}; };
userDataStore = UserDataStoreMock.UserDataStore(); mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.saveTOTPSecretStub.returns(BluebirdPromise.resolve({}));
userDataStore.set_totp_secret = sinon.stub().returns(BluebirdPromise.resolve({}));
mocks.userDataStore = userDataStore as any;
res = ExpressMock.ResponseMock(); res = ExpressMock.ResponseMock();
}); });

View File

@ -9,15 +9,14 @@ import AuthenticationSession = require("../../../../../../../src/server/lib/Auth
import SignPost = require("../../../../../../../src/server/lib/routes/secondfactor/totp/sign/post"); import SignPost = require("../../../../../../../src/server/lib/routes/secondfactor/totp/sign/post");
import ExpressMock = require("../../../../mocks/express"); import ExpressMock = require("../../../../mocks/express");
import UserDataStoreMock = require("../../../../mocks/UserDataStore");
import TOTPValidatorMock = require("../../../../mocks/TOTPValidator"); import TOTPValidatorMock = require("../../../../mocks/TOTPValidator");
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
describe("test totp route", function () { describe("test totp route", function () {
let req: ExpressMock.RequestMock; let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock; let res: ExpressMock.ResponseMock;
let totpValidator: TOTPValidatorMock.TOTPValidatorMock; let totpValidator: TOTPValidatorMock.TOTPValidatorMock;
let userDataStore: UserDataStoreMock.UserDataStore;
let authSession: AuthenticationSession.AuthenticationSession; let authSession: AuthenticationSession.AuthenticationSession;
beforeEach(function () { beforeEach(function () {
@ -42,20 +41,17 @@ describe("test totp route", function () {
const config = { totp_secret: "secret" }; const config = { totp_secret: "secret" };
totpValidator = TOTPValidatorMock.TOTPValidatorMock(); totpValidator = TOTPValidatorMock.TOTPValidatorMock();
userDataStore = UserDataStoreMock.UserDataStore();
const doc = { const doc = {
userid: "user", userid: "user",
secret: { secret: {
base32: "ABCDEF" base32: "ABCDEF"
} }
}; };
userDataStore.get_totp_secret.returns(BluebirdPromise.resolve(doc)); mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc));
mocks.logger = winston; mocks.logger = winston;
mocks.totpValidator = totpValidator as any; mocks.totpValidator = totpValidator;
mocks.config = config as any; mocks.config = config;
mocks.userDataStore = userDataStore as any;
}); });

View File

@ -8,13 +8,12 @@ import RegistrationHandler from "../../../../../../../src/server/lib/routes/seco
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession"); import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
import ExpressMock = require("../../../../mocks/express"); import ExpressMock = require("../../../../mocks/express");
import UserDataStoreMock = require("../../../../mocks/UserDataStore"); import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
describe("test register handler", function () { describe("test register handler", function () {
let req: ExpressMock.RequestMock; let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock; let res: ExpressMock.ResponseMock;
let userDataStore: UserDataStoreMock.UserDataStore;
let authSession: AuthenticationSession.AuthenticationSession; let authSession: AuthenticationSession.AuthenticationSession;
beforeEach(function () { beforeEach(function () {
@ -36,12 +35,10 @@ describe("test register handler", function () {
inMemoryOnly: true inMemoryOnly: true
}; };
userDataStore = UserDataStoreMock.UserDataStore(); mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
mocks.userDataStore = userDataStore as any;
res = ExpressMock.ResponseMock(); res = ExpressMock.ResponseMock();
res.send = sinon.spy(); res.send = sinon.spy();

View File

@ -4,11 +4,11 @@ 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 { ServerVariablesHandler } from "../../../../../../../src/server/lib/ServerVariablesHandler";
import winston = require("winston"); import winston = require("winston");
import ExpressMock = require("../../../../mocks/express"); import ExpressMock = require("../../../../mocks/express");
import UserDataStoreMock = require("../../../../mocks/UserDataStore"); import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
import U2FMock = require("../../../../mocks/u2f"); import U2FMock = require("../../../../mocks/u2f");
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
import U2f = require("u2f"); import U2f = require("u2f");
@ -16,8 +16,7 @@ import U2f = require("u2f");
describe("test u2f routes: register", function () { 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 mocks: ServerVariablesMock.ServerVariablesMock;
let mocks: ServerVariables;
let authSession: AuthenticationSession.AuthenticationSession; let authSession: AuthenticationSession.AuthenticationSession;
beforeEach(function () { beforeEach(function () {
@ -44,10 +43,8 @@ describe("test u2f routes: register", function () {
inMemoryOnly: true inMemoryOnly: true
}; };
userDataStore = UserDataStoreMock.UserDataStore(); mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
mocks.userDataStore = userDataStore as any;
res = ExpressMock.ResponseMock(); res = ExpressMock.ResponseMock();
res.send = sinon.spy(); res.send = sinon.spy();
@ -77,7 +74,7 @@ describe("test u2f routes: register", function () {
mocks.u2f = u2f_mock; mocks.u2f = u2f_mock;
return U2FRegisterPost.default(req as any, res as any) return U2FRegisterPost.default(req as any, res as any)
.then(function () { .then(function () {
assert.equal("user", userDataStore.set_u2f_meta.getCall(0).args[0]); assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]);
assert.equal(authSession.identity_check, undefined); assert.equal(authSession.identity_check, undefined);
}); });
}); });

View File

@ -4,11 +4,11 @@ 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 { ServerVariablesHandler } from "../../../../../../../src/server/lib/ServerVariablesHandler";
import winston = require("winston"); import winston = require("winston");
import ExpressMock = require("../../../../mocks/express"); import ExpressMock = require("../../../../mocks/express");
import UserDataStoreMock = require("../../../../mocks/UserDataStore"); import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
import U2FMock = require("../../../../mocks/u2f"); import U2FMock = require("../../../../mocks/u2f");
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
import U2f = require("u2f"); import U2f = require("u2f");
@ -16,8 +16,7 @@ import U2f = require("u2f");
describe("test u2f routes: register_request", function () { 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 mocks: ServerVariablesMock.ServerVariablesMock;
let mocks: ServerVariables;
let authSession: AuthenticationSession.AuthenticationSession; let authSession: AuthenticationSession.AuthenticationSession;
beforeEach(function () { beforeEach(function () {
@ -44,10 +43,9 @@ describe("test u2f routes: register_request", function () {
inMemoryOnly: true inMemoryOnly: true
}; };
userDataStore = UserDataStoreMock.UserDataStore();
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({})); mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
mocks.userDataStore = userDataStore as any;
res = ExpressMock.ResponseMock(); res = ExpressMock.ResponseMock();
res.send = sinon.spy(); res.send = sinon.spy();

Some files were not shown because too many files have changed in this diff Show More