Fix unit tests.

pull/330/head
Clement Michaud 2019-02-22 10:27:54 +01:00
parent 50d4ab1368
commit 5614bea827
29 changed files with 551 additions and 1128 deletions

View File

@ -1,49 +0,0 @@
module.exports = function (grunt) {
const buildDir = "dist";
const schemaDir = "server/src/lib/configuration/Configuration.schema.json"
grunt.initConfig({
clean: {
dist: ['dist'],
},
run: {
"test-server-unit": {
cmd: "./node_modules/.bin/mocha",
args: ['--colors', '--require', 'ts-node/register', 'server/src/**/*.spec.ts']
},
"test-shared-unit": {
cmd: "./node_modules/.bin/mocha",
args: ['--colors', '--require', 'ts-node/register', 'shared/**/*.spec.ts']
},
"test-cucumber": {
cmd: "./scripts/run-cucumber.sh",
args: ["./test/features"]
},
"test-complete-config": {
cmd: "./node_modules/.bin/mocha",
args: ['--colors', '--require', 'ts-node/register', 'test/complete-config/**/*.ts']
},
"test-minimal-config": {
cmd: "./node_modules/.bin/mocha",
args: ['--colors', '--require', 'ts-node/register', 'test/minimal-config/**/*.ts']
},
"test-inactivity": {
cmd: "./node_modules/.bin/mocha",
args: ['--colors', '--require', 'ts-node/register', 'test/inactivity/**/*.ts']
},
"apidoc": {
cmd: "./node_modules/.bin/apidoc",
args: ["-i", "src/server", "-o", "doc"]
},
},
});
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-run');
grunt.registerTask('test-server', ['run:test-server-unit'])
grunt.registerTask('test-shared', ['run:test-shared-unit'])
grunt.registerTask('test-unit', ['test-server', 'test-shared']);
grunt.registerTask('test-int', ['run:test-cucumber', 'run:test-minimal-config', 'run:test-complete-config', 'run:test-inactivity']);
};

View File

@ -7,6 +7,7 @@ import CircleLoader, { Status } from "../CircleLoader/CircleLoader";
export interface OwnProps { export interface OwnProps {
username: string; username: string;
redirectionUrl: string;
} }
export interface DispatchProps { export interface DispatchProps {
@ -26,6 +27,7 @@ class AlreadyAuthenticated extends Component<Props> {
</div> </div>
<div className={styles.statusIcon}><CircleLoader status={Status.SUCCESSFUL} /></div> <div className={styles.statusIcon}><CircleLoader status={Status.SUCCESSFUL} /></div>
</div> </div>
<a href={this.props.redirectionUrl}>{this.props.redirectionUrl}</a>
<div className={styles.logoutButtonContainer}> <div className={styles.logoutButtonContainer}>
<Button <Button
onClick={this.props.onLogoutClicked} onClick={this.props.onLogoutClicked}

View File

@ -39,7 +39,8 @@ class AuthenticationView extends Component<Props> {
redirectionUrl={this.props.redirectionUrl} />; redirectionUrl={this.props.redirectionUrl} />;
} else if (this.props.stage === Stage.ALREADY_AUTHENTICATED) { } else if (this.props.stage === Stage.ALREADY_AUTHENTICATED) {
return <AlreadyAuthenticated return <AlreadyAuthenticated
username={this.props.remoteState.username}/>; username={this.props.remoteState.username}
redirectionUrl={this.props.remoteState.default_redirection_url} />;
} }
return <FirstFactorForm redirectionUrl={this.props.redirectionUrl} />; return <FirstFactorForm redirectionUrl={this.props.redirectionUrl} />;
} }

View File

@ -3,6 +3,7 @@ import AuthenticationLevel from '../../types/AuthenticationLevel';
interface RemoteState { interface RemoteState {
username: string; username: string;
authentication_level: AuthenticationLevel; authentication_level: AuthenticationLevel;
default_redirection_url: string;
} }
export default RemoteState; export default RemoteState;

1410
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
"scripts": { "scripts": {
"start": "./scripts/authelia-scripts start", "start": "./scripts/authelia-scripts start",
"build": "./scripts/authelia-scripts build", "build": "./scripts/authelia-scripts build",
"unittest": "./scripts/authelia-scripts unittest",
"test": "./scripts/authelia-scripts test", "test": "./scripts/authelia-scripts test",
"cover": "NODE_ENV=test nyc npm t" "cover": "NODE_ENV=test nyc npm t"
}, },
@ -59,13 +60,12 @@
"@types/bluebird": "^3.5.4", "@types/bluebird": "^3.5.4",
"@types/body-parser": "^1.16.3", "@types/body-parser": "^1.16.3",
"@types/connect-redis": "0.0.7", "@types/connect-redis": "0.0.7",
"@types/cucumber": "^4.0.1",
"@types/ejs": "^2.3.33", "@types/ejs": "^2.3.33",
"@types/express": "^4.0.35", "@types/express": "^4.0.35",
"@types/express-session": "1.15.8", "@types/express-session": "1.15.8",
"@types/helmet": "0.0.37", "@types/helmet": "0.0.37",
"@types/ldapjs": "^1.0.3", "@types/ldapjs": "^1.0.3",
"@types/mocha": "^5.0.0", "@types/mocha": "^5.2.6",
"@types/mockdate": "^2.0.0", "@types/mockdate": "^2.0.0",
"@types/mongodb": "^3.0.9", "@types/mongodb": "^3.0.9",
"@types/nedb": "^1.8.3", "@types/nedb": "^1.8.3",
@ -90,7 +90,6 @@
"chromedriver": "^2.37.0", "chromedriver": "^2.37.0",
"commander": "^2.19.0", "commander": "^2.19.0",
"concurrently": "^4.1.0", "concurrently": "^4.1.0",
"cucumber": "^4.0.0",
"grunt": "^1.0.3", "grunt": "^1.0.3",
"grunt-contrib-clean": "^2.0.0", "grunt-contrib-clean": "^2.0.0",
"grunt-contrib-copy": "^1.0.0", "grunt-contrib-copy": "^1.0.0",
@ -101,7 +100,7 @@
"mockdate": "^2.0.1", "mockdate": "^2.0.1",
"node-fetch": "^2.3.0", "node-fetch": "^2.3.0",
"nodemon": "^1.18.9", "nodemon": "^1.18.9",
"nyc": "^13.1.0", "nyc": "^13.3.0",
"query-string": "^6.0.0", "query-string": "^6.0.0",
"readable-stream": "^2.3.3", "readable-stream": "^2.3.3",
"request": "^2.88.0", "request": "^2.88.0",

View File

@ -9,7 +9,8 @@ program
.command('build', 'Build production version of Authelia from source.') .command('build', 'Build production version of Authelia from source.')
.command('clean', 'Clean the production version of Authelia.') .command('clean', 'Clean the production version of Authelia.')
.command('serve', 'Serve production version of Authelia.') .command('serve', 'Serve production version of Authelia.')
.command('test', 'Run Authelia unit tests.') .command('test', 'Run Authelia integration tests.')
.command('unittest', 'Run Authelia integration tests.')
.command('build-docker', 'Build Docker image containing production version of Authelia.') .command('build-docker', 'Build Docker image containing production version of Authelia.')
.command('publish-docker', 'Publish Docker image containing production version of Authelia to Dockerhub.') .command('publish-docker', 'Publish Docker image containing production version of Authelia to Dockerhub.')

View File

@ -16,9 +16,10 @@ if (!program.suite) {
const ENVIRONMENT_FILENAME = '.suite'; const ENVIRONMENT_FILENAME = '.suite';
const AUTHELIA_INTERRUPT_FILENAME = '.authelia-interrupt'; const AUTHELIA_INTERRUPT_FILENAME = '.authelia-interrupt';
const CONFIG_FILEPATH = `test/suites/${program.suite}/config.yml`;
var tsWatcher = chokidar.watch(['server', 'shared/**/*.ts', 'node_modules', AUTHELIA_INTERRUPT_FILENAME], { var tsWatcher = chokidar.watch(['server', 'shared/**/*.ts', 'node_modules', AUTHELIA_INTERRUPT_FILENAME, CONFIG_FILEPATH], {
persistent: true, persistent: true,
ignoreInitial: true, ignoreInitial: true,
}); });
@ -46,7 +47,7 @@ function startServer() {
} }
exec('./node_modules/.bin/tslint', ['-c', 'server/tslint.json', '-p', 'server/tsconfig.json']) exec('./node_modules/.bin/tslint', ['-c', 'server/tslint.json', '-p', 'server/tsconfig.json'])
.then(function() { .then(function() {
serverProcess = spawn('./scripts/run-dev-server.sh', [`test/suites/${program.suite}/config.yml`]); serverProcess = spawn('./scripts/run-dev-server.sh', [CONFIG_FILEPATH]);
serverProcess.stdout.pipe(process.stdout); serverProcess.stdout.pipe(process.stdout);
serverProcess.stderr.pipe(process.stderr); serverProcess.stderr.pipe(process.stderr);
}); });

View File

@ -25,9 +25,9 @@ else if (program.args.length > 0) {
args = program.args; args = program.args;
// Render the production version of the nginx portal configuration // Render the production version of the nginx portal configuration
// execSync('./example/compose/nginx/portal/render.js --production'); execSync('./example/compose/nginx/portal/render.js --production');
// Prepare the environment // Prepare the environment
// execSync('./scripts/utils/prepare-environment.sh'); execSync('./scripts/utils/prepare-environment.sh');
} }
else { else {
console.log('No suite detected but no tests have been selected...'); console.log('No suite detected but no tests have been selected...');

View File

@ -0,0 +1,40 @@
#!/usr/bin/env node
var program = require('commander');
var spawn = require('child_process').spawn;
program
.parse(process.argv);
async function runTests(pattern) {
return new Promise((resolve, reject) => {
mocha = spawn('./node_modules/.bin/mocha', ['--colors', '--require', 'ts-node/register', pattern], {
env: {
...process.env,
'TS_NODE_PROJECT': './server/tsconfig.json'
}
});
mocha.stdout.pipe(process.stdout);
mocha.stderr.pipe(process.stderr);
mocha.on('exit', (status) => {
if (status == 0) {
resolve();
}
reject(new Error('Status code ' + status));
});
});
}
async function test() {
await runTests('server/src/**/*.spec.ts');
await runTests('shared/**/*.spec.ts');
}
test()
.then(() => {
process.exit(0);
}, (err) => {
process.exit(1);
});

View File

@ -1,3 +0,0 @@
#!/bin/bash
WITH_SERVER=n TS_NODE_PROJECT=test/tsconfig.json ./node_modules/.bin/mocha --exit --colors --require ts-node/register $*

View File

@ -1,5 +0,0 @@
#!/bin/bash
REQ=`for f in test/features/step_definitions/*.ts; do echo "--require $f"; done;`
./node_modules/.bin/cucumber-js --format-options '{"colorsEnabled": true}' --require-module ts-node/register --require test/features/support/world.ts $REQ $*

View File

@ -1,3 +1,6 @@
#!/bin/sh #!/bin/sh
# Starts the server with the provided configuration in $1
# This scripts is called from authelia-scripts.
./node_modules/.bin/ts-node -P ./server/tsconfig.json ./server/src/index.ts $* ./node_modules/.bin/ts-node -P ./server/tsconfig.json ./server/src/index.ts $*

View File

@ -10,13 +10,13 @@ echo "node `node -v`"
echo "npm `npm -v`" echo "npm `npm -v`"
# Run unit tests # Run unit tests
authelia-scripts test authelia-scripts unittest
# Run integration tests
authelia-scripts test --headless test/suites/**/*.ts
# Build # Build
authelia-scripts build authelia-scripts build
# Run integration/example tests
./scripts/integration-tests.sh
# Test npm deployment before actual deployment # Test npm deployment before actual deployment
# ./scripts/npm-deployment-test.sh # ./scripts/npm-deployment-test.sh

View File

@ -1,6 +1,6 @@
import sinon = require("sinon"); import sinon = require("sinon");
import IdentityValidator = require("./IdentityCheckMiddleware"); import * as IdentityCheckMiddleware from "./IdentityCheckMiddleware";
import exceptions = require("./Exceptions"); import exceptions = require("./Exceptions");
import { ServerVariables } from "./ServerVariables"; import { ServerVariables } from "./ServerVariables";
import Assert = require("assert"); import Assert = require("assert");
@ -10,6 +10,7 @@ import ExpressMock = require("./stubs/express.spec");
import { IdentityValidableStub } from "./IdentityValidableStub.spec"; import { IdentityValidableStub } from "./IdentityValidableStub.spec";
import { ServerVariablesMock, ServerVariablesMockBuilder } import { ServerVariablesMock, ServerVariablesMockBuilder }
from "./ServerVariablesMockBuilder.spec"; from "./ServerVariablesMockBuilder.spec";
import { OPERATION_FAILED } from "../../../shared/UserMessages";
describe("IdentityCheckMiddleware", function () { describe("IdentityCheckMiddleware", function () {
let req: ExpressMock.RequestMock; let req: ExpressMock.RequestMock;
@ -60,8 +61,8 @@ throws a first factor error", function () {
identityValidable.preValidationInitStub.returns(BluebirdPromise.reject( identityValidable.preValidationInitStub.returns(BluebirdPromise.reject(
new exceptions.FirstFactorValidationError( new exceptions.FirstFactorValidationError(
"Error during prevalidation"))); "Error during prevalidation")));
const callback = IdentityValidator.get_start_validation( const callback = IdentityCheckMiddleware.post_start_validation(
identityValidable, "/endpoint", vars); identityValidable, vars);
return callback(req as any, res as any, undefined) return callback(req as any, res as any, undefined)
.then(() => { .then(() => {
@ -75,8 +76,8 @@ throws a first factor error", function () {
identityValidable.preValidationInitStub identityValidable.preValidationInitStub
.returns(BluebirdPromise.resolve(identity)); .returns(BluebirdPromise.resolve(identity));
const callback = IdentityValidator const callback = IdentityCheckMiddleware
.get_start_validation(identityValidable, "/endpoint", vars); .post_start_validation(identityValidable, vars);
return callback(req as any, res as any, undefined) return callback(req as any, res as any, undefined)
.then(function () { .then(function () {
@ -87,13 +88,12 @@ throws a first factor error", function () {
// 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",
function () { function () {
const endpoint = "/protected";
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 = IdentityValidator const callback = IdentityCheckMiddleware
.get_start_validation(identityValidable, "/endpoint", vars); .post_start_validation(identityValidable, vars);
return callback(req as any, res as any, undefined) return callback(req as any, res as any, undefined)
.then(function () { .then(function () {
@ -101,52 +101,49 @@ throws a first factor error", function () {
}); });
}); });
it("should issue a token, send an email and return 204", function () { it("should issue a token, send an email and return 204", async function () {
const endpoint = "/protected";
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));
const callback = IdentityValidator const callback = IdentityCheckMiddleware
.get_start_validation(identityValidable, "/finish_endpoint", 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(mocks.notifier.notifyStub.calledOnce);
Assert(mocks.notifier.notifyStub.calledOnce); Assert(mocks.userDataStore.produceIdentityValidationTokenStub
Assert(mocks.userDataStore.produceIdentityValidationTokenStub .calledOnce);
.calledOnce); Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub
Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub .getCall(0).args[0], "user");
.getCall(0).args[0], "user"); Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub
Assert.equal(mocks.userDataStore.produceIdentityValidationTokenStub .getCall(0).args[3], 240000);
.getCall(0).args[3], 240000);
});
}); });
}); });
describe("test finish GET", function () { describe("test finish GET", function () {
it("should send 401 if no identity_token is provided", () => { it("should return an error if no identity_token is provided", () => {
const callback = IdentityValidator const callback = IdentityCheckMiddleware
.get_finish_validation(identityValidable, vars); .post_finish_validation(identityValidable, vars);
return callback(req as any, res as any, undefined) return callback(req as any, res as any, undefined)
.then(function () { .then(function () {
Assert(res.redirect.calledWith("/error/401")); Assert(res.status.calledWith(200));
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", function () { valid", function () {
req.query.identity_token = "token"; req.query.identity_token = "token";
const callback = IdentityCheckMiddleware
const callback = IdentityValidator .post_finish_validation(identityValidable, vars);
.get_finish_validation(identityValidable, vars);
return callback(req as any, res as any, undefined); return callback(req as any, res as any, undefined);
}); });
it("should return 401 if identity_token is provided but invalid", it("should return an error if identity_token is provided but invalid",
function () { function () {
req.query.identity_token = "token"; req.query.identity_token = "token";
@ -156,11 +153,12 @@ valid", function () {
mocks.userDataStore.consumeIdentityValidationTokenStub mocks.userDataStore.consumeIdentityValidationTokenStub
.returns(BluebirdPromise.reject(new Error("Invalid token"))); .returns(BluebirdPromise.reject(new Error("Invalid token")));
const callback = IdentityValidator const callback = IdentityCheckMiddleware
.get_finish_validation(identityValidable, vars); .post_finish_validation(identityValidable, vars);
return callback(req as any, res as any, undefined) return callback(req as any, res as any, undefined)
.then(() => { .then(() => {
Assert(res.redirect.calledWith("/error/401")); Assert(res.status.calledWith(200));
Assert(res.send.calledWith({'error': OPERATION_FAILED}));
}); });
}); });
}); });

View File

@ -110,7 +110,7 @@ export function post_start_validation(handler: IdentityValidable,
return createAndSaveToken(userid, handler.challenge(), return createAndSaveToken(userid, handler.challenge(),
vars.userDataStore); vars.userDataStore);
}) })
.then((token) => { .then((token: string) => {
const host = req.get("Host"); const host = req.get("Host");
const link_url = util.format("https://%s%s?token=%s", host, const link_url = util.format("https://%s%s?token=%s", host,
handler.destinationPath(), token); handler.destinationPath(), token);
@ -124,6 +124,7 @@ export function post_start_validation(handler: IdentityValidable,
return BluebirdPromise.resolve(); return BluebirdPromise.resolve();
}) })
.catch(Exceptions.IdentityError, (err: Error) => { .catch(Exceptions.IdentityError, (err: Error) => {
vars.logger.error(req, err.message);
handler.preValidationResponse(req, res); handler.preValidationResponse(req, res);
return BluebirdPromise.resolve(); return BluebirdPromise.resolve();
}) })

View File

@ -24,6 +24,7 @@ export class IdentityValidableStub implements IdentityValidable {
this.postValidationResponseStub = Sinon.stub(); this.postValidationResponseStub = Sinon.stub();
this.mailSubjectStub = Sinon.stub(); this.mailSubjectStub = Sinon.stub();
this.destinationPathStub = Sinon.stub();
} }
challenge(): string { challenge(): string {

View File

@ -6,11 +6,10 @@ 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 Endpoints = require("../../../../../shared/api");
import AuthenticationRegulatorMock = require("../../regulation/RegulatorStub.spec");
import ExpressMock = require("../../stubs/express.spec"); import ExpressMock = require("../../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";
describe("routes/firstfactor/post", function () { describe("routes/firstfactor/post", function () {
let req: ExpressMock.RequestMock; let req: ExpressMock.RequestMock;
@ -73,7 +72,7 @@ describe("routes/firstfactor/post", function () {
emails: emails, emails: emails,
groups: groups groups: groups
})); }));
req.body.keepMeLoggedIn = "true"; req.body.keepMeLoggedIn = true;
return FirstFactorPost.default(vars)(req as any, res as any); return FirstFactorPost.default(vars)(req as any, res as any);
}); });
@ -108,7 +107,7 @@ describe("routes/firstfactor/post", function () {
it("should return error message when LDAP authenticator throws", function () { it("should return error message when LDAP authenticator throws", function () {
mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password") mocks.usersDatabase.checkUserPasswordStub.withArgs("username", "password")
.returns(BluebirdPromise.reject(new exceptions.LdapBindError("Bad credentials"))); .returns(BluebirdPromise.reject(new AuthenticationError("Bad credentials")));
return FirstFactorPost.default(vars)(req as any, res as any) return FirstFactorPost.default(vars)(req as any, res as any)
.then(function () { .then(function () {

View File

@ -1,5 +1,3 @@
import Exceptions = require("../../Exceptions");
import * as ObjectPath from "object-path"; import * as ObjectPath from "object-path";
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
import express = require("express"); import express = require("express");
@ -98,8 +96,8 @@ export default function (vars: ServerVariables) {
}) })
.catch(AuthenticationError, function (err: Error) { .catch(AuthenticationError, function (err: Error) {
vars.regulator.mark(username, false); vars.regulator.mark(username, false);
return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.AUTHENTICATION_FAILED)(err); return ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED)(err);
}) })
.catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.AUTHENTICATION_FAILED)); .catch(ErrorReplies.replyWithError200(req, res, vars.logger, UserMessages.OPERATION_FAILED));
}; };
} }

View File

@ -1,10 +1,6 @@
import PasswordResetHandler import PasswordResetHandler
from "./PasswordResetHandler"; from "./PasswordResetHandler";
import { UserDataStore } from "../../../storage/UserDataStore";
import Sinon = require("sinon");
import winston = require("winston");
import assert = require("assert");
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 }
@ -13,15 +9,14 @@ import { ServerVariables } from "../../../ServerVariables";
describe("routes/password-reset/identity/PasswordResetHandler", function () { describe("routes/password-reset/identity/PasswordResetHandler", function () {
let req: ExpressMock.RequestMock; let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let mocks: ServerVariablesMock; let mocks: ServerVariablesMock;
let vars: ServerVariables; let vars: ServerVariables;
beforeEach(function () { beforeEach(function () {
req = { req = {
originalUrl: "/non-api/xxx", originalUrl: "/non-api/xxx",
query: { body: {
userid: "user" username: "user"
}, },
session: { session: {
auth: { auth: {
@ -36,10 +31,6 @@ describe("routes/password-reset/identity/PasswordResetHandler", function () {
} }
}; };
const options = {
inMemoryOnly: true
};
const s = ServerVariablesMockBuilder.build(); const s = ServerVariablesMockBuilder.build();
mocks = s.mocks; mocks = s.mocks;
vars = s.variables; vars = s.variables;
@ -52,12 +43,11 @@ describe("routes/password-reset/identity/PasswordResetHandler", function () {
.returns(BluebirdPromise.resolve({})); .returns(BluebirdPromise.resolve({}));
mocks.userDataStore.consumeIdentityValidationTokenStub mocks.userDataStore.consumeIdentityValidationTokenStub
.returns(BluebirdPromise.resolve({})); .returns(BluebirdPromise.resolve({}));
res = ExpressMock.ResponseMock();
}); });
describe("test reset password identity pre check", () => { describe("test reset password identity pre check", () => {
it("should fail when no userid is provided", function () { it("should fail when no userid is provided", function () {
req.query.userid = undefined; req.body.username = undefined;
const handler = new PasswordResetHandler(vars.logger, const handler = new PasswordResetHandler(vars.logger,
vars.usersDatabase); vars.usersDatabase);
return handler.preValidationInit(req as any) return handler.preValidationInit(req as any)

View File

@ -29,10 +29,10 @@ export default class PasswordResetHandler implements IdentityValidable {
return BluebirdPromise.resolve() return BluebirdPromise.resolve()
.then(function () { .then(function () {
that.logger.debug(req, "User '%s' requested a password reset", userid); that.logger.debug(req, "User '%s' requested a password reset", userid);
if (!userid) if (!userid) {
return BluebirdPromise.reject( return BluebirdPromise.reject(
new exceptions.AccessDeniedError("No user id provided")); new exceptions.AccessDeniedError("No user id provided"));
}
return that.usersDatabase.getEmails(userid); return that.usersDatabase.getEmails(userid);
}) })
.then(function (emails: string[]) { .then(function (emails: string[]) {

View File

@ -34,7 +34,7 @@ export default function (vars: ServerVariables) {
return Bluebird.resolve(); return Bluebird.resolve();
}) })
.catch(ErrorReplies.replyWithError200(req, res, vars.logger, .catch(ErrorReplies.replyWithError200(req, res, vars.logger,
UserMessages.AUTHENTICATION_TOTP_FAILED)); UserMessages.OPERATION_FAILED));
} }
return handler; return handler;
} }

View File

@ -36,10 +36,6 @@ describe("routes/secondfactor/u2f/register_request/get", 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({}));
@ -63,7 +59,6 @@ describe("routes/secondfactor/u2f/register_request/get", function () {
it("should return internal error on registration request", function () { it("should return internal error on registration request", function () {
res.send = sinon.spy(); res.send = sinon.spy();
const user_key_container = {};
mocks.u2f.requestStub.returns(BluebirdPromise.reject("Internal error")); mocks.u2f.requestStub.returns(BluebirdPromise.reject("Internal error"));
return U2FRegisterRequestGet.default(vars)(req as any, res as any) return U2FRegisterRequestGet.default(vars)(req as any, res as any)
.then(function () { .then(function () {
@ -78,7 +73,10 @@ describe("routes/secondfactor/u2f/register_request/get", function () {
req.session.auth.identity_check = undefined; req.session.auth.identity_check = undefined;
return U2FRegisterRequestGet.default(vars)(req as any, res as any) return U2FRegisterRequestGet.default(vars)(req as any, res as any)
.then(function () { .then(function () {
Assert.equal(403, res.status.getCall(0).args[0]); Assert.equal(200, res.status.getCall(0).args[0]);
Assert.deepEqual(res.send.getCall(0).args[0], {
error: "Operation failed."
});
}); });
}); });
}); });

View File

@ -4,12 +4,9 @@ import BluebirdPromise = require("bluebird");
import Assert = require("assert"); import Assert = require("assert");
import U2FSignPost = require("./post"); import U2FSignPost = require("./post");
import { ServerVariables } from "../../../../ServerVariables"; import { ServerVariables } from "../../../../ServerVariables";
import winston = require("winston"); import UserMessages = require("../../../../../../../shared/UserMessages");
import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec"; import { ServerVariablesMockBuilder, ServerVariablesMock } from "../../../../ServerVariablesMockBuilder.spec";
import ExpressMock = require("../../../../stubs/express.spec"); import ExpressMock = require("../../../../stubs/express.spec");
import U2FMock = require("../../../../stubs/u2f.spec");
import U2f = require("u2f");
import { Level } from "../../../../authentication/Level"; import { Level } from "../../../../authentication/Level";
describe("routes/secondfactor/u2f/sign/post", function () { describe("routes/secondfactor/u2f/sign/post", function () {
@ -40,10 +37,6 @@ describe("routes/secondfactor/u2f/sign/post", function () {
req.headers = {}; req.headers = {};
req.headers.host = "localhost"; req.headers.host = "localhost";
const options = {
inMemoryOnly: true
};
res = ExpressMock.ResponseMock(); res = ExpressMock.ResponseMock();
res.send = sinon.spy(); res.send = sinon.spy();
res.json = sinon.spy(); res.json = sinon.spy();
@ -94,7 +87,7 @@ describe("routes/secondfactor/u2f/sign/post", function () {
.then(function () { .then(function () {
Assert.equal(res.status.getCall(0).args[0], 200); Assert.equal(res.status.getCall(0).args[0], 200);
Assert.deepEqual(res.send.getCall(0).args[0], Assert.deepEqual(res.send.getCall(0).args[0],
{ error: "Operation failed." }); { error: UserMessages.OPERATION_FAILED });
}); });
}); });
}); });

View File

@ -1,4 +1,3 @@
import objectPath = require("object-path"); import objectPath = require("object-path");
import u2f_common = require("../U2FCommon"); import u2f_common = require("../U2FCommon");
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
@ -46,7 +45,7 @@ export default function (vars: ServerVariables) {
return BluebirdPromise.resolve(); return BluebirdPromise.resolve();
}) })
.catch(ErrorReplies.replyWithError200(req, res, vars.logger, .catch(ErrorReplies.replyWithError200(req, res, vars.logger,
UserMessages.AUTHENTICATION_U2F_FAILED)); UserMessages.OPERATION_FAILED));
} }
return handler; return handler;

View File

@ -9,7 +9,8 @@ export default function (vars: ServerVariables) {
const authSession = AuthenticationSessionHandler.get(req, vars.logger); const authSession = AuthenticationSessionHandler.get(req, vars.logger);
res.json({ res.json({
username: authSession.userid, username: authSession.userid,
authentication_level: authSession.authentication_level authentication_level: authSession.authentication_level,
default_redirection_url: vars.config.default_redirection_url,
}); });
resolve(); resolve();
}); });

View File

@ -5,7 +5,7 @@ import VerifyForwardedHeaderIs from "../../../helpers/assertions/VerifyForwarded
import LoginOneFactor from "../../../helpers/behaviors/LoginOneFactor"; import LoginOneFactor from "../../../helpers/behaviors/LoginOneFactor";
export default function() { export default function() {
describe("Custom-Forwarded-User and Custom-Forwarded-Groups are correctly forwarded to protected backend", function() { describe.only("Custom-Forwarded-User and Custom-Forwarded-Groups are correctly forwarded to protected backend", function() {
this.timeout(10000); this.timeout(10000);
describe("With single factor", function() { describe("With single factor", function() {

View File

@ -14,7 +14,7 @@ export default function() {
it("should be able to login after mongo restarts", async function() { it("should be able to login after mongo restarts", async function() {
this.timeout(30000); this.timeout(30000);
const secret = await LoginAndRegisterTotp(this.driver, "john", true); const secret = await LoginAndRegisterTotp(this.driver, "john", "password", true);
child_process.execSync("./scripts/dc-dev.sh restart mongo"); child_process.execSync("./scripts/dc-dev.sh restart mongo");
await FullLogin(this.driver, "john", secret, "https://admin.example.com:8080/secret.html"); await FullLogin(this.driver, "john", secret, "https://admin.example.com:8080/secret.html");
}); });

View File

@ -6,6 +6,8 @@ port: 9091
logs_level: debug logs_level: debug
default_redirection_url: https://home.example.com:8080/
authentication_backend: authentication_backend:
file: file:
path: ./users_database.yml path: ./users_database.yml