Merge pull request #89 from clems4ever/redis-connection-issue-logs

Make failing connection to redis more clear in the logs
pull/92/head
Clément Michaud 2017-09-22 21:05:33 +02:00 committed by GitHub
commit ae5b647d23
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,
@ -30,11 +31,25 @@ export function reset(req: express.Request): void {
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)
reset(req);
return 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 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

@ -15,13 +15,17 @@ 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

@ -14,12 +14,16 @@ 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,6 +2,12 @@ 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 }) {
setDefaultTimeout(20 * 1000);
@ -14,11 +20,72 @@ Cucumber.defineSupportCode(function({ After, Before }) {
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();
});
PasswordResetFormPost.default(req as any, res as any);
return BluebirdPromise.resolve();
});
});
});
});

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";
@ -133,4 +138,5 @@ describe("test authentication token verification", function () {
});
});
});
});