Log what is retrieved from headers to help debugging.
parent
76fa325f08
commit
7c3d6cc376
|
@ -19,7 +19,7 @@ async function runTests(suite, withEnv) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let mochaArgs = ['--exit', '--colors', '--require', 'ts-node/register', 'test/suites/' + suite + '/test.ts']
|
let mochaArgs = ['--exit', '--colors', '--require', 'ts-node/register', 'test/suites/' + suite + '/test.ts']
|
||||||
if (program.forbidOnly) {
|
if (program.forbidOnly) {
|
||||||
mochaArgs = ['--forbid-only', '--forbid-pending'] + mochaArgs;
|
mochaArgs = ['--forbid-only', '--forbid-pending'].concat(mochaArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mochaCommand = './node_modules/.bin/mocha ' + mochaArgs.join(' ');
|
const mochaCommand = './node_modules/.bin/mocha ' + mochaArgs.join(' ');
|
||||||
|
|
|
@ -4,11 +4,17 @@ var program = require('commander');
|
||||||
var spawn = require('child_process').spawn;
|
var spawn = require('child_process').spawn;
|
||||||
|
|
||||||
program
|
program
|
||||||
|
.option('--forbid-only', 'Forbid only and pending filters.')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
async function runTests(pattern) {
|
async function runTests(patterns) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
mocha = spawn('./node_modules/.bin/mocha', ['--colors', '--require', 'ts-node/register', pattern], {
|
let mochaArgs = ['--require', 'ts-node/register', '--require', './spec-helper.js', '--colors'];
|
||||||
|
if (program.forbidOnly) {
|
||||||
|
mochaArgs = ['--forbid-only', '--forbid-pending'].concat(mochaArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocha = spawn('./node_modules/.bin/mocha', mochaArgs.concat(patterns), {
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
'TS_NODE_PROJECT': './server/tsconfig.json'
|
'TS_NODE_PROJECT': './server/tsconfig.json'
|
||||||
|
@ -27,8 +33,10 @@ async function runTests(pattern) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function test() {
|
async function test() {
|
||||||
await runTests('server/src/**/*.spec.ts');
|
await runTests([
|
||||||
await runTests('shared/**/*.spec.ts');
|
'server/src/**/*.spec.ts',
|
||||||
|
'shared/**/*.spec.ts'
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
test()
|
test()
|
||||||
|
|
|
@ -1,25 +1,18 @@
|
||||||
import express = require("express");
|
import * as Express from "express";
|
||||||
import { IRequestLogger } from "./logging/IRequestLogger";
|
import { IRequestLogger } from "./logging/IRequestLogger";
|
||||||
|
|
||||||
function replyWithError(req: express.Request, res: express.Response,
|
function replyWithError(req: Express.Request, res: Express.Response,
|
||||||
code: number, logger: IRequestLogger, body?: Object): (err: Error) => void {
|
code: number, logger: IRequestLogger, body?: Object): (err: Error) => void {
|
||||||
return function (err: Error): void {
|
return function (err: Error): void {
|
||||||
if (req.originalUrl.startsWith("/api/") || code == 200) {
|
logger.error(req, "Reply with error %d: %s", code, err.message);
|
||||||
logger.error(req, "Reply with error %d: %s", code, err.message);
|
logger.debug(req, "%s", err.stack);
|
||||||
logger.debug(req, "%s", err.stack);
|
res.status(code);
|
||||||
res.status(code);
|
res.send(body);
|
||||||
res.send(body);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger.error(req, "Redirect to error %d: %s", code, err.message);
|
|
||||||
logger.debug(req, "%s", err.stack);
|
|
||||||
res.redirect("/error/" + code);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function redirectTo(redirectUrl: string, req: express.Request,
|
export function redirectTo(redirectUrl: string, req: Express.Request,
|
||||||
res: express.Response, logger: IRequestLogger) {
|
res: Express.Response, logger: IRequestLogger) {
|
||||||
return function(err: Error) {
|
return function(err: Error) {
|
||||||
logger.error(req, "Error: %s", err.message);
|
logger.error(req, "Error: %s", err.message);
|
||||||
logger.debug(req, "Redirecting to %s", redirectUrl);
|
logger.debug(req, "Redirecting to %s", redirectUrl);
|
||||||
|
@ -27,22 +20,22 @@ export function redirectTo(redirectUrl: string, req: express.Request,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replyWithError400(req: express.Request,
|
export function replyWithError400(req: Express.Request,
|
||||||
res: express.Response, logger: IRequestLogger) {
|
res: Express.Response, logger: IRequestLogger) {
|
||||||
return replyWithError(req, res, 400, logger);
|
return replyWithError(req, res, 400, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replyWithError401(req: express.Request,
|
export function replyWithError401(req: Express.Request,
|
||||||
res: express.Response, logger: IRequestLogger) {
|
res: Express.Response, logger: IRequestLogger) {
|
||||||
return replyWithError(req, res, 401, logger);
|
return replyWithError(req, res, 401, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replyWithError403(req: express.Request,
|
export function replyWithError403(req: Express.Request,
|
||||||
res: express.Response, logger: IRequestLogger) {
|
res: Express.Response, logger: IRequestLogger) {
|
||||||
return replyWithError(req, res, 403, logger);
|
return replyWithError(req, res, 403, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function replyWithError200(req: express.Request,
|
export function replyWithError200(req: Express.Request,
|
||||||
res: express.Response, logger: IRequestLogger, message: string) {
|
res: Express.Response, logger: IRequestLogger, message: string) {
|
||||||
return replyWithError(req, res, 200, logger, { error: message });
|
return replyWithError(req, res, 200, logger, { error: message });
|
||||||
}
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
|
|
||||||
import sinon = require("sinon");
|
import * as Sinon from "sinon";
|
||||||
import * as IdentityCheckMiddleware from "./IdentityCheckMiddleware";
|
import * as IdentityCheckMiddleware from "./IdentityCheckMiddleware";
|
||||||
import exceptions = require("./Exceptions");
|
|
||||||
import { ServerVariables } from "./ServerVariables";
|
import { ServerVariables } from "./ServerVariables";
|
||||||
import Assert = require("assert");
|
import * as Assert from "assert";
|
||||||
import express = require("express");
|
import * as Express from "express";
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import ExpressMock = require("./stubs/express.spec");
|
import ExpressMock = require("./stubs/express.spec");
|
||||||
import { IdentityValidableStub } from "./IdentityValidableStub.spec";
|
import { IdentityValidableStub } from "./IdentityValidableStub.spec";
|
||||||
|
@ -13,11 +12,11 @@ import { ServerVariablesMock, ServerVariablesMockBuilder }
|
||||||
import { OPERATION_FAILED } from "../../../shared/UserMessages";
|
import { OPERATION_FAILED } from "../../../shared/UserMessages";
|
||||||
|
|
||||||
describe("IdentityCheckMiddleware", function () {
|
describe("IdentityCheckMiddleware", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let app: express.Application;
|
let app: Express.Application;
|
||||||
let app_get: sinon.SinonStub;
|
let app_get: Sinon.SinonStub;
|
||||||
let app_post: sinon.SinonStub;
|
let app_post: Sinon.SinonStub;
|
||||||
let identityValidable: IdentityValidableStub;
|
let identityValidable: IdentityValidableStub;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
@ -29,14 +28,6 @@ describe("IdentityCheckMiddleware", function () {
|
||||||
|
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
|
|
||||||
req.headers = {};
|
|
||||||
req.originalUrl = "/non-api/xxx";
|
|
||||||
req.session = {};
|
|
||||||
|
|
||||||
req.query = {};
|
|
||||||
req.app = {};
|
|
||||||
|
|
||||||
identityValidable = new IdentityValidableStub();
|
identityValidable = new IdentityValidableStub();
|
||||||
|
|
||||||
mocks.notifier.notifyStub.returns(BluebirdPromise.resolve());
|
mocks.notifier.notifyStub.returns(BluebirdPromise.resolve());
|
||||||
|
@ -45,9 +36,9 @@ describe("IdentityCheckMiddleware", function () {
|
||||||
mocks.userDataStore.consumeIdentityValidationTokenStub
|
mocks.userDataStore.consumeIdentityValidationTokenStub
|
||||||
.returns(BluebirdPromise.resolve({ userId: "user" }));
|
.returns(BluebirdPromise.resolve({ userId: "user" }));
|
||||||
|
|
||||||
app = express();
|
app = Express();
|
||||||
app_get = sinon.stub(app, "get");
|
app_get = Sinon.stub(app, "get");
|
||||||
app_post = sinon.stub(app, "post");
|
app_post = Sinon.stub(app, "post");
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
@ -56,22 +47,8 @@ describe("IdentityCheckMiddleware", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("test start GET", function () {
|
describe("test start GET", function () {
|
||||||
it("should redirect to error 401 if pre validation initialization \
|
|
||||||
throws a first factor error", function () {
|
|
||||||
identityValidable.preValidationInitStub.returns(BluebirdPromise.reject(
|
|
||||||
new exceptions.FirstFactorValidationError(
|
|
||||||
"Error during prevalidation")));
|
|
||||||
const callback = IdentityCheckMiddleware.post_start_validation(
|
|
||||||
identityValidable, vars);
|
|
||||||
|
|
||||||
return callback(req as any, res as any, undefined)
|
|
||||||
.then(() => {
|
|
||||||
Assert(res.redirect.calledWith("/error/401"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// In that case we answer with 200 to avoid user enumeration.
|
// In that case we answer with 200 to avoid user enumeration.
|
||||||
it("should send 200 if email is missing in provided identity", function () {
|
it("should send 200 if email is missing in provided identity", async function () {
|
||||||
const identity = { userid: "abc" };
|
const identity = { userid: "abc" };
|
||||||
|
|
||||||
identityValidable.preValidationInitStub
|
identityValidable.preValidationInitStub
|
||||||
|
@ -79,31 +56,26 @@ throws a first factor error", function () {
|
||||||
const callback = IdentityCheckMiddleware
|
const callback = IdentityCheckMiddleware
|
||||||
.post_start_validation(identityValidable, vars);
|
.post_start_validation(identityValidable, vars);
|
||||||
|
|
||||||
return callback(req as any, res as any, undefined)
|
await callback(req as any, res as any, undefined)
|
||||||
.then(function () {
|
Assert(identityValidable.preValidationResponseStub.called);
|
||||||
Assert(identityValidable.preValidationResponseStub.called);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// In that case we answer with 200 to avoid user enumeration.
|
// In that case we answer with 200 to avoid user enumeration.
|
||||||
it("should send 200 if userid is missing in provided identity",
|
it("should send 200 if userid is missing in provided identity", async function () {
|
||||||
function () {
|
const identity = { email: "abc@example.com" };
|
||||||
const identity = { email: "abc@example.com" };
|
|
||||||
|
|
||||||
identityValidable.preValidationInitStub
|
identityValidable.preValidationInitStub
|
||||||
.returns(BluebirdPromise.resolve(identity));
|
.returns(BluebirdPromise.resolve(identity));
|
||||||
const callback = IdentityCheckMiddleware
|
const callback = IdentityCheckMiddleware
|
||||||
.post_start_validation(identityValidable, vars);
|
.post_start_validation(identityValidable, vars);
|
||||||
|
|
||||||
return callback(req as any, res as any, undefined)
|
await callback(req as any, res as any, undefined)
|
||||||
.then(function () {
|
Assert(identityValidable.preValidationResponseStub.called);
|
||||||
Assert(identityValidable.preValidationResponseStub.called);
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should issue a token, send an email and return 204", async function () {
|
it("should issue a token, send an email and return 204", async function () {
|
||||||
const identity = { userid: "user", email: "abc@example.com" };
|
const identity = { userid: "user", email: "abc@example.com" };
|
||||||
req.get = sinon.stub().withArgs("Host").returns("localhost");
|
req.get = Sinon.stub().withArgs("Host").returns("localhost");
|
||||||
|
|
||||||
identityValidable.preValidationInitStub
|
identityValidable.preValidationInitStub
|
||||||
.returns(BluebirdPromise.resolve(identity));
|
.returns(BluebirdPromise.resolve(identity));
|
||||||
|
@ -124,42 +96,36 @@ throws a first factor error", function () {
|
||||||
|
|
||||||
|
|
||||||
describe("test finish GET", function () {
|
describe("test finish GET", function () {
|
||||||
it("should return an error if no identity_token is provided", () => {
|
it("should return an error if no identity_token is provided", async () => {
|
||||||
const callback = IdentityCheckMiddleware
|
const callback = IdentityCheckMiddleware
|
||||||
.post_finish_validation(identityValidable, vars);
|
.post_finish_validation(identityValidable, vars);
|
||||||
|
|
||||||
return callback(req as any, res as any, undefined)
|
await callback(req as any, res as any, undefined)
|
||||||
.then(function () {
|
Assert(res.status.calledWith(200));
|
||||||
Assert(res.status.calledWith(200));
|
Assert(res.send.calledWith({'error': OPERATION_FAILED}));
|
||||||
Assert(res.send.calledWith({'error': OPERATION_FAILED}));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call postValidation if identity_token is provided and still \
|
it("should call postValidation if identity_token is provided and still valid", async function () {
|
||||||
valid", function () {
|
req.query.identity_token = "token";
|
||||||
req.query.identity_token = "token";
|
const callback = IdentityCheckMiddleware
|
||||||
const callback = IdentityCheckMiddleware
|
.post_finish_validation(identityValidable, vars);
|
||||||
.post_finish_validation(identityValidable, vars);
|
await callback(req as any, res as any, undefined);
|
||||||
return callback(req as any, res as any, undefined);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it("should return an error if identity_token is provided but invalid",
|
it("should return an error if identity_token is provided but invalid", async function () {
|
||||||
function () {
|
req.query.identity_token = "token";
|
||||||
req.query.identity_token = "token";
|
|
||||||
|
|
||||||
identityValidable.postValidationInitStub
|
identityValidable.postValidationInitStub
|
||||||
.returns(BluebirdPromise.resolve());
|
.returns(BluebirdPromise.resolve());
|
||||||
mocks.userDataStore.consumeIdentityValidationTokenStub.reset();
|
mocks.userDataStore.consumeIdentityValidationTokenStub.reset();
|
||||||
mocks.userDataStore.consumeIdentityValidationTokenStub
|
mocks.userDataStore.consumeIdentityValidationTokenStub
|
||||||
.returns(BluebirdPromise.reject(new Error("Invalid token")));
|
.returns(() => Promise.reject(new Error("Invalid token")));
|
||||||
|
|
||||||
const callback = IdentityCheckMiddleware
|
const callback = IdentityCheckMiddleware
|
||||||
.post_finish_validation(identityValidable, vars);
|
.post_finish_validation(identityValidable, vars);
|
||||||
return callback(req as any, res as any, undefined)
|
await callback(req as any, res as any, undefined)
|
||||||
.then(() => {
|
Assert(res.status.calledWith(200));
|
||||||
Assert(res.status.calledWith(200));
|
Assert(res.send.calledWith({'error': OPERATION_FAILED}));
|
||||||
Assert(res.send.calledWith({'error': OPERATION_FAILED}));
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import BluebirdPromise = require("bluebird");
|
import * as Bluebird from "bluebird";
|
||||||
|
import * as Express from "express";
|
||||||
|
import * as http from "http";
|
||||||
|
|
||||||
import { Configuration } from "./configuration/schema/Configuration";
|
import { Configuration } from "./configuration/schema/Configuration";
|
||||||
import { GlobalDependencies } from "../../types/Dependencies";
|
import { GlobalDependencies } from "../../types/Dependencies";
|
||||||
|
@ -9,8 +11,7 @@ import { ServerVariables } from "./ServerVariables";
|
||||||
import { ServerVariablesInitializer } from "./ServerVariablesInitializer";
|
import { ServerVariablesInitializer } from "./ServerVariablesInitializer";
|
||||||
import { Configurator } from "./web_server/Configurator";
|
import { Configurator } from "./web_server/Configurator";
|
||||||
|
|
||||||
import * as Express from "express";
|
import { GET_VARIABLE_KEY } from "../../../shared/constants";
|
||||||
import * as http from "http";
|
|
||||||
|
|
||||||
function clone(obj: any) {
|
function clone(obj: any) {
|
||||||
return JSON.parse(JSON.stringify(obj));
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
@ -44,11 +45,11 @@ export default class Server {
|
||||||
JSON.stringify(displayableConfiguration, undefined, 2));
|
JSON.stringify(displayableConfiguration, undefined, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private setup(config: Configuration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise<void> {
|
private setup(config: Configuration, app: Express.Application, deps: GlobalDependencies): Bluebird<void> {
|
||||||
const that = this;
|
|
||||||
return ServerVariablesInitializer.initialize(
|
return ServerVariablesInitializer.initialize(
|
||||||
config, this.globalLogger, this.requestLogger, deps)
|
config, this.globalLogger, this.requestLogger, deps)
|
||||||
.then(function (vars: ServerVariables) {
|
.then(function (vars: ServerVariables) {
|
||||||
|
app.set(GET_VARIABLE_KEY, vars);
|
||||||
return Configurator.configure(config, app, vars, deps);
|
return Configurator.configure(config, app, vars, deps);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -56,7 +57,7 @@ export default class Server {
|
||||||
private startServer(app: Express.Application, port: number) {
|
private startServer(app: Express.Application, port: number) {
|
||||||
const that = this;
|
const that = this;
|
||||||
that.globalLogger.info("Starting Authelia...");
|
that.globalLogger.info("Starting Authelia...");
|
||||||
return new BluebirdPromise<void>((resolve, reject) => {
|
return new Bluebird<void>((resolve, reject) => {
|
||||||
this.httpServer = app.listen(port, function (err: string) {
|
this.httpServer = app.listen(port, function (err: string) {
|
||||||
that.globalLogger.info("Listening on port %d...", port);
|
that.globalLogger.info("Listening on port %d...", port);
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -65,7 +66,7 @@ export default class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
start(configuration: Configuration, deps: GlobalDependencies)
|
start(configuration: Configuration, deps: GlobalDependencies)
|
||||||
: BluebirdPromise<void> {
|
: Bluebird<void> {
|
||||||
const that = this;
|
const that = this;
|
||||||
const app = Express();
|
const app = Express();
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
import * as Express from 'express';
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import FirstFactorPost = require("./post");
|
import FirstFactorPost = require("./post");
|
||||||
import exceptions = require("../../Exceptions");
|
import exceptions = require("../../Exceptions");
|
||||||
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
||||||
import ExpressMock = require("../../stubs/express.spec");
|
import * as ExpressMock from "../../stubs/express.spec";
|
||||||
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../ServerVariablesMockBuilder.spec";
|
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../ServerVariablesMockBuilder.spec";
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import AuthenticationError from "../../authentication/AuthenticationError";
|
import AuthenticationError from "../../authentication/AuthenticationError";
|
||||||
|
|
||||||
describe("routes/firstfactor/post", function () {
|
describe("routes/firstfactor/post", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let emails: string[];
|
let emails: string[];
|
||||||
let groups: string[];
|
let groups: string[];
|
||||||
|
@ -29,23 +30,11 @@ describe("routes/firstfactor/post", function () {
|
||||||
mocks.regulator.regulateStub.returns(BluebirdPromise.resolve());
|
mocks.regulator.regulateStub.returns(BluebirdPromise.resolve());
|
||||||
mocks.regulator.markStub.returns(BluebirdPromise.resolve());
|
mocks.regulator.markStub.returns(BluebirdPromise.resolve());
|
||||||
|
|
||||||
req = {
|
req = ExpressMock.RequestMock();
|
||||||
originalUrl: "/api/firstfactor",
|
req.body = {
|
||||||
body: {
|
username: "username",
|
||||||
username: "username",
|
password: "password"
|
||||||
password: "password"
|
}
|
||||||
},
|
|
||||||
query: {
|
|
||||||
redirect: "http://redirect.url"
|
|
||||||
},
|
|
||||||
session: {
|
|
||||||
cookie: {}
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
host: "home.example.com"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
authSession = AuthenticationSessionHandler.get(req as any, vars.logger);
|
authSession = AuthenticationSessionHandler.get(req as any, vars.logger);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import * as ObjectPath from "object-path";
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import ErrorReplies = require("../../ErrorReplies");
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
|
@ -16,6 +15,7 @@ import { Subject } from "../../../lib/authorization/Subject";
|
||||||
import AuthenticationError from "../../../lib/authentication/AuthenticationError";
|
import AuthenticationError from "../../../lib/authentication/AuthenticationError";
|
||||||
import IsRedirectionSafe from "../../../lib/utils/IsRedirectionSafe";
|
import IsRedirectionSafe from "../../../lib/utils/IsRedirectionSafe";
|
||||||
import * as URLParse from "url-parse";
|
import * as URLParse from "url-parse";
|
||||||
|
import GetHeader from "../../utils/GetHeader";
|
||||||
|
|
||||||
export default function (vars: ServerVariables) {
|
export default function (vars: ServerVariables) {
|
||||||
return function (req: express.Request, res: express.Response)
|
return function (req: express.Request, res: express.Response)
|
||||||
|
@ -63,7 +63,7 @@ export default function (vars: ServerVariables) {
|
||||||
vars.regulator.mark(username, true);
|
vars.regulator.mark(username, true);
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
const targetUrl = ObjectPath.get<Express.Request, string>(req, "headers.x-target-url", undefined);
|
const targetUrl = GetHeader(req, "x-target-url");
|
||||||
|
|
||||||
if (!targetUrl) {
|
if (!targetUrl) {
|
||||||
res.status(204);
|
res.status(204);
|
||||||
|
@ -85,7 +85,7 @@ export default function (vars: ServerVariables) {
|
||||||
|
|
||||||
const authorizationLevel = vars.authorizer.authorization(resObject, subject);
|
const authorizationLevel = vars.authorizer.authorization(resObject, subject);
|
||||||
if (authorizationLevel <= AuthorizationLevel.ONE_FACTOR) {
|
if (authorizationLevel <= AuthorizationLevel.ONE_FACTOR) {
|
||||||
if (IsRedirectionSafe(vars, authSession, new URLParse(targetUrl))) {
|
if (IsRedirectionSafe(vars, new URLParse(targetUrl))) {
|
||||||
res.json({redirect: targetUrl});
|
res.json({redirect: targetUrl});
|
||||||
return BluebirdPromise.resolve();
|
return BluebirdPromise.resolve();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
|
import * as Express from "express";
|
||||||
import PasswordResetFormPost = require("./post");
|
import PasswordResetFormPost = require("./post");
|
||||||
import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler";
|
import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
|
||||||
import { UserDataStore } from "../../../storage/UserDataStore";
|
|
||||||
import Sinon = require("sinon");
|
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import ExpressMock = require("../../../stubs/express.spec");
|
import ExpressMock = require("../../../stubs/express.spec");
|
||||||
|
@ -12,32 +10,19 @@ import { ServerVariables } from "../../../ServerVariables";
|
||||||
import { Level } from "../../../authentication/Level";
|
import { Level } from "../../../authentication/Level";
|
||||||
|
|
||||||
describe("routes/password-reset/form/post", function () {
|
describe("routes/password-reset/form/post", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let authSession: AuthenticationSession;
|
let authSession: AuthenticationSession;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = {
|
req = ExpressMock.RequestMock();
|
||||||
originalUrl: "/api/password-reset",
|
|
||||||
body: {
|
|
||||||
userid: "user"
|
|
||||||
},
|
|
||||||
session: {},
|
|
||||||
headers: {
|
|
||||||
host: "localhost"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const s = ServerVariablesMockBuilder.build();
|
const s = ServerVariablesMockBuilder.build();
|
||||||
mocks = s.mocks;
|
mocks = s.mocks;
|
||||||
vars = s.variables;
|
vars = s.variables;
|
||||||
|
|
||||||
const options = {
|
|
||||||
inMemoryOnly: true
|
|
||||||
};
|
|
||||||
|
|
||||||
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||||
|
@ -65,7 +50,6 @@ describe("routes/password-reset/form/post", function () {
|
||||||
|
|
||||||
describe("test reset password post", () => {
|
describe("test reset password post", () => {
|
||||||
it("should update the password and reset auth_session for reauthentication", function () {
|
it("should update the password and reset auth_session for reauthentication", function () {
|
||||||
req.body = {};
|
|
||||||
req.body.password = "new-password";
|
req.body.password = "new-password";
|
||||||
|
|
||||||
mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve());
|
mocks.usersDatabase.updatePasswordStub.returns(BluebirdPromise.resolve());
|
||||||
|
@ -99,7 +83,6 @@ describe("routes/password-reset/form/post", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail when ldap fails", function () {
|
it("should fail when ldap fails", function () {
|
||||||
req.body = {};
|
|
||||||
req.body.password = "new-password";
|
req.body.password = "new-password";
|
||||||
|
|
||||||
mocks.usersDatabase.updatePasswordStub
|
mocks.usersDatabase.updatePasswordStub
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
import * as Express from "express";
|
||||||
import PasswordResetHandler
|
import PasswordResetHandler
|
||||||
from "./PasswordResetHandler";
|
from "./PasswordResetHandler";
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
@ -8,28 +8,20 @@ import { ServerVariablesMock, ServerVariablesMockBuilder }
|
||||||
import { ServerVariables } from "../../../ServerVariables";
|
import { ServerVariables } from "../../../ServerVariables";
|
||||||
|
|
||||||
describe("routes/password-reset/identity/PasswordResetHandler", function () {
|
describe("routes/password-reset/identity/PasswordResetHandler", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = {
|
req = ExpressMock.RequestMock();
|
||||||
originalUrl: "/non-api/xxx",
|
req.body.username = "user";
|
||||||
body: {
|
req.session.auth = {
|
||||||
username: "user"
|
userid: "user",
|
||||||
},
|
email: "user@example.com",
|
||||||
session: {
|
first_factor: true,
|
||||||
auth: {
|
second_factor: false
|
||||||
userid: "user",
|
|
||||||
email: "user@example.com",
|
|
||||||
first_factor: true,
|
|
||||||
second_factor: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
headers: {
|
|
||||||
host: "localhost"
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
req.headers.host = "localhost";
|
||||||
|
|
||||||
const s = ServerVariablesMockBuilder.build();
|
const s = ServerVariablesMockBuilder.build();
|
||||||
mocks = s.mocks;
|
mocks = s.mocks;
|
||||||
|
|
|
@ -1,41 +1,44 @@
|
||||||
|
import * as Express from "express";
|
||||||
import Redirect from "./redirect";
|
import Redirect from "./redirect";
|
||||||
import ExpressMock = require("../../stubs/express.spec");
|
import ExpressMock = require("../../stubs/express.spec");
|
||||||
import { ServerVariablesMockBuilder, ServerVariablesMock }
|
import { ServerVariablesMockBuilder }
|
||||||
from "../../ServerVariablesMockBuilder.spec";
|
from "../../ServerVariablesMockBuilder.spec";
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
import { HEADER_X_TARGET_URL } from "../../../../../shared/constants";
|
||||||
|
|
||||||
describe("routes/secondfactor/redirect", function() {
|
describe("routes/secondfactor/redirect", function() {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
const s = ServerVariablesMockBuilder.build();
|
const s = ServerVariablesMockBuilder.build();
|
||||||
mocks = s.mocks;
|
|
||||||
vars = s.variables;
|
vars = s.variables;
|
||||||
|
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
|
vars.config.session.domain = 'example.com';
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should redirect to default_redirection_url", function() {
|
describe('redirect to default url if no target provided', () => {
|
||||||
vars.config.default_redirection_url = "http://default_redirection_url";
|
it("should redirect to default url", async () => {
|
||||||
Redirect(vars)(req as any, res as any)
|
vars.config.default_redirection_url = "https://home.example.com";
|
||||||
.then(function() {
|
await Redirect(vars)(req, res as any)
|
||||||
Assert(res.json.calledWith({
|
Assert(res.json.calledWith({redirect: "https://home.example.com"}));
|
||||||
redirect: "http://default_redirection_url"
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should redirect to /", function() {
|
it("should redirect to safe url https://test.example.com/", async () => {
|
||||||
Redirect(vars)(req as any, res as any)
|
req.headers[HEADER_X_TARGET_URL] = "https://test.example.com/";
|
||||||
.then(function() {
|
await Redirect(vars)(req, res as any);
|
||||||
Assert(res.json.calledWith({
|
Assert(res.json.calledWith({redirect: "https://test.example.com/"}));
|
||||||
redirect: "/"
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not redirect to unsafe target url', async () => {
|
||||||
|
vars.config.default_redirection_url = "https://home.example.com";
|
||||||
|
req.headers[HEADER_X_TARGET_URL] = "http://test.example.com/";
|
||||||
|
await Redirect(vars)(req, res as any);
|
||||||
|
Assert(res.status.calledWith(204));
|
||||||
|
})
|
||||||
});
|
});
|
|
@ -1,39 +1,27 @@
|
||||||
|
|
||||||
import express = require("express");
|
import * as Express from "express";
|
||||||
import * as URLParse from "url-parse";
|
import * as URLParse from "url-parse";
|
||||||
import * as ObjectPath from "object-path";
|
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import BluebirdPromise = require("bluebird");
|
|
||||||
import ErrorReplies = require("../../ErrorReplies");
|
|
||||||
import UserMessages = require("../../../../../shared/UserMessages");
|
|
||||||
import IsRedirectionSafe from "../../../lib/utils/IsRedirectionSafe";
|
import IsRedirectionSafe from "../../../lib/utils/IsRedirectionSafe";
|
||||||
import { AuthenticationSessionHandler } from "../../../lib/AuthenticationSessionHandler";
|
import GetHeader from "../../utils/GetHeader";
|
||||||
|
import { HEADER_X_TARGET_URL } from "../../../../../shared/constants";
|
||||||
|
|
||||||
|
|
||||||
export default function (vars: ServerVariables) {
|
export default function (vars: ServerVariables) {
|
||||||
return function (req: express.Request, res: express.Response)
|
return async function (req: Express.Request, res: Express.Response): Promise<void> {
|
||||||
: BluebirdPromise<void> {
|
let redirectUrl = GetHeader(req, HEADER_X_TARGET_URL);
|
||||||
|
|
||||||
return new BluebirdPromise<void>(function (resolve, reject) {
|
if (!redirectUrl && vars.config.default_redirection_url) {
|
||||||
let redirectUrl: string = ObjectPath.get<Express.Request, string>(
|
redirectUrl = vars.config.default_redirection_url;
|
||||||
req, "headers.x-target-url", undefined);
|
}
|
||||||
|
|
||||||
if (!redirectUrl && vars.config.default_redirection_url) {
|
if ((redirectUrl && !IsRedirectionSafe(vars, new URLParse(redirectUrl)))
|
||||||
redirectUrl = vars.config.default_redirection_url;
|
|| !redirectUrl) {
|
||||||
}
|
res.status(204);
|
||||||
|
res.send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
res.json({redirect: redirectUrl});
|
||||||
if ((redirectUrl && !IsRedirectionSafe(vars, authSession, new URLParse(redirectUrl)))
|
|
||||||
|| !redirectUrl) {
|
|
||||||
res.status(204);
|
|
||||||
res.send();
|
|
||||||
return resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({redirect: redirectUrl});
|
|
||||||
return resolve();
|
|
||||||
})
|
|
||||||
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
|
|
||||||
UserMessages.OPERATION_FAILED));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
import Sinon = require("sinon");
|
|
||||||
import RegistrationHandler from "./RegistrationHandler";
|
import RegistrationHandler from "./RegistrationHandler";
|
||||||
import { Identity } from "../../../../../../types/Identity";
|
|
||||||
import { UserDataStore } from "../../../../storage/UserDataStore";
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import ExpressMock = require("../../../../stubs/express.spec");
|
import ExpressMock = require("../../../../stubs/express.spec");
|
||||||
import { ServerVariablesMock, ServerVariablesMockBuilder }
|
import { ServerVariablesMock, ServerVariablesMockBuilder }
|
||||||
|
@ -10,7 +7,7 @@ import { ServerVariables } from "../../../../ServerVariables";
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
|
||||||
describe("routes/secondfactor/totp/identity/RegistrationHandler", function () {
|
describe("routes/secondfactor/totp/identity/RegistrationHandler", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
@ -22,6 +19,7 @@ describe("routes/secondfactor/totp/identity/RegistrationHandler", function () {
|
||||||
|
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
req.session = {
|
req.session = {
|
||||||
|
...req.session,
|
||||||
auth: {
|
auth: {
|
||||||
userid: "user",
|
userid: "user",
|
||||||
email: "user@example.com",
|
email: "user@example.com",
|
||||||
|
@ -33,12 +31,6 @@ describe("routes/secondfactor/totp/identity/RegistrationHandler", function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
req.headers = {};
|
|
||||||
req.headers.host = "localhost";
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
inMemoryOnly: true
|
|
||||||
};
|
|
||||||
|
|
||||||
mocks.userDataStore.saveU2FRegistrationStub
|
mocks.userDataStore.saveU2FRegistrationStub
|
||||||
.returns(BluebirdPromise.resolve({}));
|
.returns(BluebirdPromise.resolve({}));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
import express = require("express");
|
import * as Express from "express";
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
import { Identity } from "../../../../../../types/Identity";
|
import { Identity } from "../../../../../../types/Identity";
|
||||||
|
@ -35,7 +35,7 @@ export default class RegistrationHandler implements IdentityValidable {
|
||||||
return Constants.CHALLENGE;
|
return Constants.CHALLENGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private retrieveIdentity(req: express.Request): BluebirdPromise<Identity> {
|
private retrieveIdentity(req: Express.Request): BluebirdPromise<Identity> {
|
||||||
const that = this;
|
const that = this;
|
||||||
return new BluebirdPromise(function (resolve, reject) {
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
const authSession = AuthenticationSessionHandler.get(req, that.logger);
|
const authSession = AuthenticationSessionHandler.get(req, that.logger);
|
||||||
|
@ -54,7 +54,7 @@ export default class RegistrationHandler implements IdentityValidable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
preValidationInit(req: express.Request): BluebirdPromise<Identity> {
|
preValidationInit(req: Express.Request): BluebirdPromise<Identity> {
|
||||||
const that = this;
|
const that = this;
|
||||||
return FirstFactorValidator.validate(req, this.logger)
|
return FirstFactorValidator.validate(req, this.logger)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
@ -62,16 +62,16 @@ export default class RegistrationHandler implements IdentityValidable {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
preValidationResponse(req: express.Request, res: express.Response) {
|
preValidationResponse(req: Express.Request, res: Express.Response) {
|
||||||
res.status(204);
|
res.status(204);
|
||||||
res.send();
|
res.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
postValidationInit(req: express.Request) {
|
postValidationInit(req: Express.Request) {
|
||||||
return FirstFactorValidator.validate(req, this.logger);
|
return FirstFactorValidator.validate(req, this.logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
postValidationResponse(req: express.Request, res: express.Response)
|
postValidationResponse(req: Express.Request, res: Express.Response)
|
||||||
: BluebirdPromise<void> {
|
: BluebirdPromise<void> {
|
||||||
const that = this;
|
const that = this;
|
||||||
let secret: TOTPSecret;
|
let secret: TOTPSecret;
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Sinon = require("sinon");
|
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import Exceptions = require("../../../../Exceptions");
|
import * as Express from "express";
|
||||||
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
||||||
import SignPost = require("./post");
|
import SignPost = require("./post");
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
|
|
||||||
import ExpressMock = require("../../../../stubs/express.spec");
|
import ExpressMock = require("../../../../stubs/express.spec");
|
||||||
import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec";
|
|
||||||
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec";
|
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec";
|
||||||
import { Level } from "../../../../authentication/Level";
|
import { Level } from "../../../../authentication/Level";
|
||||||
|
|
||||||
describe("routes/secondfactor/totp/sign/post", function () {
|
describe("routes/secondfactor/totp/sign/post", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let authSession: AuthenticationSession;
|
let authSession: AuthenticationSession;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
@ -24,17 +21,9 @@ describe("routes/secondfactor/totp/sign/post", function () {
|
||||||
const s = ServerVariablesMockBuilder.build();
|
const s = ServerVariablesMockBuilder.build();
|
||||||
vars = s.variables;
|
vars = s.variables;
|
||||||
mocks = s.mocks;
|
mocks = s.mocks;
|
||||||
const app_get = Sinon.stub();
|
req = ExpressMock.RequestMock();
|
||||||
req = {
|
req.body = {
|
||||||
originalUrl: "/api/totp-register",
|
token: "abc",
|
||||||
app: {},
|
|
||||||
body: {
|
|
||||||
token: "abc"
|
|
||||||
},
|
|
||||||
session: {},
|
|
||||||
query: {
|
|
||||||
redirect: "http://redirect"
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
|
import * as Express from "express";
|
||||||
import Sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import Assert = require("assert");
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
import { Identity } from "../../../../../../types/Identity";
|
|
||||||
import RegistrationHandler from "./RegistrationHandler";
|
import RegistrationHandler from "./RegistrationHandler";
|
||||||
import ExpressMock = require("../../../../stubs/express.spec");
|
import ExpressMock = require("../../../../stubs/express.spec");
|
||||||
import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec";
|
|
||||||
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec";
|
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec";
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
|
|
||||||
describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () {
|
describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
@ -21,8 +18,8 @@ describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () {
|
||||||
vars = s.variables;
|
vars = s.variables;
|
||||||
|
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
req.app = {};
|
|
||||||
req.session = {
|
req.session = {
|
||||||
|
...req.session,
|
||||||
auth: {
|
auth: {
|
||||||
userid: "user",
|
userid: "user",
|
||||||
email: "user@example.com",
|
email: "user@example.com",
|
||||||
|
@ -33,10 +30,6 @@ describe("routes/secondfactor/u2f/identity/RegistrationHandler", function () {
|
||||||
req.headers = {};
|
req.headers = {};
|
||||||
req.headers.host = "localhost";
|
req.headers.host = "localhost";
|
||||||
|
|
||||||
const options = {
|
|
||||||
inMemoryOnly: true
|
|
||||||
};
|
|
||||||
|
|
||||||
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.produceIdentityValidationTokenStub.returns(BluebirdPromise.resolve({}));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
import * as Express from "express";
|
||||||
import sinon = require("sinon");
|
import sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
|
@ -6,13 +6,12 @@ import U2FRegisterPost = require("./post");
|
||||||
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
|
||||||
import ExpressMock = require("../../../../stubs/express.spec");
|
import ExpressMock = require("../../../../stubs/express.spec");
|
||||||
import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec";
|
|
||||||
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec";
|
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec";
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
|
|
||||||
|
|
||||||
describe("routes/secondfactor/u2f/register/post", function () {
|
describe("routes/secondfactor/u2f/register/post", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
@ -21,8 +20,8 @@ describe("routes/secondfactor/u2f/register/post", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
req.originalUrl = "/api/xxxx";
|
req.originalUrl = "/api/xxxx";
|
||||||
req.app = {};
|
|
||||||
req.session = {
|
req.session = {
|
||||||
|
...req.session,
|
||||||
auth: {
|
auth: {
|
||||||
userid: "user",
|
userid: "user",
|
||||||
first_factor: true,
|
first_factor: true,
|
||||||
|
@ -40,10 +39,6 @@ describe("routes/secondfactor/u2f/register/post", function () {
|
||||||
mocks = s.mocks;
|
mocks = s.mocks;
|
||||||
vars = s.variables;
|
vars = s.variables;
|
||||||
|
|
||||||
const options = {
|
|
||||||
inMemoryOnly: true
|
|
||||||
};
|
|
||||||
|
|
||||||
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.saveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||||
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
mocks.userDataStore.retrieveU2FRegistrationStub.returns(BluebirdPromise.resolve({}));
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
|
import * as Express from "express";
|
||||||
import sinon = require("sinon");
|
import sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
import U2FRegisterRequestGet = require("./get");
|
import U2FRegisterRequestGet = require("./get");
|
||||||
import ExpressMock = require("../../../../stubs/express.spec");
|
import ExpressMock = require("../../../../stubs/express.spec");
|
||||||
import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec";
|
|
||||||
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec";
|
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec";
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
|
|
||||||
describe("routes/secondfactor/u2f/register_request/get", function () {
|
describe("routes/secondfactor/u2f/register_request/get", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
@ -17,8 +16,8 @@ describe("routes/secondfactor/u2f/register_request/get", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
req.originalUrl = "/api/xxxx";
|
req.originalUrl = "/api/xxxx";
|
||||||
req.app = {};
|
|
||||||
req.session = {
|
req.session = {
|
||||||
|
...req.session,
|
||||||
auth: {
|
auth: {
|
||||||
userid: "user",
|
userid: "user",
|
||||||
first_factor: true,
|
first_factor: true,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
import * as Express from "express";
|
||||||
import sinon = require("sinon");
|
import sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
@ -10,14 +10,13 @@ import ExpressMock = require("../../../../stubs/express.spec");
|
||||||
import { Level } from "../../../../authentication/Level";
|
import { Level } from "../../../../authentication/Level";
|
||||||
|
|
||||||
describe("routes/secondfactor/u2f/sign/post", function () {
|
describe("routes/secondfactor/u2f/sign/post", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
req.app = {};
|
|
||||||
req.originalUrl = "/api/xxxx";
|
req.originalUrl = "/api/xxxx";
|
||||||
|
|
||||||
const s = ServerVariablesMockBuilder.build();
|
const s = ServerVariablesMockBuilder.build();
|
||||||
|
@ -25,6 +24,7 @@ describe("routes/secondfactor/u2f/sign/post", function () {
|
||||||
vars = s.variables;
|
vars = s.variables;
|
||||||
|
|
||||||
req.session = {
|
req.session = {
|
||||||
|
...req.session,
|
||||||
auth: {
|
auth: {
|
||||||
userid: "user",
|
userid: "user",
|
||||||
authentication_level: Level.ONE_FACTOR,
|
authentication_level: Level.ONE_FACTOR,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
import * as Express from "express";
|
||||||
import sinon = require("sinon");
|
import sinon = require("sinon");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import assert = require("assert");
|
import assert = require("assert");
|
||||||
|
@ -8,10 +8,8 @@ import { Request } from "u2f";
|
||||||
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec";
|
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec";
|
||||||
import { ServerVariables } from "../../../../ServerVariables";
|
import { ServerVariables } from "../../../../ServerVariables";
|
||||||
|
|
||||||
import { SignMessage } from "../../../../../../../shared/SignMessage";
|
|
||||||
|
|
||||||
describe("routes/secondfactor/u2f/sign_request/get", function () {
|
describe("routes/secondfactor/u2f/sign_request/get", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
@ -19,8 +17,8 @@ describe("routes/secondfactor/u2f/sign_request/get", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
req.originalUrl = "/api/xxxx";
|
req.originalUrl = "/api/xxxx";
|
||||||
req.app = {};
|
|
||||||
req.session = {
|
req.session = {
|
||||||
|
...req.session,
|
||||||
auth: {
|
auth: {
|
||||||
userid: "user",
|
userid: "user",
|
||||||
first_factor: true,
|
first_factor: true,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
|
|
||||||
import Assert = require("assert");
|
import * as Assert from "assert";
|
||||||
import BluebirdPromise = require("bluebird");
|
import * as Express from "express";
|
||||||
import Express = require("express");
|
import * as Sinon from "sinon";
|
||||||
import Sinon = require("sinon");
|
|
||||||
import winston = require("winston");
|
|
||||||
|
|
||||||
import VerifyGet = require("./get");
|
import VerifyGet = require("./get");
|
||||||
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
|
||||||
|
@ -13,9 +11,10 @@ import { ServerVariables } from "../../ServerVariables";
|
||||||
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec";
|
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../ServerVariablesMockBuilder.spec";
|
||||||
import { Level } from "../../authentication/Level";
|
import { Level } from "../../authentication/Level";
|
||||||
import { Level as AuthorizationLevel } from "../../authorization/Level";
|
import { Level as AuthorizationLevel } from "../../authorization/Level";
|
||||||
|
import { HEADER_X_ORIGINAL_URL } from "../../../../../shared/constants";
|
||||||
|
|
||||||
describe("routes/verify/get", function () {
|
describe("routes/verify/get", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: Express.Request;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let mocks: ServerVariablesMock;
|
let mocks: ServerVariablesMock;
|
||||||
let vars: ServerVariables;
|
let vars: ServerVariables;
|
||||||
|
@ -29,7 +28,7 @@ describe("routes/verify/get", function () {
|
||||||
redirect: "undefined"
|
redirect: "undefined"
|
||||||
};
|
};
|
||||||
AuthenticationSessionHandler.reset(req as any);
|
AuthenticationSessionHandler.reset(req as any);
|
||||||
req.headers["x-original-url"] = "https://secret.example.com/";
|
req.headers[HEADER_X_ORIGINAL_URL] = "https://secret.example.com/";
|
||||||
const s = ServerVariablesMockBuilder.build(false);
|
const s = ServerVariablesMockBuilder.build(false);
|
||||||
mocks = s.mocks;
|
mocks = s.mocks;
|
||||||
vars = s.variables;
|
vars = s.variables;
|
||||||
|
@ -37,40 +36,37 @@ describe("routes/verify/get", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("with session cookie", function () {
|
describe("with session cookie", function () {
|
||||||
it("should be already authenticated", function () {
|
it("should be already authenticated", async function () {
|
||||||
mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR);
|
mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR);
|
||||||
authSession.authentication_level = Level.TWO_FACTOR;
|
authSession.authentication_level = Level.TWO_FACTOR;
|
||||||
authSession.userid = "myuser";
|
authSession.userid = "myuser";
|
||||||
authSession.groups = ["mygroup", "othergroup"];
|
authSession.groups = ["mygroup", "othergroup"];
|
||||||
return VerifyGet.default(vars)(req as Express.Request, res as any)
|
await VerifyGet.default(vars)(req as Express.Request, res as any);
|
||||||
.then(function () {
|
res.setHeader.calledWith("Remote-User", "myuser");
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
res.setHeader.calledWith("Remote-Groups", "mygroup,othergroup");
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
Assert.equal(204, res.status.getCall(0).args[0]);
|
||||||
Assert.equal(204, res.status.getCall(0).args[0]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function test_session(_authSession: AuthenticationSession, status_code: number) {
|
function test_session(_authSession: AuthenticationSession, status_code: number) {
|
||||||
|
const GetMock = Sinon.stub(AuthenticationSessionHandler, 'get');
|
||||||
|
GetMock.returns(_authSession);
|
||||||
return VerifyGet.default(vars)(req as Express.Request, res as any)
|
return VerifyGet.default(vars)(req as Express.Request, res as any)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
Assert.equal(status_code, res.status.getCall(0).args[0]);
|
Assert.equal(status_code, res.status.getCall(0).args[0]);
|
||||||
});
|
GetMock.restore();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_non_authenticated_401(authSession: AuthenticationSession) {
|
function test_non_authenticated_401(_authSession: AuthenticationSession) {
|
||||||
return test_session(authSession, 401);
|
return test_session(_authSession, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_unauthorized_403(authSession: AuthenticationSession) {
|
function test_unauthorized_403(_authSession: AuthenticationSession) {
|
||||||
return test_session(authSession, 403);
|
return test_session(_authSession, 403);
|
||||||
}
|
|
||||||
|
|
||||||
function test_authorized(authSession: AuthenticationSession) {
|
|
||||||
return test_session(authSession, 204);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("given user tries to access a 2-factor endpoint", function () {
|
describe("given user tries to access a 2-factor endpoint", function () {
|
||||||
before(function () {
|
beforeEach(function () {
|
||||||
mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR);
|
mocks.authorizer.authorizationMock.returns(AuthorizationLevel.TWO_FACTOR);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -115,7 +111,7 @@ describe("routes/verify/get", function () {
|
||||||
it("should not be authenticated when domain is not allowed for user", function () {
|
it("should not be authenticated when domain is not allowed for user", function () {
|
||||||
authSession.authentication_level = Level.TWO_FACTOR;
|
authSession.authentication_level = Level.TWO_FACTOR;
|
||||||
authSession.userid = "myuser";
|
authSession.userid = "myuser";
|
||||||
req.headers["x-original-url"] = "https://test.example.com/";
|
req.headers[HEADER_X_ORIGINAL_URL] = "https://test.example.com/";
|
||||||
mocks.authorizer.authorizationMock.returns(AuthorizationLevel.DENY);
|
mocks.authorizer.authorizationMock.returns(AuthorizationLevel.DENY);
|
||||||
|
|
||||||
return test_unauthorized_403({
|
return test_unauthorized_403({
|
||||||
|
@ -132,7 +128,7 @@ describe("routes/verify/get", function () {
|
||||||
|
|
||||||
describe("given user tries to access a single factor endpoint", function () {
|
describe("given user tries to access a single factor endpoint", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
req.headers["x-original-url"] = "https://redirect.url/";
|
req.headers[HEADER_X_ORIGINAL_URL] = "https://redirect.url/";
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be authenticated when first factor is validated", function () {
|
it("should be authenticated when first factor is validated", function () {
|
||||||
|
@ -227,7 +223,7 @@ describe("routes/verify/get", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("with basic auth", function () {
|
describe("with basic auth", function () {
|
||||||
it("should authenticate correctly", function () {
|
it("should authenticate correctly", async function () {
|
||||||
mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR);
|
mocks.authorizer.authorizationMock.returns(AuthorizationLevel.ONE_FACTOR);
|
||||||
mocks.config.access_control.default_policy = "one_factor";
|
mocks.config.access_control.default_policy = "one_factor";
|
||||||
mocks.usersDatabase.checkUserPasswordStub.returns({
|
mocks.usersDatabase.checkUserPasswordStub.returns({
|
||||||
|
@ -235,12 +231,10 @@ describe("routes/verify/get", function () {
|
||||||
});
|
});
|
||||||
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
req.headers["proxy-authorization"] = "Basic am9objpwYXNzd29yZA==";
|
||||||
|
|
||||||
return VerifyGet.default(vars)(req as Express.Request, res as any)
|
await VerifyGet.default(vars)(req as Express.Request, res as any)
|
||||||
.then(function () {
|
res.setHeader.calledWith("Remote-User", "john");
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "john");
|
res.setHeader.calledWith("Remote-Groups", "mygroup,othergroup");
|
||||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
Assert.equal(204, res.status.getCall(0).args[0]);
|
||||||
Assert.equal(204, res.status.getCall(0).args[0]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail when endpoint is protected by two factors", function () {
|
it("should fail when endpoint is protected by two factors", function () {
|
||||||
|
|
|
@ -6,34 +6,44 @@ import { ServerVariables } from "../../ServerVariables";
|
||||||
import GetWithSessionCookieMethod from "./get_session_cookie";
|
import GetWithSessionCookieMethod from "./get_session_cookie";
|
||||||
import GetWithBasicAuthMethod from "./get_basic_auth";
|
import GetWithBasicAuthMethod from "./get_basic_auth";
|
||||||
import Constants = require("../../../../../shared/constants");
|
import Constants = require("../../../../../shared/constants");
|
||||||
import ObjectPath = require("object-path");
|
|
||||||
|
|
||||||
import { AuthenticationSessionHandler }
|
import { AuthenticationSessionHandler }
|
||||||
from "../../AuthenticationSessionHandler";
|
from "../../AuthenticationSessionHandler";
|
||||||
import { AuthenticationSession }
|
import { AuthenticationSession }
|
||||||
from "../../../../types/AuthenticationSession";
|
from "../../../../types/AuthenticationSession";
|
||||||
|
import GetHeader from "../../utils/GetHeader";
|
||||||
|
|
||||||
const REMOTE_USER = "Remote-User";
|
const REMOTE_USER = "Remote-User";
|
||||||
const REMOTE_GROUPS = "Remote-Groups";
|
const REMOTE_GROUPS = "Remote-Groups";
|
||||||
|
|
||||||
|
|
||||||
function verifyWithSelectedMethod(req: Express.Request, res: Express.Response,
|
function verifyWithSelectedMethod(req: Express.Request, res: Express.Response,
|
||||||
vars: ServerVariables, authSession: AuthenticationSession)
|
vars: ServerVariables, authSession: AuthenticationSession | undefined)
|
||||||
: () => BluebirdPromise<{ username: string, groups: string[] }> {
|
: () => BluebirdPromise<{ username: string, groups: string[] }> {
|
||||||
return function () {
|
return function () {
|
||||||
const authorization: string = "" + req.headers["proxy-authorization"];
|
const authorization = GetHeader(req, Constants.HEADER_PROXY_AUTHORIZATION);
|
||||||
if (authorization && authorization.startsWith("Basic "))
|
if (authorization) {
|
||||||
return GetWithBasicAuthMethod(req, res, vars, authorization);
|
if (authorization.startsWith("Basic ")) {
|
||||||
|
return GetWithBasicAuthMethod(req, res, vars, authorization);
|
||||||
return GetWithSessionCookieMethod(req, res, vars, authSession);
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("The authorization header should be of the form 'Basic XXXXXX'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (authSession) {
|
||||||
|
return GetWithSessionCookieMethod(req, res, vars, authSession);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error("No cookie detected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRedirectHeader(req: Express.Request, res: Express.Response) {
|
function setRedirectHeader(req: Express.Request, res: Express.Response) {
|
||||||
return function () {
|
return function () {
|
||||||
const originalUrl = ObjectPath.get<Express.Request, string>(
|
const originalUrl = GetHeader(req, Constants.HEADER_X_ORIGINAL_URL);
|
||||||
req, "headers.x-original-url");
|
res.set(Constants.HEADER_REDIRECT, originalUrl);
|
||||||
res.set("Redirect", originalUrl);
|
|
||||||
return BluebirdPromise.resolve();
|
return BluebirdPromise.resolve();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -62,7 +72,7 @@ function getRedirectParam(req: Express.Request) {
|
||||||
export default function (vars: ServerVariables) {
|
export default function (vars: ServerVariables) {
|
||||||
return function (req: Express.Request, res: Express.Response)
|
return function (req: Express.Request, res: Express.Response)
|
||||||
: BluebirdPromise<void> {
|
: BluebirdPromise<void> {
|
||||||
let authSession: AuthenticationSession;
|
let authSession: AuthenticationSession | undefined;
|
||||||
return new BluebirdPromise(function (resolve, reject) {
|
return new BluebirdPromise(function (resolve, reject) {
|
||||||
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
authSession = AuthenticationSessionHandler.get(req, vars.logger);
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -78,6 +88,7 @@ export default function (vars: ServerVariables) {
|
||||||
ErrorReplies.replyWithError401(req, res, vars.logger))
|
ErrorReplies.replyWithError401(req, res, vars.logger))
|
||||||
// The user is not yet authenticated -> 401
|
// The user is not yet authenticated -> 401
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
// This redirect parameter is used in Kubernetes to annotate the ingress with
|
// This redirect parameter is used in Kubernetes to annotate the ingress with
|
||||||
// the url to the authentication portal.
|
// the url to the authentication portal.
|
||||||
const redirectUrl = getRedirectParam(req);
|
const redirectUrl = getRedirectParam(req);
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
import Express = require("express");
|
import Express = require("express");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import ObjectPath = require("object-path");
|
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
import { AuthenticationSession }
|
|
||||||
from "../../../../types/AuthenticationSession";
|
|
||||||
import AccessControl from "./access_control";
|
import AccessControl from "./access_control";
|
||||||
import { URLDecomposer } from "../../utils/URLDecomposer";
|
import { URLDecomposer } from "../../utils/URLDecomposer";
|
||||||
import { Level } from "../../authentication/Level";
|
import { Level } from "../../authentication/Level";
|
||||||
|
import GetHeader from "../../utils/GetHeader";
|
||||||
|
import { HEADER_X_ORIGINAL_URL } from "../../../../../shared/constants";
|
||||||
|
|
||||||
export default function (req: Express.Request, res: Express.Response,
|
export default function (req: Express.Request, res: Express.Response,
|
||||||
vars: ServerVariables, authorizationHeader: string)
|
vars: ServerVariables, authorizationHeader: string)
|
||||||
: BluebirdPromise<{ username: string, groups: string[] }> {
|
: BluebirdPromise<{ username: string, groups: string[] }> {
|
||||||
let username: string;
|
let username: string;
|
||||||
const uri = ObjectPath.get<Express.Request, string>(req, "headers.x-original-url");
|
const uri = GetHeader(req, HEADER_X_ORIGINAL_URL);
|
||||||
const urlDecomposition = URLDecomposer.fromUrl(uri);
|
const urlDecomposition = URLDecomposer.fromUrl(uri);
|
||||||
|
|
||||||
return BluebirdPromise.resolve()
|
return BluebirdPromise.resolve()
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import Express = require("express");
|
import Express = require("express");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Util = require("util");
|
|
||||||
import ObjectPath = require("object-path");
|
|
||||||
|
|
||||||
import Exceptions = require("../../Exceptions");
|
import Exceptions = require("../../Exceptions");
|
||||||
import { Configuration } from "../../configuration/schema/Configuration";
|
import { Configuration } from "../../configuration/schema/Configuration";
|
||||||
import { ServerVariables } from "../../ServerVariables";
|
import { ServerVariables } from "../../ServerVariables";
|
||||||
|
@ -13,6 +10,8 @@ import { AuthenticationSessionHandler }
|
||||||
from "../../AuthenticationSessionHandler";
|
from "../../AuthenticationSessionHandler";
|
||||||
import AccessControl from "./access_control";
|
import AccessControl from "./access_control";
|
||||||
import { URLDecomposer } from "../../utils/URLDecomposer";
|
import { URLDecomposer } from "../../utils/URLDecomposer";
|
||||||
|
import GetHeader from "../../utils/GetHeader";
|
||||||
|
import { HEADER_X_ORIGINAL_URL } from "../../../../../shared/constants";
|
||||||
|
|
||||||
function verify_inactivity(req: Express.Request,
|
function verify_inactivity(req: Express.Request,
|
||||||
authSession: AuthenticationSession,
|
authSession: AuthenticationSession,
|
||||||
|
@ -54,8 +53,7 @@ export default function (req: Express.Request, res: Express.Response,
|
||||||
"userid is missing"));
|
"userid is missing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const originalUrl = ObjectPath.get<Express.Request, string>(
|
const originalUrl = GetHeader(req, HEADER_X_ORIGINAL_URL);
|
||||||
req, "headers.x-original-url");
|
|
||||||
|
|
||||||
const d = URLDecomposer.fromUrl(originalUrl);
|
const d = URLDecomposer.fromUrl(originalUrl);
|
||||||
vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain,
|
vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain,
|
||||||
|
|
|
@ -1,103 +1,117 @@
|
||||||
|
|
||||||
import sinon = require("sinon");
|
import * as Sinon from "sinon";
|
||||||
import express = require("express");
|
import * as Express from "express";
|
||||||
|
import { GET_VARIABLE_KEY } from "../../../../shared/constants";
|
||||||
export interface RequestMock {
|
import { RequestLoggerStub } from "../logging/RequestLoggerStub.spec";
|
||||||
app?: any;
|
|
||||||
body?: any;
|
|
||||||
session?: any;
|
|
||||||
headers?: any;
|
|
||||||
get?: any;
|
|
||||||
query?: any;
|
|
||||||
originalUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResponseMock {
|
export interface ResponseMock {
|
||||||
send: sinon.SinonStub | sinon.SinonSpy;
|
send: Sinon.SinonStub | Sinon.SinonSpy;
|
||||||
sendStatus: sinon.SinonStub;
|
sendStatus: Sinon.SinonStub;
|
||||||
sendFile: sinon.SinonStub;
|
sendFile: Sinon.SinonStub;
|
||||||
sendfile: sinon.SinonStub;
|
sendfile: Sinon.SinonStub;
|
||||||
status: sinon.SinonStub | sinon.SinonSpy;
|
status: Sinon.SinonStub | Sinon.SinonSpy;
|
||||||
json: sinon.SinonStub | sinon.SinonSpy;
|
json: Sinon.SinonStub | Sinon.SinonSpy;
|
||||||
links: sinon.SinonStub;
|
links: Sinon.SinonStub;
|
||||||
jsonp: sinon.SinonStub;
|
jsonp: Sinon.SinonStub;
|
||||||
download: sinon.SinonStub;
|
download: Sinon.SinonStub;
|
||||||
contentType: sinon.SinonStub;
|
contentType: Sinon.SinonStub;
|
||||||
type: sinon.SinonStub;
|
type: Sinon.SinonStub;
|
||||||
format: sinon.SinonStub;
|
format: Sinon.SinonStub;
|
||||||
attachment: sinon.SinonStub;
|
attachment: Sinon.SinonStub;
|
||||||
set: sinon.SinonStub;
|
set: Sinon.SinonStub;
|
||||||
header: sinon.SinonStub;
|
header: Sinon.SinonStub;
|
||||||
headersSent: boolean;
|
headersSent: boolean;
|
||||||
get: sinon.SinonStub;
|
get: Sinon.SinonStub;
|
||||||
clearCookie: sinon.SinonStub;
|
clearCookie: Sinon.SinonStub;
|
||||||
cookie: sinon.SinonStub;
|
cookie: Sinon.SinonStub;
|
||||||
location: sinon.SinonStub;
|
location: Sinon.SinonStub;
|
||||||
redirect: sinon.SinonStub | sinon.SinonSpy;
|
redirect: Sinon.SinonStub | Sinon.SinonSpy;
|
||||||
render: sinon.SinonStub | sinon.SinonSpy;
|
render: Sinon.SinonStub | Sinon.SinonSpy;
|
||||||
locals: sinon.SinonStub;
|
locals: Sinon.SinonStub;
|
||||||
charset: string;
|
charset: string;
|
||||||
vary: sinon.SinonStub;
|
vary: Sinon.SinonStub;
|
||||||
app: any;
|
app: any;
|
||||||
write: sinon.SinonStub;
|
write: Sinon.SinonStub;
|
||||||
writeContinue: sinon.SinonStub;
|
writeContinue: Sinon.SinonStub;
|
||||||
writeHead: sinon.SinonStub;
|
writeHead: Sinon.SinonStub;
|
||||||
statusCode: number;
|
statusCode: number;
|
||||||
statusMessage: string;
|
statusMessage: string;
|
||||||
setHeader: sinon.SinonStub;
|
setHeader: Sinon.SinonStub;
|
||||||
setTimeout: sinon.SinonStub;
|
setTimeout: Sinon.SinonStub;
|
||||||
sendDate: boolean;
|
sendDate: boolean;
|
||||||
getHeader: sinon.SinonStub;
|
getHeader: Sinon.SinonStub;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RequestMock(): RequestMock {
|
export function RequestMock(): Express.Request {
|
||||||
|
const getMock = Sinon.mock()
|
||||||
|
.withArgs(GET_VARIABLE_KEY).atLeast(0).returns({
|
||||||
|
logger: new RequestLoggerStub()
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
originalUrl: "/non-api/xxx",
|
id: '1234',
|
||||||
|
headers: {},
|
||||||
app: {
|
app: {
|
||||||
get: sinon.stub()
|
get: getMock,
|
||||||
|
set: Sinon.mock(),
|
||||||
},
|
},
|
||||||
headers: {
|
body: {},
|
||||||
"x-forwarded-for": "127.0.0.1"
|
query: {},
|
||||||
},
|
session: {
|
||||||
session: {}
|
id: '1234',
|
||||||
};
|
regenerate: function() {},
|
||||||
|
reload: function() {},
|
||||||
|
destroy: function() {},
|
||||||
|
save: function() {},
|
||||||
|
touch: function() {},
|
||||||
|
cookie: {
|
||||||
|
domain: 'example.com',
|
||||||
|
expires: true,
|
||||||
|
httpOnly: true,
|
||||||
|
maxAge: 36000,
|
||||||
|
originalMaxAge: 36000,
|
||||||
|
path: '/',
|
||||||
|
secure: true,
|
||||||
|
serialize: () => '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as any;
|
||||||
}
|
}
|
||||||
export function ResponseMock(): ResponseMock {
|
export function ResponseMock(): ResponseMock {
|
||||||
return {
|
return {
|
||||||
send: sinon.stub(),
|
send: Sinon.stub(),
|
||||||
status: sinon.stub(),
|
status: Sinon.stub(),
|
||||||
json: sinon.stub(),
|
json: Sinon.stub(),
|
||||||
sendStatus: sinon.stub(),
|
sendStatus: Sinon.stub(),
|
||||||
links: sinon.stub(),
|
links: Sinon.stub(),
|
||||||
jsonp: sinon.stub(),
|
jsonp: Sinon.stub(),
|
||||||
sendFile: sinon.stub(),
|
sendFile: Sinon.stub(),
|
||||||
sendfile: sinon.stub(),
|
sendfile: Sinon.stub(),
|
||||||
download: sinon.stub(),
|
download: Sinon.stub(),
|
||||||
contentType: sinon.stub(),
|
contentType: Sinon.stub(),
|
||||||
type: sinon.stub(),
|
type: Sinon.stub(),
|
||||||
format: sinon.stub(),
|
format: Sinon.stub(),
|
||||||
attachment: sinon.stub(),
|
attachment: Sinon.stub(),
|
||||||
set: sinon.stub(),
|
set: Sinon.stub(),
|
||||||
header: sinon.stub(),
|
header: Sinon.stub(),
|
||||||
headersSent: true,
|
headersSent: true,
|
||||||
get: sinon.stub(),
|
get: Sinon.stub(),
|
||||||
clearCookie: sinon.stub(),
|
clearCookie: Sinon.stub(),
|
||||||
cookie: sinon.stub(),
|
cookie: Sinon.stub(),
|
||||||
location: sinon.stub(),
|
location: Sinon.stub(),
|
||||||
redirect: sinon.stub(),
|
redirect: Sinon.stub(),
|
||||||
render: sinon.stub(),
|
render: Sinon.stub(),
|
||||||
locals: sinon.stub(),
|
locals: Sinon.stub(),
|
||||||
charset: "utf-8",
|
charset: "utf-8",
|
||||||
vary: sinon.stub(),
|
vary: Sinon.stub(),
|
||||||
app: sinon.stub(),
|
app: Sinon.stub(),
|
||||||
write: sinon.stub(),
|
write: Sinon.stub(),
|
||||||
writeContinue: sinon.stub(),
|
writeContinue: Sinon.stub(),
|
||||||
writeHead: sinon.stub(),
|
writeHead: Sinon.stub(),
|
||||||
statusCode: 200,
|
statusCode: 200,
|
||||||
statusMessage: "message",
|
statusMessage: "message",
|
||||||
setHeader: sinon.stub(),
|
setHeader: Sinon.stub(),
|
||||||
setTimeout: sinon.stub(),
|
setTimeout: Sinon.stub(),
|
||||||
sendDate: true,
|
sendDate: true,
|
||||||
getHeader: sinon.stub()
|
getHeader: Sinon.stub()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import * as Express from "express";
|
||||||
|
import GetHeader from "./GetHeader";
|
||||||
|
import { RequestMock } from "../stubs/express.spec";
|
||||||
|
import * as Assert from "assert";
|
||||||
|
|
||||||
|
describe('GetHeader', function() {
|
||||||
|
let req: Express.Request;
|
||||||
|
beforeEach(() => {
|
||||||
|
req = RequestMock();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the header if it exists', function() {
|
||||||
|
req.headers["x-target-url"] = 'www.example.com';
|
||||||
|
Assert.equal(GetHeader(req, 'x-target-url'), 'www.example.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return undefined if header does not exist', function() {
|
||||||
|
Assert.equal(GetHeader(req, 'x-target-url'), undefined);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,19 @@
|
||||||
|
import * as Express from "express";
|
||||||
|
import * as ObjectPath from "object-path";
|
||||||
|
import { ServerVariables } from "../ServerVariables";
|
||||||
|
import { GET_VARIABLE_KEY } from "../../../../shared/constants";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param req The express request to extract headers from
|
||||||
|
* @param header The name of the header to extract in lowercase.
|
||||||
|
* @returns The header if found, otherwise undefined.
|
||||||
|
*/
|
||||||
|
export default function(req: Express.Request, header: string): string | undefined {
|
||||||
|
const variables: ServerVariables = req.app.get(GET_VARIABLE_KEY);
|
||||||
|
if (!variables) return undefined;
|
||||||
|
|
||||||
|
const value = ObjectPath.get<Express.Request, string>(req, "headers." + header, undefined);
|
||||||
|
variables.logger.debug(req, "Header %s is set to %s", header, value);
|
||||||
|
return value;
|
||||||
|
}
|
|
@ -1,11 +1,8 @@
|
||||||
import { ServerVariables } from "../ServerVariables";
|
import { ServerVariables } from "../ServerVariables";
|
||||||
import { Level } from "../authentication/Level";
|
|
||||||
import * as URLParse from "url-parse";
|
import * as URLParse from "url-parse";
|
||||||
import { AuthenticationSession } from "AuthenticationSession";
|
|
||||||
|
|
||||||
export default function IsRedirectionSafe(
|
export default function IsRedirectionSafe(
|
||||||
vars: ServerVariables,
|
vars: ServerVariables,
|
||||||
authSession: AuthenticationSession,
|
|
||||||
url: URLParse): boolean {
|
url: URLParse): boolean {
|
||||||
|
|
||||||
const urlInDomain = url.hostname.endsWith(vars.config.session.domain);
|
const urlInDomain = url.hostname.endsWith(vars.config.session.domain);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { DomainExtractor } from "./DomainExtractor";
|
import { DomainExtractor } from "./DomainExtractor";
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
|
||||||
describe.only("shared/DomainExtractor", function () {
|
describe("shared/DomainExtractor", function () {
|
||||||
describe("test fromUrl", function () {
|
describe("test fromUrl", function () {
|
||||||
it("should return domain from https url", function () {
|
it("should return domain from https url", function () {
|
||||||
const domain = DomainExtractor.fromUrl("https://www.example.com/test/abc");
|
const domain = DomainExtractor.fromUrl("https://www.example.com/test/abc");
|
||||||
|
|
|
@ -1 +1,11 @@
|
||||||
export const REDIRECT_QUERY_PARAM = "rd";
|
export const REDIRECT_QUERY_PARAM = "rd";
|
||||||
|
|
||||||
|
// Used as a first factor script parameter for knowing the authorizations
|
||||||
|
// related to the domain and resource.
|
||||||
|
export const HEADER_X_TARGET_URL = "x-target-url";
|
||||||
|
|
||||||
|
export const HEADER_X_ORIGINAL_URL = "x-original-url";
|
||||||
|
export const HEADER_PROXY_AUTHORIZATION = "proxy-authorization";
|
||||||
|
export const HEADER_REDIRECT = "redirect";
|
||||||
|
|
||||||
|
export const GET_VARIABLE_KEY = "variables";
|
|
@ -0,0 +1,3 @@
|
||||||
|
var colors = require('mocha/lib/reporters/base').colors;
|
||||||
|
colors['pass'] = 32;
|
||||||
|
colors['error stack'] = 34;
|
Loading…
Reference in New Issue