diff --git a/Gruntfile.js b/Gruntfile.js index cf7c78a1b..92bd0d4bd 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,5 +1,6 @@ module.exports = function (grunt) { const buildDir = "dist"; + const schemaDir = "server/src/lib/configuration/Configuration.schema.json" grunt.initConfig({ env: { @@ -15,6 +16,11 @@ module.exports = function (grunt) { cmd: "./node_modules/.bin/tsc", 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": { cmd: "./node_modules/.bin/tsc", args: ['-p', 'client/tsconfig.json'] @@ -81,6 +87,10 @@ module.exports = function (grunt) { src: '**', dest: `${buildDir}/server/src/public_html/js/` }, + schema: { + src: schemaDir, + dest: `${buildDir}/${schemaDir}` + } }, browserify: { dist: { @@ -176,9 +186,10 @@ module.exports = function (grunt) { grunt.registerTask('test-int', ['run:test-int']); 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-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-dist', ['build', 'run:minify', 'cssmin', 'run:include-minified-script']); diff --git a/config.template.yml b/config.template.yml index 810f0133a..8bef04b69 100644 --- a/config.template.yml +++ b/config.template.yml @@ -204,6 +204,7 @@ notifier: # gmail: # username: user@example.com # password: yourpassword + # sender: admin@example.com # Use a SMTP server for sending notifications smtp: @@ -212,3 +213,4 @@ notifier: secure: false host: 'smtp' port: 1025 + sender: admin@example.com \ No newline at end of file diff --git a/config.test.yml b/config.test.yml index 7b9d1e1ff..034d12ad0 100644 --- a/config.test.yml +++ b/config.test.yml @@ -166,6 +166,7 @@ notifier: # gmail: # username: user@example.com # password: yourpassword + # sender: admin@example.com # Use a SMTP server for sending notifications smtp: @@ -174,4 +175,4 @@ notifier: secure: false host: 'smtp' port: 1025 - + sender: admin@example.com diff --git a/package.json b/package.json index 5a3e0a067..c30331f86 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "title": "Authelia API documentation" }, "dependencies": { + "ajv": "^5.2.3", "bluebird": "^3.4.7", "body-parser": "^1.15.2", "connect-redis": "^3.3.0", @@ -106,6 +107,7 @@ "ts-node": "^3.3.0", "tslint": "^5.2.0", "typescript": "^2.3.2", + "typescript-json-schema": "^0.17.0", "u2f-api": "0.0.9", "uglify-es": "^3.0.15" }, diff --git a/server/src/lib/IdentityCheckMiddleware.ts b/server/src/lib/IdentityCheckMiddleware.ts index ffe9292f5..0625c13df 100644 --- a/server/src/lib/IdentityCheckMiddleware.ts +++ b/server/src/lib/IdentityCheckMiddleware.ts @@ -121,7 +121,7 @@ export function get_start_validation(handler: IdentityValidable, postValidationE const host = req.get("Host"); const link_url = util.format("https://%s%s?identity_token=%s", host, postValidationEndpoint, token); 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 () { handler.preValidationResponse(req, res); diff --git a/server/src/lib/Server.ts b/server/src/lib/Server.ts index 8f9f092af..436571fc8 100644 --- a/server/src/lib/Server.ts +++ b/server/src/lib/Server.ts @@ -6,7 +6,7 @@ import { AppConfiguration, UserConfiguration } from "./configuration/Configurati import { GlobalDependencies } from "../../types/Dependencies"; import { AuthenticationRegulator } from "./AuthenticationRegulator"; import { UserDataStore } from "./storage/UserDataStore"; -import { ConfigurationAdapter } from "./configuration/ConfigurationAdapter"; +import { ConfigurationParser } from "./configuration/ConfigurationParser"; import { TOTPValidator } from "./TOTPValidator"; import { TOTPGenerator } from "./TOTPGenerator"; import { RestApi } from "./RestApi"; @@ -110,7 +110,7 @@ export default class Server { const that = this; const app = Express(); - const appConfiguration = ConfigurationAdapter.adapt(userConfiguration); + const appConfiguration = ConfigurationParser.parse(userConfiguration); // by default the level of logs is info deps.winston.level = userConfiguration.logs_level; diff --git a/server/src/lib/configuration/Configuration.d.ts b/server/src/lib/configuration/Configuration.d.ts index 8faa487d5..802572b2f 100644 --- a/server/src/lib/configuration/Configuration.d.ts +++ b/server/src/lib/configuration/Configuration.d.ts @@ -48,10 +48,10 @@ export type ACLGroupsRules = { [group: string]: ACLRule[]; }; export type ACLUsersRules = { [user: string]: ACLRule[]; }; export interface ACLConfiguration { - default_policy: ACLPolicy; - any: ACLDefaultRules; - groups: ACLGroupsRules; - users: ACLUsersRules; + default_policy?: ACLPolicy; + any?: ACLDefaultRules; + groups?: ACLGroupsRules; + users?: ACLUsersRules; } export interface SessionRedisOptions { @@ -69,6 +69,7 @@ interface SessionCookieConfiguration { export interface GmailNotifierConfiguration { username: string; password: string; + sender: string; } export interface SmtpNotifierConfiguration { @@ -77,6 +78,7 @@ export interface SmtpNotifierConfiguration { host: string; port: number; secure: boolean; + sender: string; } export interface FileSystemNotifierConfiguration { diff --git a/server/src/lib/configuration/Configuration.schema.json b/server/src/lib/configuration/Configuration.schema.json new file mode 100644 index 000000000..9fd4e9238 --- /dev/null +++ b/server/src/lib/configuration/Configuration.schema.json @@ -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" +} + diff --git a/server/src/lib/configuration/ConfigurationAdapter.ts b/server/src/lib/configuration/ConfigurationParser.ts similarity index 87% rename from server/src/lib/configuration/ConfigurationAdapter.ts rename to server/src/lib/configuration/ConfigurationParser.ts index da5807bdc..32f5acf53 100644 --- a/server/src/lib/configuration/ConfigurationAdapter.ts +++ b/server/src/lib/configuration/ConfigurationParser.ts @@ -9,6 +9,7 @@ import { import Util = require("util"); import { ACLAdapter } from "./adapters/ACLAdapter"; import { AuthenticationMethodsAdapter } from "./adapters/AuthenticationMethodsAdapter"; +import { Validator } from "./Validator"; const LDAP_URL_ENV_VARIABLE = "LDAP_URL"; @@ -58,9 +59,8 @@ function adaptLdapConfiguration(userConfig: UserLdapConfiguration): LdapConfigur function adaptFromUserConfiguration(userConfiguration: UserConfiguration) : AppConfiguration { - ensure_key_existence(userConfiguration, "ldap"); - ensure_key_existence(userConfiguration, "session.secret"); - ensure_key_existence(userConfiguration, "regulation"); + 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); @@ -88,8 +88,13 @@ function adaptFromUserConfiguration(userConfiguration: UserConfiguration) }; } -export class ConfigurationAdapter { - static adapt(userConfiguration: UserConfiguration): AppConfiguration { +export class ConfigurationParser { + 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 ldapUrl = process.env[LDAP_URL_ENV_VARIABLE]; diff --git a/server/src/lib/configuration/Validator.ts b/server/src/lib/configuration/Validator.ts new file mode 100644 index 000000000..5058d08d7 --- /dev/null +++ b/server/src/lib/configuration/Validator.ts @@ -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); + } +} \ No newline at end of file diff --git a/server/src/lib/notifiers/AbstractEmailNotifier.ts b/server/src/lib/notifiers/AbstractEmailNotifier.ts index 2a925fd88..198e4e5db 100644 --- a/server/src/lib/notifiers/AbstractEmailNotifier.ts +++ b/server/src/lib/notifiers/AbstractEmailNotifier.ts @@ -10,15 +10,14 @@ import BluebirdPromise = require("bluebird"); const email_template = Fs.readFileSync(Path.join(__dirname, "../../resources/email-template.ejs"), "UTF-8"); export abstract class AbstractEmailNotifier implements INotifier { - - notify(identity: Identity, subject: string, link: string): BluebirdPromise { + notify(to: string, subject: string, link: string): BluebirdPromise { const d = { url: link, button_title: "Continue", 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; + abstract sendEmail(to: string, subject: string, content: string): BluebirdPromise; } \ No newline at end of file diff --git a/server/src/lib/notifiers/FileSystemNotifier.ts b/server/src/lib/notifiers/FileSystemNotifier.ts index efa9c6229..c5a9f30cd 100644 --- a/server/src/lib/notifiers/FileSystemNotifier.ts +++ b/server/src/lib/notifiers/FileSystemNotifier.ts @@ -13,9 +13,9 @@ export class FileSystemNotifier implements INotifier { this.filename = options.filename; } - notify(identity: Identity, subject: string, link: string): BluebirdPromise { - const content = util.format("Date: %s\nUser: %s\nSubject: %s\nLink: %s", new Date().toString(), identity.userid, - subject, link); + notify(to: string, subject: string, link: string): BluebirdPromise { + const content = util.format("Date: %s\nEmail: %s\nSubject: %s\nLink: %s", + new Date().toString(), to, subject, link); const writeFilePromised: any = BluebirdPromise.promisify(Fs.writeFile); return writeFilePromised(this.filename, content); } diff --git a/server/src/lib/notifiers/GMailNotifier.ts b/server/src/lib/notifiers/GMailNotifier.ts index 16aeb4171..caa81581c 100644 --- a/server/src/lib/notifiers/GMailNotifier.ts +++ b/server/src/lib/notifiers/GMailNotifier.ts @@ -7,16 +7,18 @@ import { IMailSender } from "./IMailSender"; export class GMailNotifier extends AbstractEmailNotifier { private mailSender: IMailSender; + private sender: string; constructor(options: GmailNotifierConfiguration, mailSender: IMailSender) { super(); this.mailSender = mailSender; + this.sender = options.sender; } - sendEmail(email: string, subject: string, content: string) { + sendEmail(to: string, subject: string, content: string) { const mailOptions = { - from: "authelia@authelia.com", - to: email, + from: this.sender, + to: to, subject: subject, html: content }; diff --git a/server/src/lib/notifiers/INotifier.d.ts b/server/src/lib/notifiers/INotifier.d.ts deleted file mode 100644 index 63f26097d..000000000 --- a/server/src/lib/notifiers/INotifier.d.ts +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/server/src/lib/notifiers/INotifier.ts b/server/src/lib/notifiers/INotifier.ts new file mode 100644 index 000000000..b9a6b138c --- /dev/null +++ b/server/src/lib/notifiers/INotifier.ts @@ -0,0 +1,5 @@ +import * as BluebirdPromise from "bluebird"; + +export interface INotifier { + notify(to: string, subject: string, link: string): BluebirdPromise; +} \ No newline at end of file diff --git a/server/src/lib/notifiers/SmtpNotifier.ts b/server/src/lib/notifiers/SmtpNotifier.ts index 5ed86621d..cda174c98 100644 --- a/server/src/lib/notifiers/SmtpNotifier.ts +++ b/server/src/lib/notifiers/SmtpNotifier.ts @@ -8,17 +8,19 @@ import { SmtpNotifierConfiguration } from "../configuration/Configuration"; export class SmtpNotifier extends AbstractEmailNotifier { private mailSender: IMailSender; + private sender: string; constructor(options: SmtpNotifierConfiguration, mailSender: IMailSender) { super(); this.mailSender = mailSender; + this.sender = options.sender; } - sendEmail(email: string, subject: string, content: string) { + sendEmail(to: string, subject: string, content: string) { const mailOptions = { - from: "authelia@authelia.com", - to: email, + from: this.sender, + to: to, subject: subject, html: content }; diff --git a/server/test/ServerConfiguration.test.ts b/server/test/ServerConfiguration.test.ts index 3f3002732..e33277655 100644 --- a/server/test/ServerConfiguration.test.ts +++ b/server/test/ServerConfiguration.test.ts @@ -55,7 +55,8 @@ describe("test server configuration", function () { notifier: { gmail: { username: "user@example.com", - password: "password" + password: "password", + sender: "test@authelia.com" } }, regulation: { diff --git a/server/test/configuration/ConfigurationAdapter.test.ts b/server/test/configuration/ConfigurationParser.test.ts similarity index 70% rename from server/test/configuration/ConfigurationAdapter.test.ts rename to server/test/configuration/ConfigurationParser.test.ts index 62f6f6e39..f851dde02 100644 --- a/server/test/configuration/ConfigurationAdapter.test.ts +++ b/server/test/configuration/ConfigurationParser.test.ts @@ -3,10 +3,10 @@ import { UserConfiguration, LdapConfiguration, ACLConfiguration } from "../../src/lib/configuration/Configuration"; -import { ConfigurationAdapter } from "../../src/lib/configuration/ConfigurationAdapter"; +import { ConfigurationParser } from "../../src/lib/configuration/ConfigurationParser"; -describe("test config adapter", function () { - function build_yaml_config(): UserConfiguration { +describe("test config parser", function () { + function buildYamlConfig(): UserConfiguration { const yaml_config: UserConfiguration = { port: 8080, ldap: { @@ -36,7 +36,8 @@ describe("test config adapter", function () { notifier: { gmail: { username: "user", - password: "password" + password: "password", + sender: "admin@example.com" } } }; @@ -45,61 +46,63 @@ describe("test config adapter", function () { describe("port", 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; - const config = ConfigurationAdapter.adapt(yaml_config); + const config = ConfigurationParser.parse(yaml_config); Assert.equal(config.port, 7070); }); 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; - const config = ConfigurationAdapter.adapt(yaml_config); + const config = ConfigurationParser.parse(yaml_config); Assert.equal(config.port, 8080); }); }); it("should get the session attributes", function () { - const yaml_config = build_yaml_config(); + const yaml_config = buildYamlConfig(); yaml_config.session = { domain: "example.com", secret: "secret", 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.secret, "secret"); Assert.equal(config.session.expiration, 3600); }); it("should get the log level", function () { - const yaml_config = build_yaml_config(); + const yaml_config = buildYamlConfig(); yaml_config.logs_level = "debug"; - const config = ConfigurationAdapter.adapt(yaml_config); + const config = ConfigurationParser.parse(yaml_config); Assert.equal(config.logs_level, "debug"); }); it("should get the notifier config", function () { - const yaml_config = build_yaml_config(); - yaml_config.notifier = { + const userConfig = buildYamlConfig(); + userConfig.notifier = { gmail: { 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, { gmail: { username: "user", - password: "pass" + password: "pass", + sender: "admin@example.com" } }); }); describe("access_control", function() { it("should adapt access_control when it is already ok", function () { - const yaml_config = build_yaml_config(); - yaml_config.access_control = { + const userConfig = buildYamlConfig(); + userConfig.access_control = { default_policy: "deny", any: [{ domain: "public.example.com", @@ -113,7 +116,7 @@ describe("test config adapter", function () { }, groups: {} }; - const config = ConfigurationAdapter.adapt(yaml_config); + const config = ConfigurationParser.parse(userConfig); Assert.deepEqual(config.access_control, { default_policy: "deny", any: [{ @@ -132,9 +135,9 @@ describe("test config adapter", function () { it("should adapt access_control when it is empty", function () { - const yaml_config = build_yaml_config(); - yaml_config.access_control = {} as any; - const config = ConfigurationAdapter.adapt(yaml_config); + const userConfig = buildYamlConfig(); + userConfig.access_control = {} as any; + const config = ConfigurationParser.parse(userConfig); Assert.deepEqual(config.access_control, { default_policy: "deny", any: [], diff --git a/server/test/configuration/LdapConfigurationAdaptation.test.ts b/server/test/configuration/LdapConfigurationAdaptation.test.ts index 63c0a8bdd..59aab2537 100644 --- a/server/test/configuration/LdapConfigurationAdaptation.test.ts +++ b/server/test/configuration/LdapConfigurationAdaptation.test.ts @@ -1,6 +1,6 @@ import * as Assert from "assert"; 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 () { function build_yaml_config(): UserConfiguration { @@ -33,7 +33,8 @@ describe("test ldap configuration adaptation", function () { notifier: { gmail: { 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 () { - const yaml_config = build_yaml_config(); - yaml_config.ldap = { + const userConfig = build_yaml_config(); + userConfig.ldap = { url: "http://ldap", base_dn: "dc=example,dc=com", user: "admin", password: "password" }; - const config = ConfigurationAdapter.adapt(yaml_config); + const config = ConfigurationParser.parse(userConfig); const expectedConfig: LdapConfiguration = { url: "http://ldap", 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 () { - const yaml_config = build_yaml_config(); - yaml_config.ldap = { + const userConfig = build_yaml_config(); + userConfig.ldap = { url: "http://ldap-server", base_dn: "dc=example,dc=com", additional_users_dn: "ou=users", @@ -80,7 +81,7 @@ describe("test ldap configuration adaptation", function () { password: "password2" }; - const config = ConfigurationAdapter.adapt(yaml_config); + const config = ConfigurationParser.parse(userConfig); const expectedConfig: LdapConfiguration = { url: "http://ldap-server", users_dn: "ou=users,dc=example,dc=com", diff --git a/server/test/configuration/Validator.test.ts b/server/test/configuration/Validator.test.ts new file mode 100644 index 000000000..69d3afca5 --- /dev/null +++ b/server/test/configuration/Validator.test.ts @@ -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" + } + } + }), []); + }); +}); \ No newline at end of file diff --git a/server/test/notifiers/GMailNotifier.test.ts b/server/test/notifiers/GMailNotifier.test.ts index 7efba4446..8d598d5da 100644 --- a/server/test/notifiers/GMailNotifier.test.ts +++ b/server/test/notifiers/GMailNotifier.test.ts @@ -1,5 +1,5 @@ import * as sinon from "sinon"; -import * as assert from "assert"; +import * as Assert from "assert"; import BluebirdPromise = require("bluebird"); import { MailSenderStub } from "../mocks/notifiers/MailSenderStub"; @@ -11,24 +11,19 @@ describe("test gmail notifier", function () { const mailSender = new MailSenderStub(); const options = { username: "user_gmail", - password: "pass_gmail" + password: "pass_gmail", + sender: "admin@example.com" }; mailSender.sendStub.returns(BluebirdPromise.resolve()); const sender = new GMailNotifier.GMailNotifier(options, mailSender); const subject = "subject"; - - const identity = { - userid: "user", - email: "user@example.com" - }; - const url = "http://test.com"; - return sender.notify(identity, subject, url) + return sender.notify("user@example.com", subject, url) .then(function () { - 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].to, "user@example.com"); + Assert.equal(mailSender.sendStub.getCall(0).args[0].subject, "subject"); return BluebirdPromise.resolve(); }); }); @@ -37,24 +32,20 @@ describe("test gmail notifier", function () { const mailSender = new MailSenderStub(); const options = { username: "user_gmail", - password: "pass_gmail" + password: "pass_gmail", + sender: "admin@example.com" }; mailSender.sendStub.returns(BluebirdPromise.reject(new Error("Failed to send mail"))); const sender = new GMailNotifier.GMailNotifier(options, mailSender); const subject = "subject"; - - const identity = { - userid: "user", - email: "user@example.com" - }; - const url = "http://test.com"; - return sender.notify(identity, subject, url) + return sender.notify("user@example.com", subject, url) .then(function () { return BluebirdPromise.reject(new Error()); }, function() { + Assert.equal(mailSender.sendStub.getCall(0).args[0].from, "admin@example.com"); return BluebirdPromise.resolve(); }); }); diff --git a/server/test/notifiers/MailSenderBuilder.test.ts b/server/test/notifiers/MailSenderBuilder.test.ts index 9cb088804..f0d769116 100644 --- a/server/test/notifiers/MailSenderBuilder.test.ts +++ b/server/test/notifiers/MailSenderBuilder.test.ts @@ -18,7 +18,8 @@ describe("test MailSenderBuilder", function() { const mailSenderBuilder = new MailSenderBuilder(Nodemailer); mailSenderBuilder.buildGmail({ 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.pass, "pass_gmail"); @@ -31,7 +32,8 @@ describe("test MailSenderBuilder", function() { password: "password", port: 25, secure: true, - username: "user" + username: "user", + sender: "admin@example.com" }); Assert.deepStrictEqual(createTransportStub.getCall(0).args[0], { host: "mail.example.com", diff --git a/server/test/notifiers/NotifierFactory.test.ts b/server/test/notifiers/NotifierFactory.test.ts index 5763f7038..6dd25d4ac 100644 --- a/server/test/notifiers/NotifierFactory.test.ts +++ b/server/test/notifiers/NotifierFactory.test.ts @@ -15,7 +15,8 @@ describe("test notifier factory", function () { const options = { gmail: { username: "abc", - password: "password" + password: "password", + sender: "admin@example.com" } }; mailSenderBuilderStub = new MailSenderBuilderStub(); @@ -29,7 +30,8 @@ describe("test notifier factory", function () { password: "pass", secure: true, host: "localhost", - port: 25 + port: 25, + sender: "admin@example.com" } }; diff --git a/server/test/server/PrivatePages.ts b/server/test/server/PrivatePages.ts index 09bdb383b..dee301190 100644 --- a/server/test/server/PrivatePages.ts +++ b/server/test/server/PrivatePages.ts @@ -54,7 +54,8 @@ describe("Private pages of the server must not be accessible without session", f notifier: { gmail: { username: "user@example.com", - password: "password" + password: "password", + sender: "admin@example.com" } } }; diff --git a/server/test/server/PublicPages.ts b/server/test/server/PublicPages.ts index aef0bf6b0..21f099176 100644 --- a/server/test/server/PublicPages.ts +++ b/server/test/server/PublicPages.ts @@ -54,7 +54,8 @@ describe("Public pages of the server must be accessible without session", functi notifier: { gmail: { username: "user@example.com", - password: "password" + password: "password", + sender: "admin@example.com" } } };