Add a schema validator to check user configuration

pull/137/head
Clement Michaud 2017-10-10 01:14:36 +02:00
parent 1ab09b71d4
commit 2a3fde5ee7
6 changed files with 415 additions and 4 deletions

View File

@ -15,6 +15,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", `${buildDir}/server/src/lib/configuration/Configuration.schema.json`, "--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']
@ -178,7 +183,7 @@ module.exports = function (grunt) {
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('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', 'run: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

@ -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

@ -0,0 +1,374 @@
{
"$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"
}
},
"required": [
"any",
"default_policy",
"groups",
"users"
],
"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);

View File

@ -0,0 +1,20 @@
import Ajv = require("ajv");
import Path = require("path");
export class Validator {
static isValid(configuration: any) {
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) {
for (const i in ajv.errors) {
console.log(ajv.errorsText([ajv.errors[i]]));
}
}
return valid;
}
}

View File

@ -0,0 +1,10 @@
import { Validator } from "../../src/lib/configuration/Validator";
import Assert = require("assert");
describe.only("test validator", function() {
it("should validate a correct user configuration", function() {
Assert(Validator.validate({
ldap: {}
}));
});
});