Refactor configuration to remove optional sections from minimal template

Also move tests from dedicated directory to source dir with .spec.ts extension
pull/242/head
Clement Michaud 2018-07-08 17:02:28 +02:00
parent 97e0ee8ff6
commit c82f910da3
136 changed files with 951 additions and 1464 deletions

View File

@ -1,6 +1,4 @@
dist: trusty
language: node_js
sudo: required
node_js:
- "8"
services:

View File

@ -19,7 +19,7 @@ module.exports = function (grunt) {
"generate-config-schema": {
cmd: "./node_modules/.bin/typescript-json-schema",
args: ["-o", schemaDir, "--strictNullChecks",
"--required", "server/tsconfig.json", "UserConfiguration"]
"--required", "server/tsconfig.json", "Configuration"]
},
"compile-client": {
cmd: "./node_modules/.bin/tsc",
@ -35,11 +35,11 @@ module.exports = function (grunt) {
},
"test-server-unit": {
cmd: "./node_modules/.bin/mocha",
args: ['--colors', '--compilers', 'ts:ts-node/register', '--recursive', 'server/test']
args: ['--colors', '--require', 'ts-node/register', 'server/src/**/*.spec.ts']
},
"test-client-unit": {
cmd: "./node_modules/.bin/mocha",
args: ['--colors', '--compilers', 'ts:ts-node/register', '--recursive', 'client/test']
args: ['--colors', '--require', 'ts-node/register', 'client/test/**/*.test.ts']
},
"test-int": {
cmd: "./scripts/run-cucumber.sh",

24
config.minimal.yml 100644
View File

@ -0,0 +1,24 @@
###############################################################
# Authelia minimal configuration #
###############################################################
ldap:
url: ldap://openldap
base_dn: dc=example,dc=com
additional_users_dn: ou=users
additional_groups_dn: ou=groups
groups_filter: (&(member={dn})(objectclass=groupOfNames))
user: cn=admin,dc=example,dc=com
password: password
session:
# The secret to encrypt the session cookies.
secret: unsecure_session_secret
# The domain to protect.
# Note: the authenticator must also be in that domain. If empty, the cookie
# is restricted to the subdomain of the issuer.
domain: example.com

View File

@ -191,10 +191,11 @@ session:
# time.
regulation:
# The number of failed login attempts before user is banned.
# Set it to 0 for disabling regulation.
# Set it to 0 to disable regulation.
max_retries: 3
# The length of time between login attempts before user is banned.
# The time range during which the user can attempt login before being banned.
# The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window.
find_time: 120
# The length of time before a banned user can login again.

View File

@ -29,7 +29,7 @@ export class AuthenticationSessionHandler {
static get(req: express.Request, logger: IRequestLogger): AuthenticationSession {
if (!req.session) {
const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can contact it.";
const errorMsg = "Something is wrong with session cookies. Please check Redis is running and Authelia can connect to it.";
logger.error(req, errorMsg);
throw new Error(errorMsg);
}

View File

@ -1,32 +1,32 @@
import sinon = require("sinon");
import IdentityValidator = require("../src/lib/IdentityCheckMiddleware");
import IdentityValidator = require("./IdentityCheckMiddleware");
import { AuthenticationSessionHandler }
from "../src/lib/AuthenticationSessionHandler";
import { AuthenticationSession } from "../types/AuthenticationSession";
import { UserDataStore } from "../src/lib/storage/UserDataStore";
import exceptions = require("../src/lib/Exceptions");
import { ServerVariables } from "../src/lib/ServerVariables";
from "./AuthenticationSessionHandler";
import { AuthenticationSession } from "../../types/AuthenticationSession";
import { UserDataStore } from "./storage/UserDataStore";
import exceptions = require("./Exceptions");
import { ServerVariables } from "./ServerVariables";
import Assert = require("assert");
import express = require("express");
import BluebirdPromise = require("bluebird");
import ExpressMock = require("./mocks/express");
import NotifierMock = require("./mocks/Notifier");
import IdentityValidatorMock = require("./mocks/IdentityValidator");
import { RequestLoggerStub } from "./mocks/RequestLoggerStub";
import ExpressMock = require("./stubs/express.spec");
import NotifierMock = require("./notifiers/NotifierStub.spec");
import { IdentityValidableStub } from "./IdentityValidableStub.spec";
import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec";
import { ServerVariablesMock, ServerVariablesMockBuilder }
from "./mocks/ServerVariablesMockBuilder";
from "./ServerVariablesMockBuilder.spec";
import { PRE_VALIDATION_TEMPLATE }
from "../src/lib/IdentityCheckPreValidationTemplate";
from "./IdentityCheckPreValidationTemplate";
describe("test identity check process", function () {
describe("IdentityCheckMiddleware", function () {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let app: express.Application;
let app_get: sinon.SinonStub;
let app_post: sinon.SinonStub;
let identityValidable: IdentityValidatorMock.IdentityValidableMock;
let identityValidable: IdentityValidableStub;
let mocks: ServerVariablesMock;
let vars: ServerVariables;
@ -38,8 +38,6 @@ describe("test identity check process", function () {
req = ExpressMock.RequestMock();
res = ExpressMock.ResponseMock();
identityValidable = IdentityValidatorMock.IdentityValidableMock();
req.headers = {};
req.originalUrl = "/non-api/xxx";
req.session = {};
@ -47,6 +45,8 @@ describe("test identity check process", function () {
req.query = {};
req.app = {};
identityValidable = new IdentityValidableStub();
mocks.notifier.notifyStub.returns(BluebirdPromise.resolve());
mocks.userDataStore.produceIdentityValidationTokenStub
.returns(BluebirdPromise.resolve());
@ -66,7 +66,7 @@ describe("test identity check process", function () {
describe("test start GET", function () {
it("should redirect to error 401 if pre validation initialization \
throws a first factor error", function () {
identityValidable.preValidationInit.returns(BluebirdPromise.reject(
identityValidable.preValidationInitStub.returns(BluebirdPromise.reject(
new exceptions.FirstFactorValidationError(
"Error during prevalidation")));
const callback = IdentityValidator.get_start_validation(
@ -83,14 +83,14 @@ throws a first factor error", function () {
it("should send 200 if email is missing in provided identity", function () {
const identity = { userid: "abc" };
identityValidable.preValidationInit
identityValidable.preValidationInitStub
.returns(BluebirdPromise.resolve(identity));
const callback = IdentityValidator
.get_start_validation(identityValidable, "/endpoint", vars);
return callback(req as any, res as any, undefined)
.then(function () {
Assert(identityValidable.preValidationResponse.called);
Assert(identityValidable.preValidationResponseStub.called);
});
});
@ -100,14 +100,14 @@ throws a first factor error", function () {
const endpoint = "/protected";
const identity = { email: "abc@example.com" };
identityValidable.preValidationInit
identityValidable.preValidationInitStub
.returns(BluebirdPromise.resolve(identity));
const callback = IdentityValidator
.get_start_validation(identityValidable, "/endpoint", vars);
return callback(req as any, res as any, undefined)
.then(function () {
Assert(identityValidable.preValidationResponse.called);
Assert(identityValidable.preValidationResponseStub.called);
});
});
@ -116,7 +116,7 @@ throws a first factor error", function () {
const identity = { userid: "user", email: "abc@example.com" };
req.get = sinon.stub().withArgs("Host").returns("localhost");
identityValidable.preValidationInit
identityValidable.preValidationInitStub
.returns(BluebirdPromise.resolve(identity));
const callback = IdentityValidator
.get_start_validation(identityValidable, "/finish_endpoint", vars);
@ -177,24 +177,5 @@ valid", function () {
Assert(res.redirect.calledWith("/error/401"));
});
});
it("should set the identity_check session object even if session does \
not exist yet", function () {
req.query.identity_token = "token";
req.session = {};
const authSession =
AuthenticationSessionHandler.get(req as any, vars.logger);
const callback = IdentityValidator
.get_finish_validation(identityValidable, vars);
return callback(req as any, res as any, undefined)
.then(function () {
return BluebirdPromise.reject("Should fail");
})
.catch(function () {
Assert.equal(authSession.identity_check.userid, "user");
});
});
});
});

View File

@ -1,4 +1,3 @@
import objectPath = require("object-path");
import randomstring = require("randomstring");
import BluebirdPromise = require("bluebird");
@ -12,6 +11,7 @@ import ErrorReplies = require("./ErrorReplies");
import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler";
import { AuthenticationSession } from "../../types/AuthenticationSession";
import { ServerVariables } from "./ServerVariables";
import { IdentityValidable } from "./IdentityValidable";
import Identity = require("../../types/Identity");
import { IdentityValidationDocument }
@ -20,26 +20,9 @@ import { IdentityValidationDocument }
const filePath = __dirname + "/../resources/email-template.ejs";
const email_template = fs.readFileSync(filePath, "utf8");
// IdentityValidator allows user to go through a identity validation process
// in two steps:
// - Request an operation to be performed (password reset, registration).
// - Confirm operation with email.
export interface IdentityValidable {
challenge(): string;
preValidationInit(req: Express.Request): BluebirdPromise<Identity.Identity>;
postValidationInit(req: Express.Request): BluebirdPromise<void>;
// Serves a page after identity check request
preValidationResponse(req: Express.Request, res: Express.Response): void;
// Serves the page if identity validated
postValidationResponse(req: Express.Request, res: Express.Response): void;
mailSubject(): string;
}
function createAndSaveToken(userid: string, challenge: string,
userDataStore: IUserDataStore)
: BluebirdPromise<string> {
userDataStore: IUserDataStore): BluebirdPromise<string> {
const five_minutes = 4 * 60 * 1000;
const token = randomstring.generate({ length: 64 });
const that = this;
@ -80,8 +63,10 @@ function checkIdentityToken(req: Express.Request, identityToken: string)
export function get_finish_validation(handler: IdentityValidable,
vars: ServerVariables)
: Express.RequestHandler {
return function (req: Express.Request, res: Express.Response)
: BluebirdPromise<void> {
let authSession: AuthenticationSession;
const identityToken = objectPath.get<Express.Request, string>(
req, "query.identity_token");

View File

@ -0,0 +1,19 @@
import Bluebird = require("bluebird");
import Identity = require("../../types/Identity");
// IdentityValidator allows user to go through a identity validation process
// in two steps:
// - Request an operation to be performed (password reset, registration).
// - Confirm operation with email.
export interface IdentityValidable {
challenge(): string;
preValidationInit(req: Express.Request): Bluebird<Identity.Identity>;
postValidationInit(req: Express.Request): Bluebird<void>;
// Serves a page after identity check request
preValidationResponse(req: Express.Request, res: Express.Response): void;
// Serves the page if identity validated
postValidationResponse(req: Express.Request, res: Express.Response): void;
mailSubject(): string;
}

View File

@ -0,0 +1,52 @@
import Sinon = require("sinon");
import { IdentityValidable } from "./IdentityValidable";
import express = require("express");
import Bluebird = require("bluebird");
import { Identity } from "../../types/Identity";
export class IdentityValidableStub implements IdentityValidable {
challengeStub: Sinon.SinonStub;
preValidationInitStub: Sinon.SinonStub;
postValidationInitStub: Sinon.SinonStub;
preValidationResponseStub: Sinon.SinonStub;
postValidationResponseStub: Sinon.SinonStub;
mailSubjectStub: Sinon.SinonStub;
constructor() {
this.challengeStub = Sinon.stub();
this.preValidationInitStub = Sinon.stub();
this.postValidationResponseStub = Sinon.stub();
this.preValidationResponseStub = Sinon.stub();
this.postValidationResponseStub = Sinon.stub();
this.mailSubjectStub = Sinon.stub();
}
challenge(): string {
return this.challengeStub();
}
preValidationInit(req: Express.Request): Bluebird<Identity> {
return this.preValidationInitStub(req);
}
postValidationInit(req: Express.Request): Bluebird<void> {
return this.postValidationInitStub(req);
}
preValidationResponse(req: Express.Request, res: Express.Response): void {
return this.preValidationResponseStub(req, res);
}
postValidationResponse(req: Express.Request, res: Express.Response): void {
return this.postValidationResponseStub(req, res);
}
mailSubject(): string {
return this.mailSubjectStub();
}
}

View File

@ -7,13 +7,13 @@ import winston = require("winston");
import speakeasy = require("speakeasy");
import u2f = require("u2f");
import session = require("express-session");
import { AppConfiguration, UserConfiguration } from "../src/lib/configuration/Configuration";
import { GlobalDependencies } from "../types/Dependencies";
import Server from "../src/lib/Server";
import { LdapjsMock, LdapjsClientMock } from "./mocks/ldapjs";
import { Configuration } from "./configuration/schema/Configuration";
import { GlobalDependencies } from "../../types/Dependencies";
import Server from "./Server";
import { LdapjsMock, LdapjsClientMock } from "./stubs/ldapjs.spec";
describe("test server configuration", function () {
describe("Server", function () {
let deps: GlobalDependencies;
let sessionMock: Sinon.SinonSpy;
let ldapjsMock: LdapjsMock;
@ -36,7 +36,7 @@ describe("test server configuration", function () {
it("should set cookie scope to domain set in the config", function () {
const config: UserConfiguration = {
const config: Configuration = {
port: 8081,
session: {
domain: "example.com",

View File

@ -2,7 +2,7 @@ import BluebirdPromise = require("bluebird");
import ObjectPath = require("object-path");
import { AccessController } from "./access_control/AccessController";
import { AppConfiguration, UserConfiguration } from "./configuration/Configuration";
import { Configuration } from "./configuration/schema/Configuration";
import { GlobalDependencies } from "../../types/Dependencies";
import { UserDataStore } from "./storage/UserDataStore";
import { ConfigurationParser } from "./configuration/ConfigurationParser";
@ -31,33 +31,22 @@ export default class Server {
this.requestLogger = new RequestLogger(deps.winston);
}
private displayConfigurations(userConfiguration: UserConfiguration,
appConfiguration: AppConfiguration) {
const displayableUserConfiguration = clone(userConfiguration);
const displayableAppConfiguration = clone(appConfiguration);
private displayConfigurations(configuration: Configuration) {
const displayableConfiguration: Configuration = clone(configuration);
const STARS = "*****";
displayableUserConfiguration.ldap.password = STARS;
displayableUserConfiguration.session.secret = STARS;
if (displayableUserConfiguration.notifier && displayableUserConfiguration.notifier.email)
displayableUserConfiguration.notifier.email.password = STARS;
if (displayableUserConfiguration.notifier && displayableUserConfiguration.notifier.smtp)
displayableUserConfiguration.notifier.smtp.password = STARS;
displayableAppConfiguration.ldap.password = STARS;
displayableAppConfiguration.session.secret = STARS;
if (displayableAppConfiguration.notifier && displayableAppConfiguration.notifier.email)
displayableAppConfiguration.notifier.email.password = STARS;
if (displayableAppConfiguration.notifier && displayableAppConfiguration.notifier.smtp)
displayableAppConfiguration.notifier.smtp.password = STARS;
displayableConfiguration.ldap.password = STARS;
displayableConfiguration.session.secret = STARS;
if (displayableConfiguration.notifier && displayableConfiguration.notifier.email)
displayableConfiguration.notifier.email.password = STARS;
if (displayableConfiguration.notifier && displayableConfiguration.notifier.smtp)
displayableConfiguration.notifier.smtp.password = STARS;
this.globalLogger.debug("User configuration is %s",
JSON.stringify(displayableUserConfiguration, undefined, 2));
this.globalLogger.debug("Adapted configuration is %s",
JSON.stringify(displayableAppConfiguration, undefined, 2));
JSON.stringify(displayableConfiguration, undefined, 2));
}
private setup(config: AppConfiguration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise<void> {
private setup(config: Configuration, app: Express.Application, deps: GlobalDependencies): BluebirdPromise<void> {
const that = this;
return ServerVariablesInitializer.initialize(config, this.requestLogger, deps)
.then(function (vars: ServerVariables) {
@ -76,16 +65,16 @@ export default class Server {
});
}
start(userConfiguration: UserConfiguration, deps: GlobalDependencies)
start(configuration: Configuration, deps: GlobalDependencies)
: BluebirdPromise<void> {
const that = this;
const app = Express();
const appConfiguration = ConfigurationParser.parse(userConfiguration);
const appConfiguration = ConfigurationParser.parse(configuration);
// by default the level of logs is info
deps.winston.level = userConfiguration.logs_level;
this.displayConfigurations(userConfiguration, appConfiguration);
deps.winston.level = configuration.logs_level;
this.displayConfigurations(configuration);
return this.setup(appConfiguration, app, deps)
.then(function () {

View File

@ -7,7 +7,7 @@ import { IU2fHandler } from "./authentication/u2f/IU2fHandler";
import { IUserDataStore } from "./storage/IUserDataStore";
import { INotifier } from "./notifiers/INotifier";
import { IRegulator } from "./regulation/IRegulator";
import { AppConfiguration } from "./configuration/Configuration";
import { Configuration } from "./configuration/schema/Configuration";
import { IAccessController } from "./access_control/IAccessController";
export interface ServerVariables {
@ -20,6 +20,6 @@ export interface ServerVariables {
userDataStore: IUserDataStore;
notifier: INotifier;
regulator: IRegulator;
config: AppConfiguration;
config: Configuration;
accessController: IAccessController;
}

View File

@ -26,7 +26,7 @@ import { UserDataStore } from "./storage/UserDataStore";
import { INotifier } from "./notifiers/INotifier";
import { Regulator } from "./regulation/Regulator";
import { IRegulator } from "./regulation/IRegulator";
import Configuration = require("./configuration/Configuration");
import Configuration = require("./configuration/schema/Configuration");
import { AccessController } from "./access_control/AccessController";
import { IAccessController } from "./access_control/IAccessController";
import { CollectionFactoryFactory } from "./storage/CollectionFactoryFactory";
@ -40,7 +40,7 @@ import { ServerVariables } from "./ServerVariables";
import { MethodCalculator } from "./authentication/MethodCalculator";
class UserDataStoreFactory {
static create(config: Configuration.AppConfiguration): BluebirdPromise<UserDataStore> {
static create(config: Configuration.Configuration): BluebirdPromise<UserDataStore> {
if (config.storage.local) {
const nedbOptions: Nedb.DataStoreOptions = {
filename: config.storage.local.path,
@ -64,7 +64,7 @@ class UserDataStoreFactory {
}
export class ServerVariablesInitializer {
static initialize(config: Configuration.AppConfiguration, requestLogger: IRequestLogger,
static initialize(config: Configuration.Configuration, requestLogger: IRequestLogger,
deps: GlobalDependencies): BluebirdPromise<ServerVariables> {
const mailSenderBuilder = new MailSenderBuilder(Nodemailer);
const notifier = NotifierFactory.build(config.notifier, mailSenderBuilder);

View File

@ -1,20 +1,20 @@
import { ServerVariables } from "../../src/lib/ServerVariables";
import { ServerVariables } from "./ServerVariables";
import { AppConfiguration } from "../../src/lib/configuration/Configuration";
import { AuthenticatorStub } from "./ldap/AuthenticatorStub";
import { EmailsRetrieverStub } from "./ldap/EmailsRetrieverStub";
import { PasswordUpdaterStub } from "./ldap/PasswordUpdaterStub";
import { AccessControllerStub } from "./AccessControllerStub";
import { RequestLoggerStub } from "./RequestLoggerStub";
import { NotifierStub } from "./NotifierStub";
import { RegulatorStub } from "./RegulatorStub";
import { TotpHandlerStub } from "./TotpHandlerStub";
import { UserDataStoreStub } from "./storage/UserDataStoreStub";
import { U2fHandlerStub } from "./U2fHandlerStub";
import { Configuration } from "./configuration/schema/Configuration";
import { AuthenticatorStub } from "./ldap/AuthenticatorStub.spec";
import { EmailsRetrieverStub } from "./ldap/EmailsRetrieverStub.spec";
import { PasswordUpdaterStub } from "./ldap/PasswordUpdaterStub.spec";
import { AccessControllerStub } from "./access_control/AccessControllerStub.spec";
import { RequestLoggerStub } from "./logging/RequestLoggerStub.spec";
import { NotifierStub } from "./notifiers/NotifierStub.spec";
import { RegulatorStub } from "./regulation/RegulatorStub.spec";
import { TotpHandlerStub } from "./authentication/totp/TotpHandlerStub.spec";
import { UserDataStoreStub } from "./storage/UserDataStoreStub.spec";
import { U2fHandlerStub } from "./authentication/u2f/U2fHandlerStub.spec";
export interface ServerVariablesMock {
accessController: AccessControllerStub;
config: AppConfiguration;
config: Configuration;
ldapAuthenticator: AuthenticatorStub;
ldapEmailsRetriever: EmailsRetrieverStub;
ldapPasswordUpdater: PasswordUpdaterStub;
@ -40,11 +40,12 @@ export class ServerVariablesMockBuilder {
},
ldap: {
url: "ldap://ldap",
base_dn: "dc=example,dc=com",
user: "user",
password: "password",
mail_attribute: "mail",
users_dn: "ou=users,dc=example,dc=com",
groups_dn: "ou=groups,dc=example,dc=com",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
users_filter: "cn={0}",
groups_filter: "member={dn}",
group_name_attribute: "cn"

View File

@ -1,10 +1,10 @@
import Assert = require("assert");
import winston = require("winston");
import { AccessController } from "../../src/lib/access_control/AccessController";
import { ACLConfiguration, ACLRule } from "../../src/lib/configuration/Configuration";
import { AccessController } from "./AccessController";
import { ACLConfiguration, ACLRule } from "../configuration/schema/AclConfiguration";
describe("test access control manager", function () {
describe("access_control/AccessController", function () {
let accessController: AccessController;
let configuration: ACLConfiguration;

View File

@ -1,5 +1,5 @@
import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/Configuration";
import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/schema/AclConfiguration";
import { IAccessController } from "./IAccessController";
import { Winston } from "../../../types/Dependencies";
import { MultipleDomainMatcher } from "./MultipleDomainMatcher";

View File

@ -1,6 +1,5 @@
import Sinon = require("sinon");
import { IAccessController } from "../../src/lib/access_control/IAccessController";
import { IAccessController } from "./IAccessController";
export class AccessControllerStub implements IAccessController {
isAccessAllowedMock: Sinon.SinonStub;

View File

@ -1,10 +1,9 @@
import { MethodCalculator }
from "../../src/lib/authentication/MethodCalculator";
import { MethodCalculator } from "./MethodCalculator";
import { AuthenticationMethodsConfiguration }
from "../../src/lib/configuration/Configuration";
from "../configuration/schema/AuthenticationMethodsConfiguration";
import Assert = require("assert");
describe("test MethodCalculator", function () {
describe("authentication/MethodCalculator", function () {
describe("test compute method", function () {
it("should return default method when sub domain not overriden",
function () {

View File

@ -1,7 +1,7 @@
import {
AuthenticationMethod,
AuthenticationMethodsConfiguration
} from "../configuration/Configuration";
} from "../configuration/schema/AuthenticationMethodsConfiguration";
function computeIsSingleFactorOnlyMode(
configuration: AuthenticationMethodsConfiguration): boolean {

View File

@ -1,10 +1,10 @@
import { TotpHandler } from "../../../src/lib/authentication/totp/TotpHandler";
import { TotpHandler } from "./TotpHandler";
import Sinon = require("sinon");
import Speakeasy = require("speakeasy");
import Assert = require("assert");
describe("test TOTP validation", function() {
describe("authentication/totp/TotpHandler", function() {
let totpValidator: TotpHandler;
let validateStub: Sinon.SinonStub;

View File

@ -1,7 +1,7 @@
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import { ITotpHandler } from "../../src/lib/authentication/totp/ITotpHandler";
import { TOTPSecret } from "../../types/TOTPSecret";
import { ITotpHandler } from "./ITotpHandler";
import { TOTPSecret } from "../../../../types/TOTPSecret";
export class TotpHandlerStub implements ITotpHandler {
generateStub: Sinon.SinonStub;

View File

@ -1,7 +1,7 @@
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import U2f = require("u2f");
import { IU2fHandler } from "../../src/lib/authentication/u2f/IU2fHandler";
import { IU2fHandler } from "./IU2fHandler";
export class U2fHandlerStub implements IU2fHandler {

View File

@ -1,155 +0,0 @@
export interface UserLdapConfiguration {
url: string;
base_dn: string;
additional_users_dn?: string;
users_filter?: string;
additional_groups_dn?: string;
groups_filter?: string;
group_name_attribute?: string;
mail_attribute?: string;
user: string; // admin username
password: string; // admin password
}
export interface LdapConfiguration {
url: string;
users_dn: string;
users_filter: string;
groups_dn: string;
groups_filter: string;
group_name_attribute: string;
mail_attribute: string;
user: string; // admin username
password: string; // admin password
}
type UserName = string;
type GroupName = string;
type DomainPattern = string;
export type ACLPolicy = 'deny' | 'allow';
export type ACLRule = {
domain: string;
policy: ACLPolicy;
resources?: string[];
}
export type ACLDefaultRules = ACLRule[];
export type ACLGroupsRules = { [group: string]: ACLRule[]; };
export type ACLUsersRules = { [user: string]: ACLRule[]; };
export interface ACLConfiguration {
default_policy?: ACLPolicy;
any?: ACLDefaultRules;
groups?: ACLGroupsRules;
users?: ACLUsersRules;
}
export interface SessionRedisOptions {
host: string;
port: number;
}
interface SessionCookieConfiguration {
secret: string;
expiration?: number;
inactivity?: number;
domain: string;
redis?: SessionRedisOptions;
}
export interface EmailNotifierConfiguration {
username: string;
password: string;
sender: string;
service: string;
}
export interface SmtpNotifierConfiguration {
username?: string;
password?: string;
host: string;
port: number;
secure: boolean;
sender: string;
}
export interface FileSystemNotifierConfiguration {
filename: string;
}
export interface NotifierConfiguration {
email?: EmailNotifierConfiguration;
smtp?: SmtpNotifierConfiguration;
filesystem?: FileSystemNotifierConfiguration;
}
export interface MongoStorageConfiguration {
url: string;
database: string;
}
export interface LocalStorageConfiguration {
path?: string;
in_memory?: boolean;
}
export interface StorageConfiguration {
local?: LocalStorageConfiguration;
mongo?: MongoStorageConfiguration;
}
export interface RegulationConfiguration {
max_retries: number;
find_time: number;
ban_time: number;
}
declare type AuthenticationMethod = 'two_factor' | 'single_factor';
declare type AuthenticationMethodPerSubdomain = { [subdomain: string]: AuthenticationMethod }
export interface AuthenticationMethodsConfiguration {
default_method: AuthenticationMethod;
per_subdomain_methods?: AuthenticationMethodPerSubdomain;
}
export interface TOTPConfiguration {
issuer: string;
}
export interface UserConfiguration {
port?: number;
logs_level?: string;
ldap: UserLdapConfiguration;
session: SessionCookieConfiguration;
storage: StorageConfiguration;
notifier: NotifierConfiguration;
authentication_methods?: AuthenticationMethodsConfiguration;
access_control?: ACLConfiguration;
regulation: RegulationConfiguration;
default_redirection_url?: string;
totp?: TOTPConfiguration;
}
export interface AppConfiguration {
port: number;
logs_level: string;
ldap: LdapConfiguration;
session: SessionCookieConfiguration;
storage: StorageConfiguration;
notifier: NotifierConfiguration;
authentication_methods: AuthenticationMethodsConfiguration;
access_control?: ACLConfiguration;
regulation: RegulationConfiguration;
default_redirection_url?: string;
totp: TOTPConfiguration;
}

View File

@ -1,13 +1,11 @@
import * as Assert from "assert";
import {
UserConfiguration,
LdapConfiguration, ACLConfiguration
} from "../../src/lib/configuration/Configuration";
import { ConfigurationParser } from "../../src/lib/configuration/ConfigurationParser";
import { Configuration } from "./schema/Configuration";
import { ACLConfiguration } from "./schema/AclConfiguration";
import { ConfigurationParser } from "./ConfigurationParser";
describe("test config parser", function () {
function buildYamlConfig(): UserConfiguration {
const yaml_config: UserConfiguration = {
describe("configuration/ConfigurationParser", function () {
function buildYamlConfig(): Configuration {
const yaml_config: Configuration = {
port: 8080,
ldap: {
url: "http://ldap",
@ -160,11 +158,11 @@ describe("test config parser", function () {
userConfig.access_control = {} as any;
const config = ConfigurationParser.parse(userConfig);
Assert.deepEqual(config.access_control, {
default_policy: "deny",
default_policy: "allow",
any: [],
users: {},
groups: {}
} as ACLConfiguration);
});
});
});

View File

@ -1,109 +1,39 @@
import * as ObjectPath from "object-path";
import {
AppConfiguration, UserConfiguration, NotifierConfiguration,
ACLConfiguration, LdapConfiguration, SessionRedisOptions,
MongoStorageConfiguration, LocalStorageConfiguration,
UserLdapConfiguration
} from "./Configuration";
import { Configuration, complete } from "./schema/Configuration";
import Ajv = require("ajv");
import Path = require("path");
import Util = require("util");
import { ACLAdapter } from "./adapters/ACLAdapter";
import { TOTPAdapter } from "./adapters/TOTPAdapter";
import { AuthenticationMethodsAdapter } from "./adapters/AuthenticationMethodsAdapter";
import { Validator } from "./Validator";
const LDAP_URL_ENV_VARIABLE = "LDAP_URL";
function get_optional<T>(config: object, path: string, default_value: T): T {
let entry = default_value;
if (ObjectPath.has(config, path)) {
entry = ObjectPath.get<object, T>(config, path);
}
return entry;
}
function ensure_key_existence(config: object, path: string): void {
if (!ObjectPath.has(config, path)) {
throw new Error(`Configuration error: key '${path}' is missing in configuration file`);
}
}
function adaptLdapConfiguration(userConfig: UserLdapConfiguration): LdapConfiguration {
const DEFAULT_USERS_FILTER = "cn={0}";
const DEFAULT_GROUPS_FILTER = "member={dn}";
const DEFAULT_GROUP_NAME_ATTRIBUTE = "cn";
const DEFAULT_MAIL_ATTRIBUTE = "mail";
let usersDN = userConfig.base_dn;
if (userConfig.additional_users_dn)
usersDN = userConfig.additional_users_dn + "," + usersDN;
let groupsDN = userConfig.base_dn;
if (userConfig.additional_groups_dn)
groupsDN = userConfig.additional_groups_dn + "," + groupsDN;
return {
url: userConfig.url,
users_dn: usersDN,
users_filter: userConfig.users_filter || DEFAULT_USERS_FILTER,
groups_dn: groupsDN,
groups_filter: userConfig.groups_filter || DEFAULT_GROUPS_FILTER,
group_name_attribute: userConfig.group_name_attribute || DEFAULT_GROUP_NAME_ATTRIBUTE,
mail_attribute: userConfig.mail_attribute || DEFAULT_MAIL_ATTRIBUTE,
password: userConfig.password,
user: userConfig.user
};
}
function adaptFromUserConfiguration(userConfiguration: UserConfiguration)
: AppConfiguration {
if (!Validator.isValid(userConfiguration))
throw new Error("Configuration is malformed. Please double check your configuration file.");
const port = userConfiguration.port || 8080;
const ldapConfiguration = adaptLdapConfiguration(userConfiguration.ldap);
const authenticationMethods = AuthenticationMethodsAdapter
.adapt(userConfiguration.authentication_methods);
const totpConfiguration = TOTPAdapter.adapt(userConfiguration.totp);
return {
port: port,
ldap: ldapConfiguration,
session: {
domain: ObjectPath.get<object, string>(userConfiguration, "session.domain"),
secret: ObjectPath.get<object, string>(userConfiguration, "session.secret"),
expiration: get_optional<number>(userConfiguration, "session.expiration", 3600000), // in ms
inactivity: get_optional<number>(userConfiguration, "session.inactivity", undefined),
redis: ObjectPath.get<object, SessionRedisOptions>(userConfiguration, "session.redis")
},
storage: {
local: get_optional<LocalStorageConfiguration>(userConfiguration, "storage.local", undefined),
mongo: get_optional<MongoStorageConfiguration>(userConfiguration, "storage.mongo", undefined)
},
logs_level: get_optional<string>(userConfiguration, "logs_level", "info"),
notifier: ObjectPath.get<object, NotifierConfiguration>(userConfiguration, "notifier"),
access_control: ACLAdapter.adapt(userConfiguration.access_control),
regulation: userConfiguration.regulation,
authentication_methods: authenticationMethods,
default_redirection_url: userConfiguration.default_redirection_url,
totp: totpConfiguration
};
}
export class ConfigurationParser {
static parse(userConfiguration: UserConfiguration): AppConfiguration {
const errors = Validator.isValid(userConfiguration);
if (errors.length > 0) {
errors.forEach((e: string) => { console.log(e); });
private static parseTypes(configuration: Configuration): string[] {
const schema = require(Path.resolve(__dirname, "./Configuration.schema.json"));
const ajv = new Ajv({
allErrors: true,
missingRefs: "fail"
});
ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json"));
const valid = ajv.validate(schema, configuration);
if (!valid)
return ajv.errors.map(
(e: Ajv.ErrorObject) => { return ajv.errorsText([e]); });
return [];
}
static parse(configuration: Configuration): Configuration {
const validationErrors = this.parseTypes(configuration);
if (validationErrors.length > 0) {
validationErrors.forEach((e: string) => { console.log(e); });
throw new Error("Malformed configuration. Please double-check your configuration file.");
}
const appConfiguration = adaptFromUserConfiguration(userConfiguration);
const ldapUrl = process.env[LDAP_URL_ENV_VARIABLE];
if (ldapUrl)
appConfiguration.ldap.url = ldapUrl;
const [newConfiguration, completionErrors] = complete(configuration);
return appConfiguration;
if (completionErrors.length > 0) {
completionErrors.forEach((e: string) => { console.log(e); });
throw new Error("Malformed configuration. Please double-check your configuration file.");
}
return newConfiguration;
}
}

View File

@ -1,15 +1,15 @@
import { SessionConfigurationBuilder } from "../src/lib/configuration/SessionConfigurationBuilder";
import { AppConfiguration } from "../src/lib/configuration/Configuration";
import { GlobalDependencies } from "../types/Dependencies";
import { SessionConfigurationBuilder } from "./SessionConfigurationBuilder";
import { Configuration } from "./schema/Configuration";
import { GlobalDependencies } from "../../../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 () {
describe("configuration/SessionConfigurationBuilder", function () {
it("should return session options without redis options", function () {
const configuration: AppConfiguration = {
const configuration: Configuration = {
access_control: {
default_policy: "deny",
any: [],
@ -22,9 +22,10 @@ describe("test session configuration builder", function () {
ldap: {
url: "ldap://ldap",
user: "user",
base_dn: "dc=example,dc=com",
password: "password",
groups_dn: "ou=groups,dc=example,dc=com",
users_dn: "ou=users,dc=example,dc=com",
additional_groups_dn: "ou=groups",
additional_users_dn: "ou=users",
group_name_attribute: "",
groups_filter: "",
mail_attribute: "",
@ -87,7 +88,7 @@ describe("test session configuration builder", function () {
});
it("should return session options with redis options", function () {
const configuration: AppConfiguration = {
const configuration: Configuration = {
access_control: {
default_policy: "deny",
any: [],
@ -101,8 +102,9 @@ describe("test session configuration builder", function () {
url: "ldap://ldap",
user: "user",
password: "password",
groups_dn: "ou=groups,dc=example,dc=com",
users_dn: "ou=users,dc=example,dc=com",
base_dn: "dc=example,dc=com",
additional_groups_dn: "ou=groups",
additional_users_dn: "ou=users",
group_name_attribute: "",
groups_filter: "",
mail_attribute: "",

View File

@ -1,11 +1,11 @@
import ExpressSession = require("express-session");
import { AppConfiguration } from "./Configuration";
import { Configuration } from "./schema/Configuration";
import { GlobalDependencies } from "../../../types/Dependencies";
export class SessionConfigurationBuilder {
static build(configuration: AppConfiguration, deps: GlobalDependencies): ExpressSession.SessionOptions {
static build(configuration: Configuration, deps: GlobalDependencies): ExpressSession.SessionOptions {
const sessionOptions: ExpressSession.SessionOptions = {
secret: configuration.session.secret,
resave: false,

View File

@ -1,94 +0,0 @@
import Ajv = require("ajv");
import Path = require("path");
import Util = require("util");
import {
UserConfiguration, StorageConfiguration,
NotifierConfiguration, AuthenticationMethodsConfiguration
} from "./Configuration";
import { MethodCalculator } from "../authentication/MethodCalculator";
function validateSchema(configuration: UserConfiguration): string[] {
const schema = require(Path.resolve(__dirname, "./Configuration.schema.json"));
const ajv = new Ajv({
allErrors: true,
missingRefs: "fail"
});
ajv.addMetaSchema(require("ajv/lib/refs/json-schema-draft-06.json"));
const valid = ajv.validate(schema, configuration);
if (!valid)
return ajv.errors.map(
(e: Ajv.ErrorObject) => { return ajv.errorsText([e]); });
return [];
}
function diff(a: string[], b: string[]) {
return a.filter(function(i) {return b.indexOf(i) < 0; });
}
function validateUnknownKeys(path: string, obj: any, knownKeys: string[]) {
const keysSet = Object.keys(obj);
const unknownKeysSet = diff(keysSet, knownKeys);
if (unknownKeysSet.length > 0) {
const unknownKeys = Array.from(unknownKeysSet);
return unknownKeys.map((k: string) => { return Util.format("data.%s has unknown key '%s'", path, k); });
}
return [];
}
function validateStorage(storage: any): string[] {
const ERROR = "Storage must be either 'local' or 'mongo'";
if (!storage)
return [];
const errors = validateUnknownKeys("storage", storage, ["local", "mongo"]);
if (errors.length > 0)
return errors;
if (storage.local && storage.mongo)
return [ERROR];
if (!storage.local && !storage.mongo)
return [ERROR];
return [];
}
function validateNotifier(notifier: NotifierConfiguration,
authenticationMethods: AuthenticationMethodsConfiguration): string[] {
const ERROR = "Notifier must be either 'filesystem', 'email' or 'smtp'";
if (!notifier)
return [];
if (!MethodCalculator.isSingleFactorOnlyMode(authenticationMethods)) {
if (Object.keys(notifier).length != 1)
return ["A notifier needs to be declared when server is used with two-factor"];
if (notifier && notifier.filesystem && notifier.email && notifier.smtp)
return [ERROR];
if (notifier && !notifier.filesystem && !notifier.email && !notifier.smtp)
return [ERROR];
}
const errors = validateUnknownKeys("notifier", notifier, ["filesystem", "email", "smtp"]);
if (errors.length > 0)
return errors;
return [];
}
export class Validator {
static isValid(configuration: any): string[] {
const schemaErrors = validateSchema(configuration);
const storageErrors = validateStorage(configuration.storage);
const notifierErrors = validateNotifier(configuration.notifier,
configuration.authentication_methods);
return schemaErrors
.concat(storageErrors)
.concat(notifierErrors);
}
}

View File

@ -1,39 +0,0 @@
import { ACLConfiguration } from "../Configuration";
import { ObjectCloner } from "../../utils/ObjectCloner";
const DEFAULT_POLICY = "deny";
function adaptDefaultPolicy(configuration: ACLConfiguration) {
if (!configuration.default_policy)
configuration.default_policy = DEFAULT_POLICY;
if (configuration.default_policy != "deny" && configuration.default_policy != "allow")
configuration.default_policy = DEFAULT_POLICY;
}
function adaptAny(configuration: ACLConfiguration) {
if (!configuration.any || !(configuration.any.constructor === Array))
configuration.any = [];
}
function adaptGroups(configuration: ACLConfiguration) {
if (!configuration.groups || !(configuration.groups.constructor === Object))
configuration.groups = {};
}
function adaptUsers(configuration: ACLConfiguration) {
if (!configuration.users || !(configuration.users.constructor === Object))
configuration.users = {};
}
export class ACLAdapter {
static adapt(configuration: ACLConfiguration): ACLConfiguration {
if (!configuration) return;
const newConfiguration: ACLConfiguration = ObjectCloner.clone(configuration);
adaptDefaultPolicy(newConfiguration);
adaptAny(newConfiguration);
adaptGroups(newConfiguration);
adaptUsers(newConfiguration);
return newConfiguration;
}
}

View File

@ -1,30 +0,0 @@
import { AuthenticationMethodsConfiguration } from "../Configuration";
import { ObjectCloner } from "../../utils/ObjectCloner";
function clone(obj: any): any {
return JSON.parse(JSON.stringify(obj));
}
export class AuthenticationMethodsAdapter {
static adapt(authentication_methods: AuthenticationMethodsConfiguration)
: AuthenticationMethodsConfiguration {
if (!authentication_methods) {
return {
default_method: "two_factor",
per_subdomain_methods: {}
};
}
const newAuthMethods: AuthenticationMethodsConfiguration
= ObjectCloner.clone(authentication_methods);
if (!newAuthMethods.default_method)
newAuthMethods.default_method = "two_factor";
if (!newAuthMethods.per_subdomain_methods ||
newAuthMethods.per_subdomain_methods.constructor !== Object)
newAuthMethods.per_subdomain_methods = {};
return newAuthMethods;
}
}

View File

@ -1,20 +0,0 @@
import { TOTPConfiguration } from "../Configuration";
import { ObjectCloner } from "../../utils/ObjectCloner";
const DEFAULT_ISSUER = "authelia.com";
export class TOTPAdapter {
static adapt(configuration: TOTPConfiguration): TOTPConfiguration {
const newConfiguration = {
issuer: DEFAULT_ISSUER
};
if (!configuration)
return newConfiguration;
if (configuration && configuration.issuer)
newConfiguration.issuer = configuration.issuer;
return newConfiguration;
}
}

View File

@ -0,0 +1,14 @@
import { ACLConfiguration, complete } from "./AclConfiguration";
import Assert = require("assert");
describe("configuration/schema/AclConfiguration", function() {
it("should complete ACLConfiguration", function() {
const configuration: ACLConfiguration = {};
const newConfiguration = complete(configuration);
Assert.deepEqual(newConfiguration.default_policy, "allow");
Assert.deepEqual(newConfiguration.any, []);
Assert.deepEqual(newConfiguration.groups, {});
Assert.deepEqual(newConfiguration.users, {});
});
});

View File

@ -0,0 +1,42 @@
export type ACLPolicy = "deny" | "allow";
export type ACLRule = {
domain: string;
policy: ACLPolicy;
resources?: string[];
};
export type ACLDefaultRules = ACLRule[];
export type ACLGroupsRules = { [group: string]: ACLRule[]; };
export type ACLUsersRules = { [user: string]: ACLRule[]; };
export interface ACLConfiguration {
default_policy?: ACLPolicy;
any?: ACLDefaultRules;
groups?: ACLGroupsRules;
users?: ACLUsersRules;
}
export function complete(configuration: ACLConfiguration): ACLConfiguration {
const newConfiguration: ACLConfiguration = (configuration)
? JSON.parse(JSON.stringify(configuration)) : {};
if (!newConfiguration.default_policy) {
newConfiguration.default_policy = "allow";
}
if (!newConfiguration.any) {
newConfiguration.any = [];
}
if (!newConfiguration.groups) {
newConfiguration.groups = {};
}
if (!newConfiguration.users) {
newConfiguration.users = {};
}
return newConfiguration;
}

View File

@ -0,0 +1,12 @@
import Assert = require("assert");
import { AuthenticationMethodsConfiguration, complete } from "./AuthenticationMethodsConfiguration";
describe("configuration/schema/AuthenticationMethodsConfiguration", function() {
it("should ensure at least one key is provided", function() {
const configuration: AuthenticationMethodsConfiguration = {};
const newConfiguration = complete(configuration);
Assert.deepEqual(newConfiguration.default_method, "two_factor");
Assert.deepEqual(newConfiguration.per_subdomain_methods, []);
});
});

View File

@ -0,0 +1,21 @@
export type AuthenticationMethod = "two_factor" | "single_factor";
export type AuthenticationMethodPerSubdomain = { [subdomain: string]: AuthenticationMethod };
export interface AuthenticationMethodsConfiguration {
default_method?: AuthenticationMethod;
per_subdomain_methods?: AuthenticationMethodPerSubdomain;
}
export function complete(configuration: AuthenticationMethodsConfiguration): AuthenticationMethodsConfiguration {
const newConfiguration: AuthenticationMethodsConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
if (!newConfiguration.default_method) {
newConfiguration.default_method = "two_factor";
}
if (!newConfiguration.per_subdomain_methods) {
newConfiguration.per_subdomain_methods = {};
}
return newConfiguration;
}

View File

@ -0,0 +1,55 @@
import { ACLConfiguration, complete as AclConfigurationComplete } from "./AclConfiguration";
import { AuthenticationMethodsConfiguration, complete as AuthenticationMethodsConfigurationComplete } from "./AuthenticationMethodsConfiguration";
import { LdapConfiguration, complete as LdapConfigurationComplete } from "./LdapConfiguration";
import { NotifierConfiguration, complete as NotifierConfigurationComplete } from "./NotifierConfiguration";
import { RegulationConfiguration, complete as RegulationConfigurationComplete } from "./RegulationConfiguration";
import { SessionConfiguration, complete as SessionConfigurationComplete } from "./SessionConfiguration";
import { StorageConfiguration, complete as StorageConfigurationComplete } from "./StorageConfiguration";
import { TotpConfiguration, complete as TotpConfigurationComplete } from "./TotpConfiguration";
import { MethodCalculator } from "../../authentication/MethodCalculator";
export interface Configuration {
access_control?: ACLConfiguration;
ldap: LdapConfiguration;
authentication_methods?: AuthenticationMethodsConfiguration;
default_redirection_url?: string;
logs_level?: string;
notifier?: NotifierConfiguration;
port?: number;
regulation?: RegulationConfiguration;
session?: SessionConfiguration;
storage?: StorageConfiguration;
totp?: TotpConfiguration;
}
export function complete(configuration: Configuration): [Configuration, string[]] {
const newConfiguration: Configuration = JSON.parse(JSON.stringify(configuration));
const errors: string[] = [];
newConfiguration.access_control = AclConfigurationComplete(newConfiguration.access_control);
newConfiguration.ldap = LdapConfigurationComplete(newConfiguration.ldap);
newConfiguration.authentication_methods = AuthenticationMethodsConfigurationComplete(newConfiguration.authentication_methods);
if (!newConfiguration.logs_level) {
newConfiguration.logs_level = "info";
}
// In single factor mode, notifier section is optional.
if (!MethodCalculator.isSingleFactorOnlyMode(newConfiguration.authentication_methods)) {
const [notifier, error] = NotifierConfigurationComplete(newConfiguration.notifier);
newConfiguration.notifier = notifier;
if (error) errors.push(error);
}
if (!newConfiguration.port) {
newConfiguration.port = 8080;
}
newConfiguration.regulation = RegulationConfigurationComplete(newConfiguration.regulation);
newConfiguration.session = SessionConfigurationComplete(newConfiguration.session);
newConfiguration.storage = StorageConfigurationComplete(newConfiguration.storage);
newConfiguration.totp = TotpConfigurationComplete(newConfiguration.totp);
return [newConfiguration, errors];
}

View File

@ -0,0 +1,25 @@
import Assert = require("assert");
import { LdapConfiguration, complete } from "./LdapConfiguration";
describe("configuration/schema/AuthenticationMethodsConfiguration", function() {
it("should ensure at least one key is provided", function() {
const configuration: LdapConfiguration = {
url: "ldap.example.com",
base_dn: "dc=example,dc=com",
user: "admin",
password: "password"
};
const newConfiguration = complete(configuration);
Assert.deepEqual(newConfiguration, {
url: "ldap.example.com",
base_dn: "dc=example,dc=com",
user: "admin",
password: "password",
users_filter: "cn={0}",
group_name_attribute: "cn",
groups_filter: "member={dn}",
mail_attribute: "mail"
});
});
});

View File

@ -0,0 +1,40 @@
import Util = require("util");
export interface LdapConfiguration {
url: string;
base_dn: string;
additional_users_dn?: string;
users_filter?: string;
additional_groups_dn?: string;
groups_filter?: string;
group_name_attribute?: string;
mail_attribute?: string;
user: string; // admin username
password: string; // admin password
}
export function complete(configuration: LdapConfiguration): LdapConfiguration {
const newConfiguration: LdapConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
if (!newConfiguration.users_filter) {
newConfiguration.users_filter = "cn={0}";
}
if (!newConfiguration.groups_filter) {
newConfiguration.groups_filter = "member={dn}";
}
if (!newConfiguration.group_name_attribute) {
newConfiguration.group_name_attribute = "cn";
}
if (!newConfiguration.mail_attribute) {
newConfiguration.mail_attribute = "mail";
}
return newConfiguration;
}

View File

@ -0,0 +1,31 @@
import Assert = require("assert");
import { NotifierConfiguration, complete } from "./NotifierConfiguration";
describe("configuration/schema/NotifierConfiguration", function() {
it("should ensure at least one key is provided", function() {
const configuration: NotifierConfiguration = {};
const [newConfiguration, error] = complete(configuration);
Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'");
});
it("should ensure there is no more than one key", function() {
const configuration: NotifierConfiguration = {
smtp: {
host: "smtp.example.com",
port: 25,
secure: false,
sender: "test@example.com"
},
email: {
username: "test",
password: "test",
sender: "test@example.com",
service: "gmail"
}
};
const [newConfiguration, error] = complete(configuration);
Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'");
});
});

View File

@ -0,0 +1,42 @@
export interface EmailNotifierConfiguration {
username: string;
password: string;
sender: string;
service: string;
}
export interface SmtpNotifierConfiguration {
username?: string;
password?: string;
host: string;
port: number;
secure: boolean;
sender: string;
}
export interface FileSystemNotifierConfiguration {
filename: string;
}
export interface NotifierConfiguration {
email?: EmailNotifierConfiguration;
smtp?: SmtpNotifierConfiguration;
filesystem?: FileSystemNotifierConfiguration;
}
export function complete(configuration: NotifierConfiguration): [NotifierConfiguration, string] {
const newConfiguration: NotifierConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
const ERROR = "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'";
if (Object.keys(newConfiguration).length != 1)
return [newConfiguration, ERROR];
const key = Object.keys(newConfiguration)[0];
if (key != "filesystem" && key != "smtp" && key != "email")
return [newConfiguration, ERROR];
return [newConfiguration, undefined];
}

View File

@ -0,0 +1,13 @@
import Assert = require("assert");
import { RegulationConfiguration, complete } from "./RegulationConfiguration";
describe("configuration/schema/RegulationConfiguration", function() {
it("should return default regulation configuration", function() {
const configuration: RegulationConfiguration = {};
const newConfiguration = complete(configuration);
Assert.equal(newConfiguration.ban_time, 300);
Assert.equal(newConfiguration.find_time, 120);
Assert.equal(newConfiguration.max_retries, 3);
});
});

View File

@ -0,0 +1,23 @@
export interface RegulationConfiguration {
max_retries?: number;
find_time?: number;
ban_time?: number;
}
export function complete(configuration: RegulationConfiguration): RegulationConfiguration {
const newConfiguration: RegulationConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
if (!newConfiguration.max_retries) {
newConfiguration.max_retries = 3;
}
if (!newConfiguration.find_time) {
newConfiguration.find_time = 120; // seconds
}
if (!newConfiguration.ban_time) {
newConfiguration.ban_time = 300; // seconds
}
return newConfiguration;
}

View File

@ -0,0 +1,15 @@
import Assert = require("assert");
import { SessionConfiguration, complete } from "./SessionConfiguration";
describe("configuration/schema/SessionConfiguration", function() {
it("should return default regulation configuration", function() {
const configuration: SessionConfiguration = {
domain: "example.com",
secret: "unsecure_secret"
};
const newConfiguration = complete(configuration);
Assert.equal(newConfiguration.expiration, 3600000);
Assert.equal(newConfiguration.inactivity, undefined);
});
});

View File

@ -0,0 +1,26 @@
export interface SessionRedisOptions {
host: string;
port: number;
}
export interface SessionConfiguration {
domain: string;
secret: string;
expiration?: number;
inactivity?: number;
redis?: SessionRedisOptions;
}
export function complete(configuration: SessionConfiguration): SessionConfiguration {
const newConfiguration: SessionConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
if (!newConfiguration.expiration) {
newConfiguration.expiration = 3600000; // 1 hour
}
if (!newConfiguration.inactivity) {
newConfiguration.inactivity = undefined; // disabled
}
return newConfiguration;
}

View File

@ -0,0 +1,15 @@
import Assert = require("assert");
import { StorageConfiguration, complete } from "./StorageConfiguration";
describe("configuration/schema/StorageConfiguration", function() {
it("should return default regulation configuration", function() {
const configuration: StorageConfiguration = {};
const newConfiguration = complete(configuration);
Assert.deepEqual(newConfiguration, {
local: {
in_memory: true
}
});
});
});

View File

@ -0,0 +1,25 @@
export interface MongoStorageConfiguration {
url: string;
database: string;
}
export interface LocalStorageConfiguration {
path?: string;
in_memory?: boolean;
}
export interface StorageConfiguration {
local?: LocalStorageConfiguration;
mongo?: MongoStorageConfiguration;
}
export function complete(configuration: StorageConfiguration): StorageConfiguration {
const newConfiguration: StorageConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
if (!newConfiguration.local && !newConfiguration.mongo) {
newConfiguration.local = {
in_memory: true
};
}
return newConfiguration;
}

View File

@ -0,0 +1,13 @@
export interface TotpConfiguration {
issuer: string;
}
export function complete(configuration: TotpConfiguration): TotpConfiguration {
const newConfiguration: TotpConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
if (!newConfiguration.issuer) {
newConfiguration.issuer = "authelia.com";
}
return newConfiguration;
}

View File

@ -0,0 +1,9 @@
export interface UserInfo {
username: string;
password_hash: string;
email: string;
groups?: string[];
}
export type UserDatabaseConfiguration = UserInfo[];

View File

@ -1,9 +1,9 @@
import Assert = require("assert");
import Sinon = require("sinon");
import MongoDB = require("mongodb");
import { MongoClient } from "../../../src/lib/connectors/mongo/MongoClient";
import { MongoClient } from "./MongoClient";
describe("MongoClient", function () {
describe("connectors/mongo/MongoClient", function () {
let mongoClientConnectStub: Sinon.SinonStub;
let mongoDatabase: any;
let mongoDatabaseCollectionStub: Sinon.SinonStub;

View File

@ -2,10 +2,10 @@ import Assert = require("assert");
import Sinon = require("sinon");
import MongoDB = require("mongodb");
import BluebirdPromise = require("bluebird");
import { IMongoClient } from "../../../src/lib/connectors/mongo/IMongoClient";
import { MongoConnector } from "../../../src/lib/connectors/mongo/MongoConnector";
import { IMongoClient } from "./IMongoClient";
import { MongoConnector } from "./MongoConnector";
describe("MongoConnector", function () {
describe("connectors/mongo/MongoConnector", function () {
let mongoClientConnectStub: Sinon.SinonStub;
describe("create", function () {

View File

@ -1,7 +1,7 @@
import Assert = require("assert");
import { MongoConnectorFactory } from "../../../src/lib/connectors/mongo/MongoConnectorFactory";
import { MongoConnectorFactory } from "./MongoConnectorFactory";
describe("MongoConnectorFactory", function () {
describe("connectors/mongo/MongoConnectorFactory", function () {
describe("create", function () {
it("should create a connector", function () {
const factory = new MongoConnectorFactory();

View File

@ -1,16 +1,16 @@
import { Authenticator } from "../../src/lib/ldap/Authenticator";
import { LdapConfiguration } from "../../src/lib/configuration/Configuration";
import { Authenticator } from "./Authenticator";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import Assert = require("assert");
import { ClientFactoryStub } from "../mocks/ldap/ClientFactoryStub";
import { ClientStub } from "../mocks/ldap/ClientStub";
import { ClientFactoryStub } from "./ClientFactoryStub.spec";
import { ClientStub } from "./ClientStub.spec";
describe("test ldap authentication", function () {
describe("ldap/Authenticator", function () {
const USERNAME = "username";
const PASSWORD = "password";
@ -31,9 +31,10 @@ describe("test ldap authentication", function () {
ldapConfig = {
url: "http://localhost:324",
users_dn: "ou=users,dc=example,dc=com",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
base_dn: "dc=example,dc=com",
users_filter: "cn={0}",
groups_dn: "ou=groups,dc=example,dc=com",
groups_filter: "member={0}",
mail_attribute: "mail",
group_name_attribute: "cn",

View File

@ -6,7 +6,7 @@ import { IClientFactory } from "./IClientFactory";
import { GroupsAndEmails } from "./IClient";
import { IAuthenticator } from "./IAuthenticator";
import { LdapConfiguration } from "../configuration/Configuration";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import { EmailsAndGroupsRetriever } from "./EmailsAndGroupsRetriever";

View File

@ -1,6 +1,6 @@
import BluebirdPromise = require("bluebird");
import { IAuthenticator } from "../../../src/lib/ldap/IAuthenticator";
import { GroupsAndEmails } from "../../../src/lib/ldap/IClient";
import { GroupsAndEmails } from "./IClient";
import Sinon = require("sinon");
export class AuthenticatorStub implements IAuthenticator {

View File

@ -1,15 +1,15 @@
import { LdapConfiguration } from "../../src/lib/configuration/Configuration";
import { Client } from "../../src/lib/ldap/Client";
import { LdapClientFactoryStub } from "../mocks/ldap/LdapClientFactoryStub";
import { LdapClientStub } from "../mocks/ldap/LdapClientStub";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import { Client } from "./Client";
import { LdapClientFactoryStub } from "./LdapClientFactoryStub.spec";
import { LdapClientStub } from "./LdapClientStub.spec";
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import Assert = require("assert");
import Winston = require("winston");
describe("test authelia ldap client", function () {
describe("ldap/Client", function () {
const USERNAME = "username";
const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
const ADMIN_PASSWORD = "password";
@ -17,9 +17,10 @@ describe("test authelia ldap client", function () {
it("should replace {0} by username when searching for groups in LDAP", function () {
const options: LdapConfiguration = {
url: "ldap://ldap",
users_dn: "ou=users,dc=example,dc=com",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
base_dn: "dc=example,dc=com",
users_filter: "cn={0}",
groups_dn: "ou=groups,dc=example,dc=com",
groups_filter: "member=cn={0},ou=users,dc=example,dc=com",
group_name_attribute: "cn",
mail_attribute: "mail",
@ -46,9 +47,10 @@ describe("test authelia ldap client", function () {
const USER_DN = "cn=user1,ou=users,dc=example,dc=com";
const options: LdapConfiguration = {
url: "ldap://ldap",
users_dn: "ou=users,dc=example,dc=com",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
base_dn: "dc=example,dc=com",
users_filter: "cn={0}",
groups_dn: "ou=groups,dc=example,dc=com",
groups_filter: "member={dn}",
group_name_attribute: "cn",
mail_attribute: "mail",
@ -91,9 +93,10 @@ describe("test authelia ldap client", function () {
const USER_DN = "cn=user1,ou=users,dc=example,dc=com";
const options: LdapConfiguration = {
url: "ldap://ldap",
users_dn: "ou=users,dc=example,dc=com",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
base_dn: "dc=example,dc=com",
users_filter: "cn={0}",
groups_dn: "ou=groups,dc=example,dc=com",
groups_filter: "member={dn}",
group_name_attribute: "cn",
mail_attribute: "custom_mail",

View File

@ -4,7 +4,7 @@ import { EventEmitter } from "events";
import { IClient, GroupsAndEmails } from "./IClient";
import { ILdapClient } from "./ILdapClient";
import { ILdapClientFactory } from "./ILdapClientFactory";
import { LdapConfiguration } from "../configuration/Configuration";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import { Winston } from "../../../types/Dependencies";
import Util = require("util");
import { HashGenerator } from "../utils/HashGenerator";
@ -16,6 +16,9 @@ export class Client implements IClient {
private logger: Winston;
private options: LdapConfiguration;
private groupsSearchBase: string;
private usersSearchBase: string;
constructor(userDN: string, password: string, options: LdapConfiguration,
ldapClientFactory: ILdapClientFactory, logger: Winston) {
this.options = options;
@ -23,6 +26,14 @@ export class Client implements IClient {
this.userDN = userDN;
this.password = password;
this.ldapClient = ldapClientFactory.create();
this.groupsSearchBase = (this.options.additional_groups_dn)
? Util.format("%s,%s", this.options.additional_groups_dn, this.options.base_dn)
: this.options.base_dn;
this.usersSearchBase = (this.options.additional_users_dn)
? Util.format("%s,%s", this.options.additional_users_dn, this.options.base_dn)
: this.options.base_dn;
}
open(): BluebirdPromise<void> {
@ -64,7 +75,7 @@ export class Client implements IClient {
attributes: [that.options.group_name_attribute],
filter: groupsFilter
};
return that.ldapClient.searchAsync(that.options.groups_dn, query);
return that.ldapClient.searchAsync(that.groupsSearchBase, query);
})
.then(function (docs: { cn: string }[]) {
const groups = docs.map((doc: any) => { return doc.cn; });
@ -85,7 +96,7 @@ export class Client implements IClient {
};
that.logger.debug("LDAP: searching for user dn of %s", username);
return that.ldapClient.searchAsync(this.options.users_dn, query)
return that.ldapClient.searchAsync(this.usersSearchBase, query)
.then(function (users: { dn: string }[]) {
if (users.length > 0) {
that.logger.debug("LDAP: retrieved user dn is %s", users[0].dn);

View File

@ -3,7 +3,7 @@ import { IClient } from "./IClient";
import { Client } from "./Client";
import { SanitizedClient } from "./SanitizedClient";
import { ILdapClientFactory } from "./ILdapClientFactory";
import { LdapConfiguration } from "../configuration/Configuration";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import Ldapjs = require("ldapjs");
import Winston = require("winston");

View File

@ -1,6 +1,6 @@
import BluebirdPromise = require("bluebird");
import { IClient, GroupsAndEmails } from "../../../src/lib/ldap/IClient";
import { IClient, GroupsAndEmails } from "./IClient";
import Sinon = require("sinon");
export class ClientStub implements IClient {

View File

@ -3,7 +3,7 @@ import exceptions = require("../Exceptions");
import ldapjs = require("ldapjs");
import { Client } from "./Client";
import { IClientFactory } from "./IClientFactory";
import { LdapConfiguration } from "../configuration/Configuration";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import { GroupsAndEmails } from "./IClient";

View File

@ -1,15 +1,15 @@
import { EmailsRetriever } from "../../src/lib/ldap/EmailsRetriever";
import { LdapConfiguration } from "../../src/lib/configuration/Configuration";
import { EmailsRetriever } from "./EmailsRetriever";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import Assert = require("assert");
import { ClientFactoryStub } from "../mocks/ldap/ClientFactoryStub";
import { ClientStub } from "../mocks/ldap/ClientStub";
import { ClientFactoryStub } from "./ClientFactoryStub.spec";
import { ClientStub } from "./ClientStub.spec";
describe("test emails retriever", function () {
describe("ldap/EmailsRetriever", function () {
const USERNAME = "username";
const ADMIN_USER_DN = "cn=admin,dc=example,dc=com";
const ADMIN_PASSWORD = "password";
@ -28,8 +28,9 @@ describe("test emails retriever", function () {
url: "http://ldap",
user: ADMIN_USER_DN,
password: ADMIN_PASSWORD,
users_dn: "ou=users,dc=example,dc=com",
groups_dn: "ou=groups,dc=example,dc=com",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
base_dn: "dc=example,dc=com",
group_name_attribute: "cn",
groups_filter: "cn={0}",
mail_attribute: "mail",

View File

@ -5,7 +5,7 @@ import { Client } from "./Client";
import { IClientFactory } from "./IClientFactory";
import { IEmailsRetriever } from "./IEmailsRetriever";
import { LdapConfiguration } from "../configuration/Configuration";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
export class EmailsRetriever implements IEmailsRetriever {

View File

@ -1,6 +1,6 @@
import BluebirdPromise = require("bluebird");
import { IClient } from "../../../src/lib/ldap/IClient";
import { IEmailsRetriever } from "../../../src/lib/ldap/IEmailsRetriever";
import { IClient } from "./IClient";
import { IEmailsRetriever } from "./IEmailsRetriever";
import Sinon = require("sinon");
export class EmailsRetrieverStub implements IEmailsRetriever {

View File

@ -1,7 +1,7 @@
import Assert = require("assert");
import { InputsSanitizer } from "../../src/lib/ldap/InputsSanitizer";
import { InputsSanitizer } from "./InputsSanitizer";
describe("test InputsSanitizer", function () {
describe("ldap/InputsSanitizer", function () {
it("should fail when special characters are used", function () {
Assert.throws(() => { InputsSanitizer.sanitize("ab,c"); }, Error);
Assert.throws(() => { InputsSanitizer.sanitize("a\\bc"); }, Error);

View File

@ -1,7 +1,7 @@
import { ILdapClientFactory } from "./ILdapClientFactory";
import { ILdapClient } from "./ILdapClient";
import { LdapClient } from "./LdapClient";
import { LdapConfiguration } from "../configuration/Configuration";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import Ldapjs = require("ldapjs");

View File

@ -1,7 +1,7 @@
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import { ILdapClientFactory } from "../../../src/lib/ldap/ILdapClientFactory";
import { ILdapClient } from "../../../src/lib/ldap/ILdapClient";
import { ILdapClientFactory } from "./ILdapClientFactory";
import { ILdapClient } from "./ILdapClient";
export class LdapClientFactoryStub implements ILdapClientFactory {
createStub: Sinon.SinonStub;

View File

@ -1,6 +1,6 @@
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import { ILdapClient } from "../../../src/lib/ldap/ILdapClient";
import { ILdapClient } from "./ILdapClient";
export class LdapClientStub implements ILdapClient {
bindAsyncStub: Sinon.SinonStub;

View File

@ -1,13 +1,13 @@
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import Assert = require("assert");
import { PasswordUpdater } from "../../src/lib/ldap/PasswordUpdater";
import { LdapConfiguration } from "../../src/lib/configuration/Configuration";
import { ClientFactoryStub } from "../mocks/ldap/ClientFactoryStub";
import { ClientStub } from "../mocks/ldap/ClientStub";
import { HashGenerator } from "../../src/lib/utils/HashGenerator";
import { PasswordUpdater } from "./PasswordUpdater";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import { ClientFactoryStub } from "./ClientFactoryStub.spec";
import { ClientStub } from "./ClientStub.spec";
import { HashGenerator } from "../utils/HashGenerator";
describe("test password update", function () {
describe("ldap/PasswordUpdater", function () {
const USERNAME = "username";
const NEW_PASSWORD = "new-password";
@ -28,8 +28,9 @@ describe("test password update", function () {
url: "http://ldap",
user: ADMIN_USER_DN,
password: ADMIN_PASSWORD,
users_dn: "ou=users,dc=example,dc=com",
groups_dn: "ou=groups,dc=example,dc=com",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
base_dn: "dc=example,dc=com",
group_name_attribute: "cn",
groups_filter: "cn={0}",
mail_attribute: "mail",

View File

@ -4,7 +4,7 @@ import ldapjs = require("ldapjs");
import { Client } from "./Client";
import { IPasswordUpdater } from "./IPasswordUpdater";
import { LdapConfiguration } from "../configuration/Configuration";
import { LdapConfiguration } from "../configuration/schema/LdapConfiguration";
import { IClientFactory } from "./IClientFactory";

View File

@ -1,6 +1,6 @@
import BluebirdPromise = require("bluebird");
import { IClient } from "../../../src/lib/ldap/IClient";
import { IPasswordUpdater } from "../../../src/lib/ldap/IPasswordUpdater";
import { IClient } from "./IClient";
import { IPasswordUpdater } from "./IPasswordUpdater";
import Sinon = require("sinon");
export class PasswordUpdaterStub implements IPasswordUpdater {

View File

@ -1,8 +1,8 @@
import BluebirdPromise = require("bluebird");
import { ClientStub } from "../mocks/ldap/ClientStub";
import { SanitizedClient } from "../../src/lib/ldap/SanitizedClient";
import { ClientStub } from "./ClientStub.spec";
import { SanitizedClient } from "./SanitizedClient";
describe("test SanitizedClient", function () {
describe("ldap/SanitizedClient", function () {
let client: SanitizedClient;
beforeEach(function () {

View File

@ -1,6 +1,6 @@
import { IRequestLogger } from "../../src/lib/logging/IRequestLogger";
import { IRequestLogger } from "./IRequestLogger";
import Sinon = require("sinon");
import { RequestLogger } from "../../src/lib/logging/RequestLogger";
import { RequestLogger } from "./RequestLogger";
import Winston = require("winston");
import Express = require("express");

View File

@ -2,11 +2,11 @@ import * as sinon from "sinon";
import * as Assert from "assert";
import BluebirdPromise = require("bluebird");
import { MailSenderStub } from "../mocks/notifiers/MailSenderStub";
import EMailNotifier = require("../../src/lib/notifiers/EMailNotifier");
import { MailSenderStub } from "./MailSenderStub.spec";
import EmailNotifier = require("./EmailNotifier");
describe("test email notifier", function () {
describe("notifiers/EmailNotifier", function () {
it("should send an email to given user", function () {
const mailSender = new MailSenderStub();
const options = {
@ -17,7 +17,7 @@ describe("test email notifier", function () {
};
mailSender.sendStub.returns(BluebirdPromise.resolve());
const sender = new EMailNotifier.EMailNotifier(options, mailSender);
const sender = new EmailNotifier.EmailNotifier(options, mailSender);
const subject = "subject";
const url = "http://test.com";
@ -39,7 +39,7 @@ describe("test email notifier", function () {
};
mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail")));
const sender = new EMailNotifier.EMailNotifier(options, mailSender);
const sender = new EmailNotifier.EmailNotifier(options, mailSender);
const subject = "subject";
const url = "http://test.com";

View File

@ -2,10 +2,10 @@
import * as BluebirdPromise from "bluebird";
import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier";
import { EmailNotifierConfiguration } from "../configuration/Configuration";
import { EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration";
import { IMailSender } from "./IMailSender";
export class EMailNotifier extends AbstractEmailNotifier {
export class EmailNotifier extends AbstractEmailNotifier {
private mailSender: IMailSender;
private sender: string;

View File

@ -4,7 +4,7 @@ import * as Fs from "fs";
import { INotifier } from "./INotifier";
import { Identity } from "../../../types/Identity";
import { FileSystemNotifierConfiguration } from "../configuration/Configuration";
import { FileSystemNotifierConfiguration } from "../configuration/schema/NotifierConfiguration";
export class FileSystemNotifier implements INotifier {
private filename: string;

View File

@ -1,5 +1,5 @@
import { IMailSender } from "./IMailSender";
import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/Configuration";
import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration";
export interface IMailSenderBuilder {
buildEmail(options: EmailNotifierConfiguration): IMailSender;

View File

@ -1,10 +1,10 @@
import { MailSenderBuilder } from "../../src/lib/notifiers/MailSenderBuilder";
import { MailSenderBuilder } from ".//MailSenderBuilder";
import Nodemailer = require("nodemailer");
import Sinon = require("sinon");
import Assert = require("assert");
describe("test MailSenderBuilder", function() {
describe("notifiers/MailSenderBuilder", function() {
let createTransportStub: Sinon.SinonStub;
beforeEach(function() {
createTransportStub = Sinon.stub(Nodemailer, "createTransport");

View File

@ -3,7 +3,7 @@ import { IMailSenderBuilder } from "./IMailSenderBuilder";
import { MailSender } from "./MailSender";
import Nodemailer = require("nodemailer");
import NodemailerSmtpTransport = require("nodemailer-smtp-transport");
import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/Configuration";
import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../configuration/schema/NotifierConfiguration";
export class MailSenderBuilder implements IMailSenderBuilder {
private nodemailer: typeof Nodemailer;

View File

@ -3,7 +3,7 @@ import BluebirdPromise = require("bluebird");
import Nodemailer = require("nodemailer");
import Sinon = require("sinon");
import { IMailSender } from "../../../src/lib/notifiers/IMailSender";
import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../../../src/lib/configuration/Configuration";
import { SmtpNotifierConfiguration, EmailNotifierConfiguration } from "../../../src/lib/configuration/schema/NotifierConfiguration";
export class MailSenderBuilderStub implements IMailSenderBuilder {
buildEmailStub: Sinon.SinonStub;

View File

@ -3,13 +3,13 @@ import * as sinon from "sinon";
import * as BluebirdPromise from "bluebird";
import * as assert from "assert";
import { NotifierFactory } from "../../src/lib/notifiers/NotifierFactory";
import { EMailNotifier } from "../../src/lib/notifiers/EMailNotifier";
import { SmtpNotifier } from "../../src/lib/notifiers/SmtpNotifier";
import { MailSenderBuilderStub } from "../mocks/notifiers/MailSenderBuilderStub";
import { NotifierFactory } from "./NotifierFactory";
import { EmailNotifier } from "./EmailNotifier";
import { SmtpNotifier } from "./SmtpNotifier";
import { MailSenderBuilderStub } from "./MailSenderBuilderStub.spec";
describe("test notifier factory", function () {
describe("notifiers/NotifierFactory", function () {
let mailSenderBuilderStub: MailSenderBuilderStub;
it("should build a Email Notifier", function () {
const options = {
@ -21,7 +21,7 @@ describe("test notifier factory", function () {
}
};
mailSenderBuilderStub = new MailSenderBuilderStub();
assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof EMailNotifier);
assert(NotifierFactory.build(options, mailSenderBuilderStub) instanceof EmailNotifier);
});
it("should build a SMTP Notifier", function () {

View File

@ -1,10 +1,10 @@
import { NotifierConfiguration } from "../configuration/Configuration";
import { NotifierConfiguration } from "../configuration/schema/NotifierConfiguration";
import Nodemailer = require("nodemailer");
import { INotifier } from "./INotifier";
import { FileSystemNotifier } from "./FileSystemNotifier";
import { EMailNotifier } from "./EMailNotifier";
import { EmailNotifier } from "./EmailNotifier";
import { SmtpNotifier } from "./SmtpNotifier";
import { IMailSender } from "./IMailSender";
import { IMailSenderBuilder } from "./IMailSenderBuilder";
@ -13,7 +13,7 @@ export class NotifierFactory {
static build(options: NotifierConfiguration, mailSenderBuilder: IMailSenderBuilder): INotifier {
if ("email" in options) {
const mailSender = mailSenderBuilder.buildEmail(options.email);
return new EMailNotifier(options.email, mailSender);
return new EmailNotifier(options.email, mailSender);
}
else if ("smtp" in options) {
const mailSender = mailSenderBuilder.buildSmtp(options.smtp);

View File

@ -1,7 +1,7 @@
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import { INotifier } from "../../src/lib/notifiers/INotifier";
import { INotifier } from "./INotifier";
export class NotifierStub implements INotifier {
notifyStub: Sinon.SinonStub;

View File

@ -4,7 +4,7 @@ import * as BluebirdPromise from "bluebird";
import { IMailSender } from "./IMailSender";
import { AbstractEmailNotifier } from "../notifiers/AbstractEmailNotifier";
import { SmtpNotifierConfiguration } from "../configuration/Configuration";
import { SmtpNotifierConfiguration } from "../configuration/schema/NotifierConfiguration";
export class SmtpNotifier extends AbstractEmailNotifier {
private mailSender: IMailSender;

View File

@ -3,12 +3,12 @@ import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import Assert = require("assert");
import { Regulator } from "../../src/lib/regulation/Regulator";
import { Regulator } from "./Regulator";
import MockDate = require("mockdate");
import exceptions = require("../../src/lib/Exceptions");
import { UserDataStoreStub } from "../mocks/storage/UserDataStoreStub";
import exceptions = require("../Exceptions");
import { UserDataStoreStub } from "../storage/UserDataStoreStub.spec";
describe("test authentication regulator", function () {
describe("regulation/Regulator", function () {
const USER1 = "USER1";
const USER2 = "USER2";
let userDataStoreStub: UserDataStoreStub;

View File

@ -0,0 +1,22 @@
import Bluebird = require("bluebird");
import Sinon = require("sinon");
import { IRegulator } from "./IRegulator";
export class RegulatorStub implements IRegulator {
markStub: Sinon.SinonStub;
regulateStub: Sinon.SinonStub;
constructor() {
this.markStub = Sinon.stub();
this.regulateStub = Sinon.stub();
}
mark(userId: string, isAuthenticationSuccessful: boolean): Bluebird<void> {
return this.markStub(userId, isAuthenticationSuccessful);
}
regulate(userId: string): Bluebird<void> {
return this.regulateStub(userId);
}
}

View File

@ -1,12 +1,12 @@
import Sinon = require("sinon");
import Express = require("express");
import Assert = require("assert");
import Get401 from "../../../../src/lib/routes/error/401/get";
import { ServerVariables } from "../../../../src/lib/ServerVariables";
import Get401 from "./get";
import { ServerVariables } from "../../../ServerVariables";
import { ServerVariablesMockBuilder, ServerVariablesMock }
from "../../../mocks/ServerVariablesMockBuilder";
from "../../../ServerVariablesMockBuilder.spec";
describe("Server error 401", function () {
describe("routes/error/401/get", function () {
let vars: ServerVariables;
let mocks: ServerVariablesMock;
let req: any;

View File

@ -1,12 +1,12 @@
import Sinon = require("sinon");
import Express = require("express");
import Assert = require("assert");
import Get403 from "../../../../src/lib/routes/error/403/get";
import { ServerVariables } from "../../../../src/lib/ServerVariables";
import Get403 from "./get";
import { ServerVariables } from "../../../ServerVariables";
import { ServerVariablesMockBuilder, ServerVariablesMock }
from "../../../mocks/ServerVariablesMockBuilder";
from "../../../ServerVariablesMockBuilder.spec";
describe("Server error 403", function () {
describe("routes/error/403/get", function () {
let vars: ServerVariables;
let mocks: ServerVariablesMock;
let req: any;

View File

@ -1,9 +1,9 @@
import Sinon = require("sinon");
import Express = require("express");
import Assert = require("assert");
import Get404 from "../../../../src/lib/routes/error/404/get";
import Get404 from "./get";
describe("Server error 404", function () {
describe("routes/error/404/get", function () {
it("should render the page", function () {
const req = {} as Express.Request;
const res = {

View File

@ -2,18 +2,18 @@
import Sinon = require("sinon");
import BluebirdPromise = require("bluebird");
import Assert = require("assert");
import FirstFactorPost = require("../../../src/lib/routes/firstfactor/post");
import exceptions = require("../../../src/lib/Exceptions");
import { AuthenticationSessionHandler } from "../../../src/lib/AuthenticationSessionHandler";
import { AuthenticationSession } from "../../../types/AuthenticationSession";
import Endpoints = require("../../../../shared/api");
import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator");
import { AccessControllerStub } from "../../mocks/AccessControllerStub";
import ExpressMock = require("../../mocks/express");
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../mocks/ServerVariablesMockBuilder";
import { ServerVariables } from "../../../src/lib/ServerVariables";
import FirstFactorPost = require("./post");
import exceptions = require("../../Exceptions");
import { AuthenticationSessionHandler } from "../../AuthenticationSessionHandler";
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
import Endpoints = require("../../../../../shared/api");
import AuthenticationRegulatorMock = require("../../regulation/RegulatorStub.spec");
import { AccessControllerStub } from "../../access_control/AccessControllerStub.spec";
import ExpressMock = require("../../stubs/express.spec");
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../ServerVariablesMockBuilder.spec";
import { ServerVariables } from "../../ServerVariables";
describe("test the first factor validation route", function () {
describe("routes/firstfactor/post", function () {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let emails: string[];

View File

@ -1,17 +1,17 @@
import PasswordResetFormPost = require("../../../src/lib/routes/password-reset/form/post");
import { PasswordUpdater } from "../../../src/lib/ldap/PasswordUpdater";
import { AuthenticationSessionHandler } from "../../../src/lib/AuthenticationSessionHandler";
import { AuthenticationSession } from "../../../types/AuthenticationSession";
import { UserDataStore } from "../../../src/lib/storage/UserDataStore";
import PasswordResetFormPost = require("./post");
import { PasswordUpdater } from "../../../ldap/PasswordUpdater";
import { AuthenticationSessionHandler } from "../../../AuthenticationSessionHandler";
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
import { UserDataStore } from "../../../storage/UserDataStore";
import Sinon = require("sinon");
import Assert = require("assert");
import BluebirdPromise = require("bluebird");
import ExpressMock = require("../../mocks/express");
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../mocks/ServerVariablesMockBuilder";
import { ServerVariables } from "../../../src/lib/ServerVariables";
import ExpressMock = require("../../../stubs/express.spec");
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../ServerVariablesMockBuilder.spec";
import { ServerVariables } from "../../../ServerVariables";
describe("test reset password route", function () {
describe("routes/password-reset/form/post", function () {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let vars: ServerVariables;
@ -48,8 +48,9 @@ describe("test reset password route", function () {
mail_attribute: "mail",
user: "user",
password: "password",
users_dn: "ou=users,dc=example,dc=com",
groups_dn: "ou=groups,dc=example,dc=com",
additional_users_dn: "ou=users",
additional_groups_dn: "ou=groups",
base_dn: "dc=example,dc=com",
users_filter: "user",
group_name_attribute: "cn",
groups_filter: "groups"

View File

@ -1,18 +1,18 @@
import PasswordResetHandler
from "../../../../src/lib/routes/password-reset/identity/PasswordResetHandler";
import PasswordUpdater = require("../../../../src/lib/ldap/PasswordUpdater");
import { UserDataStore } from "../../../../src/lib/storage/UserDataStore";
from "./PasswordResetHandler";
import PasswordUpdater = require("../../../ldap/PasswordUpdater");
import { UserDataStore } from "../../../storage/UserDataStore";
import Sinon = require("sinon");
import winston = require("winston");
import assert = require("assert");
import BluebirdPromise = require("bluebird");
import ExpressMock = require("../../../mocks/express");
import ExpressMock = require("../../../stubs/express.spec");
import { ServerVariablesMock, ServerVariablesMockBuilder }
from "../../../mocks/ServerVariablesMockBuilder";
import { ServerVariables } from "../../../../src/lib/ServerVariables";
from "../../../ServerVariablesMockBuilder.spec";
import { ServerVariables } from "../../../ServerVariables";
describe("test reset password identity check", function () {
describe("routes/password-reset/identity/PasswordResetHandler", function () {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let mocks: ServerVariablesMock;

View File

@ -4,7 +4,7 @@ import objectPath = require("object-path");
import exceptions = require("../../../Exceptions");
import { Identity } from "../../../../../types/Identity";
import { IdentityValidable } from "../../../IdentityCheckMiddleware";
import { IdentityValidable } from "../../../IdentityValidable";
import { PRE_VALIDATION_TEMPLATE } from "../../../IdentityCheckPreValidationTemplate";
import Constants = require("../constants");
import { IRequestLogger } from "../../../logging/IRequestLogger";

View File

@ -1,14 +1,14 @@
import SecondFactorGet from "../../../src/lib/routes/secondfactor/get";
import SecondFactorGet from "./get";
import { ServerVariablesMockBuilder, ServerVariablesMock }
from "../../mocks/ServerVariablesMockBuilder";
import { ServerVariables } from "../../../src/lib/ServerVariables";
from "../../ServerVariablesMockBuilder.spec";
import { ServerVariables } from "../../ServerVariables";
import Sinon = require("sinon");
import ExpressMock = require("../../mocks/express");
import ExpressMock = require("../../stubs/express.spec");
import Assert = require("assert");
import Endpoints = require("../../../../shared/api");
import Endpoints = require("../../../../../shared/api");
import BluebirdPromise = require("bluebird");
describe("test second factor GET endpoint handler", function () {
describe("routes/secondfactor/get", function () {
let mocks: ServerVariablesMock;
let vars: ServerVariables;
let req: ExpressMock.RequestMock;

View File

@ -1,11 +1,11 @@
import Redirect from "../../../src/lib/routes/secondfactor/redirect";
import ExpressMock = require("../../mocks/express");
import Redirect from "./redirect";
import ExpressMock = require("../../stubs/express.spec");
import { ServerVariablesMockBuilder, ServerVariablesMock }
from "../../mocks/ServerVariablesMockBuilder";
import { ServerVariables } from "../../../src/lib/ServerVariables";
from "../../ServerVariablesMockBuilder.spec";
import { ServerVariables } from "../../ServerVariables";
import Assert = require("assert");
describe("test second factor redirect", function() {
describe("routes/secondfactor/redirect", function() {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let mocks: ServerVariablesMock;

View File

@ -1,15 +1,15 @@
import Sinon = require("sinon");
import RegistrationHandler from "../../../../../src/lib/routes/secondfactor/totp/identity/RegistrationHandler";
import { Identity } from "../../../../../types/Identity";
import { UserDataStore } from "../../../../../src/lib/storage/UserDataStore";
import RegistrationHandler from "./RegistrationHandler";
import { Identity } from "../../../../../../types/Identity";
import { UserDataStore } from "../../../../storage/UserDataStore";
import BluebirdPromise = require("bluebird");
import ExpressMock = require("../../../../mocks/express");
import ExpressMock = require("../../../../stubs/express.spec");
import { ServerVariablesMock, ServerVariablesMockBuilder }
from "../../../../mocks/ServerVariablesMockBuilder";
import { ServerVariables } from "../../../../../src/lib/ServerVariables";
from "../../../../ServerVariablesMockBuilder.spec";
import { ServerVariables } from "../../../../ServerVariables";
import Assert = require("assert");
describe("test totp register", function () {
describe("routes/secondfactor/totp/identity/RegistrationHandler", function () {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let mocks: ServerVariablesMock;

View File

@ -4,7 +4,7 @@ import BluebirdPromise = require("bluebird");
import objectPath = require("object-path");
import { Identity } from "../../../../../../types/Identity";
import { IdentityValidable } from "../../../../IdentityCheckMiddleware";
import { IdentityValidable } from "../../../../IdentityValidable";
import { PRE_VALIDATION_TEMPLATE } from "../../../../IdentityCheckPreValidationTemplate";
import Constants = require("../constants");
import Endpoints = require("../../../../../../../shared/api");
@ -16,18 +16,18 @@ import { IRequestLogger } from "../../../../logging/IRequestLogger";
import { IUserDataStore } from "../../../../storage/IUserDataStore";
import { ITotpHandler } from "../../../../authentication/totp/ITotpHandler";
import { TOTPSecret } from "../../../../../../types/TOTPSecret";
import { TOTPConfiguration } from "../../../../configuration/Configuration";
import { TotpConfiguration } from "../../../../configuration/schema/TotpConfiguration";
export default class RegistrationHandler implements IdentityValidable {
private logger: IRequestLogger;
private userDataStore: IUserDataStore;
private totp: ITotpHandler;
private configuration: TOTPConfiguration;
private configuration: TotpConfiguration;
constructor(logger: IRequestLogger,
userDataStore: IUserDataStore,
totp: ITotpHandler, configuration: TOTPConfiguration) {
totp: ITotpHandler, configuration: TotpConfiguration) {
this.logger = logger;
this.userDataStore = userDataStore;
this.totp = totp;

View File

@ -2,17 +2,17 @@
import BluebirdPromise = require("bluebird");
import Sinon = require("sinon");
import Assert = require("assert");
import Exceptions = require("../../../../../src/lib/Exceptions");
import { AuthenticationSessionHandler } from "../../../../../src/lib/AuthenticationSessionHandler";
import { AuthenticationSession } from "../../../../../types/AuthenticationSession";
import SignPost = require("../../../../../src/lib/routes/secondfactor/totp/sign/post");
import { ServerVariables } from "../../../../../src/lib/ServerVariables";
import Exceptions = require("../../../../Exceptions");
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
import SignPost = require("./post");
import { ServerVariables } from "../../../../ServerVariables";
import ExpressMock = require("../../../../mocks/express");
import { UserDataStoreStub } from "../../../../mocks/storage/UserDataStoreStub";
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../mocks/ServerVariablesMockBuilder";
import ExpressMock = require("../../../../stubs/express.spec");
import { UserDataStoreStub } from "../../../../storage/UserDataStoreStub.spec";
import { ServerVariablesMock, ServerVariablesMockBuilder } from "../../../../ServerVariablesMockBuilder.spec";
describe("test totp route", function () {
describe("routes/secondfactor/totp/sign/post", function () {
let req: ExpressMock.RequestMock;
let res: ExpressMock.ResponseMock;
let authSession: AuthenticationSession;

View File

@ -1,11 +1,9 @@
import Bluebird = require("bluebird");
import Express = require("express");
import exceptions = require("../../../../Exceptions");
import objectPath = require("object-path");
import express = require("express");
import { TOTPSecretDocument } from "../../../../storage/TOTPSecretDocument";
import BluebirdPromise = require("bluebird");
import Endpoints = require("../../../../../../../shared/api");
import redirect from "../../redirect";
import Redirect from "../../redirect";
import ErrorReplies = require("../../../../ErrorReplies");
import { AuthenticationSessionHandler } from "../../../../AuthenticationSessionHandler";
import { AuthenticationSession } from "../../../../../../types/AuthenticationSession";
@ -15,11 +13,11 @@ import { ServerVariables } from "../../../../ServerVariables";
const UNAUTHORIZED_MESSAGE = "Unauthorized access";
export default function (vars: ServerVariables) {
function handler(req: express.Request, res: express.Response): BluebirdPromise<void> {
function handler(req: Express.Request, res: Express.Response): Bluebird<void> {
let authSession: AuthenticationSession;
const token = req.body.token;
return new BluebirdPromise(function (resolve, reject) {
return new Bluebird(function (resolve, reject) {
authSession = AuthenticationSessionHandler.get(req, vars.logger);
vars.logger.info(req, "Initiate TOTP validation for user \"%s\".", authSession.userid);
resolve();
@ -29,12 +27,12 @@ export default function (vars: ServerVariables) {
})
.then(function (doc: TOTPSecretDocument) {
if (!vars.totpHandler.validate(token, doc.secret.base32))
return BluebirdPromise.reject(new Error("Invalid TOTP token."));
return Bluebird.reject(new Error("Invalid TOTP token."));
vars.logger.debug(req, "TOTP validation succeeded.");
authSession.second_factor = true;
redirect(vars)(req, res);
return BluebirdPromise.resolve();
Redirect(vars)(req, res);
return Bluebird.resolve();
})
.catch(ErrorReplies.replyWithError200(req, res, vars.logger,
UserMessages.OPERATION_FAILED));

Some files were not shown because too many files have changed in this diff Show More