diff --git a/Gruntfile.js b/Gruntfile.js
index 775c85d3..0c977751 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -13,16 +13,20 @@ module.exports = function (grunt) {
args: ['-c', 'tslint.json', '-p', 'tsconfig.json']
},
"test": {
- cmd: "npm",
- args: ['run', 'test']
+ cmd: "./node_modules/.bin/mocha",
+ args: ['--compilers', 'ts:ts-node/register', '--recursive', 'test/client', 'test/server']
+ },
+ "test-int": {
+ cmd: "./node_modules/.bin/mocha",
+ args: ['--compilers', 'ts:ts-node/register', '--recursive', 'test/integration']
},
"docker-build": {
cmd: "docker",
args: ['build', '-t', 'clems4ever/authelia', '.']
},
"docker-restart": {
- cmd: "docker-compose",
- args: ['-f', 'docker-compose.yml', '-f', 'docker-compose.dev.yml', 'restart', 'auth']
+ cmd: "./scripts/dc-example.sh",
+ args: ['up', '-d']
},
"minify": {
cmd: "./node_modules/.bin/uglifyjs",
@@ -109,7 +113,7 @@ module.exports = function (grunt) {
},
client: {
files: ['src/client/**/*.ts', 'test/client/**/*.ts'],
- tasks: ['build'],
+ tasks: ['build-dev'],
options: {
interrupt: true,
atBegin: true
@@ -117,9 +121,10 @@ module.exports = function (grunt) {
},
server: {
files: ['src/server/**/*.ts', 'test/server/**/*.ts'],
- tasks: ['build', 'run:docker-restart', 'run:make-dev-views' ],
+ tasks: ['build-dev', 'run:docker-restart', 'run:make-dev-views' ],
options: {
interrupt: true,
+ atBegin: true
}
}
},
diff --git a/README.md b/README.md
index 450c7106..14108ddf 100644
--- a/README.md
+++ b/README.md
@@ -7,13 +7,37 @@
nginx. It has been made to work with nginx [auth_request] module and is currently
used in production to secure internal services in a small docker swarm cluster.
-## Features
+# Table of Contents
+1. [Features summary](#features-summary)
+2. [Deployment](#deployment)
+ 1. [With NPM](#with-npm)
+ 2. [With Docker](#with-docker)
+3. [Getting started](#getting-started)
+ 1. [Pre-requisites](#pre-requisites)
+ 2. [Run it!](#run-it)
+4. [Features in details](#features-in-details)
+ 1. [First factor with LDAP and ACL](#first-factor-with-ldap-and-acl)
+ 2. [Second factor with TOTP](#second-factor-with-totp)
+ 3. [Second factor with U2F security keys](#second-factor-with-u2f-security-keys)
+ 4. [Password reset](#password-reset)
+ 5. [Access control](#access-control)
+ 6. [Session management with Redis](#session-management-with-redis)
+4. [Documentation](#documentation)
+ 1. [Authelia configuration](#authelia-configuration)
+ 1. [API documentation](#api-documentation)
+5. [Contributing to Authelia](#contributing-to-authelia)
+6. [License](#license)
+
+---
+
+## Features summary
* Two-factor authentication using either
**[TOTP] - Time-Base One Time password -** or **[U2F] - Universal 2-Factor -**
as 2nd factor.
* Password reset with identity verification by sending links to user email
address.
* Access restriction after too many authentication attempts.
+* Session management using Redis key/value store.
## Deployment
@@ -73,7 +97,7 @@ Add the following lines to your **/etc/hosts** to alias multiple subdomains so t
127.0.0.1 mx2.mail.test.local
127.0.0.1 auth.test.local
-### Deployment
+### Run it!
Deploy **Authelia** example with the following command:
@@ -93,7 +117,9 @@ Below is what the login page looks like:
-### First factor: LDAP and ACL
+## Features in details
+
+### First factor with LDAP and ACL
An LDAP server has been deployed for you with the following credentials and
access control list:
@@ -117,8 +143,8 @@ your credentials are wrong.
-### Second factor: TOTP (Time-Base One Time Password)
-In **Authelia**, you need to register a per user TOTP secret before
+### Second factor with TOTP
+In **Authelia**, you need to register a per user TOTP (Time-Based One Time Password) secret before
authenticating. To do that, you need to click on the register button. It will
send a link to the user email address. Since this is an example, no email will
be sent, the link is rather delivered in the file
@@ -129,8 +155,8 @@ to store them and get the generated tokens with the app.
-### 2nd factor: U2F (Universal 2-Factor) with security keys
-**Authelia** also offers authentication using U2F devices like [Yubikey](Yubikey)
+### Second factor with U2F security keys
+**Authelia** also offers authentication using U2F (Universal 2-Factor) devices like [Yubikey](Yubikey)
USB security keys. U2F is one of the most secure authentication protocol and is
already available for Google, Facebook, Github accounts and more.
@@ -160,8 +186,11 @@ the user access to some subdomains. Those rules are defined in the
configuration file and can be set either for everyone, per-user or per-group policies.
Check out the *config.template.yml* to see how they are defined.
+### Session management with Redis
+When your users authenticate against Authelia, sessions are stored in a Redis key/value store. You can specify your own Redis instance in the [configuration file](#authelia-configuration).
+
## Documentation
-### Configuration
+### Authelia configuration
The configuration of the server is defined in the file
**configuration.template.yml**. All the details are documented there.
You can specify another configuration file by giving it as first argument of
diff --git a/config.template.yml b/config.template.yml
index acff1362..c44ac688 100644
--- a/config.template.yml
+++ b/config.template.yml
@@ -73,7 +73,9 @@ session:
secret: unsecure_secret
expiration: 3600000
domain: test.local
-
+ redis:
+ host: redis
+ port: 6379
# The directory where the DB files will be saved
store_directory: /var/lib/authelia/store
diff --git a/docker-compose.yml b/docker-compose.yml
index deee95c9..ace1d5db 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -6,6 +6,7 @@ services:
volumes:
- ./config.template.yml:/etc/authelia/config.yml:ro
- ./notifications:/var/lib/authelia/notifications
+ depends_on:
+ - redis
networks:
- example-network
-
diff --git a/example/redis/docker-compose.yml b/example/redis/docker-compose.yml
new file mode 100644
index 00000000..5e415362
--- /dev/null
+++ b/example/redis/docker-compose.yml
@@ -0,0 +1,6 @@
+version: '2'
+services:
+ redis:
+ image: redis
+ networks:
+ - example-network
diff --git a/package.json b/package.json
index 9dd508f4..fa8d9788 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,7 @@
"authelia": "dist/src/server/index.js"
},
"scripts": {
- "test": "./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/client test/server",
+ "test": "./node_modules/.bin/grunt test",
"cover": "NODE_ENV=test nyc npm t",
"serve": "node dist/server/index.js"
},
@@ -27,6 +27,7 @@
"@types/cors": "^2.8.1",
"bluebird": "^3.4.7",
"body-parser": "^1.15.2",
+ "connect-redis": "^3.3.0",
"dovehash": "0.0.5",
"ejs": "^2.5.5",
"express": "^4.14.0",
@@ -45,6 +46,7 @@
"devDependencies": {
"@types/bluebird": "^3.5.4",
"@types/body-parser": "^1.16.3",
+ "@types/connect-redis": "0.0.6",
"@types/ejs": "^2.3.33",
"@types/express": "^4.0.35",
"@types/express-session": "0.0.32",
@@ -59,7 +61,7 @@
"@types/proxyquire": "^1.3.27",
"@types/query-string": "^4.3.1",
"@types/randomstring": "^1.1.5",
- "@types/request": "0.0.45",
+ "@types/request": "0.0.46",
"@types/sinon": "^2.2.1",
"@types/speakeasy": "^2.0.1",
"@types/tmp": "0.0.33",
diff --git a/scripts/check-services.sh b/scripts/check-services.sh
index 773ffca5..a1085e56 100755
--- a/scripts/check-services.sh
+++ b/scripts/check-services.sh
@@ -2,7 +2,7 @@
service_count=`docker ps -a | grep "Up " | wc -l`
-if [ "${service_count}" -eq "3" ]
+if [ "${service_count}" -eq "4" ]
then
echo "Service are up and running."
exit 0
diff --git a/scripts/dc-example.sh b/scripts/dc-example.sh
index 1abd427b..4b04a22e 100755
--- a/scripts/dc-example.sh
+++ b/scripts/dc-example.sh
@@ -2,4 +2,4 @@
set -e
-docker-compose -f docker-compose.base.yml -f docker-compose.yml -f example/nginx/docker-compose.yml -f example/ldap/docker-compose.yml $*
+docker-compose -f docker-compose.base.yml -f docker-compose.yml -f example/redis/docker-compose.yml -f example/nginx/docker-compose.yml -f example/ldap/docker-compose.yml $*
diff --git a/scripts/dc-test.sh b/scripts/dc-test.sh
index df285b0c..533f363c 100755
--- a/scripts/dc-test.sh
+++ b/scripts/dc-test.sh
@@ -2,4 +2,4 @@
set -e
-docker-compose -f docker-compose.base.yml -f example/ldap/docker-compose.yml -f test/integration/docker-compose.yml $*
+docker-compose -f docker-compose.base.yml -f example/redis/docker-compose.yml -f example/ldap/docker-compose.yml -f test/integration/docker-compose.yml $*
diff --git a/scripts/run-int-test.sh b/scripts/run-int-test.sh
index efa09a77..cf3df97a 100755
--- a/scripts/run-int-test.sh
+++ b/scripts/run-int-test.sh
@@ -6,7 +6,9 @@ echo "Build services images..."
./scripts/dc-test.sh build
echo "Start services..."
-./scripts/dc-test.sh up -d authelia nginx openldap
+./scripts/dc-test.sh up -d redis openldap
+sleep 2
+./scripts/dc-test.sh up -d authelia nginx
sleep 3
docker ps -a
diff --git a/src/server/index.ts b/src/server/index.ts
index 68fa6e20..70f2b72c 100755
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -3,31 +3,33 @@
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
import Server from "./lib/Server";
+import { GlobalDependencies } from "../types/Dependencies";
const YAML = require("yamljs");
-const config_path = process.argv[2];
-if (!config_path) {
+const configurationFilepath = process.argv[2];
+if (!configurationFilepath) {
console.log("No config file has been provided.");
console.log("Usage: authelia ");
process.exit(0);
}
-console.log("Parse configuration file: %s", config_path);
+console.log("Parse configuration file: %s", configurationFilepath);
-const yaml_config = YAML.load(config_path);
+const yamlContent = YAML.load(configurationFilepath);
-const deps = {
+const deps: GlobalDependencies = {
u2f: require("u2f"),
nodemailer: require("nodemailer"),
ldapjs: require("ldapjs"),
session: require("express-session"),
winston: require("winston"),
speakeasy: require("speakeasy"),
- nedb: require("nedb")
+ nedb: require("nedb"),
+ ConnectRedis: require("connect-redis")
};
const server = new Server();
-server.start(yaml_config, deps)
+server.start(yamlContent, deps)
.then(() => {
console.log("The server is started!");
});
diff --git a/src/server/lib/ConfigurationAdapter.ts b/src/server/lib/ConfigurationAdapter.ts
index 4779fa08..3cbca916 100644
--- a/src/server/lib/ConfigurationAdapter.ts
+++ b/src/server/lib/ConfigurationAdapter.ts
@@ -1,6 +1,6 @@
import * as ObjectPath from "object-path";
-import { AppConfiguration, UserConfiguration, NotifierConfiguration, ACLConfiguration, LdapConfiguration } from "./../../types/Configuration";
+import { AppConfiguration, UserConfiguration, NotifierConfiguration, ACLConfiguration, LdapConfiguration, SessionRedisOptions } from "./../../types/Configuration";
const LDAP_URL_ENV_VARIABLE = "LDAP_URL";
@@ -32,6 +32,7 @@ function adaptFromUserConfiguration(userConfiguration: UserConfiguration): AppCo
domain: ObjectPath.get(userConfiguration, "session.domain"),
secret: ObjectPath.get(userConfiguration, "session.secret"),
expiration: get_optional(userConfiguration, "session.expiration", 3600000), // in ms
+ redis: ObjectPath.get(userConfiguration, "session.redis")
},
store_directory: get_optional(userConfiguration, "store_directory", undefined),
logs_level: get_optional(userConfiguration, "logs_level", "info"),
diff --git a/src/server/lib/Server.ts b/src/server/lib/Server.ts
index 1b1525ea..adda7e43 100644
--- a/src/server/lib/Server.ts
+++ b/src/server/lib/Server.ts
@@ -5,12 +5,13 @@ import { GlobalDependencies } from "../../types/Dependencies";
import { AuthenticationRegulator } from "./AuthenticationRegulator";
import UserDataStore from "./UserDataStore";
import ConfigurationAdapter from "./ConfigurationAdapter";
-import { Ā TOTPValidator } from "./TOTPValidator";
+import { TOTPValidator } from "./TOTPValidator";
import { TOTPGenerator } from "./TOTPGenerator";
import RestApi from "./RestApi";
import { LdapClient } from "./LdapClient";
import BluebirdPromise = require("bluebird");
import ServerVariables = require("./ServerVariables");
+import SessionConfigurationBuilder from "./SessionConfigurationBuilder";
import * as Express from "express";
import * as BodyParser from "body-parser";
@@ -20,40 +21,32 @@ import * as http from "http";
export default class Server {
private httpServer: http.Server;
- start(yaml_configuration: UserConfiguration, deps: GlobalDependencies): BluebirdPromise {
- const config = ConfigurationAdapter.adapt(yaml_configuration);
+ start(yamlConfiguration: UserConfiguration, deps: GlobalDependencies): BluebirdPromise {
+ const config = ConfigurationAdapter.adapt(yamlConfiguration);
- const view_directory = Path.resolve(__dirname, "../views");
- const public_html_directory = Path.resolve(__dirname, "../public_html");
+ const viewsDirectory = Path.resolve(__dirname, "../views");
+ const publicHtmlDirectory = Path.resolve(__dirname, "../public_html");
+
+ const expressSessionOptions = SessionConfigurationBuilder.build(config, deps);
const app = Express();
- app.use(Express.static(public_html_directory));
+ app.use(Express.static(publicHtmlDirectory));
app.use(BodyParser.urlencoded({ extended: false }));
app.use(BodyParser.json());
+ app.use(deps.session(expressSessionOptions));
- app.set("trust proxy", 1); // trust first proxy
-
- app.use(deps.session({
- secret: config.session.secret,
- resave: false,
- saveUninitialized: true,
- cookie: {
- secure: false,
- maxAge: config.session.expiration,
- domain: config.session.domain
- },
- }));
-
- app.set("views", view_directory);
+ app.set("trust proxy", 1);
+ app.set("views", viewsDirectory);
app.set("view engine", "pug");
// by default the level of logs is info
deps.winston.level = config.logs_level;
console.log("Log level = ", deps.winston.level);
+
+ deps.winston.debug("Content of YAML configuration file is %s", JSON.stringify(yamlConfiguration, undefined, 2));
deps.winston.debug("Authelia configuration is %s", JSON.stringify(config, undefined, 2));
ServerVariables.fill(app, config, deps);
-
RestApi.setup(app);
return new BluebirdPromise((resolve, reject) => {
diff --git a/src/server/lib/SessionConfigurationBuilder.ts b/src/server/lib/SessionConfigurationBuilder.ts
new file mode 100644
index 00000000..3fa6c661
--- /dev/null
+++ b/src/server/lib/SessionConfigurationBuilder.ts
@@ -0,0 +1,37 @@
+
+import ExpressSession = require("express-session");
+import { AppConfiguration } from "../../types/Configuration";
+import { GlobalDependencies } from "../../types/Dependencies";
+
+export default class SessionConfigurationBuilder {
+
+ static build(configuration: AppConfiguration, deps: GlobalDependencies): ExpressSession.SessionOptions {
+ const sessionOptions: ExpressSession.SessionOptions = {
+ secret: configuration.session.secret,
+ resave: false,
+ saveUninitialized: true,
+ cookie: {
+ secure: false,
+ maxAge: configuration.session.expiration,
+ domain: configuration.session.domain
+ },
+ };
+
+ if (configuration.session.redis) {
+ let redisOptions;
+ if (configuration.session.redis.host
+ && configuration.session.redis.port) {
+ redisOptions = {
+ host: configuration.session.redis.host,
+ port: configuration.session.redis.port
+ };
+ }
+
+ if (redisOptions) {
+ const RedisStore = deps.ConnectRedis(deps.session);
+ sessionOptions.store = new RedisStore(redisOptions);
+ }
+ }
+ return sessionOptions;
+ }
+}
\ No newline at end of file
diff --git a/src/types/Configuration.ts b/src/types/Configuration.ts
index ece9acfc..8c2c9192 100644
--- a/src/types/Configuration.ts
+++ b/src/types/Configuration.ts
@@ -24,10 +24,16 @@ export interface ACLConfiguration {
users: ACLUsersRules;
}
+export interface SessionRedisOptions {
+ host: string;
+ port: number;
+}
+
interface SessionCookieConfiguration {
secret: string;
expiration?: number;
domain?: string;
+ redis?: SessionRedisOptions;
}
export interface GmailNotifierConfiguration {
diff --git a/src/types/Dependencies.ts b/src/types/Dependencies.ts
index 261cd2ff..beabd9cf 100644
--- a/src/types/Dependencies.ts
+++ b/src/types/Dependencies.ts
@@ -5,6 +5,7 @@ import session = require("express-session");
import nedb = require("nedb");
import ldapjs = require("ldapjs");
import u2f = require("u2f");
+import RedisSession = require("connect-redis");
export type Nodemailer = typeof nodemailer;
export type Speakeasy = typeof speakeasy;
@@ -13,12 +14,14 @@ export type Session = typeof session;
export type Nedb = typeof nedb;
export type Ldapjs = typeof ldapjs;
export type U2f = typeof u2f;
+export type ConnectRedis = typeof RedisSession;
export interface GlobalDependencies {
u2f: U2f;
nodemailer: Nodemailer;
ldapjs: Ldapjs;
session: Session;
+ ConnectRedis: ConnectRedis;
winston: Winston;
speakeasy: Speakeasy;
nedb: Nedb;
diff --git a/test/integration/Dockerfile b/test/integration/Dockerfile
index fff70158..16e8a209 100644
--- a/test/integration/Dockerfile
+++ b/test/integration/Dockerfile
@@ -2,4 +2,3 @@ FROM node:7-alpine
WORKDIR /usr/src
-CMD ["./node_modules/.bin/mocha", "--compilers", "ts:ts-node/register", "--recursive", "test/integration"]
diff --git a/test/integration/config.yml b/test/integration/config.yml
index 5ec8a6d7..7854350e 100644
--- a/test/integration/config.yml
+++ b/test/integration/config.yml
@@ -73,6 +73,9 @@ session:
secret: unsecure_secret
expiration: 3600000
domain: test.local
+ redis:
+ host: redis
+ port: 6379
# The directory where the DB files will be saved
diff --git a/test/integration/docker-compose.yml b/test/integration/docker-compose.yml
index 2920725a..afaab5b1 100644
--- a/test/integration/docker-compose.yml
+++ b/test/integration/docker-compose.yml
@@ -11,6 +11,7 @@ services:
int-test:
build: ./test/integration
+ command: ./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/integration
volumes:
- ./:/usr/src
networks:
diff --git a/test/integration/redis.test.ts b/test/integration/redis.test.ts
new file mode 100644
index 00000000..185d9da4
--- /dev/null
+++ b/test/integration/redis.test.ts
@@ -0,0 +1,23 @@
+
+import Redis = require("redis");
+import Assert = require("assert");
+
+const redisOptions = {
+ host: "redis",
+ port: 6379
+};
+
+describe("test redis is correctly used", function () {
+ let redisClient: Redis.RedisClient;
+
+ before(function () {
+ redisClient = Redis.createClient(redisOptions);
+ });
+
+ it("should have registered at least one session", function (done) {
+ redisClient.dbsize(function (err: Error, count: number) {
+ Assert.equal(1, count);
+ done();
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/server/config_adapter.test.ts b/test/server/ConfigurationAdapter.test.ts
similarity index 100%
rename from test/server/config_adapter.test.ts
rename to test/server/ConfigurationAdapter.test.ts
diff --git a/test/server/DataPersistence.test.ts b/test/server/DataPersistence.test.ts
index e6c33f7d..ae4d85ff 100644
--- a/test/server/DataPersistence.test.ts
+++ b/test/server/DataPersistence.test.ts
@@ -100,15 +100,16 @@ describe("test data persistence", function () {
sendMail: sinon.stub().yields()
};
- const deps = {
+ const deps: GlobalDependencies = {
u2f: u2f,
nedb: nedb,
nodemailer: nodemailer,
session: session,
winston: winston,
ldapjs: ldap,
- speakeasy: speakeasy
- } as GlobalDependencies;
+ speakeasy: speakeasy,
+ ConnectRedis: sinon.spy()
+ };
const j1 = request.jar();
const j2 = request.jar();
diff --git a/test/server/ServerConfig.test.ts b/test/server/ServerConfiguration.test.ts
similarity index 94%
rename from test/server/ServerConfig.test.ts
rename to test/server/ServerConfiguration.test.ts
index 4773539a..f9052bd9 100644
--- a/test/server/ServerConfig.test.ts
+++ b/test/server/ServerConfiguration.test.ts
@@ -38,11 +38,12 @@ describe("test server configuration", function () {
createClient: sinon.spy(function () {
return {
on: sinon.spy(),
- bind: sinon.spy()
+ bind: sinon.spy(),
};
})
},
- session: sessionMock as any
+ session: sessionMock as any,
+ ConnectRedis: sinon.spy()
};
});
diff --git a/test/server/SessionConfigurationBuilder.test.ts b/test/server/SessionConfigurationBuilder.test.ts
new file mode 100644
index 00000000..e6f94b23
--- /dev/null
+++ b/test/server/SessionConfigurationBuilder.test.ts
@@ -0,0 +1,131 @@
+import SessionConfigurationBuilder from "../../src/server/lib/SessionConfigurationBuilder";
+import { AppConfiguration } from "../../src/types/Configuration";
+import { GlobalDependencies } from "../../src/types/Dependencies";
+import ExpressSession = require("express-session");
+import ConnectRedis = require("connect-redis");
+import sinon = require("sinon");
+import Assert = require("assert");
+
+describe("test session configuration builder", function () {
+ it("should return session options without redis options", function () {
+ const configuration: AppConfiguration = {
+ access_control: {
+ default: [],
+ users: {},
+ groups: {}
+ },
+ ldap: {
+ url: "ldap://ldap",
+ base_dn: "dc=example,dc=com",
+ user: "user",
+ password: "password"
+ },
+ logs_level: "debug",
+ notifier: {
+ filesystem: {
+ filename: "/test"
+ }
+ },
+ port: 8080,
+ session: {
+ domain: "example.com",
+ expiration: 3600,
+ secret: "secret"
+ },
+ store_in_memory: true
+ };
+
+ const deps: GlobalDependencies = {
+ ConnectRedis: sinon.spy() as any,
+ ldapjs: sinon.spy() as any,
+ nedb: sinon.spy() as any,
+ nodemailer: sinon.spy() as any,
+ session: sinon.spy() as any,
+ speakeasy: sinon.spy() as any,
+ u2f: sinon.spy() as any,
+ winston: sinon.spy() as any
+ };
+
+ const options = SessionConfigurationBuilder.build(configuration, deps);
+
+ const expectedOptions = {
+ secret: "secret",
+ resave: false,
+ saveUninitialized: true,
+ cookie: {
+ secure: false,
+ maxAge: 3600,
+ domain: "example.com"
+ }
+ };
+
+ Assert.deepEqual(expectedOptions, options);
+ });
+
+ it("should return session options with redis options", function () {
+ const configuration: AppConfiguration = {
+ access_control: {
+ default: [],
+ users: {},
+ groups: {}
+ },
+ ldap: {
+ url: "ldap://ldap",
+ base_dn: "dc=example,dc=com",
+ user: "user",
+ password: "password"
+ },
+ logs_level: "debug",
+ notifier: {
+ filesystem: {
+ filename: "/test"
+ }
+ },
+ port: 8080,
+ session: {
+ domain: "example.com",
+ expiration: 3600,
+ secret: "secret",
+ redis: {
+ host: "redis.example.com",
+ port: 6379
+ }
+ },
+ store_in_memory: true
+ };
+
+ const RedisStoreMock = sinon.spy();
+
+ const deps: GlobalDependencies = {
+ ConnectRedis: sinon.stub().returns(RedisStoreMock) as any,
+ ldapjs: sinon.spy() as any,
+ nedb: sinon.spy() as any,
+ nodemailer: sinon.spy() as any,
+ session: sinon.spy() as any,
+ speakeasy: sinon.spy() as any,
+ u2f: sinon.spy() as any,
+ winston: sinon.spy() as any
+ };
+
+ const options = SessionConfigurationBuilder.build(configuration, deps);
+
+ const expectedOptions: ExpressSession.SessionOptions = {
+ secret: "secret",
+ resave: false,
+ saveUninitialized: true,
+ cookie: {
+ secure: false,
+ maxAge: 3600,
+ domain: "example.com"
+ },
+ store: sinon.match.object as any
+ };
+
+ Assert((deps.ConnectRedis as sinon.SinonStub).calledWith(deps.session));
+ Assert.equal(options.secret, expectedOptions.secret);
+ Assert.equal(options.resave, expectedOptions.resave);
+ Assert.equal(options.saveUninitialized, expectedOptions.saveUninitialized);
+ Assert.deepEqual(options.cookie, expectedOptions.cookie);
+ Assert(options.store != undefined);
+ });
+});
\ No newline at end of file
diff --git a/test/server/server/PrivatePages.ts b/test/server/server/PrivatePages.ts
new file mode 100644
index 00000000..dcc690ab
--- /dev/null
+++ b/test/server/server/PrivatePages.ts
@@ -0,0 +1,177 @@
+
+import Server from "../../../src/server/lib/Server";
+import LdapClient = require("../../../src/server/lib/LdapClient");
+
+import BluebirdPromise = require("bluebird");
+import speakeasy = require("speakeasy");
+import request = require("request");
+import nedb = require("nedb");
+import { GlobalDependencies } from "../../../src/types/Dependencies";
+import { TOTPSecret } from "../../../src/types/TOTPSecret";
+import U2FMock = require("./../mocks/u2f");
+import Endpoints = require("../../../src/server/endpoints");
+import Requests = require("../requests");
+import Assert = require("assert");
+import Sinon = require("sinon");
+import Winston = require("winston");
+import MockDate = require("mockdate");
+import ExpressSession = require("express-session");
+import ldapjs = require("ldapjs");
+
+const requestp = BluebirdPromise.promisifyAll(request) as typeof request;
+
+const PORT = 8090;
+const BASE_URL = "http://localhost:" + PORT;
+const requests = Requests(PORT);
+
+describe("Private pages of the server must not be accessible without session", function () {
+ let server: Server;
+ let transporter: any;
+ let u2f: U2FMock.U2FMock;
+
+ beforeEach(function () {
+ const config = {
+ port: PORT,
+ ldap: {
+ url: "ldap://127.0.0.1:389",
+ base_dn: "ou=users,dc=example,dc=com",
+ user_name_attribute: "cn",
+ user: "cn=admin,dc=example,dc=com",
+ password: "password",
+ },
+ session: {
+ secret: "session_secret",
+ expiration: 50000,
+ },
+ store_in_memory: true,
+ notifier: {
+ gmail: {
+ username: "user@example.com",
+ password: "password"
+ }
+ }
+ };
+
+ const ldap_client = {
+ bind: Sinon.stub(),
+ search: Sinon.stub(),
+ modify: Sinon.stub(),
+ on: Sinon.spy()
+ };
+ const ldap = {
+ Change: Sinon.spy(),
+ createClient: Sinon.spy(function () {
+ return ldap_client;
+ })
+ };
+
+ u2f = U2FMock.U2FMock();
+
+ transporter = {
+ sendMail: Sinon.stub().yields()
+ };
+
+ const nodemailer = {
+ createTransport: Sinon.spy(function () {
+ return transporter;
+ })
+ };
+
+ const ldap_document = {
+ object: {
+ mail: "test_ok@example.com",
+ }
+ };
+
+ const search_res = {
+ on: Sinon.spy(function (event: string, fn: (s: any) => void) {
+ if (event != "error") fn(ldap_document);
+ })
+ };
+
+ ldap_client.bind.withArgs("cn=test_ok,ou=users,dc=example,dc=com",
+ "password").yields(undefined);
+ ldap_client.bind.withArgs("cn=admin,dc=example,dc=com",
+ "password").yields(undefined);
+
+ ldap_client.bind.withArgs("cn=test_nok,ou=users,dc=example,dc=com",
+ "password").yields("error");
+
+ ldap_client.modify.yields(undefined);
+ ldap_client.search.yields(undefined, search_res);
+
+ const deps: GlobalDependencies = {
+ u2f: u2f,
+ nedb: nedb,
+ nodemailer: nodemailer,
+ ldapjs: ldap,
+ session: ExpressSession,
+ winston: Winston,
+ speakeasy: speakeasy,
+ ConnectRedis: Sinon.spy()
+ };
+
+ server = new Server();
+ return server.start(config, deps);
+ });
+
+ afterEach(function () {
+ server.stop();
+ });
+
+ describe("Second factor endpoints must be protected if first factor is not validated", function () {
+ function should_post_and_reply_with(url: string, status_code: number): BluebirdPromise {
+ return requestp.postAsync(url).then(function (response: request.RequestResponse) {
+ Assert.equal(response.statusCode, status_code);
+ return BluebirdPromise.resolve();
+ });
+ }
+
+ function should_get_and_reply_with(url: string, status_code: number): BluebirdPromise {
+ return requestp.getAsync(url).then(function (response: request.RequestResponse) {
+ Assert.equal(response.statusCode, status_code);
+ return BluebirdPromise.resolve();
+ });
+ }
+
+ function should_post_and_reply_with_401(url: string): BluebirdPromise {
+ return should_post_and_reply_with(url, 401);
+ }
+ function should_get_and_reply_with_401(url: string): BluebirdPromise {
+ return should_get_and_reply_with(url, 401);
+ }
+
+ it("should block " + Endpoints.SECOND_FACTOR_GET, function () {
+ return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_GET);
+ });
+
+ it("should block " + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, function () {
+ return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET);
+ });
+
+ it("should block " + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, function () {
+ return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET + "?identity_token=dummy");
+ });
+
+ it("should block " + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, function () {
+ return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET);
+ });
+
+ it("should block " + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, function () {
+ return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST);
+ });
+
+ it("should block " + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, function () {
+ return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET);
+ });
+
+ it("should block " + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, function () {
+ return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST);
+ });
+
+ it("should block " + Endpoints.SECOND_FACTOR_TOTP_POST, function () {
+ return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST);
+ });
+ });
+});
+
diff --git a/test/server/server/PublicPages.ts b/test/server/server/PublicPages.ts
new file mode 100644
index 00000000..5d8fcec3
--- /dev/null
+++ b/test/server/server/PublicPages.ts
@@ -0,0 +1,165 @@
+
+import Server from "../../../src/server/lib/Server";
+import LdapClient = require("../../../src/server/lib/LdapClient");
+
+import BluebirdPromise = require("bluebird");
+import speakeasy = require("speakeasy");
+import Request = require("request");
+import nedb = require("nedb");
+import { GlobalDependencies } from "../../../src/types/Dependencies";
+import { TOTPSecret } from "../../../src/types/TOTPSecret";
+import U2FMock = require("./../mocks/u2f");
+import Endpoints = require("../../../src/server/endpoints");
+import Requests = require("../requests");
+import Assert = require("assert");
+import Sinon = require("sinon");
+import Winston = require("winston");
+import MockDate = require("mockdate");
+import ExpressSession = require("express-session");
+import ldapjs = require("ldapjs");
+
+const requestp = BluebirdPromise.promisifyAll(Request) as typeof Request;
+
+const PORT = 8090;
+const BASE_URL = "http://localhost:" + PORT;
+const requests = Requests(PORT);
+
+describe("Public pages of the server must be accessible without session", function () {
+ let server: Server;
+ let transporter: object;
+ let u2f: U2FMock.U2FMock;
+
+ beforeEach(function () {
+ const config = {
+ port: PORT,
+ ldap: {
+ url: "ldap://127.0.0.1:389",
+ base_dn: "ou=users,dc=example,dc=com",
+ user_name_attribute: "cn",
+ user: "cn=admin,dc=example,dc=com",
+ password: "password",
+ },
+ session: {
+ secret: "session_secret",
+ expiration: 50000,
+ },
+ store_in_memory: true,
+ notifier: {
+ gmail: {
+ username: "user@example.com",
+ password: "password"
+ }
+ }
+ };
+
+ const ldap_client = {
+ bind: Sinon.stub(),
+ search: Sinon.stub(),
+ modify: Sinon.stub(),
+ on: Sinon.spy()
+ };
+ const ldap = {
+ Change: Sinon.spy(),
+ createClient: Sinon.spy(function () {
+ return ldap_client;
+ })
+ };
+
+ u2f = U2FMock.U2FMock();
+
+ transporter = {
+ sendMail: Sinon.stub().yields()
+ };
+
+ const nodemailer = {
+ createTransport: Sinon.spy(function () {
+ return transporter;
+ })
+ };
+
+ const ldap_document = {
+ object: {
+ mail: "test_ok@example.com",
+ }
+ };
+
+ const search_res = {
+ on: Sinon.spy(function (event: string, fn: (s: any) => void) {
+ if (event != "error") fn(ldap_document);
+ })
+ };
+
+ ldap_client.bind.withArgs("cn=test_ok,ou=users,dc=example,dc=com",
+ "password").yields(undefined);
+ ldap_client.bind.withArgs("cn=admin,dc=example,dc=com",
+ "password").yields(undefined);
+
+ ldap_client.bind.withArgs("cn=test_nok,ou=users,dc=example,dc=com",
+ "password").yields("error");
+
+ ldap_client.modify.yields(undefined);
+ ldap_client.search.yields(undefined, search_res);
+
+ const deps: GlobalDependencies = {
+ u2f: u2f,
+ nedb: nedb,
+ nodemailer: nodemailer,
+ ldapjs: ldap,
+ session: ExpressSession,
+ winston: Winston,
+ speakeasy: speakeasy,
+ ConnectRedis: Sinon.spy()
+ };
+
+ server = new Server();
+ return server.start(config, deps);
+ });
+
+ afterEach(function () {
+ server.stop();
+ });
+
+ describe("test GET " + Endpoints.FIRST_FACTOR_GET, function () {
+ test_login();
+ });
+
+ describe("test GET " + Endpoints.LOGOUT_GET, function () {
+ test_logout();
+ });
+
+ describe("test GET" + Endpoints.RESET_PASSWORD_REQUEST_GET, function () {
+ test_reset_password_form();
+ });
+
+
+ function test_reset_password_form() {
+ it("should serve the reset password form page", function (done) {
+ requestp.getAsync(BASE_URL + Endpoints.RESET_PASSWORD_REQUEST_GET)
+ .then(function (response: Request.RequestResponse) {
+ Assert.equal(response.statusCode, 200);
+ done();
+ });
+ });
+ }
+
+ function test_login() {
+ it("should serve the login page", function (done) {
+ requestp.getAsync(BASE_URL + Endpoints.FIRST_FACTOR_GET)
+ .then(function (response: Request.RequestResponse) {
+ Assert.equal(response.statusCode, 200);
+ done();
+ });
+ });
+ }
+
+ function test_logout() {
+ it("should logout and redirect to /", function (done) {
+ requestp.getAsync(BASE_URL + Endpoints.LOGOUT_GET)
+ .then(function (response: any) {
+ Assert.equal(response.req.path, "/");
+ done();
+ });
+ });
+ }
+});
+
diff --git a/test/server/Server.test.ts b/test/server/server/Server.test.ts
similarity index 53%
rename from test/server/Server.test.ts
rename to test/server/server/Server.test.ts
index 5b4673fe..b7396aa0 100644
--- a/test/server/Server.test.ts
+++ b/test/server/server/Server.test.ts
@@ -1,32 +1,34 @@
-import Server from "../../src/server/lib/Server";
-import LdapClient = require("../../src/server/lib/LdapClient");
-import { LdapjsClientMock } from "./mocks/ldapjs";
+
+import Server from "../../../src/server/lib/Server";
+import LdapClient = require("../../../src/server/lib/LdapClient");
+import { LdapjsClientMock } from "./../mocks/ldapjs";
import BluebirdPromise = require("bluebird");
import speakeasy = require("speakeasy");
import request = require("request");
import nedb = require("nedb");
-import { TOTPSecret } from "../../src/types/TOTPSecret";
-import U2FMock = require("./mocks/u2f");
-import Endpoints = require("../../src/server/endpoints");
-
+import { GlobalDependencies } from "../../../src/types/Dependencies";
+import { TOTPSecret } from "../../../src/types/TOTPSecret";
+import U2FMock = require("./../mocks/u2f");
+import Endpoints = require("../../../src/server/endpoints");
+import Requests = require("../requests");
+import Assert = require("assert");
+import Sinon = require("sinon");
+import Winston = require("winston");
+import MockDate = require("mockdate");
+import ExpressSession = require("express-session");
+import ldapjs = require("ldapjs");
const requestp = BluebirdPromise.promisifyAll(request) as typeof request;
-const assert = require("assert");
-const sinon = require("sinon");
-const MockDate = require("mockdate");
-const session = require("express-session");
-const winston = require("winston");
-const ldapjs = require("ldapjs");
const PORT = 8090;
const BASE_URL = "http://localhost:" + PORT;
-const requests = require("./requests")(PORT);
+const requests = Requests(PORT);
describe("test the server", function () {
let server: Server;
- let transporter: object;
+ let transporter: any;
let u2f: U2FMock.U2FMock;
beforeEach(function () {
@@ -54,8 +56,8 @@ describe("test the server", function () {
const ldapClient = LdapjsClientMock();
const ldap = {
- Change: sinon.spy(),
- createClient: sinon.spy(function () {
+ Change: Sinon.spy(),
+ createClient: Sinon.spy(function () {
return ldapClient;
})
};
@@ -63,11 +65,11 @@ describe("test the server", function () {
u2f = U2FMock.U2FMock();
transporter = {
- sendMail: sinon.stub().yields()
+ sendMail: Sinon.stub().yields()
};
const nodemailer = {
- createTransport: sinon.spy(function () {
+ createTransport: Sinon.spy(function () {
return transporter;
})
};
@@ -79,7 +81,7 @@ describe("test the server", function () {
};
const search_res = {
- on: sinon.spy(function (event: string, fn: (s: any) => void) {
+ on: Sinon.spy(function (event: string, fn: (s: any) => void) {
if (event != "error") fn(ldapDocument);
})
};
@@ -96,14 +98,15 @@ describe("test the server", function () {
ldapClient.modify.yields();
ldapClient.search.yields(undefined, search_res);
- const deps = {
+ const deps: GlobalDependencies = {
u2f: u2f,
nedb: nedb,
nodemailer: nodemailer,
ldapjs: ldap,
- session: session,
- winston: winston,
- speakeasy: speakeasy
+ session: ExpressSession,
+ winston: Winston,
+ speakeasy: speakeasy,
+ ConnectRedis: Sinon.spy()
};
server = new Server();
@@ -114,114 +117,17 @@ describe("test the server", function () {
server.stop();
});
- describe("test GET " + Endpoints.FIRST_FACTOR_GET, function () {
- test_login();
- });
-
- describe("test GET " + Endpoints.LOGOUT_GET, function () {
- test_logout();
- });
-
- describe("test GET" + Endpoints.RESET_PASSWORD_REQUEST_GET, function () {
- test_reset_password_form();
- });
-
- describe("Second factor endpoints must be protected if first factor is not validated", function () {
- function should_post_and_reply_with(url: string, status_code: number): BluebirdPromise {
- return requestp.postAsync(url).then(function (response: request.RequestResponse) {
- assert.equal(response.statusCode, status_code);
- return BluebirdPromise.resolve();
- });
- }
-
- function should_get_and_reply_with(url: string, status_code: number): BluebirdPromise {
- return requestp.getAsync(url).then(function (response: request.RequestResponse) {
- assert.equal(response.statusCode, status_code);
- return BluebirdPromise.resolve();
- });
- }
-
- function should_post_and_reply_with_401(url: string): BluebirdPromise {
- return should_post_and_reply_with(url, 401);
- }
- function should_get_and_reply_with_401(url: string): BluebirdPromise {
- return should_get_and_reply_with(url, 401);
- }
-
- it("should block " + Endpoints.SECOND_FACTOR_GET, function () {
- return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_GET);
- });
-
- it("should block " + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET, function () {
- return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET);
- });
-
- it("should block " + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET, function () {
- return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_IDENTITY_FINISH_GET + "?identity_token=dummy");
- });
-
- it("should block " + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET, function () {
- return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_REQUEST_GET);
- });
-
- it("should block " + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST, function () {
- return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_REGISTER_POST);
- });
-
- it("should block " + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET, function () {
- return should_get_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_REQUEST_GET);
- });
-
- it("should block " + Endpoints.SECOND_FACTOR_U2F_SIGN_POST, function () {
- return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_U2F_SIGN_POST);
- });
-
- it("should block " + Endpoints.SECOND_FACTOR_TOTP_POST, function () {
- return should_post_and_reply_with_401(BASE_URL + Endpoints.SECOND_FACTOR_TOTP_POST);
- });
- });
-
describe("test authentication and verification", function () {
test_authentication();
test_reset_password();
test_regulation();
});
- function test_reset_password_form() {
- it("should serve the reset password form page", function (done) {
- requestp.getAsync(BASE_URL + Endpoints.RESET_PASSWORD_REQUEST_GET)
- .then(function (response: request.RequestResponse) {
- assert.equal(response.statusCode, 200);
- done();
- });
- });
- }
-
- function test_login() {
- it("should serve the login page", function (done) {
- requestp.getAsync(BASE_URL + Endpoints.FIRST_FACTOR_GET)
- .then(function (response: request.RequestResponse) {
- assert.equal(response.statusCode, 200);
- done();
- });
- });
- }
-
- function test_logout() {
- it("should logout and redirect to /", function (done) {
- requestp.getAsync(BASE_URL + Endpoints.LOGOUT_GET)
- .then(function (response: any) {
- assert.equal(response.req.path, "/");
- done();
- });
- });
- }
-
function test_authentication() {
it("should return status code 401 when user is not authenticated", function () {
return requestp.getAsync({ url: BASE_URL + Endpoints.VERIFY_GET })
.then(function (response: request.RequestResponse) {
- assert.equal(response.statusCode, 401);
+ Assert.equal(response.statusCode, 401);
return BluebirdPromise.resolve();
});
});
@@ -230,11 +136,11 @@ describe("test the server", function () {
const j = requestp.jar();
return requests.login(j)
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "get login page failed");
+ Assert.equal(res.statusCode, 200, "get login page failed");
return requests.first_factor(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 302, "first factor failed");
+ Assert.equal(res.statusCode, 302, "first factor failed");
return requests.register_totp(j, transporter);
})
.then(function (base32_secret: string) {
@@ -245,11 +151,11 @@ describe("test the server", function () {
return requests.totp(j, realToken);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "second factor failed");
+ Assert.equal(res.statusCode, 200, "second factor failed");
return requests.verify(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 204, "verify failed");
+ Assert.equal(res.statusCode, 204, "verify failed");
return BluebirdPromise.resolve();
})
.catch(function (err: Error) { return BluebirdPromise.reject(err); });
@@ -259,11 +165,11 @@ describe("test the server", function () {
const j = requestp.jar();
return requests.login(j)
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "get login page failed");
+ Assert.equal(res.statusCode, 200, "get login page failed");
return requests.first_factor(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 302, "first factor failed");
+ Assert.equal(res.statusCode, 302, "first factor failed");
return requests.register_totp(j, transporter);
})
.then(function (base32_secret: string) {
@@ -274,15 +180,15 @@ describe("test the server", function () {
return requests.totp(j, realToken);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "second factor failed");
+ Assert.equal(res.statusCode, 200, "second factor failed");
return requests.login(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "login page loading failed");
+ Assert.equal(res.statusCode, 200, "login page loading failed");
return requests.verify(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 204, "verify failed");
+ Assert.equal(res.statusCode, 204, "verify failed");
return BluebirdPromise.resolve();
})
.catch(function (err: Error) { return BluebirdPromise.reject(err); });
@@ -300,25 +206,25 @@ describe("test the server", function () {
const j = requestp.jar();
return requests.login(j)
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "get login page failed");
+ Assert.equal(res.statusCode, 200, "get login page failed");
return requests.first_factor(j);
})
.then(function (res: request.RequestResponse) {
// console.log(res);
- assert.equal(res.headers.location, Endpoints.SECOND_FACTOR_GET);
- assert.equal(res.statusCode, 302, "first factor failed");
+ Assert.equal(res.headers.location, Endpoints.SECOND_FACTOR_GET);
+ Assert.equal(res.statusCode, 302, "first factor failed");
return requests.u2f_registration(j, transporter);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "second factor, finish register failed");
+ Assert.equal(res.statusCode, 200, "second factor, finish register failed");
return requests.u2f_authentication(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "second factor, finish sign failed");
+ Assert.equal(res.statusCode, 200, "second factor, finish sign failed");
return requests.verify(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 204, "verify failed");
+ Assert.equal(res.statusCode, 204, "verify failed");
return BluebirdPromise.resolve();
});
});
@@ -329,16 +235,16 @@ describe("test the server", function () {
const j = requestp.jar();
return requests.login(j)
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "get login page failed");
+ Assert.equal(res.statusCode, 200, "get login page failed");
return requests.first_factor(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.headers.location, Endpoints.SECOND_FACTOR_GET);
- assert.equal(res.statusCode, 302, "first factor failed");
+ Assert.equal(res.headers.location, Endpoints.SECOND_FACTOR_GET);
+ Assert.equal(res.statusCode, 302, "first factor failed");
return requests.reset_password(j, transporter, "user", "new-password");
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 204, "second factor, finish register failed");
+ Assert.equal(res.statusCode, 204, "second factor, finish register failed");
return BluebirdPromise.resolve();
});
});
@@ -350,28 +256,28 @@ describe("test the server", function () {
MockDate.set("1/2/2017 00:00:00");
return requests.login(j)
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 200, "get login page failed");
+ Assert.equal(res.statusCode, 200, "get login page failed");
return requests.failing_first_factor(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 401, "first factor failed");
+ Assert.equal(res.statusCode, 401, "first factor failed");
return requests.failing_first_factor(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 401, "first factor failed");
+ Assert.equal(res.statusCode, 401, "first factor failed");
return requests.failing_first_factor(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 401, "first factor failed");
+ Assert.equal(res.statusCode, 401, "first factor failed");
return requests.failing_first_factor(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 403, "first factor failed");
+ Assert.equal(res.statusCode, 403, "first factor failed");
MockDate.set("1/2/2017 00:30:00");
return requests.failing_first_factor(j);
})
.then(function (res: request.RequestResponse) {
- assert.equal(res.statusCode, 401, "first factor failed");
+ Assert.equal(res.statusCode, 401, "first factor failed");
return BluebirdPromise.resolve();
});
});