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..600aacdf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,3 +9,7 @@ services: networks: - example-network + redis: + image: redis + networks: + - example-network diff --git a/package.json b/package.json index 9dd508f4..abaab5f4 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/server/index.ts b/src/server/index.ts index 68fa6e20..a40dc23a 100755 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -3,27 +3,29 @@ 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 yaml_config = 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(); diff --git a/src/server/lib/Server.ts b/src/server/lib/Server.ts index 1b1525ea..84c01d04 100644 --- a/src/server/lib/Server.ts +++ b/src/server/lib/Server.ts @@ -11,6 +11,7 @@ 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"; @@ -33,16 +34,8 @@ export default class Server { 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 - }, - })); + const sessionOptions = SessionConfigurationBuilder.build(config, deps); + app.use(deps.session(sessionOptions)); app.set("views", view_directory); app.set("view engine", "pug"); diff --git a/src/server/lib/SessionConfigurationBuilder.ts b/src/server/lib/SessionConfigurationBuilder.ts new file mode 100644 index 00000000..8d846a37 --- /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..ce88fe35 100644 --- a/src/types/Configuration.ts +++ b/src/types/Configuration.ts @@ -28,6 +28,10 @@ interface SessionCookieConfiguration { secret: string; expiration?: number; domain?: string; + redis?: { + host: string; + port: number; + }; } 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/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/Server.test.ts b/test/server/Server.test.ts index 5b4673fe..29266927 100644 --- a/test/server/Server.test.ts +++ b/test/server/Server.test.ts @@ -7,6 +7,7 @@ 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"); @@ -96,14 +97,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 + speakeasy: speakeasy, + ConnectRedis: sinon.spy() }; server = new Server(); 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() }; });