diff --git a/client/src/lib/secondfactor/TOTPValidator.ts b/client/src/lib/secondfactor/TOTPValidator.ts index 5d845edc..5394139a 100644 --- a/client/src/lib/secondfactor/TOTPValidator.ts +++ b/client/src/lib/secondfactor/TOTPValidator.ts @@ -1,6 +1,8 @@ import BluebirdPromise = require("bluebird"); import Endpoints = require("../../../../shared/api"); +import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; +import { ErrorMessage } from "../../../../shared/ErrorMessage"; export function validate(token: string, $: JQueryStatic): BluebirdPromise { return new BluebirdPromise(function (resolve, reject) { @@ -12,12 +14,12 @@ export function validate(token: string, $: JQueryStatic): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { +function finishU2fAuthentication(responseData: U2fApi.SignResponse, $: JQueryStatic): BluebirdPromise { + return new BluebirdPromise(function (resolve, reject) { $.ajax({ url: Endpoints.SECOND_FACTOR_U2F_SIGN_POST, data: responseData, method: "POST", dataType: "json" } as JQueryAjaxSettings) - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); + .done(function (body: RedirectionMessage | ErrorMessage) { + if (body && "error" in body) { + reject(new Error((body as ErrorMessage).error)); return; } - resolve(body); + resolve((body as RedirectionMessage).redirect); }) .fail(function (xhr: JQueryXHR, textStatus: string) { reject(new Error(textStatus)); @@ -28,8 +30,8 @@ function finishU2fAuthentication(responseData: U2fApi.SignResponse, $: JQuerySta }); } -function startU2fAuthentication($: JQueryStatic, notifier: INotifier, u2fApi: typeof U2fApi): BluebirdPromise { - return new BluebirdPromise(function (resolve, reject) { +function startU2fAuthentication($: JQueryStatic, notifier: INotifier, u2fApi: typeof U2fApi): BluebirdPromise { + return new BluebirdPromise(function (resolve, reject) { $.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, {}, undefined, "json") .done(function (signResponse: SignMessage) { notifier.info(UserMessages.PLEASE_TOUCH_TOKEN); @@ -44,8 +46,8 @@ function startU2fAuthentication($: JQueryStatic, notifier: INotifier, u2fApi: ty u2fApi.sign([signRequest], 60) .then(function (signResponse: U2fApi.SignResponse) { finishU2fAuthentication(signResponse, $) - .then(function (data) { - resolve(data); + .then(function (redirect: string) { + resolve(redirect); }, function (err) { notifier.error(UserMessages.U2F_TRANSACTION_FINISH_FAILED); reject(err); @@ -62,6 +64,6 @@ function startU2fAuthentication($: JQueryStatic, notifier: INotifier, u2fApi: ty } -export function validate($: JQueryStatic, notifier: INotifier, u2fApi: typeof U2fApi): BluebirdPromise { +export function validate($: JQueryStatic, notifier: INotifier, u2fApi: typeof U2fApi): BluebirdPromise { return startU2fAuthentication($, notifier, u2fApi); } diff --git a/client/src/lib/secondfactor/index.ts b/client/src/lib/secondfactor/index.ts index c829dea8..bd3f9e13 100644 --- a/client/src/lib/secondfactor/index.ts +++ b/client/src/lib/secondfactor/index.ts @@ -4,35 +4,37 @@ import jslogger = require("js-logger"); import TOTPValidator = require("./TOTPValidator"); import U2FValidator = require("./U2FValidator"); -import Constants = require("./constants"); +import ClientConstants = require("./constants"); import { Notifier } from "../Notifier"; import { QueryParametersRetriever } from "../QueryParametersRetriever"; import Endpoints = require("../../../../shared/api"); import ServerConstants = require("../../../../shared/constants"); import UserMessages = require("../../../../shared/UserMessages"); +import SharedConstants = require("../../../../shared/constants"); export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi) { const notifierTotp = new Notifier(".notification-totp", $); const notifierU2f = new Notifier(".notification-u2f", $); - function onAuthenticationSuccess(data: any, notifier: Notifier) { - const redirectUrl = QueryParametersRetriever.get(ServerConstants.REDIRECT_QUERY_PARAM); - if (redirectUrl) - window.location.href = redirectUrl; + function onAuthenticationSuccess(serverRedirectUrl: string, notifier: Notifier) { + if (QueryParametersRetriever.get(SharedConstants.REDIRECT_QUERY_PARAM)) + window.location.href = QueryParametersRetriever.get(SharedConstants.REDIRECT_QUERY_PARAM); + else if (serverRedirectUrl) + window.location.href = serverRedirectUrl; else notifier.success(UserMessages.AUTHENTICATION_SUCCEEDED); } - function onSecondFactorTotpSuccess(data: any) { - onAuthenticationSuccess(data, notifierTotp); + function onSecondFactorTotpSuccess(redirectUrl: string) { + onAuthenticationSuccess(redirectUrl, notifierTotp); } function onSecondFactorTotpFailure(err: Error) { notifierTotp.error(UserMessages.AUTHENTICATION_TOTP_FAILED); } - function onU2fAuthenticationSuccess(data: any) { - onAuthenticationSuccess(data, notifierU2f); + function onU2fAuthenticationSuccess(redirectUrl: string) { + onAuthenticationSuccess(redirectUrl, notifierU2f); } function onU2fAuthenticationFailure() { @@ -40,7 +42,7 @@ export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi) } function onTOTPFormSubmitted(): boolean { - const token = $(Constants.TOTP_TOKEN_SELECTOR).val(); + const token = $(ClientConstants.TOTP_TOKEN_SELECTOR).val(); TOTPValidator.validate(token, $) .then(onSecondFactorTotpSuccess) .catch(onSecondFactorTotpFailure); @@ -54,7 +56,7 @@ export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi) } $(window.document).ready(function () { - $(Constants.TOTP_FORM_SELECTOR).on("submit", onTOTPFormSubmitted); - $(Constants.U2F_FORM_SELECTOR).on("submit", onU2FFormSubmitted); + $(ClientConstants.TOTP_FORM_SELECTOR).on("submit", onTOTPFormSubmitted); + $(ClientConstants.U2F_FORM_SELECTOR).on("submit", onU2FFormSubmitted); }); } \ No newline at end of file diff --git a/client/src/lib/u2f-register/u2f-register.ts b/client/src/lib/u2f-register/u2f-register.ts index f66e3c80..7c36094d 100644 --- a/client/src/lib/u2f-register/u2f-register.ts +++ b/client/src/lib/u2f-register/u2f-register.ts @@ -6,6 +6,8 @@ import jslogger = require("js-logger"); import { Notifier } from "../Notifier"; import Endpoints = require("../../../../shared/api"); import UserMessages = require("../../../../shared/UserMessages"); +import { RedirectionMessage } from "../../../../shared/RedirectionMessage"; +import { ErrorMessage } from "../../../../shared/ErrorMessage"; export default function (window: Window, $: JQueryStatic) { const notifier = new Notifier(".notification", $); @@ -17,12 +19,12 @@ export default function (window: Window, $: JQueryStatic) { return new BluebirdPromise(function (resolve, reject) { $.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, registrationData, undefined, "json") - .done(function (body: any) { - if (body && body.error) { - reject(new Error(body.error)); + .done(function (body: RedirectionMessage | ErrorMessage) { + if (body && "error" in body) { + reject(new Error((body as ErrorMessage).error)); return; } - resolve(body.redirection_url); + resolve((body as RedirectionMessage).redirect); }) .fail(function (xhr, status) { reject(); diff --git a/client/test/secondfactor/TOTPValidator.test.ts b/client/test/secondfactor/TOTPValidator.test.ts index 5952eb61..5dd6f15c 100644 --- a/client/test/secondfactor/TOTPValidator.test.ts +++ b/client/test/secondfactor/TOTPValidator.test.ts @@ -7,7 +7,7 @@ import Assert = require("assert"); describe("test TOTPValidator", function () { it("should initiate an identity check successfully", () => { const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields(); + postPromise.done.yields({ redirect: "https://home.test.url" }); postPromise.done.returns(postPromise); const jqueryMock = JQueryMock.JQueryMock(); diff --git a/client/test/secondfactor/U2FValidator.test.ts b/client/test/secondfactor/U2FValidator.test.ts index c57906a4..4c3fdef3 100644 --- a/client/test/secondfactor/U2FValidator.test.ts +++ b/client/test/secondfactor/U2FValidator.test.ts @@ -32,7 +32,7 @@ describe("test U2F validation", function () { getPromise.done.returns(getPromise); const postPromise = JQueryMock.JQueryDeferredMock(); - postPromise.done.yields(); + postPromise.done.yields({ redirect: "https://home.test.url" }); postPromise.done.returns(postPromise); const jqueryMock = JQueryMock.JQueryMock(); diff --git a/config.template.yml b/config.template.yml index a712a672..55bd58ee 100644 --- a/config.template.yml +++ b/config.template.yml @@ -10,6 +10,18 @@ port: 80 # Level of verbosity for logs logs_level: debug +# Default redirection URL +# +# If user tries to authenticate without any referer, Authelia +# does not know where to redirect the user to at the end of the +# authentication process. +# This parameter allows you to specify the default redirection +# URL Authelia will use in such a case. +# +# Note: this parameter is optional. If not provided, user won't +# be redirected upon successful authentication. +default_redirection_url: https://home.test.local:8080/ + # LDAP configuration # # Example: for user john, the DN will be cn=john,ou=users,dc=example,dc=com diff --git a/server/src/lib/AuthenticationSession.ts b/server/src/lib/AuthenticationSession.ts index 93317132..1e91a8d0 100644 --- a/server/src/lib/AuthenticationSession.ts +++ b/server/src/lib/AuthenticationSession.ts @@ -2,24 +2,9 @@ import express = require("express"); import U2f = require("u2f"); -import { ServerVariablesHandler } from "./ServerVariablesHandler"; import BluebirdPromise = require("bluebird"); - -export interface AuthenticationSession { - userid: string; - first_factor: boolean; - second_factor: boolean; - last_activity_datetime: number; - identity_check?: { - challenge: string; - userid: string; - }; - register_request?: U2f.Request; - sign_request?: U2f.Request; - email: string; - groups: string[]; - redirect?: string; -} +import { AuthenticationSession } from "../../types/AuthenticationSession"; +import { IRequestLogger } from "./logging/IRequestLogger"; const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = { first_factor: false, @@ -35,16 +20,13 @@ const INITIAL_AUTHENTICATION_SESSION: AuthenticationSession = { }; export function reset(req: express.Request): void { - const logger = ServerVariablesHandler.getLogger(req.app); - logger.debug(req, "Authentication session %s is being reset.", req.sessionID); req.session.auth = Object.assign({}, INITIAL_AUTHENTICATION_SESSION, {}); // Initialize last activity with current time req.session.auth.last_activity_datetime = new Date().getTime(); } -export function get(req: express.Request): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); +export function get(req: express.Request, logger: IRequestLogger): BluebirdPromise { if (!req.session) { const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can contact it."; logger.error(req, errorMsg); diff --git a/server/src/lib/AuthenticationValidator.ts b/server/src/lib/AuthenticationValidator.ts index 95c6d368..fabda338 100644 --- a/server/src/lib/AuthenticationValidator.ts +++ b/server/src/lib/AuthenticationValidator.ts @@ -2,16 +2,16 @@ import BluebirdPromise = require("bluebird"); import express = require("express"); import objectPath = require("object-path"); - import FirstFactorValidator = require("./FirstFactorValidator"); -import AuthenticationSession = require("./AuthenticationSession"); +import AuthenticationSessionHandler = require("./AuthenticationSession"); +import { IRequestLogger } from "./logging/IRequestLogger"; -export function validate(req: express.Request): BluebirdPromise { - return FirstFactorValidator.validate(req) +export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise { + return FirstFactorValidator.validate(req, logger) .then(function () { - return AuthenticationSession.get(req); + return AuthenticationSessionHandler.get(req, logger); }) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { + .then(function (authSession) { if (!authSession.second_factor) return BluebirdPromise.reject("No second factor variable."); return BluebirdPromise.resolve(); diff --git a/server/src/lib/FirstFactorValidator.ts b/server/src/lib/FirstFactorValidator.ts index 045d63f0..603dea20 100644 --- a/server/src/lib/FirstFactorValidator.ts +++ b/server/src/lib/FirstFactorValidator.ts @@ -3,11 +3,12 @@ import BluebirdPromise = require("bluebird"); import express = require("express"); import objectPath = require("object-path"); import Exceptions = require("./Exceptions"); -import AuthenticationSession = require("./AuthenticationSession"); +import AuthenticationSessionHandler = require("./AuthenticationSession"); +import { IRequestLogger } from "./logging/IRequestLogger"; -export function validate(req: express.Request): BluebirdPromise { - return AuthenticationSession.get(req) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { +export function validate(req: express.Request, logger: IRequestLogger): BluebirdPromise { + return AuthenticationSessionHandler.get(req, logger) + .then(function (authSession) { if (!authSession.userid || !authSession.first_factor) return BluebirdPromise.reject( new Exceptions.FirstFactorValidationError( diff --git a/server/src/lib/IdentityCheckMiddleware.ts b/server/src/lib/IdentityCheckMiddleware.ts index 07c83cb0..17f39b33 100644 --- a/server/src/lib/IdentityCheckMiddleware.ts +++ b/server/src/lib/IdentityCheckMiddleware.ts @@ -10,8 +10,9 @@ import { IUserDataStore } from "./storage/IUserDataStore"; import { Winston } from "../../types/Dependencies"; import express = require("express"); import ErrorReplies = require("./ErrorReplies"); -import { ServerVariablesHandler } from "./ServerVariablesHandler"; -import AuthenticationSession = require("./AuthenticationSession"); +import AuthenticationSessionHandler = require("./AuthenticationSession"); +import { AuthenticationSession } from "../../types/AuthenticationSession"; +import { ServerVariables } from "./ServerVariables"; import Identity = require("../../types/Identity"); import { IdentityValidationDocument } from "./storage/IdentityValidationDocument"; @@ -53,9 +54,9 @@ function consumeToken(token: string, challenge: string, userDataStore: IUserData } export function register(app: express.Application, pre_validation_endpoint: string, - post_validation_endpoint: string, handler: IdentityValidable) { - app.get(pre_validation_endpoint, get_start_validation(handler, post_validation_endpoint)); - app.get(post_validation_endpoint, get_finish_validation(handler)); + post_validation_endpoint: string, handler: IdentityValidable, vars: ServerVariables) { + app.get(pre_validation_endpoint, get_start_validation(handler, post_validation_endpoint, vars)); + app.get(post_validation_endpoint, get_finish_validation(handler, vars)); } function checkIdentityToken(req: express.Request, identityToken: string): BluebirdPromise { @@ -64,27 +65,26 @@ function checkIdentityToken(req: express.Request, identityToken: string): Bluebi return BluebirdPromise.resolve(); } -export function get_finish_validation(handler: IdentityValidable): express.RequestHandler { +export function get_finish_validation(handler: IdentityValidable, + vars: ServerVariables) + : express.RequestHandler { return function (req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - const userDataStore = ServerVariablesHandler.getUserDataStore(req.app); - - let authSession: AuthenticationSession.AuthenticationSession; + let authSession: AuthenticationSession; const identityToken = objectPath.get(req, "query.identity_token"); - logger.debug(req, "Identity token provided is %s", identityToken); + vars.logger.debug(req, "Identity token provided is %s", identityToken); return checkIdentityToken(req, identityToken) .then(function () { return handler.postValidationInit(req); }) .then(function () { - return AuthenticationSession.get(req); + return AuthenticationSessionHandler.get(req, vars.logger); }) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { + .then(function (_authSession) { authSession = _authSession; }) .then(function () { - return consumeToken(identityToken, handler.challenge(), userDataStore); + return consumeToken(identityToken, handler.challenge(), vars.userDataStore); }) .then(function (doc: IdentityValidationDocument) { authSession.identity_check = { @@ -94,17 +94,16 @@ export function get_finish_validation(handler: IdentityValidable): express.Reque handler.postValidationResponse(req, res); return BluebirdPromise.resolve(); }) - .catch(ErrorReplies.replyWithError401(req, res, logger)); + .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); }; } -export function get_start_validation(handler: IdentityValidable, postValidationEndpoint: string) +export function get_start_validation(handler: IdentityValidable, + postValidationEndpoint: string, + vars: ServerVariables) : express.RequestHandler { return function (req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - const notifier = ServerVariablesHandler.getNotifier(req.app); - const userDataStore = ServerVariablesHandler.getUserDataStore(req.app); let identity: Identity.Identity; return handler.preValidationInit(req) @@ -112,25 +111,25 @@ export function get_start_validation(handler: IdentityValidable, postValidationE identity = id; const email = identity.email; const userid = identity.userid; - logger.info(req, "Start identity validation of user \"%s\"", userid); + vars.logger.info(req, "Start identity validation of user \"%s\"", userid); if (!(email && userid)) return BluebirdPromise.reject(new Exceptions.IdentityError( "Missing user id or email address")); - return createAndSaveToken(userid, handler.challenge(), userDataStore); + return createAndSaveToken(userid, handler.challenge(), vars.userDataStore); }) .then(function (token: string) { const host = req.get("Host"); const link_url = util.format("https://%s%s?identity_token=%s", host, postValidationEndpoint, token); - logger.info(req, "Notification sent to user \"%s\"", identity.userid); - return notifier.notify(identity.email, handler.mailSubject(), link_url); + vars.logger.info(req, "Notification sent to user \"%s\"", identity.userid); + return vars.notifier.notify(identity.email, handler.mailSubject(), link_url); }) .then(function () { handler.preValidationResponse(req, res); return BluebirdPromise.resolve(); }) - .catch(ErrorReplies.replyWithError401(req, res, logger)); + .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); }; } diff --git a/server/src/lib/RestApi.ts b/server/src/lib/RestApi.ts index 74a240d0..a8a82dee 100644 --- a/server/src/lib/RestApi.ts +++ b/server/src/lib/RestApi.ts @@ -32,14 +32,14 @@ import Error404Get = require("./routes/error/404/get"); import LoggedIn = require("./routes/loggedin/get"); -import { ServerVariablesHandler } from "./ServerVariablesHandler"; import { ServerVariables } from "./ServerVariables"; +import { IRequestLogger } from "./logging/IRequestLogger"; import Endpoints = require("../../../shared/api"); -function withLog(fn: (req: Express.Request, res: Express.Response) => void) { - return function(req: Express.Request, res: Express.Response) { - const logger = ServerVariablesHandler.getLogger(req.app); +function withHeadersLogged(fn: (req: Express.Request, res: Express.Response) => void, + logger: IRequestLogger) { + return function (req: Express.Request, res: Express.Response) { logger.debug(req, "Headers = %s", JSON.stringify(req.headers)); fn(req, res); }; @@ -47,35 +47,38 @@ function withLog(fn: (req: Express.Request, res: Express.Response) => void) { export class RestApi { static setup(app: Express.Application, vars: ServerVariables): 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)); + app.get(Endpoints.FIRST_FACTOR_GET, withHeadersLogged(FirstFactorGet.default(vars), vars.logger)); + app.get(Endpoints.SECOND_FACTOR_GET, withHeadersLogged(SecondFactorGet.default(vars), vars.logger)); + app.get(Endpoints.LOGOUT_GET, withHeadersLogged(LogoutGet.default, vars.logger)); IdentityCheckMiddleware.register(app, Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, new TOTPRegistrationIdentityHandler()); + Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET, + new TOTPRegistrationIdentityHandler(vars.logger, vars.userDataStore, vars.totpHandler), vars); IdentityCheckMiddleware.register(app, Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, - Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, new U2FRegistrationIdentityHandler()); + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, + new U2FRegistrationIdentityHandler(vars.logger), vars); IdentityCheckMiddleware.register(app, Endpoints.RESET_PASSWORD_IDENTITY_START_GET, - Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET, new ResetPasswordIdentityHandler()); + Endpoints.RESET_PASSWORD_IDENTITY_FINISH_GET, + new ResetPasswordIdentityHandler(vars.logger, vars.ldapEmailsRetriever), vars); - app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, withLog(ResetPasswordRequestPost.default)); - app.post(Endpoints.RESET_PASSWORD_FORM_POST, withLog(ResetPasswordFormPost.default)); + app.get(Endpoints.RESET_PASSWORD_REQUEST_GET, withHeadersLogged(ResetPasswordRequestPost.default, vars.logger)); + app.post(Endpoints.RESET_PASSWORD_FORM_POST, withHeadersLogged(ResetPasswordFormPost.default(vars), vars.logger)); - app.get(Endpoints.VERIFY_GET, withLog(VerifyGet.default(vars))); - app.post(Endpoints.FIRST_FACTOR_POST, withLog(FirstFactorPost.default(vars))); - app.post(Endpoints.SECOND_FACTOR_TOTP_POST, withLog(TOTPSignGet.default(vars))); + app.get(Endpoints.VERIFY_GET, withHeadersLogged(VerifyGet.default(vars), vars.logger)); + app.post(Endpoints.FIRST_FACTOR_POST, withHeadersLogged(FirstFactorPost.default(vars), vars.logger)); + app.post(Endpoints.SECOND_FACTOR_TOTP_POST, withHeadersLogged(TOTPSignGet.default(vars), vars.logger)); - app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, withLog(U2FSignRequestGet.default)); - app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, withLog(U2FSignPost.default)); + app.get(Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, withHeadersLogged(U2FSignRequestGet.default(vars), vars.logger)); + app.post(Endpoints.SECOND_FACTOR_U2F_SIGN_POST, withHeadersLogged(U2FSignPost.default(vars), vars.logger)); - app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, withLog(U2FRegisterRequestGet.default)); - app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, withLog(U2FRegisterPost.default)); + app.get(Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, withHeadersLogged(U2FRegisterRequestGet.default(vars), vars.logger)); + app.post(Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, withHeadersLogged(U2FRegisterPost.default(vars), vars.logger)); - app.get(Endpoints.ERROR_401_GET, withLog(Error401Get.default)); - app.get(Endpoints.ERROR_403_GET, withLog(Error403Get.default)); - app.get(Endpoints.ERROR_404_GET, withLog(Error404Get.default)); - app.get(Endpoints.LOGGED_IN, withLog(LoggedIn.default)); + app.get(Endpoints.ERROR_401_GET, withHeadersLogged(Error401Get.default, vars.logger)); + app.get(Endpoints.ERROR_403_GET, withHeadersLogged(Error403Get.default, vars.logger)); + app.get(Endpoints.ERROR_404_GET, withHeadersLogged(Error404Get.default, vars.logger)); + app.get(Endpoints.LOGGED_IN, withHeadersLogged(LoggedIn.default(vars), vars.logger)); } } diff --git a/server/src/lib/Server.ts b/server/src/lib/Server.ts index 00d7e3d9..94c2f60c 100644 --- a/server/src/lib/Server.ts +++ b/server/src/lib/Server.ts @@ -7,11 +7,11 @@ import { GlobalDependencies } from "../../types/Dependencies"; import { UserDataStore } from "./storage/UserDataStore"; import { ConfigurationParser } from "./configuration/ConfigurationParser"; import { RestApi } from "./RestApi"; -import { ServerVariablesHandler, ServerVariablesInitializer } from "./ServerVariablesHandler"; import { SessionConfigurationBuilder } from "./configuration/SessionConfigurationBuilder"; import { GlobalLogger } from "./logging/GlobalLogger"; import { RequestLogger } from "./logging/RequestLogger"; import { ServerVariables } from "./ServerVariables"; +import { ServerVariablesInitializer } from "./ServerVariablesInitializer"; import * as Express from "express"; import * as BodyParser from "body-parser"; @@ -96,7 +96,6 @@ export default class Server { .then(function (vars: ServerVariables) { that.serverVariables = vars; that.setupExpressApplication(config, app, deps); - ServerVariablesHandler.setup(app, vars); return BluebirdPromise.resolve(); }); } diff --git a/server/src/lib/ServerVariablesHandler.ts b/server/src/lib/ServerVariablesInitializer.ts similarity index 72% rename from server/src/lib/ServerVariablesHandler.ts rename to server/src/lib/ServerVariablesInitializer.ts index c0a84765..37c76330 100644 --- a/server/src/lib/ServerVariablesHandler.ts +++ b/server/src/lib/ServerVariablesInitializer.ts @@ -39,11 +39,6 @@ import { GlobalDependencies } from "../../types/Dependencies"; import { ServerVariables } from "./ServerVariables"; import { AuthenticationMethodCalculator } from "./AuthenticationMethodCalculator"; - -import express = require("express"); - -export const VARIABLES_KEY = "authelia-variables"; - class UserDataStoreFactory { static create(config: Configuration.AppConfiguration): BluebirdPromise { if (config.storage.local) { @@ -104,53 +99,3 @@ export class ServerVariablesInitializer { }); } } - -export class ServerVariablesHandler { - static setup(app: express.Application, variables: ServerVariables): void { - app.set(VARIABLES_KEY, variables); - } - - static getLogger(app: express.Application): IRequestLogger { - return (app.get(VARIABLES_KEY) as ServerVariables).logger; - } - - static getUserDataStore(app: express.Application): IUserDataStore { - return (app.get(VARIABLES_KEY) as ServerVariables).userDataStore; - } - - static getNotifier(app: express.Application): INotifier { - return (app.get(VARIABLES_KEY) as ServerVariables).notifier; - } - - static getLdapAuthenticator(app: express.Application): IAuthenticator { - return (app.get(VARIABLES_KEY) as ServerVariables).ldapAuthenticator; - } - - static getLdapPasswordUpdater(app: express.Application): IPasswordUpdater { - return (app.get(VARIABLES_KEY) as ServerVariables).ldapPasswordUpdater; - } - - static getLdapEmailsRetriever(app: express.Application): IEmailsRetriever { - return (app.get(VARIABLES_KEY) as ServerVariables).ldapEmailsRetriever; - } - - static getConfiguration(app: express.Application): Configuration.AppConfiguration { - return (app.get(VARIABLES_KEY) as ServerVariables).config; - } - - static getAuthenticationRegulator(app: express.Application): IRegulator { - return (app.get(VARIABLES_KEY) as ServerVariables).regulator; - } - - static getAccessController(app: express.Application): IAccessController { - return (app.get(VARIABLES_KEY) as ServerVariables).accessController; - } - - static getTotpHandler(app: express.Application): ITotpHandler { - return (app.get(VARIABLES_KEY) as ServerVariables).totpHandler; - } - - static getU2F(app: express.Application): typeof U2F { - return (app.get(VARIABLES_KEY) as ServerVariables).u2f; - } -} diff --git a/server/src/lib/configuration/Configuration.d.ts b/server/src/lib/configuration/Configuration.d.ts index 7582ea0f..7c45fad4 100644 --- a/server/src/lib/configuration/Configuration.d.ts +++ b/server/src/lib/configuration/Configuration.d.ts @@ -131,6 +131,7 @@ export interface UserConfiguration { authentication_methods?: AuthenticationMethodsConfiguration; access_control?: ACLConfiguration; regulation: RegulationConfiguration; + default_redirection_url?: string; } export interface AppConfiguration { @@ -143,4 +144,5 @@ export interface AppConfiguration { authentication_methods: AuthenticationMethodsConfiguration; access_control?: ACLConfiguration; regulation: RegulationConfiguration; + default_redirection_url?: string; } diff --git a/server/src/lib/configuration/ConfigurationParser.ts b/server/src/lib/configuration/ConfigurationParser.ts index 186ce5da..02cf8077 100644 --- a/server/src/lib/configuration/ConfigurationParser.ts +++ b/server/src/lib/configuration/ConfigurationParser.ts @@ -82,7 +82,8 @@ function adaptFromUserConfiguration(userConfiguration: UserConfiguration) notifier: ObjectPath.get(userConfiguration, "notifier"), access_control: ACLAdapter.adapt(userConfiguration.access_control), regulation: userConfiguration.regulation, - authentication_methods: authenticationMethods + authentication_methods: authenticationMethods, + default_redirection_url: userConfiguration.default_redirection_url }; } diff --git a/server/src/lib/routes/FirstFactorBlocker.ts b/server/src/lib/routes/FirstFactorBlocker.ts index 41b88240..0fdbfdd3 100644 --- a/server/src/lib/routes/FirstFactorBlocker.ts +++ b/server/src/lib/routes/FirstFactorBlocker.ts @@ -5,19 +5,17 @@ import FirstFactorValidator = require("../FirstFactorValidator"); import Exceptions = require("../Exceptions"); import ErrorReplies = require("../ErrorReplies"); import objectPath = require("object-path"); -import { ServerVariablesHandler } from "../ServerVariablesHandler"; import AuthenticationSession = require("../AuthenticationSession"); import UserMessages = require("../../../../shared/UserMessages"); +import { IRequestLogger } from "../logging/IRequestLogger"; type Handler = (req: express.Request, res: express.Response) => BluebirdPromise; -export default function (callback: Handler): Handler { +export default function (callback: Handler, logger: IRequestLogger): Handler { return function (req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - - return AuthenticationSession.get(req) + return AuthenticationSession.get(req, logger) .then(function (authSession) { - return FirstFactorValidator.validate(req); + return FirstFactorValidator.validate(req, logger); }) .then(function () { return callback(req, res); diff --git a/server/src/lib/routes/firstfactor/get.ts b/server/src/lib/routes/firstfactor/get.ts index 22972ae4..700ea932 100644 --- a/server/src/lib/routes/firstfactor/get.ts +++ b/server/src/lib/routes/firstfactor/get.ts @@ -4,11 +4,11 @@ import objectPath = require("object-path"); import winston = require("winston"); import Endpoints = require("../../../../../shared/api"); import AuthenticationValidator = require("../../AuthenticationValidator"); -import { ServerVariablesHandler } from "../../ServerVariablesHandler"; import BluebirdPromise = require("bluebird"); import AuthenticationSession = require("../../AuthenticationSession"); import Constants = require("../../../../../shared/constants"); import Util = require("util"); +import { ServerVariables } from "../../ServerVariables"; function getRedirectParam(req: express.Request) { return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined" @@ -40,18 +40,20 @@ function renderFirstFactor(res: express.Response) { }); } -export default function (req: express.Request, res: express.Response): BluebirdPromise { - return AuthenticationSession.get(req) - .then(function (authSession) { - if (authSession.first_factor) { - if (authSession.second_factor) - redirectToService(req, res); - else - redirectToSecondFactorPage(req, res); - return BluebirdPromise.resolve(); - } +export default function (vars: ServerVariables) { + return function (req: express.Request, res: express.Response): BluebirdPromise { + return AuthenticationSession.get(req, vars.logger) + .then(function (authSession) { + if (authSession.first_factor) { + if (authSession.second_factor) + redirectToService(req, res); + else + redirectToSecondFactorPage(req, res); + return BluebirdPromise.resolve(); + } - renderFirstFactor(res); - return BluebirdPromise.resolve(); - }); + renderFirstFactor(res); + return BluebirdPromise.resolve(); + }); + }; } \ No newline at end of file diff --git a/server/src/lib/routes/firstfactor/post.ts b/server/src/lib/routes/firstfactor/post.ts index 1aacb50c..9cbf45c9 100644 --- a/server/src/lib/routes/firstfactor/post.ts +++ b/server/src/lib/routes/firstfactor/post.ts @@ -8,20 +8,20 @@ import { Regulator } from "../../regulation/Regulator"; import { GroupsAndEmails } from "../../ldap/IClient"; import Endpoint = require("../../../../../shared/api"); import ErrorReplies = require("../../ErrorReplies"); -import { ServerVariablesHandler } from "../../ServerVariablesHandler"; -import AuthenticationSession = require("../../AuthenticationSession"); +import AuthenticationSessionHandler = require("../../AuthenticationSession"); import Constants = require("../../../../../shared/constants"); import { DomainExtractor } from "../../utils/DomainExtractor"; import UserMessages = require("../../../../../shared/UserMessages"); import { AuthenticationMethodCalculator } from "../../AuthenticationMethodCalculator"; import { ServerVariables } from "../../ServerVariables"; +import { AuthenticationSession } from "../../../../types/AuthenticationSession"; export default function (vars: ServerVariables) { return function (req: express.Request, res: express.Response) : BluebirdPromise { const username: string = req.body.username; const password: string = req.body.password; - let authSession: AuthenticationSession.AuthenticationSession; + let authSession: AuthenticationSession; return BluebirdPromise.resolve() .then(function () { @@ -29,9 +29,9 @@ export default function (vars: ServerVariables) { return BluebirdPromise.reject(new Error("No username or password.")); } vars.logger.info(req, "Starting authentication of user \"%s\"", username); - return AuthenticationSession.get(req); + return AuthenticationSessionHandler.get(req, vars.logger); }) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { + .then(function (_authSession) { authSession = _authSession; return vars.regulator.regulate(username); }) diff --git a/server/src/lib/routes/loggedin/get.ts b/server/src/lib/routes/loggedin/get.ts index 0585d660..432fa2e7 100644 --- a/server/src/lib/routes/loggedin/get.ts +++ b/server/src/lib/routes/loggedin/get.ts @@ -2,16 +2,20 @@ import Express = require("express"); import Endpoints = require("../../../../../shared/api"); import FirstFactorBlocker from "../FirstFactorBlocker"; import BluebirdPromise = require("bluebird"); -import AuthenticationSession = require("../../AuthenticationSession"); +import AuthenticationSessionHandler = require("../../AuthenticationSession"); +import { ServerVariables } from "../../ServerVariables"; -export default FirstFactorBlocker(handler); - -function handler(req: Express.Request, res: Express.Response): BluebirdPromise { - return AuthenticationSession.get(req) - .then(function (authSession) { - res.render("already-logged-in", { - logout_endpoint: Endpoints.LOGOUT_GET, - username: authSession.userid +export default function (vars: ServerVariables) { + function handler(req: Express.Request, res: Express.Response): BluebirdPromise { + return AuthenticationSessionHandler.get(req, vars.logger) + .then(function (authSession) { + res.render("already-logged-in", { + logout_endpoint: Endpoints.LOGOUT_GET, + username: authSession.userid, + redirection_url: vars.config.default_redirection_url + }); }); - }); + } + + return FirstFactorBlocker(handler, vars.logger); } diff --git a/server/src/lib/routes/password-reset/form/post.ts b/server/src/lib/routes/password-reset/form/post.ts index 15dba4be..7af57fce 100644 --- a/server/src/lib/routes/password-reset/form/post.ts +++ b/server/src/lib/routes/password-reset/form/post.ts @@ -3,38 +3,40 @@ import express = require("express"); import BluebirdPromise = require("bluebird"); import objectPath = require("object-path"); import exceptions = require("../../../Exceptions"); -import { ServerVariablesHandler } from "../../../ServerVariablesHandler"; -import AuthenticationSession = require("../../../AuthenticationSession"); +import AuthenticationSessionHandler = require("../../../AuthenticationSession"); +import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; import ErrorReplies = require("../../../ErrorReplies"); import UserMessages = require("../../../../../../shared/UserMessages"); +import { ServerVariables } from "../../../ServerVariables"; import Constants = require("./../constants"); -export default function (req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - const ldapPasswordUpdater = ServerVariablesHandler.getLdapPasswordUpdater(req.app); - let authSession: AuthenticationSession.AuthenticationSession; - const newPassword = objectPath.get(req, "body.password"); +export default function (vars: ServerVariables) { + return function (req: express.Request, res: express.Response): BluebirdPromise { + let authSession: AuthenticationSession; + const newPassword = objectPath.get(req, "body.password"); - return AuthenticationSession.get(req) - .then(function (_authSession) { - authSession = _authSession; - logger.info(req, "User %s wants to reset his/her password.", - authSession.identity_check.userid); - logger.debug(req, "Challenge %s", authSession.identity_check.challenge); + return AuthenticationSessionHandler.get(req, vars.logger) + .then(function (_authSession) { + authSession = _authSession; + vars.logger.info(req, "User %s wants to reset his/her password.", + authSession.identity_check.userid); + vars.logger.debug(req, "Challenge %s", authSession.identity_check.challenge); - if (authSession.identity_check.challenge != Constants.CHALLENGE) { - return BluebirdPromise.reject(new Error("Bad challenge.")); - } - return ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword); - }) - .then(function () { - logger.info(req, "Password reset for user '%s'", - authSession.identity_check.userid); - AuthenticationSession.reset(req); - res.status(204); - res.send(); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, logger, UserMessages.RESET_PASSWORD_FAILED)); + if (authSession.identity_check.challenge != Constants.CHALLENGE) { + return BluebirdPromise.reject(new Error("Bad challenge.")); + } + return vars.ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword); + }) + .then(function () { + vars.logger.info(req, "Password reset for user '%s'", + authSession.identity_check.userid); + AuthenticationSessionHandler.reset(req); + res.status(204); + res.send(); + return BluebirdPromise.resolve(); + }) + .catch(ErrorReplies.replyWithError200(req, res, vars.logger, + UserMessages.RESET_PASSWORD_FAILED)); + }; } diff --git a/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts b/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts index 42647bfd..66458fb1 100644 --- a/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts +++ b/server/src/lib/routes/password-reset/identity/PasswordResetHandler.ts @@ -7,49 +7,55 @@ import { Identity } from "../../../../../types/Identity"; import { IdentityValidable } from "../../../IdentityCheckMiddleware"; import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate"; import Constants = require("../constants"); -import { Winston } from "winston"; -import { ServerVariablesHandler } from "../../../ServerVariablesHandler"; +import { IRequestLogger } from "../../../logging/IRequestLogger"; +import { IEmailsRetriever } from "../../../ldap/IEmailsRetriever"; export const TEMPLATE_NAME = "password-reset-form"; export default class PasswordResetHandler implements IdentityValidable { - challenge(): string { - return Constants.CHALLENGE; - } + private logger: IRequestLogger; + private emailsRetriever: IEmailsRetriever; - preValidationInit(req: express.Request): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - const userid: string = objectPath.get(req, "query.userid"); + constructor(logger: IRequestLogger, emailsRetriever: IEmailsRetriever) { + this.logger = logger; + this.emailsRetriever = emailsRetriever; + } - logger.debug(req, "User '%s' requested a password reset", userid); - if (!userid) - return BluebirdPromise.reject(new exceptions.AccessDeniedError("No user id provided")); + challenge(): string { + return Constants.CHALLENGE; + } - const emailsRetriever = ServerVariablesHandler.getLdapEmailsRetriever(req.app); - return emailsRetriever.retrieve(userid) - .then(function (emails: string[]) { - if (!emails && emails.length <= 0) throw new Error("No email found"); - const identity = { - email: emails[0], - userid: userid - }; - return BluebirdPromise.resolve(identity); - }); - } + preValidationInit(req: express.Request): BluebirdPromise { + const userid: string = objectPath.get(req, "query.userid"); - preValidationResponse(req: express.Request, res: express.Response) { - res.render(PRE_VALIDATION_TEMPLATE); - } + this.logger.debug(req, "User '%s' requested a password reset", userid); + if (!userid) + return BluebirdPromise.reject(new exceptions.AccessDeniedError("No user id provided")); - postValidationInit(req: express.Request) { - return BluebirdPromise.resolve(); - } + return this.emailsRetriever.retrieve(userid) + .then(function (emails: string[]) { + if (!emails && emails.length <= 0) throw new Error("No email found"); + const identity = { + email: emails[0], + userid: userid + }; + return BluebirdPromise.resolve(identity); + }); + } - postValidationResponse(req: express.Request, res: express.Response) { - res.render(TEMPLATE_NAME); - } + preValidationResponse(req: express.Request, res: express.Response) { + res.render(PRE_VALIDATION_TEMPLATE); + } - mailSubject(): string { - return "Reset your password"; - } + postValidationInit(req: express.Request) { + return BluebirdPromise.resolve(); + } + + postValidationResponse(req: express.Request, res: express.Response) { + res.render(TEMPLATE_NAME); + } + + mailSubject(): string { + return "Reset your password"; + } } \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/get.ts b/server/src/lib/routes/secondfactor/get.ts index b3cc003b..ac657266 100644 --- a/server/src/lib/routes/secondfactor/get.ts +++ b/server/src/lib/routes/secondfactor/get.ts @@ -3,26 +3,28 @@ import Express = require("express"); import Endpoints = require("../../../../../shared/api"); import FirstFactorBlocker = require("../FirstFactorBlocker"); import BluebirdPromise = require("bluebird"); -import { ServerVariablesHandler } from "../../ServerVariablesHandler"; -import AuthenticationSession = require("../../AuthenticationSession"); +import AuthenticationSessionHandler = require("../../AuthenticationSession"); +import { ServerVariables } from "../../ServerVariables"; const TEMPLATE_NAME = "secondfactor"; -export default FirstFactorBlocker.default(handler); +export default function (vars: ServerVariables) { + function handler(req: Express.Request, res: Express.Response): BluebirdPromise { + return AuthenticationSessionHandler.get(req, vars.logger) + .then(function (authSession) { + if (authSession.first_factor && authSession.second_factor) { + res.redirect(Endpoints.LOGGED_IN); + return BluebirdPromise.resolve(); + } -function handler(req: Express.Request, res: Express.Response): BluebirdPromise { - return AuthenticationSession.get(req) - .then(function (authSession) { - if (authSession.first_factor && authSession.second_factor) { - res.redirect(Endpoints.LOGGED_IN); - return BluebirdPromise.resolve(); - } - - res.render(TEMPLATE_NAME, { - username: authSession.userid, - totp_identity_start_endpoint: Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, - u2f_identity_start_endpoint: Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET - }); - return BluebirdPromise.resolve(); + res.render(TEMPLATE_NAME, { + username: authSession.userid, + totp_identity_start_endpoint: Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET, + u2f_identity_start_endpoint: Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET }); + return BluebirdPromise.resolve(); + }); + } + + return FirstFactorBlocker.default(handler, vars.logger); } \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/redirect.ts b/server/src/lib/routes/secondfactor/redirect.ts index cbfba27b..adc1f5c4 100644 --- a/server/src/lib/routes/secondfactor/redirect.ts +++ b/server/src/lib/routes/secondfactor/redirect.ts @@ -3,22 +3,29 @@ import express = require("express"); import objectPath = require("object-path"); import winston = require("winston"); import Endpoints = require("../../../../../shared/api"); -import { ServerVariablesHandler } from "../../ServerVariablesHandler"; +import { ServerVariables } from "../../ServerVariables"; import AuthenticationSession = require("../../AuthenticationSession"); import BluebirdPromise = require("bluebird"); import ErrorReplies = require("../../ErrorReplies"); import UserMessages = require("../../../../../shared/UserMessages"); +import { RedirectionMessage } from "../../../../../shared/RedirectionMessage"; +import Constants = require("../../../../../shared/constants"); -export default function (req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - 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(); - }) - .catch(ErrorReplies.replyWithError200(req, res, logger, - UserMessages.OPERATION_FAILED)); +export default function (vars: ServerVariables) { + return function (req: express.Request, res: express.Response): BluebirdPromise { + return AuthenticationSession.get(req, vars.logger) + .then(function (authSession) { + let redirectUrl: string; + if (vars.config.default_redirection_url) { + redirectUrl = vars.config.default_redirection_url; + } + vars.logger.debug(req, "Request redirection to \"%s\".", redirectUrl); + res.json({ + redirect: redirectUrl + } as RedirectionMessage); + return BluebirdPromise.resolve(); + }) + .catch(ErrorReplies.replyWithError200(req, res, vars.logger, + UserMessages.OPERATION_FAILED)); + }; } \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts b/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts index c63cbae7..fa37c154 100644 --- a/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts +++ b/server/src/lib/routes/secondfactor/totp/identity/RegistrationHandler.ts @@ -9,21 +9,34 @@ import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationT import Constants = require("../constants"); import Endpoints = require("../../../../../../../shared/api"); import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariablesHandler } from "../../../../ServerVariablesHandler"; import AuthenticationSession = require("../../../../AuthenticationSession"); import UserMessages = require("../../../../../../../shared/UserMessages"); - import FirstFactorValidator = require("../../../../FirstFactorValidator"); +import { IRequestLogger } from "../../../../logging/IRequestLogger"; +import { IUserDataStore } from "../../../../storage/IUserDataStore"; +import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler"; export default class RegistrationHandler implements IdentityValidable { + private logger: IRequestLogger; + private userDataStore: IUserDataStore; + private totp: ITotpHandler; + + constructor(logger: IRequestLogger, + userDataStore: IUserDataStore, + totp: ITotpHandler) { + this.logger = logger; + this.userDataStore = userDataStore; + this.totp = totp; + } + challenge(): string { return Constants.CHALLENGE; } private retrieveIdentity(req: express.Request): BluebirdPromise { - return AuthenticationSession.get(req) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { + return AuthenticationSession.get(req, this.logger) + .then(function (authSession) { const userid = authSession.userid; const email = authSession.email; @@ -41,7 +54,7 @@ export default class RegistrationHandler implements IdentityValidable { preValidationInit(req: express.Request): BluebirdPromise { const that = this; - return FirstFactorValidator.validate(req) + return FirstFactorValidator.validate(req, this.logger) .then(function () { return that.retrieveIdentity(req); }); @@ -52,26 +65,22 @@ export default class RegistrationHandler implements IdentityValidable { } postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req); + return FirstFactorValidator.validate(req, this.logger); } postValidationResponse(req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - return AuthenticationSession.get(req) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { + const that = this; + return AuthenticationSession.get(req, this.logger) + .then(function (authSession) { const userid = authSession.identity_check.userid; const challenge = authSession.identity_check.challenge; if (challenge != Constants.CHALLENGE || !userid) { return BluebirdPromise.reject(new Error("Bad challenge.")); } - - const userDataStore = ServerVariablesHandler.getUserDataStore(req.app); - const totpHandler = ServerVariablesHandler.getTotpHandler(req.app); - const secret = totpHandler.generate(); - - logger.debug(req, "Save the TOTP secret in DB"); - return userDataStore.saveTOTPSecret(userid, secret) + const secret = that.totp.generate(); + that.logger.debug(req, "Save the TOTP secret in DB"); + return that.userDataStore.saveTOTPSecret(userid, secret) .then(function () { AuthenticationSession.reset(req); @@ -82,7 +91,7 @@ export default class RegistrationHandler implements IdentityValidable { }); }); }) - .catch(ErrorReplies.replyWithError200(req, res, logger, UserMessages.OPERATION_FAILED)); + .catch(ErrorReplies.replyWithError200(req, res, that.logger, UserMessages.OPERATION_FAILED)); } mailSubject(): string { diff --git a/server/src/lib/routes/secondfactor/totp/sign/post.ts b/server/src/lib/routes/secondfactor/totp/sign/post.ts index b5091579..d3f9a28d 100644 --- a/server/src/lib/routes/secondfactor/totp/sign/post.ts +++ b/server/src/lib/routes/secondfactor/totp/sign/post.ts @@ -8,8 +8,8 @@ import FirstFactorBlocker from "../../../FirstFactorBlocker"; import Endpoints = require("../../../../../../../shared/api"); import redirect from "../../redirect"; import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariablesHandler } from "./../../../../ServerVariablesHandler"; -import AuthenticationSession = require("../../../../AuthenticationSession"); +import AuthenticationSessionHandler = require("../../../../AuthenticationSession"); +import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; import UserMessages = require("../../../../../../../shared/UserMessages"); import { ServerVariables } from "../../../../ServerVariables"; @@ -17,11 +17,11 @@ const UNAUTHORIZED_MESSAGE = "Unauthorized access"; export default function (vars: ServerVariables) { function handler(req: express.Request, res: express.Response): BluebirdPromise { - let authSession: AuthenticationSession.AuthenticationSession; + let authSession: AuthenticationSession; const token = req.body.token; - return AuthenticationSession.get(req) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { + return AuthenticationSessionHandler.get(req, vars.logger) + .then(function (_authSession) { authSession = _authSession; vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid); return vars.userDataStore.retrieveTOTPSecret(authSession.userid); @@ -32,11 +32,11 @@ export default function (vars: ServerVariables) { vars.logger.debug(req, "TOTP validation succeeded."); authSession.second_factor = true; - redirect(req, res); + redirect(vars)(req, res); return BluebirdPromise.resolve(); }) .catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)); } - return FirstFactorBlocker(handler); + return FirstFactorBlocker(handler, vars.logger); } diff --git a/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts b/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts index 002904e7..bd41ecae 100644 --- a/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts +++ b/server/src/lib/routes/secondfactor/u2f/identity/RegistrationHandler.ts @@ -8,6 +8,7 @@ import { Identity } from "../../../../../../types/Identity"; import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate"; import FirstFactorValidator = require("../../../../FirstFactorValidator"); import AuthenticationSession = require("../../../../AuthenticationSession"); +import { IRequestLogger } from "../../../../logging/IRequestLogger"; const CHALLENGE = "u2f-register"; const MAIL_SUBJECT = "Register your U2F device"; @@ -16,13 +17,19 @@ const POST_VALIDATION_TEMPLATE_NAME = "u2f-register"; export default class RegistrationHandler implements IdentityValidable { + private logger: IRequestLogger; + + constructor(logger: IRequestLogger) { + this.logger = logger; + } + challenge(): string { return CHALLENGE; } private retrieveIdentity(req: express.Request): BluebirdPromise { - return AuthenticationSession.get(req) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { + return AuthenticationSession.get(req, this.logger) + .then(function (authSession) { const userid = authSession.userid; const email = authSession.email; @@ -40,7 +47,7 @@ export default class RegistrationHandler implements IdentityValidable { preValidationInit(req: express.Request): BluebirdPromise { const that = this; - return FirstFactorValidator.validate(req) + return FirstFactorValidator.validate(req, this.logger) .then(function () { return that.retrieveIdentity(req); }); @@ -51,7 +58,7 @@ export default class RegistrationHandler implements IdentityValidable { } postValidationInit(req: express.Request) { - return FirstFactorValidator.validate(req); + return FirstFactorValidator.validate(req, this.logger); } postValidationResponse(req: express.Request, res: express.Response) { diff --git a/server/src/lib/routes/secondfactor/u2f/register/post.ts b/server/src/lib/routes/secondfactor/u2f/register/post.ts index 6d7e9d4b..5649bb40 100644 --- a/server/src/lib/routes/secondfactor/u2f/register/post.ts +++ b/server/src/lib/routes/secondfactor/u2f/register/post.ts @@ -10,60 +10,59 @@ import { U2FRegistration } from "../../../../../../types/U2FRegistration"; import FirstFactorBlocker from "../../../FirstFactorBlocker"; import redirect from "../../redirect"; import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariablesHandler } from "../../../../ServerVariablesHandler"; -import AuthenticationSession = require("../../../../AuthenticationSession"); +import { ServerVariables } from "../../../../ServerVariables"; +import AuthenticationSessionHandler = require("../../../../AuthenticationSession"); import UserMessages = require("../../../../../../../shared/UserMessages"); +import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -export default FirstFactorBlocker(handler); +export default function (vars: ServerVariables) { + function handler(req: express.Request, res: express.Response): BluebirdPromise { + let authSession: AuthenticationSession; + const appid = u2f_common.extract_app_id(req); + const registrationResponse: U2f.RegistrationData = req.body; + return AuthenticationSessionHandler.get(req, vars.logger) + .then(function (_authSession) { + authSession = _authSession; + const registrationRequest = authSession.register_request; -function handler(req: express.Request, res: express.Response): BluebirdPromise { - 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; + if (!registrationRequest) { + return BluebirdPromise.reject(new Error("No registration request")); + } - return AuthenticationSession.get(req) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { - authSession = _authSession; - const registrationRequest = authSession.register_request; + if (!authSession.identity_check + || authSession.identity_check.challenge != "u2f-register") { + return BluebirdPromise.reject(new Error("Bad challenge for registration request")); + } - if (!registrationRequest) { - return BluebirdPromise.reject(new Error("No registration request")); - } + vars.logger.info(req, "Finishing registration"); + vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); + vars.logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse)); - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - return BluebirdPromise.reject(new Error("Bad challenge for registration request")); - } + return BluebirdPromise.resolve(vars.u2f.checkRegistration(registrationRequest, registrationResponse)); + }) + .then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise { + if (objectPath.has(u2fResult, "errorCode")) + return BluebirdPromise.reject(new Error("Error while registering.")); - logger.info(req, "Finishing registration"); - logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - logger.debug(req, "RegistrationResponse = %s", JSON.stringify(registrationResponse)); + const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult; + vars.logger.info(req, "Store registration and reply"); + vars.logger.debug(req, "RegistrationResult = %s", JSON.stringify(registrationResult)); + const registration: U2FRegistration = { + keyHandle: registrationResult.keyHandle, + publicKey: registrationResult.publicKey + }; + return vars.userDataStore.saveU2FRegistration(authSession.userid, appid, registration); + }) + .then(function () { + authSession.identity_check = undefined; + redirect(vars)(req, res); + return BluebirdPromise.resolve(); + }) + .catch(ErrorReplies.replyWithError200(req, res, vars.logger, + UserMessages.OPERATION_FAILED)); + } - return BluebirdPromise.resolve(u2f.checkRegistration(registrationRequest, registrationResponse)); - }) - .then(function (u2fResult: U2f.RegistrationResult | U2f.Error): BluebirdPromise { - if (objectPath.has(u2fResult, "errorCode")) - return BluebirdPromise.reject(new Error("Error while registering.")); - - const registrationResult: U2f.RegistrationResult = u2fResult as U2f.RegistrationResult; - logger.info(req, "Store registration and reply"); - logger.debug(req, "RegistrationResult = %s", JSON.stringify(registrationResult)); - const registration: U2FRegistration = { - keyHandle: registrationResult.keyHandle, - publicKey: registrationResult.publicKey - }; - return userDataStore.saveU2FRegistration(authSession.userid, appid, registration); - }) - .then(function () { - authSession.identity_check = undefined; - redirect(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, logger, - UserMessages.OPERATION_FAILED)); + return FirstFactorBlocker(handler, vars.logger); } diff --git a/server/src/lib/routes/secondfactor/u2f/register_request/get.ts b/server/src/lib/routes/secondfactor/u2f/register_request/get.ts index 4c9a148c..87487ee1 100644 --- a/server/src/lib/routes/secondfactor/u2f/register_request/get.ts +++ b/server/src/lib/routes/secondfactor/u2f/register_request/get.ts @@ -8,40 +8,40 @@ import express = require("express"); import U2f = require("u2f"); import FirstFactorBlocker from "../../../FirstFactorBlocker"; import ErrorReplies = require("../../../../ErrorReplies"); -import {  ServerVariablesHandler } from "../../../../ServerVariablesHandler"; -import AuthenticationSession = require("../../../../AuthenticationSession"); +import AuthenticationSessionHandler = require("../../../../AuthenticationSession"); +import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; import UserMessages = require("../../../../../../../shared/UserMessages"); +import { ServerVariables } from "../../../../ServerVariables"; -export default FirstFactorBlocker(handler); +export default function (vars: ServerVariables) { + function handler(req: express.Request, res: express.Response): BluebirdPromise { + let authSession: AuthenticationSession; + const appid: string = u2f_common.extract_app_id(req); -function handler(req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - let authSession: AuthenticationSession.AuthenticationSession; + return AuthenticationSessionHandler.get(req, vars.logger) + .then(function (_authSession) { + authSession = _authSession; - 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 BluebirdPromise.reject(new Error("Bad challenge.")); + } - if (!authSession.identity_check - || authSession.identity_check.challenge != "u2f-register") { - res.status(403); - res.send(); - return BluebirdPromise.reject(new Error("Bad challenge.")); - } + vars.logger.info(req, "Starting registration for appId '%s'", appid); - const u2f = ServerVariablesHandler.getU2F(req.app); - const appid: string = u2f_common.extract_app_id(req); + return BluebirdPromise.resolve(vars.u2f.request(appid)); + }) + .then(function (registrationRequest: U2f.Request) { + vars.logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); + authSession.register_request = registrationRequest; + res.json(registrationRequest); + return BluebirdPromise.resolve(); + }) + .catch(ErrorReplies.replyWithError200(req, res, vars.logger, + UserMessages.OPERATION_FAILED)); + } - logger.info(req, "Starting registration for appId '%s'", appid); - - return BluebirdPromise.resolve(u2f.request(appid)); - }) - .then(function (registrationRequest: U2f.Request) { - logger.debug(req, "RegistrationRequest = %s", JSON.stringify(registrationRequest)); - authSession.register_request = registrationRequest; - res.json(registrationRequest); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, logger, - UserMessages.OPERATION_FAILED)); + return FirstFactorBlocker(handler, vars.logger); } \ No newline at end of file diff --git a/server/src/lib/routes/secondfactor/u2f/sign/post.ts b/server/src/lib/routes/secondfactor/u2f/sign/post.ts index dfa9c38d..fe3ade2d 100644 --- a/server/src/lib/routes/secondfactor/u2f/sign/post.ts +++ b/server/src/lib/routes/secondfactor/u2f/sign/post.ts @@ -11,47 +11,45 @@ import exceptions = require("../../../../Exceptions"); import FirstFactorBlocker from "../../../FirstFactorBlocker"; import redirect from "../../redirect"; import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariablesHandler } from "../../../../ServerVariablesHandler"; -import AuthenticationSession = require("../../../../AuthenticationSession"); +import { ServerVariables } from "../../../../ServerVariables"; +import AuthenticationSessionHandler = require("../../../../AuthenticationSession"); import UserMessages = require("../../../../../../../shared/UserMessages"); +import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -export default FirstFactorBlocker(handler); +export default function (vars: ServerVariables) { + function handler(req: express.Request, res: express.Response): BluebirdPromise { + let authSession: AuthenticationSession; + const appId = u2f_common.extract_app_id(req); -export function handler(req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - const userDataStore = ServerVariablesHandler.getUserDataStore(req.app); - let authSession: AuthenticationSession.AuthenticationSession; + return AuthenticationSessionHandler.get(req, vars.logger) + .then(function (_authSession) { + authSession = _authSession; + if (!authSession.sign_request) { + const err = new Error("No sign request"); + ErrorReplies.replyWithError401(req, res, vars.logger)(err); + return BluebirdPromise.reject(err); + } + const userid = authSession.userid; + return vars.userDataStore.retrieveU2FRegistration(userid, appId); + }) + .then(function (doc: U2FRegistrationDocument): BluebirdPromise { + const signRequest = authSession.sign_request; + const signData: U2f.SignatureData = req.body; + vars.logger.info(req, "Finish authentication"); + return BluebirdPromise.resolve(vars.u2f.checkSignature(signRequest, signData, doc.registration.publicKey)); + }) + .then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise { + if (objectPath.has(result, "errorCode")) + return BluebirdPromise.reject(new Error("Error while signing")); + vars.logger.info(req, "Successful authentication"); + authSession.second_factor = true; + redirect(vars)(req, res); + return BluebirdPromise.resolve(); + }) + .catch(ErrorReplies.replyWithError200(req, res, vars.logger, + UserMessages.OPERATION_FAILED)); + } - return AuthenticationSession.get(req) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { - authSession = _authSession; - if (!authSession.sign_request) { - const err = new Error("No sign request"); - ErrorReplies.replyWithError401(req, res, logger)(err); - return BluebirdPromise.reject(err); - } - - const userid = authSession.userid; - const appid = u2f_common.extract_app_id(req); - return userDataStore.retrieveU2FRegistration(userid, appid); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - const appId = u2f_common.extract_app_id(req); - const u2f = ServerVariablesHandler.getU2F(req.app); - const signRequest = authSession.sign_request; - const signData: U2f.SignatureData = req.body; - logger.info(req, "Finish authentication"); - return BluebirdPromise.resolve(u2f.checkSignature(signRequest, signData, doc.registration.publicKey)); - }) - .then(function (result: U2f.SignatureResult | U2f.Error): BluebirdPromise { - if (objectPath.has(result, "errorCode")) - return BluebirdPromise.reject(new Error("Error while signing")); - logger.info(req, "Successful authentication"); - authSession.second_factor = true; - redirect(req, res); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, logger, - UserMessages.OPERATION_FAILED)); + return FirstFactorBlocker(handler, vars.logger); } diff --git a/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts b/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts index bdd40f44..897af4ab 100644 --- a/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts +++ b/server/src/lib/routes/secondfactor/u2f/sign_request/get.ts @@ -11,47 +11,46 @@ import exceptions = require("../../../../Exceptions"); import { SignMessage } from "../../../../../../../shared/SignMessage"; import FirstFactorBlocker from "../../../FirstFactorBlocker"; import ErrorReplies = require("../../../../ErrorReplies"); -import { ServerVariablesHandler } from "../../../../ServerVariablesHandler"; -import AuthenticationSession = require("../../../../AuthenticationSession"); +import AuthenticationSessionHandler = require("../../../../AuthenticationSession"); import UserMessages = require("../../../../../../../shared/UserMessages"); +import { ServerVariables } from "../../../../ServerVariables"; +import { AuthenticationSession } from "../../../../../../types/AuthenticationSession"; -export default FirstFactorBlocker(handler); +export default function (vars: ServerVariables) { + function handler(req: express.Request, res: express.Response): BluebirdPromise { + let authSession: AuthenticationSession; + const appId = u2f_common.extract_app_id(req); -export function handler(req: express.Request, res: express.Response): BluebirdPromise { - const logger = ServerVariablesHandler.getLogger(req.app); - const userDataStore = ServerVariablesHandler.getUserDataStore(req.app); - let authSession: AuthenticationSession.AuthenticationSession; - const appId = u2f_common.extract_app_id(req); + return AuthenticationSessionHandler.get(req, vars.logger) + .then(function (_authSession) { + authSession = _authSession; + return vars.userDataStore.retrieveU2FRegistration(authSession.userid, appId); + }) + .then(function (doc: U2FRegistrationDocument): BluebirdPromise { + if (!doc) + return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration found")); - return AuthenticationSession.get(req) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { - authSession = _authSession; - return userDataStore.retrieveU2FRegistration(authSession.userid, appId); - }) - .then(function (doc: U2FRegistrationDocument): BluebirdPromise { - if (!doc) - return BluebirdPromise.reject(new exceptions.AccessDeniedError("No U2F registration found")); + const appId: string = u2f_common.extract_app_id(req); + vars.logger.info(req, "Start authentication of app '%s'", appId); + vars.logger.debug(req, "AppId = %s, keyHandle = %s", appId, JSON.stringify(doc.registration.keyHandle)); - const u2f = ServerVariablesHandler.getU2F(req.app); - const appId: string = u2f_common.extract_app_id(req); - logger.info(req, "Start authentication of app '%s'", appId); - logger.debug(req, "AppId = %s, keyHandle = %s", appId, JSON.stringify(doc.registration.keyHandle)); + const request = vars.u2f.request(appId, doc.registration.keyHandle); + const authenticationMessage: SignMessage = { + request: request, + keyHandle: doc.registration.keyHandle + }; + return BluebirdPromise.resolve(authenticationMessage); + }) + .then(function (authenticationMessage: SignMessage) { + vars.logger.info(req, "Store authentication request and reply"); + vars.logger.debug(req, "AuthenticationRequest = %s", authenticationMessage); + authSession.sign_request = authenticationMessage.request; + res.json(authenticationMessage); + return BluebirdPromise.resolve(); + }) + .catch(ErrorReplies.replyWithError200(req, res, vars.logger, + UserMessages.OPERATION_FAILED)); + } - const request = u2f.request(appId, doc.registration.keyHandle); - const authenticationMessage: SignMessage = { - request: request, - keyHandle: doc.registration.keyHandle - }; - return BluebirdPromise.resolve(authenticationMessage); - }) - .then(function (authenticationMessage: SignMessage) { - logger.info(req, "Store authentication request and reply"); - logger.debug(req, "AuthenticationRequest = %s", authenticationMessage); - authSession.sign_request = authenticationMessage.request; - res.json(authenticationMessage); - return BluebirdPromise.resolve(); - }) - .catch(ErrorReplies.replyWithError200(req, res, logger, - UserMessages.OPERATION_FAILED)); + return FirstFactorBlocker(handler, vars.logger); } - diff --git a/server/src/lib/routes/verify/get.ts b/server/src/lib/routes/verify/get.ts index 13a86c0f..15c2cffc 100644 --- a/server/src/lib/routes/verify/get.ts +++ b/server/src/lib/routes/verify/get.ts @@ -7,7 +7,8 @@ import winston = require("winston"); import AuthenticationValidator = require("../../AuthenticationValidator"); import ErrorReplies = require("../../ErrorReplies"); import { AppConfiguration } from "../../configuration/Configuration"; -import AuthenticationSession = require("../../AuthenticationSession"); +import AuthenticationSessionHandler = require("../../AuthenticationSession"); +import { AuthenticationSession } from "../../../../types/AuthenticationSession"; import Constants = require("../../../../../shared/constants"); import Util = require("util"); import { DomainExtractor } from "../../utils/DomainExtractor"; @@ -22,7 +23,7 @@ const REMOTE_USER = "Remote-User"; const REMOTE_GROUPS = "Remote-Groups"; function verify_inactivity(req: express.Request, - authSession: AuthenticationSession.AuthenticationSession, + authSession: AuthenticationSession, configuration: AppConfiguration, logger: IRequestLogger) : BluebirdPromise { @@ -43,17 +44,17 @@ function verify_inactivity(req: express.Request, } logger.debug(req, "Session has been reset after too long inactivity period."); - AuthenticationSession.reset(req); + AuthenticationSessionHandler.reset(req); return BluebirdPromise.reject(new Error("Inactivity period exceeded.")); } function verify_filter(req: express.Request, res: express.Response, vars: ServerVariables): BluebirdPromise { - let _authSession: AuthenticationSession.AuthenticationSession; + let _authSession: AuthenticationSession; let username: string; let groups: string[]; - return AuthenticationSession.get(req) + return AuthenticationSessionHandler.get(req, vars.logger) .then(function (authSession) { _authSession = authSession; username = _authSession.userid; @@ -97,6 +98,7 @@ function verify_filter(req: express.Request, res: express.Response, .then(function () { res.setHeader(REMOTE_USER, username); res.setHeader(REMOTE_GROUPS, groups.join(",")); + return BluebirdPromise.resolve(); }); } diff --git a/server/src/views/already-logged-in.pug b/server/src/views/already-logged-in.pug index 40920e08..765e0344 100644 --- a/server/src/views/already-logged-in.pug +++ b/server/src/views/already-logged-in.pug @@ -5,5 +5,6 @@ block form-header block content -

You are already logged in as #{ username }.
- | Click here to log off.

+

You are already logged in as #{ username }.

+ | If you are not redirected in few seconds, click here.

+ | Otherwise, click here to log off.

diff --git a/server/src/views/layout/layout.pug b/server/src/views/layout/layout.pug index 24d96080..80f9f3fa 100644 --- a/server/src/views/layout/layout.pug +++ b/server/src/views/layout/layout.pug @@ -7,7 +7,7 @@ html link(rel="icon", href="/img/icon.png" type="image/png" sizes="32x32")/ link(rel="stylesheet", type="text/css", href="/css/authelia.css")/ if redirection_url - + body

diff --git a/server/test/IdentityCheckMiddleware.test.ts b/server/test/IdentityCheckMiddleware.test.ts index 62416179..eac77824 100644 --- a/server/test/IdentityCheckMiddleware.test.ts +++ b/server/test/IdentityCheckMiddleware.test.ts @@ -1,39 +1,41 @@ import sinon = require("sinon"); import IdentityValidator = require("../src/lib/IdentityCheckMiddleware"); -import AuthenticationSession = require("../src/lib/AuthenticationSession"); +import AuthenticationSessionHandler = require("../src/lib/AuthenticationSession"); +import { AuthenticationSession } from "../types/AuthenticationSession"; import { UserDataStore } from "../src/lib/storage/UserDataStore"; - import exceptions = require("../src/lib/Exceptions"); +import { ServerVariables } from "../src/lib/ServerVariables"; import Assert = require("assert"); -import Promise = require("bluebird"); import express = require("express"); import BluebirdPromise = require("bluebird"); - import ExpressMock = require("./mocks/express"); import NotifierMock = require("./mocks/Notifier"); import IdentityValidatorMock = require("./mocks/IdentityValidator"); -import ServerVariablesMock = require("./mocks/ServerVariablesMock"); +import { RequestLoggerStub } from "./mocks/RequestLoggerStub"; +import { ServerVariablesMock, ServerVariablesMockBuilder } from "./mocks/ServerVariablesMockBuilder"; describe("test identity check process", function () { - let mocks: ServerVariablesMock.ServerVariablesMock; let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - let notifier: NotifierMock.NotifierMock; let app: express.Application; let app_get: sinon.SinonStub; let app_post: sinon.SinonStub; let identityValidable: IdentityValidatorMock.IdentityValidableMock; + let mocks: ServerVariablesMock; + let vars: ServerVariables; beforeEach(function () { + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; + req = ExpressMock.RequestMock(); res = ExpressMock.ResponseMock(); identityValidable = IdentityValidatorMock.IdentityValidableMock(); - notifier = NotifierMock.NotifierMock(); - notifier.notify = sinon.stub().returns(Promise.resolve()); req.headers = {}; req.session = {}; @@ -42,9 +44,7 @@ describe("test identity check process", function () { req.query = {}; req.app = {}; - mocks = ServerVariablesMock.mock(req.app); - mocks.notifier = notifier; - + mocks.notifier.notifyStub.returns(BluebirdPromise.resolve()); mocks.userDataStore.produceIdentityValidationTokenStub.returns(Promise.resolve()); mocks.userDataStore.consumeIdentityValidationTokenStub.returns(Promise.resolve({ userId: "user" })); @@ -64,7 +64,7 @@ describe("test identity check process", function () { function test_start_get_handler() { it("should send 401 if pre validation initialization throws a first factor error", function () { identityValidable.preValidationInit.returns(BluebirdPromise.reject(new exceptions.FirstFactorValidationError("Error during prevalidation"))); - const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint"); + const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint", vars); return callback(req as any, res as any, undefined) .then(function () { return BluebirdPromise.reject("Should fail"); }) @@ -77,7 +77,7 @@ describe("test identity check process", function () { const identity = { userid: "abc" }; identityValidable.preValidationInit.returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint"); + const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint", vars); return callback(req as any, res as any, undefined) .then(function () { return BluebirdPromise.reject("Should fail"); }) @@ -91,7 +91,7 @@ describe("test identity check process", function () { const identity = { email: "abc@example.com" }; identityValidable.preValidationInit.returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint"); + const callback = IdentityValidator.get_start_validation(identityValidable, "/endpoint", vars); return callback(req as any, res as any, undefined) .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) @@ -107,11 +107,11 @@ describe("test identity check process", function () { req.get = sinon.stub().withArgs("Host").returns("localhost"); identityValidable.preValidationInit.returns(BluebirdPromise.resolve(identity)); - const callback = IdentityValidator.get_start_validation(identityValidable, "/finish_endpoint"); + const callback = IdentityValidator.get_start_validation(identityValidable, "/finish_endpoint", vars); return callback(req as any, res as any, undefined) .then(function () { - Assert(notifier.notify.calledOnce); + Assert(mocks.notifier.notifyStub.calledOnce); Assert(mocks.userDataStore.produceIdentityValidationTokenStub.calledOnce); Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[0], "user"); Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub.getCall(0).args[3], 240000); @@ -122,7 +122,7 @@ describe("test identity check process", function () { function test_finish_get_handler() { it("should send 401 if no identity_token is provided", function () { - const callback = IdentityValidator.get_finish_validation(identityValidable); + const callback = IdentityValidator.get_finish_validation(identityValidable, vars); return callback(req as any, res as any, undefined) .then(function () { return BluebirdPromise.reject("Should fail"); }) @@ -134,7 +134,7 @@ describe("test identity check process", function () { it("should call postValidation if identity_token is provided and still valid", function () { req.query.identity_token = "token"; - const callback = IdentityValidator.get_finish_validation(identityValidable); + const callback = IdentityValidator.get_finish_validation(identityValidable, vars); return callback(req as any, res as any, undefined); }); @@ -143,7 +143,7 @@ describe("test identity check process", function () { mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.reject(new Error("Invalid token"))); - const callback = IdentityValidator.get_finish_validation(identityValidable); + const callback = IdentityValidator.get_finish_validation(identityValidable, vars); return callback(req as any, res as any, undefined) .then(function () { return BluebirdPromise.reject("Should fail"); }) .catch(function () { @@ -155,11 +155,11 @@ describe("test identity check process", function () { req.query.identity_token = "token"; req.session = {}; - let authSession: AuthenticationSession.AuthenticationSession; - const callback = IdentityValidator.get_finish_validation(identityValidable); + let authSession: AuthenticationSession; + const callback = IdentityValidator.get_finish_validation(identityValidable, vars); - return AuthenticationSession.get(req as any) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { + return AuthenticationSessionHandler.get(req as any, vars.logger) + .then(function (_authSession) { authSession = _authSession; return callback(req as any, res as any, undefined); }) diff --git a/server/test/configuration/ConfigurationParser.test.ts b/server/test/configuration/ConfigurationParser.test.ts index c31e18b6..107a94eb 100644 --- a/server/test/configuration/ConfigurationParser.test.ts +++ b/server/test/configuration/ConfigurationParser.test.ts @@ -167,4 +167,13 @@ describe("test config parser", function () { } as ACLConfiguration); }); }); + + describe("default_redirection_url", function() { + it("should parse default_redirection_url", function() { + const userConfig = buildYamlConfig(); + userConfig.default_redirection_url = "dummy_url"; + const config = ConfigurationParser.parse(userConfig); + Assert.deepEqual(config.default_redirection_url, "dummy_url"); + }); + }); }); diff --git a/server/test/mocks/ServerVariablesMock.ts b/server/test/mocks/ServerVariablesMock.ts deleted file mode 100644 index 997b99de..00000000 --- a/server/test/mocks/ServerVariablesMock.ts +++ /dev/null @@ -1,46 +0,0 @@ -import Sinon = require("sinon"); -import express = require("express"); -import { RequestLoggerStub } from "./RequestLoggerStub"; -import { UserDataStoreStub } from "./storage/UserDataStoreStub"; -import { AuthenticationMethodCalculator } from "../../src/lib/AuthenticationMethodCalculator"; -import { VARIABLES_KEY } from "../../src/lib/ServerVariablesHandler"; - -export interface ServerVariablesMock { - logger: any; - ldapAuthenticator: any; - ldapEmailsRetriever: any; - ldapPasswordUpdater: any; - totpValidator: any; - totpGenerator: any; - u2f: any; - userDataStore: UserDataStoreStub; - notifier: any; - regulator: any; - config: any; - accessController: any; - authenticationMethodsCalculator: any; -} - - -export function mock(app: express.Application): ServerVariablesMock { - const mocks: ServerVariablesMock = { - accessController: Sinon.stub(), - config: Sinon.stub(), - ldapAuthenticator: Sinon.stub() as any, - ldapEmailsRetriever: Sinon.stub() as any, - ldapPasswordUpdater: Sinon.stub() as any, - logger: new RequestLoggerStub(), - notifier: Sinon.stub(), - regulator: Sinon.stub(), - totpGenerator: Sinon.stub(), - totpValidator: Sinon.stub(), - u2f: Sinon.stub(), - userDataStore: new UserDataStoreStub(), - authenticationMethodsCalculator: new AuthenticationMethodCalculator({ - default_method: "two_factor", - per_subdomain_methods: {} - }) - }; - app.get = Sinon.stub().withArgs(VARIABLES_KEY).returns(mocks); - return mocks; -} \ No newline at end of file diff --git a/server/test/routes/firstfactor/post.test.ts b/server/test/routes/firstfactor/post.test.ts index eb5fafec..9648ad1f 100644 --- a/server/test/routes/firstfactor/post.test.ts +++ b/server/test/routes/firstfactor/post.test.ts @@ -2,13 +2,11 @@ import Sinon = require("sinon"); import BluebirdPromise = require("bluebird"); import Assert = require("assert"); -import Winston = require("winston"); - import FirstFactorPost = require("../../../src/lib/routes/firstfactor/post"); import exceptions = require("../../../src/lib/Exceptions"); -import AuthenticationSession = require("../../../src/lib/AuthenticationSession"); +import AuthenticationSessionHandler = require("../../../src/lib/AuthenticationSession"); +import { AuthenticationSession } from "../../../types/AuthenticationSession"; import Endpoints = require("../../../../shared/api"); - import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator"); import { AccessControllerStub } from "../../mocks/AccessControllerStub"; import ExpressMock = require("../../mocks/express"); @@ -35,9 +33,6 @@ describe("test the first factor validation route", function () { mocks.regulator.markStub.returns(BluebirdPromise.resolve()); req = { - app: { - get: Sinon.stub().returns({ logger: Winston }) - }, body: { username: "username", password: "password" @@ -52,7 +47,6 @@ describe("test the first factor validation route", function () { } }; - AuthenticationSession.reset(req as any); res = ExpressMock.ResponseMock(); }); @@ -62,9 +56,9 @@ describe("test the first factor validation route", function () { emails: emails, groups: groups })); - let authSession: AuthenticationSession.AuthenticationSession; - return AuthenticationSession.get(req as any) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { + let authSession: AuthenticationSession; + return AuthenticationSessionHandler.get(req as any, vars.logger) + .then(function (_authSession) { authSession = _authSession; return FirstFactorPost.default(vars)(req as any, res as any); }) @@ -82,15 +76,15 @@ 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"]; - let authSession: AuthenticationSession.AuthenticationSession; + let authSession: AuthenticationSession; mocks.ldapAuthenticator.authenticateStub.withArgs("username", "password") .returns(BluebirdPromise.resolve({ emails: emails, groups: groups })); - return AuthenticationSession.get(req as any) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { + return AuthenticationSessionHandler.get(req as any, vars.logger) + .then(function (_authSession) { authSession = _authSession; return FirstFactorPost.default(vars)(req as any, res as any); }) diff --git a/server/test/routes/password-reset/identity/PasswordResetHandler.test.ts b/server/test/routes/password-reset/identity/PasswordResetHandler.test.ts index 9f51e200..e2bd14e1 100644 --- a/server/test/routes/password-reset/identity/PasswordResetHandler.test.ts +++ b/server/test/routes/password-reset/identity/PasswordResetHandler.test.ts @@ -1,103 +1,77 @@ import PasswordResetHandler from "../../../../src/lib/routes/password-reset/identity/PasswordResetHandler"; import PasswordUpdater = require("../../../../src/lib/ldap/PasswordUpdater"); -import { ServerVariablesHandler } from "../../../../src/lib/ServerVariablesHandler"; import { UserDataStore } from "../../../../src/lib/storage/UserDataStore"; import Sinon = require("sinon"); import winston = require("winston"); import assert = require("assert"); import BluebirdPromise = require("bluebird"); - import ExpressMock = require("../../../mocks/express"); -import ServerVariablesMock = require("../../../mocks/ServerVariablesMock"); +import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../mocks/ServerVariablesMockBuilder"; +import { ServerVariables } from "../../../../src/lib/ServerVariables"; describe("test reset password identity check", function () { - let req: ExpressMock.RequestMock; - let res: ExpressMock.ResponseMock; - let configuration: any; - let serverVariables: ServerVariablesMock.ServerVariablesMock; + let req: ExpressMock.RequestMock; + let res: ExpressMock.ResponseMock; + let mocks: ServerVariablesMock; + let vars: ServerVariables; - beforeEach(function () { - req = { - query: { - userid: "user" - }, - app: { - get: Sinon.stub() - }, - session: { - auth_session: { - userid: "user", - email: "user@example.com", - first_factor: true, - second_factor: false - } - }, - headers: { - host: "localhost" - } - }; + beforeEach(function () { + req = { + query: { + userid: "user" + }, + session: { + auth: { + userid: "user", + email: "user@example.com", + first_factor: true, + second_factor: false + } + }, + headers: { + host: "localhost" + } + }; - const options = { - inMemoryOnly: true - }; + const options = { + inMemoryOnly: true + }; - serverVariables = ServerVariablesMock.mock(req.app); + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; - serverVariables.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - serverVariables.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - serverVariables.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - serverVariables.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); + mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); + mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); + mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); + mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); + res = ExpressMock.ResponseMock(); + }); - configuration = { - ldap: { - base_dn: "dc=example,dc=com", - user_name_attribute: "cn" - } - }; - - serverVariables.config = configuration; - serverVariables.ldapEmailsRetriever = { - retrieve: Sinon.stub() - } as any; - res = ExpressMock.ResponseMock(); - }); - - describe("test reset password identity pre check", () => { - it("should fail when no userid is provided", function () { - req.query.userid = undefined; - const handler = new PasswordResetHandler(); - return handler.preValidationInit(req as any) - .then(function () { return BluebirdPromise.reject("It should fail"); }) - .catch(function (err: Error) { - return BluebirdPromise.resolve(); - }); - }); - - it("should fail if ldap fail", function (done) { - (serverVariables.ldapEmailsRetriever as any).retrieve.returns(BluebirdPromise.reject("Internal error")); - new PasswordResetHandler().preValidationInit(req as any) - .catch(function (err: Error) { - done(); - }); - }); - - it("should perform a search in ldap to find email address", function (done) { - configuration.ldap.user_name_attribute = "uid"; - (serverVariables.ldapEmailsRetriever as any).retrieve.returns(BluebirdPromise.resolve([])); - new PasswordResetHandler().preValidationInit(req as any) - .then(function () { - assert.equal("user", (serverVariables.ldapEmailsRetriever as any).retrieve.getCall(0).args[0]); - done(); - }); - }); - - it("should returns identity when ldap replies", function (done) { - (serverVariables.ldapEmailsRetriever as any).retrieve.returns(BluebirdPromise.resolve(["test@example.com"])); - new PasswordResetHandler().preValidationInit(req as any) - .then(function () { - done(); - }); + describe("test reset password identity pre check", () => { + it("should fail when no userid is provided", function () { + req.query.userid = undefined; + const handler = new PasswordResetHandler(vars.logger, vars.ldapEmailsRetriever); + return handler.preValidationInit(req as any) + .then(function () { return BluebirdPromise.reject("It should fail"); }) + .catch(function (err: Error) { + return BluebirdPromise.resolve(); }); }); + + it("should fail if ldap fail", function () { + mocks.ldapEmailsRetriever.retrieveStub.returns(BluebirdPromise.reject("Internal error")); + new PasswordResetHandler(vars.logger, vars.ldapEmailsRetriever).preValidationInit(req as any) + .then(function () { return BluebirdPromise.reject(new Error("should not be here")); }, + function (err: Error) { + return BluebirdPromise.resolve(); + }); + }); + + it("should returns identity when ldap replies", function () { + mocks.ldapEmailsRetriever.retrieveStub.returns(BluebirdPromise.resolve(["test@example.com"])); + return new PasswordResetHandler(vars.logger, vars.ldapEmailsRetriever).preValidationInit(req as any); + }); + }); }); diff --git a/server/test/routes/password-reset/post.test.ts b/server/test/routes/password-reset/post.test.ts index 436715ad..ed1d45b2 100644 --- a/server/test/routes/password-reset/post.test.ts +++ b/server/test/routes/password-reset/post.test.ts @@ -1,65 +1,60 @@ import PasswordResetFormPost = require("../../../src/lib/routes/password-reset/form/post"); import { PasswordUpdater } from "../../../src/lib/ldap/PasswordUpdater"; -import AuthenticationSession = require("../../../src/lib/AuthenticationSession"); -import { ServerVariablesHandler } from "../../../src/lib/ServerVariablesHandler"; +import AuthenticationSessionHandler = require("../../../src/lib/AuthenticationSession"); import { UserDataStore } from "../../../src/lib/storage/UserDataStore"; import Sinon = require("sinon"); -import winston = require("winston"); import Assert = require("assert"); import BluebirdPromise = require("bluebird"); - import ExpressMock = require("../../mocks/express"); -import ServerVariablesMock = require("../../mocks/ServerVariablesMock"); +import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../mocks/ServerVariablesMockBuilder"; +import { ServerVariables } from "../../../src/lib/ServerVariables"; describe("test reset password route", function () { let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - let configuration: any; - let serverVariables: ServerVariablesMock.ServerVariablesMock; + let vars: ServerVariables; + let mocks: ServerVariablesMock; beforeEach(function () { req = { body: { userid: "user" }, - app: { - get: Sinon.stub().returns({ logger: winston }) - }, session: {}, headers: { host: "localhost" } }; - AuthenticationSession.reset(req as any); + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; const options = { inMemoryOnly: true }; - serverVariables = ServerVariablesMock.mock(req.app); - serverVariables.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - serverVariables.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); - serverVariables.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - serverVariables.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); + mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); + mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); + mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); + mocks.userDataStore.consumeIdentityValidationTokenStub.returns(BluebirdPromise.resolve({})); - configuration = { - ldap: { - base_dn: "dc=example,dc=com", - user_name_attribute: "cn" - } + mocks.config.ldap = { + url: "ldap://ldapjs", + mail_attribute: "mail", + user: "user", + password: "password", + users_dn: "ou=users,dc=example,dc=com", + groups_dn: "ou=groups,dc=example,dc=com", + users_filter: "user", + group_name_attribute: "cn", + groups_filter: "groups" }; - serverVariables.config = configuration; - - serverVariables.ldapPasswordUpdater = { - updatePassword: Sinon.stub() - } as any; - res = ExpressMock.ResponseMock(); - AuthenticationSession.get(req as any) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { + AuthenticationSessionHandler.get(req as any, vars.logger) + .then(function (authSession) { authSession.userid = "user"; authSession.email = "user@example.com"; authSession.first_factor = true; @@ -72,19 +67,19 @@ describe("test reset password route", function () { req.body = {}; req.body.password = "new-password"; - (serverVariables.ldapPasswordUpdater.updatePassword as sinon.SinonStub).returns(BluebirdPromise.resolve()); + mocks.ldapPasswordUpdater.updatePasswordStub.returns(BluebirdPromise.resolve()); - return AuthenticationSession.get(req as any) + return AuthenticationSessionHandler.get(req as any, vars.logger) .then(function (authSession) { authSession.identity_check = { userid: "user", challenge: "reset-password" }; - return PasswordResetFormPost.default(req as any, res as any); + return PasswordResetFormPost.default(vars)(req as any, res as any); }) .then(function () { - return AuthenticationSession.get(req as any); - }).then(function (_authSession: AuthenticationSession.AuthenticationSession) { + return AuthenticationSessionHandler.get(req as any, vars.logger); + }).then(function (_authSession) { Assert.equal(res.status.getCall(0).args[0], 204); Assert.equal(_authSession.first_factor, false); Assert.equal(_authSession.second_factor, false); @@ -93,13 +88,13 @@ describe("test reset password route", function () { }); it("should fail if identity_challenge does not exist", function () { - return AuthenticationSession.get(req as any) + return AuthenticationSessionHandler.get(req as any, vars.logger) .then(function (authSession) { authSession.identity_check = { userid: "user", challenge: undefined }; - return PasswordResetFormPost.default(req as any, res as any); + return PasswordResetFormPost.default(vars)(req as any, res as any); }) .then(function () { Assert.equal(res.status.getCall(0).args[0], 200); @@ -113,16 +108,16 @@ describe("test reset password route", function () { req.body = {}; req.body.password = "new-password"; - (serverVariables.ldapPasswordUpdater.updatePassword as Sinon.SinonStub) + mocks.ldapPasswordUpdater.updatePasswordStub .returns(BluebirdPromise.reject("Internal error with LDAP")); - return AuthenticationSession.get(req as any) + return AuthenticationSessionHandler.get(req as any, vars.logger) .then(function (authSession) { authSession.identity_check = { challenge: "reset-password", userid: "user" }; - return PasswordResetFormPost.default(req as any, res as any); + return PasswordResetFormPost.default(vars)(req as any, res as any); }).then(function () { Assert.equal(res.status.getCall(0).args[0], 200); Assert.deepEqual(res.send.getCall(0).args[0], { diff --git a/server/test/routes/secondfactor/totp/register/RegistrationHandler.test.ts b/server/test/routes/secondfactor/totp/register/RegistrationHandler.test.ts index e1b322cd..436c9fe3 100644 --- a/server/test/routes/secondfactor/totp/register/RegistrationHandler.test.ts +++ b/server/test/routes/secondfactor/totp/register/RegistrationHandler.test.ts @@ -6,23 +6,30 @@ import AuthenticationSession = require("../../../../../src/lib/AuthenticationSes import { UserDataStore } from "../../../../../src/lib/storage/UserDataStore"; import assert = require("assert"); import BluebirdPromise = require("bluebird"); - import ExpressMock = require("../../../../mocks/express"); -import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); +import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../mocks/ServerVariablesMockBuilder"; +import { ServerVariables } from "../../../../../src/lib/ServerVariables"; describe("test totp register", function () { let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - const registrationHandler: RegistrationHandler = new RegistrationHandler(); - let authSession: AuthenticationSession.AuthenticationSession; + let mocks: ServerVariablesMock; + let vars: ServerVariables; beforeEach(function () { + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; + req = ExpressMock.RequestMock(); - const mocks = ServerVariablesMock.mock(req.app); - req.session = {}; - - AuthenticationSession.reset(req as any); - + req.session = { + auth: { + userid: "user", + email: "user@example.com", + first_factor: true, + second_factor: false + } + }; req.headers = {}; req.headers.host = "localhost"; @@ -37,23 +44,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); function test_registration_check() { it("should fail if first_factor has not been passed", function () { - authSession.first_factor = false; - return registrationHandler.preValidationInit(req as any) + req.session.auth.first_factor = false; + return new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler) + .preValidationInit(req as any) .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) .catch(function (err: Error) { return BluebirdPromise.resolve(); @@ -61,27 +60,30 @@ describe("test totp register", function () { }); it("should fail if userid is missing", function (done) { - authSession.first_factor = false; - authSession.userid = undefined; + req.session.auth.first_factor = false; + req.session.auth.userid = undefined; - registrationHandler.preValidationInit(req as any) + new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler) + .preValidationInit(req as any) .catch(function (err: Error) { done(); }); }); it("should fail if email is missing", function (done) { - authSession.first_factor = false; - authSession.email = undefined; + req.session.auth.first_factor = false; + req.session.auth.email = undefined; - registrationHandler.preValidationInit(req as any) + new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler) + .preValidationInit(req as any) .catch(function (err: Error) { done(); }); }); it("should succeed if first factor passed, userid and email are provided", function (done) { - registrationHandler.preValidationInit(req as any) + new RegistrationHandler(vars.logger, vars.userDataStore, vars.totpHandler) + .preValidationInit(req as any) .then(function (identity: Identity) { done(); }); diff --git a/server/test/routes/secondfactor/totp/sign/post.test.ts b/server/test/routes/secondfactor/totp/sign/post.test.ts index 373308c3..1cfc23e4 100644 --- a/server/test/routes/secondfactor/totp/sign/post.test.ts +++ b/server/test/routes/secondfactor/totp/sign/post.test.ts @@ -5,7 +5,8 @@ import assert = require("assert"); import winston = require("winston"); import exceptions = require("../../../../../src/lib/Exceptions"); -import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession"); +import AuthenticationSessionHandler = require("../../../../../src/lib/AuthenticationSession"); +import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; import SignPost = require("../../../../../src/lib/routes/secondfactor/totp/sign/post"); import { ServerVariables } from "../../../../../src/lib/ServerVariables"; @@ -16,7 +17,7 @@ import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../moc describe("test totp route", function () { let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - let authSession: AuthenticationSession.AuthenticationSession; + let authSession: AuthenticationSession; let vars: ServerVariables; let mocks: ServerVariablesMock; @@ -38,7 +39,6 @@ describe("test totp route", function () { } }; res = ExpressMock.ResponseMock(); - AuthenticationSession.reset(req as any); const doc = { userid: "user", @@ -47,8 +47,8 @@ describe("test totp route", function () { } }; mocks.userDataStore.retrieveTOTPSecretStub.returns(BluebirdPromise.resolve(doc)); - return AuthenticationSession.get(req as any) - .then(function (_authSession: AuthenticationSession.AuthenticationSession) { + return AuthenticationSessionHandler.get(req as any, vars.logger) + .then(function (_authSession) { authSession = _authSession; authSession.userid = "user"; authSession.first_factor = true; diff --git a/server/test/routes/secondfactor/u2f/identity/RegistrationHandler.test.ts b/server/test/routes/secondfactor/u2f/identity/RegistrationHandler.test.ts index f7feaf1c..ce0de7d6 100644 --- a/server/test/routes/secondfactor/u2f/identity/RegistrationHandler.test.ts +++ b/server/test/routes/secondfactor/u2f/identity/RegistrationHandler.test.ts @@ -9,19 +9,30 @@ import AuthenticationSession = require("../../../../../src/lib/AuthenticationSes import ExpressMock = require("../../../../mocks/express"); import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub"; -import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); +import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../mocks/ServerVariablesMockBuilder"; +import { ServerVariables } from "../../../../../src/lib/ServerVariables"; describe("test register handler", function () { let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - let authSession: AuthenticationSession.AuthenticationSession; + let mocks: ServerVariablesMock; + let vars: ServerVariables; beforeEach(function () { + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; + req = ExpressMock.RequestMock(); req.app = {}; - const mocks = ServerVariablesMock.mock(req.app); - req.session = {}; - AuthenticationSession.reset(req as any); + req.session = { + auth: { + userid: "user", + email: "user@example.com", + first_factor: true, + second_factor: false + } + }; req.headers = {}; req.headers.host = "localhost"; @@ -38,23 +49,14 @@ 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); function test_registration_check() { it("should fail if first_factor has not been passed", function () { - authSession.first_factor = false; - return new RegistrationHandler().preValidationInit(req as any) + req.session.auth.first_factor = false; + return new RegistrationHandler(vars.logger).preValidationInit(req as any) .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) .catch(function (err: Error) { return BluebirdPromise.resolve(); @@ -62,27 +64,27 @@ describe("test register handler", function () { }); it("should fail if userid is missing", function (done) { - authSession.first_factor = false; - authSession.userid = undefined; + req.session.auth.first_factor = false; + req.session.auth.userid = undefined; - new RegistrationHandler().preValidationInit(req as any) + new RegistrationHandler(vars.logger).preValidationInit(req as any) .catch(function (err: Error) { done(); }); }); it("should fail if email is missing", function (done) { - authSession.first_factor = false; - authSession.email = undefined; + req.session.auth.first_factor = false; + req.session.auth.email = undefined; - new RegistrationHandler().preValidationInit(req as any) + new RegistrationHandler(vars.logger).preValidationInit(req as any) .catch(function (err: Error) { done(); }); }); it("should succeed if first factor passed, userid and email are provided", function (done) { - new RegistrationHandler().preValidationInit(req as any) + new RegistrationHandler(vars.logger).preValidationInit(req as any) .then(function (identity: Identity) { done(); }); diff --git a/server/test/routes/secondfactor/u2f/register/post.test.ts b/server/test/routes/secondfactor/u2f/register/post.test.ts index 86d0cd01..8425cc7e 100644 --- a/server/test/routes/secondfactor/u2f/register/post.test.ts +++ b/server/test/routes/secondfactor/u2f/register/post.test.ts @@ -4,28 +4,39 @@ import BluebirdPromise = require("bluebird"); import assert = require("assert"); import U2FRegisterPost = require("../../../../../src/lib/routes/secondfactor/u2f/register/post"); import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession"); -import { ServerVariablesHandler } from "../../../../../src/lib/ServerVariablesHandler"; -import winston = require("winston"); - import ExpressMock = require("../../../../mocks/express"); import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub"; -import U2FMock = require("../../../../mocks/u2f"); -import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); -import U2f = require("u2f"); +import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../mocks/ServerVariablesMockBuilder"; +import { ServerVariables } from "../../../../../src/lib/ServerVariables"; + describe("test u2f routes: register", function () { let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock.ServerVariablesMock; + let mocks: ServerVariablesMock; + let vars: ServerVariables; beforeEach(function () { req = ExpressMock.RequestMock(); req.app = {}; - mocks = ServerVariablesMock.mock(req.app); - req.session = {}; + req.session = { + auth: { + userid: "user", + first_factor: true, + second_factor: false, + identity_check: { + challenge: "u2f-register", + userid: "user" + } + } + }; req.headers = {}; req.headers.host = "localhost"; + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; + const options = { inMemoryOnly: true }; @@ -37,18 +48,6 @@ 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); @@ -61,11 +60,9 @@ describe("test u2f routes: register", function () { publicKey: "pbk", certificate: "cert" }; - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.checkRegistration.returns(BluebirdPromise.resolve(expectedStatus)); - mocks.u2f = u2f_mock; + mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve(expectedStatus)); - return AuthenticationSession.get(req as any) + return AuthenticationSession.get(req as any, vars.logger) .then(function (authSession) { authSession.register_request = { appId: "app", @@ -73,10 +70,10 @@ describe("test u2f routes: register", function () { keyHandle: "key", version: "U2F_V2" }; - return U2FRegisterPost.default(req as any, res as any); + return U2FRegisterPost.default(vars)(req as any, res as any); }) .then(function () { - return AuthenticationSession.get(req as any); + return AuthenticationSession.get(req as any, vars.logger); }) .then(function (authSession) { assert.equal("user", mocks.userDataStore.saveU2FRegistrationStub.getCall(0).args[0]); @@ -85,12 +82,9 @@ describe("test u2f routes: register", function () { }); it("should return error message on finishRegistration error", function () { - const user_key_container = {}; - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.checkRegistration.returns({ errorCode: 500 }); - mocks.u2f = u2f_mock; + mocks.u2f.checkRegistrationStub.returns({ errorCode: 500 }); - return AuthenticationSession.get(req as any) + return AuthenticationSession.get(req as any, vars.logger) .then(function (authSession) { authSession.register_request = { appId: "app", @@ -99,7 +93,7 @@ describe("test u2f routes: register", function () { version: "U2F_V2" }; - return U2FRegisterPost.default(req as any, res as any); + return U2FRegisterPost.default(vars)(req as any, res as any); }) .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) .catch(function () { @@ -112,15 +106,11 @@ describe("test u2f routes: register", function () { }); it("should return error message when register_request is not provided", function () { - const user_key_container = {}; - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.checkRegistration.returns(BluebirdPromise.resolve()); - - mocks.u2f = u2f_mock; - return AuthenticationSession.get(req as any) + mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); + return AuthenticationSession.get(req as any, vars.logger) .then(function (authSession) { authSession.register_request = undefined; - return U2FRegisterPost.default(req as any, res as any); + return U2FRegisterPost.default(vars)(req as any, res as any); }) .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) .catch(function () { @@ -133,15 +123,11 @@ describe("test u2f routes: register", function () { }); it("should return error message when no auth request has been initiated", function () { - const user_key_container = {}; - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.checkRegistration.returns(BluebirdPromise.resolve()); - - mocks.u2f = u2f_mock; - return AuthenticationSession.get(req as any) + mocks.u2f.checkRegistrationStub.returns(BluebirdPromise.resolve()); + return AuthenticationSession.get(req as any, vars.logger) .then(function (authSession) { authSession.register_request = undefined; - return U2FRegisterPost.default(req as any, res as any); + return U2FRegisterPost.default(vars)(req as any, res as any); }) .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) .catch(function () { @@ -154,10 +140,10 @@ describe("test u2f routes: register", function () { }); it("should return error message when identity has not been verified", function () { - return AuthenticationSession.get(req as any) + return AuthenticationSession.get(req as any, vars.logger) .then(function (authSession) { authSession.identity_check = undefined; - return U2FRegisterPost.default(req as any, res as any); + return U2FRegisterPost.default(vars)(req as any, res as any); }) .then(function () { return BluebirdPromise.reject(new Error("It should fail")); }) .catch(function () { diff --git a/server/test/routes/secondfactor/u2f/register_request/get.test.ts b/server/test/routes/secondfactor/u2f/register_request/get.test.ts index 168dca30..90db3af6 100644 --- a/server/test/routes/secondfactor/u2f/register_request/get.test.ts +++ b/server/test/routes/secondfactor/u2f/register_request/get.test.ts @@ -4,36 +4,42 @@ import BluebirdPromise = require("bluebird"); import Assert = require("assert"); import U2FRegisterRequestGet = require("../../../../../src/lib/routes/secondfactor/u2f/register_request/get"); import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession"); -import { ServerVariablesHandler } from "../../../../../src/lib/ServerVariablesHandler"; -import winston = require("winston"); - import ExpressMock = require("../../../../mocks/express"); import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub"; -import U2FMock = require("../../../../mocks/u2f"); -import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); -import U2f = require("u2f"); +import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../mocks/ServerVariablesMockBuilder"; +import { ServerVariables } from "../../../../../src/lib/ServerVariables"; describe("test u2f routes: register_request", function () { let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock.ServerVariablesMock; - let authSession: AuthenticationSession.AuthenticationSession; + let mocks: ServerVariablesMock; + let vars: ServerVariables; beforeEach(function () { req = ExpressMock.RequestMock(); req.app = {}; - mocks = ServerVariablesMock.mock(req.app); - req.session = {}; - AuthenticationSession.reset(req as any); - + req.session = { + auth: { + userid: "user", + first_factor: true, + second_factor: false, + identity_check: { + challenge: "u2f-register", + userid: "user" + } + } + }; req.headers = {}; req.headers.host = "localhost"; + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; + const options = { inMemoryOnly: true }; - mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({})); @@ -41,18 +47,6 @@ 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", () => { @@ -60,12 +54,8 @@ describe("test u2f routes: register_request", function () { const expectedRequest = { test: "abc" }; - const user_key_container = {}; - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.request.returns(BluebirdPromise.resolve(expectedRequest)); - - mocks.u2f = u2f_mock; - return U2FRegisterRequestGet.default(req as any, res as any) + mocks.u2f.requestStub.returns(BluebirdPromise.resolve(expectedRequest)); + return U2FRegisterRequestGet.default(vars)(req as any, res as any) .then(function () { Assert.deepEqual(expectedRequest, res.json.getCall(0).args[0]); }); @@ -74,22 +64,19 @@ describe("test u2f routes: register_request", function () { it("should return internal error on registration request", function () { res.send = sinon.spy(); const user_key_container = {}; - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.request.returns(BluebirdPromise.reject("Internal error")); - - mocks.u2f = u2f_mock; - return U2FRegisterRequestGet.default(req as any, res as any) - .then(function() { - Assert.equal(res.status.getCall(0).args[0], 200); - Assert.deepEqual(res.send.getCall(0).args[0], { - error: "Operation failed." + mocks.u2f.requestStub.returns(BluebirdPromise.reject("Internal error")); + return U2FRegisterRequestGet.default(vars)(req as any, res as any) + .then(function () { + Assert.equal(res.status.getCall(0).args[0], 200); + Assert.deepEqual(res.send.getCall(0).args[0], { + error: "Operation failed." + }); }); - }); }); it("should return forbidden if identity has not been verified", function () { - authSession.identity_check = undefined; - return U2FRegisterRequestGet.default(req as any, res as any) + req.session.auth.identity_check = undefined; + return U2FRegisterRequestGet.default(vars)(req as any, res as any) .then(function () { Assert.equal(403, res.status.getCall(0).args[0]); }); diff --git a/server/test/routes/secondfactor/u2f/sign/post.test.ts b/server/test/routes/secondfactor/u2f/sign/post.test.ts index aef978bb..855112c7 100644 --- a/server/test/routes/secondfactor/u2f/sign/post.test.ts +++ b/server/test/routes/secondfactor/u2f/sign/post.test.ts @@ -4,27 +4,39 @@ import BluebirdPromise = require("bluebird"); import Assert = require("assert"); import U2FSignPost = require("../../../../../src/lib/routes/secondfactor/u2f/sign/post"); import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession"); -import { ServerVariablesHandler } from "../../../../../src/lib/ServerVariablesHandler"; +import { ServerVariables } from "../../../../../src/lib/ServerVariables"; import winston = require("winston"); +import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../mocks/ServerVariablesMockBuilder"; import ExpressMock = require("../../../../mocks/express"); -import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); import U2FMock = require("../../../../mocks/u2f"); import U2f = require("u2f"); describe("test u2f routes: sign", function () { let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - let authSession: AuthenticationSession.AuthenticationSession; - let mocks: ServerVariablesMock.ServerVariablesMock; + let mocks: ServerVariablesMock; + let vars: ServerVariables; beforeEach(function () { req = ExpressMock.RequestMock(); req.app = {}; - mocks = ServerVariablesMock.mock(req.app); - req.session = {}; - AuthenticationSession.reset(req as any); + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; + + req.session = { + auth: { + userid: "user", + first_factor: true, + second_factor: false, + identity_check: { + challenge: "u2f-register", + userid: "user" + } + } + }; req.headers = {}; req.headers.host = "localhost"; @@ -36,18 +48,6 @@ 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 () { @@ -56,8 +56,7 @@ describe("test u2f routes: sign", function () { publicKey: "pbk", certificate: "cert" }; - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.checkSignature.returns(expectedStatus); + mocks.u2f.checkSignatureStub.returns(expectedStatus); mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ registration: { @@ -65,16 +64,15 @@ describe("test u2f routes: sign", function () { } })); - authSession.sign_request = { + req.session.auth.sign_request = { appId: "app", challenge: "challenge", keyHandle: "key", version: "U2F_V2" }; - mocks.u2f = u2f_mock; - return U2FSignPost.default(req as any, res as any) + return U2FSignPost.default(vars)(req as any, res as any) .then(function () { - Assert(authSession.second_factor); + Assert(req.session.auth.second_factor); }); }); @@ -84,18 +82,15 @@ describe("test u2f routes: sign", function () { publicKey: "PUBKEY" } })); + mocks.u2f.checkSignatureStub.returns({ errorCode: 500 }); - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.checkSignature.returns({ errorCode: 500 }); - - authSession.sign_request = { + req.session.auth.sign_request = { appId: "app", challenge: "challenge", keyHandle: "key", version: "U2F_V2" }; - mocks.u2f = u2f_mock; - return U2FSignPost.default(req as any, res as any) + return U2FSignPost.default(vars)(req as any, res as any) .then(function () { Assert.equal(res.status.getCall(0).args[0], 200); Assert.deepEqual(res.send.getCall(0).args[0], diff --git a/server/test/routes/secondfactor/u2f/sign_request/get.test.ts b/server/test/routes/secondfactor/u2f/sign_request/get.test.ts index 2bff38ce..2493d8c8 100644 --- a/server/test/routes/secondfactor/u2f/sign_request/get.test.ts +++ b/server/test/routes/secondfactor/u2f/sign_request/get.test.ts @@ -3,37 +3,44 @@ import sinon = require("sinon"); import BluebirdPromise = require("bluebird"); import assert = require("assert"); import U2FSignRequestGet = require("../../../../../src/lib/routes/secondfactor/u2f/sign_request/get"); -import AuthenticationSession = require("../../../../../src/lib/AuthenticationSession"); -import { ServerVariablesHandler } from "../../../../../src/lib/ServerVariablesHandler"; -import winston = require("winston"); - +import AuthenticationSessionHandler = require("../../../../../src/lib/AuthenticationSession"); +import { AuthenticationSession } from "../../../../../types/AuthenticationSession"; import ExpressMock = require("../../../../mocks/express"); import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub"; -import ServerVariablesMock = require("../../../../mocks/ServerVariablesMock"); import U2FMock = require("../../../../mocks/u2f"); import U2f = require("u2f"); +import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../mocks/ServerVariablesMockBuilder"; +import { ServerVariables } from "../../../../../src/lib/ServerVariables"; import { SignMessage } from "../../../../../../shared/SignMessage"; describe("test u2f routes: sign_request", function () { let req: ExpressMock.RequestMock; let res: ExpressMock.ResponseMock; - let mocks: ServerVariablesMock.ServerVariablesMock; - let authSession: AuthenticationSession.AuthenticationSession; + let mocks: ServerVariablesMock; + let vars: ServerVariables; beforeEach(function () { req = ExpressMock.RequestMock(); req.app = {}; - - mocks = ServerVariablesMock.mock(req.app); - - req.session = {}; - - AuthenticationSession.reset(req as any); - + req.session = { + auth: { + userid: "user", + first_factor: true, + second_factor: false, + identity_check: { + challenge: "u2f-register", + userid: "user" + } + } + }; req.headers = {}; req.headers.host = "localhost"; + const s = ServerVariablesMockBuilder.build(); + mocks = s.mocks; + vars = s.variables; + const options = { inMemoryOnly: true }; @@ -42,18 +49,6 @@ 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 () { @@ -63,9 +58,7 @@ describe("test u2f routes: sign_request", function () { certificate: "Certificate", successful: true }; - const u2f_mock = U2FMock.U2FMock(); - u2f_mock.request.returns(expectedRequest); - + mocks.u2f.requestStub.returns(expectedRequest); mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({ registration: { publicKey: "PUBKEY", @@ -73,10 +66,9 @@ describe("test u2f routes: sign_request", function () { } })); - mocks.u2f = u2f_mock; - return U2FSignRequestGet.default(req as any, res as any) + return U2FSignRequestGet.default(vars)(req as any, res as any) .then(function () { - assert.deepEqual(expectedRequest, authSession.sign_request); + assert.deepEqual(expectedRequest, req.session.auth.sign_request); assert.deepEqual(expectedRequest, res.json.getCall(0).args[0].request); }); }); diff --git a/server/test/routes/verify/get.test.ts b/server/test/routes/verify/get.test.ts index 61c8e053..fd87ed0a 100644 --- a/server/test/routes/verify/get.test.ts +++ b/server/test/routes/verify/get.test.ts @@ -1,7 +1,8 @@ import Assert = require("assert"); import VerifyGet = require("../../../src/lib/routes/verify/get"); -import AuthenticationSession = require("../../../src/lib/AuthenticationSession"); +import AuthenticationSessionHandler = require("../../../src/lib/AuthenticationSession"); +import { AuthenticationSession } from "../../../types/AuthenticationSession"; import { AuthenticationMethodCalculator } from "../../../src/lib/AuthenticationMethodCalculator"; import { AuthenticationMethodsConfiguration } from "../../../src/lib/configuration/Configuration"; import Sinon = require("sinon"); @@ -28,7 +29,7 @@ describe("test /verify endpoint", function () { req.app = { get: Sinon.stub().returns({ logger: winston }) }; - AuthenticationSession.reset(req as any); + AuthenticationSessionHandler.reset(req as any); req.headers = {}; req.headers.host = "secret.example.com"; const s = ServerVariablesMockBuilder.build(); @@ -39,9 +40,9 @@ describe("test /verify endpoint", function () { it("should be already authenticated", function () { req.session = {}; mocks.accessController.isAccessAllowedMock.returns(true); - AuthenticationSession.reset(req as any); - return AuthenticationSession.get(req as any) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { + AuthenticationSessionHandler.reset(req as any); + return AuthenticationSessionHandler.get(req as any, vars.logger) + .then(function (authSession) { authSession.first_factor = true; authSession.second_factor = true; authSession.userid = "myuser"; @@ -55,8 +56,8 @@ describe("test /verify endpoint", function () { }); }); - function test_session(_authSession: AuthenticationSession.AuthenticationSession, status_code: number) { - return AuthenticationSession.get(req as any) + function test_session(_authSession: AuthenticationSession, status_code: number) { + return AuthenticationSessionHandler.get(req as any, vars.logger) .then(function (authSession) { authSession = _authSession; return VerifyGet.default(vars)(req as express.Request, res as any); @@ -66,15 +67,15 @@ describe("test /verify endpoint", function () { }); } - function test_non_authenticated_401(authSession: AuthenticationSession.AuthenticationSession) { + function test_non_authenticated_401(authSession: AuthenticationSession) { return test_session(authSession, 401); } - function test_unauthorized_403(authSession: AuthenticationSession.AuthenticationSession) { + function test_unauthorized_403(authSession: AuthenticationSession) { return test_session(authSession, 403); } - function test_authorized(authSession: AuthenticationSession.AuthenticationSession) { + function test_authorized(authSession: AuthenticationSession) { return test_session(authSession, 204); } @@ -133,7 +134,7 @@ describe("test /verify endpoint", function () { }); it("should not be authenticated when domain is not allowed for user", function () { - return AuthenticationSession.get(req as any) + return AuthenticationSessionHandler.get(req as any, vars.logger) .then(function (authSession) { authSession.first_factor = true; authSession.second_factor = true; @@ -167,7 +168,7 @@ describe("test /verify endpoint", function () { it("should be authenticated when first factor is validated and second factor is not", function () { mocks.accessController.isAccessAllowedMock.returns(true); - return AuthenticationSession.get(req as any) + return AuthenticationSessionHandler.get(req as any, vars.logger) .then(function (authSession) { authSession.first_factor = true; authSession.userid = "user1"; @@ -181,7 +182,7 @@ describe("test /verify endpoint", function () { it("should be rejected with 401 when first factor is not validated", function () { mocks.accessController.isAccessAllowedMock.returns(true); - return AuthenticationSession.get(req as any) + return AuthenticationSessionHandler.get(req as any, vars.logger) .then(function (authSession) { authSession.first_factor = false; return VerifyGet.default(vars)(req as express.Request, res as any); @@ -197,9 +198,9 @@ describe("test /verify endpoint", function () { mocks.config.session.inactivity = 200000; mocks.accessController.isAccessAllowedMock.returns(true); const currentTime = new Date().getTime() - 1000; - AuthenticationSession.reset(req as any); - return AuthenticationSession.get(req as any) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { + AuthenticationSessionHandler.reset(req as any); + return AuthenticationSessionHandler.get(req as any, vars.logger) + .then(function (authSession) { authSession.first_factor = true; authSession.second_factor = true; authSession.userid = "myuser"; @@ -208,7 +209,7 @@ describe("test /verify endpoint", function () { return VerifyGet.default(vars)(req as express.Request, res as any); }) .then(function () { - return AuthenticationSession.get(req as any); + return AuthenticationSessionHandler.get(req as any, vars.logger); }) .then(function (authSession) { Assert(authSession.last_activity_datetime > currentTime); @@ -219,9 +220,9 @@ describe("test /verify endpoint", function () { mocks.config.session.inactivity = 1; mocks.accessController.isAccessAllowedMock.returns(true); const currentTime = new Date().getTime() - 1000; - AuthenticationSession.reset(req as any); - return AuthenticationSession.get(req as any) - .then(function (authSession: AuthenticationSession.AuthenticationSession) { + AuthenticationSessionHandler.reset(req as any); + return AuthenticationSessionHandler.get(req as any, vars.logger) + .then(function (authSession) { authSession.first_factor = true; authSession.second_factor = true; authSession.userid = "myuser"; @@ -230,7 +231,7 @@ describe("test /verify endpoint", function () { return VerifyGet.default(vars)(req as express.Request, res as any); }) .then(function () { - return AuthenticationSession.get(req as any); + return AuthenticationSessionHandler.get(req as any, vars.logger); }) .then(function (authSession) { Assert.equal(authSession.first_factor, false); diff --git a/server/types/AuthenticationSession.ts b/server/types/AuthenticationSession.ts new file mode 100644 index 00000000..05aab533 --- /dev/null +++ b/server/types/AuthenticationSession.ts @@ -0,0 +1,17 @@ +import U2f = require("u2f"); + +export interface AuthenticationSession { + userid: string; + first_factor: boolean; + second_factor: boolean; + last_activity_datetime: number; + identity_check?: { + challenge: string; + userid: string; + }; + register_request?: U2f.Request; + sign_request?: U2f.Request; + email: string; + groups: string[]; + redirect?: string; +} \ No newline at end of file diff --git a/shared/ErrorMessage.ts b/shared/ErrorMessage.ts new file mode 100644 index 00000000..c5d9810a --- /dev/null +++ b/shared/ErrorMessage.ts @@ -0,0 +1,4 @@ + +export interface ErrorMessage { + error: string; +} \ No newline at end of file diff --git a/shared/RedirectionMessage.ts b/shared/RedirectionMessage.ts new file mode 100644 index 00000000..4c2dff07 --- /dev/null +++ b/shared/RedirectionMessage.ts @@ -0,0 +1,4 @@ + +export interface RedirectionMessage { + redirect: string; +} \ No newline at end of file diff --git a/test/features/auth-portal-redirection.feature b/test/features/auth-portal-redirection.feature index f4b7a247..675a47b5 100644 --- a/test/features/auth-portal-redirection.feature +++ b/test/features/auth-portal-redirection.feature @@ -10,7 +10,7 @@ Feature: User is redirected when factors are already validated Then I'm redirected to "https://auth.test.local:8080/secondfactor?redirect=https%3A%2F%2Fpublic.test.local%3A8080%2Fsecret.html" @need-registered-user-john - Scenario: User who has validated second factor and access auth portal should be redirected to "Already logged in page" + Scenario: User who has validated second factor and access auth portal should be redirected to "Already logged in page" and redirected to default URL declared in configuration When I visit "https://public.test.local:8080/secret.html" And I'm redirected to "https://auth.test.local:8080/?redirect=https%3A%2F%2Fpublic.test.local%3A8080%2Fsecret.html" And I login with user "john" and password "password" @@ -19,6 +19,8 @@ Feature: User is redirected when factors are already validated And I'm redirected to "https://public.test.local:8080/secret.html" And I visit "https://auth.test.local:8080" Then I'm redirected to "https://auth.test.local:8080/loggedin" + And I sleep for 5 seconds + And I'm redirected to "https://home.test.local:8080/" @need-registered-user-john Scenario: User who has validated second factor and access auth portal with rediction param should be redirected to that URL diff --git a/test/features/redirection.feature b/test/features/redirection.feature index 1693357c..b7d6584f 100644 --- a/test/features/redirection.feature +++ b/test/features/redirection.feature @@ -41,3 +41,11 @@ Feature: User is correctly redirected And I use "Sec0" as TOTP token handle And I click on "TOTP" Then I'm redirected to "https://public.test.local:8080/secret.html" + + @need-registered-user-john + Scenario: User is redirected to default URL defined in configuration when authentication is successful + 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'm redirected to "https://home.test.local:8080/" \ No newline at end of file