Disable notifiers when server uses single factor method only
Notifier is not mandatory when authentication method is single_factor for all sub-domains since there is no registration required.pull/175/head
parent
3052c883a0
commit
73d5253297
|
@ -71,6 +71,9 @@ ldap:
|
||||||
# values must be one of the two possible methods.
|
# values must be one of the two possible methods.
|
||||||
#
|
#
|
||||||
# Note: 'per_subdomain_methods' is optional.
|
# Note: 'per_subdomain_methods' is optional.
|
||||||
|
#
|
||||||
|
# Note: authentication_methods is optional. If it is not set all sub-domains
|
||||||
|
# are protected by two factors.
|
||||||
authentication_methods:
|
authentication_methods:
|
||||||
default_method: two_factor
|
default_method: two_factor
|
||||||
per_subdomain_methods:
|
per_subdomain_methods:
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
import { AuthenticationMethod, AuthenticationMethodsConfiguration } from "./configuration/Configuration";
|
|
||||||
|
|
||||||
export class AuthenticationMethodCalculator {
|
|
||||||
private configuration: AuthenticationMethodsConfiguration;
|
|
||||||
|
|
||||||
constructor(config: AuthenticationMethodsConfiguration) {
|
|
||||||
this.configuration = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
compute(subDomain: string): AuthenticationMethod {
|
|
||||||
if (this.configuration
|
|
||||||
&& this.configuration.per_subdomain_methods
|
|
||||||
&& subDomain in this.configuration.per_subdomain_methods) {
|
|
||||||
return this.configuration.per_subdomain_methods[subDomain];
|
|
||||||
}
|
|
||||||
return this.configuration.default_method;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
import express = require("express");
|
|
||||||
import U2f = require("u2f");
|
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import { AuthenticationSession } from "../../types/AuthenticationSession";
|
|
||||||
import { IRequestLogger } from "./logging/IRequestLogger";
|
|
||||||
|
|
||||||
const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = {
|
|
||||||
first_factor: false,
|
|
||||||
second_factor: false,
|
|
||||||
last_activity_datetime: undefined,
|
|
||||||
userid: undefined,
|
|
||||||
email: undefined,
|
|
||||||
groups: [],
|
|
||||||
register_request: undefined,
|
|
||||||
sign_request: undefined,
|
|
||||||
identity_check: undefined,
|
|
||||||
redirect: undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
export function reset(req: express.Request): void {
|
|
||||||
req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {});
|
|
||||||
|
|
||||||
// Initialize last activity with current time
|
|
||||||
req.session.auth.last_activity_datetime = new Date().getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function get(req: express.Request, logger: IRequestLogger): BluebirdPromise<AuthenticationSession> {
|
|
||||||
if (!req.session) {
|
|
||||||
const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can contact it.";
|
|
||||||
logger.error(req, errorMsg);
|
|
||||||
return BluebirdPromise.reject(new Error(errorMsg));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!req.session.auth) {
|
|
||||||
logger.debug(req, "Authentication session %s was undefined. Resetting.", req.sessionID);
|
|
||||||
reset(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
return BluebirdPromise.resolve(req.session.auth);
|
|
||||||
}
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
|
||||||
|
import express = require("express");
|
||||||
|
import U2f = require("u2f");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import { AuthenticationSession } from "../../types/AuthenticationSession";
|
||||||
|
import { IRequestLogger } from "./logging/IRequestLogger";
|
||||||
|
|
||||||
|
const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = {
|
||||||
|
first_factor: false,
|
||||||
|
second_factor: false,
|
||||||
|
last_activity_datetime: undefined,
|
||||||
|
userid: undefined,
|
||||||
|
email: undefined,
|
||||||
|
groups: [],
|
||||||
|
register_request: undefined,
|
||||||
|
sign_request: undefined,
|
||||||
|
identity_check: undefined,
|
||||||
|
redirect: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
export class AuthenticationSessionHandler {
|
||||||
|
static reset(req: express.Request): void {
|
||||||
|
req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {});
|
||||||
|
|
||||||
|
// Initialize last activity with current time
|
||||||
|
req.session.auth.last_activity_datetime = new Date().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(req: express.Request, logger: IRequestLogger): AuthenticationSession {
|
||||||
|
if (!req.session) {
|
||||||
|
const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can contact it.";
|
||||||
|
logger.error(req, errorMsg);
|
||||||
|
throw new Error(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.session.auth) {
|
||||||
|
logger.debug(req, "Authentication session %s was undefined. Resetting.", req.sessionID);
|
||||||
|
AuthenticationSessionHandler.reset(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
return req.session.auth;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,17 +3,15 @@ import BluebirdPromise = require("bluebird");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import objectPath = require("object-path");
|
import objectPath = require("object-path");
|
||||||
import FirstFactorValidator = require("./FirstFactorValidator");
|
import FirstFactorValidator = require("./FirstFactorValidator");
|
||||||
import AuthenticationSessionHandler = require("./AuthenticationSession");
|
import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler";
|
||||||
import { IRequestLogger } from "./logging/IRequestLogger";
|
import { IRequestLogger } from "./logging/IRequestLogger";
|
||||||
|
|
||||||
export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise<void> {
|
export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise<void> {
|
||||||
return FirstFactorValidator.validate(req, logger)
|
return FirstFactorValidator.validate(req, logger)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return AuthenticationSessionHandler.get(req, logger);
|
const authSession = AuthenticationSessionHandler.get(req, logger);
|
||||||
})
|
if (!authSession.second_factor)
|
||||||
.then(function (authSession) {
|
return BluebirdPromise.reject("No second factor variable.");
|
||||||
if (!authSession.second_factor)
|
return BluebirdPromise.resolve();
|
||||||
return BluebirdPromise.reject("No second factor variable.");
|
});
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
}
|
}
|
|
@ -3,17 +3,17 @@ import BluebirdPromise = require("bluebird");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import objectPath = require("object-path");
|
import objectPath = require("object-path");
|
||||||
import Exceptions = require("./Exceptions");
|
import Exceptions = require("./Exceptions");
|
||||||
import AuthenticationSessionHandler = require("./AuthenticationSession");
|
import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler";
|
||||||
import { IRequestLogger } from "./logging/IRequestLogger";
|
import { IRequestLogger } from "./logging/IRequestLogger";
|
||||||
|
|
||||||
export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise<void> {
|
export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise<void> {
|
||||||
return AuthenticationSessionHandler.get(req, logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (authSession) {
|
const authSession = AuthenticationSessionHandler.get(req, logger);
|
||||||
if (!authSession.userid || !authSession.first_factor)
|
|
||||||
return BluebirdPromise.reject(
|
|
||||||
new Exceptions.FirstFactorValidationError(
|
|
||||||
"First factor has not been validated yet."));
|
|
||||||
|
|
||||||
return BluebirdPromise.resolve();
|
if (!authSession.userid || !authSession.first_factor)
|
||||||
});
|
return reject(new Exceptions.FirstFactorValidationError(
|
||||||
|
"First factor has not been validated yet."));
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ import ejs = require("ejs");
|
||||||
import { IUserDataStore } from "./storage/IUserDataStore";
|
import { IUserDataStore } from "./storage/IUserDataStore";
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import ErrorReplies = require("./ErrorReplies");
|
import ErrorReplies = require("./ErrorReplies");
|
||||||
import AuthenticationSessionHandler = require("./AuthenticationSession");
|
import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../types/AuthenticationSession";
|
||||||
import { ServerVariables } from "./ServerVariables";
|
import { ServerVariables } from "./ServerVariables";
|
||||||
|
|
||||||
|
@ -74,14 +74,9 @@ export function get_finish_validation(handler: IdentityValidable,
|
||||||
|
|
||||||
return checkIdentityToken(req, identityToken)
|
return checkIdentityToken(req, identityToken)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
return handler.postValidationInit(req);
|
return handler.postValidationInit(req);
|
||||||
})
|
})
|
||||||
.then(function () {
|
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger);
|
|
||||||
})
|
|
||||||
.then(function (_authSession) {
|
|
||||||
authSession = _authSession;
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return consumeToken(identityToken, handler.challenge(), vars.userDataStore);
|
return consumeToken(identityToken, handler.challenge(), vars.userDataStore);
|
||||||
})
|
})
|
||||||
|
@ -97,7 +92,6 @@ export function get_finish_validation(handler: IdentityValidable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function get_start_validation(handler: IdentityValidable,
|
export function get_start_validation(handler: IdentityValidable,
|
||||||
postValidationEndpoint: string,
|
postValidationEndpoint: string,
|
||||||
vars: ServerVariables)
|
vars: ServerVariables)
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
|
|
||||||
import Express = require("express");
|
|
||||||
import { UserDataStore } from "./storage/UserDataStore";
|
|
||||||
import { Winston } from "../../types/Dependencies";
|
|
||||||
|
|
||||||
import FirstFactorGet = require("./routes/firstfactor/get");
|
|
||||||
import SecondFactorGet = require("./routes/secondfactor/get");
|
|
||||||
|
|
||||||
import FirstFactorPost = require("./routes/firstfactor/post");
|
|
||||||
import LogoutGet = require("./routes/logout/get");
|
|
||||||
import VerifyGet = require("./routes/verify/get");
|
|
||||||
import TOTPSignGet = require("./routes/secondfactor/totp/sign/post");
|
|
||||||
|
|
||||||
import IdentityCheckMiddleware = require("./IdentityCheckMiddleware");
|
|
||||||
|
|
||||||
import TOTPRegistrationIdentityHandler from "./routes/secondfactor/totp/identity/RegistrationHandler";
|
|
||||||
import U2FRegistrationIdentityHandler from "./routes/secondfactor/u2f/identity/RegistrationHandler";
|
|
||||||
import ResetPasswordIdentityHandler from "./routes/password-reset/identity/PasswordResetHandler";
|
|
||||||
|
|
||||||
import U2FSignPost = require("./routes/secondfactor/u2f/sign/post");
|
|
||||||
import U2FSignRequestGet = require("./routes/secondfactor/u2f/sign_request/get");
|
|
||||||
|
|
||||||
import U2FRegisterPost = require("./routes/secondfactor/u2f/register/post");
|
|
||||||
import U2FRegisterRequestGet = require("./routes/secondfactor/u2f/register_request/get");
|
|
||||||
|
|
||||||
import ResetPasswordFormPost = require("./routes/password-reset/form/post");
|
|
||||||
import ResetPasswordRequestPost = require("./routes/password-reset/request/get");
|
|
||||||
|
|
||||||
import Error401Get = require("./routes/error/401/get");
|
|
||||||
import Error403Get = require("./routes/error/403/get");
|
|
||||||
import Error404Get = require("./routes/error/404/get");
|
|
||||||
|
|
||||||
import LoggedIn = require("./routes/loggedin/get");
|
|
||||||
|
|
||||||
import { ServerVariables } from "./ServerVariables";
|
|
||||||
import { IRequestLogger } from "./logging/IRequestLogger";
|
|
||||||
|
|
||||||
import Endpoints = require("../../../shared/api");
|
|
||||||
|
|
||||||
function withHeadersLogged(fn: (req: Express.Request, res: Express.Response) => void,
|
|
||||||
logger: IRequestLogger) {
|
|
||||||
return function (req: Express.Request, res: Express.Response) {
|
|
||||||
logger.debug(req, "Headers = %s", JSON.stringify(req.headers));
|
|
||||||
fn(req, res);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RestApi {
|
|
||||||
static setup(app: Express.Application, vars: ServerVariables): void {
|
|
||||||
app.get(Endpoints.FIRST_FACTOR_GET, withHeadersLogged(FirstFactorGet.default(vars), vars.logger));
|
|
||||||
app.get(Endpoints.SECOND_FACTOR_GET, withHeadersLogged(SecondFactorGet.default(vars), vars.logger));
|
|
||||||
app.get(Endpoints.LOGOUT_GET, withHeadersLogged(LogoutGet.default, vars.logger));
|
|
||||||
|
|
||||||
IdentityCheckMiddleware.register(app, Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
|
||||||
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET,
|
|
||||||
new TOTPRegistrationIdentityHandler(vars.logger, vars.userDataStore, vars.totpHandler), vars);
|
|
||||||
|
|
||||||
IdentityCheckMiddleware.register(app, Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET,
|
|
||||||
Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET,
|
|
||||||
new U2FRegistrationIdentityHandler(vars.logger), vars);
|
|
||||||
|
|
||||||
IdentityCheckMiddleware.register(app, Endpoints.RESET_PASSWORD_IDENTITY_START_GET,
|
|
||||||
Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET,
|
|
||||||
new ResetPasswordIdentityHandler(vars.logger, vars.ldapEmailsRetriever), vars);
|
|
||||||
|
|
||||||
app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, withHeadersLogged(ResetPasswordRequestPost.default, vars.logger));
|
|
||||||
app.post(Endpoints.RESET_PASSWORD_FORM_POST, withHeadersLogged(ResetPasswordFormPost.default(vars), vars.logger));
|
|
||||||
|
|
||||||
app.get(Endpoints.VERIFY_GET, withHeadersLogged(VerifyGet.default(vars), vars.logger));
|
|
||||||
app.post(Endpoints.FIRST_FACTOR_POST, withHeadersLogged(FirstFactorPost.default(vars), vars.logger));
|
|
||||||
app.post(Endpoints.SECOND_FACTOR_TOTP_POST, withHeadersLogged(TOTPSignGet.default(vars), vars.logger));
|
|
||||||
|
|
||||||
app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, withHeadersLogged(U2FSignRequestGet.default(vars), vars.logger));
|
|
||||||
app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, withHeadersLogged(U2FSignPost.default(vars), vars.logger));
|
|
||||||
|
|
||||||
app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, withHeadersLogged(U2FRegisterRequestGet.default(vars), vars.logger));
|
|
||||||
app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, withHeadersLogged(U2FRegisterPost.default(vars), vars.logger));
|
|
||||||
|
|
||||||
app.get(Endpoints.ERROR_401_GET, withHeadersLogged(Error401Get.default, vars.logger));
|
|
||||||
app.get(Endpoints.ERROR_403_GET, withHeadersLogged(Error403Get.default, vars.logger));
|
|
||||||
app.get(Endpoints.ERROR_404_GET, withHeadersLogged(Error404Get.default, vars.logger));
|
|
||||||
app.get(Endpoints.LOGGED_IN, withHeadersLogged(LoggedIn.default(vars), vars.logger));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,27 +6,17 @@ import { AppConfiguration, UserConfiguration } from "./configuration/Configurati
|
||||||
import { GlobalDependencies } from "../../types/Dependencies";
|
import { GlobalDependencies } from "../../types/Dependencies";
|
||||||
import { UserDataStore } from "./storage/UserDataStore";
|
import { UserDataStore } from "./storage/UserDataStore";
|
||||||
import { ConfigurationParser } from "./configuration/ConfigurationParser";
|
import { ConfigurationParser } from "./configuration/ConfigurationParser";
|
||||||
import { RestApi } from "./RestApi";
|
|
||||||
import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder";
|
import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder";
|
||||||
import { GlobalLogger } from "./logging/GlobalLogger";
|
import { GlobalLogger } from "./logging/GlobalLogger";
|
||||||
import { RequestLogger } from "./logging/RequestLogger";
|
import { RequestLogger } from "./logging/RequestLogger";
|
||||||
import { ServerVariables } from "./ServerVariables";
|
import { ServerVariables } from "./ServerVariables";
|
||||||
import { ServerVariablesInitializer } from "./ServerVariablesInitializer";
|
import { ServerVariablesInitializer } from "./ServerVariablesInitializer";
|
||||||
|
import { Configurator } from "./web_server/Configurator";
|
||||||
|
|
||||||
import * as Express from "express";
|
import * as Express from "express";
|
||||||
import * as BodyParser from "body-parser";
|
|
||||||
import * as Path from "path";
|
import * as Path from "path";
|
||||||
import * as http from "http";
|
import * as http from "http";
|
||||||
|
|
||||||
const addRequestId = require("express-request-id")();
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
const TRUST_PROXY = "trust proxy";
|
|
||||||
const X_POWERED_BY = "x-powered-by";
|
|
||||||
const VIEWS = "views";
|
|
||||||
const VIEW_ENGINE = "view engine";
|
|
||||||
const PUG = "pug";
|
|
||||||
|
|
||||||
function clone(obj: any) {
|
function clone(obj: any) {
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
}
|
}
|
||||||
|
@ -35,35 +25,12 @@ export default class Server {
|
||||||
private httpServer: http.Server;
|
private httpServer: http.Server;
|
||||||
private globalLogger: GlobalLogger;
|
private globalLogger: GlobalLogger;
|
||||||
private requestLogger: RequestLogger;
|
private requestLogger: RequestLogger;
|
||||||
private serverVariables: ServerVariables;
|
|
||||||
|
|
||||||
constructor(deps: GlobalDependencies) {
|
constructor(deps: GlobalDependencies) {
|
||||||
this.globalLogger = new GlobalLogger(deps.winston);
|
this.globalLogger = new GlobalLogger(deps.winston);
|
||||||
this.requestLogger = new RequestLogger(deps.winston);
|
this.requestLogger = new RequestLogger(deps.winston);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
app.use(Express.static(publicHtmlDirectory));
|
|
||||||
app.use(BodyParser.urlencoded({ extended: false }));
|
|
||||||
app.use(BodyParser.json());
|
|
||||||
app.use(deps.session(expressSessionOptions));
|
|
||||||
app.use(addRequestId);
|
|
||||||
app.disable(X_POWERED_BY);
|
|
||||||
app.enable(TRUST_PROXY);
|
|
||||||
|
|
||||||
app.set(VIEWS, viewsDirectory);
|
|
||||||
app.set(VIEW_ENGINE, PUG);
|
|
||||||
|
|
||||||
RestApi.setup(app, this.serverVariables);
|
|
||||||
}
|
|
||||||
|
|
||||||
private displayConfigurations(userConfiguration: UserConfiguration,
|
private displayConfigurations(userConfiguration: UserConfiguration,
|
||||||
appConfiguration: AppConfiguration) {
|
appConfiguration: AppConfiguration) {
|
||||||
const displayableUserConfiguration = clone(userConfiguration);
|
const displayableUserConfiguration = clone(userConfiguration);
|
||||||
|
@ -94,8 +61,7 @@ export default class Server {
|
||||||
const that = this;
|
const that = this;
|
||||||
return ServerVariablesInitializer.initialize(config, this.requestLogger, deps)
|
return ServerVariablesInitializer.initialize(config, this.requestLogger, deps)
|
||||||
.then(function (vars: ServerVariables) {
|
.then(function (vars: ServerVariables) {
|
||||||
that.serverVariables = vars;
|
Configurator.configure(config, app, vars, deps);
|
||||||
that.setupExpressApplication(config, app, deps);
|
|
||||||
return BluebirdPromise.resolve();
|
return BluebirdPromise.resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ import { IMongoClient } from "./connectors/mongo/IMongoClient";
|
||||||
|
|
||||||
import { GlobalDependencies } from "../../types/Dependencies";
|
import { GlobalDependencies } from "../../types/Dependencies";
|
||||||
import { ServerVariables } from "./ServerVariables";
|
import { ServerVariables } from "./ServerVariables";
|
||||||
import { AuthenticationMethodCalculator } from "./AuthenticationMethodCalculator";
|
import { MethodCalculator } from "./authentication/MethodCalculator";
|
||||||
|
|
||||||
class UserDataStoreFactory {
|
class UserDataStoreFactory {
|
||||||
static create(config: Configuration.AppConfiguration): BluebirdPromise<UserDataStore> {
|
static create(config: Configuration.AppConfiguration): BluebirdPromise<UserDataStore> {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import {
|
||||||
|
AuthenticationMethod,
|
||||||
|
AuthenticationMethodsConfiguration
|
||||||
|
} from "../configuration/Configuration";
|
||||||
|
|
||||||
|
function computeIsSingleFactorOnlyMode(
|
||||||
|
configuration: AuthenticationMethodsConfiguration): boolean {
|
||||||
|
if (!configuration)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const method: AuthenticationMethod = configuration.default_method;
|
||||||
|
if (configuration.default_method == "two_factor")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (configuration.per_subdomain_methods) {
|
||||||
|
for (const key in configuration.per_subdomain_methods) {
|
||||||
|
const method = configuration.per_subdomain_methods[key];
|
||||||
|
if (method == "two_factor")
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MethodCalculator {
|
||||||
|
static compute(config: AuthenticationMethodsConfiguration, subDomain: string)
|
||||||
|
: AuthenticationMethod {
|
||||||
|
if (config
|
||||||
|
&& config.per_subdomain_methods
|
||||||
|
&& subDomain in config.per_subdomain_methods) {
|
||||||
|
return config.per_subdomain_methods[subDomain];
|
||||||
|
}
|
||||||
|
return config.default_method;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isSingleFactorOnlyMode(config: AuthenticationMethodsConfiguration)
|
||||||
|
: boolean {
|
||||||
|
return computeIsSingleFactorOnlyMode(config);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,9 @@ import Path = require("path");
|
||||||
import Util = require("util");
|
import Util = require("util");
|
||||||
import {
|
import {
|
||||||
UserConfiguration, StorageConfiguration,
|
UserConfiguration, StorageConfiguration,
|
||||||
NotifierConfiguration
|
NotifierConfiguration, AuthenticationMethodsConfiguration
|
||||||
} from "./Configuration";
|
} from "./Configuration";
|
||||||
|
import { MethodCalculator } from "../authentication/MethodCalculator";
|
||||||
|
|
||||||
function validateSchema(configuration: UserConfiguration): string[] {
|
function validateSchema(configuration: UserConfiguration): string[] {
|
||||||
const schema = require(Path.resolve(__dirname, "./Configuration.schema.json"));
|
const schema = require(Path.resolve(__dirname, "./Configuration.schema.json"));
|
||||||
|
@ -34,7 +35,7 @@ function validateUnknownKeys(path: string, obj: any, knownKeys: string[]) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateStorage(storage: any) {
|
function validateStorage(storage: any): string[] {
|
||||||
const ERROR = "Storage must be either 'local' or 'mongo'";
|
const ERROR = "Storage must be either 'local' or 'mongo'";
|
||||||
|
|
||||||
if (!storage)
|
if (!storage)
|
||||||
|
@ -53,22 +54,28 @@ function validateStorage(storage: any) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateNotifier(notifier: NotifierConfiguration) {
|
function validateNotifier(notifier: NotifierConfiguration,
|
||||||
|
authenticationMethods: AuthenticationMethodsConfiguration): string[] {
|
||||||
const ERROR = "Notifier must be either 'filesystem', 'email' or 'smtp'";
|
const ERROR = "Notifier must be either 'filesystem', 'email' or 'smtp'";
|
||||||
|
|
||||||
if (!notifier)
|
if (!notifier)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
|
if (!MethodCalculator.isSingleFactorOnlyMode(authenticationMethods)) {
|
||||||
|
if (Object.keys(notifier).length != 1)
|
||||||
|
return ["A notifier needs to be declared when server is used with two-factor"];
|
||||||
|
|
||||||
|
if (notifier && notifier.filesystem && notifier.email && notifier.smtp)
|
||||||
|
return [ERROR];
|
||||||
|
|
||||||
|
if (notifier && !notifier.filesystem && !notifier.email && !notifier.smtp)
|
||||||
|
return [ERROR];
|
||||||
|
}
|
||||||
|
|
||||||
const errors = validateUnknownKeys("notifier", notifier, ["filesystem", "email", "smtp"]);
|
const errors = validateUnknownKeys("notifier", notifier, ["filesystem", "email", "smtp"]);
|
||||||
if (errors.length > 0)
|
if (errors.length > 0)
|
||||||
return errors;
|
return errors;
|
||||||
|
|
||||||
if (notifier && notifier.filesystem && notifier.email && notifier.smtp)
|
|
||||||
return [ERROR];
|
|
||||||
|
|
||||||
if (notifier && !notifier.filesystem && !notifier.email && !notifier.smtp)
|
|
||||||
return [ERROR];
|
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +83,8 @@ export class Validator {
|
||||||
static isValid(configuration: any): string[] {
|
static isValid(configuration: any): string[] {
|
||||||
const schemaErrors = validateSchema(configuration);
|
const schemaErrors = validateSchema(configuration);
|
||||||
const storageErrors = validateStorage(configuration.storage);
|
const storageErrors = validateStorage(configuration.storage);
|
||||||
const notifierErrors = validateNotifier(configuration.notifier);
|
const notifierErrors = validateNotifier(configuration.notifier,
|
||||||
|
configuration.authentication_methods);
|
||||||
|
|
||||||
return schemaErrors
|
return schemaErrors
|
||||||
.concat(storageErrors)
|
.concat(storageErrors)
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class LdapClient implements ILdapClient {
|
||||||
clientLogger.level("trace");
|
clientLogger.level("trace");
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
this.client = BluebirdPromise.promisifyAll(ldapClient) as LdapJs.ClientAsync;
|
this.client = BluebirdPromise.promisifyAll(ldapClient) as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindAsync(username: string, password: string): BluebirdPromise<void> {
|
bindAsync(username: string, password: string): BluebirdPromise<void> {
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
import express = require("express");
|
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import FirstFactorValidator = require("../FirstFactorValidator");
|
|
||||||
import Exceptions = require("../Exceptions");
|
|
||||||
import ErrorReplies = require("../ErrorReplies");
|
|
||||||
import objectPath = require("object-path");
|
|
||||||
import AuthenticationSession = require("../AuthenticationSession");
|
|
||||||
import UserMessages = require("../../../../shared/UserMessages");
|
|
||||||
import { IRequestLogger } from "../logging/IRequestLogger";
|
|
||||||
|
|
||||||
type Handler = (req: express.Request, res: express.Response) => BluebirdPromise<void>;
|
|
||||||
|
|
||||||
export default function (callback: Handler, logger: IRequestLogger): Handler {
|
|
||||||
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
|
||||||
return AuthenticationSession.get(req, logger)
|
|
||||||
.then(function (authSession) {
|
|
||||||
return FirstFactorValidator.validate(req, logger);
|
|
||||||
})
|
|
||||||
.then(function () {
|
|
||||||
return callback(req, res);
|
|
||||||
})
|
|
||||||
.catch(ErrorReplies.replyWithError401(req, res, logger));
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@ import winston = require("winston");
|
||||||
import Endpoints = require("../../../../../shared/api");
|
import Endpoints = require("../../../../../shared/api");
|
||||||
import AuthenticationValidator = require("../../AuthenticationValidator");
|
import AuthenticationValidator = require("../../AuthenticationValidator");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import AuthenticationSession = require("../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
import Constants = require("../../../../../shared/constants");
|
import Constants = require("../../../../../shared/constants");
|
||||||
import Util = require("util");
|
import Util = require("util");
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
|
@ -42,18 +42,18 @@ function renderFirstFactor(res: express.Response) {
|
||||||
|
|
||||||
export default function (vars: ServerVariables) {
|
export default function (vars: ServerVariables) {
|
||||||
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
return AuthenticationSession.get(req, vars.logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (authSession) {
|
const authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
if (authSession.first_factor) {
|
if (authSession.first_factor) {
|
||||||
if (authSession.second_factor)
|
if (authSession.second_factor)
|
||||||
redirectToService(req, res);
|
redirectToService(req, res);
|
||||||
else
|
else
|
||||||
redirectToSecondFactorPage(req, res);
|
redirectToSecondFactorPage(req, res);
|
||||||
return BluebirdPromise.resolve();
|
resolve();
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
renderFirstFactor(res);
|
renderFirstFactor(res);
|
||||||
return BluebirdPromise.resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -8,11 +8,11 @@ import { Regulator } from "../../regulation/Regulator";
|
||||||
import { GroupsAndEmails } from "../../ldap/IClient";
|
import { GroupsAndEmails } from "../../ldap/IClient";
|
||||||
import Endpoint = require("../../../../../shared/api");
|
import Endpoint = require("../../../../../shared/api");
|
||||||
import ErrorReplies = require("../../ErrorReplies");
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
import AuthenticationSessionHandler = require("../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
import Constants = require("../../../../../shared/constants");
|
import Constants = require("../../../../../shared/constants");
|
||||||
import { DomainExtractor } from "../../utils/DomainExtractor";
|
import { DomainExtractor } from "../../utils/DomainExtractor";
|
||||||
import UserMessages = require("../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../shared/UserMessages");
|
||||||
import { AuthenticationMethodCalculator } from "../../AuthenticationMethodCalculator";
|
import { MethodCalculator } from "../../authentication/MethodCalculator";
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
||||||
|
|
||||||
|
@ -29,10 +29,7 @@ export default function (vars: ServerVariables) {
|
||||||
return BluebirdPromise.reject(new Error("No username or password."));
|
return BluebirdPromise.reject(new Error("No username or password."));
|
||||||
}
|
}
|
||||||
vars.logger.info(req, "Starting authentication of user \"%s\"", username);
|
vars.logger.info(req, "Starting authentication of user \"%s\"", username);
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger);
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
})
|
|
||||||
.then(function (_authSession) {
|
|
||||||
authSession = _authSession;
|
|
||||||
return vars.regulator.regulate(username);
|
return vars.regulator.regulate(username);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
@ -40,7 +37,8 @@ export default function (vars: ServerVariables) {
|
||||||
return vars.ldapAuthenticator.authenticate(username, password);
|
return vars.ldapAuthenticator.authenticate(username, password);
|
||||||
})
|
})
|
||||||
.then(function (groupsAndEmails: GroupsAndEmails) {
|
.then(function (groupsAndEmails: GroupsAndEmails) {
|
||||||
vars.logger.info(req, "LDAP binding successful. Retrieved information about user are %s",
|
vars.logger.info(req,
|
||||||
|
"LDAP binding successful. Retrieved information about user are %s",
|
||||||
JSON.stringify(groupsAndEmails));
|
JSON.stringify(groupsAndEmails));
|
||||||
authSession.userid = username;
|
authSession.userid = username;
|
||||||
authSession.first_factor = true;
|
authSession.first_factor = true;
|
||||||
|
@ -52,27 +50,24 @@ export default function (vars: ServerVariables) {
|
||||||
const emails: string[] = groupsAndEmails.emails;
|
const emails: string[] = groupsAndEmails.emails;
|
||||||
const groups: string[] = groupsAndEmails.groups;
|
const groups: string[] = groupsAndEmails.groups;
|
||||||
const redirectHost: string = DomainExtractor.fromUrl(redirectUrl);
|
const redirectHost: string = DomainExtractor.fromUrl(redirectUrl);
|
||||||
const authMethod =
|
const authMethod = MethodCalculator.compute(
|
||||||
new AuthenticationMethodCalculator(vars.config.authentication_methods)
|
vars.config.authentication_methods, redirectHost);
|
||||||
.compute(redirectHost);
|
vars.logger.debug(req, "Authentication method for \"%s\" is \"%s\"",
|
||||||
vars.logger.debug(req, "Authentication method for \"%s\" is \"%s\"", redirectHost, authMethod);
|
redirectHost, authMethod);
|
||||||
|
|
||||||
if (!emails || emails.length <= 0) {
|
if (emails.length > 0)
|
||||||
const errMessage =
|
authSession.email = emails[0];
|
||||||
"No emails found. The user should have at least one email address to reset password.";
|
|
||||||
vars.logger.error(req, "%s", errMessage);
|
|
||||||
return BluebirdPromise.reject(new Error(errMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
authSession.email = emails[0];
|
|
||||||
authSession.groups = groups;
|
authSession.groups = groups;
|
||||||
|
|
||||||
vars.logger.debug(req, "Mark successful authentication to regulator.");
|
vars.logger.debug(req, "Mark successful authentication to regulator.");
|
||||||
vars.regulator.mark(username, true);
|
vars.regulator.mark(username, true);
|
||||||
|
|
||||||
if (authMethod == "single_factor") {
|
if (authMethod == "single_factor") {
|
||||||
|
let newRedirectionUrl: string = redirectUrl;
|
||||||
|
if (!newRedirectionUrl)
|
||||||
|
newRedirectionUrl = Endpoint.LOGGED_IN;
|
||||||
res.send({
|
res.send({
|
||||||
redirect: redirectUrl
|
redirect: newRedirectionUrl
|
||||||
});
|
});
|
||||||
vars.logger.debug(req, "Redirect to '%s'", redirectUrl);
|
vars.logger.debug(req, "Redirect to '%s'", redirectUrl);
|
||||||
}
|
}
|
||||||
|
@ -82,7 +77,7 @@ export default function (vars: ServerVariables) {
|
||||||
newRedirectUrl += "?" + Constants.REDIRECT_QUERY_PARAM + "="
|
newRedirectUrl += "?" + Constants.REDIRECT_QUERY_PARAM + "="
|
||||||
+ encodeURIComponent(redirectUrl);
|
+ encodeURIComponent(redirectUrl);
|
||||||
}
|
}
|
||||||
vars.logger.debug(req, "Redirect to '%s'", newRedirectUrl, typeof redirectUrl);
|
vars.logger.debug(req, "Redirect to '%s'", newRedirectUrl);
|
||||||
res.send({
|
res.send({
|
||||||
redirect: newRedirectUrl
|
redirect: newRedirectUrl
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
import Express = require("express");
|
import Express = require("express");
|
||||||
import Endpoints = require("../../../../../shared/api");
|
import Endpoints = require("../../../../../shared/api");
|
||||||
import FirstFactorBlocker from "../FirstFactorBlocker";
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import AuthenticationSessionHandler = require("../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
|
|
||||||
export default function (vars: ServerVariables) {
|
export default function (vars: ServerVariables) {
|
||||||
function handler(req: Express.Request, res: Express.Response): BluebirdPromise<void> {
|
function handler(req: Express.Request, res: Express.Response): BluebirdPromise<void> {
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
return new BluebirdPromise<void>(function (resolve, reject) {
|
||||||
.then(function (authSession) {
|
const authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
res.render("already-logged-in", {
|
res.render("already-logged-in", {
|
||||||
logout_endpoint: Endpoints.LOGOUT_GET,
|
logout_endpoint: Endpoints.LOGOUT_GET,
|
||||||
username: authSession.userid,
|
username: authSession.userid,
|
||||||
redirection_url: vars.config.default_redirection_url
|
redirection_url: vars.config.default_redirection_url
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(ErrorReplies.replyWithError401(req, res, vars.logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
return FirstFactorBlocker(handler, vars.logger);
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import AuthenticationSession = require("../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
|
|
||||||
export default function(req: express.Request, res: express.Response) {
|
export default function(req: express.Request, res: express.Response) {
|
||||||
const redirect_param = req.query.redirect;
|
const redirect_param = req.query.redirect;
|
||||||
const redirect_url = redirect_param || "/";
|
const redirect_url = redirect_param || "/";
|
||||||
AuthenticationSession.reset(req);
|
AuthenticationSessionHandler.reset(req);
|
||||||
res.redirect(redirect_url);
|
res.redirect(redirect_url);
|
||||||
}
|
}
|
|
@ -3,7 +3,7 @@ import express = require("express");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import objectPath = require("object-path");
|
import objectPath = require("object-path");
|
||||||
import exceptions = require("../../../Exceptions");
|
import exceptions = require("../../../Exceptions");
|
||||||
import AuthenticationSessionHandler = require("../../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
||||||
import ErrorReplies = require("../../../ErrorReplies");
|
import ErrorReplies = require("../../../ErrorReplies");
|
||||||
import UserMessages = require("../../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../../shared/UserMessages");
|
||||||
|
@ -16,16 +16,24 @@ export default function (vars: ServerVariables) {
|
||||||
let authSession: AuthenticationSession;
|
let authSession: AuthenticationSession;
|
||||||
const newPassword = objectPath.get<express.Request, string>(req, "body.password");
|
const newPassword = objectPath.get<express.Request, string>(req, "body.password");
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (_authSession) {
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
authSession = _authSession;
|
if (!authSession.identity_check) {
|
||||||
vars.logger.info(req, "User %s wants to reset his/her password.",
|
reject(new Error("No identity check initiated"));
|
||||||
authSession.identity_check.userid);
|
return;
|
||||||
vars.logger.debug(req, "Challenge %s", authSession.identity_check.challenge);
|
}
|
||||||
|
|
||||||
if (authSession.identity_check.challenge != Constants.CHALLENGE) {
|
vars.logger.info(req, "User %s wants to reset his/her password.",
|
||||||
return BluebirdPromise.reject(new Error("Bad challenge."));
|
authSession.identity_check.userid);
|
||||||
}
|
vars.logger.debug(req, "Challenge %s", authSession.identity_check.challenge);
|
||||||
|
|
||||||
|
if (authSession.identity_check.challenge != Constants.CHALLENGE) {
|
||||||
|
reject(new Error("Bad challenge."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
return vars.ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
|
return vars.ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
|
|
@ -1,30 +1,37 @@
|
||||||
|
|
||||||
import Express = require("express");
|
import Express = require("express");
|
||||||
import Endpoints = require("../../../../../shared/api");
|
import Endpoints = require("../../../../../shared/api");
|
||||||
import FirstFactorBlocker = require("../FirstFactorBlocker");
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import AuthenticationSessionHandler = require("../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
|
import { MethodCalculator } from "../../authentication/MethodCalculator";
|
||||||
|
|
||||||
const TEMPLATE_NAME = "secondfactor";
|
const TEMPLATE_NAME = "secondfactor";
|
||||||
|
|
||||||
export default function (vars: ServerVariables) {
|
export default function (vars: ServerVariables) {
|
||||||
function handler(req: Express.Request, res: Express.Response): BluebirdPromise<void> {
|
function handler(req: Express.Request, res: Express.Response)
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
: BluebirdPromise<void> {
|
||||||
.then(function (authSession) {
|
|
||||||
if (authSession.first_factor && authSession.second_factor) {
|
|
||||||
res.redirect(Endpoints.LOGGED_IN);
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
res.render(TEMPLATE_NAME, {
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
username: authSession.userid,
|
const isSingleFactorMode: boolean = MethodCalculator.isSingleFactorOnlyMode(
|
||||||
totp_identity_start_endpoint: Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
vars.config.authentication_methods);
|
||||||
u2f_identity_start_endpoint: Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET
|
const authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
});
|
if (isSingleFactorMode
|
||||||
return BluebirdPromise.resolve();
|
|| (authSession.first_factor && authSession.second_factor)) {
|
||||||
|
res.redirect(Endpoints.LOGGED_IN);
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.render(TEMPLATE_NAME, {
|
||||||
|
username: authSession.userid,
|
||||||
|
totp_identity_start_endpoint:
|
||||||
|
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
||||||
|
u2f_identity_start_endpoint:
|
||||||
|
Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET
|
||||||
});
|
});
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
return handler;
|
||||||
return FirstFactorBlocker.default(handler, vars.logger);
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import objectPath = require("object-path");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import Endpoints = require("../../../../../shared/api");
|
import Endpoints = require("../../../../../shared/api");
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import AuthenticationSession = require("../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import ErrorReplies = require("../../ErrorReplies");
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
import UserMessages = require("../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../shared/UserMessages");
|
||||||
|
@ -13,18 +13,19 @@ import Constants = require("../../../../../shared/constants");
|
||||||
|
|
||||||
export default function (vars: ServerVariables) {
|
export default function (vars: ServerVariables) {
|
||||||
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
return AuthenticationSession.get(req, vars.logger)
|
|
||||||
.then(function (authSession) {
|
return new BluebirdPromise<void>(function (resolve, reject) {
|
||||||
let redirectUrl: string;
|
const authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
if (vars.config.default_redirection_url) {
|
let redirectUrl: string;
|
||||||
redirectUrl = vars.config.default_redirection_url;
|
if (vars.config.default_redirection_url) {
|
||||||
}
|
redirectUrl = vars.config.default_redirection_url;
|
||||||
vars.logger.debug(req, "Request redirection to \"%s\".", redirectUrl);
|
}
|
||||||
res.json({
|
vars.logger.debug(req, "Request redirection to \"%s\".", redirectUrl);
|
||||||
redirect: redirectUrl
|
res.json({
|
||||||
} as RedirectionMessage);
|
redirect: redirectUrl
|
||||||
return BluebirdPromise.resolve();
|
} as RedirectionMessage);
|
||||||
})
|
return resolve();
|
||||||
|
})
|
||||||
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
||||||
UserMessages.OPERATION_FAILED));
|
UserMessages.OPERATION_FAILED));
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,12 +9,13 @@ import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationT
|
||||||
import Constants = require("../constants");
|
import Constants = require("../constants");
|
||||||
import Endpoints = require("../../../../../../../shared/api");
|
import Endpoints = require("../../../../../../../shared/api");
|
||||||
import ErrorReplies = require("../../../../ErrorReplies");
|
import ErrorReplies = require("../../../../ErrorReplies");
|
||||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||||
import FirstFactorValidator = require("../../../../FirstFactorValidator");
|
import FirstFactorValidator = require("../../../../FirstFactorValidator");
|
||||||
import { IRequestLogger } from "../../../../logging/IRequestLogger";
|
import { IRequestLogger } from "../../../../logging/IRequestLogger";
|
||||||
import { IUserDataStore } from "../../../../storage/IUserDataStore";
|
import { IUserDataStore } from "../../../../storage/IUserDataStore";
|
||||||
import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler";
|
import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler";
|
||||||
|
import { TOTPSecret } from "../../../../../../types/TOTPSecret";
|
||||||
|
|
||||||
|
|
||||||
export default class RegistrationHandler implements IdentityValidable {
|
export default class RegistrationHandler implements IdentityValidable {
|
||||||
|
@ -35,21 +36,22 @@ export default class RegistrationHandler implements IdentityValidable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private retrieveIdentity(req: express.Request): BluebirdPromise<Identity> {
|
private retrieveIdentity(req: express.Request): BluebirdPromise<Identity> {
|
||||||
return AuthenticationSession.get(req, this.logger)
|
const that = this;
|
||||||
.then(function (authSession) {
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
const userid = authSession.userid;
|
const authSession = AuthenticationSessionHandler.get(req, that.logger);
|
||||||
const email = authSession.email;
|
const userid = authSession.userid;
|
||||||
|
const email = authSession.email;
|
||||||
|
|
||||||
if (!(userid && email)) {
|
if (!(userid && email)) {
|
||||||
return BluebirdPromise.reject(new Error("User ID or email is missing."));
|
return reject(new Error("User ID or email is missing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const identity = {
|
const identity = {
|
||||||
email: email,
|
email: email,
|
||||||
userid: userid
|
userid: userid
|
||||||
};
|
};
|
||||||
return BluebirdPromise.resolve(identity);
|
return resolve(identity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
preValidationInit(req: express.Request): BluebirdPromise<Identity> {
|
preValidationInit(req: express.Request): BluebirdPromise<Identity> {
|
||||||
|
@ -70,26 +72,31 @@ export default class RegistrationHandler implements IdentityValidable {
|
||||||
|
|
||||||
postValidationResponse(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
postValidationResponse(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
const that = this;
|
const that = this;
|
||||||
return AuthenticationSession.get(req, this.logger)
|
let secret: TOTPSecret;
|
||||||
.then(function (authSession) {
|
let userId: string;
|
||||||
const userid = authSession.identity_check.userid;
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
const challenge = authSession.identity_check.challenge;
|
const authSession = AuthenticationSessionHandler.get(req, that.logger);
|
||||||
|
const challenge = authSession.identity_check.challenge;
|
||||||
|
userId = authSession.identity_check.userid;
|
||||||
|
|
||||||
if (challenge != Constants.CHALLENGE || !userid) {
|
if (challenge != Constants.CHALLENGE || !userId) {
|
||||||
return BluebirdPromise.reject(new Error("Bad challenge."));
|
return reject(new Error("Bad challenge."));
|
||||||
}
|
}
|
||||||
const secret = that.totp.generate();
|
resolve();
|
||||||
that.logger.debug(req, "Save the TOTP secret in DB.");
|
})
|
||||||
return that.userDataStore.saveTOTPSecret(userid, secret)
|
.then(function () {
|
||||||
.then(function () {
|
secret = that.totp.generate();
|
||||||
AuthenticationSession.reset(req);
|
that.logger.debug(req, "Save the TOTP secret in DB");
|
||||||
|
return that.userDataStore.saveTOTPSecret(userId, secret);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
AuthenticationSessionHandler.reset(req);
|
||||||
|
|
||||||
res.render(Constants.TEMPLATE_NAME, {
|
res.render(Constants.TEMPLATE_NAME, {
|
||||||
base32_secret: secret.base32,
|
base32_secret: secret.base32,
|
||||||
otpauth_url: secret.otpauth_url,
|
otpauth_url: secret.otpauth_url,
|
||||||
login_endpoint: Endpoints.FIRST_FACTOR_GET
|
login_endpoint: Endpoints.FIRST_FACTOR_GET
|
||||||
});
|
});
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.catch(ErrorReplies.replyWithError200(req, res, that.logger, UserMessages.OPERATION_FAILED));
|
.catch(ErrorReplies.replyWithError200(req, res, that.logger, UserMessages.OPERATION_FAILED));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,10 @@ import objectPath = require("object-path");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument";
|
import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument";
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
|
||||||
import Endpoints = require("../../../../../../../shared/api");
|
import Endpoints = require("../../../../../../../shared/api");
|
||||||
import redirect from "../../redirect";
|
import redirect from "../../redirect";
|
||||||
import ErrorReplies = require("../../../../ErrorReplies");
|
import ErrorReplies = require("../../../../ErrorReplies");
|
||||||
import AuthenticationSessionHandler = require("../../../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
||||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
|
@ -20,10 +19,12 @@ export default function (vars: ServerVariables) {
|
||||||
let authSession: AuthenticationSession;
|
let authSession: AuthenticationSession;
|
||||||
const token = req.body.token;
|
const token = req.body.token;
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (_authSession) {
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
authSession = _authSession;
|
vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid);
|
||||||
vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid);
|
resolve();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
return vars.userDataStore.retrieveTOTPSecret(authSession.userid);
|
return vars.userDataStore.retrieveTOTPSecret(authSession.userid);
|
||||||
})
|
})
|
||||||
.then(function (doc: TOTPSecretDocument) {
|
.then(function (doc: TOTPSecretDocument) {
|
||||||
|
@ -38,5 +39,5 @@ export default function (vars: ServerVariables) {
|
||||||
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
||||||
UserMessages.OPERATION_FAILED));
|
UserMessages.OPERATION_FAILED));
|
||||||
}
|
}
|
||||||
return FirstFactorBlocker(handler, vars.logger);
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { IdentityValidable } from "../../../../IdentityCheckMiddleware";
|
||||||
import { Identity } from "../../../../../../types/Identity";
|
import { Identity } from "../../../../../../types/Identity";
|
||||||
import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate";
|
import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate";
|
||||||
import FirstFactorValidator = require("../../../../FirstFactorValidator");
|
import FirstFactorValidator = require("../../../../FirstFactorValidator");
|
||||||
import AuthenticationSession = require("../../../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import { IRequestLogger } from "../../../../logging/IRequestLogger";
|
import { IRequestLogger } from "../../../../logging/IRequestLogger";
|
||||||
|
|
||||||
const CHALLENGE = "u2f-register";
|
const CHALLENGE = "u2f-register";
|
||||||
|
@ -28,21 +28,22 @@ export default class RegistrationHandler implements IdentityValidable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private retrieveIdentity(req: express.Request): BluebirdPromise<Identity> {
|
private retrieveIdentity(req: express.Request): BluebirdPromise<Identity> {
|
||||||
return AuthenticationSession.get(req, this.logger)
|
const that = this;
|
||||||
.then(function (authSession) {
|
return new BluebirdPromise(function(resolve, reject) {
|
||||||
const userid = authSession.userid;
|
const authSession = AuthenticationSessionHandler.get(req, that.logger);
|
||||||
const email = authSession.email;
|
const userid = authSession.userid;
|
||||||
|
const email = authSession.email;
|
||||||
|
|
||||||
if (!(userid && email)) {
|
if (!(userid && email)) {
|
||||||
return BluebirdPromise.reject(new Error("User ID or email is missing"));
|
return reject(new Error("User ID or email is missing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const identity = {
|
const identity = {
|
||||||
email: email,
|
email: email,
|
||||||
userid: userid
|
userid: userid
|
||||||
};
|
};
|
||||||
return BluebirdPromise.resolve(identity);
|
return resolve(identity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
preValidationInit(req: express.Request): BluebirdPromise<Identity> {
|
preValidationInit(req: express.Request): BluebirdPromise<Identity> {
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
|
|
||||||
import { UserDataStore } from "../../../../storage/UserDataStore";
|
import { UserDataStore } from "../../../../storage/UserDataStore";
|
||||||
|
|
||||||
import objectPath = require("object-path");
|
import objectPath = require("object-path");
|
||||||
import u2f_common = require("../U2FCommon");
|
import u2f_common = require("../U2FCommon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import U2f = require("u2f");
|
import U2f = require("u2f");
|
||||||
import { U2FRegistration } from "../../../../../../types/U2FRegistration";
|
import { U2FRegistration } from "../../../../../../types/U2FRegistration";
|
||||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
|
||||||
import redirect from "../../redirect";
|
import redirect from "../../redirect";
|
||||||
import ErrorReplies = require("../../../../ErrorReplies");
|
import ErrorReplies = require("../../../../ErrorReplies");
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
import AuthenticationSessionHandler = require("../../../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||||
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
||||||
|
|
||||||
|
@ -22,26 +20,25 @@ export default function (vars: ServerVariables) {
|
||||||
const appid = u2f_common.extract_app_id(req);
|
const appid = u2f_common.extract_app_id(req);
|
||||||
const registrationResponse: U2f.RegistrationData = req.body;
|
const registrationResponse: U2f.RegistrationData = req.body;
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (_authSession) {
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
authSession = _authSession;
|
const registrationRequest = authSession.register_request;
|
||||||
const registrationRequest = authSession.register_request;
|
|
||||||
|
|
||||||
if (!registrationRequest) {
|
if (!registrationRequest) {
|
||||||
return BluebirdPromise.reject(new Error("No registration request"));
|
return reject(new Error("No registration request"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authSession.identity_check
|
if (!authSession.identity_check
|
||||||
|| authSession.identity_check.challenge != "u2f-register") {
|
|| authSession.identity_check.challenge != "u2f-register") {
|
||||||
return BluebirdPromise.reject(new Error("Bad challenge for registration request"));
|
return reject(new Error("Bad challenge for registration request"));
|
||||||
}
|
}
|
||||||
|
|
||||||
vars.logger.info(req, "Finishing registration");
|
vars.logger.info(req, "Finishing registration");
|
||||||
vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest));
|
vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest));
|
||||||
vars.logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse));
|
vars.logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse));
|
||||||
|
|
||||||
return BluebirdPromise.resolve(vars.u2f.checkRegistration(registrationRequest, registrationResponse));
|
return resolve(vars.u2f.checkRegistration(registrationRequest, registrationResponse));
|
||||||
})
|
})
|
||||||
.then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise<void> {
|
.then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise<void> {
|
||||||
if (objectPath.has(u2fResult, "errorCode"))
|
if (objectPath.has(u2fResult, "errorCode"))
|
||||||
return BluebirdPromise.reject(new Error("Error while registering."));
|
return BluebirdPromise.reject(new Error("Error while registering."));
|
||||||
|
@ -63,6 +60,5 @@ export default function (vars: ServerVariables) {
|
||||||
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
||||||
UserMessages.OPERATION_FAILED));
|
UserMessages.OPERATION_FAILED));
|
||||||
}
|
}
|
||||||
|
return handler;
|
||||||
return FirstFactorBlocker(handler, vars.logger);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,8 @@ import u2f_common = require("../U2FCommon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import U2f = require("u2f");
|
import U2f = require("u2f");
|
||||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
|
||||||
import ErrorReplies = require("../../../../ErrorReplies");
|
import ErrorReplies = require("../../../../ErrorReplies");
|
||||||
import AuthenticationSessionHandler = require("../../../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
||||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
|
@ -18,21 +17,18 @@ export default function (vars: ServerVariables) {
|
||||||
let authSession: AuthenticationSession;
|
let authSession: AuthenticationSession;
|
||||||
const appid: string = u2f_common.extract_app_id(req);
|
const appid: string = u2f_common.extract_app_id(req);
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (_authSession) {
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
authSession = _authSession;
|
if (!authSession.identity_check
|
||||||
|
|| authSession.identity_check.challenge != "u2f-register") {
|
||||||
|
res.status(403);
|
||||||
|
res.send();
|
||||||
|
return reject(new Error("Bad challenge."));
|
||||||
|
}
|
||||||
|
|
||||||
if (!authSession.identity_check
|
vars.logger.info(req, "Starting registration for appId '%s'", appid);
|
||||||
|| authSession.identity_check.challenge != "u2f-register") {
|
return resolve(vars.u2f.request(appid));
|
||||||
res.status(403);
|
})
|
||||||
res.send();
|
|
||||||
return BluebirdPromise.reject(new Error("Bad challenge."));
|
|
||||||
}
|
|
||||||
|
|
||||||
vars.logger.info(req, "Starting registration for appId '%s'", appid);
|
|
||||||
|
|
||||||
return BluebirdPromise.resolve(vars.u2f.request(appid));
|
|
||||||
})
|
|
||||||
.then(function (registrationRequest: U2f.Request) {
|
.then(function (registrationRequest: U2f.Request) {
|
||||||
vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest));
|
vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest));
|
||||||
authSession.register_request = registrationRequest;
|
authSession.register_request = registrationRequest;
|
||||||
|
@ -43,5 +39,5 @@ export default function (vars: ServerVariables) {
|
||||||
UserMessages.OPERATION_FAILED));
|
UserMessages.OPERATION_FAILED));
|
||||||
}
|
}
|
||||||
|
|
||||||
return FirstFactorBlocker(handler, vars.logger);
|
return handler;
|
||||||
}
|
}
|
|
@ -8,11 +8,10 @@ import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocu
|
||||||
import { Winston } from "../../../../../../types/Dependencies";
|
import { Winston } from "../../../../../../types/Dependencies";
|
||||||
import U2f = require("u2f");
|
import U2f = require("u2f");
|
||||||
import exceptions = require("../../../../Exceptions");
|
import exceptions = require("../../../../Exceptions");
|
||||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
|
||||||
import redirect from "../../redirect";
|
import redirect from "../../redirect";
|
||||||
import ErrorReplies = require("../../../../ErrorReplies");
|
import ErrorReplies = require("../../../../ErrorReplies");
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
import AuthenticationSessionHandler = require("../../../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||||
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
||||||
|
|
||||||
|
@ -21,14 +20,16 @@ export default function (vars: ServerVariables) {
|
||||||
let authSession: AuthenticationSession;
|
let authSession: AuthenticationSession;
|
||||||
const appId = u2f_common.extract_app_id(req);
|
const appId = u2f_common.extract_app_id(req);
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (_authSession) {
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
authSession = _authSession;
|
if (!authSession.sign_request) {
|
||||||
if (!authSession.sign_request) {
|
const err = new Error("No sign request");
|
||||||
const err = new Error("No sign request");
|
ErrorReplies.replyWithError401(req, res, vars.logger)(err);
|
||||||
ErrorReplies.replyWithError401(req, res, vars.logger)(err);
|
return reject(err);
|
||||||
return BluebirdPromise.reject(err);
|
}
|
||||||
}
|
resolve();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
const userid = authSession.userid;
|
const userid = authSession.userid;
|
||||||
return vars.userDataStore.retrieveU2FRegistration(userid, appId);
|
return vars.userDataStore.retrieveU2FRegistration(userid, appId);
|
||||||
})
|
})
|
||||||
|
@ -50,6 +51,6 @@ export default function (vars: ServerVariables) {
|
||||||
UserMessages.OPERATION_FAILED));
|
UserMessages.OPERATION_FAILED));
|
||||||
}
|
}
|
||||||
|
|
||||||
return FirstFactorBlocker(handler, vars.logger);
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,8 @@ import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocu
|
||||||
import { Winston } from "../../../../../../types/Dependencies";
|
import { Winston } from "../../../../../../types/Dependencies";
|
||||||
import exceptions = require("../../../../Exceptions");
|
import exceptions = require("../../../../Exceptions");
|
||||||
import { SignMessage } from "../../../../../../../shared/SignMessage";
|
import { SignMessage } from "../../../../../../../shared/SignMessage";
|
||||||
import FirstFactorBlocker from "../../../FirstFactorBlocker";
|
|
||||||
import ErrorReplies = require("../../../../ErrorReplies");
|
import ErrorReplies = require("../../../../ErrorReplies");
|
||||||
import AuthenticationSessionHandler = require("../../../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import UserMessages = require("../../../../../../../shared/UserMessages");
|
import UserMessages = require("../../../../../../../shared/UserMessages");
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
||||||
|
@ -21,9 +20,11 @@ export default function (vars: ServerVariables) {
|
||||||
let authSession: AuthenticationSession;
|
let authSession: AuthenticationSession;
|
||||||
const appId = u2f_common.extract_app_id(req);
|
const appId = u2f_common.extract_app_id(req);
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (_authSession) {
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
authSession = _authSession;
|
resolve();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
return vars.userDataStore.retrieveU2FRegistration(authSession.userid, appId);
|
return vars.userDataStore.retrieveU2FRegistration(authSession.userid, appId);
|
||||||
})
|
})
|
||||||
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<SignMessage> {
|
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<SignMessage> {
|
||||||
|
@ -51,6 +52,5 @@ export default function (vars: ServerVariables) {
|
||||||
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
||||||
UserMessages.OPERATION_FAILED));
|
UserMessages.OPERATION_FAILED));
|
||||||
}
|
}
|
||||||
|
return handler;
|
||||||
return FirstFactorBlocker(handler, vars.logger);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,13 @@ import winston = require("winston");
|
||||||
import AuthenticationValidator = require("../../AuthenticationValidator");
|
import AuthenticationValidator = require("../../AuthenticationValidator");
|
||||||
import ErrorReplies = require("../../ErrorReplies");
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
import { AppConfiguration } from "../../configuration/Configuration";
|
import { AppConfiguration } from "../../configuration/Configuration";
|
||||||
import AuthenticationSessionHandler = require("../../AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
||||||
import Constants = require("../../../../../shared/constants");
|
import Constants = require("../../../../../shared/constants");
|
||||||
import Util = require("util");
|
import Util = require("util");
|
||||||
import { DomainExtractor } from "../../utils/DomainExtractor";
|
import { DomainExtractor } from "../../utils/DomainExtractor";
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import { AuthenticationMethodCalculator } from "../../AuthenticationMethodCalculator";
|
import { MethodCalculator } from "../../authentication/MethodCalculator";
|
||||||
import { IRequestLogger } from "../../logging/IRequestLogger";
|
import { IRequestLogger } from "../../logging/IRequestLogger";
|
||||||
|
|
||||||
const FIRST_FACTOR_NOT_VALIDATED_MESSAGE = "First factor not yet validated";
|
const FIRST_FACTOR_NOT_VALIDATED_MESSAGE = "First factor not yet validated";
|
||||||
|
@ -50,49 +50,50 @@ function verify_inactivity(req: express.Request,
|
||||||
|
|
||||||
function verify_filter(req: express.Request, res: express.Response,
|
function verify_filter(req: express.Request, res: express.Response,
|
||||||
vars: ServerVariables): BluebirdPromise<void> {
|
vars: ServerVariables): BluebirdPromise<void> {
|
||||||
let _authSession: AuthenticationSession;
|
let authSession: AuthenticationSession;
|
||||||
let username: string;
|
let username: string;
|
||||||
let groups: string[];
|
let groups: string[];
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req, vars.logger)
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
.then(function (authSession) {
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
_authSession = authSession;
|
username = authSession.userid;
|
||||||
username = _authSession.userid;
|
groups = authSession.groups;
|
||||||
groups = _authSession.groups;
|
|
||||||
|
|
||||||
res.set("Redirect", encodeURIComponent("https://" + req.headers["host"] +
|
res.set("Redirect", encodeURIComponent("https://" + req.headers["host"] +
|
||||||
req.headers["x-original-uri"]));
|
req.headers["x-original-uri"]));
|
||||||
|
|
||||||
if (!_authSession.userid)
|
if (!authSession.userid) {
|
||||||
return BluebirdPromise.reject(
|
reject(new exceptions.AccessDeniedError(
|
||||||
new exceptions.AccessDeniedError(FIRST_FACTOR_NOT_VALIDATED_MESSAGE));
|
Util.format("%s: %s.", FIRST_FACTOR_NOT_VALIDATED_MESSAGE, "userid is missing")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const host = objectPath.get<express.Request, string>(req, "headers.host");
|
const host = objectPath.get<express.Request, string>(req, "headers.host");
|
||||||
const path = objectPath.get<express.Request, string>(req, "headers.x-original-uri");
|
const path = objectPath.get<express.Request, string>(req, "headers.x-original-uri");
|
||||||
|
|
||||||
const domain = DomainExtractor.fromHostHeader(host);
|
const domain = DomainExtractor.fromHostHeader(host);
|
||||||
const authenticationMethod =
|
const authenticationMethod =
|
||||||
new AuthenticationMethodCalculator(vars.config.authentication_methods)
|
MethodCalculator.compute(vars.config.authentication_methods, domain);
|
||||||
.compute(domain);
|
vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", domain, path,
|
||||||
vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", domain, path,
|
username, groups.join(","));
|
||||||
username, groups.join(","));
|
|
||||||
|
|
||||||
if (!_authSession.first_factor)
|
if (!authSession.first_factor)
|
||||||
return BluebirdPromise.reject(
|
return reject(new exceptions.AccessDeniedError(
|
||||||
new exceptions.AccessDeniedError(FIRST_FACTOR_NOT_VALIDATED_MESSAGE));
|
Util.format("%s: %s.", FIRST_FACTOR_NOT_VALIDATED_MESSAGE, "first factor is false")));
|
||||||
|
|
||||||
if (authenticationMethod == "two_factor" && !_authSession.second_factor)
|
if (authenticationMethod == "two_factor" && !authSession.second_factor)
|
||||||
return BluebirdPromise.reject(
|
return reject(new exceptions.AccessDeniedError(
|
||||||
new exceptions.AccessDeniedError(SECOND_FACTOR_NOT_VALIDATED_MESSAGE));
|
Util.format("%s: %s.", SECOND_FACTOR_NOT_VALIDATED_MESSAGE, "second factor is false")));
|
||||||
|
|
||||||
const isAllowed = vars.accessController.isAccessAllowed(domain, path, username, groups);
|
const isAllowed = vars.accessController.isAccessAllowed(domain, path, username, groups);
|
||||||
if (!isAllowed) return BluebirdPromise.reject(
|
if (!isAllowed) return reject(
|
||||||
new exceptions.DomainAccessDenied(Util.format("User '%s' does not have access to '%s'",
|
new exceptions.DomainAccessDenied(Util.format("User '%s' does not have access to '%s'",
|
||||||
username, domain)));
|
username, domain)));
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
})
|
resolve();
|
||||||
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return verify_inactivity(req, _authSession,
|
return verify_inactivity(req, authSession,
|
||||||
vars.config, vars.logger);
|
vars.config, vars.logger);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { AppConfiguration } from "../configuration/Configuration";
|
||||||
|
import { GlobalDependencies } from "../../../types/Dependencies";
|
||||||
|
import { SessionConfigurationBuilder } from
|
||||||
|
"../configuration/SessionConfigurationBuilder";
|
||||||
|
import Path = require("path");
|
||||||
|
import Express = require("express");
|
||||||
|
import * as BodyParser from "body-parser";
|
||||||
|
import { RestApi } from "./RestApi";
|
||||||
|
import { WithHeadersLogged } from "./middlewares/WithHeadersLogged";
|
||||||
|
import { ServerVariables } from "../ServerVariables";
|
||||||
|
|
||||||
|
const addRequestId = require("express-request-id")();
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
const TRUST_PROXY = "trust proxy";
|
||||||
|
const X_POWERED_BY = "x-powered-by";
|
||||||
|
const VIEWS = "views";
|
||||||
|
const VIEW_ENGINE = "view engine";
|
||||||
|
const PUG = "pug";
|
||||||
|
|
||||||
|
export class Configurator {
|
||||||
|
static configure(config: AppConfiguration,
|
||||||
|
app: Express.Application,
|
||||||
|
vars: ServerVariables,
|
||||||
|
deps: GlobalDependencies): void {
|
||||||
|
const viewsDirectory = Path.resolve(__dirname, "../../views");
|
||||||
|
const publicHtmlDirectory = Path.resolve(__dirname, "../../public_html");
|
||||||
|
|
||||||
|
const expressSessionOptions = SessionConfigurationBuilder.build(config, deps);
|
||||||
|
|
||||||
|
app.use(Express.static(publicHtmlDirectory));
|
||||||
|
app.use(BodyParser.urlencoded({ extended: false }));
|
||||||
|
app.use(BodyParser.json());
|
||||||
|
app.use(deps.session(expressSessionOptions));
|
||||||
|
app.use(addRequestId);
|
||||||
|
app.use(WithHeadersLogged.middleware(vars.logger));
|
||||||
|
app.disable(X_POWERED_BY);
|
||||||
|
app.enable(TRUST_PROXY);
|
||||||
|
|
||||||
|
app.set(VIEWS, viewsDirectory);
|
||||||
|
app.set(VIEW_ENGINE, PUG);
|
||||||
|
|
||||||
|
RestApi.setup(app, vars);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
import Express = require("express");
|
||||||
|
|
||||||
|
import FirstFactorGet = require("../routes/firstfactor/get");
|
||||||
|
import SecondFactorGet = require("../routes/secondfactor/get");
|
||||||
|
|
||||||
|
import FirstFactorPost = require("../routes/firstfactor/post");
|
||||||
|
import LogoutGet = require("../routes/logout/get");
|
||||||
|
import VerifyGet = require("../routes/verify/get");
|
||||||
|
import TOTPSignGet = require("../routes/secondfactor/totp/sign/post");
|
||||||
|
|
||||||
|
import IdentityCheckMiddleware = require("../IdentityCheckMiddleware");
|
||||||
|
|
||||||
|
import TOTPRegistrationIdentityHandler from "../routes/secondfactor/totp/identity/RegistrationHandler";
|
||||||
|
import U2FRegistrationIdentityHandler from "../routes/secondfactor/u2f/identity/RegistrationHandler";
|
||||||
|
import ResetPasswordIdentityHandler from "../routes/password-reset/identity/PasswordResetHandler";
|
||||||
|
|
||||||
|
import U2FSignPost = require("../routes/secondfactor/u2f/sign/post");
|
||||||
|
import U2FSignRequestGet = require("../routes/secondfactor/u2f/sign_request/get");
|
||||||
|
|
||||||
|
import U2FRegisterPost = require("../routes/secondfactor/u2f/register/post");
|
||||||
|
import U2FRegisterRequestGet = require("../routes/secondfactor/u2f/register_request/get");
|
||||||
|
|
||||||
|
import ResetPasswordFormPost = require("../routes/password-reset/form/post");
|
||||||
|
import ResetPasswordRequestPost = require("../routes/password-reset/request/get");
|
||||||
|
|
||||||
|
import Error401Get = require("../routes/error/401/get");
|
||||||
|
import Error403Get = require("../routes/error/403/get");
|
||||||
|
import Error404Get = require("../routes/error/404/get");
|
||||||
|
|
||||||
|
import LoggedIn = require("../routes/loggedin/get");
|
||||||
|
|
||||||
|
import { ServerVariables } from "../ServerVariables";
|
||||||
|
import Endpoints = require("../../../../shared/api");
|
||||||
|
import { RequireValidatedFirstFactor } from "./middlewares/RequireValidatedFirstFactor";
|
||||||
|
import { RequireTwoFactorEnabled } from "./middlewares/RequireTwoFactorEnabled";
|
||||||
|
|
||||||
|
function setupTotp(app: Express.Application, vars: ServerVariables) {
|
||||||
|
app.post(Endpoints.SECOND_FACTOR_TOTP_POST,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger),
|
||||||
|
TOTPSignGet.default(vars));
|
||||||
|
|
||||||
|
app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger));
|
||||||
|
|
||||||
|
app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger));
|
||||||
|
|
||||||
|
IdentityCheckMiddleware.register(app,
|
||||||
|
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
||||||
|
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET,
|
||||||
|
new TOTPRegistrationIdentityHandler(vars.logger,
|
||||||
|
vars.userDataStore, vars.totpHandler),
|
||||||
|
vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupU2f(app: Express.Application, vars: ServerVariables) {
|
||||||
|
app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger),
|
||||||
|
U2FSignRequestGet.default(vars));
|
||||||
|
|
||||||
|
app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger),
|
||||||
|
U2FSignPost.default(vars));
|
||||||
|
|
||||||
|
app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger),
|
||||||
|
U2FRegisterRequestGet.default(vars));
|
||||||
|
|
||||||
|
app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger),
|
||||||
|
U2FRegisterPost.default(vars));
|
||||||
|
|
||||||
|
app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger));
|
||||||
|
|
||||||
|
app.get(Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger));
|
||||||
|
|
||||||
|
IdentityCheckMiddleware.register(app,
|
||||||
|
Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET,
|
||||||
|
Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET,
|
||||||
|
new U2FRegistrationIdentityHandler(vars.logger), vars);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupResetPassword(app: Express.Application, vars: ServerVariables) {
|
||||||
|
IdentityCheckMiddleware.register(app, Endpoints.RESET_PASSWORD_IDENTITY_START_GET,
|
||||||
|
Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET,
|
||||||
|
new ResetPasswordIdentityHandler(vars.logger, vars.ldapEmailsRetriever), vars);
|
||||||
|
|
||||||
|
app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, ResetPasswordRequestPost.default);
|
||||||
|
app.post(Endpoints.RESET_PASSWORD_FORM_POST, ResetPasswordFormPost.default(vars));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupErrors(app: Express.Application) {
|
||||||
|
app.get(Endpoints.ERROR_401_GET, Error401Get.default);
|
||||||
|
app.get(Endpoints.ERROR_403_GET, Error403Get.default);
|
||||||
|
app.get(Endpoints.ERROR_404_GET, Error404Get.default);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RestApi {
|
||||||
|
static setup(app: Express.Application, vars: ServerVariables): void {
|
||||||
|
app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default(vars));
|
||||||
|
|
||||||
|
app.get(Endpoints.SECOND_FACTOR_GET,
|
||||||
|
RequireTwoFactorEnabled.middleware(vars.logger,
|
||||||
|
vars.config.authentication_methods),
|
||||||
|
RequireValidatedFirstFactor.middleware(vars.logger),
|
||||||
|
SecondFactorGet.default(vars));
|
||||||
|
app.get(Endpoints.LOGOUT_GET, LogoutGet.default);
|
||||||
|
|
||||||
|
app.get(Endpoints.VERIFY_GET, VerifyGet.default(vars));
|
||||||
|
app.post(Endpoints.FIRST_FACTOR_POST, FirstFactorPost.default(vars));
|
||||||
|
|
||||||
|
setupTotp(app, vars);
|
||||||
|
setupU2f(app, vars);
|
||||||
|
setupResetPassword(app, vars);
|
||||||
|
setupErrors(app);
|
||||||
|
|
||||||
|
app.get(Endpoints.LOGGED_IN, LoggedIn.default(vars));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import Express = require("express");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
|
import { IRequestLogger } from "../../logging/IRequestLogger";
|
||||||
|
import { MethodCalculator } from "../../authentication/MethodCalculator";
|
||||||
|
import { AuthenticationMethodsConfiguration } from
|
||||||
|
"../../configuration/Configuration";
|
||||||
|
|
||||||
|
export class RequireTwoFactorEnabled {
|
||||||
|
static middleware(logger: IRequestLogger,
|
||||||
|
configuration: AuthenticationMethodsConfiguration) {
|
||||||
|
|
||||||
|
return function (req: Express.Request, res: Express.Response,
|
||||||
|
next: Express.NextFunction): void {
|
||||||
|
|
||||||
|
const isSingleFactorMode = MethodCalculator.isSingleFactorOnlyMode(
|
||||||
|
configuration);
|
||||||
|
|
||||||
|
if (isSingleFactorMode) {
|
||||||
|
ErrorReplies.replyWithError401(req, res, logger)(new Error(
|
||||||
|
"Restricted access because server is in single factor mode."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Express = require("express");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
|
import { IRequestLogger } from "../../logging/IRequestLogger";
|
||||||
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
|
import Exceptions = require("../../Exceptions");
|
||||||
|
|
||||||
|
export class RequireValidatedFirstFactor {
|
||||||
|
static middleware(logger: IRequestLogger) {
|
||||||
|
return function (req: Express.Request, res: Express.Response,
|
||||||
|
next: Express.NextFunction): BluebirdPromise<void> {
|
||||||
|
|
||||||
|
return new BluebirdPromise<void>(function (resolve, reject) {
|
||||||
|
const authSession = AuthenticationSessionHandler.get(req, logger);
|
||||||
|
if (!authSession.userid || !authSession.first_factor)
|
||||||
|
return reject(
|
||||||
|
new Exceptions.FirstFactorValidationError(
|
||||||
|
"First factor has not been validated yet."));
|
||||||
|
|
||||||
|
next();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.catch(ErrorReplies.replyWithError401(req, res, logger));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Express = require("express");
|
||||||
|
import { IRequestLogger } from "../../logging/IRequestLogger";
|
||||||
|
|
||||||
|
export class WithHeadersLogged {
|
||||||
|
static middleware(logger: IRequestLogger) {
|
||||||
|
return function (req: Express.Request, res: Express.Response,
|
||||||
|
next: Express.NextFunction): void {
|
||||||
|
logger.debug(req, "Headers = %s", JSON.stringify(req.headers));
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
import { AuthenticationMethodCalculator } from "../src/lib/AuthenticationMethodCalculator";
|
|
||||||
import { AuthenticationMethodsConfiguration } from "../src/lib/configuration/Configuration";
|
|
||||||
import Assert = require("assert");
|
|
||||||
|
|
||||||
describe("test authentication method calculator", function() {
|
|
||||||
it("should return default method when sub domain not overriden", function() {
|
|
||||||
const options1: AuthenticationMethodsConfiguration = {
|
|
||||||
default_method: "two_factor",
|
|
||||||
per_subdomain_methods: {}
|
|
||||||
};
|
|
||||||
const options2: AuthenticationMethodsConfiguration = {
|
|
||||||
default_method: "single_factor",
|
|
||||||
per_subdomain_methods: {}
|
|
||||||
};
|
|
||||||
const calculator1 = new AuthenticationMethodCalculator(options1);
|
|
||||||
const calculator2 = new AuthenticationMethodCalculator(options2);
|
|
||||||
Assert.equal(calculator1.compute("www.example.com"), "two_factor");
|
|
||||||
Assert.equal(calculator2.compute("www.example.com"), "single_factor");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return overridden method when sub domain method is defined", function() {
|
|
||||||
const options1: AuthenticationMethodsConfiguration = {
|
|
||||||
default_method: "two_factor",
|
|
||||||
per_subdomain_methods: {
|
|
||||||
"www.example.com": "single_factor"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const calculator1 = new AuthenticationMethodCalculator(options1);
|
|
||||||
Assert.equal(calculator1.compute("www.example.com"), "single_factor");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
import sinon = require("sinon");
|
import sinon = require("sinon");
|
||||||
import IdentityValidator = require("../src/lib/IdentityCheckMiddleware");
|
import IdentityValidator = require("../src/lib/IdentityCheckMiddleware");
|
||||||
import AuthenticationSessionHandler = require("../src/lib/AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../src/lib/AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../types/AuthenticationSession";
|
import { AuthenticationSession } from "../types/AuthenticationSession";
|
||||||
import { UserDataStore } from "../src/lib/storage/UserDataStore";
|
import { UserDataStore } from "../src/lib/storage/UserDataStore";
|
||||||
import exceptions = require("../src/lib/Exceptions");
|
import exceptions = require("../src/lib/Exceptions");
|
||||||
|
@ -155,14 +155,10 @@ describe("test identity check process", function () {
|
||||||
req.query.identity_token = "token";
|
req.query.identity_token = "token";
|
||||||
|
|
||||||
req.session = {};
|
req.session = {};
|
||||||
let authSession: AuthenticationSession;
|
const authSession: AuthenticationSession = AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
const callback = IdentityValidator.get_finish_validation(identityValidable, vars);
|
const callback = IdentityValidator.get_finish_validation(identityValidable, vars);
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
return callback(req as any, res as any, undefined)
|
||||||
.then(function (_authSession) {
|
|
||||||
authSession = _authSession;
|
|
||||||
return callback(req as any, res as any, undefined);
|
|
||||||
})
|
|
||||||
.then(function () { return BluebirdPromise.reject("Should fail"); })
|
.then(function () { return BluebirdPromise.reject("Should fail"); })
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
Assert.equal(authSession.identity_check.userid, "user");
|
Assert.equal(authSession.identity_check.userid, "user");
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { MethodCalculator }
|
||||||
|
from "../../src/lib/authentication/MethodCalculator";
|
||||||
|
import { AuthenticationMethodsConfiguration }
|
||||||
|
from "../../src/lib/configuration/Configuration";
|
||||||
|
import Assert = require("assert");
|
||||||
|
|
||||||
|
describe("test MethodCalculator", function () {
|
||||||
|
describe("test compute method", function () {
|
||||||
|
it("should return default method when sub domain not overriden",
|
||||||
|
function () {
|
||||||
|
const options1: AuthenticationMethodsConfiguration = {
|
||||||
|
default_method: "two_factor",
|
||||||
|
per_subdomain_methods: {}
|
||||||
|
};
|
||||||
|
const options2: AuthenticationMethodsConfiguration = {
|
||||||
|
default_method: "single_factor",
|
||||||
|
per_subdomain_methods: {}
|
||||||
|
};
|
||||||
|
Assert.equal(MethodCalculator.compute(options1, "www.example.com"),
|
||||||
|
"two_factor");
|
||||||
|
Assert.equal(MethodCalculator.compute(options2, "www.example.com"),
|
||||||
|
"single_factor");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return overridden method when sub domain method is defined",
|
||||||
|
function () {
|
||||||
|
const options1: AuthenticationMethodsConfiguration = {
|
||||||
|
default_method: "two_factor",
|
||||||
|
per_subdomain_methods: {
|
||||||
|
"www.example.com": "single_factor"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Assert.equal(MethodCalculator.compute(options1, "www.example.com"),
|
||||||
|
"single_factor");
|
||||||
|
Assert.equal(MethodCalculator.compute(options1, "home.example.com"),
|
||||||
|
"two_factor");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("test isSingleFactorOnlyMode method", function () {
|
||||||
|
it("should return true when default domains and all domains are single_factor",
|
||||||
|
function () {
|
||||||
|
const options: AuthenticationMethodsConfiguration = {
|
||||||
|
default_method: "single_factor",
|
||||||
|
per_subdomain_methods: {
|
||||||
|
"www.example.com": "single_factor"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Assert(MethodCalculator.isSingleFactorOnlyMode(options));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when default domains is single_factor and at least one sub-domain is two_factor", function () {
|
||||||
|
const options: AuthenticationMethodsConfiguration = {
|
||||||
|
default_method: "single_factor",
|
||||||
|
per_subdomain_methods: {
|
||||||
|
"www.example.com": "two_factor",
|
||||||
|
"home.example.com": "single_factor"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Assert(!MethodCalculator.isSingleFactorOnlyMode(options));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when default domains is two_factor", function () {
|
||||||
|
const options: AuthenticationMethodsConfiguration = {
|
||||||
|
default_method: "two_factor",
|
||||||
|
per_subdomain_methods: {
|
||||||
|
"www.example.com": "single_factor",
|
||||||
|
"home.example.com": "single_factor"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Assert(!MethodCalculator.isSingleFactorOnlyMode(options));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -28,7 +28,7 @@ describe("test validator", function () {
|
||||||
"data.regulation should have required property 'max_retries'",
|
"data.regulation should have required property 'max_retries'",
|
||||||
"data.session should have required property 'secret'",
|
"data.session should have required property 'secret'",
|
||||||
"Storage must be either 'local' or 'mongo'",
|
"Storage must be either 'local' or 'mongo'",
|
||||||
"Notifier must be either 'filesystem', 'email' or 'smtp'"
|
"A notifier needs to be declared when server is used with two-factor"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Assert.deepStrictEqual(Validator.isValid({
|
Assert.deepStrictEqual(Validator.isValid({
|
||||||
|
@ -54,7 +54,7 @@ describe("test validator", function () {
|
||||||
}
|
}
|
||||||
}), [
|
}), [
|
||||||
"data.storage has unknown key 'abc'",
|
"data.storage has unknown key 'abc'",
|
||||||
"data.notifier has unknown key 'abcd'"
|
"Notifier must be either 'filesystem', 'email' or 'smtp'"
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -89,4 +89,93 @@ describe("test validator", function () {
|
||||||
}
|
}
|
||||||
}), []);
|
}), []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should return false when notifier is not defined while there is at least \
|
||||||
|
one second factor enabled sub-domain", function () {
|
||||||
|
const options1 = {
|
||||||
|
ldap: {
|
||||||
|
base_dn: "dc=example,dc=com",
|
||||||
|
password: "password",
|
||||||
|
url: "ldap://ldap",
|
||||||
|
user: "user"
|
||||||
|
},
|
||||||
|
authentication_methods: {
|
||||||
|
default_method: "two_factor"
|
||||||
|
},
|
||||||
|
notifier: {},
|
||||||
|
regulation: {
|
||||||
|
ban_time: 120,
|
||||||
|
find_time: 30,
|
||||||
|
max_retries: 3
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
secret: "unsecure_secret"
|
||||||
|
},
|
||||||
|
storage: {
|
||||||
|
local: {
|
||||||
|
path: "/var/lib/authelia"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const options2 = {
|
||||||
|
ldap: {
|
||||||
|
base_dn: "dc=example,dc=com",
|
||||||
|
password: "password",
|
||||||
|
url: "ldap://ldap",
|
||||||
|
user: "user"
|
||||||
|
},
|
||||||
|
authentication_methods: {
|
||||||
|
default_method: "two_factor"
|
||||||
|
},
|
||||||
|
notifier: {
|
||||||
|
email: {
|
||||||
|
username: "user@gmail.com",
|
||||||
|
password: "pass",
|
||||||
|
sender: "admin@example.com",
|
||||||
|
service: "gmail"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
regulation: {
|
||||||
|
ban_time: 120,
|
||||||
|
find_time: 30,
|
||||||
|
max_retries: 3
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
secret: "unsecure_secret"
|
||||||
|
},
|
||||||
|
storage: {
|
||||||
|
local: {
|
||||||
|
path: "/var/lib/authelia"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const options3 = {
|
||||||
|
ldap: {
|
||||||
|
base_dn: "dc=example,dc=com",
|
||||||
|
password: "password",
|
||||||
|
url: "ldap://ldap",
|
||||||
|
user: "user"
|
||||||
|
},
|
||||||
|
authentication_methods: {
|
||||||
|
default_method: "single_factor"
|
||||||
|
},
|
||||||
|
notifier: {},
|
||||||
|
regulation: {
|
||||||
|
ban_time: 120,
|
||||||
|
find_time: 30,
|
||||||
|
max_retries: 3
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
secret: "unsecure_secret"
|
||||||
|
},
|
||||||
|
storage: {
|
||||||
|
local: {
|
||||||
|
path: "/var/lib/authelia"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Assert.deepStrictEqual(Validator.isValid(options1), ["A notifier needs to be declared when server is used with two-factor"]);
|
||||||
|
Assert.deepStrictEqual(Validator.isValid(options2), []);
|
||||||
|
Assert.deepStrictEqual(Validator.isValid(options3), []);
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -1,26 +1,38 @@
|
||||||
import { IRequestLogger } from "../../src/lib/logging/IRequestLogger";
|
import { IRequestLogger } from "../../src/lib/logging/IRequestLogger";
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
|
import { RequestLogger } from "../../src/lib/logging/RequestLogger";
|
||||||
|
import Winston = require("winston");
|
||||||
|
import Express = require("express");
|
||||||
|
|
||||||
export class RequestLoggerStub implements IRequestLogger {
|
export class RequestLoggerStub implements IRequestLogger {
|
||||||
infoStub: Sinon.SinonStub;
|
infoStub: Sinon.SinonStub;
|
||||||
debugStub: Sinon.SinonStub;
|
debugStub: Sinon.SinonStub;
|
||||||
errorStub: Sinon.SinonStub;
|
errorStub: Sinon.SinonStub;
|
||||||
|
private requestLogger: RequestLogger;
|
||||||
|
|
||||||
constructor() {
|
constructor(enableLogging?: boolean) {
|
||||||
this.infoStub = Sinon.stub();
|
this.infoStub = Sinon.stub();
|
||||||
this.debugStub = Sinon.stub();
|
this.debugStub = Sinon.stub();
|
||||||
this.errorStub = Sinon.stub();
|
this.errorStub = Sinon.stub();
|
||||||
|
if (enableLogging)
|
||||||
|
this.requestLogger = new RequestLogger(Winston);
|
||||||
}
|
}
|
||||||
|
|
||||||
info(req: Express.Request, message: string, ...args: any[]): void {
|
info(req: Express.Request, message: string, ...args: any[]): void {
|
||||||
return this.infoStub(req, message, ...args);
|
if (this.requestLogger)
|
||||||
|
this.requestLogger.info(req, message, ...args);
|
||||||
|
this.infoStub(req, message, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(req: Express.Request, message: string, ...args: any[]): void {
|
debug(req: Express.Request, message: string, ...args: any[]): void {
|
||||||
return this.debugStub(req, message, ...args);
|
if (this.requestLogger)
|
||||||
|
this.requestLogger.info(req, message, ...args);
|
||||||
|
this.debugStub(req, message, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
error(req: Express.Request, message: string, ...args: any[]): void {
|
error(req: Express.Request, message: string, ...args: any[]): void {
|
||||||
return this.errorStub(req, message, ...args);
|
if (this.requestLogger)
|
||||||
|
this.requestLogger.info(req, message, ...args);
|
||||||
|
this.errorStub(req, message, ...args);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,7 +27,7 @@ export interface ServerVariablesMock {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ServerVariablesMockBuilder {
|
export class ServerVariablesMockBuilder {
|
||||||
static build(): { variables: ServerVariables, mocks: ServerVariablesMock} {
|
static build(enableLogging?: boolean): { variables: ServerVariables, mocks: ServerVariablesMock} {
|
||||||
const mocks: ServerVariablesMock = {
|
const mocks: ServerVariablesMock = {
|
||||||
accessController: new AccessControllerStub(),
|
accessController: new AccessControllerStub(),
|
||||||
config: {
|
config: {
|
||||||
|
@ -62,7 +62,7 @@ export class ServerVariablesMockBuilder {
|
||||||
ldapAuthenticator: new AuthenticatorStub(),
|
ldapAuthenticator: new AuthenticatorStub(),
|
||||||
ldapEmailsRetriever: new EmailsRetrieverStub(),
|
ldapEmailsRetriever: new EmailsRetrieverStub(),
|
||||||
ldapPasswordUpdater: new PasswordUpdaterStub(),
|
ldapPasswordUpdater: new PasswordUpdaterStub(),
|
||||||
logger: new RequestLoggerStub(),
|
logger: new RequestLoggerStub(enableLogging),
|
||||||
notifier: new NotifierStub(),
|
notifier: new NotifierStub(),
|
||||||
regulator: new RegulatorStub(),
|
regulator: new RegulatorStub(),
|
||||||
totpHandler: new TotpHandlerStub(),
|
totpHandler: new TotpHandlerStub(),
|
||||||
|
|
|
@ -53,7 +53,11 @@ export function RequestMock(): RequestMock {
|
||||||
return {
|
return {
|
||||||
app: {
|
app: {
|
||||||
get: sinon.stub()
|
get: sinon.stub()
|
||||||
}
|
},
|
||||||
|
headers: {
|
||||||
|
"x-forwarded-for": "127.0.0.1"
|
||||||
|
},
|
||||||
|
session: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export function ResponseMock(): ResponseMock {
|
export function ResponseMock(): ResponseMock {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import BluebirdPromise = require("bluebird");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import FirstFactorPost = require("../../../src/lib/routes/firstfactor/post");
|
import FirstFactorPost = require("../../../src/lib/routes/firstfactor/post");
|
||||||
import exceptions = require("../../../src/lib/Exceptions");
|
import exceptions = require("../../../src/lib/Exceptions");
|
||||||
import AuthenticationSessionHandler = require("../../../src/lib/AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../src/lib/AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../types/AuthenticationSession";
|
||||||
import Endpoints = require("../../../../shared/api");
|
import Endpoints = require("../../../../shared/api");
|
||||||
import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator");
|
import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator");
|
||||||
|
@ -20,6 +20,7 @@ describe("test the first factor validation route", function () {
|
||||||
let groups: string[];
|
let groups: string[];
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
|
let authSession: AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
emails = ["test_ok@example.com"];
|
emails = ["test_ok@example.com"];
|
||||||
|
@ -48,6 +49,7 @@ describe("test the first factor validation route", function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
|
authSession = AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should reply with 204 if success", function () {
|
it("should reply with 204 if success", function () {
|
||||||
|
@ -56,12 +58,7 @@ describe("test the first factor validation route", function () {
|
||||||
emails: emails,
|
emails: emails,
|
||||||
groups: groups
|
groups: groups
|
||||||
}));
|
}));
|
||||||
let authSession: AuthenticationSession;
|
return FirstFactorPost.default(vars)(req as any, res as any)
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
|
||||||
.then(function (_authSession) {
|
|
||||||
authSession = _authSession;
|
|
||||||
return FirstFactorPost.default(vars)(req as any, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
Assert.equal("username", authSession.userid);
|
Assert.equal("username", authSession.userid);
|
||||||
Assert(res.send.calledOnce);
|
Assert(res.send.calledOnce);
|
||||||
|
@ -76,18 +73,13 @@ describe("test the first factor validation route", function () {
|
||||||
|
|
||||||
it("should set first email address as user session variable", function () {
|
it("should set first email address as user session variable", function () {
|
||||||
const emails = ["test_ok@example.com"];
|
const emails = ["test_ok@example.com"];
|
||||||
let authSession: AuthenticationSession;
|
|
||||||
mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password")
|
mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password")
|
||||||
.returns(BluebirdPromise.resolve({
|
.returns(BluebirdPromise.resolve({
|
||||||
emails: emails,
|
emails: emails,
|
||||||
groups: groups
|
groups: groups
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
return FirstFactorPost.default(vars)(req as any, res as any)
|
||||||
.then(function (_authSession) {
|
|
||||||
authSession = _authSession;
|
|
||||||
return FirstFactorPost.default(vars)(req as any, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
Assert.equal("test_ok@example.com", authSession.email);
|
Assert.equal("test_ok@example.com", authSession.email);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
import PasswordResetFormPost = require("../../../src/lib/routes/password-reset/form/post");
|
import PasswordResetFormPost = require("../../../src/lib/routes/password-reset/form/post");
|
||||||
import { PasswordUpdater } from "../../../src/lib/ldap/PasswordUpdater";
|
import { PasswordUpdater } from "../../../src/lib/ldap/PasswordUpdater";
|
||||||
import AuthenticationSessionHandler = require("../../../src/lib/AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../src/lib/AuthenticationSessionHandler";
|
||||||
|
import { AuthenticationSession } from "../../../types/AuthenticationSession";
|
||||||
import { UserDataStore } from "../../../src/lib/storage/UserDataStore";
|
import { UserDataStore } from "../../../src/lib/storage/UserDataStore";
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
@ -15,6 +16,7 @@ describe("test reset password route", function () {
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
|
let authSession: AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = {
|
req = {
|
||||||
|
@ -53,13 +55,11 @@ describe("test reset password route", function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession = AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
.then(function (authSession) {
|
authSession.userid = "user";
|
||||||
authSession.userid = "user";
|
authSession.email = "user@example.com";
|
||||||
authSession.email = "user@example.com";
|
authSession.first_factor = true;
|
||||||
authSession.first_factor = true;
|
authSession.second_factor = false;
|
||||||
authSession.second_factor = false;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("test reset password post", () => {
|
describe("test reset password post", () => {
|
||||||
|
@ -69,14 +69,11 @@ describe("test reset password route", function () {
|
||||||
|
|
||||||
mocks.ldapPasswordUpdater.updatePasswordStub.returns(BluebirdPromise.resolve());
|
mocks.ldapPasswordUpdater.updatePasswordStub.returns(BluebirdPromise.resolve());
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.identity_check = {
|
||||||
.then(function (authSession) {
|
userid: "user",
|
||||||
authSession.identity_check = {
|
challenge: "reset-password"
|
||||||
userid: "user",
|
};
|
||||||
challenge: "reset-password"
|
return PasswordResetFormPost.default(vars)(req as any, res as any)
|
||||||
};
|
|
||||||
return PasswordResetFormPost.default(vars)(req as any, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
}).then(function (_authSession) {
|
}).then(function (_authSession) {
|
||||||
|
@ -88,14 +85,11 @@ describe("test reset password route", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail if identity_challenge does not exist", function () {
|
it("should fail if identity_challenge does not exist", function () {
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.identity_check = {
|
||||||
.then(function (authSession) {
|
userid: "user",
|
||||||
authSession.identity_check = {
|
challenge: undefined
|
||||||
userid: "user",
|
};
|
||||||
challenge: undefined
|
return PasswordResetFormPost.default(vars)(req as any, res as any)
|
||||||
};
|
|
||||||
return PasswordResetFormPost.default(vars)(req as any, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
Assert.equal(res.status.getCall(0).args[0], 200);
|
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||||
Assert.deepEqual(res.send.getCall(0).args[0], {
|
Assert.deepEqual(res.send.getCall(0).args[0], {
|
||||||
|
@ -111,14 +105,12 @@ describe("test reset password route", function () {
|
||||||
mocks.ldapPasswordUpdater.updatePasswordStub
|
mocks.ldapPasswordUpdater.updatePasswordStub
|
||||||
.returns(BluebirdPromise.reject("Internal error with LDAP"));
|
.returns(BluebirdPromise.reject("Internal error with LDAP"));
|
||||||
|
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.identity_check = {
|
||||||
.then(function (authSession) {
|
challenge: "reset-password",
|
||||||
authSession.identity_check = {
|
userid: "user"
|
||||||
challenge: "reset-password",
|
};
|
||||||
userid: "user"
|
return PasswordResetFormPost.default(vars)(req as any, res as any)
|
||||||
};
|
.then(function () {
|
||||||
return PasswordResetFormPost.default(vars)(req as any, res as any);
|
|
||||||
}).then(function () {
|
|
||||||
Assert.equal(res.status.getCall(0).args[0], 200);
|
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||||
Assert.deepEqual(res.send.getCall(0).args[0], {
|
Assert.deepEqual(res.send.getCall(0).args[0], {
|
||||||
error: "An error occurred during password reset. Your password has not been changed."
|
error: "An error occurred during password reset. Your password has not been changed."
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import SecondFactorGet from "../../../src/lib/routes/secondfactor/get";
|
||||||
|
import { ServerVariablesMockBuilder, ServerVariablesMock }
|
||||||
|
from "../../mocks/ServerVariablesMockBuilder";
|
||||||
|
import { ServerVariables } from "../../../src/lib/ServerVariables";
|
||||||
|
import Sinon = require("sinon");
|
||||||
|
import ExpressMock = require("../../mocks/express");
|
||||||
|
import Assert = require("assert");
|
||||||
|
import Endpoints = require("../../../../shared/api");
|
||||||
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
|
describe("test second factor GET endpoint handler", function () {
|
||||||
|
let mocks: ServerVariablesMock;
|
||||||
|
let vars: ServerVariables;
|
||||||
|
let req: ExpressMock.RequestMock;
|
||||||
|
let res: ExpressMock.ResponseMock;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
const s = ServerVariablesMockBuilder.build(true);
|
||||||
|
mocks = s.mocks;
|
||||||
|
vars = s.variables;
|
||||||
|
|
||||||
|
req = ExpressMock.RequestMock();
|
||||||
|
res = ExpressMock.ResponseMock();
|
||||||
|
|
||||||
|
req.session = {
|
||||||
|
auth: {
|
||||||
|
userid: "user",
|
||||||
|
first_factor: true,
|
||||||
|
second_factor: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("test redirection", function () {
|
||||||
|
it("should redirect to already logged in page if server is in single_factor mode", function () {
|
||||||
|
vars.config.authentication_methods.default_method = "single_factor";
|
||||||
|
return SecondFactorGet(vars)(req as any, res as any)
|
||||||
|
.then(function () {
|
||||||
|
Assert(res.redirect.calledWith(Endpoints.LOGGED_IN));
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should redirect to already logged in page if user already authenticated", function () {
|
||||||
|
vars.config.authentication_methods.default_method = "two_factor";
|
||||||
|
req.session.auth.second_factor = true;
|
||||||
|
return SecondFactorGet(vars)(req as any, res as any)
|
||||||
|
.then(function () {
|
||||||
|
Assert(res.redirect.calledWith(Endpoints.LOGGED_IN));
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render second factor page", function () {
|
||||||
|
vars.config.authentication_methods.default_method = "two_factor";
|
||||||
|
req.session.auth.second_factor = false;
|
||||||
|
return SecondFactorGet(vars)(req as any, res as any)
|
||||||
|
.then(function () {
|
||||||
|
Assert(res.render.calledWith("secondfactor"));
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -2,7 +2,6 @@ import Sinon = require("sinon");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import RegistrationHandler from "../../../../../src/lib/routes/secondfactor/totp/identity/RegistrationHandler";
|
import RegistrationHandler from "../../../../../src/lib/routes/secondfactor/totp/identity/RegistrationHandler";
|
||||||
import { Identity } from "../../../../../types/Identity";
|
import { Identity } from "../../../../../types/Identity";
|
||||||
import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession");
|
|
||||||
import { UserDataStore } from "../../../../../src/lib/storage/UserDataStore";
|
import { UserDataStore } from "../../../../../src/lib/storage/UserDataStore";
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import assert = require("assert");
|
import Assert = require("assert");
|
||||||
import winston = require("winston");
|
import Exceptions = require("../../../../../src/lib/Exceptions");
|
||||||
|
import { AuthenticationSessionHandler } from "../../../../../src/lib/AuthenticationSessionHandler";
|
||||||
import exceptions = require("../../../../../src/lib/Exceptions");
|
|
||||||
import AuthenticationSessionHandler = require("../../../../../src/lib/AuthenticationSession");
|
|
||||||
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
||||||
import SignPost = require("../../../../../src/lib/routes/secondfactor/totp/sign/post");
|
import SignPost = require("../../../../../src/lib/routes/secondfactor/totp/sign/post");
|
||||||
import { ServerVariables } from "../../../../../src/lib/ServerVariables";
|
import { ServerVariables } from "../../../../../src/lib/ServerVariables";
|
||||||
|
@ -27,9 +25,7 @@ describe("test totp route", function () {
|
||||||
mocks = s.mocks;
|
mocks = s.mocks;
|
||||||
const app_get = Sinon.stub();
|
const app_get = Sinon.stub();
|
||||||
req = {
|
req = {
|
||||||
app: {
|
app: {},
|
||||||
get: Sinon.stub().returns({ logger: winston })
|
|
||||||
},
|
|
||||||
body: {
|
body: {
|
||||||
token: "abc"
|
token: "abc"
|
||||||
},
|
},
|
||||||
|
@ -47,13 +43,10 @@ describe("test totp route", function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc));
|
mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc));
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession = AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
.then(function (_authSession) {
|
authSession.userid = "user";
|
||||||
authSession = _authSession;
|
authSession.first_factor = true;
|
||||||
authSession.userid = "user";
|
authSession.second_factor = false;
|
||||||
authSession.first_factor = true;
|
|
||||||
authSession.second_factor = false;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,7 +54,7 @@ describe("test totp route", function () {
|
||||||
mocks.totpHandler.validateStub.returns(true);
|
mocks.totpHandler.validateStub.returns(true);
|
||||||
return SignPost.default(vars)(req as any, res as any)
|
return SignPost.default(vars)(req as any, res as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
assert.equal(true, authSession.second_factor);
|
Assert.equal(true, authSession.second_factor);
|
||||||
return BluebirdPromise.resolve();
|
return BluebirdPromise.resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -70,24 +63,13 @@ describe("test totp route", function () {
|
||||||
mocks.totpHandler.validateStub.returns(false);
|
mocks.totpHandler.validateStub.returns(false);
|
||||||
return SignPost.default(vars)(req as any, res as any)
|
return SignPost.default(vars)(req as any, res as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
assert.equal(false, authSession.second_factor);
|
Assert.equal(false, authSession.second_factor);
|
||||||
assert.equal(res.status.getCall(0).args[0], 200);
|
Assert.equal(res.status.getCall(0).args[0], 200);
|
||||||
assert.deepEqual(res.send.getCall(0).args[0], {
|
Assert.deepEqual(res.send.getCall(0).args[0], {
|
||||||
error: "Operation failed."
|
error: "Operation failed."
|
||||||
});
|
});
|
||||||
return BluebirdPromise.resolve();
|
return BluebirdPromise.resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should send status code 401 when session has not been initiated", function () {
|
|
||||||
mocks.totpHandler.validateStub.returns(true);
|
|
||||||
req.session = {};
|
|
||||||
return SignPost.default(vars)(req as any, res as any)
|
|
||||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
|
||||||
.catch(function () {
|
|
||||||
assert.equal(401, res.status.getCall(0).args[0]);
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
import sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import winston = require("winston");
|
import Assert = require("assert");
|
||||||
import assert = require("assert");
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
import { Identity } from "../../../../../types/Identity";
|
import { Identity } from "../../../../../types/Identity";
|
||||||
import RegistrationHandler from "../../../../../src/lib/routes/secondfactor/u2f/identity/RegistrationHandler";
|
import RegistrationHandler from "../../../../../src/lib/routes/secondfactor/u2f/identity/RegistrationHandler";
|
||||||
import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession");
|
|
||||||
|
|
||||||
import ExpressMock = require("../../../../mocks/express");
|
import ExpressMock = require("../../../../mocks/express");
|
||||||
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
||||||
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../mocks/ServerVariablesMockBuilder";
|
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../mocks/ServerVariablesMockBuilder";
|
||||||
import { ServerVariables } from "../../../../../src/lib/ServerVariables";
|
import { ServerVariables } from "../../../../../src/lib/ServerVariables";
|
||||||
|
|
||||||
describe("test register handler", function () {
|
describe("test U2F register handler", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
|
@ -46,9 +43,9 @@ describe("test register handler", function () {
|
||||||
mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
res.send = sinon.spy();
|
res.send = Sinon.spy();
|
||||||
res.json = sinon.spy();
|
res.json = Sinon.spy();
|
||||||
res.status = sinon.spy();
|
res.status = Sinon.spy();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("test u2f registration check", test_registration_check);
|
describe("test u2f registration check", test_registration_check);
|
||||||
|
@ -63,31 +60,37 @@ describe("test register handler", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail if userid is missing", function (done) {
|
it("should fail if userid is missing", function () {
|
||||||
req.session.auth.first_factor = false;
|
req.session.auth.first_factor = false;
|
||||||
req.session.auth.userid = undefined;
|
req.session.auth.userid = undefined;
|
||||||
|
|
||||||
new RegistrationHandler(vars.logger).preValidationInit(req as any)
|
return new RegistrationHandler(vars.logger).preValidationInit(req as any)
|
||||||
.catch(function (err: Error) {
|
.then(function () {
|
||||||
done();
|
return BluebirdPromise.reject(new Error("should not be here"));
|
||||||
|
},
|
||||||
|
function (err: Error) {
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail if email is missing", function (done) {
|
it("should fail if email is missing", function () {
|
||||||
req.session.auth.first_factor = false;
|
req.session.auth.first_factor = false;
|
||||||
req.session.auth.email = undefined;
|
req.session.auth.email = undefined;
|
||||||
|
|
||||||
new RegistrationHandler(vars.logger).preValidationInit(req as any)
|
return new RegistrationHandler(vars.logger).preValidationInit(req as any)
|
||||||
.catch(function (err: Error) {
|
.then(function () {
|
||||||
done();
|
return BluebirdPromise.reject(new Error("should not be here"));
|
||||||
|
},
|
||||||
|
function (err: Error) {
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should succeed if first factor passed, userid and email are provided", function (done) {
|
it("should succeed if first factor passed, userid and email are provided", function () {
|
||||||
new RegistrationHandler(vars.logger).preValidationInit(req as any)
|
req.session.auth.first_factor = true;
|
||||||
.then(function (identity: Identity) {
|
req.session.auth.email = "admin@example.com";
|
||||||
done();
|
req.session.auth.userid = "user";
|
||||||
});
|
return new RegistrationHandler(vars.logger).preValidationInit(req as any);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,8 @@ import sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import U2FRegisterPost = require("../../../../../src/lib/routes/secondfactor/u2f/register/post");
|
import U2FRegisterPost = require("../../../../../src/lib/routes/secondfactor/u2f/register/post");
|
||||||
import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../../../src/lib/AuthenticationSessionHandler";
|
||||||
|
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
||||||
import ExpressMock = require("../../../../mocks/express");
|
import ExpressMock = require("../../../../mocks/express");
|
||||||
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
||||||
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../mocks/ServerVariablesMockBuilder";
|
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../mocks/ServerVariablesMockBuilder";
|
||||||
|
@ -15,6 +16,7 @@ describe("test u2f routes: register", function () {
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
let authSession: AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
|
@ -48,6 +50,8 @@ describe("test u2f routes: register", function () {
|
||||||
res.send = sinon.spy();
|
res.send = sinon.spy();
|
||||||
res.json = sinon.spy();
|
res.json = sinon.spy();
|
||||||
res.status = sinon.spy();
|
res.status = sinon.spy();
|
||||||
|
|
||||||
|
authSession = AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("test registration", test_registration);
|
describe("test registration", test_registration);
|
||||||
|
@ -62,20 +66,14 @@ describe("test u2f routes: register", function () {
|
||||||
};
|
};
|
||||||
mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve(expectedStatus));
|
mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve(expectedStatus));
|
||||||
|
|
||||||
return AuthenticationSession.get(req as any, vars.logger)
|
authSession.register_request = {
|
||||||
.then(function (authSession) {
|
appId: "app",
|
||||||
authSession.register_request = {
|
challenge: "challenge",
|
||||||
appId: "app",
|
keyHandle: "key",
|
||||||
challenge: "challenge",
|
version: "U2F_V2"
|
||||||
keyHandle: "key",
|
};
|
||||||
version: "U2F_V2"
|
return U2FRegisterPost.default(vars)(req as any, res as any)
|
||||||
};
|
|
||||||
return U2FRegisterPost.default(vars)(req as any, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return AuthenticationSession.get(req as any, vars.logger);
|
|
||||||
})
|
|
||||||
.then(function (authSession) {
|
|
||||||
assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]);
|
assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]);
|
||||||
assert.equal(authSession.identity_check, undefined);
|
assert.equal(authSession.identity_check, undefined);
|
||||||
});
|
});
|
||||||
|
@ -84,17 +82,14 @@ describe("test u2f routes: register", function () {
|
||||||
it("should return error message on finishRegistration error", function () {
|
it("should return error message on finishRegistration error", function () {
|
||||||
mocks.u2f.checkRegistrationStub.returns({ errorCode: 500 });
|
mocks.u2f.checkRegistrationStub.returns({ errorCode: 500 });
|
||||||
|
|
||||||
return AuthenticationSession.get(req as any, vars.logger)
|
authSession.register_request = {
|
||||||
.then(function (authSession) {
|
appId: "app",
|
||||||
authSession.register_request = {
|
challenge: "challenge",
|
||||||
appId: "app",
|
keyHandle: "key",
|
||||||
challenge: "challenge",
|
version: "U2F_V2"
|
||||||
keyHandle: "key",
|
};
|
||||||
version: "U2F_V2"
|
|
||||||
};
|
|
||||||
|
|
||||||
return U2FRegisterPost.default(vars)(req as any, res as any);
|
return U2FRegisterPost.default(vars)(req as any, res as any)
|
||||||
})
|
|
||||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
assert.equal(200, res.status.getCall(0).args[0]);
|
assert.equal(200, res.status.getCall(0).args[0]);
|
||||||
|
@ -107,11 +102,8 @@ describe("test u2f routes: register", function () {
|
||||||
|
|
||||||
it("should return error message when register_request is not provided", function () {
|
it("should return error message when register_request is not provided", function () {
|
||||||
mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve());
|
mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve());
|
||||||
return AuthenticationSession.get(req as any, vars.logger)
|
authSession.register_request = undefined;
|
||||||
.then(function (authSession) {
|
return U2FRegisterPost.default(vars)(req as any, res as any)
|
||||||
authSession.register_request = undefined;
|
|
||||||
return U2FRegisterPost.default(vars)(req as any, res as any);
|
|
||||||
})
|
|
||||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
assert.equal(200, res.status.getCall(0).args[0]);
|
assert.equal(200, res.status.getCall(0).args[0]);
|
||||||
|
@ -124,11 +116,8 @@ describe("test u2f routes: register", function () {
|
||||||
|
|
||||||
it("should return error message when no auth request has been initiated", function () {
|
it("should return error message when no auth request has been initiated", function () {
|
||||||
mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve());
|
mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve());
|
||||||
return AuthenticationSession.get(req as any, vars.logger)
|
authSession.register_request = undefined;
|
||||||
.then(function (authSession) {
|
return U2FRegisterPost.default(vars)(req as any, res as any)
|
||||||
authSession.register_request = undefined;
|
|
||||||
return U2FRegisterPost.default(vars)(req as any, res as any);
|
|
||||||
})
|
|
||||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
assert.equal(200, res.status.getCall(0).args[0]);
|
assert.equal(200, res.status.getCall(0).args[0]);
|
||||||
|
@ -140,11 +129,8 @@ describe("test u2f routes: register", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return error message when identity has not been verified", function () {
|
it("should return error message when identity has not been verified", function () {
|
||||||
return AuthenticationSession.get(req as any, vars.logger)
|
authSession.identity_check = undefined;
|
||||||
.then(function (authSession) {
|
return U2FRegisterPost.default(vars)(req as any, res as any)
|
||||||
authSession.identity_check = undefined;
|
|
||||||
return U2FRegisterPost.default(vars)(req as any, res as any);
|
|
||||||
})
|
|
||||||
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
assert.equal(200, res.status.getCall(0).args[0]);
|
assert.equal(200, res.status.getCall(0).args[0]);
|
||||||
|
|
|
@ -3,7 +3,6 @@ import sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import U2FRegisterRequestGet = require("../../../../../src/lib/routes/secondfactor/u2f/register_request/get");
|
import U2FRegisterRequestGet = require("../../../../../src/lib/routes/secondfactor/u2f/register_request/get");
|
||||||
import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession");
|
|
||||||
import ExpressMock = require("../../../../mocks/express");
|
import ExpressMock = require("../../../../mocks/express");
|
||||||
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
||||||
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../mocks/ServerVariablesMockBuilder";
|
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../mocks/ServerVariablesMockBuilder";
|
||||||
|
|
|
@ -3,7 +3,6 @@ import sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import U2FSignPost = require("../../../../../src/lib/routes/secondfactor/u2f/sign/post");
|
import U2FSignPost = require("../../../../../src/lib/routes/secondfactor/u2f/sign/post");
|
||||||
import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession");
|
|
||||||
import { ServerVariables } from "../../../../../src/lib/ServerVariables";
|
import { ServerVariables } from "../../../../../src/lib/ServerVariables";
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ import sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
import U2FSignRequestGet = require("../../../../../src/lib/routes/secondfactor/u2f/sign_request/get");
|
import U2FSignRequestGet = require("../../../../../src/lib/routes/secondfactor/u2f/sign_request/get");
|
||||||
import AuthenticationSessionHandler = require("../../../../../src/lib/AuthenticationSession");
|
|
||||||
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
|
||||||
import ExpressMock = require("../../../../mocks/express");
|
import ExpressMock = require("../../../../mocks/express");
|
||||||
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
|
||||||
import U2FMock = require("../../../../mocks/u2f");
|
import U2FMock = require("../../../../mocks/u2f");
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import VerifyGet = require("../../../src/lib/routes/verify/get");
|
import VerifyGet = require("../../../src/lib/routes/verify/get");
|
||||||
import AuthenticationSessionHandler = require("../../../src/lib/AuthenticationSession");
|
import { AuthenticationSessionHandler } from "../../../src/lib/AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../types/AuthenticationSession";
|
||||||
import { AuthenticationMethodCalculator } from "../../../src/lib/AuthenticationMethodCalculator";
|
|
||||||
import { AuthenticationMethodsConfiguration } from "../../../src/lib/configuration/Configuration";
|
import { AuthenticationMethodsConfiguration } from "../../../src/lib/configuration/Configuration";
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
|
@ -18,37 +17,31 @@ describe("test /verify endpoint", function () {
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
let authSession: AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
req.session = {};
|
|
||||||
req.query = {
|
req.query = {
|
||||||
redirect: "http://redirect.url"
|
redirect: "http://redirect.url"
|
||||||
};
|
};
|
||||||
req.app = {
|
|
||||||
get: Sinon.stub().returns({ logger: winston })
|
|
||||||
};
|
|
||||||
AuthenticationSessionHandler.reset(req as any);
|
AuthenticationSessionHandler.reset(req as any);
|
||||||
req.headers = {};
|
|
||||||
req.headers.host = "secret.example.com";
|
req.headers.host = "secret.example.com";
|
||||||
const s = ServerVariablesMockBuilder.build();
|
const s = ServerVariablesMockBuilder.build();
|
||||||
mocks = s.mocks;
|
mocks = s.mocks;
|
||||||
vars = s.variables;
|
vars = s.variables;
|
||||||
|
vars.config.authentication_methods.default_method = "two_factor";
|
||||||
|
|
||||||
|
authSession = AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be already authenticated", function () {
|
it("should be already authenticated", function () {
|
||||||
req.session = {};
|
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
AuthenticationSessionHandler.reset(req as any);
|
authSession.first_factor = true;
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.second_factor = true;
|
||||||
.then(function (authSession) {
|
authSession.userid = "myuser";
|
||||||
authSession.first_factor = true;
|
authSession.groups = ["mygroup", "othergroup"];
|
||||||
authSession.second_factor = true;
|
return VerifyGet.default(vars)(req as express.Request, res as any)
|
||||||
authSession.userid = "myuser";
|
|
||||||
authSession.groups = ["mygroup", "othergroup"];
|
|
||||||
return VerifyGet.default(vars)(req as express.Request, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
||||||
|
@ -57,11 +50,7 @@ describe("test /verify endpoint", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
function test_session(_authSession: AuthenticationSession, status_code: number) {
|
function test_session(_authSession: AuthenticationSession, status_code: number) {
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
return VerifyGet.default(vars)(req as express.Request, res as any)
|
||||||
.then(function (authSession) {
|
|
||||||
authSession = _authSession;
|
|
||||||
return VerifyGet.default(vars)(req as express.Request, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
Assert.equal(status_code, res.status.getCall(0).args[0]);
|
Assert.equal(status_code, res.status.getCall(0).args[0]);
|
||||||
});
|
});
|
||||||
|
@ -134,23 +123,20 @@ describe("test /verify endpoint", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not be authenticated when domain is not allowed for user", function () {
|
it("should not be authenticated when domain is not allowed for user", function () {
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.first_factor = true;
|
||||||
.then(function (authSession) {
|
authSession.second_factor = true;
|
||||||
authSession.first_factor = true;
|
authSession.userid = "myuser";
|
||||||
authSession.second_factor = true;
|
req.headers.host = "test.example.com";
|
||||||
authSession.userid = "myuser";
|
mocks.accessController.isAccessAllowedMock.returns(false);
|
||||||
req.headers.host = "test.example.com";
|
|
||||||
mocks.accessController.isAccessAllowedMock.returns(false);
|
|
||||||
|
|
||||||
return test_unauthorized_403({
|
return test_unauthorized_403({
|
||||||
first_factor: true,
|
first_factor: true,
|
||||||
second_factor: true,
|
second_factor: true,
|
||||||
userid: "user",
|
userid: "user",
|
||||||
groups: ["group1", "group2"],
|
groups: ["group1", "group2"],
|
||||||
email: undefined,
|
email: undefined,
|
||||||
last_activity_datetime: new Date().getTime()
|
last_activity_datetime: new Date().getTime()
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -168,12 +154,9 @@ describe("test /verify endpoint", function () {
|
||||||
|
|
||||||
it("should be authenticated when first factor is validated and second factor is not", function () {
|
it("should be authenticated when first factor is validated and second factor is not", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.first_factor = true;
|
||||||
.then(function (authSession) {
|
authSession.userid = "user1";
|
||||||
authSession.first_factor = true;
|
return VerifyGet.default(vars)(req as express.Request, res as any)
|
||||||
authSession.userid = "user1";
|
|
||||||
return VerifyGet.default(vars)(req as express.Request, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
Assert(res.status.calledWith(204));
|
Assert(res.status.calledWith(204));
|
||||||
Assert(res.send.calledOnce);
|
Assert(res.send.calledOnce);
|
||||||
|
@ -182,11 +165,8 @@ describe("test /verify endpoint", function () {
|
||||||
|
|
||||||
it("should be rejected with 401 when first factor is not validated", function () {
|
it("should be rejected with 401 when first factor is not validated", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.first_factor = false;
|
||||||
.then(function (authSession) {
|
return VerifyGet.default(vars)(req as express.Request, res as any)
|
||||||
authSession.first_factor = false;
|
|
||||||
return VerifyGet.default(vars)(req as express.Request, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
Assert(res.status.calledWith(401));
|
Assert(res.status.calledWith(401));
|
||||||
});
|
});
|
||||||
|
@ -199,15 +179,12 @@ describe("test /verify endpoint", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
const currentTime = new Date().getTime() - 1000;
|
const currentTime = new Date().getTime() - 1000;
|
||||||
AuthenticationSessionHandler.reset(req as any);
|
AuthenticationSessionHandler.reset(req as any);
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.first_factor = true;
|
||||||
.then(function (authSession) {
|
authSession.second_factor = true;
|
||||||
authSession.first_factor = true;
|
authSession.userid = "myuser";
|
||||||
authSession.second_factor = true;
|
authSession.groups = ["mygroup", "othergroup"];
|
||||||
authSession.userid = "myuser";
|
authSession.last_activity_datetime = currentTime;
|
||||||
authSession.groups = ["mygroup", "othergroup"];
|
return VerifyGet.default(vars)(req as express.Request, res as any)
|
||||||
authSession.last_activity_datetime = currentTime;
|
|
||||||
return VerifyGet.default(vars)(req as express.Request, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
})
|
})
|
||||||
|
@ -221,15 +198,12 @@ describe("test /verify endpoint", function () {
|
||||||
mocks.accessController.isAccessAllowedMock.returns(true);
|
mocks.accessController.isAccessAllowedMock.returns(true);
|
||||||
const currentTime = new Date().getTime() - 1000;
|
const currentTime = new Date().getTime() - 1000;
|
||||||
AuthenticationSessionHandler.reset(req as any);
|
AuthenticationSessionHandler.reset(req as any);
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger)
|
authSession.first_factor = true;
|
||||||
.then(function (authSession) {
|
authSession.second_factor = true;
|
||||||
authSession.first_factor = true;
|
authSession.userid = "myuser";
|
||||||
authSession.second_factor = true;
|
authSession.groups = ["mygroup", "othergroup"];
|
||||||
authSession.userid = "myuser";
|
authSession.last_activity_datetime = currentTime;
|
||||||
authSession.groups = ["mygroup", "othergroup"];
|
return VerifyGet.default(vars)(req as express.Request, res as any)
|
||||||
authSession.last_activity_datetime = currentTime;
|
|
||||||
return VerifyGet.default(vars)(req as express.Request, res as any);
|
|
||||||
})
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
return AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,182 +0,0 @@
|
||||||
|
|
||||||
import Server from "../../src/lib/Server";
|
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import speakeasy = require("speakeasy");
|
|
||||||
import request = require("request");
|
|
||||||
import nedb = require("nedb");
|
|
||||||
import { GlobalDependencies } from "../../types/Dependencies";
|
|
||||||
import { UserConfiguration } from "../../src/lib/configuration/Configuration";
|
|
||||||
import { TOTPSecret } from "../../types/TOTPSecret";
|
|
||||||
import U2FMock = require("./../mocks/u2f");
|
|
||||||
import Endpoints = require("../../../shared/api");
|
|
||||||
import Requests = require("../requests");
|
|
||||||
import Assert = require("assert");
|
|
||||||
import Sinon = require("sinon");
|
|
||||||
import Winston = require("winston");
|
|
||||||
import MockDate = require("mockdate");
|
|
||||||
import ExpressSession = require("express-session");
|
|
||||||
import ldapjs = require("ldapjs");
|
|
||||||
|
|
||||||
const requestp = BluebirdPromise.promisifyAll(request) as typeof request;
|
|
||||||
|
|
||||||
const PORT = 8090;
|
|
||||||
const BASE_URL = "http://localhost:" + PORT;
|
|
||||||
const requests = Requests(PORT);
|
|
||||||
|
|
||||||
describe("Private pages of the server must not be accessible without session", function () {
|
|
||||||
let server: Server;
|
|
||||||
let transporter: any;
|
|
||||||
let u2f: U2FMock.U2FMock;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
const config: UserConfiguration = {
|
|
||||||
port: PORT,
|
|
||||||
ldap: {
|
|
||||||
url: "ldap://127.0.0.1:389",
|
|
||||||
base_dn: "ou=users,dc=example,dc=com",
|
|
||||||
user: "cn=admin,dc=example,dc=com",
|
|
||||||
password: "password",
|
|
||||||
},
|
|
||||||
session: {
|
|
||||||
secret: "session_secret",
|
|
||||||
expiration: 50000,
|
|
||||||
},
|
|
||||||
regulation: {
|
|
||||||
max_retries: 3,
|
|
||||||
ban_time: 5 * 60,
|
|
||||||
find_time: 5 * 60
|
|
||||||
},
|
|
||||||
storage: {
|
|
||||||
local: {
|
|
||||||
in_memory: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
notifier: {
|
|
||||||
email: {
|
|
||||||
username: "user@example.com",
|
|
||||||
password: "password",
|
|
||||||
sender: "admin@example.com",
|
|
||||||
service: "gmail"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ldap_client = {
|
|
||||||
bind: Sinon.stub(),
|
|
||||||
search: Sinon.stub(),
|
|
||||||
modify: Sinon.stub(),
|
|
||||||
on: Sinon.spy()
|
|
||||||
};
|
|
||||||
const ldap = {
|
|
||||||
Change: Sinon.spy(),
|
|
||||||
createClient: Sinon.spy(function () {
|
|
||||||
return ldap_client;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
u2f = U2FMock.U2FMock();
|
|
||||||
|
|
||||||
transporter = {
|
|
||||||
sendMail: Sinon.stub().yields()
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodemailer = {
|
|
||||||
createTransport: Sinon.spy(function () {
|
|
||||||
return transporter;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
const ldap_document = {
|
|
||||||
object: {
|
|
||||||
mail: "test_ok@example.com",
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const search_res = {
|
|
||||||
on: Sinon.spy(function (event: string, fn: (s: any) => void) {
|
|
||||||
if (event != "error") fn(ldap_document);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
ldap_client.bind.withArgs("cn=test_ok,ou=users,dc=example,dc=com",
|
|
||||||
"password").yields(undefined);
|
|
||||||
ldap_client.bind.withArgs("cn=admin,dc=example,dc=com",
|
|
||||||
"password").yields(undefined);
|
|
||||||
|
|
||||||
ldap_client.bind.withArgs("cn=test_nok,ou=users,dc=example,dc=com",
|
|
||||||
"password").yields("error");
|
|
||||||
|
|
||||||
ldap_client.modify.yields(undefined);
|
|
||||||
ldap_client.search.yields(undefined, search_res);
|
|
||||||
|
|
||||||
const deps: GlobalDependencies = {
|
|
||||||
u2f: u2f as any,
|
|
||||||
nedb: nedb,
|
|
||||||
ldapjs: ldap,
|
|
||||||
session: ExpressSession,
|
|
||||||
winston: Winston,
|
|
||||||
speakeasy: speakeasy,
|
|
||||||
ConnectRedis: Sinon.spy()
|
|
||||||
};
|
|
||||||
|
|
||||||
server = new Server(deps);
|
|
||||||
return server.start(config, deps);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
server.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Second factor endpoints must be protected if first factor is not validated", function () {
|
|
||||||
function should_post_and_reply_with_401(url: string): BluebirdPromise<void> {
|
|
||||||
return requestp.postAsync(url).then(function (response: request.RequestResponse) {
|
|
||||||
Assert.equal(response.statusCode, 401);
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function should_get_and_reply_with_401(url: string): BluebirdPromise<void> {
|
|
||||||
return requestp.getAsync(url).then(function (response: request.RequestResponse) {
|
|
||||||
Assert.equal(response.statusCode, 401);
|
|
||||||
return BluebirdPromise.resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it("should block " + Endpoints.SECOND_FACTOR_GET, function () {
|
|
||||||
return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_GET);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should block " + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, function () {
|
|
||||||
return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should block " + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, function () {
|
|
||||||
return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET + "?identity_token=dummy");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should block " + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, function () {
|
|
||||||
return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should block " + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, function () {
|
|
||||||
return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should block " + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, function () {
|
|
||||||
return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should block " + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, function () {
|
|
||||||
return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should block " + Endpoints.SECOND_FACTOR_TOTP_POST, function () {
|
|
||||||
return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should block " + Endpoints.LOGGED_IN, function () {
|
|
||||||
return should_get_and_reply_with_401(BASE_URL + Endpoints.LOGGED_IN);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,173 +0,0 @@
|
||||||
|
|
||||||
import Server from "../../src/lib/Server";
|
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import speakeasy = require("speakeasy");
|
|
||||||
import Request = require("request");
|
|
||||||
import nedb = require("nedb");
|
|
||||||
import { GlobalDependencies } from "../../types/Dependencies";
|
|
||||||
import { UserConfiguration } from "../../src/lib/configuration/Configuration";
|
|
||||||
import { TOTPSecret } from "../../types/TOTPSecret";
|
|
||||||
import U2FMock = require("./../mocks/u2f");
|
|
||||||
import Endpoints = require("../../../shared/api");
|
|
||||||
import Requests = require("../requests");
|
|
||||||
import Assert = require("assert");
|
|
||||||
import Sinon = require("sinon");
|
|
||||||
import Winston = require("winston");
|
|
||||||
import MockDate = require("mockdate");
|
|
||||||
import ExpressSession = require("express-session");
|
|
||||||
import ldapjs = require("ldapjs");
|
|
||||||
|
|
||||||
const requestp = BluebirdPromise.promisifyAll(Request) as typeof Request;
|
|
||||||
|
|
||||||
const PORT = 8090;
|
|
||||||
const BASE_URL = "http://localhost:" + PORT;
|
|
||||||
const requests = Requests(PORT);
|
|
||||||
|
|
||||||
describe("Public pages of the server must be accessible without session", function () {
|
|
||||||
let server: Server;
|
|
||||||
let transporter: object;
|
|
||||||
let u2f: U2FMock.U2FMock;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
const config: UserConfiguration = {
|
|
||||||
port: PORT,
|
|
||||||
ldap: {
|
|
||||||
url: "ldap://127.0.0.1:389",
|
|
||||||
base_dn: "ou=users,dc=example,dc=com",
|
|
||||||
user: "cn=admin,dc=example,dc=com",
|
|
||||||
password: "password",
|
|
||||||
},
|
|
||||||
session: {
|
|
||||||
secret: "session_secret",
|
|
||||||
expiration: 50000,
|
|
||||||
},
|
|
||||||
storage: {
|
|
||||||
local: {
|
|
||||||
in_memory: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
regulation: {
|
|
||||||
max_retries: 3,
|
|
||||||
ban_time: 5 * 60,
|
|
||||||
find_time: 5 * 60
|
|
||||||
},
|
|
||||||
notifier: {
|
|
||||||
email: {
|
|
||||||
username: "user@example.com",
|
|
||||||
password: "password",
|
|
||||||
sender: "admin@example.com",
|
|
||||||
service: "gmail"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ldap_client = {
|
|
||||||
bind: Sinon.stub(),
|
|
||||||
search: Sinon.stub(),
|
|
||||||
modify: Sinon.stub(),
|
|
||||||
on: Sinon.spy()
|
|
||||||
};
|
|
||||||
const ldap = {
|
|
||||||
Change: Sinon.spy(),
|
|
||||||
createClient: Sinon.spy(function () {
|
|
||||||
return ldap_client;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
u2f = U2FMock.U2FMock();
|
|
||||||
|
|
||||||
transporter = {
|
|
||||||
sendMail: Sinon.stub().yields()
|
|
||||||
};
|
|
||||||
|
|
||||||
const nodemailer = {
|
|
||||||
createTransport: Sinon.spy(function () {
|
|
||||||
return transporter;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
const ldap_document = {
|
|
||||||
object: {
|
|
||||||
mail: "test_ok@example.com",
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const search_res = {
|
|
||||||
on: Sinon.spy(function (event: string, fn: (s: any) => void) {
|
|
||||||
if (event != "error") fn(ldap_document);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
ldap_client.bind.withArgs("cn=test_ok,ou=users,dc=example,dc=com",
|
|
||||||
"password").yields(undefined);
|
|
||||||
ldap_client.bind.withArgs("cn=admin,dc=example,dc=com",
|
|
||||||
"password").yields(undefined);
|
|
||||||
|
|
||||||
ldap_client.bind.withArgs("cn=test_nok,ou=users,dc=example,dc=com",
|
|
||||||
"password").yields("error");
|
|
||||||
|
|
||||||
ldap_client.modify.yields(undefined);
|
|
||||||
ldap_client.search.yields(undefined, search_res);
|
|
||||||
|
|
||||||
const deps: GlobalDependencies = {
|
|
||||||
u2f: u2f as any,
|
|
||||||
nedb: nedb,
|
|
||||||
ldapjs: ldap,
|
|
||||||
session: ExpressSession,
|
|
||||||
winston: Winston,
|
|
||||||
speakeasy: speakeasy,
|
|
||||||
ConnectRedis: Sinon.spy()
|
|
||||||
};
|
|
||||||
|
|
||||||
server = new Server(deps);
|
|
||||||
return server.start(config, deps);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
server.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("test GET " + Endpoints.FIRST_FACTOR_GET, function () {
|
|
||||||
test_login();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("test GET " + Endpoints.LOGOUT_GET, function () {
|
|
||||||
test_logout();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("test GET" + Endpoints.RESET_PASSWORD_REQUEST_GET, function () {
|
|
||||||
test_reset_password_form();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function test_reset_password_form() {
|
|
||||||
it("should serve the reset password form page", function (done) {
|
|
||||||
requestp.getAsync(BASE_URL + Endpoints.RESET_PASSWORD_REQUEST_GET)
|
|
||||||
.then(function (response: Request.RequestResponse) {
|
|
||||||
Assert.equal(response.statusCode, 200);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_login() {
|
|
||||||
it("should serve the login page", function (done) {
|
|
||||||
requestp.getAsync(BASE_URL + Endpoints.FIRST_FACTOR_GET)
|
|
||||||
.then(function (response: Request.RequestResponse) {
|
|
||||||
Assert.equal(response.statusCode, 200);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_logout() {
|
|
||||||
it("should logout and redirect to /", function (done) {
|
|
||||||
requestp.getAsync(BASE_URL + Endpoints.LOGOUT_GET)
|
|
||||||
.then(function (response: any) {
|
|
||||||
Assert.equal(response.req.path, "/");
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ Feature: User has access restricted access to domains
|
||||||
| https://dev.test.local:8080/users/bob/secret.html |
|
| https://dev.test.local:8080/users/bob/secret.html |
|
||||||
| https://admin.test.local:8080/secret.html |
|
| https://admin.test.local:8080/secret.html |
|
||||||
| https://mx1.mail.test.local:8080/secret.html |
|
| https://mx1.mail.test.local:8080/secret.html |
|
||||||
| https://single_factor.test.local:8080/secret.html |
|
| https://single_factor.test.local:8080/secret.html |
|
||||||
And I have no access to:
|
And I have no access to:
|
||||||
| url |
|
| url |
|
||||||
| https://mx2.mail.test.local:8080/secret.html |
|
| https://mx2.mail.test.local:8080/secret.html |
|
||||||
|
@ -42,7 +42,7 @@ Feature: User has access restricted access to domains
|
||||||
| https://admin.test.local:8080/secret.html |
|
| https://admin.test.local:8080/secret.html |
|
||||||
| https://dev.test.local:8080/users/john/secret.html |
|
| https://dev.test.local:8080/users/john/secret.html |
|
||||||
| https://dev.test.local:8080/users/harry/secret.html |
|
| https://dev.test.local:8080/users/harry/secret.html |
|
||||||
| https://single_factor.test.local:8080/secret.html |
|
| https://single_factor.test.local:8080/secret.html |
|
||||||
|
|
||||||
@need-registered-user-harry
|
@need-registered-user-harry
|
||||||
Scenario: User harry has restricted access
|
Scenario: User harry has restricted access
|
||||||
|
@ -64,4 +64,4 @@ Feature: User has access restricted access to domains
|
||||||
| https://dev.test.local:8080/users/john/secret.html |
|
| https://dev.test.local:8080/users/john/secret.html |
|
||||||
| https://mx1.mail.test.local:8080/secret.html |
|
| https://mx1.mail.test.local:8080/secret.html |
|
||||||
| https://mx2.mail.test.local:8080/secret.html |
|
| https://mx2.mail.test.local:8080/secret.html |
|
||||||
| https://single_factor.test.local:8080/secret.html |
|
| https://single_factor.test.local:8080/secret.html |
|
|
@ -1,16 +1,36 @@
|
||||||
Feature: Non authenticated users have no access to certain pages
|
Feature: Non authenticated users have no access to certain pages
|
||||||
|
|
||||||
Scenario Outline: Anonymous user has no access to protected pages
|
Scenario: Anonymous user has no access to protected pages
|
||||||
When I visit "<url>"
|
Then I get the following status code when requesting:
|
||||||
Then I get an error <error code>
|
| url | code | method |
|
||||||
|
| https://auth.test.local:8080/secondfactor | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/secondfactor/u2f/identity/start | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/secondfactor/u2f/identity/finish | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/secondfactor/totp/identity/start | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/secondfactor/totp/identity/finish | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/api/totp | 401 | POST |
|
||||||
|
| https://auth.test.local:8080/api/u2f/sign_request | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/api/u2f/sign | 401 | POST |
|
||||||
|
| https://auth.test.local:8080/api/u2f/register_request | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/api/u2f/register | 401 | POST |
|
||||||
|
|
||||||
Examples:
|
|
||||||
| url | error code |
|
@needs-single_factor-config
|
||||||
| https://auth.test.local:8080/secondfactor | 401 |
|
@need-registered-user-john
|
||||||
| https://auth.test.local:8080/verify | 401 |
|
Scenario: User does not have acces to second factor related endpoints when in single factor mode
|
||||||
| https://auth.test.local:8080/secondfactor/u2f/identity/start | 401 |
|
Given I post "https://auth.test.local:8080/api/firstfactor" with body:
|
||||||
| https://auth.test.local:8080/secondfactor/u2f/identity/finish | 401 |
|
| key | value |
|
||||||
| https://auth.test.local:8080/secondfactor/totp/identity/start | 401 |
|
| username | john |
|
||||||
| https://auth.test.local:8080/secondfactor/totp/identity/finish | 401 |
|
| password | password |
|
||||||
| https://auth.test.local:8080/password-reset/identity/start | 401 |
|
Then I get the following status code when requesting:
|
||||||
| https://auth.test.local:8080/password-reset/identity/finish | 401 |
|
| url | code | method |
|
||||||
|
| https://auth.test.local:8080/secondfactor | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/secondfactor/u2f/identity/start | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/secondfactor/u2f/identity/finish | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/secondfactor/totp/identity/start | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/secondfactor/totp/identity/finish | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/api/totp | 401 | POST |
|
||||||
|
| https://auth.test.local:8080/api/u2f/sign_request | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/api/u2f/sign | 401 | POST |
|
||||||
|
| https://auth.test.local:8080/api/u2f/register_request | 401 | GET |
|
||||||
|
| https://auth.test.local:8080/api/u2f/register | 401 | POST |
|
|
@ -0,0 +1,16 @@
|
||||||
|
@needs-single_factor-config
|
||||||
|
Feature: Server is configured as a single factor only server
|
||||||
|
|
||||||
|
@need-registered-user-john
|
||||||
|
Scenario: User is redirected to service after first factor if allowed
|
||||||
|
When I visit "https://auth.test.local:8080/?redirect=https%3A%2F%2Fpublic.test.local%3A8080%2Fsecret.html"
|
||||||
|
And I login with user "john" and password "password"
|
||||||
|
Then I'm redirected to "https://public.test.local:8080/secret.html"
|
||||||
|
|
||||||
|
@need-registered-user-john
|
||||||
|
Scenario: User is correctly redirected according to default redirection URL
|
||||||
|
When I visit "https://auth.test.local:8080"
|
||||||
|
And I login with user "john" and password "password"
|
||||||
|
Then I'm redirected to "https://auth.test.local:8080/loggedin"
|
||||||
|
And I sleep for 5 seconds
|
||||||
|
Then I'm redirected to "https://home.test.local:8080/"
|
|
@ -5,19 +5,20 @@ import Fs = require("fs");
|
||||||
import Speakeasy = require("speakeasy");
|
import Speakeasy = require("speakeasy");
|
||||||
import CustomWorld = require("../support/world");
|
import CustomWorld = require("../support/world");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import Request = require("request-promise");
|
||||||
|
|
||||||
Cucumber.defineSupportCode(function ({ Given, When, Then }) {
|
Cucumber.defineSupportCode(function ({ Given, When, Then }) {
|
||||||
When(/^I visit "(https:\/\/[a-zA-Z0-9:%&._\/=?-]+)"$/, function (link: string) {
|
When(/^I visit "(https:\/\/[a-zA-Z0-9:%&._\/=?-]+)"$/, function (link: string) {
|
||||||
return this.visit(link);
|
return this.visit(link);
|
||||||
});
|
});
|
||||||
|
|
||||||
When("I wait for notification to disappear", function() {
|
When("I wait for notification to disappear", function () {
|
||||||
const that = this;
|
const that = this;
|
||||||
const notificationEl = this.driver.findElement(seleniumWebdriver.By.className("notification"));
|
const notificationEl = this.driver.findElement(seleniumWebdriver.By.className("notification"));
|
||||||
return this.driver.wait(seleniumWebdriver.until.elementIsVisible(notificationEl), 15000)
|
return this.driver.wait(seleniumWebdriver.until.elementIsVisible(notificationEl), 15000)
|
||||||
.then(function() {
|
.then(function () {
|
||||||
return that.driver.wait(seleniumWebdriver.until.elementIsNotVisible(notificationEl), 15000);
|
return that.driver.wait(seleniumWebdriver.until.elementIsNotVisible(notificationEl), 15000);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
When("I set field {stringInDoubleQuotes} to {stringInDoubleQuotes}", function (fieldName: string, content: string) {
|
When("I set field {stringInDoubleQuotes} to {stringInDoubleQuotes}", function (fieldName: string, content: string) {
|
||||||
|
@ -104,4 +105,29 @@ and I use TOTP token handle {stringInDoubleQuotes}",
|
||||||
}
|
}
|
||||||
return BluebirdPromise.all(promises);
|
return BluebirdPromise.all(promises);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function endpointReplyWith(context: any, link: string, method: string,
|
||||||
|
returnCode: number) {
|
||||||
|
return Request(link, {
|
||||||
|
method: method
|
||||||
|
})
|
||||||
|
.then(function (response: string) {
|
||||||
|
Assert(response.indexOf("Error " + returnCode) >= 0);
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
}, function (response: any) {
|
||||||
|
Assert.equal(response.statusCode, returnCode);
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Then("the following endpoints reply with:", function (dataTable: Cucumber.TableDefinition) {
|
||||||
|
const promises = [];
|
||||||
|
for (let i = 0; i < dataTable.rows().length; i++) {
|
||||||
|
const url: string = (dataTable.hashes() as any)[i].url;
|
||||||
|
const method: string = (dataTable.hashes() as any)[i].method;
|
||||||
|
const code: number = (dataTable.hashes() as any)[i].code;
|
||||||
|
promises.push(endpointReplyWith(this, url, method, code));
|
||||||
|
}
|
||||||
|
return BluebirdPromise.all(promises);
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -36,6 +36,13 @@ Cucumber.defineSupportCode(function ({ After, Before }) {
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createSingleFactorConfiguration(): BluebirdPromise<void> {
|
||||||
|
return exec("\
|
||||||
|
cat config.template.yml | \
|
||||||
|
sed 's/default_method: two_factor/default_method: single_factor/' > config.test.yml \
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
function declareNeedsConfiguration(tag: string, cb: () => BluebirdPromise<void>) {
|
function declareNeedsConfiguration(tag: string, cb: () => BluebirdPromise<void>) {
|
||||||
Before({ tags: "@needs-" + tag + "-config", timeout: 20 * 1000 }, function () {
|
Before({ tags: "@needs-" + tag + "-config", timeout: 20 * 1000 }, function () {
|
||||||
return cb()
|
return cb()
|
||||||
|
@ -54,6 +61,7 @@ Cucumber.defineSupportCode(function ({ After, Before }) {
|
||||||
|
|
||||||
declareNeedsConfiguration("regulation", createRegulationConfiguration);
|
declareNeedsConfiguration("regulation", createRegulationConfiguration);
|
||||||
declareNeedsConfiguration("inactivity", createInactivityConfiguration);
|
declareNeedsConfiguration("inactivity", createInactivityConfiguration);
|
||||||
|
declareNeedsConfiguration("single_factor", createSingleFactorConfiguration);
|
||||||
|
|
||||||
function registerUser(context: any, username: string) {
|
function registerUser(context: any, username: string) {
|
||||||
let secret: Speakeasy.Key;
|
let secret: Speakeasy.Key;
|
||||||
|
|
|
@ -1,9 +1,72 @@
|
||||||
import Cucumber = require("cucumber");
|
import Cucumber = require("cucumber");
|
||||||
import seleniumWebdriver = require("selenium-webdriver");
|
import seleniumWebdriver = require("selenium-webdriver");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
import Request = require("request-promise");
|
||||||
|
import Bluebird = require("bluebird");
|
||||||
|
|
||||||
|
Cucumber.defineSupportCode(function ({ Given, When, Then, Before, After }) {
|
||||||
|
Before(function () {
|
||||||
|
this.jar = Request.jar();
|
||||||
|
})
|
||||||
|
|
||||||
Cucumber.defineSupportCode(function ({ Given, When, Then }) {
|
|
||||||
Then("I get an error {number}", function (code: number) {
|
Then("I get an error {number}", function (code: number) {
|
||||||
return this.getErrorPage(code);
|
return this.getErrorPage(code);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
When("I request {stringInDoubleQuotes} with method {stringInDoubleQuotes}",
|
||||||
|
function (url: string, method: string) {
|
||||||
|
const that = this;
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
function requestAndExpectStatusCode(ctx: any, url: string, method: string,
|
||||||
|
expectedStatusCode: number) {
|
||||||
|
return Request(url, {
|
||||||
|
method: method,
|
||||||
|
jar: ctx.jar
|
||||||
|
})
|
||||||
|
.then(function (body: string) {
|
||||||
|
return Bluebird.resolve(parseInt(body.match(/Error ([0-9]{3})/)[1]));
|
||||||
|
}, function (response: any) {
|
||||||
|
return Bluebird.resolve(response.statusCode)
|
||||||
|
})
|
||||||
|
.then(function (statusCode: number) {
|
||||||
|
try {
|
||||||
|
Assert.equal(statusCode, expectedStatusCode);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.log(url);
|
||||||
|
console.log("%s (actual) != %s (expected)", statusCode,
|
||||||
|
expectedStatusCode);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Then("I get the following status code when requesting:",
|
||||||
|
function (dataTable: Cucumber.TableDefinition) {
|
||||||
|
const promises: Bluebird<void>[] = [];
|
||||||
|
for (let i = 0; i < dataTable.rows().length; i++) {
|
||||||
|
const url: string = (dataTable.hashes() as any)[i].url;
|
||||||
|
const method: string = (dataTable.hashes() as any)[i].method;
|
||||||
|
const code: number = (dataTable.hashes() as any)[i].code;
|
||||||
|
promises.push(requestAndExpectStatusCode(this, url, method, code));
|
||||||
|
}
|
||||||
|
return Bluebird.all(promises);
|
||||||
|
})
|
||||||
|
|
||||||
|
When("I post {stringInDoubleQuotes} with body:", function (url: string,
|
||||||
|
dataTable: Cucumber.TableDefinition) {
|
||||||
|
const body = {};
|
||||||
|
for (let i = 0; i < dataTable.rows().length; i++) {
|
||||||
|
const key = (dataTable.hashes() as any)[i].key;
|
||||||
|
const value = (dataTable.hashes() as any)[i].value;
|
||||||
|
body[key] = value;
|
||||||
|
}
|
||||||
|
return Request.post(url, {
|
||||||
|
body: body,
|
||||||
|
jar: this.jar,
|
||||||
|
json: true
|
||||||
|
});
|
||||||
|
})
|
||||||
});
|
});
|
|
@ -7,6 +7,8 @@ import Assert = require("assert");
|
||||||
import Request = require("request-promise");
|
import Request = require("request-promise");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
|
||||||
|
|
||||||
function CustomWorld() {
|
function CustomWorld() {
|
||||||
const that = this;
|
const that = this;
|
||||||
this.driver = new seleniumWebdriver.Builder()
|
this.driver = new seleniumWebdriver.Builder()
|
||||||
|
@ -38,8 +40,13 @@ function CustomWorld() {
|
||||||
.findElement(seleniumWebdriver.By.tagName("h1")).getText();
|
.findElement(seleniumWebdriver.By.tagName("h1")).getText();
|
||||||
})
|
})
|
||||||
.then(function (txt: string) {
|
.then(function (txt: string) {
|
||||||
Assert.equal(txt, "Error " + code);
|
try {
|
||||||
});
|
Assert.equal(txt, "Error " + code);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(txt);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
this.clickOnButton = function (buttonText: string) {
|
this.clickOnButton = function (buttonText: string) {
|
||||||
|
|
Loading…
Reference in New Issue