Merge pull request #137 from clems4ever/mail-sender

Specify mail sender for SMTP and Gmail notifiers
pull/144/head
Clément Michaud 2017-10-10 23:08:55 +02:00 committed by GitHub
commit 3bea8a290a
25 changed files with 661 additions and 91 deletions

View File

@ -1,5 +1,6 @@
module.exports = function (grunt) { module.exports = function (grunt) {
const buildDir = "dist"; const buildDir = "dist";
const schemaDir = "server/src/lib/configuration/Configuration.schema.json"
grunt.initConfig({ grunt.initConfig({
env: { env: {
@ -15,6 +16,11 @@ module.exports = function (grunt) {
cmd: "./node_modules/.bin/tsc", cmd: "./node_modules/.bin/tsc",
args: ['-p', 'server/tsconfig.json'] args: ['-p', 'server/tsconfig.json']
}, },
"generate-config-schema": {
cmd: "./node_modules/.bin/typescript-json-schema",
args: ["-o", schemaDir, "--strictNullChecks",
"--required", "server/tsconfig.json", "UserConfiguration"]
},
"compile-client": { "compile-client": {
cmd: "./node_modules/.bin/tsc", cmd: "./node_modules/.bin/tsc",
args: ['-p', 'client/tsconfig.json'] args: ['-p', 'client/tsconfig.json']
@ -81,6 +87,10 @@ module.exports = function (grunt) {
src: '**', src: '**',
dest: `${buildDir}/server/src/public_html/js/` dest: `${buildDir}/server/src/public_html/js/`
}, },
schema: {
src: schemaDir,
dest: `${buildDir}/${schemaDir}`
}
}, },
browserify: { browserify: {
dist: { dist: {
@ -176,9 +186,10 @@ module.exports = function (grunt) {
grunt.registerTask('test-int', ['run:test-int']); grunt.registerTask('test-int', ['run:test-int']);
grunt.registerTask('copy-resources', ['copy:resources', 'copy:views', 'copy:images', 'copy:thirdparties', 'concat:css']); grunt.registerTask('copy-resources', ['copy:resources', 'copy:views', 'copy:images', 'copy:thirdparties', 'concat:css']);
grunt.registerTask('generate-config-schema', ['run:generate-config-schema', 'copy:schema']);
grunt.registerTask('build-client', ['compile-client', 'browserify']); grunt.registerTask('build-client', ['compile-client', 'browserify']);
grunt.registerTask('build-server', ['compile-server', 'copy-resources']); grunt.registerTask('build-server', ['compile-server', 'copy-resources', 'generate-config-schema']);
grunt.registerTask('build', ['build-client', 'build-server']); grunt.registerTask('build', ['build-client', 'build-server']);
grunt.registerTask('build-dist', ['build', 'run:minify', 'cssmin', 'run:include-minified-script']); grunt.registerTask('build-dist', ['build', 'run:minify', 'cssmin', 'run:include-minified-script']);

View File

@ -204,6 +204,7 @@ notifier:
# gmail: # gmail:
# username: user@example.com # username: user@example.com
# password: yourpassword # password: yourpassword
# sender: admin@example.com
# Use a SMTP server for sending notifications # Use a SMTP server for sending notifications
smtp: smtp:
@ -212,3 +213,4 @@ notifier:
secure: false secure: false
host: 'smtp' host: 'smtp'
port: 1025 port: 1025
sender: admin@example.com

View File

@ -166,6 +166,7 @@ notifier:
# gmail: # gmail:
# username: user@example.com # username: user@example.com
# password: yourpassword # password: yourpassword
# sender: admin@example.com
# Use a SMTP server for sending notifications # Use a SMTP server for sending notifications
smtp: smtp:
@ -174,4 +175,4 @@ notifier:
secure: false secure: false
host: 'smtp' host: 'smtp'
port: 1025 port: 1025
sender: admin@example.com

View File

@ -23,6 +23,7 @@
"title": "Authelia API documentation" "title": "Authelia API documentation"
}, },
"dependencies": { "dependencies": {
"ajv": "^5.2.3",
"bluebird": "^3.4.7", "bluebird": "^3.4.7",
"body-parser": "^1.15.2", "body-parser": "^1.15.2",
"connect-redis": "^3.3.0", "connect-redis": "^3.3.0",
@ -106,6 +107,7 @@
"ts-node": "^3.3.0", "ts-node": "^3.3.0",
"tslint": "^5.2.0", "tslint": "^5.2.0",
"typescript": "^2.3.2", "typescript": "^2.3.2",
"typescript-json-schema": "^0.17.0",
"u2f-api": "0.0.9", "u2f-api": "0.0.9",
"uglify-es": "^3.0.15" "uglify-es": "^3.0.15"
}, },

View File

@ -121,7 +121,7 @@ export function get_start_validation(handler: IdentityValidable, postValidationE
const host = req.get("Host"); const host = req.get("Host");
const link_url = util.format("https://%s%s?identity_token=%s", host, postValidationEndpoint, token); const link_url = util.format("https://%s%s?identity_token=%s", host, postValidationEndpoint, token);
logger.info(req, "Notification sent to user \"%s\"", identity.userid); logger.info(req, "Notification sent to user \"%s\"", identity.userid);
return notifier.notify(identity, handler.mailSubject(), link_url); return notifier.notify(identity.email, handler.mailSubject(), link_url);
}) })
.then(function () { .then(function () {
handler.preValidationResponse(req, res); handler.preValidationResponse(req, res);

View File

@ -6,7 +6,7 @@ import { AppConfiguration, UserConfiguration } from "./configuration/Configurati
import { GlobalDependencies } from "../../types/Dependencies"; import { GlobalDependencies } from "../../types/Dependencies";
import { AuthenticationRegulator } from "./AuthenticationRegulator"; import { AuthenticationRegulator } from "./AuthenticationRegulator";
import { UserDataStore } from "./storage/UserDataStore"; import { UserDataStore } from "./storage/UserDataStore";
import { ConfigurationAdapter } from "./configuration/ConfigurationAdapter"; import { ConfigurationParser } from "./configuration/ConfigurationParser";
import { TOTPValidator } from "./TOTPValidator"; import { TOTPValidator } from "./TOTPValidator";
import { TOTPGenerator } from "./TOTPGenerator"; import { TOTPGenerator } from "./TOTPGenerator";
import { RestApi } from "./RestApi"; import { RestApi } from "./RestApi";
@ -110,7 +110,7 @@ export default class Server {
const that = this; const that = this;
const app = Express(); const app = Express();
const appConfiguration = ConfigurationAdapter.adapt(userConfiguration); const appConfiguration = ConfigurationParser.parse(userConfiguration);
// by default the level of logs is info // by default the level of logs is info
deps.winston.level = userConfiguration.logs_level; deps.winston.level = userConfiguration.logs_level;

View File

@ -48,10 +48,10 @@ export type ACLGroupsRules = { [group: string]: ACLRule[]; };
export type ACLUsersRules = { [user: string]: ACLRule[]; }; export type ACLUsersRules = { [user: string]: ACLRule[]; };
export interface ACLConfiguration { export interface ACLConfiguration {
default_policy: ACLPolicy; default_policy?: ACLPolicy;
any: ACLDefaultRules; any?: ACLDefaultRules;
groups: ACLGroupsRules; groups?: ACLGroupsRules;
users: ACLUsersRules; users?: ACLUsersRules;
} }
export interface SessionRedisOptions { export interface SessionRedisOptions {
@ -69,6 +69,7 @@ interface SessionCookieConfiguration {
export interface GmailNotifierConfiguration { export interface GmailNotifierConfiguration {
username: string; username: string;
password: string; password: string;
sender: string;
} }
export interface SmtpNotifierConfiguration { export interface SmtpNotifierConfiguration {
@ -77,6 +78,7 @@ export interface SmtpNotifierConfiguration {
host: string; host: string;
port: number; port: number;
secure: boolean; secure: boolean;
sender: string;
} }
export interface FileSystemNotifierConfiguration { export interface FileSystemNotifierConfiguration {

View File

@ -0,0 +1,368 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"ACLConfiguration": {
"properties": {
"any": {
"items": {
"properties": {
"domain": {
"type": "string"
},
"policy": {
"$ref": "#/definitions/ACLPolicy"
},
"resources": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"domain",
"policy"
],
"type": "object"
},
"type": "array"
},
"default_policy": {
"$ref": "#/definitions/ACLPolicy"
},
"groups": {
"additionalProperties": {
"items": {
"properties": {
"domain": {
"type": "string"
},
"policy": {
"$ref": "#/definitions/ACLPolicy"
},
"resources": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"domain",
"policy"
],
"type": "object"
},
"type": "array"
},
"type": "object"
},
"users": {
"additionalProperties": {
"items": {
"properties": {
"domain": {
"type": "string"
},
"policy": {
"$ref": "#/definitions/ACLPolicy"
},
"resources": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"domain",
"policy"
],
"type": "object"
},
"type": "array"
},
"type": "object"
}
},
"type": "object"
},
"ACLPolicy": {
"enum": [
"allow",
"deny"
],
"type": "string"
},
"AuthenticationMethod": {
"enum": [
"basic_auth",
"two_factor"
],
"type": "string"
},
"AuthenticationMethodsConfiguration": {
"properties": {
"default_method": {
"$ref": "#/definitions/AuthenticationMethod"
},
"per_subdomain_methods": {
"additionalProperties": {
"enum": [
"basic_auth",
"two_factor"
],
"type": "string"
},
"type": "object"
}
},
"required": [
"default_method",
"per_subdomain_methods"
],
"type": "object"
},
"FileSystemNotifierConfiguration": {
"properties": {
"filename": {
"type": "string"
}
},
"required": [
"filename"
],
"type": "object"
},
"GmailNotifierConfiguration": {
"properties": {
"password": {
"type": "string"
},
"sender": {
"type": "string"
},
"username": {
"type": "string"
}
},
"required": [
"password",
"sender",
"username"
],
"type": "object"
},
"LocalStorageConfiguration": {
"properties": {
"in_memory": {
"type": "boolean"
},
"path": {
"type": "string"
}
},
"type": "object"
},
"MongoStorageConfiguration": {
"properties": {
"url": {
"type": "string"
}
},
"required": [
"url"
],
"type": "object"
},
"NotifierConfiguration": {
"properties": {
"filesystem": {
"$ref": "#/definitions/FileSystemNotifierConfiguration"
},
"gmail": {
"$ref": "#/definitions/GmailNotifierConfiguration"
},
"smtp": {
"$ref": "#/definitions/SmtpNotifierConfiguration"
}
},
"type": "object"
},
"RegulationConfiguration": {
"properties": {
"ban_time": {
"type": "number"
},
"find_time": {
"type": "number"
},
"max_retries": {
"type": "number"
}
},
"required": [
"ban_time",
"find_time",
"max_retries"
],
"type": "object"
},
"SessionCookieConfiguration": {
"properties": {
"domain": {
"type": "string"
},
"expiration": {
"type": "number"
},
"redis": {
"$ref": "#/definitions/SessionRedisOptions"
},
"secret": {
"type": "string"
}
},
"required": [
"secret"
],
"type": "object"
},
"SessionRedisOptions": {
"properties": {
"host": {
"type": "string"
},
"port": {
"type": "number"
}
},
"required": [
"host",
"port"
],
"type": "object"
},
"SmtpNotifierConfiguration": {
"properties": {
"host": {
"type": "string"
},
"password": {
"type": "string"
},
"port": {
"type": "number"
},
"secure": {
"type": "boolean"
},
"sender": {
"type": "string"
},
"username": {
"type": "string"
}
},
"required": [
"host",
"password",
"port",
"secure",
"sender",
"username"
],
"type": "object"
},
"StorageConfiguration": {
"properties": {
"local": {
"$ref": "#/definitions/LocalStorageConfiguration"
},
"mongo": {
"$ref": "#/definitions/MongoStorageConfiguration"
}
},
"type": "object"
},
"UserLdapConfiguration": {
"properties": {
"additional_groups_dn": {
"type": "string"
},
"additional_users_dn": {
"type": "string"
},
"base_dn": {
"type": "string"
},
"group_name_attribute": {
"type": "string"
},
"groups_filter": {
"type": "string"
},
"mail_attribute": {
"type": "string"
},
"password": {
"type": "string"
},
"url": {
"type": "string"
},
"user": {
"type": "string"
},
"users_filter": {
"type": "string"
}
},
"required": [
"base_dn",
"password",
"url",
"user"
],
"type": "object"
}
},
"properties": {
"access_control": {
"$ref": "#/definitions/ACLConfiguration"
},
"authentication_methods": {
"$ref": "#/definitions/AuthenticationMethodsConfiguration"
},
"ldap": {
"$ref": "#/definitions/UserLdapConfiguration"
},
"logs_level": {
"type": "string"
},
"notifier": {
"$ref": "#/definitions/NotifierConfiguration"
},
"port": {
"type": "number"
},
"regulation": {
"$ref": "#/definitions/RegulationConfiguration"
},
"session": {
"$ref": "#/definitions/SessionCookieConfiguration"
},
"storage": {
"$ref": "#/definitions/StorageConfiguration"
}
},
"required": [
"ldap",
"notifier",
"regulation",
"session",
"storage"
],
"type": "object"
}

View File

@ -9,6 +9,7 @@ import {
import Util = require("util"); import Util = require("util");
import { ACLAdapter } from "./adapters/ACLAdapter"; import { ACLAdapter } from "./adapters/ACLAdapter";
import { AuthenticationMethodsAdapter } from "./adapters/AuthenticationMethodsAdapter"; import { AuthenticationMethodsAdapter } from "./adapters/AuthenticationMethodsAdapter";
import { Validator } from "./Validator";
const LDAP_URL_ENV_VARIABLE = "LDAP_URL"; const LDAP_URL_ENV_VARIABLE = "LDAP_URL";
@ -58,9 +59,8 @@ function adaptLdapConfiguration(userConfig: UserLdapConfiguration): LdapConfigur
function adaptFromUserConfiguration(userConfiguration: UserConfiguration) function adaptFromUserConfiguration(userConfiguration: UserConfiguration)
: AppConfiguration { : AppConfiguration {
ensure_key_existence(userConfiguration, "ldap"); if (!Validator.isValid(userConfiguration))
ensure_key_existence(userConfiguration, "session.secret"); throw new Error("Configuration is malformed. Please double check your configuration file.");
ensure_key_existence(userConfiguration, "regulation");
const port = userConfiguration.port || 8080; const port = userConfiguration.port || 8080;
const ldapConfiguration = adaptLdapConfiguration(userConfiguration.ldap); const ldapConfiguration = adaptLdapConfiguration(userConfiguration.ldap);
@ -88,8 +88,13 @@ function adaptFromUserConfiguration(userConfiguration: UserConfiguration)
}; };
} }
export class ConfigurationAdapter { export class ConfigurationParser {
static adapt(userConfiguration: UserConfiguration): AppConfiguration { static parse(userConfiguration: UserConfiguration): AppConfiguration {
const errors = Validator.isValid(userConfiguration);
if (errors.length > 0) {
errors.forEach((e: string) => { console.log(e); });
throw new Error("Malformed configuration. Please double-check your configuration file.");
}
const appConfiguration = adaptFromUserConfiguration(userConfiguration); const appConfiguration = adaptFromUserConfiguration(userConfiguration);
const ldapUrl = process.env[LDAP_URL_ENV_VARIABLE]; const ldapUrl = process.env[LDAP_URL_ENV_VARIABLE];

View File

@ -0,0 +1,85 @@
import Ajv = require("ajv");
import Path = require("path");
import Util = require("util");
import {
UserConfiguration, StorageConfiguration,
NotifierConfiguration
} from "./Configuration";
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-04.json"));
const valid = ajv.validate(schema, configuration);
if (!valid)
return ajv.errors.map(
(e: Ajv.ErrorObject) => { return ajv.errorsText([e]); });
return [];
}
function validateUnknownKeys(path: string, obj: any, knownKeys: string[]) {
const keysSet = new Set(Object.keys(obj));
const knownKeysSet = new Set(knownKeys);
const unknownKeysSet = new Set(
[...keysSet].filter(x => !knownKeysSet.has(x)));
if (unknownKeysSet.size > 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) {
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) {
const ERROR = "Notifier must be either 'filesystem', 'gmail' or 'smtp'";
if (!notifier)
return [];
const errors = validateUnknownKeys("notifier", notifier, ["filesystem", "gmail", "smtp"]);
if (errors.length > 0)
return errors;
if (notifier && notifier.filesystem && notifier.gmail && notifier.smtp)
return [ERROR];
if (notifier && !notifier.filesystem && !notifier.gmail && !notifier.smtp)
return [ERROR];
return [];
}
export class Validator {
static isValid(configuration: any): string[] {
const schemaErrors = validateSchema(configuration);
const storageErrors = validateStorage(configuration.storage);
const notifierErrors = validateNotifier(configuration.notifier);
return schemaErrors
.concat(storageErrors)
.concat(notifierErrors);
}
}

View File

@ -10,15 +10,14 @@ import BluebirdPromise = require("bluebird");
const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8"); const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8");
export abstract class AbstractEmailNotifier implements INotifier { export abstract class AbstractEmailNotifier implements INotifier {
notify(to: string, subject: string, link: string): BluebirdPromise<void> {
notify(identity: Identity, subject: string, link: string): BluebirdPromise<void> {
const d = { const d = {
url: link, url: link,
button_title: "Continue", button_title: "Continue",
title: subject title: subject
}; };
return this.sendEmail(identity.email, subject, Ejs.render(email_template, d)); return this.sendEmail(to, subject, Ejs.render(email_template, d));
} }
abstract sendEmail(email: string, subject: string, content: string): BluebirdPromise<void>; abstract sendEmail(to: string, subject: string, content: string): BluebirdPromise<void>;
} }

View File

@ -13,9 +13,9 @@ export class FileSystemNotifier implements INotifier {
this.filename = options.filename; this.filename = options.filename;
} }
notify(identity: Identity, subject: string, link: string): BluebirdPromise<void> { notify(to: string, subject: string, link: string): BluebirdPromise<void> {
const content = util.format("Date: %s\nUser: %s\nSubject: %s\nLink: %s", new Date().toString(), identity.userid, const content = util.format("Date: %s\nEmail: %s\nSubject: %s\nLink: %s",
subject, link); new Date().toString(), to, subject, link);
const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile); const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile);
return writeFilePromised(this.filename, content); return writeFilePromised(this.filename, content);
} }

View File

@ -7,16 +7,18 @@ import { IMailSender } from "./IMailSender";
export class GMailNotifier extends AbstractEmailNotifier { export class GMailNotifier extends AbstractEmailNotifier {
private mailSender: IMailSender; private mailSender: IMailSender;
private sender: string;
constructor(options: GmailNotifierConfiguration, mailSender: IMailSender) { constructor(options: GmailNotifierConfiguration, mailSender: IMailSender) {
super(); super();
this.mailSender = mailSender; this.mailSender = mailSender;
this.sender = options.sender;
} }
sendEmail(email: string, subject: string, content: string) { sendEmail(to: string, subject: string, content: string) {
const mailOptions = { const mailOptions = {
from: "authelia@authelia.com", from: this.sender,
to: email, to: to,
subject: subject, subject: subject,
html: content html: content
}; };

View File

@ -1,7 +0,0 @@
import * as BluebirdPromise from "bluebird";
import { Identity } from "../../../types/Identity";
export interface INotifier {
notify(identity: Identity, subject: string, link: string): BluebirdPromise<void>;
}

View File

@ -0,0 +1,5 @@
import * as BluebirdPromise from "bluebird";
export interface INotifier {
notify(to: string, subject: string, link: string): BluebirdPromise<void>;
}

View File

@ -8,17 +8,19 @@ import { SmtpNotifierConfiguration } from "../configuration/Configuration";
export class SmtpNotifier extends AbstractEmailNotifier { export class SmtpNotifier extends AbstractEmailNotifier {
private mailSender: IMailSender; private mailSender: IMailSender;
private sender: string;
constructor(options: SmtpNotifierConfiguration, constructor(options: SmtpNotifierConfiguration,
mailSender: IMailSender) { mailSender: IMailSender) {
super(); super();
this.mailSender = mailSender; this.mailSender = mailSender;
this.sender = options.sender;
} }
sendEmail(email: string, subject: string, content: string) { sendEmail(to: string, subject: string, content: string) {
const mailOptions = { const mailOptions = {
from: "authelia@authelia.com", from: this.sender,
to: email, to: to,
subject: subject, subject: subject,
html: content html: content
}; };

View File

@ -55,7 +55,8 @@ describe("test server configuration", function () {
notifier: { notifier: {
gmail: { gmail: {
username: "user@example.com", username: "user@example.com",
password: "password" password: "password",
sender: "test@authelia.com"
} }
}, },
regulation: { regulation: {

View File

@ -3,10 +3,10 @@ import {
UserConfiguration, UserConfiguration,
LdapConfiguration, ACLConfiguration LdapConfiguration, ACLConfiguration
} from "../../src/lib/configuration/Configuration"; } from "../../src/lib/configuration/Configuration";
import { ConfigurationAdapter } from "../../src/lib/configuration/ConfigurationAdapter"; import { ConfigurationParser } from "../../src/lib/configuration/ConfigurationParser";
describe("test config adapter", function () { describe("test config parser", function () {
function build_yaml_config(): UserConfiguration { function buildYamlConfig(): UserConfiguration {
const yaml_config: UserConfiguration = { const yaml_config: UserConfiguration = {
port: 8080, port: 8080,
ldap: { ldap: {
@ -36,7 +36,8 @@ describe("test config adapter", function () {
notifier: { notifier: {
gmail: { gmail: {
username: "user", username: "user",
password: "password" password: "password",
sender: "admin@example.com"
} }
} }
}; };
@ -45,61 +46,63 @@ describe("test config adapter", function () {
describe("port", function () { describe("port", function () {
it("should read the port from the yaml file", function () { it("should read the port from the yaml file", function () {
const yaml_config = build_yaml_config(); const yaml_config = buildYamlConfig();
yaml_config.port = 7070; yaml_config.port = 7070;
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(yaml_config);
Assert.equal(config.port, 7070); Assert.equal(config.port, 7070);
}); });
it("should default the port to 8080 if not provided", function () { it("should default the port to 8080 if not provided", function () {
const yaml_config = build_yaml_config(); const yaml_config = buildYamlConfig();
delete yaml_config.port; delete yaml_config.port;
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(yaml_config);
Assert.equal(config.port, 8080); Assert.equal(config.port, 8080);
}); });
}); });
it("should get the session attributes", function () { it("should get the session attributes", function () {
const yaml_config = build_yaml_config(); const yaml_config = buildYamlConfig();
yaml_config.session = { yaml_config.session = {
domain: "example.com", domain: "example.com",
secret: "secret", secret: "secret",
expiration: 3600 expiration: 3600
}; };
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(yaml_config);
Assert.equal(config.session.domain, "example.com"); Assert.equal(config.session.domain, "example.com");
Assert.equal(config.session.secret, "secret"); Assert.equal(config.session.secret, "secret");
Assert.equal(config.session.expiration, 3600); Assert.equal(config.session.expiration, 3600);
}); });
it("should get the log level", function () { it("should get the log level", function () {
const yaml_config = build_yaml_config(); const yaml_config = buildYamlConfig();
yaml_config.logs_level = "debug"; yaml_config.logs_level = "debug";
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(yaml_config);
Assert.equal(config.logs_level, "debug"); Assert.equal(config.logs_level, "debug");
}); });
it("should get the notifier config", function () { it("should get the notifier config", function () {
const yaml_config = build_yaml_config(); const userConfig = buildYamlConfig();
yaml_config.notifier = { userConfig.notifier = {
gmail: { gmail: {
username: "user", username: "user",
password: "pass" password: "pass",
sender: "admin@example.com"
} }
}; };
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(userConfig);
Assert.deepEqual(config.notifier, { Assert.deepEqual(config.notifier, {
gmail: { gmail: {
username: "user", username: "user",
password: "pass" password: "pass",
sender: "admin@example.com"
} }
}); });
}); });
describe("access_control", function() { describe("access_control", function() {
it("should adapt access_control when it is already ok", function () { it("should adapt access_control when it is already ok", function () {
const yaml_config = build_yaml_config(); const userConfig = buildYamlConfig();
yaml_config.access_control = { userConfig.access_control = {
default_policy: "deny", default_policy: "deny",
any: [{ any: [{
domain: "public.example.com", domain: "public.example.com",
@ -113,7 +116,7 @@ describe("test config adapter", function () {
}, },
groups: {} groups: {}
}; };
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(userConfig);
Assert.deepEqual(config.access_control, { Assert.deepEqual(config.access_control, {
default_policy: "deny", default_policy: "deny",
any: [{ any: [{
@ -132,9 +135,9 @@ describe("test config adapter", function () {
it("should adapt access_control when it is empty", function () { it("should adapt access_control when it is empty", function () {
const yaml_config = build_yaml_config(); const userConfig = buildYamlConfig();
yaml_config.access_control = {} as any; userConfig.access_control = {} as any;
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(userConfig);
Assert.deepEqual(config.access_control, { Assert.deepEqual(config.access_control, {
default_policy: "deny", default_policy: "deny",
any: [], any: [],

View File

@ -1,6 +1,6 @@
import * as Assert from "assert"; import * as Assert from "assert";
import { UserConfiguration, LdapConfiguration } from "../../src/lib/configuration/Configuration"; import { UserConfiguration, LdapConfiguration } from "../../src/lib/configuration/Configuration";
import { ConfigurationAdapter } from "../../src/lib/configuration/ConfigurationAdapter"; import { ConfigurationParser } from "../../src/lib/configuration/ConfigurationParser";
describe("test ldap configuration adaptation", function () { describe("test ldap configuration adaptation", function () {
function build_yaml_config(): UserConfiguration { function build_yaml_config(): UserConfiguration {
@ -33,7 +33,8 @@ describe("test ldap configuration adaptation", function () {
notifier: { notifier: {
gmail: { gmail: {
username: "user", username: "user",
password: "password" password: "password",
sender: "admin@example.com"
} }
} }
}; };
@ -41,15 +42,15 @@ describe("test ldap configuration adaptation", function () {
} }
it("should adapt correctly while user only specify mandatory fields", function () { it("should adapt correctly while user only specify mandatory fields", function () {
const yaml_config = build_yaml_config(); const userConfig = build_yaml_config();
yaml_config.ldap = { userConfig.ldap = {
url: "http://ldap", url: "http://ldap",
base_dn: "dc=example,dc=com", base_dn: "dc=example,dc=com",
user: "admin", user: "admin",
password: "password" password: "password"
}; };
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(userConfig);
const expectedConfig: LdapConfiguration = { const expectedConfig: LdapConfiguration = {
url: "http://ldap", url: "http://ldap",
users_dn: "dc=example,dc=com", users_dn: "dc=example,dc=com",
@ -66,8 +67,8 @@ describe("test ldap configuration adaptation", function () {
}); });
it("should adapt correctly while user specify every fields", function () { it("should adapt correctly while user specify every fields", function () {
const yaml_config = build_yaml_config(); const userConfig = build_yaml_config();
yaml_config.ldap = { userConfig.ldap = {
url: "http://ldap-server", url: "http://ldap-server",
base_dn: "dc=example,dc=com", base_dn: "dc=example,dc=com",
additional_users_dn: "ou=users", additional_users_dn: "ou=users",
@ -80,7 +81,7 @@ describe("test ldap configuration adaptation", function () {
password: "password2" password: "password2"
}; };
const config = ConfigurationAdapter.adapt(yaml_config); const config = ConfigurationParser.parse(userConfig);
const expectedConfig: LdapConfiguration = { const expectedConfig: LdapConfiguration = {
url: "http://ldap-server", url: "http://ldap-server",
users_dn: "ou=users,dc=example,dc=com", users_dn: "ou=users,dc=example,dc=com",

View File

@ -0,0 +1,91 @@
import { Validator } from "../../src/lib/configuration/Validator";
import Assert = require("assert");
describe("test validator", function () {
it("should validate wrong user configurations", function () {
// Some examples
Assert.deepStrictEqual(Validator.isValid({}), [
"data should have required property 'ldap'",
"data should have required property 'notifier'",
"data should have required property 'regulation'",
"data should have required property 'session'",
"data should have required property 'storage'"
]);
Assert.deepStrictEqual(Validator.isValid({
ldap: {},
notifier: {},
regulation: {},
session: {},
storage: {}
}), [
"data.ldap should have required property 'base_dn'",
"data.ldap should have required property 'password'",
"data.ldap should have required property 'url'",
"data.ldap should have required property 'user'",
"data.regulation should have required property 'ban_time'",
"data.regulation should have required property 'find_time'",
"data.regulation should have required property 'max_retries'",
"data.session should have required property 'secret'",
"Storage must be either 'local' or 'mongo'",
"Notifier must be either 'filesystem', 'gmail' or 'smtp'"
]);
Assert.deepStrictEqual(Validator.isValid({
ldap: {
base_dn: "dc=example,dc=com",
password: "password",
url: "ldap://ldap",
user: "user"
},
notifier: {
abcd: []
},
regulation: {
ban_time: 120,
find_time: 30,
max_retries: 3
},
session: {
secret: "unsecure_secret"
},
storage: {
abc: {}
}
}), [
"data.storage has unknown key 'abc'",
"data.notifier has unknown key 'abcd'"
]);
});
it("should validate correct user configurations", function () {
Assert.deepStrictEqual(Validator.isValid({
ldap: {
base_dn: "dc=example,dc=com",
password: "password",
url: "ldap://ldap",
user: "user"
},
notifier: {
gmail: {
username: "user@gmail.com",
password: "pass",
sender: "admin@example.com"
}
},
regulation: {
ban_time: 120,
find_time: 30,
max_retries: 3
},
session: {
secret: "unsecure_secret"
},
storage: {
local: {
path: "/var/lib/authelia"
}
}
}), []);
});
});

View File

@ -1,5 +1,5 @@
import * as sinon from "sinon"; import * as sinon from "sinon";
import * as assert from "assert"; import * as Assert from "assert";
import BluebirdPromise = require("bluebird"); import BluebirdPromise = require("bluebird");
import { MailSenderStub } from "../mocks/notifiers/MailSenderStub"; import { MailSenderStub } from "../mocks/notifiers/MailSenderStub";
@ -11,24 +11,19 @@ describe("test gmail notifier", function () {
const mailSender = new MailSenderStub(); const mailSender = new MailSenderStub();
const options = { const options = {
username: "user_gmail", username: "user_gmail",
password: "pass_gmail" password: "pass_gmail",
sender: "admin@example.com"
}; };
mailSender.sendStub.returns(BluebirdPromise.resolve()); mailSender.sendStub.returns(BluebirdPromise.resolve());
const sender = new GMailNotifier.GMailNotifier(options, mailSender); const sender = new GMailNotifier.GMailNotifier(options, mailSender);
const subject = "subject"; const subject = "subject";
const identity = {
userid: "user",
email: "user@example.com"
};
const url = "http://test.com"; const url = "http://test.com";
return sender.notify(identity, subject, url) return sender.notify("user@example.com", subject, url)
.then(function () { .then(function () {
assert.equal(mailSender.sendStub.getCall(0).args[0].to, "user@example.com"); Assert.equal(mailSender.sendStub.getCall(0).args[0].to, "user@example.com");
assert.equal(mailSender.sendStub.getCall(0).args[0].subject, "subject"); Assert.equal(mailSender.sendStub.getCall(0).args[0].subject, "subject");
return BluebirdPromise.resolve(); return BluebirdPromise.resolve();
}); });
}); });
@ -37,24 +32,20 @@ describe("test gmail notifier", function () {
const mailSender = new MailSenderStub(); const mailSender = new MailSenderStub();
const options = { const options = {
username: "user_gmail", username: "user_gmail",
password: "pass_gmail" password: "pass_gmail",
sender: "admin@example.com"
}; };
mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail"))); mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail")));
const sender = new GMailNotifier.GMailNotifier(options, mailSender); const sender = new GMailNotifier.GMailNotifier(options, mailSender);
const subject = "subject"; const subject = "subject";
const identity = {
userid: "user",
email: "user@example.com"
};
const url = "http://test.com"; const url = "http://test.com";
return sender.notify(identity, subject, url) return sender.notify("user@example.com", subject, url)
.then(function () { .then(function () {
return BluebirdPromise.reject(new Error()); return BluebirdPromise.reject(new Error());
}, function() { }, function() {
Assert.equal(mailSender.sendStub.getCall(0).args[0].from, "admin@example.com");
return BluebirdPromise.resolve(); return BluebirdPromise.resolve();
}); });
}); });

View File

@ -18,7 +18,8 @@ describe("test MailSenderBuilder", function() {
const mailSenderBuilder = new MailSenderBuilder(Nodemailer); const mailSenderBuilder = new MailSenderBuilder(Nodemailer);
mailSenderBuilder.buildGmail({ mailSenderBuilder.buildGmail({
username: "user_gmail", username: "user_gmail",
password: "pass_gmail" password: "pass_gmail",
sender: "admin@example.com"
}); });
Assert.equal(createTransportStub.getCall(0).args[0].auth.user, "user_gmail"); Assert.equal(createTransportStub.getCall(0).args[0].auth.user, "user_gmail");
Assert.equal(createTransportStub.getCall(0).args[0].auth.pass, "pass_gmail"); Assert.equal(createTransportStub.getCall(0).args[0].auth.pass, "pass_gmail");
@ -31,7 +32,8 @@ describe("test MailSenderBuilder", function() {
password: "password", password: "password",
port: 25, port: 25,
secure: true, secure: true,
username: "user" username: "user",
sender: "admin@example.com"
}); });
Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], {
host: "mail.example.com", host: "mail.example.com",

View File

@ -15,7 +15,8 @@ describe("test notifier factory", function () {
const options = { const options = {
gmail: { gmail: {
username: "abc", username: "abc",
password: "password" password: "password",
sender: "admin@example.com"
} }
}; };
mailSenderBuilderStub = new MailSenderBuilderStub(); mailSenderBuilderStub = new MailSenderBuilderStub();
@ -29,7 +30,8 @@ describe("test notifier factory", function () {
password: "pass", password: "pass",
secure: true, secure: true,
host: "localhost", host: "localhost",
port: 25 port: 25,
sender: "admin@example.com"
} }
}; };

View File

@ -54,7 +54,8 @@ describe("Private pages of the server must not be accessible without session", f
notifier: { notifier: {
gmail: { gmail: {
username: "user@example.com", username: "user@example.com",
password: "password" password: "password",
sender: "admin@example.com"
} }
} }
}; };

View File

@ -54,7 +54,8 @@ describe("Public pages of the server must be accessible without session", functi
notifier: { notifier: {
gmail: { gmail: {
username: "user@example.com", username: "user@example.com",
password: "password" password: "password",
sender: "admin@example.com"
} }
} }
}; };