Add logs to detect redis connection issues earlier

Before this fix, the application was simply crashing during execution
when connection to redis was failing.

Now, it is correctly handled with failing promises and logs have been
enabled to clearly see the problem
This commit is contained in:
Clement Michaud 2017-09-21 22:07:34 +02:00
parent 36962cfc2c
commit 0a33b2d5ee
44 changed files with 800 additions and 586 deletions

View File

@ -112,7 +112,7 @@ module.exports = function (grunt) {
}
},
client: {
files: ['src/client/**/*.ts', 'test/client/**/*.ts'],
files: ['src/client/**/*.ts'],
tasks: ['build-dev'],
options: {
interrupt: true,
@ -120,7 +120,7 @@ module.exports = function (grunt) {
}
},
server: {
files: ['src/server/**/*.ts', 'test/server/**/*.ts'],
files: ['src/server/**/*.ts'],
tasks: ['build-dev', 'run:docker-restart', 'run:make-dev-views' ],
options: {
interrupt: true,

View File

@ -2,5 +2,7 @@ version: '2'
services:
mongo:
image: mongo:3.4
ports:
- "27017:27017"
networks:
- example-network

View File

@ -37,6 +37,7 @@
"object-path": "^0.11.3",
"pug": "^2.0.0-rc.2",
"randomstring": "^1.1.5",
"redis": "^2.8.0",
"speakeasy": "^2.0.0",
"u2f": "^0.1.2",
"winston": "^2.3.1",
@ -63,6 +64,7 @@
"@types/proxyquire": "^1.3.27",
"@types/query-string": "^4.3.1",
"@types/randomstring": "^1.1.5",
"@types/redis": "^2.6.0",
"@types/request": "0.0.46",
"@types/selenium-webdriver": "^3.0.4",
"@types/sinon": "^2.2.1",

View File

@ -2,6 +2,8 @@
import express = require("express");
import U2f = require("u2f");
import { ServerVariablesHandler } from "./ServerVariablesHandler";
import BluebirdPromise = require("bluebird");
export interface AuthenticationSession {
userid: string;
@ -18,8 +20,7 @@ export interface AuthenticationSession {
redirect?: string;
}
export function reset(req: express.Request): void {
const authSession: AuthenticationSession = {
const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = {
first_factor: false,
second_factor: false,
userid: undefined,
@ -29,12 +30,26 @@ export function reset(req: express.Request): void {
sign_request: undefined,
identity_check: undefined,
redirect: undefined
};
req.session.auth = authSession;
};
export function reset(req: express.Request): void {
const logger = ServerVariablesHandler.getLogger(req.app);
logger.debug("Authentication session %s is being reset.", req.sessionID);
req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {});
}
export function get(req: express.Request): AuthenticationSession {
if (!req.session.auth)
export function get(req: express.Request): BluebirdPromise<AuthenticationSession> {
const logger = ServerVariablesHandler.getLogger(req.app);
if (!req.session) {
const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can contact it.";
logger.error(errorMsg);
return BluebirdPromise.reject(new Error(errorMsg));
}
if (!req.session.auth) {
logger.debug("Authentication session %s was undefined. Resetting.", req.sessionID);
reset(req);
return req.session.auth;
}
return BluebirdPromise.resolve(req.session.auth);
}

View File

@ -9,10 +9,11 @@ import AuthenticationSession = require("./AuthenticationSession");
export function validate(req: express.Request): BluebirdPromise<void> {
return FirstFactorValidator.validate(req)
.then(function () {
const authSession = AuthenticationSession.get(req);
return AuthenticationSession.get(req);
})
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
if (!authSession.second_factor)
return BluebirdPromise.reject("No second factor variable");
return BluebirdPromise.reject("No second factor variable.");
return BluebirdPromise.resolve();
});
}

View File

@ -6,9 +6,11 @@ import Exceptions = require("./Exceptions");
import AuthenticationSession = require("./AuthenticationSession");
export function validate(req: express.Request): BluebirdPromise<void> {
const authSession = AuthenticationSession.get(req);
return AuthenticationSession.get(req)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
if (!authSession.userid || !authSession.first_factor)
return BluebirdPromise.reject(new Exceptions.FirstFactorValidationError("First factor has not been validated yet."));
return BluebirdPromise.resolve();
});
}

View File

@ -10,7 +10,7 @@ import { IUserDataStore } from "./storage/IUserDataStore";
import { Winston } from "../../types/Dependencies";
import express = require("express");
import ErrorReplies = require("./ErrorReplies");
import { ServerVariablesHandler } from "./ServerVariablesHandler";
import { ServerVariablesHandler } from "./ServerVariablesHandler";
import AuthenticationSession = require("./AuthenticationSession");
import Identity = require("../../types/Identity");
@ -66,7 +66,7 @@ export function get_finish_validation(handler: IdentityValidable): express.Reque
const logger = ServerVariablesHandler.getLogger(req.app);
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const authSession = AuthenticationSession.get(req);
let authSession: AuthenticationSession.AuthenticationSession;
const identityToken = objectPath.get<express.Request, string>(req, "query.identity_token");
logger.info("GET identity_check: identity token provided is %s", identityToken);
@ -74,6 +74,12 @@ export function get_finish_validation(handler: IdentityValidable): express.Reque
.then(function () {
return handler.postValidationInit(req);
})
.then(function () {
return AuthenticationSession.get(req);
})
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
})
.then(function () {
return consumeToken(identityToken, handler.challenge(), userDataStore, logger);
})

View File

@ -1,5 +1,5 @@
import express = require("express");
import Express = require("express");
import { UserDataStore } from "./storage/UserDataStore";
import { Winston } from "../../types/Dependencies";
@ -23,22 +23,29 @@ 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 { ServerVariablesHandler } from "./ServerVariablesHandler";
import Endpoints = require("../endpoints");
function withLog(fn: (req: Express.Request, res: Express.Response) => void) {
return function(req: Express.Request, res: Express.Response) {
const logger = ServerVariablesHandler.getLogger(req.app);
logger.info("Request %s handled on %s", req.method, req.originalUrl);
fn(req, res);
};
}
export class RestApi {
static setup(app: express.Application): void {
app.get(Endpoints.FIRST_FACTOR_GET, FirstFactorGet.default);
app.get(Endpoints.SECOND_FACTOR_GET, SecondFactorGet.default);
app.get(Endpoints.LOGOUT_GET, LogoutGet.default);
static setup(app: Express.Application): void {
app.get(Endpoints.FIRST_FACTOR_GET, withLog(FirstFactorGet.default));
app.get(Endpoints.SECOND_FACTOR_GET, withLog(SecondFactorGet.default));
app.get(Endpoints.LOGOUT_GET, withLog(LogoutGet.default));
IdentityCheckMiddleware.register(app, Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, new TOTPRegistrationIdentityHandler());
@ -49,25 +56,21 @@ export class RestApi {
IdentityCheckMiddleware.register(app, Endpoints.RESET_PASSWORD_IDENTITY_START_GET,
Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET, new ResetPasswordIdentityHandler());
app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, ResetPasswordRequestPost.default);
app.post(Endpoints.RESET_PASSWORD_FORM_POST, ResetPasswordFormPost.default);
app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, withLog(ResetPasswordRequestPost.default));
app.post(Endpoints.RESET_PASSWORD_FORM_POST, withLog(ResetPasswordFormPost.default));
app.get(Endpoints.VERIFY_GET, VerifyGet.default);
app.get(Endpoints.VERIFY_GET, withLog(VerifyGet.default));
app.post(Endpoints.FIRST_FACTOR_POST, withLog(FirstFactorPost.default));
app.post(Endpoints.SECOND_FACTOR_TOTP_POST, withLog(TOTPSignGet.default));
app.post(Endpoints.FIRST_FACTOR_POST, FirstFactorPost.default);
app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, withLog(U2FSignRequestGet.default));
app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, withLog(U2FSignPost.default));
app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, withLog(U2FRegisterRequestGet.default));
app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, withLog(U2FRegisterPost.default));
app.post(Endpoints.SECOND_FACTOR_TOTP_POST, TOTPSignGet.default);
app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, U2FSignRequestGet.default);
app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, U2FSignPost.default);
app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, U2FRegisterRequestGet.default);
app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, U2FRegisterPost.default);
app.get(Endpoints.ERROR_401_GET, Error401Get.default);
app.get(Endpoints.ERROR_403_GET, Error403Get.default);
app.get(Endpoints.ERROR_404_GET, Error404Get.default);
app.get(Endpoints.ERROR_401_GET, withLog(Error401Get.default));
app.get(Endpoints.ERROR_403_GET, withLog(Error403Get.default));
app.get(Endpoints.ERROR_404_GET, withLog(Error404Get.default));
}
}

View File

@ -2,6 +2,7 @@
import ExpressSession = require("express-session");
import { AppConfiguration } from "./Configuration";
import { GlobalDependencies } from "../../../types/Dependencies";
import Redis = require("redis");
export class SessionConfigurationBuilder {
@ -21,9 +22,16 @@ export class SessionConfigurationBuilder {
let redisOptions;
if (configuration.session.redis.host
&& configuration.session.redis.port) {
redisOptions = {
const client = Redis.createClient({
host: configuration.session.redis.host,
port: configuration.session.redis.port
});
client.on("error", function (err: Error) {
console.error("Redis error:", err);
});
redisOptions = {
client: client,
logErrors: true
};
}

View File

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

View File

@ -5,19 +5,29 @@ import winston = require("winston");
import Endpoints = require("../../../endpoints");
import AuthenticationValidator = require("../../AuthenticationValidator");
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
import BluebirdPromise = require("bluebird");
export default function (req: express.Request, res: express.Response) {
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariablesHandler.getLogger(req.app);
logger.debug("First factor: headers are %s", JSON.stringify(req.headers));
AuthenticationValidator.validate(req)
return AuthenticationValidator.validate(req)
.then(function () {
const redirectUrl = req.query.redirect;
if (redirectUrl) {
res.redirect(redirectUrl);
return BluebirdPromise.resolve();
}
else {
res.render("already-logged-in", { logout_endpoint: Endpoints.LOGOUT_GET });
return BluebirdPromise.resolve();
}
}, function () {
res.render("firstfactor", {
first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST,
reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET
});
return BluebirdPromise.resolve();
});
}

View File

@ -27,13 +27,17 @@ export default function (req: express.Request, res: express.Response): BluebirdP
const regulator = ServerVariablesHandler.getAuthenticationRegulator(req.app);
const accessController = ServerVariablesHandler.getAccessController(req.app);
const authSession = AuthenticationSession.get(req);
let authSession: AuthenticationSession.AuthenticationSession;
logger.info("1st factor: Starting authentication of user \"%s\"", username);
logger.debug("1st factor: Start bind operation against LDAP");
logger.debug("1st factor: username=%s", username);
return regulator.regulate(username)
return AuthenticationSession.get(req)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
return regulator.regulate(username);
})
.then(function () {
logger.info("1st factor: No regulation applied.");
return ldap.authenticate(username, password);

View File

@ -3,7 +3,7 @@ import express = require("express");
import BluebirdPromise = require("bluebird");
import objectPath = require("object-path");
import exceptions = require("../../../Exceptions");
import { ServerVariablesHandler } from "../../../ServerVariablesHandler";
import { ServerVariablesHandler } from "../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../AuthenticationSession");
import ErrorReplies = require("../../../ErrorReplies");
@ -12,23 +12,27 @@ import Constants = require("./../constants");
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariablesHandler.getLogger(req.app);
const ldapPasswordUpdater = ServerVariablesHandler.getLdapPasswordUpdater(req.app);
const authSession = AuthenticationSession.get(req);
let authSession: AuthenticationSession.AuthenticationSession;
const newPassword = objectPath.get<express.Request, string>(req, "body.password");
const userid = authSession.identity_check.userid;
const challenge = authSession.identity_check.challenge;
if (challenge != Constants.CHALLENGE) {
return AuthenticationSession.get(req)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
logger.info("POST reset-password: User %s wants to reset his/her password.",
authSession.identity_check.userid);
logger.info("POST reset-password: Challenge %s", authSession.identity_check.challenge);
if (authSession.identity_check.challenge != Constants.CHALLENGE) {
res.status(403);
res.send();
return;
return BluebirdPromise.reject(new Error("Bad challenge."));
}
logger.info("POST reset-password: User %s wants to reset his/her password", userid);
return ldapPasswordUpdater.updatePassword(userid, newPassword)
return ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
})
.then(function () {
logger.info("POST reset-password: Password reset for user '%s'", userid);
logger.info("POST reset-password: Password reset for user '%s'",
authSession.identity_check.userid);
AuthenticationSession.reset(req);
res.status(204);
res.send();

View File

@ -1,14 +1,17 @@
import express = require("express");
import Express = require("express");
import Endpoints = require("../../../endpoints");
import FirstFactorBlocker = require("../FirstFactorBlocker");
import BluebirdPromise = require("bluebird");
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
const TEMPLATE_NAME = "secondfactor";
export default FirstFactorBlocker.default(handler);
function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
function handler(req: Express.Request, res: Express.Response): BluebirdPromise<void> {
const logger = ServerVariablesHandler.getLogger(req.app);
logger.debug("secondfactor request is coming from %s", req.originalUrl);
res.render(TEMPLATE_NAME, {
totp_identity_start_endpoint: Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
u2f_identity_start_endpoint: Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET

View File

@ -5,11 +5,15 @@ import winston = require("winston");
import Endpoints = require("../../../endpoints");
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
import AuthenticationSession = require("../../AuthenticationSession");
import BluebirdPromise = require("bluebird");
export default function (req: express.Request, res: express.Response) {
const authSession = AuthenticationSession.get(req);
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
return AuthenticationSession.get(req)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
const redirectUrl = req.query.redirect || Endpoints.FIRST_FACTOR_GET;
res.json({
redirection_url: redirectUrl
});
return BluebirdPromise.resolve();
});
}

View File

@ -9,7 +9,7 @@ import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationT
import Constants = require("../constants");
import Endpoints = require("../../../../../endpoints");
import ErrorReplies = require("../../../../ErrorReplies");
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession");
import FirstFactorValidator = require("../../../../FirstFactorValidator");
@ -21,7 +21,8 @@ export default class RegistrationHandler implements IdentityValidable {
}
private retrieveIdentity(req: express.Request): BluebirdPromise<Identity> {
const authSession = AuthenticationSession.get(req);
return AuthenticationSession.get(req)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
const userid = authSession.userid;
const email = authSession.email;
@ -34,6 +35,7 @@ export default class RegistrationHandler implements IdentityValidable {
userid: userid
};
return BluebirdPromise.resolve(identity);
});
}
preValidationInit(req: express.Request): BluebirdPromise<Identity> {
@ -52,17 +54,17 @@ export default class RegistrationHandler implements IdentityValidable {
return FirstFactorValidator.validate(req);
}
postValidationResponse(req: express.Request, res: express.Response) {
postValidationResponse(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariablesHandler.getLogger(req.app);
const authSession = AuthenticationSession.get(req);
return AuthenticationSession.get(req)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
const userid = authSession.identity_check.userid;
const challenge = authSession.identity_check.challenge;
if (challenge != Constants.CHALLENGE || !userid) {
res.status(403);
res.send();
return;
return BluebirdPromise.reject(new Error("Bad challenge."));
}
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
@ -70,7 +72,7 @@ export default class RegistrationHandler implements IdentityValidable {
const secret = totpGenerator.generate();
logger.debug("POST new-totp-secret: save the TOTP secret in DB");
userDataStore.saveTOTPSecret(userid, secret)
return userDataStore.saveTOTPSecret(userid, secret)
.then(function () {
objectPath.set(req, "session", undefined);
@ -79,6 +81,7 @@ export default class RegistrationHandler implements IdentityValidable {
otpauth_url: secret.otpauth_url,
login_endpoint: Endpoints.FIRST_FACTOR_GET
});
});
})
.catch(ErrorReplies.replyWithError500(res, logger));
}

View File

@ -16,17 +16,18 @@ const UNAUTHORIZED_MESSAGE = "Unauthorized access";
export default FirstFactorBlocker(handler);
export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
let authSession: AuthenticationSession.AuthenticationSession;
const logger = ServerVariablesHandler.getLogger(req.app);
const authSession = AuthenticationSession.get(req);
const userid = authSession.userid;
logger.info("POST 2ndfactor totp: Initiate TOTP validation for user %s", userid);
const token = req.body.token;
const totpValidator = ServerVariablesHandler.getTOTPValidator(req.app);
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
logger.debug("POST 2ndfactor totp: Fetching secret for user %s", userid);
return userDataStore.retrieveTOTPSecret(userid)
return AuthenticationSession.get(req)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
logger.info("POST 2ndfactor totp: Initiate TOTP validation for user %s", authSession.userid);
return userDataStore.retrieveTOTPSecret(authSession.userid);
})
.then(function (doc: TOTPSecretDocument) {
logger.debug("POST 2ndfactor totp: TOTP secret is %s", JSON.stringify(doc));
return totpValidator.validate(token, doc.secret.base32);

View File

@ -20,13 +20,14 @@ export default class RegistrationHandler implements IdentityValidable {
return CHALLENGE;
}
private retrieveIdentity(req: express.Request) {
const authSession = AuthenticationSession.get(req);
private retrieveIdentity(req: express.Request): BluebirdPromise<Identity> {
return AuthenticationSession.get(req)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
const userid = authSession.userid;
const email = authSession.email;
if (!(userid && email)) {
return BluebirdPromise.reject("User ID or email is missing");
return BluebirdPromise.reject(new Error("User ID or email is missing"));
}
const identity = {
@ -34,6 +35,7 @@ export default class RegistrationHandler implements IdentityValidable {
userid: userid
};
return BluebirdPromise.resolve(identity);
});
}
preValidationInit(req: express.Request): BluebirdPromise<Identity> {

View File

@ -18,7 +18,16 @@ export default FirstFactorBlocker(handler);
function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
const authSession = AuthenticationSession.get(req);
let authSession: AuthenticationSession.AuthenticationSession;
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const u2f = ServerVariablesHandler.getU2F(req.app);
const appid = u2f_common.extract_app_id(req);
const logger = ServerVariablesHandler.getLogger(req.app);
const registrationResponse: U2f.RegistrationData = req.body;
return AuthenticationSession.get(req)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
const registrationRequest = authSession.register_request;
if (!registrationRequest) {
@ -34,20 +43,12 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
return BluebirdPromise.reject(new Error("Bad challenge for registration request"));
}
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const u2f = ServerVariablesHandler.getU2F(req.app);
const userid = authSession.userid;
const appid = u2f_common.extract_app_id(req);
const logger = ServerVariablesHandler.getLogger(req.app);
const registrationResponse: U2f.RegistrationData = req.body;
logger.info("U2F register: Finishing registration");
logger.debug("U2F register: registrationRequest = %s", JSON.stringify(registrationRequest));
logger.debug("U2F register: registrationResponse = %s", JSON.stringify(registrationResponse));
BluebirdPromise.resolve(u2f.checkRegistration(registrationRequest, registrationResponse))
return BluebirdPromise.resolve(u2f.checkRegistration(registrationRequest, registrationResponse));
})
.then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise<void> {
if (objectPath.has(u2fResult, "errorCode"))
return BluebirdPromise.reject(new Error("Error while registering."));
@ -59,7 +60,7 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
keyHandle: registrationResult.keyHandle,
publicKey: registrationResult.publicKey
};
return userDataStore.saveU2FRegistration(userid, appid, registration);
return userDataStore.saveU2FRegistration(authSession.userid, appid, registration);
})
.then(function () {
authSession.identity_check = undefined;

View File

@ -8,20 +8,24 @@ import express = require("express");
import U2f = require("u2f");
import FirstFactorBlocker from "../../../FirstFactorBlocker";
import ErrorReplies = require("../../../../ErrorReplies");
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import {  ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession");
export default FirstFactorBlocker(handler);
function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariablesHandler.getLogger(req.app);
const authSession = AuthenticationSession.get(req);
let authSession: AuthenticationSession.AuthenticationSession;
return AuthenticationSession.get(req)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
if (!authSession.identity_check
|| authSession.identity_check.challenge != "u2f-register") {
res.status(403);
res.send();
return;
return BluebirdPromise.reject(new Error("Bad challenge."));
}
const u2f = ServerVariablesHandler.getU2F(req.app);
@ -30,7 +34,8 @@ function handler(req: express.Request, res: express.Response): BluebirdPromise<v
logger.debug("U2F register_request: headers=%s", JSON.stringify(req.headers));
logger.info("U2F register_request: Starting registration for appId %s", appid);
return BluebirdPromise.resolve(u2f.request(appid))
return BluebirdPromise.resolve(u2f.request(appid));
})
.then(function (registrationRequest: U2f.Request) {
logger.debug("U2F register_request: registrationRequest = %s", JSON.stringify(registrationRequest));
authSession.register_request = registrationRequest;

View File

@ -11,7 +11,7 @@ import exceptions = require("../../../../Exceptions");
import FirstFactorBlocker from "../../../FirstFactorBlocker";
import redirect from "../../redirect";
import ErrorReplies = require("../../../../ErrorReplies");
import {  ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession");
export default FirstFactorBlocker(handler);
@ -19,8 +19,11 @@ export default FirstFactorBlocker(handler);
export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariablesHandler.getLogger(req.app);
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const authSession = AuthenticationSession.get(req);
let authSession: AuthenticationSession.AuthenticationSession;
return AuthenticationSession.get(req)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
if (!authSession.sign_request) {
const err = new Error("No sign request");
ErrorReplies.replyWithError401(res, logger)(err);
@ -29,7 +32,8 @@ export function handler(req: express.Request, res: express.Response): BluebirdPr
const userid = authSession.userid;
const appid = u2f_common.extract_app_id(req);
return userDataStore.retrieveU2FRegistration(userid, appid)
return userDataStore.retrieveU2FRegistration(userid, appid);
})
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<U2f.SignatureResult | U2f.Error> {
const appId = u2f_common.extract_app_id(req);
const u2f = ServerVariablesHandler.getU2F(req.app);

View File

@ -11,7 +11,7 @@ import exceptions = require("../../../../Exceptions");
import { SignMessage } from "./SignMessage";
import FirstFactorBlocker from "../../../FirstFactorBlocker";
import ErrorReplies = require("../../../../ErrorReplies");
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import { ServerVariablesHandler } from "../../../../ServerVariablesHandler";
import AuthenticationSession = require("../../../../AuthenticationSession");
export default FirstFactorBlocker(handler);
@ -19,11 +19,14 @@ export default FirstFactorBlocker(handler);
export function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariablesHandler.getLogger(req.app);
const userDataStore = ServerVariablesHandler.getUserDataStore(req.app);
const authSession = AuthenticationSession.get(req);
const userId = authSession.userid;
let authSession: AuthenticationSession.AuthenticationSession;
const appId = u2f_common.extract_app_id(req);
return userDataStore.retrieveU2FRegistration(userId, appId)
return AuthenticationSession.get(req)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
return userDataStore.retrieveU2FRegistration(authSession.userid, appId);
})
.then(function (doc: U2FRegistrationDocument): BluebirdPromise<SignMessage> {
if (!doc)
return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration found"));

View File

@ -8,18 +8,22 @@ import exceptions = require("../../Exceptions");
import winston = require("winston");
import AuthenticationValidator = require("../../AuthenticationValidator");
import ErrorReplies = require("../../ErrorReplies");
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
import {  ServerVariablesHandler } from "../../ServerVariablesHandler";
import AuthenticationSession = require("../../AuthenticationSession");
function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> {
const logger = ServerVariablesHandler.getLogger(req.app);
const accessController = ServerVariablesHandler.getAccessController(req.app);
const authSession = AuthenticationSession.get(req);
let authSession: AuthenticationSession.AuthenticationSession;
return AuthenticationSession.get(req)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
logger.debug("Verify: headers are %s", JSON.stringify(req.headers));
res.set("Redirect", encodeURIComponent("https://" + req.headers["host"] + req.headers["x-original-uri"]));
return AuthenticationValidator.validate(req)
return AuthenticationValidator.validate(req);
})
.then(function () {
const username = authSession.userid;
const groups = authSession.groups;

View File

@ -1,7 +1,11 @@
Feature: User has access restricted access to domains
@need-registered-user-john
Scenario: User john has admin access
When I register TOTP and login with user "john" and password "password"
When I visit "https://auth.test.local:8080"
And I login with user "john" and password "password"
And I use "REGISTERED" as TOTP token handle
And I click on "TOTP"
Then I have access to:
| url |
| https://public.test.local:8080/secret.html |
@ -11,8 +15,12 @@ Feature: User has access restricted access to domains
| https://mx1.mail.test.local:8080/secret.html |
| https://mx2.mail.test.local:8080/secret.html |
@need-registered-user-bob
Scenario: User bob has restricted access
When I register TOTP and login with user "bob" and password "password"
When I visit "https://auth.test.local:8080"
And I login with user "bob" and password "password"
And I use "REGISTERED" as TOTP token handle
And I click on "TOTP"
Then I have access to:
| url |
| https://public.test.local:8080/secret.html |
@ -24,8 +32,12 @@ Feature: User has access restricted access to domains
| url |
| https://secret1.test.local:8080/secret.html |
@need-registered-user-harry
Scenario: User harry has restricted access
When I register TOTP and login with user "harry" and password "password"
When I visit "https://auth.test.local:8080"
And I login with user "harry" and password "password"
And I use "REGISTERED" as TOTP token handle
And I click on "TOTP"
Then I have access to:
| url |
| https://public.test.local:8080/secret.html |

View File

@ -14,7 +14,7 @@ Feature: User validate first factor
And I click on "Sign in"
Then I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
Scenario: User succeeds TOTP second factor
Scenario: User registers TOTP secret and succeeds authentication
Given I visit "https://auth.test.local:8080/"
And I login with user "john" and password "password"
And I register a TOTP secret called "Sec0"
@ -31,23 +31,6 @@ Feature: User validate first factor
And I click on "TOTP"
Then I get a notification of type "error" with message "Problem with TOTP validation."
Scenario: User logs out
Given I visit "https://auth.test.local:8080/"
And I login with user "john" and password "password"
And I register a TOTP secret called "Sec0"
And I visit "https://auth.test.local:8080/"
And I login with user "john" and password "password"
And I use "Sec0" as TOTP token handle
When I visit "https://auth.test.local:8080/logout?redirect=https://www.google.fr"
And I visit "https://secret.test.local:8080/secret.html"
Then I'm redirected to "https://auth.test.local:8080/"
Scenario: Logout redirects user
Given I visit "https://auth.test.local:8080/"
And I login with user "john" and password "password"
And I register a TOTP secret called "Sec0"
And I visit "https://auth.test.local:8080/"
And I login with user "john" and password "password"
And I use "Sec0" as TOTP token handle
Scenario: Logout redirects user to redirect URL given in parameter
When I visit "https://auth.test.local:8080/logout?redirect=https://www.google.fr"
Then I'm redirected to "https://www.google.fr"

View File

@ -5,19 +5,17 @@ Feature: User is correctly redirected
When I click on the link to secret.test.local
Then I'm redirected to "https://auth.test.local:8080/"
@need-registered-user-john
Scenario: User is redirected to home page after several authentication tries
Given I'm on https://auth.test.local:8080/
And I login with user "john" and password "password"
And I register a TOTP secret called "Sec0"
And I visit "https://public.test.local:8080/secret.html"
When I login with user "john" and password "badpassword"
When I visit "https://public.test.local:8080/secret.html"
And I login with user "john" and password "badpassword"
And I clear field "username"
And I login with user "john" and password "password"
And I use "Sec0" as TOTP token handle
And I use "REGISTERED" as TOTP token handle
And I click on "TOTP"
Then I'm redirected to "https://public.test.local:8080/secret.html"
Scenario: User Harry does not have access to https://secret.test.local:8080/secret.html and thus he must get an error 401
Scenario: User Harry does not have access to https://secret.test.local:8080/secret.html and thus he must get an error 403
When I register TOTP and login with user "harry" and password "password"
And I visit "https://secret.test.local:8080/secret.html"
Then I get an error 403

View File

@ -1,14 +1,9 @@
Feature: Authelia regulates authentication to avoid brute force
@needs-test-config
@need-registered-user-blackhat
Scenario: Attacker tries too many authentication in a short period of time and get banned
Given I visit "https://auth.test.local:8080/"
And I login with user "blackhat" and password "password"
And I register a TOTP secret called "Sec0"
And I visit "https://auth.test.local:8080/"
And I login with user "blackhat" and password "password" and I use TOTP token handle "Sec0"
And I visit "https://auth.test.local:8080/logout?redirect=https://auth.test.local:8080/"
And I visit "https://auth.test.local:8080/"
And I set field "username" to "blackhat"
And I set field "password" to "bad-password"
And I click on "Sign in"
@ -24,14 +19,9 @@ Feature: Authelia regulates authentication to avoid brute force
Then I get a notification of type "error" with message "Authentication failed. Please double check your credentials."
@needs-test-config
@need-registered-user-blackhat
Scenario: User is unbanned after a configured amount of time
Given I visit "https://auth.test.local:8080/"
And I login with user "blackhat" and password "password"
And I register a TOTP secret called "Sec0"
And I visit "https://auth.test.local:8080/"
And I login with user "blackhat" and password "password" and I use TOTP token handle "Sec0"
And I visit "https://auth.test.local:8080/logout?redirect=https://auth.test.local:8080/"
And I visit "https://auth.test.local:8080/"
Given I visit "https://auth.test.local:8080/?redirect=https%3A%2F%2Fpublic.test.local%3A8080%2Fsecret.html"
And I set field "username" to "blackhat"
And I set field "password" to "bad-password"
And I click on "Sign in"
@ -45,7 +35,7 @@ Feature: Authelia regulates authentication to avoid brute force
When I wait 6 seconds
And I set field "password" to "password"
And I click on "Sign in"
And I use "Sec0" as TOTP token handle
And I use "REGISTERED" as TOTP token handle
And I click on "TOTP"
Then I have access to:
| url |

View File

@ -1,20 +1,18 @@
Feature: Authelia keeps user sessions despite the application restart
@need-authenticated-user-john
Scenario: Session is still valid after Authelia restarts
When I register TOTP and login with user "john" and password "password"
And the application restarts
When the application restarts
Then I have access to:
| url |
| https://secret.test.local:8080/secret.html |
@need-registered-user-john
Scenario: Secrets are stored even when Authelia restarts
Given I visit "https://auth.test.local:8080/"
And I login with user "john" and password "password"
And I register a TOTP secret called "Sec0"
When the application restarts
And I visit "https://secret.test.local:8080/secret.html" and get redirected "https://auth.test.local:8080/?redirect=https%3A%2F%2Fsecret.test.local%3A8080%2Fsecret.html"
And I login with user "john" and password "password"
And I use "Sec0" as TOTP token handle
And I use "REGISTERED" as TOTP token handle
And I click on "TOTP"
Then I have access to:
| url |

View File

@ -1,6 +1,6 @@
Feature: Non authenticated users have no access to certain pages
Scenario Outline: User has no access to protected pages
Scenario Outline: Anonymous user has no access to protected pages
When I visit "<url>"
Then I get an error <error code>

View File

@ -6,7 +6,7 @@ import Speakeasy = require("speakeasy");
import CustomWorld = require("../support/world");
Cucumber.defineSupportCode(function ({ Given, When, Then }) {
When(/^I visit "(https:\/\/[a-z0-9:.\/=?-]+)"$/, function (link: string) {
When(/^I visit "(https:\/\/[a-zA-Z0-9:%.\/=?-]+)"$/, function (link: string) {
return this.visit(link);
});

View File

@ -2,23 +2,90 @@ import Cucumber = require("cucumber");
import fs = require("fs");
import BluebirdPromise = require("bluebird");
import ChildProcess = require("child_process");
import { UserDataStore } from "../../../src/server/lib/storage/UserDataStore";
import { CollectionFactoryFactory } from "../../../src/server/lib/storage/CollectionFactoryFactory";
import { MongoConnector } from "../../../src/server/lib/connectors/mongo/MongoConnector";
import { IMongoClient } from "../../../src/server/lib/connectors/mongo/IMongoClient";
import { TOTPGenerator } from "../../../src/server/lib/TOTPGenerator";
import Speakeasy = require("speakeasy");
Cucumber.defineSupportCode(function({ setDefaultTimeout }) {
Cucumber.defineSupportCode(function ({ setDefaultTimeout }) {
setDefaultTimeout(20 * 1000);
});
Cucumber.defineSupportCode(function({ After, Before }) {
Cucumber.defineSupportCode(function ({ After, Before }) {
const exec = BluebirdPromise.promisify(ChildProcess.exec);
After(function() {
After(function () {
return this.driver.quit();
});
Before({tags: "@needs-test-config", timeout: 15 * 1000}, function () {
Before({ tags: "@needs-test-config", timeout: 20 * 1000 }, function () {
return exec("./scripts/example-commit/dc-example.sh -f docker-compose.test.yml up -d authelia && sleep 2");
});
After({tags: "@needs-test-config", timeout: 15 * 1000}, function () {
After({ tags: "@needs-test-config", timeout: 20 * 1000 }, function () {
return exec("./scripts/example-commit/dc-example.sh up -d authelia && sleep 2");
});
function registerUser(context: any, username: string) {
let secret: Speakeasy.Key;
const mongoConnector = new MongoConnector("mongodb://localhost:27017/authelia");
return mongoConnector.connect()
.then(function (mongoClient: IMongoClient) {
const collectionFactory = CollectionFactoryFactory.createMongo(mongoClient);
const userDataStore = new UserDataStore(collectionFactory);
const generator = new TOTPGenerator(Speakeasy);
secret = generator.generate();
return userDataStore.saveTOTPSecret(username, secret);
})
.then(function () {
context.totpSecrets["REGISTERED"] = secret.base32;
});
}
function declareNeedRegisteredUserHooks(username: string) {
Before({ tags: "@need-registered-user-" + username, timeout: 15 * 1000 }, function () {
return registerUser(this, username);
});
After({ tags: "@need-registered-user-" + username, timeout: 15 * 1000 }, function () {
this.totpSecrets["REGISTERED"] = undefined;
});
}
function needAuthenticatedUser(context: any, username: string): BluebirdPromise<void> {
return context.visit("https://auth.test.local:8080/")
.then(function () {
return registerUser(context, username);
})
.then(function () {
return context.loginWithUserPassword(username, "password");
})
.then(function () {
return context.useTotpTokenHandle("REGISTERED");
})
.then(function() {
return context.clickOnButton("TOTP");
});
}
function declareNeedAuthenticatedUserHooks(username: string) {
Before({ tags: "@need-authenticated-user-" + username, timeout: 15 * 1000 }, function () {
return needAuthenticatedUser(this, username);
});
After({ tags: "@need-authenticated-user-" + username, timeout: 15 * 1000 }, function () {
this.totpSecrets["REGISTERED"] = undefined;
});
}
function declareHooksForUser(username: string) {
declareNeedRegisteredUserHooks(username);
declareNeedAuthenticatedUserHooks(username);
}
const users = ["harry", "john", "bob", "blackhat"];
users.forEach(declareHooksForUser);
});

View File

@ -91,6 +91,9 @@ function CustomWorld() {
};
this.useTotpTokenHandle = function (totpSecretHandle: string) {
if (!this.totpSecrets[totpSecretHandle])
throw new Error("No available TOTP token handle " + totpSecretHandle);
const token = Speakeasy.totp({
secret: this.totpSecrets[totpSecretHandle],
encoding: "base32"

View File

@ -155,9 +155,14 @@ describe("test identity check process", function () {
req.query.identity_token = "token";
req.session = {};
const authSession = AuthenticationSession.get(req as any);
let authSession: AuthenticationSession.AuthenticationSession;
const callback = IdentityValidator.get_finish_validation(identityValidable);
return callback(req as any, res as any, undefined)
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
return callback(req as any, res as any, undefined);
})
.then(function () { return BluebirdPromise.reject("Should fail"); })
.catch(function () {
assert.equal(authSession.identity_check.userid, "user");

View File

@ -45,6 +45,7 @@ describe("test the first factor validation route", function () {
req = {
app: {
get: sinon.stub().returns({ logger: winston })
},
body: {
username: "username",
@ -77,8 +78,12 @@ describe("test the first factor validation route", function () {
emails: emails,
groups: groups
}));
const authSession = AuthenticationSession.get(req as any);
return FirstFactorPost.default(req as any, res as any)
let authSession: AuthenticationSession.AuthenticationSession;
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
return FirstFactorPost.default(req as any, res as any);
})
.then(function () {
assert.equal("username", authSession.userid);
assert(res.send.calledOnce);
@ -94,13 +99,18 @@ describe("test the first factor validation route", function () {
it("should set first email address as user session variable", function () {
const emails = ["test_ok@example.com"];
const authSession = AuthenticationSession.get(req as any);
let authSession: AuthenticationSession.AuthenticationSession;
(serverVariables.ldapAuthenticator as any).authenticate.withArgs("username", "password")
.returns(BluebirdPromise.resolve({
emails: emails,
groups: groups
}));
return FirstFactorPost.default(req as any, res as any)
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
return FirstFactorPost.default(req as any, res as any);
})
.then(function () {
assert.equal("test_ok@example.com", authSession.email);
});

View File

@ -16,7 +16,6 @@ describe("test reset password route", function () {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let configuration: any;
let authSession: AuthenticationSession.AuthenticationSession;
let serverVariables: ServerVariablesMock.ServerVariablesMock;
beforeEach(function () {
@ -25,7 +24,7 @@ describe("test reset password route", function () {
userid: "user"
},
app: {
get: Sinon.stub()
get: Sinon.stub().returns({ logger: winston })
},
session: {},
headers: {
@ -34,11 +33,6 @@ describe("test reset password route", function () {
};
AuthenticationSession.reset(req as any);
authSession = AuthenticationSession.get(req as any);
authSession.userid = "user";
authSession.email = "user@example.com";
authSession.first_factor = true;
authSession.second_factor = false;
const options = {
inMemoryOnly: true
@ -65,54 +59,72 @@ describe("test reset password route", function () {
} as any;
res = ExpressMock.ResponseMock();
AuthenticationSession.get(req as any)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
authSession.userid = "user";
authSession.email = "user@example.com";
authSession.first_factor = true;
authSession.second_factor = false;
});
});
describe("test reset password post", () => {
it("should update the password and reset auth_session for reauthentication", function () {
authSession.identity_check = {
userid: "user",
challenge: "reset-password"
};
req.body = {};
req.body.password = "new-password";
(serverVariables.ldapPasswordUpdater.updatePassword as sinon.SinonStub).returns(BluebirdPromise.resolve());
return PasswordResetFormPost.default(req as any, res as any)
return AuthenticationSession.get(req as any)
.then(function (authSession) {
authSession.identity_check = {
userid: "user",
challenge: "reset-password"
};
return PasswordResetFormPost.default(req as any, res as any);
})
.then(function () {
const authSession = AuthenticationSession.get(req as any);
return AuthenticationSession.get(req as any);
}).then(function (_authSession: AuthenticationSession.AuthenticationSession) {
assert.equal(res.status.getCall(0).args[0], 204);
assert.equal(authSession.first_factor, false);
assert.equal(authSession.second_factor, false);
assert.equal(_authSession.first_factor, false);
assert.equal(_authSession.second_factor, false);
return BluebirdPromise.resolve();
});
});
it("should fail if identity_challenge does not exist", function (done) {
it("should fail if identity_challenge does not exist", function () {
return AuthenticationSession.get(req as any)
.then(function (authSession) {
authSession.identity_check = {
userid: "user",
challenge: undefined
};
res.send = Sinon.spy(function () {
return PasswordResetFormPost.default(req as any, res as any);
})
.then(function () {
assert.equal(res.status.getCall(0).args[0], 403);
done();
});
PasswordResetFormPost.default(req as any, res as any);
});
it("should fail when ldap fails", function (done) {
it("should fail when ldap fails", function () {
req.body = {};
req.body.password = "new-password";
(serverVariables.ldapPasswordUpdater.updatePassword as Sinon.SinonStub)
.returns(BluebirdPromise.reject("Internal error with LDAP"));
return AuthenticationSession.get(req as any)
.then(function (authSession) {
authSession.identity_check = {
challenge: "reset-password",
userid: "user"
};
req.body = {};
req.body.password = "new-password";
(serverVariables.ldapPasswordUpdater.updatePassword as Sinon.SinonStub).returns(BluebirdPromise.reject("Internal error with LDAP"));
res.send = Sinon.spy(function () {
return PasswordResetFormPost.default(req as any, res as any);
}).then(function () {
assert.equal(res.status.getCall(0).args[0], 500);
done();
return BluebirdPromise.resolve();
});
PasswordResetFormPost.default(req as any, res as any);
});
});
});

View File

@ -23,11 +23,6 @@ describe("test totp register", function () {
req.session = {};
AuthenticationSession.reset(req as any);
authSession = AuthenticationSession.get(req as any);
authSession.userid = "user";
authSession.email = "user@example.com";
authSession.first_factor = true;
authSession.second_factor = false;
req.headers = {};
req.headers.host = "localhost";
@ -43,6 +38,15 @@ describe("test totp register", function () {
mocks.userDataStore.saveTOTPSecretStub.returns(BluebirdPromise.resolve({}));
res = ExpressMock.ResponseMock();
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
authSession.userid = "user";
authSession.email = "user@example.com";
authSession.first_factor = true;
authSession.second_factor = false;
});
});
describe("test totp registration check", test_registration_check);

View File

@ -23,6 +23,7 @@ describe("test totp route", function () {
const app_get = sinon.stub();
req = {
app: {
get: sinon.stub().returns({ logger: winston })
},
body: {
token: "abc"
@ -30,11 +31,6 @@ describe("test totp route", function () {
session: {}
};
AuthenticationSession.reset(req as any);
authSession = AuthenticationSession.get(req as any);
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
const mocks = ServerVariablesMock.mock(req.app);
res = ExpressMock.ResponseMock();
@ -52,6 +48,14 @@ describe("test totp route", function () {
mocks.logger = winston;
mocks.totpValidator = totpValidator;
mocks.config = config;
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
});
});

View File

@ -23,11 +23,6 @@ describe("test register handler", function () {
mocks.logger = winston;
req.session = {};
AuthenticationSession.reset(req as any);
authSession = AuthenticationSession.get(req as any);
authSession.userid = "user";
authSession.email = "user@example.com";
authSession.first_factor = true;
authSession.second_factor = false;
req.headers = {};
req.headers.host = "localhost";
@ -44,6 +39,15 @@ describe("test register handler", function () {
res.send = sinon.spy();
res.json = sinon.spy();
res.status = sinon.spy();
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
authSession.userid = "user";
authSession.email = "user@example.com";
authSession.first_factor = true;
authSession.second_factor = false;
});
});
describe("test u2f registration check", test_registration_check);

View File

@ -17,7 +17,6 @@ describe("test u2f routes: register", function () {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let mocks: ServerVariablesMock.ServerVariablesMock;
let authSession: AuthenticationSession.AuthenticationSession;
beforeEach(function () {
req = ExpressMock.RequestMock();
@ -26,16 +25,6 @@ describe("test u2f routes: register", function () {
mocks.logger = winston;
req.session = {};
AuthenticationSession.reset(req as any);
authSession = AuthenticationSession.get(req as any);
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
authSession.identity_check = {
challenge: "u2f-register",
userid: "user"
};
req.headers = {};
req.headers.host = "localhost";
@ -50,6 +39,18 @@ describe("test u2f routes: register", function () {
res.send = sinon.spy();
res.json = sinon.spy();
res.status = sinon.spy();
AuthenticationSession.reset(req as any);
return AuthenticationSession.get(req as any)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
authSession.identity_check = {
challenge: "u2f-register",
userid: "user"
};
});
});
describe("test registration", test_registration);
@ -64,16 +65,22 @@ describe("test u2f routes: register", function () {
};
const u2f_mock = U2FMock.U2FMock();
u2f_mock.checkRegistration.returns(BluebirdPromise.resolve(expectedStatus));
mocks.u2f = u2f_mock;
return AuthenticationSession.get(req as any)
.then(function (authSession) {
authSession.register_request = {
appId: "app",
challenge: "challenge",
keyHandle: "key",
version: "U2F_V2"
};
mocks.u2f = u2f_mock;
return U2FRegisterPost.default(req as any, res as any)
return U2FRegisterPost.default(req as any, res as any);
})
.then(function () {
return AuthenticationSession.get(req as any);
})
.then(function (authSession) {
assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]);
assert.equal(authSession.identity_check, undefined);
});
@ -83,15 +90,19 @@ describe("test u2f routes: register", function () {
const user_key_container = {};
const u2f_mock = U2FMock.U2FMock();
u2f_mock.checkRegistration.returns({ errorCode: 500 });
mocks.u2f = u2f_mock;
return AuthenticationSession.get(req as any)
.then(function (authSession) {
authSession.register_request = {
appId: "app",
challenge: "challenge",
keyHandle: "key",
version: "U2F_V2"
};
mocks.u2f = u2f_mock;
return U2FRegisterPost.default(req as any, res as any)
return U2FRegisterPost.default(req as any, res as any);
})
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
.catch(function () {
assert.equal(500, res.status.getCall(0).args[0]);
@ -104,9 +115,12 @@ describe("test u2f routes: register", function () {
const u2f_mock = U2FMock.U2FMock();
u2f_mock.checkRegistration.returns(BluebirdPromise.resolve());
authSession.register_request = undefined;
mocks.u2f = u2f_mock;
return U2FRegisterPost.default(req as any, res as any)
return AuthenticationSession.get(req as any)
.then(function (authSession) {
authSession.register_request = undefined;
return U2FRegisterPost.default(req as any, res as any);
})
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
.catch(function () {
assert.equal(403, res.status.getCall(0).args[0]);
@ -119,9 +133,12 @@ describe("test u2f routes: register", function () {
const u2f_mock = U2FMock.U2FMock();
u2f_mock.checkRegistration.returns(BluebirdPromise.resolve());
authSession.register_request = undefined;
mocks.u2f = u2f_mock;
return U2FRegisterPost.default(req as any, res as any)
return AuthenticationSession.get(req as any)
.then(function (authSession) {
authSession.register_request = undefined;
return U2FRegisterPost.default(req as any, res as any);
})
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
.catch(function () {
assert.equal(403, res.status.getCall(0).args[0]);
@ -130,8 +147,11 @@ describe("test u2f routes: register", function () {
});
it("should return forbidden error when identity has not been verified", function () {
return AuthenticationSession.get(req as any)
.then(function (authSession) {
authSession.identity_check = undefined;
return U2FRegisterPost.default(req as any, res as any)
return U2FRegisterPost.default(req as any, res as any);
})
.then(function () { return BluebirdPromise.reject(new Error("It should fail")); })
.catch(function () {
assert.equal(403, res.status.getCall(0).args[0]);

View File

@ -26,15 +26,6 @@ describe("test u2f routes: register_request", function () {
mocks.logger = winston;
req.session = {};
AuthenticationSession.reset(req as any);
authSession = AuthenticationSession.get(req as any);
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
authSession.identity_check = {
challenge: "u2f-register",
userid: "user"
};
req.headers = {};
req.headers.host = "localhost";
@ -51,6 +42,18 @@ describe("test u2f routes: register_request", function () {
res.send = sinon.spy();
res.json = sinon.spy();
res.status = sinon.spy();
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
authSession.identity_check = {
challenge: "u2f-register",
userid: "user"
};
});
});
describe("test registration request", () => {
@ -82,13 +85,12 @@ describe("test u2f routes: register_request", function () {
U2FRegisterRequestGet.default(req as any, res as any);
});
it("should return forbidden if identity has not been verified", function (done) {
res.send = sinon.spy(function (data: any) {
assert.equal(403, res.status.getCall(0).args[0]);
done();
});
it("should return forbidden if identity has not been verified", function () {
authSession.identity_check = undefined;
U2FRegisterRequestGet.default(req as any, res as any);
return U2FRegisterRequestGet.default(req as any, res as any)
.then(function () {
assert.equal(403, res.status.getCall(0).args[0]);
});
});
});
});

View File

@ -27,14 +27,6 @@ describe("test u2f routes: sign", function () {
req.session = {};
AuthenticationSession.reset(req as any);
authSession = AuthenticationSession.get(req as any);
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
authSession.identity_check = {
challenge: "u2f-register",
userid: "user"
};
req.headers = {};
req.headers.host = "localhost";
@ -46,6 +38,18 @@ describe("test u2f routes: sign", function () {
res.send = sinon.spy();
res.json = sinon.spy();
res.status = sinon.spy();
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
authSession.identity_check = {
challenge: "u2f-register",
userid: "user"
};
});
});
it("should return status code 204", function () {

View File

@ -31,14 +31,6 @@ describe("test u2f routes: sign_request", function () {
req.session = {};
AuthenticationSession.reset(req as any);
authSession = AuthenticationSession.get(req as any);
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
authSession.identity_check = {
challenge: "u2f-register",
userid: "user"
};
req.headers = {};
req.headers.host = "localhost";
@ -51,6 +43,18 @@ describe("test u2f routes: sign_request", function () {
res.send = sinon.spy();
res.json = sinon.spy();
res.status = sinon.spy();
return AuthenticationSession.get(req as any)
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
authSession = _authSession;
authSession.userid = "user";
authSession.first_factor = true;
authSession.second_factor = false;
authSession.identity_check = {
challenge: "u2f-register",
userid: "user"
};
});
});
it("should send back the sign request and save it in the session", function () {

View File

@ -24,6 +24,9 @@ describe("test authentication token verification", function () {
req = ExpressMock.RequestMock();
res = ExpressMock.ResponseMock();
req.app = {
get: sinon.stub().returns({ logger: winston })
};
req.session = {};
AuthenticationSession.reset(req as any);
req.headers = {};
@ -37,12 +40,13 @@ describe("test authentication token verification", function () {
it("should be already authenticated", function () {
req.session = {};
AuthenticationSession.reset(req as any);
const authSession = AuthenticationSession.get(req as any);
return AuthenticationSession.get(req as any)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
authSession.first_factor = true;
authSession.second_factor = true;
authSession.userid = "myuser";
return VerifyGet.default(req as express.Request, res as any)
return VerifyGet.default(req as express.Request, res as any);
})
.then(function () {
assert.equal(204, res.status.getCall(0).args[0]);
});
@ -113,7 +117,8 @@ describe("test authentication token verification", function () {
});
it("should not be authenticated when domain is not allowed for user", function () {
const authSession = AuthenticationSession.get(req as any);
return AuthenticationSession.get(req as any)
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
authSession.first_factor = true;
authSession.second_factor = true;
authSession.userid = "myuser";
@ -132,5 +137,6 @@ describe("test authentication token verification", function () {
});
});
});
});
});