diff --git a/server/src/lib/configuration/ConfigurationAdapter.ts b/server/src/lib/configuration/ConfigurationAdapter.ts index 8931401ee..56afdd8a9 100644 --- a/server/src/lib/configuration/ConfigurationAdapter.ts +++ b/server/src/lib/configuration/ConfigurationAdapter.ts @@ -7,10 +7,10 @@ import { UserLdapConfiguration } from "./Configuration"; import Util = require("util"); +import { ACLAdapter } from "./adapters/ACLAdapter"; const LDAP_URL_ENV_VARIABLE = "LDAP_URL"; - function get_optional(config: object, path: string, default_value: T): T { let entry = default_value; if (ObjectPath.has(config, path)) { @@ -80,7 +80,7 @@ function adaptFromUserConfiguration(userConfiguration: UserConfiguration): AppCo }, logs_level: get_optional(userConfiguration, "logs_level", "info"), notifier: ObjectPath.get(userConfiguration, "notifier"), - access_control: ObjectPath.get(userConfiguration, "access_control"), + access_control: ACLAdapter.adapt(userConfiguration.access_control), regulation: userConfiguration.regulation }; } diff --git a/server/src/lib/configuration/adapters/ACLAdapter.ts b/server/src/lib/configuration/adapters/ACLAdapter.ts new file mode 100644 index 000000000..53e0801cc --- /dev/null +++ b/server/src/lib/configuration/adapters/ACLAdapter.ts @@ -0,0 +1,42 @@ +import { ACLConfiguration } from "../Configuration"; + +function clone(obj: any): any { + return JSON.parse(JSON.stringify(obj)); +} + +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 = clone(configuration); + adaptDefaultPolicy(newConfiguration); + adaptAny(newConfiguration); + adaptGroups(newConfiguration); + adaptUsers(newConfiguration); + return newConfiguration; + } +} \ No newline at end of file diff --git a/server/test/access_control/AccessController.test.ts b/server/test/access_control/AccessController.test.ts index aca3ee3ee..d0df7a901 100644 --- a/server/test/access_control/AccessController.test.ts +++ b/server/test/access_control/AccessController.test.ts @@ -173,8 +173,8 @@ describe("test access control manager", function () { }); }); - describe("check all rules", function () { - it("should control access when all rules are defined", function () { + describe("check any rules", function () { + it("should control access when any rules are defined", function () { configuration.any = [{ domain: "home.example.com", policy: "allow", @@ -307,7 +307,7 @@ describe("test access control manager", function () { Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"])); }); - it("should control access when allowed at all level and denied at user level", function () { + it("should control access when allowed at 'any' level and denied at user level", function () { configuration.any = [{ domain: "home.example.com", policy: "allow", @@ -323,7 +323,7 @@ describe("test access control manager", function () { Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"])); }); - it("should control access when allowed at all level and denied at group level", function () { + it("should control access when allowed at 'any' level and denied at group level", function () { configuration.any = [{ domain: "home.example.com", policy: "allow", diff --git a/server/test/configuration/ConfigurationAdapter.test.ts b/server/test/configuration/ConfigurationAdapter.test.ts index 100a5e182..62f6f6e39 100644 --- a/server/test/configuration/ConfigurationAdapter.test.ts +++ b/server/test/configuration/ConfigurationAdapter.test.ts @@ -43,18 +43,20 @@ describe("test config adapter", function () { return yaml_config; } - it("should read the port from the yaml file", function () { - const yaml_config = build_yaml_config(); - yaml_config.port = 7070; - const config = ConfigurationAdapter.adapt(yaml_config); - Assert.equal(config.port, 7070); - }); + describe("port", function () { + it("should read the port from the yaml file", function () { + const yaml_config = build_yaml_config(); + yaml_config.port = 7070; + const config = ConfigurationAdapter.adapt(yaml_config); + Assert.equal(config.port, 7070); + }); - it("should default the port to 8080 if not provided", function () { - const yaml_config = build_yaml_config(); - delete yaml_config.port; - const config = ConfigurationAdapter.adapt(yaml_config); - Assert.equal(config.port, 8080); + it("should default the port to 8080 if not provided", function () { + const yaml_config = build_yaml_config(); + delete yaml_config.port; + const config = ConfigurationAdapter.adapt(yaml_config); + Assert.equal(config.port, 8080); + }); }); it("should get the session attributes", function () { @@ -94,20 +96,51 @@ describe("test config adapter", function () { }); }); - it("should get the access_control config", function () { - const yaml_config = build_yaml_config(); - yaml_config.access_control = { - default_policy: "deny", - any: [], - users: {}, - groups: {} - }; - const config = ConfigurationAdapter.adapt(yaml_config); - Assert.deepEqual(config.access_control, { - default_policy: "deny", - any: [], - users: {}, - groups: {} - } as ACLConfiguration); + 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 = { + default_policy: "deny", + any: [{ + domain: "public.example.com", + policy: "allow" + }], + users: { + "user": [{ + domain: "www.example.com", + policy: "allow" + }] + }, + groups: {} + }; + const config = ConfigurationAdapter.adapt(yaml_config); + Assert.deepEqual(config.access_control, { + default_policy: "deny", + any: [{ + domain: "public.example.com", + policy: "allow" + }], + users: { + "user": [{ + domain: "www.example.com", + policy: "allow" + }] + }, + groups: {} + } as ACLConfiguration); + }); + + + 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); + Assert.deepEqual(config.access_control, { + default_policy: "deny", + any: [], + users: {}, + groups: {} + } as ACLConfiguration); + }); }); }); diff --git a/server/test/configuration/adapters/ACLAdapter.test.ts b/server/test/configuration/adapters/ACLAdapter.test.ts new file mode 100644 index 000000000..01bb32585 --- /dev/null +++ b/server/test/configuration/adapters/ACLAdapter.test.ts @@ -0,0 +1,162 @@ +import { ACLAdapter } from "../../../src/lib/configuration/adapters/ACLAdapter"; +import Assert = require("assert"); + +describe("test ACL configuration adapter", function () { + + describe("bad default_policy", function () { + it("should adapt a configuration missing default_policy", function () { + const userConfiguration: any = { + any: [], + groups: {}, + users: {} + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + + it("should adapt a configuration with bad default_policy value", function () { + const userConfiguration: any = { + default_policy: "anything", // it should be 'allow' or 'deny' + any: [], + groups: {}, + users: {} + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + + it("should adapt a configuration with bad default_policy type", function () { + const userConfiguration: any = { + default_policy: {}, // it should be 'allow' or 'deny' + any: [], + groups: {}, + users: {} + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + }); + + describe("bad any", function () { + it("should adapt a configuration missing any key", function () { + const userConfiguration: any = { + default_policy: "deny", + groups: {}, + users: {} + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + + it("should adapt a configuration with any not being an array", function () { + const userConfiguration: any = { + default_policy: "deny", + any: "abc", + groups: {}, + users: {} + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + }); + + describe("bad groups", function () { + it("should adapt a configuration missing groups key", function () { + const userConfiguration: any = { + default_policy: "deny", + any: [], + users: {} + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + + it("should adapt configuration with groups being of wrong type", function () { + const userConfiguration: any = { + default_policy: "deny", + any: [], + groups: [], + users: {} + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + }); + + describe("bad users", function () { + it("should adapt a configuration missing users key", function () { + const userConfiguration: any = { + default_policy: "deny", + any: [], + groups: {} + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + + it("should adapt a configuration with users being of wrong type", function () { + const userConfiguration: any = { + default_policy: "deny", + any: [], + groups: {}, + users: [] + }; + + const appConfiguration = ACLAdapter.adapt(userConfiguration); + Assert.deepStrictEqual(appConfiguration, { + default_policy: "deny", + any: [], + groups: {}, + users: {} + }); + }); + }); +}); \ No newline at end of file diff --git a/test/features/redirection.feature b/test/features/redirection.feature index 2b5a66026..12fba93b2 100644 --- a/test/features/redirection.feature +++ b/test/features/redirection.feature @@ -8,6 +8,7 @@ Feature: User is correctly redirected Scenario: User is redirected to home page after several authentication tries When I visit "https://public.test.local:8080/secret.html" And I login with user "john" and password "badpassword" + And I wait for notification to disappear And I clear field "username" And I login with user "john" and password "password" And I use "REGISTERED" as TOTP token handle diff --git a/test/features/regulation.feature b/test/features/regulation.feature index 54e40b79a..f361c2dd5 100644 --- a/test/features/regulation.feature +++ b/test/features/regulation.feature @@ -37,6 +37,4 @@ Feature: Authelia regulates authentication to avoid brute force And I click on "Sign in" And I use "REGISTERED" as TOTP token handle And I click on "TOTP" - Then I have access to: - | url | - | https://public.test.local:8080/secret.html | \ No newline at end of file + Then I'm redirected to "https://public.test.local:8080/secret.html" \ No newline at end of file diff --git a/test/features/step_definitions/authentication.ts b/test/features/step_definitions/authentication.ts index 140434e8e..6899d5967 100644 --- a/test/features/step_definitions/authentication.ts +++ b/test/features/step_definitions/authentication.ts @@ -11,6 +11,15 @@ Cucumber.defineSupportCode(function ({ Given, When, Then }) { return this.visit(link); }); + When("I wait for notification to disappear", function() { + const that = this; + const notificationEl = this.driver.findElement(seleniumWebdriver.By.className("notification")); + return this.driver.wait(seleniumWebdriver.until.elementIsVisible(notificationEl), 15000) + .then(function() { + return that.driver.wait(seleniumWebdriver.until.elementIsNotVisible(notificationEl), 15000); + }) + }) + When("I set field {stringInDoubleQuotes} to {stringInDoubleQuotes}", function (fieldName: string, content: string) { return this.setFieldTo(fieldName, content); }); diff --git a/test/features/step_definitions/notifications.ts b/test/features/step_definitions/notifications.ts index 3a068129b..8916ba7e9 100644 --- a/test/features/step_definitions/notifications.ts +++ b/test/features/step_definitions/notifications.ts @@ -10,7 +10,7 @@ Cucumber.defineSupportCode(function ({ Given, When, Then }) { function (notificationType: string, notificationMessage: string) { const that = this; const notificationEl = this.driver.findElement(seleniumWebdriver.By.className("notification")); - return this.driver.wait(seleniumWebdriver.until.elementIsVisible(notificationEl), 2000) + return this.driver.wait(seleniumWebdriver.until.elementIsVisible(notificationEl), 5000) .then(function () { return notificationEl.getText(); })