From 563e2da3232ca93acfb128570d32b40c65ba30bc Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Tue, 17 Oct 2017 23:24:02 +0200 Subject: [PATCH] Add default_redirection_url as configuration option This URL is used when user access the authentication domain without providing the 'redirect' query parameter. In that case, Authelia does not know where to redirect the user. If the parameter is defined, Authelia can redirect the user to a default page when no redirect parameter is provided. When user is already authenticated and tries to access the authentication domain, the "already logged in" page is rendered and it now tells the user he is to be redirected in few seconds and uses this URL to redirect. This parameter is optional. If it is not provided, there is only a notification message at the end of the authentication process, as before, and the user is not redirected when visiting the authentication domain while already authenticated. --- client/src/lib/secondfactor/TOTPValidator.ts | 10 +- client/src/lib/secondfactor/U2FValidator.ts | 24 +-- client/src/lib/secondfactor/index.ts | 26 ++-- client/src/lib/u2f-register/u2f-register.ts | 10 +- .../test/secondfactor/TOTPValidator.test.ts | 2 +- client/test/secondfactor/U2FValidator.test.ts | 2 +- config.template.yml | 12 ++ server/src/lib/AuthenticationSession.ts | 24 +-- server/src/lib/AuthenticationValidator.ts | 12 +- server/src/lib/FirstFactorValidator.ts | 9 +- server/src/lib/IdentityCheckMiddleware.ts | 47 +++--- server/src/lib/RestApi.ts | 49 +++--- server/src/lib/Server.ts | 3 +- ...ndler.ts => ServerVariablesInitializer.ts} | 55 ------- .../src/lib/configuration/Configuration.d.ts | 2 + .../lib/configuration/ConfigurationParser.ts | 3 +- server/src/lib/routes/FirstFactorBlocker.ts | 10 +- server/src/lib/routes/firstfactor/get.ts | 30 ++-- server/src/lib/routes/firstfactor/post.ts | 10 +- server/src/lib/routes/loggedin/get.ts | 24 +-- .../lib/routes/password-reset/form/post.ts | 56 +++---- .../identity/PasswordResetHandler.ts | 74 ++++----- server/src/lib/routes/secondfactor/get.ts | 36 ++--- .../src/lib/routes/secondfactor/redirect.ts | 33 ++-- .../totp/identity/RegistrationHandler.ts | 43 +++--- .../lib/routes/secondfactor/totp/sign/post.ts | 14 +- .../u2f/identity/RegistrationHandler.ts | 15 +- .../routes/secondfactor/u2f/register/post.ts | 91 ++++++----- .../secondfactor/u2f/register_request/get.ts | 58 +++---- .../lib/routes/secondfactor/u2f/sign/post.ts | 74 +++++---- .../secondfactor/u2f/sign_request/get.ts | 73 +++++---- server/src/lib/routes/verify/get.ts | 12 +- server/src/views/already-logged-in.pug | 5 +- server/src/views/layout/layout.pug | 2 +- server/test/IdentityCheckMiddleware.test.ts | 48 +++--- .../configuration/ConfigurationParser.test.ts | 9 ++ server/test/mocks/ServerVariablesMock.ts | 46 ------ server/test/routes/firstfactor/post.test.ts | 22 +-- .../identity/PasswordResetHandler.test.ts | 142 +++++++----------- .../test/routes/password-reset/post.test.ts | 73 +++++---- .../totp/register/RegistrationHandler.test.ts | 56 +++---- .../secondfactor/totp/sign/post.test.ts | 10 +- .../u2f/identity/RegistrationHandler.test.ts | 48 +++--- .../secondfactor/u2f/register/post.test.ts | 84 +++++------ .../u2f/register_request/get.test.ts | 73 ++++----- .../routes/secondfactor/u2f/sign/post.test.ts | 57 ++++--- .../secondfactor/u2f/sign_request/get.test.ts | 56 +++---- server/test/routes/verify/get.test.ts | 43 +++--- server/types/AuthenticationSession.ts | 17 +++ shared/ErrorMessage.ts | 4 + shared/RedirectionMessage.ts | 4 + test/features/auth-portal-redirection.feature | 4 +- test/features/redirection.feature | 8 + 53 files changed, 834 insertions(+), 920 deletions(-) rename server/src/lib/{ServerVariablesHandler.ts => ServerVariablesInitializer.ts} (72%) delete mode 100644 server/test/mocks/ServerVariablesMock.ts create mode 100644 server/types/AuthenticationSession.ts create mode 100644 shared/ErrorMessage.ts create mode 100644 shared/RedirectionMessage.ts 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