mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
Add Mongo as scalable and resilient storage backend
This commit is contained in:
parent
b0e3038aa6
commit
e45ac39c8f
|
@ -77,9 +77,13 @@ session:
|
|||
host: redis
|
||||
port: 6379
|
||||
|
||||
# The directory where the DB files will be saved
|
||||
store_directory: /var/lib/authelia/store
|
||||
|
||||
storage:
|
||||
# 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
|
||||
# registration or a TOTP registration.
|
||||
|
|
6
example/mongo/docker-compose.yml
Normal file
6
example/mongo/docker-compose.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
version: '2'
|
||||
services:
|
||||
mongo:
|
||||
image: mongo:3.4
|
||||
networks:
|
||||
- example-network
|
|
@ -1,6 +1,6 @@
|
|||
version: '2'
|
||||
services:
|
||||
redis:
|
||||
image: redis
|
||||
image: redis:4.0-alpine
|
||||
networks:
|
||||
- example-network
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
"title": "Authelia API documentation"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/cors": "^2.8.1",
|
||||
"bluebird": "^3.4.7",
|
||||
"body-parser": "^1.15.2",
|
||||
"connect-redis": "^3.3.0",
|
||||
|
@ -33,6 +32,7 @@
|
|||
"express": "^4.14.0",
|
||||
"express-session": "^1.14.2",
|
||||
"ldapjs": "^1.0.1",
|
||||
"mongodb": "^2.2.30",
|
||||
"nedb": "^1.8.0",
|
||||
"nodemailer": "^4.0.1",
|
||||
"object-path": "^0.11.3",
|
||||
|
@ -47,6 +47,7 @@
|
|||
"@types/bluebird": "^3.5.4",
|
||||
"@types/body-parser": "^1.16.3",
|
||||
"@types/connect-redis": "0.0.6",
|
||||
"@types/cors": "^2.8.1",
|
||||
"@types/ejs": "^2.3.33",
|
||||
"@types/express": "^4.0.35",
|
||||
"@types/express-session": "0.0.32",
|
||||
|
@ -55,6 +56,7 @@
|
|||
"@types/ldapjs": "^1.0.0",
|
||||
"@types/mocha": "^2.2.41",
|
||||
"@types/mockdate": "^2.0.0",
|
||||
"@types/mongodb": "^2.2.7",
|
||||
"@types/nedb": "^1.8.3",
|
||||
"@types/nodemailer": "^1.3.32",
|
||||
"@types/object-path": "^0.9.28",
|
||||
|
@ -89,7 +91,7 @@
|
|||
"query-string": "^4.3.4",
|
||||
"request": "^2.81.0",
|
||||
"should": "^11.1.1",
|
||||
"sinon": "^1.17.6",
|
||||
"sinon": "^2.3.8",
|
||||
"sinon-promise": "^0.1.3",
|
||||
"tmp": "0.0.31",
|
||||
"ts-node": "^3.0.4",
|
||||
|
@ -107,7 +109,8 @@
|
|||
"doc",
|
||||
"src/types",
|
||||
"dist",
|
||||
"test"
|
||||
"test",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"extension": [
|
||||
".ts"
|
||||
|
|
|
@ -2,4 +2,12 @@
|
|||
|
||||
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 $*
|
||||
|
|
|
@ -2,4 +2,11 @@
|
|||
|
||||
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 $*
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
DC_SCRIPT=./scripts/example/dc-example.sh
|
||||
|
||||
$DC_SCRIPT build
|
||||
$DC_SCRIPT up -d redis openldap authelia nginx
|
||||
$DC_SCRIPT up -d mongo redis openldap authelia nginx
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
DC_SCRIPT=./scripts/example/dc-example.sh
|
||||
EXPECTED_SERVICES_COUNT=6
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -44,7 +45,7 @@ run_integration_tests() {
|
|||
$DC_SCRIPT logs authelia
|
||||
|
||||
echo "Check number of services"
|
||||
expect_services_count 5
|
||||
expect_services_count $EXPECTED_SERVICES_COUNT
|
||||
|
||||
echo "Run integration tests..."
|
||||
$DC_SCRIPT run --rm integration-tests
|
||||
|
@ -56,7 +57,7 @@ run_integration_tests() {
|
|||
run_system_tests() {
|
||||
echo "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
|
||||
shut_services
|
||||
|
@ -67,7 +68,7 @@ run_other_tests() {
|
|||
npm install --only=dev
|
||||
./node_modules/.bin/grunt build-dist
|
||||
./scripts/example/deploy-example.sh
|
||||
expect_services_count 4
|
||||
expect_services_count 5
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,39 +1,36 @@
|
|||
|
||||
import * as BluebirdPromise from "bluebird";
|
||||
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;
|
||||
|
||||
interface DatedDocument {
|
||||
date: Date;
|
||||
}
|
||||
|
||||
export class AuthenticationRegulator {
|
||||
private _user_data_store: any;
|
||||
private _lock_time_in_seconds: number;
|
||||
private userDataStore: UserDataStore;
|
||||
private lockTimeInSeconds: number;
|
||||
|
||||
constructor(user_data_store: any, lock_time_in_seconds: number) {
|
||||
this._user_data_store = user_data_store;
|
||||
this._lock_time_in_seconds = lock_time_in_seconds;
|
||||
constructor(userDataStore: any, lockTimeInSeconds: number) {
|
||||
this.userDataStore = userDataStore;
|
||||
this.lockTimeInSeconds = lockTimeInSeconds;
|
||||
}
|
||||
|
||||
// Mark authentication
|
||||
mark(userid: string, is_success: boolean): BluebirdPromise<void> {
|
||||
return this._user_data_store.save_authentication_trace(userid, REGULATION_TRACE_TYPE, is_success);
|
||||
mark(userId: string, isAuthenticationSuccessful: boolean): BluebirdPromise<void> {
|
||||
return this.userDataStore.saveAuthenticationTrace(userId, isAuthenticationSuccessful);
|
||||
}
|
||||
|
||||
regulate(userid: string): BluebirdPromise<void> {
|
||||
return this._user_data_store.get_last_authentication_traces(userid, REGULATION_TRACE_TYPE, false, 3)
|
||||
.then((docs: Array<DatedDocument>) => {
|
||||
regulate(userId: string): BluebirdPromise<void> {
|
||||
return this.userDataStore.retrieveLatestAuthenticationTraces(userId, false, 3)
|
||||
.then((docs: AuthenticationTraceDocument[]) => {
|
||||
if (docs.length < MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE) {
|
||||
// less than the max authorized number of authentication in time range, thus authorizing access
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
||||
|
||||
const oldest_doc = docs[MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE - 1];
|
||||
const no_lock_min_date = new Date(new Date().getTime() - this._lock_time_in_seconds * 1000);
|
||||
if (oldest_doc.date > no_lock_min_date) {
|
||||
const oldestDocument = docs[MAX_AUTHENTICATION_COUNT_IN_TIME_RANGE - 1];
|
||||
const noLockMinDate = new Date(new Date().getTime() - this.lockTimeInSeconds * 1000);
|
||||
if (oldestDocument.date > noLockMinDate) {
|
||||
throw new exceptions.AuthenticationRegulationError("Max number of authentication. Please retry in few minutes.");
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ import Exceptions = require("./Exceptions");
|
|||
import AuthenticationSession = require("./AuthenticationSession");
|
||||
|
||||
export function validate(req: express.Request): BluebirdPromise<void> {
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
if (!authSession.userid || !authSession.first_factor)
|
||||
return BluebirdPromise.reject(new Exceptions.FirstFactorValidationError("First factor has not been validated yet."));
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
if (!authSession.userid || !authSession.first_factor)
|
||||
return BluebirdPromise.reject(new Exceptions.FirstFactorValidationError("First factor has not been validated yet."));
|
||||
|
||||
return BluebirdPromise.resolve();
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
|
@ -6,15 +6,15 @@ import util = require("util");
|
|||
import Exceptions = require("./Exceptions");
|
||||
import fs = require("fs");
|
||||
import ejs = require("ejs");
|
||||
import UserDataStore from "./UserDataStore";
|
||||
import { IUserDataStore } from "./storage/IUserDataStore";
|
||||
import { Winston } from "../../types/Dependencies";
|
||||
import express = require("express");
|
||||
import ErrorReplies = require("./ErrorReplies");
|
||||
import ServerVariables = require("./ServerVariables");
|
||||
import { ServerVariablesHandler } from "./ServerVariablesHandler";
|
||||
import AuthenticationSession = require("./AuthenticationSession");
|
||||
|
||||
import Identity = require("../../types/Identity");
|
||||
import { IdentityValidationRequestContent } from "./UserDataStore";
|
||||
import { IdentityValidationDocument } from "./storage/IdentityValidationDocument";
|
||||
|
||||
const filePath = __dirname + "/../resources/email-template.ejs";
|
||||
const email_template = fs.readFileSync(filePath, "utf8");
|
||||
|
@ -33,21 +33,21 @@ export interface IdentityValidable {
|
|||
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 token = randomstring.generate({ length: 64 });
|
||||
const that = this;
|
||||
|
||||
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 () {
|
||||
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);
|
||||
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) {
|
||||
|
@ -63,8 +63,8 @@ function checkIdentityToken(req: express.Request, identityToken: string): Bluebi
|
|||
|
||||
export function get_finish_validation(handler: IdentityValidable): express.RequestHandler {
|
||||
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const logger = ServerVariables.getLogger(req.app);
|
||||
const userDataStore = ServerVariables.getUserDataStore(req.app);
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
|
||||
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
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);
|
||||
})
|
||||
.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 = {
|
||||
challenge: handler.challenge(),
|
||||
userid: content.userid
|
||||
userid: doc.userId
|
||||
};
|
||||
handler.postValidationResponse(req, res);
|
||||
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 {
|
||||
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const logger = ServerVariables.getLogger(req.app);
|
||||
const notifier = ServerVariables.getNotifier(req.app);
|
||||
const userDataStore = ServerVariables.getUserDataStore(req.app);
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const notifier = ServerVariablesHandler.getNotifier(req.app);
|
||||
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
|
||||
let identity: Identity.Identity;
|
||||
logger.info("Identity Validation: Start identity validation");
|
||||
|
||||
|
@ -104,13 +104,13 @@ export function get_start_validation(handler: IdentityValidable, postValidationE
|
|||
.then(function (id: Identity.Identity) {
|
||||
logger.debug("Identity Validation: retrieved identity is %s", JSON.stringify(id));
|
||||
identity = id;
|
||||
const email_address = objectPath.get<Identity.Identity, string>(identity, "email");
|
||||
const userid = objectPath.get<Identity.Identity, string>(identity, "userid");
|
||||
const email = identity.email;
|
||||
const userid = identity.userid;
|
||||
|
||||
if (!(email_address && userid))
|
||||
if (!(email && userid))
|
||||
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) {
|
||||
const host = req.get("Host");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
import express = require("express");
|
||||
import UserDataStore from "./UserDataStore";
|
||||
import { UserDataStore } from "./storage/UserDataStore";
|
||||
import { Winston } from "../../types/Dependencies";
|
||||
|
||||
import FirstFactorGet = require("./routes/firstfactor/get");
|
||||
|
@ -34,7 +34,7 @@ import Error404Get = require("./routes/error/404/get");
|
|||
|
||||
import Endpoints = require("../endpoints");
|
||||
|
||||
export default class RestApi {
|
||||
export class RestApi {
|
||||
static setup(app: express.Application): void {
|
||||
app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default);
|
||||
app.get(Endpoints.SECOND_FACTOR_GET, SecondFactorGet.default);
|
||||
|
|
|
@ -1,43 +1,54 @@
|
|||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import { AccessController } from "./access_control/AccessController";
|
||||
import { UserConfiguration } from "./../../types/Configuration";
|
||||
import { AppConfiguration, UserConfiguration } from "./configuration/Configuration";
|
||||
import { GlobalDependencies } from "../../types/Dependencies";
|
||||
import { AuthenticationRegulator } from "./AuthenticationRegulator";
|
||||
import UserDataStore from "./UserDataStore";
|
||||
import ConfigurationAdapter from "./ConfigurationAdapter";
|
||||
import { UserDataStore } from "./storage/UserDataStore";
|
||||
import { ConfigurationAdapter } from "./configuration/ConfigurationAdapter";
|
||||
import { TOTPValidator } from "./TOTPValidator";
|
||||
import { TOTPGenerator } from "./TOTPGenerator";
|
||||
import RestApi from "./RestApi";
|
||||
import { RestApi } from "./RestApi";
|
||||
import { Client } from "./ldap/Client";
|
||||
import BluebirdPromise = require("bluebird");
|
||||
import ServerVariables = require("./ServerVariables");
|
||||
import SessionConfigurationBuilder from "./SessionConfigurationBuilder";
|
||||
import { ServerVariablesHandler } from "./ServerVariablesHandler";
|
||||
import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder";
|
||||
|
||||
import * as Express from "express";
|
||||
import * as BodyParser from "body-parser";
|
||||
import * as Path from "path";
|
||||
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 {
|
||||
private httpServer: http.Server;
|
||||
|
||||
start(yamlConfiguration: UserConfiguration, deps: GlobalDependencies): BluebirdPromise<void> {
|
||||
const config = ConfigurationAdapter.adapt(yamlConfiguration);
|
||||
|
||||
private setupExpressApplication(config: AppConfiguration, app: Express.Application, deps: GlobalDependencies): void {
|
||||
const viewsDirectory = Path.resolve(__dirname, "../views");
|
||||
const publicHtmlDirectory = Path.resolve(__dirname, "../public_html");
|
||||
|
||||
const expressSessionOptions = SessionConfigurationBuilder.build(config, deps);
|
||||
|
||||
const app = Express();
|
||||
app.use(Express.static(publicHtmlDirectory));
|
||||
app.use(BodyParser.urlencoded({ extended: false }));
|
||||
app.use(BodyParser.json());
|
||||
app.use(deps.session(expressSessionOptions));
|
||||
|
||||
app.set("trust proxy", 1);
|
||||
app.set("views", viewsDirectory);
|
||||
app.set("view engine", "pug");
|
||||
app.set(TRUST_PROXY, 1);
|
||||
app.set(VIEWS, viewsDirectory);
|
||||
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
|
||||
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("Authelia configuration is %s", JSON.stringify(config, undefined, 2));
|
||||
return config;
|
||||
}
|
||||
|
||||
ServerVariables.fill(app, config, deps);
|
||||
RestApi.setup(app);
|
||||
private setup(config: AppConfiguration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise<void> {
|
||||
this.setupExpressApplication(config, app, deps);
|
||||
return ServerVariablesHandler.initialize(app, config, deps);
|
||||
}
|
||||
|
||||
private startServer(app: Express.Application, port: number) {
|
||||
return new BluebirdPromise<void>((resolve, reject) => {
|
||||
this.httpServer = app.listen(config.port, function (err: string) {
|
||||
console.log("Listening on %d...", config.port);
|
||||
this.httpServer = app.listen(port, function (err: string) {
|
||||
console.log("Listening on %d...", port);
|
||||
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() {
|
||||
this.httpServer.close();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
151
src/server/lib/ServerVariablesHandler.ts
Normal file
151
src/server/lib/ServerVariablesHandler.ts
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import { ACLConfiguration } from "../../../types/Configuration";
|
||||
import { ACLConfiguration } from "../configuration/Configuration";
|
||||
import PatternBuilder from "./PatternBuilder";
|
||||
import { Winston } from "../../../types/Dependencies";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
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");
|
||||
|
||||
export default class AccessControlPatternBuilder {
|
||||
|
|
|
@ -50,12 +50,26 @@ export interface NotifierConfiguration {
|
|||
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 {
|
||||
port?: number;
|
||||
logs_level?: string;
|
||||
ldap: LdapConfiguration;
|
||||
session: SessionCookieConfiguration;
|
||||
store_directory?: string;
|
||||
storage: StorageConfiguration;
|
||||
notifier: NotifierConfiguration;
|
||||
access_control?: ACLConfiguration;
|
||||
}
|
||||
|
@ -65,8 +79,7 @@ export interface AppConfiguration {
|
|||
logs_level: string;
|
||||
ldap: LdapConfiguration;
|
||||
session: SessionCookieConfiguration;
|
||||
store_in_memory?: boolean;
|
||||
store_directory?: string;
|
||||
storage: StorageConfiguration;
|
||||
notifier: NotifierConfiguration;
|
||||
access_control?: ACLConfiguration;
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
|
||||
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";
|
||||
|
||||
|
@ -34,14 +38,17 @@ function adaptFromUserConfiguration(userConfiguration: UserConfiguration): AppCo
|
|||
expiration: get_optional<number>(userConfiguration, "session.expiration", 3600000), // in ms
|
||||
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"),
|
||||
notifier: ObjectPath.get<object, NotifierConfiguration>(userConfiguration, "notifier"),
|
||||
access_control: ObjectPath.get<object, ACLConfiguration>(userConfiguration, "access_control")
|
||||
};
|
||||
}
|
||||
|
||||
export default class ConfigurationAdapter {
|
||||
export class ConfigurationAdapter {
|
||||
static adapt(userConfiguration: UserConfiguration): AppConfiguration {
|
||||
const appConfiguration = adaptFromUserConfiguration(userConfiguration);
|
||||
|
37
src/server/lib/configuration/SessionConfigurationBuilder.ts
Normal file
37
src/server/lib/configuration/SessionConfigurationBuilder.ts
Normal 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;
|
||||
}
|
||||
}
|
5
src/server/lib/connectors/mongo/IMongoClient.d.ts
vendored
Normal file
5
src/server/lib/connectors/mongo/IMongoClient.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import MongoDB = require("mongodb");
|
||||
|
||||
export interface IMongoClient {
|
||||
collection(name: string): MongoDB.Collection;
|
||||
}
|
6
src/server/lib/connectors/mongo/IMongoConnector.d.ts
vendored
Normal file
6
src/server/lib/connectors/mongo/IMongoConnector.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
import BluebirdPromise = require("bluebird");
|
||||
import { IMongoClient } from "./IMongoClient";
|
||||
|
||||
export interface IMongoConnector {
|
||||
connect(): BluebirdPromise<IMongoClient>;
|
||||
}
|
5
src/server/lib/connectors/mongo/IMongoConnectorFactory.d.ts
vendored
Normal file
5
src/server/lib/connectors/mongo/IMongoConnectorFactory.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { IMongoConnector } from "./IMongoConnector";
|
||||
|
||||
export interface IMongoConnectorFactory {
|
||||
create(url: string): IMongoConnector;
|
||||
}
|
15
src/server/lib/connectors/mongo/MongoClient.ts
Normal file
15
src/server/lib/connectors/mongo/MongoClient.ts
Normal 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);
|
||||
}
|
||||
}
|
22
src/server/lib/connectors/mongo/MongoConnector.ts
Normal file
22
src/server/lib/connectors/mongo/MongoConnector.ts
Normal 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));
|
||||
});
|
||||
}
|
||||
}
|
12
src/server/lib/connectors/mongo/MongoConnectorFactory.ts
Normal file
12
src/server/lib/connectors/mongo/MongoConnectorFactory.ts
Normal 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);
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import ldapjs = require("ldapjs");
|
|||
import { Client, Attributes } from "./Client";
|
||||
import { buildUserDN } from "./common";
|
||||
|
||||
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||
import { LdapConfiguration } from "../configuration/Configuration";
|
||||
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import ldapjs = require("ldapjs");
|
|||
import { buildUserDN } from "./common";
|
||||
|
||||
import { EventEmitter } from "events";
|
||||
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||
import { LdapConfiguration } from "../configuration/Configuration";
|
||||
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
|
||||
|
||||
interface SearchEntry {
|
||||
|
|
|
@ -4,7 +4,7 @@ import ldapjs = require("ldapjs");
|
|||
import { Client } from "./Client";
|
||||
import { buildUserDN } from "./common";
|
||||
|
||||
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||
import { LdapConfiguration } from "../configuration/Configuration";
|
||||
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import ldapjs = require("ldapjs");
|
|||
import { Client } from "./Client";
|
||||
import { buildUserDN } from "./common";
|
||||
|
||||
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||
import { LdapConfiguration } from "../configuration/Configuration";
|
||||
import { Winston, Ldapjs, Dovehash } from "../../../types/Dependencies";
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import util = require("util");
|
||||
|
||||
import { LdapConfiguration } from "./../../../types/Configuration";
|
||||
import { LdapConfiguration } from "../configuration/Configuration";
|
||||
|
||||
|
||||
export function buildUserDN(username: string, options: LdapConfiguration): string {
|
||||
|
|
|
@ -5,13 +5,12 @@ import * as fs from "fs";
|
|||
import { INotifier } from "./INotifier";
|
||||
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;
|
||||
|
||||
constructor(options: FileSystemNotifierConfiguration) {
|
||||
super();
|
||||
this.filename = options.filename;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,16 +7,15 @@ import nodemailer = require("nodemailer");
|
|||
import { Nodemailer } from "../../../types/Dependencies";
|
||||
import { Identity } from "../../../types/Identity";
|
||||
import { INotifier } from "../notifiers/INotifier";
|
||||
import { GmailNotifierConfiguration } from "../../../types/Configuration";
|
||||
import { GmailNotifierConfiguration } from "../configuration/Configuration";
|
||||
import path = require("path");
|
||||
|
||||
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;
|
||||
|
||||
constructor(options: GmailNotifierConfiguration, nodemailer: Nodemailer) {
|
||||
super();
|
||||
const transporter = nodemailer.createTransport({
|
||||
service: "gmail",
|
||||
auth: {
|
||||
|
|
7
src/server/lib/notifiers/INotifier.d.ts
vendored
Normal file
7
src/server/lib/notifiers/INotifier.d.ts
vendored
Normal 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>;
|
||||
}
|
|
@ -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>;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import { NotifierConfiguration } from "../../../types/Configuration";
|
||||
import { NotifierConfiguration } from "../configuration/Configuration";
|
||||
import { Nodemailer } from "../../../types/Dependencies";
|
||||
import { INotifier } from "./INotifier";
|
||||
|
||||
|
|
|
@ -5,14 +5,14 @@ import FirstFactorValidator = require("../FirstFactorValidator");
|
|||
import Exceptions = require("../Exceptions");
|
||||
import ErrorReplies = require("../ErrorReplies");
|
||||
import objectPath = require("object-path");
|
||||
import ServerVariables = require("../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../AuthenticationSession");
|
||||
|
||||
type Handler = (req: express.Request, res: express.Response) => BluebirdPromise<void>;
|
||||
|
||||
export default function (callback: Handler): Handler {
|
||||
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);
|
||||
logger.debug("AuthSession is %s", JSON.stringify(authSession));
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
|
||||
import BluebirdPromise = require("bluebird");
|
||||
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");
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
|
||||
import BluebirdPromise = require("bluebird");
|
||||
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");
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
|
||||
import BluebirdPromise = require("bluebird");
|
||||
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");
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
|
@ -4,10 +4,10 @@ import objectPath = require("object-path");
|
|||
import winston = require("winston");
|
||||
import Endpoints = require("../../../endpoints");
|
||||
import AuthenticationValidator = require("../../AuthenticationValidator");
|
||||
import ServerVariables = require("../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||
|
||||
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));
|
||||
|
||||
|
|
|
@ -8,16 +8,16 @@ import { AuthenticationRegulator } from "../../AuthenticationRegulator";
|
|||
import { Client, Attributes } from "../../ldap/Client";
|
||||
import Endpoint = require("../../../endpoints");
|
||||
import ErrorReplies = require("../../ErrorReplies");
|
||||
import ServerVariables = require("../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../AuthenticationSession");
|
||||
|
||||
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const username: string = req.body.username;
|
||||
const password: string = req.body.password;
|
||||
|
||||
const logger = ServerVariables.getLogger(req.app);
|
||||
const ldap = ServerVariables.getLdapAuthenticator(req.app);
|
||||
const config = ServerVariables.getConfiguration(req.app);
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const ldap = ServerVariablesHandler.getLdapAuthenticator(req.app);
|
||||
const config = ServerVariablesHandler.getConfiguration(req.app);
|
||||
|
||||
if (!username || !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);
|
||||
}
|
||||
|
||||
const regulator = ServerVariables.getAuthenticationRegulator(req.app);
|
||||
const accessController = ServerVariables.getAccessController(req.app);
|
||||
const regulator = ServerVariablesHandler.getAuthenticationRegulator(req.app);
|
||||
const accessController = ServerVariablesHandler.getAccessController(req.app);
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
|
||||
logger.info("1st factor: Starting authentication of user \"%s\"", username);
|
||||
|
|
|
@ -3,15 +3,15 @@ import express = require("express");
|
|||
import BluebirdPromise = require("bluebird");
|
||||
import objectPath = require("object-path");
|
||||
import exceptions = require("../../../Exceptions");
|
||||
import ServerVariables = require("../../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../AuthenticationSession");
|
||||
import ErrorReplies = require("../../../ErrorReplies");
|
||||
|
||||
import Constants = require("./../constants");
|
||||
|
||||
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const logger = ServerVariables.getLogger(req.app);
|
||||
const ldapPasswordUpdater = ServerVariables.getLdapPasswordUpdater(req.app);
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const ldapPasswordUpdater = ServerVariablesHandler.getLdapPasswordUpdater(req.app);
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
|
||||
const newPassword = objectPath.get<express.Request, string>(req, "body.password");
|
||||
|
|
|
@ -8,7 +8,7 @@ import { IdentityValidable } from "../../../IdentityCheckMiddleware";
|
|||
import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate";
|
||||
import Constants = require("../constants");
|
||||
import { Winston } from "winston";
|
||||
import ServerVariables = require("../../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../../ServerVariablesHandler";
|
||||
|
||||
export const TEMPLATE_NAME = "password-reset-form";
|
||||
|
||||
|
@ -18,14 +18,14 @@ export default class PasswordResetHandler implements IdentityValidable {
|
|||
}
|
||||
|
||||
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");
|
||||
|
||||
logger.debug("Reset Password: user '%s' requested a password reset", userid);
|
||||
if (!userid)
|
||||
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)
|
||||
.then(function (emails: string[]) {
|
||||
if (!emails && emails.length <= 0) throw new Error("No email found");
|
||||
|
|
|
@ -3,7 +3,7 @@ import express = require("express");
|
|||
import objectPath = require("object-path");
|
||||
import winston = require("winston");
|
||||
import Endpoints = require("../../../endpoints");
|
||||
import ServerVariables = require("../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../AuthenticationSession");
|
||||
|
||||
export default function (req: express.Request, res: express.Response) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationT
|
|||
import Constants = require("../constants");
|
||||
import Endpoints = require("../../../../../endpoints");
|
||||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import ServerVariables = require("../../../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
|
||||
import FirstFactorValidator = require("../../../../FirstFactorValidator");
|
||||
|
@ -53,7 +53,7 @@ export default class RegistrationHandler implements IdentityValidable {
|
|||
}
|
||||
|
||||
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 userid = authSession.identity_check.userid;
|
||||
|
@ -65,12 +65,12 @@ export default class RegistrationHandler implements IdentityValidable {
|
|||
return;
|
||||
}
|
||||
|
||||
const userDataStore = ServerVariables.getUserDataStore(req.app);
|
||||
const totpGenerator = ServerVariables.getTOTPGenerator(req.app);
|
||||
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
|
||||
const totpGenerator = ServerVariablesHandler.getTOTPGenerator(req.app);
|
||||
const secret = totpGenerator.generate();
|
||||
|
||||
logger.debug("POST new-totp-secret: save the TOTP secret in DB");
|
||||
userDataStore.set_totp_secret(userid, secret)
|
||||
userDataStore.saveTOTPSecret(userid, secret)
|
||||
.then(function () {
|
||||
objectPath.set(req, "session", undefined);
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
import exceptions = require("../../../../Exceptions");
|
||||
import objectPath = require("object-path");
|
||||
import express = require("express");
|
||||
import { TOTPSecretDocument } from "../../../../UserDataStore";
|
||||
import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument";
|
||||
import BluebirdPromise = require("bluebird");
|
||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
||||
import Endpoints = require("../../../../../endpoints");
|
||||
import redirect from "../../redirect";
|
||||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import ServerVariables = require("./../../../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "./../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
|
||||
const UNAUTHORIZED_MESSAGE = "Unauthorized access";
|
||||
|
@ -16,17 +16,17 @@ const UNAUTHORIZED_MESSAGE = "Unauthorized access";
|
|||
export default FirstFactorBlocker(handler);
|
||||
|
||||
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 userid = authSession.userid;
|
||||
logger.info("POST 2ndfactor totp: Initiate TOTP validation for user %s", userid);
|
||||
|
||||
const token = req.body.token;
|
||||
const totpValidator = ServerVariables.getTOTPValidator(req.app);
|
||||
const userDataStore = ServerVariables.getUserDataStore(req.app);
|
||||
const totpValidator = ServerVariablesHandler.getTOTPValidator(req.app);
|
||||
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
|
||||
|
||||
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) {
|
||||
logger.debug("POST 2ndfactor totp: TOTP secret is %s", JSON.stringify(doc));
|
||||
return totpValidator.validate(token, doc.secret.base32);
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
|
||||
import UserDataStore from "../../../../UserDataStore";
|
||||
import { UserDataStore } from "../../../../storage/UserDataStore";
|
||||
|
||||
import objectPath = require("object-path");
|
||||
import u2f_common = require("../U2FCommon");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
import express = require("express");
|
||||
import U2f = require("u2f");
|
||||
import { U2FRegistration } from "../../../../../../types/U2FRegistration";
|
||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
||||
import redirect from "../../redirect";
|
||||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import ServerVariables = require("../../../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
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 u2f = ServerVariables.getU2F(req.app);
|
||||
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
|
||||
const u2f = ServerVariablesHandler.getU2F(req.app);
|
||||
const userid = authSession.userid;
|
||||
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;
|
||||
|
||||
|
@ -54,7 +55,11 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
|
|||
const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult;
|
||||
logger.info("U2F register: Store regisutration and reply");
|
||||
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 () {
|
||||
authSession.identity_check = undefined;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import UserDataStore from "../../../../UserDataStore";
|
||||
import { UserDataStore } from "../../../../storage/UserDataStore";
|
||||
|
||||
import objectPath = require("object-path");
|
||||
import u2f_common = require("../U2FCommon");
|
||||
|
@ -8,13 +8,13 @@ import express = require("express");
|
|||
import U2f = require("u2f");
|
||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
||||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import ServerVariables = require("../../../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
|
||||
export default FirstFactorBlocker(handler);
|
||||
|
||||
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);
|
||||
|
||||
if (!authSession.identity_check
|
||||
|
@ -24,7 +24,7 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
|
|||
return;
|
||||
}
|
||||
|
||||
const u2f = ServerVariables.getU2F(req.app);
|
||||
const u2f = ServerVariablesHandler.getU2F(req.app);
|
||||
const appid: string = u2f_common.extract_app_id(req);
|
||||
|
||||
logger.debug("U2F register_request: headers=%s", JSON.stringify(req.headers));
|
||||
|
|
|
@ -3,49 +3,49 @@ import objectPath = require("object-path");
|
|||
import u2f_common = require("../U2FCommon");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
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 U2f = require("u2f");
|
||||
import exceptions = require("../../../../Exceptions");
|
||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
||||
import redirect from "../../redirect";
|
||||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import ServerVariables = require("../../../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
|
||||
export default FirstFactorBlocker(handler);
|
||||
|
||||
|
||||
export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const logger = ServerVariables.getLogger(req.app);
|
||||
const userDataStore = ServerVariables.getUserDataStore(req.app);
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
|
||||
if (!authSession.sign_request) {
|
||||
const err = new Error("No sign request");
|
||||
ErrorReplies.replyWithError401(res, logger)(err);
|
||||
return BluebirdPromise.reject(err);
|
||||
}
|
||||
if (!authSession.sign_request) {
|
||||
const err = new Error("No sign request");
|
||||
ErrorReplies.replyWithError401(res, logger)(err);
|
||||
return BluebirdPromise.reject(err);
|
||||
}
|
||||
|
||||
const userid = authSession.userid;
|
||||
const appid = u2f_common.extract_app_id(req);
|
||||
return userDataStore.get_u2f_meta(userid, appid)
|
||||
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<U2f.SignatureResult | U2f.Error> {
|
||||
const appid = u2f_common.extract_app_id(req);
|
||||
const u2f = ServerVariables.getU2F(req.app);
|
||||
const signRequest = authSession.sign_request;
|
||||
const signData: U2f.SignatureData = req.body;
|
||||
logger.info("U2F sign: Finish authentication");
|
||||
return BluebirdPromise.resolve(u2f.checkSignature(signRequest, signData, doc.publicKey));
|
||||
})
|
||||
.then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise<void> {
|
||||
if (objectPath.has(result, "errorCode"))
|
||||
return BluebirdPromise.reject(new Error("Error while signing"));
|
||||
logger.info("U2F sign: Authentication successful");
|
||||
authSession.second_factor = true;
|
||||
redirect(req, res);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(ErrorReplies.replyWithError500(res, logger));
|
||||
const userid = authSession.userid;
|
||||
const appid = u2f_common.extract_app_id(req);
|
||||
return userDataStore.retrieveU2FRegistration(userid, appid)
|
||||
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<U2f.SignatureResult | U2f.Error> {
|
||||
const appId = u2f_common.extract_app_id(req);
|
||||
const u2f = ServerVariablesHandler.getU2F(req.app);
|
||||
const signRequest = authSession.sign_request;
|
||||
const signData: U2f.SignatureData = req.body;
|
||||
logger.info("U2F sign: Finish authentication");
|
||||
return BluebirdPromise.resolve(u2f.checkSignature(signRequest, signData, doc.registration.publicKey));
|
||||
})
|
||||
.then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise<void> {
|
||||
if (objectPath.has(result, "errorCode"))
|
||||
return BluebirdPromise.reject(new Error("Error while signing"));
|
||||
logger.info("U2F sign: Authentication successful");
|
||||
authSession.second_factor = true;
|
||||
redirect(req, res);
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(ErrorReplies.replyWithError500(res, logger));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,39 +4,39 @@ import U2f = require("u2f");
|
|||
import u2f_common = require("../../../secondfactor/u2f/U2FCommon");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
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 exceptions = require("../../../../Exceptions");
|
||||
import { SignMessage } from "./SignMessage";
|
||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
||||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import ServerVariables = require("../../../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
||||
|
||||
export default FirstFactorBlocker(handler);
|
||||
|
||||
|
||||
export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const logger = ServerVariables.getLogger(req.app);
|
||||
const userDataStore = ServerVariables.getUserDataStore(req.app);
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
|
||||
const userid = authSession.userid;
|
||||
const appid = u2f_common.extract_app_id(req);
|
||||
return userDataStore.get_u2f_meta(userid, appid)
|
||||
const userId = authSession.userid;
|
||||
const appId = u2f_common.extract_app_id(req);
|
||||
return userDataStore.retrieveU2FRegistration(userId, appId)
|
||||
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<SignMessage> {
|
||||
if (!doc)
|
||||
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);
|
||||
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 = {
|
||||
request: request,
|
||||
keyHandle: doc.keyHandle
|
||||
keyHandle: doc.registration.keyHandle
|
||||
};
|
||||
return BluebirdPromise.resolve(authenticationMessage);
|
||||
})
|
||||
|
|
|
@ -8,12 +8,12 @@ import exceptions = require("../../Exceptions");
|
|||
import winston = require("winston");
|
||||
import AuthenticationValidator = require("../../AuthenticationValidator");
|
||||
import ErrorReplies = require("../../ErrorReplies");
|
||||
import ServerVariables = require("../../ServerVariables");
|
||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../AuthenticationSession");
|
||||
|
||||
function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const logger = ServerVariables.getLogger(req.app);
|
||||
const accessController = ServerVariables.getAccessController(req.app);
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const accessController = ServerVariablesHandler.getAccessController(req.app);
|
||||
const authSession = AuthenticationSession.get(req);
|
||||
|
||||
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) {
|
||||
const logger = ServerVariables.getLogger(req.app);
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
verify_filter(req, res)
|
||||
.then(function () {
|
||||
res.status(204);
|
||||
|
|
6
src/server/lib/storage/AuthenticationTraceDocument.d.ts
vendored
Normal file
6
src/server/lib/storage/AuthenticationTraceDocument.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
export interface AuthenticationTraceDocument {
|
||||
userId: string;
|
||||
date: Date;
|
||||
isAuthenticationSuccessful: boolean;
|
||||
}
|
15
src/server/lib/storage/CollectionFactoryFactory.ts
Normal file
15
src/server/lib/storage/CollectionFactoryFactory.ts
Normal 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
11
src/server/lib/storage/ICollection.d.ts
vendored
Normal 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>;
|
||||
}
|
6
src/server/lib/storage/ICollectionFactory.d.ts
vendored
Normal file
6
src/server/lib/storage/ICollectionFactory.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
import { ICollection } from "./ICollection";
|
||||
|
||||
export interface ICollectionFactory {
|
||||
build(collectionName: string): ICollection;
|
||||
}
|
21
src/server/lib/storage/IUserDataStore.d.ts
vendored
Normal file
21
src/server/lib/storage/IUserDataStore.d.ts
vendored
Normal 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>;
|
||||
}
|
7
src/server/lib/storage/IdentityValidationDocument.d.ts
vendored
Normal file
7
src/server/lib/storage/IdentityValidationDocument.d.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
export interface IdentityValidationDocument {
|
||||
userId: string;
|
||||
token: string;
|
||||
challenge: string;
|
||||
maxDate: Date;
|
||||
}
|
6
src/server/lib/storage/TOTPSecretDocument.d.ts
vendored
Normal file
6
src/server/lib/storage/TOTPSecretDocument.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { TOTPSecret } from "../../../types/TOTPSecret";
|
||||
|
||||
export interface TOTPSecretDocument {
|
||||
userid: string;
|
||||
secret: TOTPSecret;
|
||||
}
|
8
src/server/lib/storage/U2FRegistrationDocument.d.ts
vendored
Normal file
8
src/server/lib/storage/U2FRegistrationDocument.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
import { U2FRegistration } from "../../../types/U2FRegistration";
|
||||
|
||||
export interface U2FRegistrationDocument {
|
||||
userId: string;
|
||||
appId: string;
|
||||
registration: U2FRegistration;
|
||||
}
|
144
src/server/lib/storage/UserDataStore.ts
Normal file
144
src/server/lib/storage/UserDataStore.ts
Normal 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);
|
||||
}
|
||||
}
|
44
src/server/lib/storage/mongo/MongoCollection.ts
Normal file
44
src/server/lib/storage/mongo/MongoCollection.ts
Normal 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);
|
||||
}
|
||||
}
|
19
src/server/lib/storage/mongo/MongoCollectionFactory.ts
Normal file
19
src/server/lib/storage/mongo/MongoCollectionFactory.ts
Normal 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));
|
||||
}
|
||||
}
|
38
src/server/lib/storage/nedb/NedbCollection.ts
Normal file
38
src/server/lib/storage/nedb/NedbCollection.ts
Normal 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);
|
||||
}
|
||||
}
|
28
src/server/lib/storage/nedb/NedbCollectionFactory.ts
Normal file
28
src/server/lib/storage/nedb/NedbCollectionFactory.ts
Normal 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);
|
||||
}
|
||||
}
|
5
src/types/U2FRegistration.ts
Normal file
5
src/types/U2FRegistration.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
export interface U2FRegistration {
|
||||
keyHandle: string;
|
||||
publicKey: string;
|
||||
}
|
3
src/types/nedb-async.d.ts
vendored
3
src/types/nedb-async.d.ts
vendored
|
@ -5,8 +5,9 @@ declare module "nedb" {
|
|||
export class NedbAsync extends Nedb {
|
||||
constructor(pathOrOptions?: string | Nedb.DataStoreOptions);
|
||||
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>;
|
||||
removeAsync(query: any): BluebirdPromise<any>;
|
||||
countAsync(query: any): BluebirdPromise<number>
|
||||
}
|
||||
}
|
|
@ -1,73 +1,120 @@
|
|||
|
||||
import { AuthenticationRegulator } from "../../../src/server/lib/AuthenticationRegulator";
|
||||
import UserDataStore from "../../../src/server/lib/UserDataStore";
|
||||
import Sinon = require("sinon");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import { AuthenticationRegulator } from "../../../src/server/lib/AuthenticationRegulator";
|
||||
import { UserDataStore } from "../../../src/server/lib/storage/UserDataStore";
|
||||
import MockDate = require("mockdate");
|
||||
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() {
|
||||
it("should mark 2 authentication and regulate (resolve)", function() {
|
||||
const options = {
|
||||
inMemoryOnly: true
|
||||
};
|
||||
const data_store = new UserDataStore(options, nedb);
|
||||
const regulator = new AuthenticationRegulator(data_store, 10);
|
||||
const user = "user";
|
||||
describe("test authentication regulator", function () {
|
||||
let collectionFactory: CollectionFactoryStub;
|
||||
let collection: CollectionStub;
|
||||
|
||||
beforeEach(function () {
|
||||
collectionFactory = new CollectionFactoryStub();
|
||||
collection = new CollectionStub();
|
||||
|
||||
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)
|
||||
.then(function() {
|
||||
return regulator.mark(user, true);
|
||||
})
|
||||
.then(function() {
|
||||
return regulator.regulate(user);
|
||||
});
|
||||
.then(function () {
|
||||
return regulator.mark(user, true);
|
||||
})
|
||||
.then(function () {
|
||||
return regulator.regulate(user);
|
||||
});
|
||||
});
|
||||
|
||||
it("should mark 3 authentications and regulate (reject)", function(done) {
|
||||
const options = {
|
||||
inMemoryOnly: true
|
||||
};
|
||||
const data_store = new UserDataStore(options, nedb);
|
||||
const regulator = new AuthenticationRegulator(data_store, 10);
|
||||
const user = "user";
|
||||
it("should mark 3 authentications and regulate (reject)", function (done) {
|
||||
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: false
|
||||
}, {
|
||||
userId: user,
|
||||
date: new Date(),
|
||||
isAuthenticationSuccessful: false
|
||||
}]));
|
||||
|
||||
const dataStore = new UserDataStore(collectionFactory);
|
||||
const regulator = new AuthenticationRegulator(dataStore, 10);
|
||||
|
||||
regulator.mark(user, false)
|
||||
.then(function() {
|
||||
return regulator.mark(user, false);
|
||||
})
|
||||
.then(function() {
|
||||
return regulator.mark(user, false);
|
||||
})
|
||||
.then(function() {
|
||||
return regulator.regulate(user);
|
||||
})
|
||||
.catch(exceptions.AuthenticationRegulationError, function() {
|
||||
done();
|
||||
});
|
||||
.then(function () {
|
||||
return regulator.mark(user, false);
|
||||
})
|
||||
.then(function () {
|
||||
return regulator.mark(user, false);
|
||||
})
|
||||
.then(function () {
|
||||
return regulator.regulate(user);
|
||||
})
|
||||
.catch(exceptions.AuthenticationRegulationError, function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should mark 3 authentications and regulate (resolve)", function(done) {
|
||||
const options = {
|
||||
inMemoryOnly: true
|
||||
};
|
||||
const data_store = new UserDataStore(options, nedb);
|
||||
it("should mark 3 authentications separated by a lot of time and allow access to user", function (done) {
|
||||
const user = "USER";
|
||||
collection.insertStub.returns(BluebirdPromise.resolve());
|
||||
collection.findStub.returns(BluebirdPromise.resolve([{
|
||||
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 user = "user";
|
||||
|
||||
MockDate.set("1/2/2000 00:00:00");
|
||||
regulator.mark(user, false)
|
||||
.then(function() {
|
||||
MockDate.set("1/2/2000 00:00:15");
|
||||
return regulator.mark(user, false);
|
||||
})
|
||||
.then(function() {
|
||||
return regulator.mark(user, false);
|
||||
})
|
||||
.then(function() {
|
||||
return regulator.regulate(user);
|
||||
})
|
||||
.then(function() {
|
||||
done();
|
||||
});
|
||||
.then(function () {
|
||||
MockDate.set("1/2/2000 00:00:15");
|
||||
return regulator.mark(user, false);
|
||||
})
|
||||
.then(function () {
|
||||
MockDate.set("1/2/2000 06:00:15");
|
||||
return regulator.mark(user, false);
|
||||
})
|
||||
.then(function () {
|
||||
return regulator.regulate(user);
|
||||
})
|
||||
.then(function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
import * as Assert from "assert";
|
||||
import { UserConfiguration } from "../../../src/types/Configuration";
|
||||
import ConfigurationAdapter from "../../../src/server/lib/ConfigurationAdapter";
|
||||
import { UserConfiguration } from "../../../src/server/lib/configuration/Configuration";
|
||||
import { ConfigurationAdapter } from "../../../src/server/lib/configuration/ConfigurationAdapter";
|
||||
|
||||
describe("test config adapter", function() {
|
||||
function build_yaml_config(): UserConfiguration {
|
||||
|
@ -17,7 +17,11 @@ describe("test config adapter", function() {
|
|||
secret: "secret",
|
||||
max_age: 40000
|
||||
},
|
||||
store_directory: "/mydirectory",
|
||||
storage: {
|
||||
local: {
|
||||
path: "/mydirectory"
|
||||
}
|
||||
},
|
||||
logs_level: "debug",
|
||||
notifier: {
|
||||
gmail: {
|
||||
|
|
|
@ -3,7 +3,7 @@ import * as BluebirdPromise from "bluebird";
|
|||
import * as request from "request";
|
||||
|
||||
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 * as tmp from "tmp";
|
||||
import U2FMock = require("./mocks/u2f");
|
||||
|
@ -70,7 +70,11 @@ describe("test data persistence", function () {
|
|||
secret: "session_secret",
|
||||
expiration: 50000,
|
||||
},
|
||||
store_directory: tmpDir.name,
|
||||
storage: {
|
||||
local: {
|
||||
path: tmpDir.name
|
||||
}
|
||||
},
|
||||
notifier: {
|
||||
gmail: {
|
||||
username: "user@example.com",
|
||||
|
|
|
@ -2,24 +2,24 @@
|
|||
import sinon = require("sinon");
|
||||
import IdentityValidator = require("../../../src/server/lib/IdentityCheckMiddleware");
|
||||
import AuthenticationSession = require("../../../src/server/lib/AuthenticationSession");
|
||||
import { UserDataStore } from "../../../src/server/lib/storage/UserDataStore";
|
||||
|
||||
import exceptions = require("../../../src/server/lib/Exceptions");
|
||||
import assert = require("assert");
|
||||
import winston = require("winston");
|
||||
import Promise = require("bluebird");
|
||||
import express = require("express");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import ExpressMock = require("./mocks/express");
|
||||
import UserDataStoreMock = require("./mocks/UserDataStore");
|
||||
import NotifierMock = require("./mocks/Notifier");
|
||||
import IdentityValidatorMock = require("./mocks/IdentityValidator");
|
||||
import ServerVariablesMock = require("./mocks/ServerVariablesMock");
|
||||
|
||||
|
||||
describe("test identity check process", function () {
|
||||
let mocks: ServerVariablesMock.ServerVariablesMock;
|
||||
let req: ExpressMock.RequestMock;
|
||||
let res: ExpressMock.ResponseMock;
|
||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||
let notifier: NotifierMock.NotifierMock;
|
||||
let app: express.Application;
|
||||
let app_get: sinon.SinonStub;
|
||||
|
@ -32,12 +32,6 @@ describe("test identity check process", function () {
|
|||
|
||||
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.notify = sinon.stub().returns(Promise.resolve());
|
||||
|
||||
|
@ -47,11 +41,13 @@ describe("test identity check process", function () {
|
|||
|
||||
req.query = {};
|
||||
req.app = {};
|
||||
const mocks = ServerVariablesMock.mock(req.app);
|
||||
mocks.logger = winston;
|
||||
mocks.userDataStore = userDataStore as any;
|
||||
|
||||
mocks = ServerVariablesMock.mock(req.app);
|
||||
mocks.notifier = notifier;
|
||||
|
||||
mocks.userDataStore.produceIdentityValidationTokenStub.returns(Promise.resolve());
|
||||
mocks.userDataStore.consumeIdentityValidationTokenStub.returns(Promise.resolve({ userId: "user" }));
|
||||
|
||||
app = express();
|
||||
app_get = sinon.stub(app, "get");
|
||||
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)
|
||||
.then(function () {
|
||||
assert(notifier.notify.calledOnce);
|
||||
assert(userDataStore.issue_identity_check_token.calledOnce);
|
||||
assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[0], "user");
|
||||
assert.equal(userDataStore.issue_identity_check_token.getCall(0).args[3], 240000);
|
||||
assert(mocks.userDataStore.produceIdentityValidationTokenStub.calledOnce);
|
||||
assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[0], "user");
|
||||
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 () {
|
||||
req.query.identity_token = "token";
|
||||
|
||||
userDataStore.consume_identity_check_token
|
||||
.returns(BluebirdPromise.reject(new Error("Invalid token")));
|
||||
mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.reject(new Error("Invalid token")));
|
||||
|
||||
const callback = IdentityValidator.get_finish_validation(identityValidable);
|
||||
return callback(req as any, res as any, undefined)
|
||||
|
|
|
@ -9,7 +9,7 @@ import u2f = require("u2f");
|
|||
import nodemailer = require("nodemailer");
|
||||
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 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 () {
|
||||
const config = {
|
||||
const config: UserConfiguration = {
|
||||
session: {
|
||||
domain: "example.com",
|
||||
secret: "secret"
|
||||
|
@ -58,15 +58,21 @@ describe("test server configuration", function () {
|
|||
ldap: {
|
||||
url: "http://ldap",
|
||||
user: "user",
|
||||
password: "password"
|
||||
password: "password",
|
||||
base_dn: "dc=example,dc=com"
|
||||
},
|
||||
notifier: {
|
||||
gmail: {
|
||||
username: "user@example.com",
|
||||
password: "password"
|
||||
}
|
||||
},
|
||||
storage: {
|
||||
local: {
|
||||
in_memory: true
|
||||
}
|
||||
}
|
||||
} as UserConfiguration;
|
||||
};
|
||||
|
||||
const server = new Server();
|
||||
server.start(config, deps);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import SessionConfigurationBuilder from "../../../src/server/lib/SessionConfigurationBuilder";
|
||||
import { AppConfiguration } from "../../../src/types/Configuration";
|
||||
import { SessionConfigurationBuilder } from "../../../src/server/lib/configuration/SessionConfigurationBuilder";
|
||||
import { AppConfiguration } from "../../../src/server/lib/configuration/Configuration";
|
||||
import { GlobalDependencies } from "../../../src/types/Dependencies";
|
||||
|
||||
import ExpressSession = require("express-session");
|
||||
import ConnectRedis = require("connect-redis");
|
||||
import Sinon = require("sinon");
|
||||
|
@ -32,7 +33,11 @@ describe("test session configuration builder", function () {
|
|||
expiration: 3600,
|
||||
secret: "secret"
|
||||
},
|
||||
store_in_memory: true
|
||||
storage: {
|
||||
local: {
|
||||
in_memory: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const deps: GlobalDependencies = {
|
||||
|
@ -92,7 +97,11 @@ describe("test session configuration builder", function () {
|
|||
port: 6379
|
||||
}
|
||||
},
|
||||
store_in_memory: true
|
||||
storage: {
|
||||
local: {
|
||||
in_memory: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const RedisStoreMock = Sinon.spy();
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,7 +2,7 @@
|
|||
import assert = require("assert");
|
||||
import winston = require("winston");
|
||||
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 () {
|
||||
let accessController: AccessController;
|
||||
|
|
|
@ -3,7 +3,7 @@ import assert = require("assert");
|
|||
import winston = require("winston");
|
||||
|
||||
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 pattern builder when no configuration is provided", () => {
|
||||
|
|
38
test/unit/server/connectors/mongo/MongoClient.test.ts
Normal file
38
test/unit/server/connectors/mongo/MongoClient.test.ts
Normal 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 ));
|
||||
});
|
||||
});
|
||||
});
|
45
test/unit/server/connectors/mongo/MongoConnector.test.ts
Normal file
45
test/unit/server/connectors/mongo/MongoConnector.test.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
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 BluebirdPromise = require("bluebird");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
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 BluebirdPromise = require("bluebird");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
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 BluebirdPromise = require("bluebird");
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import sinon = require("sinon");
|
||||
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 {
|
||||
logger: any;
|
||||
ldap: any;
|
||||
ldapAuthenticator: any;
|
||||
ldapEmailsRetriever: any;
|
||||
ldapPasswordUpdater: any;
|
||||
totpValidator: any;
|
||||
totpGenerator: any;
|
||||
u2f: any;
|
||||
userDataStore: any;
|
||||
userDataStore: UserDataStoreStub;
|
||||
notifier: any;
|
||||
regulator: any;
|
||||
config: any;
|
||||
|
@ -16,20 +20,20 @@ export interface ServerVariablesMock {
|
|||
}
|
||||
|
||||
|
||||
export function mock(app: express.Application): ServerVariables {
|
||||
const mocks: ServerVariables = {
|
||||
accessController: sinon.stub() as any,
|
||||
config: sinon.stub() as any,
|
||||
export function mock(app: express.Application): ServerVariablesMock {
|
||||
const mocks: ServerVariablesMock = {
|
||||
accessController: sinon.stub(),
|
||||
config: sinon.stub(),
|
||||
ldapAuthenticator: sinon.stub() as any,
|
||||
ldapEmailsRetriever: sinon.stub() as any,
|
||||
ldapPasswordUpdater: sinon.stub() as any,
|
||||
logger: sinon.stub() as any,
|
||||
notifier: sinon.stub() as any,
|
||||
regulator: sinon.stub() as any,
|
||||
totpGenerator: sinon.stub() as any,
|
||||
totpValidator: sinon.stub() as any,
|
||||
u2f: sinon.stub() as any,
|
||||
userDataStore: sinon.stub() as any,
|
||||
logger: winston,
|
||||
notifier: sinon.stub(),
|
||||
regulator: sinon.stub(),
|
||||
totpGenerator: sinon.stub(),
|
||||
totpValidator: sinon.stub(),
|
||||
u2f: sinon.stub(),
|
||||
userDataStore: new UserDataStoreStub()
|
||||
};
|
||||
app.get = sinon.stub().withArgs(VARIABLES_KEY).returns(mocks);
|
||||
return mocks;
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
}
|
15
test/unit/server/mocks/connectors/mongo/MongoClientStub.ts
Normal file
15
test/unit/server/mocks/connectors/mongo/MongoClientStub.ts
Normal 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);
|
||||
}
|
||||
}
|
16
test/unit/server/mocks/storage/CollectionFactoryStub.ts
Normal file
16
test/unit/server/mocks/storage/CollectionFactoryStub.ts
Normal 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);
|
||||
}
|
||||
}
|
39
test/unit/server/mocks/storage/CollectionStub.ts
Normal file
39
test/unit/server/mocks/storage/CollectionStub.ts
Normal 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);
|
||||
}
|
||||
}
|
65
test/unit/server/mocks/storage/UserDataStoreStub.ts
Normal file
65
test/unit/server/mocks/storage/UserDataStoreStub.ts
Normal 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);
|
||||
}
|
||||
}
|
19
test/unit/server/routes/errors/401/get.test.ts
Normal file
19
test/unit/server/routes/errors/401/get.test.ts
Normal 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"));
|
||||
});
|
||||
});
|
||||
});
|
19
test/unit/server/routes/errors/403/get.test.ts
Normal file
19
test/unit/server/routes/errors/403/get.test.ts
Normal 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"));
|
||||
});
|
||||
});
|
||||
});
|
19
test/unit/server/routes/errors/404/get.test.ts
Normal file
19
test/unit/server/routes/errors/404/get.test.ts
Normal 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"));
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,7 +13,7 @@ import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulato
|
|||
import AccessControllerMock = require("../../mocks/AccessController");
|
||||
import ExpressMock = require("../../mocks/express");
|
||||
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 () {
|
||||
let req: ExpressMock.RequestMock;
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
|
||||
import PasswordResetHandler from "../../../../../../src/server/lib/routes/password-reset/identity/PasswordResetHandler";
|
||||
import PasswordUpdater = require("../../../../../../src/server/lib/ldap/PasswordUpdater");
|
||||
import { ServerVariables } from "../../../../../../src/server/lib/ServerVariables";
|
||||
import sinon = require("sinon");
|
||||
import { ServerVariablesHandler } from "../../../../../../src/server/lib/ServerVariablesHandler";
|
||||
import { UserDataStore } from "../../../../../../src/server/lib/storage/UserDataStore";
|
||||
import Sinon = require("sinon");
|
||||
import winston = require("winston");
|
||||
import assert = require("assert");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import ExpressMock = require("../../../mocks/express");
|
||||
import { UserDataStore } from "../../../mocks/UserDataStore";
|
||||
import ServerVariablesMock = require("../../../mocks/ServerVariablesMock");
|
||||
|
||||
describe("test reset password identity check", function () {
|
||||
let req: ExpressMock.RequestMock;
|
||||
let res: ExpressMock.ResponseMock;
|
||||
let userDataStore: UserDataStore;
|
||||
let configuration: any;
|
||||
let serverVariables: ServerVariables;
|
||||
let serverVariables: ServerVariablesMock.ServerVariablesMock;
|
||||
|
||||
beforeEach(function () {
|
||||
req = {
|
||||
|
@ -24,7 +23,7 @@ describe("test reset password identity check", function () {
|
|||
userid: "user"
|
||||
},
|
||||
app: {
|
||||
get: sinon.stub()
|
||||
get: Sinon.stub()
|
||||
},
|
||||
session: {
|
||||
auth_session: {
|
||||
|
@ -45,14 +44,10 @@ describe("test reset password identity check", function () {
|
|||
|
||||
serverVariables = ServerVariablesMock.mock(req.app);
|
||||
|
||||
|
||||
userDataStore = UserDataStore();
|
||||
userDataStore.set_u2f_meta.returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.get_u2f_meta.returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.issue_identity_check_token.returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.consume_identity_check_token.returns(BluebirdPromise.resolve({}));
|
||||
serverVariables.userDataStore = userDataStore as any;
|
||||
|
||||
serverVariables.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
serverVariables.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
serverVariables.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||
serverVariables.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||
|
||||
configuration = {
|
||||
ldap: {
|
||||
|
@ -64,9 +59,8 @@ describe("test reset password identity check", function () {
|
|||
serverVariables.logger = winston;
|
||||
serverVariables.config = configuration;
|
||||
serverVariables.ldapEmailsRetriever = {
|
||||
retrieve: sinon.stub()
|
||||
retrieve: Sinon.stub()
|
||||
} as any;
|
||||
|
||||
res = ExpressMock.ResponseMock();
|
||||
});
|
||||
|
||||
|
|
|
@ -2,23 +2,22 @@
|
|||
import PasswordResetFormPost = require("../../../../../src/server/lib/routes/password-reset/form/post");
|
||||
import { PasswordUpdater } from "../../../../../src/server/lib/ldap/PasswordUpdater";
|
||||
import AuthenticationSession = require("../../../../../src/server/lib/AuthenticationSession");
|
||||
import { ServerVariables } from "../../../../../src/server/lib/ServerVariables";
|
||||
import sinon = require("sinon");
|
||||
import { ServerVariablesHandler } from "../../../../../src/server/lib/ServerVariablesHandler";
|
||||
import { UserDataStore } from "../../../../../src/server/lib/storage/UserDataStore";
|
||||
import Sinon = require("sinon");
|
||||
import winston = require("winston");
|
||||
import assert = require("assert");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import ExpressMock = require("../../mocks/express");
|
||||
import { UserDataStore } from "../../mocks/UserDataStore";
|
||||
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
||||
|
||||
describe("test reset password route", function () {
|
||||
let req: ExpressMock.RequestMock;
|
||||
let res: ExpressMock.ResponseMock;
|
||||
let userDataStore: UserDataStore;
|
||||
let configuration: any;
|
||||
let authSession: AuthenticationSession.AuthenticationSession;
|
||||
let serverVariables: ServerVariables;
|
||||
let serverVariables: ServerVariablesMock.ServerVariablesMock;
|
||||
|
||||
beforeEach(function () {
|
||||
req = {
|
||||
|
@ -26,7 +25,7 @@ describe("test reset password route", function () {
|
|||
userid: "user"
|
||||
},
|
||||
app: {
|
||||
get: sinon.stub()
|
||||
get: Sinon.stub()
|
||||
},
|
||||
session: {},
|
||||
headers: {
|
||||
|
@ -46,13 +45,10 @@ describe("test reset password route", function () {
|
|||
};
|
||||
|
||||
serverVariables = ServerVariablesMock.mock(req.app);
|
||||
userDataStore = UserDataStore();
|
||||
userDataStore.set_u2f_meta.returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.get_u2f_meta.returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.issue_identity_check_token.returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.consume_identity_check_token.returns(BluebirdPromise.resolve({}));
|
||||
serverVariables.userDataStore = userDataStore as any;
|
||||
|
||||
serverVariables.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
serverVariables.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
serverVariables.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||
serverVariables.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||
|
||||
configuration = {
|
||||
ldap: {
|
||||
|
@ -65,7 +61,7 @@ describe("test reset password route", function () {
|
|||
serverVariables.config = configuration;
|
||||
|
||||
serverVariables.ldapPasswordUpdater = {
|
||||
updatePassword: sinon.stub()
|
||||
updatePassword: Sinon.stub()
|
||||
} as any;
|
||||
|
||||
res = ExpressMock.ResponseMock();
|
||||
|
@ -96,7 +92,7 @@ describe("test reset password route", function () {
|
|||
userid: "user",
|
||||
challenge: undefined
|
||||
};
|
||||
res.send = sinon.spy(function () {
|
||||
res.send = Sinon.spy(function () {
|
||||
assert.equal(res.status.getCall(0).args[0], 403);
|
||||
done();
|
||||
});
|
||||
|
@ -111,8 +107,8 @@ describe("test reset password route", function () {
|
|||
req.body = {};
|
||||
req.body.password = "new-password";
|
||||
|
||||
(serverVariables.ldapPasswordUpdater.updatePassword as sinon.SinonStub).returns(BluebirdPromise.reject("Internal error with LDAP"));
|
||||
res.send = sinon.spy(function () {
|
||||
(serverVariables.ldapPasswordUpdater.updatePassword as Sinon.SinonStub).returns(BluebirdPromise.reject("Internal error with LDAP"));
|
||||
res.send = Sinon.spy(function () {
|
||||
assert.equal(res.status.getCall(0).args[0], 500);
|
||||
done();
|
||||
});
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
import sinon = require("sinon");
|
||||
import Sinon = require("sinon");
|
||||
import winston = require("winston");
|
||||
import RegistrationHandler from "../../../../../../../src/server/lib/routes/secondfactor/totp/identity/RegistrationHandler";
|
||||
import { Identity } from "../../../../../../../src/types/Identity";
|
||||
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
||||
import { UserDataStore } from "../../../../../../../src/server/lib/storage/UserDataStore";
|
||||
import assert = require("assert");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
import ExpressMock = require("../../../../mocks/express");
|
||||
import UserDataStoreMock = require("../../../../mocks/UserDataStore");
|
||||
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
|
||||
|
||||
describe("test totp register", function () {
|
||||
let req: ExpressMock.RequestMock;
|
||||
let res: ExpressMock.ResponseMock;
|
||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||
const registrationHandler: RegistrationHandler = new RegistrationHandler();
|
||||
let authSession: AuthenticationSession.AuthenticationSession;
|
||||
|
||||
|
@ -37,13 +36,11 @@ describe("test totp register", function () {
|
|||
inMemoryOnly: true
|
||||
};
|
||||
|
||||
userDataStore = UserDataStoreMock.UserDataStore();
|
||||
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.set_totp_secret = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore = userDataStore as any;
|
||||
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore.saveTOTPSecretStub.returns(BluebirdPromise.resolve({}));
|
||||
|
||||
res = ExpressMock.ResponseMock();
|
||||
});
|
||||
|
|
|
@ -9,15 +9,14 @@ import AuthenticationSession = require("../../../../../../../src/server/lib/Auth
|
|||
import SignPost = require("../../../../../../../src/server/lib/routes/secondfactor/totp/sign/post");
|
||||
|
||||
import ExpressMock = require("../../../../mocks/express");
|
||||
import UserDataStoreMock = require("../../../../mocks/UserDataStore");
|
||||
import TOTPValidatorMock = require("../../../../mocks/TOTPValidator");
|
||||
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
|
||||
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
||||
|
||||
describe("test totp route", function () {
|
||||
let req: ExpressMock.RequestMock;
|
||||
let res: ExpressMock.ResponseMock;
|
||||
let totpValidator: TOTPValidatorMock.TOTPValidatorMock;
|
||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||
let authSession: AuthenticationSession.AuthenticationSession;
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -42,20 +41,17 @@ describe("test totp route", function () {
|
|||
const config = { totp_secret: "secret" };
|
||||
totpValidator = TOTPValidatorMock.TOTPValidatorMock();
|
||||
|
||||
userDataStore = UserDataStoreMock.UserDataStore();
|
||||
|
||||
const doc = {
|
||||
userid: "user",
|
||||
secret: {
|
||||
base32: "ABCDEF"
|
||||
}
|
||||
};
|
||||
userDataStore.get_totp_secret.returns(BluebirdPromise.resolve(doc));
|
||||
mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc));
|
||||
|
||||
mocks.logger = winston;
|
||||
mocks.totpValidator = totpValidator as any;
|
||||
mocks.config = config as any;
|
||||
mocks.userDataStore = userDataStore as any;
|
||||
mocks.totpValidator = totpValidator;
|
||||
mocks.config = config;
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -8,13 +8,12 @@ import RegistrationHandler from "../../../../../../../src/server/lib/routes/seco
|
|||
import AuthenticationSession = require("../../../../../../../src/server/lib/AuthenticationSession");
|
||||
|
||||
import ExpressMock = require("../../../../mocks/express");
|
||||
import UserDataStoreMock = require("../../../../mocks/UserDataStore");
|
||||
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
||||
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
|
||||
|
||||
describe("test register handler", function () {
|
||||
let req: ExpressMock.RequestMock;
|
||||
let res: ExpressMock.ResponseMock;
|
||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||
let authSession: AuthenticationSession.AuthenticationSession;
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -36,12 +35,10 @@ describe("test register handler", function () {
|
|||
inMemoryOnly: true
|
||||
};
|
||||
|
||||
userDataStore = UserDataStoreMock.UserDataStore();
|
||||
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.issue_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.consume_identity_check_token = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore = userDataStore as any;
|
||||
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||
|
||||
res = ExpressMock.ResponseMock();
|
||||
res.send = sinon.spy();
|
||||
|
|
|
@ -4,11 +4,11 @@ import BluebirdPromise = require("bluebird");
|
|||
import assert = require("assert");
|
||||
import U2FRegisterPost = require("../../../../../../../src/server/lib/routes/secondfactor/u2f/register/post");
|
||||
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 ExpressMock = require("../../../../mocks/express");
|
||||
import UserDataStoreMock = require("../../../../mocks/UserDataStore");
|
||||
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
||||
import U2FMock = require("../../../../mocks/u2f");
|
||||
import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock");
|
||||
import U2f = require("u2f");
|
||||
|
@ -16,8 +16,7 @@ import U2f = require("u2f");
|
|||
describe("test u2f routes: register", function () {
|
||||
let req: ExpressMock.RequestMock;
|
||||
let res: ExpressMock.ResponseMock;
|
||||
let userDataStore: UserDataStoreMock.UserDataStore;
|
||||
let mocks: ServerVariables;
|
||||
let mocks: ServerVariablesMock.ServerVariablesMock;
|
||||
let authSession: AuthenticationSession.AuthenticationSession;
|
||||
|
||||
beforeEach(function () {
|
||||
|
@ -44,10 +43,8 @@ describe("test u2f routes: register", function () {
|
|||
inMemoryOnly: true
|
||||
};
|
||||
|
||||
userDataStore = UserDataStoreMock.UserDataStore();
|
||||
userDataStore.set_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
userDataStore.get_u2f_meta = sinon.stub().returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore = userDataStore as any;
|
||||
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||
|
||||
res = ExpressMock.ResponseMock();
|
||||
res.send = sinon.spy();
|
||||
|
@ -77,7 +74,7 @@ describe("test u2f routes: register", function () {
|
|||
mocks.u2f = u2f_mock;
|
||||
return U2FRegisterPost.default(req as any, res as any)
|
||||
.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);
|
||||
});
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user