From 50d4ab136883a930b34af0f40fd4a832c9410f08 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Thu, 14 Feb 2019 00:27:43 +0100 Subject: [PATCH] Finish migrating integration tests to mocha. --- test/features/auth-portal-redirection.feature | 34 ---- test/features/authentication.feature | 5 - test/features/redirection.feature | 71 ------- .../step_definitions/access-control.ts | 17 -- test/features/step_definitions/authelia.ts | 44 ----- .../step_definitions/authentication.ts | 99 ---------- .../step_definitions/forward-headers.ts | 19 -- test/features/step_definitions/hooks.ts | 173 ----------------- .../step_definitions/notifications.ts | 23 --- test/features/step_definitions/redirection.ts | 15 -- .../features/step_definitions/registration.ts | 13 -- test/features/step_definitions/regulation.ts | 9 - .../step_definitions/reset-password.ts | 16 -- test/features/step_definitions/resilience.ts | 10 - .../features/step_definitions/restrictions.ts | 63 ------ .../step_definitions/session-timeout.ts | 6 - .../step_definitions/single-factor.ts | 37 ---- test/features/support/world.ts | 183 ------------------ test/helpers/FullLogin.ts | 2 - test/helpers/LoginAndRegisterTotp.ts | 4 +- test/helpers/LoginAs.ts | 5 +- test/helpers/VisitPage.ts | 2 +- .../assertions/VerifyForwardedHeaderIs.ts | 5 +- .../VerifyIsAlreadyAuthenticatedStage.ts | 5 +- .../assertions/VerifyIsSecondFactorStage.ts | 5 +- .../assertions/VerifyNotificationDisplayed.ts | 5 +- .../behaviors/RegisterAndLoginTwoFactor.ts | 4 +- .../suites/complete/scenarii/AccessControl.ts | 6 +- .../complete/scenarii/AutheliaRestart.ts | 4 +- .../scenarii/AuthenticationRegulation.ts | 21 +- .../scenarii/CustomHeadersForwarded.ts | 2 +- .../EnforceInternalRedirectionsOnly.ts | 2 +- test/suites/minimal/index.ts | 9 +- test/suites/minimal/scenarii/Inactivity.ts | 2 +- .../LogoutRedirectToAlreadyLoggedIn.ts | 23 +++ test/suites/minimal/scenarii/RegisterTotp.ts | 2 +- .../minimal/scenarii/RequiredTwoFactor.ts | 30 +++ .../minimal/scenarii/SimpleAuthentication.ts | 38 ++++ .../suites/minimal/scenarii/TOTPValidation.ts | 4 +- 39 files changed, 131 insertions(+), 886 deletions(-) delete mode 100644 test/features/auth-portal-redirection.feature delete mode 100644 test/features/authentication.feature delete mode 100644 test/features/redirection.feature delete mode 100644 test/features/step_definitions/access-control.ts delete mode 100644 test/features/step_definitions/authelia.ts delete mode 100644 test/features/step_definitions/authentication.ts delete mode 100644 test/features/step_definitions/forward-headers.ts delete mode 100644 test/features/step_definitions/hooks.ts delete mode 100644 test/features/step_definitions/notifications.ts delete mode 100644 test/features/step_definitions/redirection.ts delete mode 100644 test/features/step_definitions/registration.ts delete mode 100644 test/features/step_definitions/regulation.ts delete mode 100644 test/features/step_definitions/reset-password.ts delete mode 100644 test/features/step_definitions/resilience.ts delete mode 100644 test/features/step_definitions/restrictions.ts delete mode 100644 test/features/step_definitions/session-timeout.ts delete mode 100644 test/features/step_definitions/single-factor.ts delete mode 100644 test/features/support/world.ts create mode 100644 test/suites/minimal/scenarii/LogoutRedirectToAlreadyLoggedIn.ts create mode 100644 test/suites/minimal/scenarii/RequiredTwoFactor.ts create mode 100644 test/suites/minimal/scenarii/SimpleAuthentication.ts diff --git a/test/features/auth-portal-redirection.feature b/test/features/auth-portal-redirection.feature deleted file mode 100644 index 1fd5bb5c..00000000 --- a/test/features/auth-portal-redirection.feature +++ /dev/null @@ -1,34 +0,0 @@ -Feature: User is redirected when factors are already validated - - @need-registered-user-john - Scenario: User has validated first factor and tries to access service protected by second factor. He is then redirect to second factor step. - When I visit "https://single_factor.example.com:8080/secret.html" - And I'm redirected to "https://login.example.com:8080/?rd=https://single_factor.example.com:8080/secret.html" - And I login with user "john" and password "password" - And I'm redirected to "https://single_factor.example.com:8080/secret.html" - And I visit "https://public.example.com:8080/secret.html" - Then I'm redirected to "https://login.example.com:8080/secondfactor?rd=https://public.example.com:8080/secret.html" - - @need-registered-user-john - Scenario: User who has validated second factor and access auth portal should be redirected to "Already logged in page" and redirected to default URL declared in configuration - When I visit "https://public.example.com:8080/secret.html" - And I'm redirected to "https://login.example.com:8080/?rd=https://public.example.com:8080/secret.html" - And I login with user "john" and password "password" - And I use "REGISTERED" as TOTP token handle - And I click on "Sign in" - And I'm redirected to "https://public.example.com:8080/secret.html" - And I visit "https://login.example.com:8080" - Then I'm redirected to "https://login.example.com:8080/loggedin" - And I sleep for 5 seconds - And I'm redirected to "https://home.example.com:8080/" - - @need-registered-user-john - Scenario: User who has validated second factor and access auth portal with rediction param should be redirected to that URL - When I visit "https://public.example.com:8080/secret.html" - And I'm redirected to "https://login.example.com:8080/?rd=https://public.example.com:8080/secret.html" - And I login with user "john" and password "password" - And I use "REGISTERED" as TOTP token handle - And I click on "Sign in" - And I'm redirected to "https://public.example.com:8080/secret.html" - And I visit "https://login.example.com:8080?rd=https://public.example.com:8080/secret.html" - Then I'm redirected to "https://public.example.com:8080/secret.html" diff --git a/test/features/authentication.feature b/test/features/authentication.feature deleted file mode 100644 index 0688916f..00000000 --- a/test/features/authentication.feature +++ /dev/null @@ -1,5 +0,0 @@ -Feature: Authentication scenarii - - Scenario: Logout redirects user to redirect URL given in parameter - When I visit "https://login.example.com:8080/logout?rd=https://home.example.com:8080/" - Then I'm redirected to "https://home.example.com:8080/" diff --git a/test/features/redirection.feature b/test/features/redirection.feature deleted file mode 100644 index 2bb39b8d..00000000 --- a/test/features/redirection.feature +++ /dev/null @@ -1,71 +0,0 @@ -Feature: User is correctly redirected - - Scenario: User is redirected to authelia when he is not authenticated - When I visit "https://public.example.com:8080" - Then I'm redirected to "https://login.example.com:8080/?rd=https://public.example.com:8080/" - - @need-registered-user-john - Scenario: User is redirected to home page after several authentication tries - When I visit "https://public.example.com: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 clear field "password" - And I login with user "john" and password "password" - And I use "REGISTERED" as TOTP token handle - And I click on "Sign in" - Then I'm redirected to "https://public.example.com:8080/secret.html" - - Scenario: User Harry does not have access to admin domain and thus he must get an error 403 - When I register TOTP and login with user "harry" and password "password" - And I visit "https://admin.example.com:8080/secret.html" - Then I get an error 403 - - Scenario: Redirection URL is propagated from restricted page to first factor - When I visit "https://public.example.com:8080/secret.html" - Then I'm redirected to "https://login.example.com:8080/?rd=https://public.example.com:8080/secret.html" - - Scenario: Redirection URL is propagated from first factor to second factor - Given I visit "https://login.example.com:8080/" - And I login with user "john" and password "password" - And I register a TOTP secret called "Sec0" - When I visit "https://public.example.com:8080/secret.html" - And I login with user "john" and password "password" - Then I'm redirected to "https://login.example.com:8080/secondfactor?rd=https://public.example.com:8080/secret.html" - - Scenario: Redirection URL is used to send user from second factor to target page - Given I visit "https://login.example.com:8080/" - And I login with user "john" and password "password" - And I register a TOTP secret called "Sec0" - When I visit "https://public.example.com:8080/secret.html" - And I login with user "john" and password "password" - And I use "Sec0" as TOTP token handle - And I click on "Sign in" - Then I'm redirected to "https://public.example.com:8080/secret.html" - - @need-registered-user-john - Scenario: User is redirected to default URL defined in configuration when authentication is successful - When I visit "https://login.example.com:8080" - And I login with user "john" and password "password" - And I use "REGISTERED" as TOTP token handle - And I click on "Sign in" - Then I'm redirected to "https://home.example.com:8080/" - - - Scenario: User is redirected when hitting an error 401 - When I visit "https://login.example.com:8080/secondfactor/u2f/identity/finish" - Then I'm redirected to "https://login.example.com:8080/error/401" - And I sleep for 5 seconds - And I'm redirected to "https://home.example.com:8080/" - - @need-registered-user-harry - Scenario: User is redirected when hitting an error 403 - When I visit "https://login.example.com:8080" - And I login with user "harry" and password "password" - And I use "REGISTERED" as TOTP token handle - And I click on "Sign in" - And I'm redirected to "https://home.example.com:8080/" - When I visit "https://admin.example.com:8080/secret.html" - Then I'm redirected to "https://login.example.com:8080/error/403" - And I sleep for 5 seconds - And I'm redirected to "https://home.example.com:8080/" diff --git a/test/features/step_definitions/access-control.ts b/test/features/step_definitions/access-control.ts deleted file mode 100644 index 674d0edf..00000000 --- a/test/features/step_definitions/access-control.ts +++ /dev/null @@ -1,17 +0,0 @@ -import {Then} from "cucumber"; - -Then("I have access to {string}", function(url: string) { - const that = this; - return this.driver.get(url) - .then(function () { - return that.waitUntilUrlContains(url); - }); -}); - -Then("I have no access to {string}", function(url: string) { - const that = this; - return this.driver.get(url) - .then(function () { - return that.getErrorPage(403); - }); -}); \ No newline at end of file diff --git a/test/features/step_definitions/authelia.ts b/test/features/step_definitions/authelia.ts deleted file mode 100644 index 7b100f0d..00000000 --- a/test/features/step_definitions/authelia.ts +++ /dev/null @@ -1,44 +0,0 @@ -import {Before, When, Then} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); -import Request = require("request-promise"); -import Bluebird = require("bluebird"); - -When("I query {string}", function (url: string) { - const that = this; - return Request(url, { followRedirect: false }) - .then(function(response) { - that.response = response; - }) - .catch(function(err: Error) { - that.error = err; - }) -}); - -Then("I get error code 401", function() { - const that = this; - return new Bluebird(function(resolve, reject) { - if(that.error && that.error.statusCode == 401) { - resolve(); - } - else { - if(that.response) - reject(new Error("No error thrown")); - else if(that.error.statusCode != 401) - reject(new Error(`Error code (${that.error.statusCode}) != 401`)); - } - }); -}); - -Then("I get redirected to {string}", function(url: string) { - const that = this; - return new Bluebird(function(resolve, reject) { - if(that.error && that.error.statusCode == 302 - && that.error.message.indexOf(url) > -1) { - resolve(); - } - else { - reject(new Error("Not redirected")); - } - }); -}); \ No newline at end of file diff --git a/test/features/step_definitions/authentication.ts b/test/features/step_definitions/authentication.ts deleted file mode 100644 index 7c12e4da..00000000 --- a/test/features/step_definitions/authentication.ts +++ /dev/null @@ -1,99 +0,0 @@ -import {Given, When, Then, TableDefinition} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); -import Fs = require("fs"); -import Speakeasy = require("speakeasy"); -import CustomWorld = require("../support/world"); -import BluebirdPromise = require("bluebird"); -import Request = require("request-promise"); - -When(/^I visit "(https:\/\/[a-zA-Z0-9:%&._\/=?-]+)"$/, function (link: string) { - 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 {string} to {string}", function (fieldName: string, content: string) { - return this.setFieldTo(fieldName, content); -}); - -When("I clear field {string}", function (fieldName: string) { - return this.clearField(fieldName); -}); - -When("I click on {string}", function (text: string) { - return this.clickOnButton(text); -}); - -Given("I login with user {string} and password {string}", - function (username: string, password: string) { - return this.loginWithUserPassword(username, password); - }); - -Given("I login with user {string} and password {string} \ -and I use TOTP token handle {string}", - function (username: string, password: string, totpTokenHandle: string) { - const that = this; - return this.loginWithUserPassword(username, password) - .then(function () { - return that.useTotpTokenHandle(totpTokenHandle); - }); - }); - -Given("I register a TOTP secret called {string}", function (handle: string) { - return this.registerTotpSecret(handle); -}); - -Given("I use {string} as TOTP token", function (token: string) { - return this.useTotpToken(token); -}); - -Given("I use {string} as TOTP token handle", function (handle) { - return this.useTotpTokenHandle(handle); -}); - -When("I visit {string} and get redirected {string}", - function (url: string, redirectUrl: string) { - const that = this; - return this.driver.get(url) - .then(function () { - return that.driver.wait(seleniumWebdriver.until.urlIs(redirectUrl), 5000); - }); - }); - -Given("I register TOTP and login with user {string} and password {string}", - function (username: string, password: string) { - return this.registerTotpAndSignin(username, password); - }); - -function endpointReplyWith(context: any, link: string, method: string, - returnCode: number) { - return Request(link, { - method: method - }) - .then(function (response: string) { - Assert(response.indexOf("Error " + returnCode) >= 0); - return BluebirdPromise.resolve(); - }, function (response: any) { - Assert.equal(response.statusCode, returnCode); - return BluebirdPromise.resolve(); - }); -} - -Then("the following endpoints reply with:", function (dataTable: TableDefinition) { - const promises = []; - for (let i = 0; i < dataTable.rows().length; i++) { - const url: string = (dataTable.hashes() as any)[i].url; - const method: string = (dataTable.hashes() as any)[i].method; - const code: number = (dataTable.hashes() as any)[i].code; - promises.push(endpointReplyWith(this, url, method, code)); - } - return BluebirdPromise.all(promises); -}); diff --git a/test/features/step_definitions/forward-headers.ts b/test/features/step_definitions/forward-headers.ts deleted file mode 100644 index a459a4bf..00000000 --- a/test/features/step_definitions/forward-headers.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {Then} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import CustomWorld = require("../support/world"); -import Util = require("util"); -import Bluebird = require("bluebird"); -import Request = require("request-promise"); - -Then("I see header {string} set to {string}", - { timeout: 5000 }, - function (expectedHeaderName: string, expectedValue: string) { - return this.driver.findElement(seleniumWebdriver.By.tagName("body")).getText() - .then(function (txt: string) { - const expectedLine = Util.format("\"%s\": \"%s\"", expectedHeaderName, expectedValue); - if (txt.indexOf(expectedLine) > 0) - return Bluebird.resolve(); - else - return Bluebird.reject(new Error(Util.format("No such header or with unexpected value."))); - }); - }) \ No newline at end of file diff --git a/test/features/step_definitions/hooks.ts b/test/features/step_definitions/hooks.ts deleted file mode 100644 index fc250a7e..00000000 --- a/test/features/step_definitions/hooks.ts +++ /dev/null @@ -1,173 +0,0 @@ -import {setDefaultTimeout, After, Before, BeforeAll, AfterAll} from "cucumber"; -import fs = require("fs"); -import BluebirdPromise = require("bluebird"); -import ChildProcess = require("child_process"); -import { UserDataStore } from "../../../server/src/lib/storage/UserDataStore"; -import { CollectionFactoryFactory } from "../../../server/src/lib/storage/CollectionFactoryFactory"; -import { IMongoClient } from "../../../server/src/lib/connectors/mongo/IMongoClient"; -import { TotpHandler } from "../../../server/src/lib/authentication/totp/TotpHandler"; -import Speakeasy = require("speakeasy"); -import Request = require("request-promise"); -import { TOTPSecret } from "../../../server/types/TOTPSecret"; -import Environment = require("../../environment"); -import { MongoClient } from "../../../server/src/lib/connectors/mongo/MongoClient"; -import { GlobalLogger } from "../../../server/src/lib/logging/GlobalLogger"; -import { GlobalLoggerStub } from "../../../server/src/lib/logging/GlobalLoggerStub.spec"; - -setDefaultTimeout(30 * 1000); - -const exec = BluebirdPromise.promisify(ChildProcess.exec); - -const includes = [ - "docker-compose.yml", - "example/compose/docker-compose.base.yml", - "example/compose/mongo/docker-compose.yml", - "example/compose/redis/docker-compose.yml", - "example/compose/nginx/backend/docker-compose.yml", - "example/compose/nginx/portal/docker-compose.yml", - "example/compose/smtp/docker-compose.yml", - "example/compose/httpbin/docker-compose.yml", - "example/compose/ldap/docker-compose.yml" -] - -const environment = new Environment.Environment(includes); - -BeforeAll(function() { - return environment.setup(10000); -}); - -AfterAll(function() { - return environment.cleanup() -}); - -Before(function () { - this.jar = Request.jar(); -}) - -After(function () { - return this.driver.quit(); -}); - -function createRegulationConfiguration(): BluebirdPromise { - return exec("\ - cat config.template.yml | \ - sed 's/find_time: [0-9]\\+/find_time: 15/' | \ - sed 's/ban_time: [0-9]\\+/ban_time: 4/' > config.test.yml \ - "); -} - -function createInactivityConfiguration(): BluebirdPromise { - return exec("\ - cat config.template.yml | \ - sed 's/expiration: [0-9]\\+/expiration: 10000/' | \ - sed 's/inactivity: [0-9]\\+/inactivity: 5000/' > config.test.yml \ - "); -} - -function createSingleFactorConfiguration(): BluebirdPromise { - return exec("\ - cat config.template.yml | \ - sed 's/default_method: two_factor/default_method: single_factor/' > config.test.yml \ - "); -} - -function createCustomTotpIssuerConfiguration(): BluebirdPromise { - return exec("\ - cat config.template.yml | \ - sed 's/issuer: authelia.com/issuer: custom.com/' > config.test.yml \ - "); -} - -function declareNeedsConfiguration(tag: string, cb: () => BluebirdPromise) { - Before({ tags: "@needs-" + tag + "-config", timeout: 20 * 1000 }, function () { - return cb() - .then(function () { - return exec("./scripts/example-commit/dc-example.sh -f " + - "./example/compose/authelia/docker-compose.test.yml up -d authelia &&" + - " sleep 3"); - }) - }); - - After({ tags: "@needs-" + tag + "-config", timeout: 20 * 1000 }, function () { - return exec("rm config.test.yml") - .then(function () { - return exec("./scripts/example-commit/dc-example.sh up -d authelia && sleep 3"); - }); - }); -} - -declareNeedsConfiguration("regulation", createRegulationConfiguration); -declareNeedsConfiguration("inactivity", createInactivityConfiguration); -declareNeedsConfiguration("single_factor", createSingleFactorConfiguration); -declareNeedsConfiguration("totp_issuer", createCustomTotpIssuerConfiguration); - -function registerUser(context: any, username: string) { - let secret: TOTPSecret; - const mongoClient = new MongoClient({ - url: "mongodb://localhost:27017", - database: "authelia", - auth: { - username: "authelia", - password: "authelia" - } - }, new GlobalLoggerStub()); - const collectionFactory = CollectionFactoryFactory.createMongo(mongoClient); - const userDataStore = new UserDataStore(collectionFactory); - - const generator = new TotpHandler(Speakeasy); - secret = generator.generate("user", "authelia.com"); - return userDataStore.saveTOTPSecret(username, secret) - .then(function () { - context.totpSecrets["REGISTERED"] = secret.base32; - return mongoClient.close(); - }); -} - -function declareNeedRegisteredUserHooks(username: string) { - Before({ tags: "@need-registered-user-" + username, timeout: 15 * 1000 }, function () { - return registerUser(this, username); - }); - - After({ tags: "@need-registered-user-" + username, timeout: 15 * 1000 }, function () { - this.totpSecrets["REGISTERED"] = undefined; - return BluebirdPromise.resolve(); - }); -} - -function needAuthenticatedUser(context: any, username: string): BluebirdPromise { - return context.visit("https://login.example.com:8080/logout") - .then(function () { - return context.visit("https://login.example.com:8080/"); - }) - .then(function () { - return registerUser(context, username); - }) - .then(function () { - return context.loginWithUserPassword(username, "password"); - }) - .then(function () { - return context.useTotpTokenHandle("REGISTERED"); - }) - .then(function () { - return context.clickOnButton("Sign in"); - }); -} - -function declareNeedAuthenticatedUserHooks(username: string) { - Before({ tags: "@need-authenticated-user-" + username, timeout: 15 * 1000 }, function () { - return needAuthenticatedUser(this, username); - }); - - After({ tags: "@need-authenticated-user-" + username, timeout: 15 * 1000 }, function () { - this.totpSecrets["REGISTERED"] = undefined; - return BluebirdPromise.resolve(); - }); -} - -function declareHooksForUser(username: string) { - declareNeedRegisteredUserHooks(username); - declareNeedAuthenticatedUserHooks(username); -} - -const users = ["harry", "john", "bob", "blackhat"]; -users.forEach(declareHooksForUser); diff --git a/test/features/step_definitions/notifications.ts b/test/features/step_definitions/notifications.ts deleted file mode 100644 index 4bb3ef70..00000000 --- a/test/features/step_definitions/notifications.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {Then} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); -import Fs = require("fs"); -import CustomWorld = require("../support/world"); - -Then("I get a notification of type {string} with message {string}", { timeout: 10 * 1000 }, -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), 5000) - .then(function () { - return notificationEl.getText(); - }) - .then(function (txt: string) { - Assert.equal(notificationMessage, txt); - return notificationEl.getAttribute("class"); - }) - .then(function (classes: string) { - Assert(classes.indexOf(notificationType) > -1, "Class '" + notificationType + "' not found in notification element."); - return that.driver.sleep(500); - }); -}); \ No newline at end of file diff --git a/test/features/step_definitions/redirection.ts b/test/features/step_definitions/redirection.ts deleted file mode 100644 index 23797c5e..00000000 --- a/test/features/step_definitions/redirection.ts +++ /dev/null @@ -1,15 +0,0 @@ -import {Given, When, Then} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); - -Given("I'm on {string}", function (link: string) { - return this.driver.get(link); -}); - -When("I click on the link to {string}", function (link: string) { - return this.driver.findElement(seleniumWebdriver.By.linkText(link)).click(); -}); - -Then("I'm redirected to {string}", function (link: string) { - return this.waitUntilUrlContains(link); -}); \ No newline at end of file diff --git a/test/features/step_definitions/registration.ts b/test/features/step_definitions/registration.ts deleted file mode 100644 index 7e861c54..00000000 --- a/test/features/step_definitions/registration.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {When} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); - -When("the otpauth url has label {string} and issuer \ -{string}", function (label: string, issuer: string) { - return this.driver.findElement(seleniumWebdriver.By.id("qrcode")) - .getAttribute("title") - .then(function (title: string) { - const re = `^otpauth://totp/${label}\\?secret=[A-Z0-9]+&issuer=${issuer}$`; - Assert(new RegExp(re).test(title)); - }) - }); diff --git a/test/features/step_definitions/regulation.ts b/test/features/step_definitions/regulation.ts deleted file mode 100644 index c2ac4628..00000000 --- a/test/features/step_definitions/regulation.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {When} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); -import Fs = require("fs"); -import CustomWorld = require("../support/world"); - -When("I wait {int} seconds", { timeout: 10 * 1000 }, function (seconds: number) { - return this.driver.sleep(seconds * 1000); -}); diff --git a/test/features/step_definitions/reset-password.ts b/test/features/step_definitions/reset-password.ts deleted file mode 100644 index d84c4a30..00000000 --- a/test/features/step_definitions/reset-password.ts +++ /dev/null @@ -1,16 +0,0 @@ -import {When} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); -import Fs = require("fs"); - -When("I click on the link {string}", function (text: string) { - return this.driver.findElement(seleniumWebdriver.By.linkText(text)).click(); -}); - -When("I click on the link of the email", function () { - const that = this; - return this.retrieveLatestMail() - .then(function (link: string) { - return that.driver.get(link); - }); -}); \ No newline at end of file diff --git a/test/features/step_definitions/resilience.ts b/test/features/step_definitions/resilience.ts deleted file mode 100644 index 897cf962..00000000 --- a/test/features/step_definitions/resilience.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {When} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); -import ChildProcess = require("child_process"); -import BluebirdPromise = require("bluebird"); - -When(/^the application restarts$/, {timeout: 15 * 1000}, function () { - const exec = BluebirdPromise.promisify(ChildProcess.exec); - return exec("./scripts/example-commit/dc-example.sh restart authelia && sleep 3"); -}); \ No newline at end of file diff --git a/test/features/step_definitions/restrictions.ts b/test/features/step_definitions/restrictions.ts deleted file mode 100644 index db218ab2..00000000 --- a/test/features/step_definitions/restrictions.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {Before, When, Then, TableDefinition} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Assert = require("assert"); -import Request = require("request-promise"); -import Bluebird = require("bluebird"); - -Before(function () { - this.jar = Request.jar(); -}); - -Then("I get an error {int}", function (code: number) { - return this.getErrorPage(code); -}); - -function requestAndExpectStatusCode(ctx: any, url: string, method: string, - expectedStatusCode: number) { - return Request(url, { - method: method, - jar: ctx.jar - }) - .then(function (body: string) { - return Bluebird.resolve(parseInt(body.match(/Error ([0-9]{3})/)[1])); - }, function (response: any) { - return Bluebird.resolve(response.statusCode) - }) - .then(function (statusCode: number) { - try { - Assert.equal(statusCode, expectedStatusCode); - } - catch (e) { - console.log("%s (actual) != %s (expected)", statusCode, - expectedStatusCode); - throw e; - } - }) -} - -Then("I get the following status code when requesting:", - function (dataTable: TableDefinition) { - const promises: Bluebird[] = []; - for (let i = 0; i < dataTable.rows().length; i++) { - const url: string = (dataTable.hashes() as any)[i].url; - const method: string = (dataTable.hashes() as any)[i].method; - const code: number = (dataTable.hashes() as any)[i].code; - promises.push(requestAndExpectStatusCode(this, url, method, code)); - } - return Bluebird.all(promises); - }) - -When("I post {string} with body:", function (url: string, - dataTable: TableDefinition) { - const body = {}; - for (let i = 0; i < dataTable.rows().length; i++) { - const key = (dataTable.hashes() as any)[i].key; - const value = (dataTable.hashes() as any)[i].value; - body[key] = value; - } - return Request.post(url, { - body: body, - jar: this.jar, - json: true - }); -}); \ No newline at end of file diff --git a/test/features/step_definitions/session-timeout.ts b/test/features/step_definitions/session-timeout.ts deleted file mode 100644 index bbeb66f7..00000000 --- a/test/features/step_definitions/session-timeout.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {When} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); - -When("I sleep for {int} seconds", function (seconds: number) { - return this.driver.sleep(seconds * 1000); -}); diff --git a/test/features/step_definitions/single-factor.ts b/test/features/step_definitions/single-factor.ts deleted file mode 100644 index 222480c7..00000000 --- a/test/features/step_definitions/single-factor.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {When, Then} from "cucumber"; -import seleniumWebdriver = require("selenium-webdriver"); -import Request = require("request-promise"); -import BluebirdPromise = require("bluebird"); -import Util = require("util"); - -When("I request {string} with username {string}" + - " and password {string} using basic authentication", - function (url: string, username: string, password: string) { - const that = this; - return Request(url, { - auth: { - username: username, - password: password - }, - resolveWithFullResponse: true - }) - .then(function (response: any) { - that.response = response; - }); - }); - -Then("I receive the secret page", function () { - if (this.response.body.match("This is a very important secret!")) - return BluebirdPromise.resolve(); - return BluebirdPromise.reject(new Error("Secret page not received.")); -}); - -Then("I received header {string} set to {string}", - function (expectedHeaderName: string, expectedValue: string) { - const expectedLine = Util.format("\"%s\": \"%s\"", expectedHeaderName, - expectedValue); - if (this.response.body.indexOf(expectedLine) > 0) - return BluebirdPromise.resolve(); - return BluebirdPromise.reject(new Error( - Util.format("No such header or with unexpected value."))); - }); diff --git a/test/features/support/world.ts b/test/features/support/world.ts deleted file mode 100644 index 1a27bd25..00000000 --- a/test/features/support/world.ts +++ /dev/null @@ -1,183 +0,0 @@ -require("chromedriver"); -import seleniumWebdriver = require("selenium-webdriver"); -import {setWorldConstructor, After} from "cucumber"; -import Fs = require("fs"); -import Speakeasy = require("speakeasy"); -import Assert = require("assert"); -import Request = require("request-promise"); -import BluebirdPromise = require("bluebird"); - -process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" - -function CustomWorld() { - const that = this; - this.driver = new seleniumWebdriver.Builder() - .forBrowser("chrome") - .build(); - - this.totpSecrets = {}; - this.configuration = {}; - - this.visit = function (link: string) { - return this.driver.get(link); - }; - - this.setFieldTo = function (fieldName: string, content: string) { - const that = this; - return this.driver.findElement(seleniumWebdriver.By.id(fieldName)) - .sendKeys(content); - }; - - this.clearField = function (fieldName: string) { - return this.driver.findElement(seleniumWebdriver.By.id(fieldName)).clear(); - }; - - this.getErrorPage = function (code: number) { - const that = this; - return this.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.tagName("h1")), 5000) - .then(function () { - return that.driver - .findElement(seleniumWebdriver.By.tagName("h1")).getText(); - }) - .then(function (txt: string) { - try { - Assert.equal(txt, "Error " + code); - } catch (e) { - console.log(txt); - throw e; - } - }) - }; - - this.clickOnButton = function (buttonText: string) { - const that = this; - return this.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.tagName("button")), 5000) - .then(function () { - return that.driver - .findElement(seleniumWebdriver.By.tagName("button")) - .findElement(seleniumWebdriver.By.xpath("//button[contains(.,'" + buttonText + "')]")) - .click(); - }); - }; - - this.waitUntilUrlContains = function (url: string) { - const that = this; - return this.driver.wait(seleniumWebdriver.until.urlIs(url), 15000) - .then(function () {return BluebirdPromise.resolve(); }, function (err: Error) { - that.driver.getCurrentUrl() - .then(function (current: string) { - console.error("====> Error due to: %s (current) != %s (expected)", current, url); - }); - return BluebirdPromise.reject(err); - }); - }; - - this.loginWithUserPassword = function (username: string, password: string) { - return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id("username")), 5000) - .then(function () { - return that.driver.findElement(seleniumWebdriver.By.id("username")) - .sendKeys(username); - }) - .then(function () { - return that.driver.findElement(seleniumWebdriver.By.id("password")) - .clear(); - }) - .then(function () { - return that.driver.findElement(seleniumWebdriver.By.id("password")) - .sendKeys(password); - }) - .then(function () { - return that.driver.findElement(seleniumWebdriver.By.tagName("button")) - .click(); - }); - }; - - this.retrieveLatestMail = function () { - return Request({ - method: "GET", - uri: "http://localhost:8085/messages", - json: true - }) - .then(function (data: any) { - const messageId = data[data.length - 1].id; - return Request({ - method: "GET", - uri: `http://localhost:8085/messages/${messageId}.html` - }); - }) - .then(function (data: any) { - const regexp = new RegExp(/Continue<\/a>/); - const match = regexp.exec(data); - const link = match[1]; - return BluebirdPromise.resolve(link); - }); - }; - - this.registerTotpSecret = function (totpSecretHandle: string) { - return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.className("register-totp")), 5000) - .then(function () { - return that.driver.findElement(seleniumWebdriver.By.className("register-totp")).click(); - }) - .then(function () { - return that.retrieveLatestMail(); - }) - .then(function (url: string) { - return that.driver.get(url); - }) - .then(function () { - return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id("secret")), 5000); - }) - .then(function () { - return that.driver.findElement(seleniumWebdriver.By.id("secret")).getText(); - }) - .then(function (secret: string) { - that.totpSecrets[totpSecretHandle] = secret; - }); - }; - - this.useTotpTokenHandle = function (totpSecretHandle: string) { - if (!this.totpSecrets[totpSecretHandle]) - throw new Error("No available TOTP token handle " + totpSecretHandle); - - const token = Speakeasy.totp({ - secret: this.totpSecrets[totpSecretHandle], - encoding: "base32" - }); - return this.useTotpToken(token); - }; - - this.useTotpToken = function (totpSecret: string) { - return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id("token")), 5000) - .then(function () { - return that.driver.findElement(seleniumWebdriver.By.id("token")) - .sendKeys(totpSecret); - }); - }; - - this.registerTotpAndSignin = function (username: string, password: string) { - const totpHandle = "HANDLE"; - const authUrl = "https://login.example.com:8080/"; - const that = this; - return this.visit(authUrl) - .then(function () { - return that.loginWithUserPassword(username, password); - }) - .then(function () { - return that.registerTotpSecret(totpHandle); - }) - .then(function () { - return that.visit(authUrl); - }) - .then(function () { - return that.loginWithUserPassword(username, password); - }) - .then(function () { - return that.useTotpTokenHandle(totpHandle); - }) - .then(function () { - return that.clickOnButton("Sign in"); - }); - }; -} - -setWorldConstructor(CustomWorld); diff --git a/test/helpers/FullLogin.ts b/test/helpers/FullLogin.ts index 2cdb5df9..6b88945c 100644 --- a/test/helpers/FullLogin.ts +++ b/test/helpers/FullLogin.ts @@ -1,6 +1,5 @@ import FillLoginPageAndClick from "./FillLoginPageAndClick"; import ValidateTotp from "./ValidateTotp"; -import VerifyUrlIs from "./assertions/VerifyUrlIs"; import { WebDriver } from "selenium-webdriver"; import VisitPageAndWaitUrlIs from "./behaviors/VisitPageAndWaitUrlIs"; @@ -9,5 +8,4 @@ export default async function(driver: WebDriver, user: string, secret: string, u await VisitPageAndWaitUrlIs(driver, `https://login.example.com:8080/?rd=${url}`); await FillLoginPageAndClick(driver, user, 'password'); await ValidateTotp(driver, secret); - await VerifyUrlIs(driver, url); } \ No newline at end of file diff --git a/test/helpers/LoginAndRegisterTotp.ts b/test/helpers/LoginAndRegisterTotp.ts index 0014970c..f62c4fbb 100644 --- a/test/helpers/LoginAndRegisterTotp.ts +++ b/test/helpers/LoginAndRegisterTotp.ts @@ -3,8 +3,8 @@ import LoginAs from './LoginAs'; import { WebDriver } from 'selenium-webdriver'; import VerifyIsSecondFactorStage from './assertions/VerifyIsSecondFactorStage'; -export default async function(driver: WebDriver, user: string, email: boolean = false) { - await LoginAs(driver, user); +export default async function(driver: WebDriver, user: string, password: string, email: boolean = false) { + await LoginAs(driver, user, password); await VerifyIsSecondFactorStage(driver); return await RegisterTotp(driver, email); } \ No newline at end of file diff --git a/test/helpers/LoginAs.ts b/test/helpers/LoginAs.ts index f2765d6f..1894d211 100644 --- a/test/helpers/LoginAs.ts +++ b/test/helpers/LoginAs.ts @@ -2,7 +2,8 @@ import FillLoginPageAndClick from './FillLoginPageAndClick'; import { WebDriver } from "selenium-webdriver"; import VisitPageAndWaitUrlIs from "./behaviors/VisitPageAndWaitUrlIs"; -export default async function(driver: WebDriver, user: string, password: string = "password") { - await VisitPageAndWaitUrlIs(driver, "https://login.example.com:8080/"); +export default async function(driver: WebDriver, user: string, password: string, targetUrl?: string) { + const urlExt = (targetUrl) ? ('rd=' + targetUrl) : ''; + await VisitPageAndWaitUrlIs(driver, "https://login.example.com:8080/" + urlExt); await FillLoginPageAndClick(driver, user, password); } \ No newline at end of file diff --git a/test/helpers/VisitPage.ts b/test/helpers/VisitPage.ts index f8dea0fe..6a6d781c 100644 --- a/test/helpers/VisitPage.ts +++ b/test/helpers/VisitPage.ts @@ -1,4 +1,4 @@ -import SeleniumWebdriver, { WebDriver } from "selenium-webdriver"; +import { WebDriver } from "selenium-webdriver"; export default async function(driver: WebDriver, url: string, timeout: number = 5000) { await driver.get(url); diff --git a/test/helpers/assertions/VerifyForwardedHeaderIs.ts b/test/helpers/assertions/VerifyForwardedHeaderIs.ts index d4caba02..2df8c6a5 100644 --- a/test/helpers/assertions/VerifyForwardedHeaderIs.ts +++ b/test/helpers/assertions/VerifyForwardedHeaderIs.ts @@ -1,8 +1,9 @@ import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; import Util from "util"; -export default async function(driver: WebDriver, header: string, expectedValue: string) { - const el = await driver.wait(SeleniumWebDriver.until.elementLocated(SeleniumWebDriver.By.tagName("body")), 5000); +export default async function(driver: WebDriver, header: string, expectedValue: string, timeout: number = 5000) { + const el = await driver.wait(SeleniumWebDriver.until.elementLocated( + SeleniumWebDriver.By.tagName("body")), timeout); const text = await el.getText(); const expectedLine = Util.format("\"%s\": \"%s\"", header, expectedValue); diff --git a/test/helpers/assertions/VerifyIsAlreadyAuthenticatedStage.ts b/test/helpers/assertions/VerifyIsAlreadyAuthenticatedStage.ts index 8475b5c0..e6e0d07f 100644 --- a/test/helpers/assertions/VerifyIsAlreadyAuthenticatedStage.ts +++ b/test/helpers/assertions/VerifyIsAlreadyAuthenticatedStage.ts @@ -1,5 +1,6 @@ import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; -export default async function(driver: WebDriver) { - await driver.wait(SeleniumWebDriver.until.elementLocated(SeleniumWebDriver.By.className('already-authenticated-step'))); +export default async function(driver: WebDriver, timeout: number = 5000) { + await driver.wait(SeleniumWebDriver.until.elementLocated( + SeleniumWebDriver.By.className('already-authenticated-step')), timeout); } \ No newline at end of file diff --git a/test/helpers/assertions/VerifyIsSecondFactorStage.ts b/test/helpers/assertions/VerifyIsSecondFactorStage.ts index 41a385df..1d7f22a7 100644 --- a/test/helpers/assertions/VerifyIsSecondFactorStage.ts +++ b/test/helpers/assertions/VerifyIsSecondFactorStage.ts @@ -1,5 +1,6 @@ import SeleniumWebDriver, { WebDriver } from "selenium-webdriver"; -export default async function(driver: WebDriver) { - await driver.wait(SeleniumWebDriver.until.elementLocated(SeleniumWebDriver.By.className('second-factor-step'))); +export default async function(driver: WebDriver, timeout: number = 5000) { + await driver.wait(SeleniumWebDriver.until.elementLocated( + SeleniumWebDriver.By.className('second-factor-step')), timeout); } \ No newline at end of file diff --git a/test/helpers/assertions/VerifyNotificationDisplayed.ts b/test/helpers/assertions/VerifyNotificationDisplayed.ts index 97865331..52bda174 100644 --- a/test/helpers/assertions/VerifyNotificationDisplayed.ts +++ b/test/helpers/assertions/VerifyNotificationDisplayed.ts @@ -1,8 +1,9 @@ import SeleniumWebdriver, { WebDriver } from "selenium-webdriver"; import Assert = require("assert"); -export default async function(driver: WebDriver, message: string) { - await driver.wait(SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.className("notification")), 5000) +export default async function(driver: WebDriver, message: string, timeout: number = 5000) { + await driver.wait(SeleniumWebdriver.until.elementLocated( + SeleniumWebdriver.By.className("notification")), timeout) const notificationEl = driver.findElement(SeleniumWebdriver.By.className("notification")); const txt = await notificationEl.getText(); Assert.equal(message, txt); diff --git a/test/helpers/behaviors/RegisterAndLoginTwoFactor.ts b/test/helpers/behaviors/RegisterAndLoginTwoFactor.ts index 475c3732..2ea4cb22 100644 --- a/test/helpers/behaviors/RegisterAndLoginTwoFactor.ts +++ b/test/helpers/behaviors/RegisterAndLoginTwoFactor.ts @@ -5,9 +5,11 @@ import FullLogin from "../FullLogin"; export default async function( driver: WebDriver, username: string, + password: string, email: boolean = false, targetUrl: string = "https://login.example.com:8080/") { - const secret = await LoginAndRegisterTotp(driver, username, email); + const secret = await LoginAndRegisterTotp(driver, username, password, email); await FullLogin(driver, username, secret, targetUrl); + return secret; }; \ No newline at end of file diff --git a/test/suites/complete/scenarii/AccessControl.ts b/test/suites/complete/scenarii/AccessControl.ts index 8743b38a..225cb436 100644 --- a/test/suites/complete/scenarii/AccessControl.ts +++ b/test/suites/complete/scenarii/AccessControl.ts @@ -34,7 +34,7 @@ export default function() { WithDriver(); before(async function() { - const secret = await LoginAndRegisterTotp(this.driver, "john", true); + const secret = await LoginAndRegisterTotp(this.driver, "john", "password", true); await VisitPageAndWaitUrlIs(this.driver, 'https://login.example.com:8080/'); await FillLoginPageAndClick(this.driver, 'john', 'password', false); await ValidateTotp(this.driver, secret); @@ -61,7 +61,7 @@ export default function() { WithDriver(); before(async function() { - const secret = await LoginAndRegisterTotp(this.driver, "bob", true); + const secret = await LoginAndRegisterTotp(this.driver, "bob", "password", true); await VisitPageAndWaitUrlIs(this.driver, 'https://login.example.com:8080/'); await FillLoginPageAndClick(this.driver, 'bob', 'password', false); await ValidateTotp(this.driver, secret); @@ -88,7 +88,7 @@ export default function() { WithDriver(); before(async function() { - const secret = await LoginAndRegisterTotp(this.driver, "harry", true); + const secret = await LoginAndRegisterTotp(this.driver, "harry", "password", true); await VisitPageAndWaitUrlIs(this.driver, 'https://login.example.com:8080/'); await FillLoginPageAndClick(this.driver, 'harry', 'password', false); await ValidateTotp(this.driver, secret); diff --git a/test/suites/complete/scenarii/AutheliaRestart.ts b/test/suites/complete/scenarii/AutheliaRestart.ts index 6f119d21..6a771cb9 100644 --- a/test/suites/complete/scenarii/AutheliaRestart.ts +++ b/test/suites/complete/scenarii/AutheliaRestart.ts @@ -15,7 +15,7 @@ export default function() { ChildProcess.execSync('rm -f .authelia-interrupt'); this.driver = await StartDriver(); - await RegisterAndLoginTwoFactor(this.driver, 'john', true, 'https://admin.example.com:8080/secret.html'); + await RegisterAndLoginTwoFactor(this.driver, 'john', "password", true, 'https://admin.example.com:8080/secret.html'); await VisitPageAndWaitUrlIs(this.driver, 'https://home.example.com:8080/'); }); @@ -46,7 +46,7 @@ export default function() { ChildProcess.execSync('rm -f .authelia-interrupt'); this.driver = await StartDriver(); - this.secret = await LoginAndRegisterTotp(this.driver, 'john', true); + this.secret = await LoginAndRegisterTotp(this.driver, 'john', "password", true); await Logout(this.driver); }); diff --git a/test/suites/complete/scenarii/AuthenticationRegulation.ts b/test/suites/complete/scenarii/AuthenticationRegulation.ts index d7ac0164..99a092fc 100644 --- a/test/suites/complete/scenarii/AuthenticationRegulation.ts +++ b/test/suites/complete/scenarii/AuthenticationRegulation.ts @@ -3,31 +3,14 @@ import LoginAs from "../../../helpers/LoginAs"; import VerifyNotificationDisplayed from "../../../helpers/assertions/VerifyNotificationDisplayed"; import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; -/* -Given I visit "https://login.example.com:8080/" - And I set field "username" to "blackhat" - And I set field "password" to "bad-password" - And I click on "Sign in" - And I get a notification of type "error" with message "Authentication failed. Please check your credentials." - And I set field "password" to "bad-password" - And I click on "Sign in" - And I get a notification of type "error" with message "Authentication failed. Please check your credentials." - And I set field "password" to "bad-password" - And I click on "Sign in" - And I get a notification of type "error" with message "Authentication failed. Please check your credentials." - When I set field "password" to "password" - And I click on "Sign in" - Then I get a notification of type "error" with message "Authentication failed. Please check your credentials." -*/ - export default function() { describe('Authelia regulates authentications when a hacker is brute forcing', function() { this.timeout(15000); - before(async function() { + beforeEach(async function() { this.driver = await StartDriver(); }); - after(async function() { + afterEach(async function() { await StopDriver(this.driver); }); diff --git a/test/suites/complete/scenarii/CustomHeadersForwarded.ts b/test/suites/complete/scenarii/CustomHeadersForwarded.ts index 546f3439..817b5b90 100644 --- a/test/suites/complete/scenarii/CustomHeadersForwarded.ts +++ b/test/suites/complete/scenarii/CustomHeadersForwarded.ts @@ -31,7 +31,7 @@ export default function() { describe("With two factors", function() { before(async function() { this.driver = await StartDriver(); - await RegisterAndLoginWith2FA(this.driver, "john", true, "https://public.example.com:8080/headers"); + await RegisterAndLoginWith2FA(this.driver, "john", "password", true, "https://public.example.com:8080/headers"); }); after(async function() { diff --git a/test/suites/complete/scenarii/EnforceInternalRedirectionsOnly.ts b/test/suites/complete/scenarii/EnforceInternalRedirectionsOnly.ts index 855be335..a8e01565 100644 --- a/test/suites/complete/scenarii/EnforceInternalRedirectionsOnly.ts +++ b/test/suites/complete/scenarii/EnforceInternalRedirectionsOnly.ts @@ -21,7 +21,7 @@ export default function() { beforeEach(async function() { this.driver = await StartDriver(); - secret = await LoginAndRegisterTotp(this.driver, "john", true) + secret = await LoginAndRegisterTotp(this.driver, "john", "password", true) }); afterEach(async function() { diff --git a/test/suites/minimal/index.ts b/test/suites/minimal/index.ts index d68ed4e9..82bf5d0e 100644 --- a/test/suites/minimal/index.ts +++ b/test/suites/minimal/index.ts @@ -9,6 +9,9 @@ import TOTPValidation from './scenarii/TOTPValidation'; import Inactivity from './scenarii/Inactivity'; import BackendProtection from './scenarii/BackendProtection'; import VerifyEndpoint from './scenarii/VerifyEndpoint'; +import RequiredTwoFactor from './scenarii/RequiredTwoFactor'; +import LogoutRedirectToAlreadyLoggedIn from './scenarii/LogoutRedirectToAlreadyLoggedIn'; +import SimpleAuthentication from './scenarii/SimpleAuthentication'; const execAsync = Bluebird.promisify(ChildProcess.exec); @@ -18,14 +21,14 @@ AutheliaSuite('Minimal configuration', __dirname + '/config.yml', function() { return execAsync("cp users_database.example.yml users_database.yml"); }); + describe('Simple authentication', SimpleAuthentication); describe('Backend protection', BackendProtection); describe('Verify API endpoint', VerifyEndpoint); - describe('Bad password', BadPassword); describe('Reset password', ResetPassword); - describe('TOTP Registration', RegisterTotp); describe('TOTP Validation', TOTPValidation); - describe('Inactivity period', Inactivity); + describe('Required two factor', RequiredTwoFactor); + describe('Logout endpoint redirect to already logged in page', LogoutRedirectToAlreadyLoggedIn); }); \ No newline at end of file diff --git a/test/suites/minimal/scenarii/Inactivity.ts b/test/suites/minimal/scenarii/Inactivity.ts index 36fa08bf..a3a9172c 100644 --- a/test/suites/minimal/scenarii/Inactivity.ts +++ b/test/suites/minimal/scenarii/Inactivity.ts @@ -12,7 +12,7 @@ export default function(this: Mocha.ISuiteCallbackContext) { beforeEach(async function() { this.driver = await StartDriver(); - this.secret = await LoginAndRegisterTotp(this.driver, "john", true); + this.secret = await LoginAndRegisterTotp(this.driver, "john", "password", true); }); afterEach(async function() { diff --git a/test/suites/minimal/scenarii/LogoutRedirectToAlreadyLoggedIn.ts b/test/suites/minimal/scenarii/LogoutRedirectToAlreadyLoggedIn.ts new file mode 100644 index 00000000..37530c9a --- /dev/null +++ b/test/suites/minimal/scenarii/LogoutRedirectToAlreadyLoggedIn.ts @@ -0,0 +1,23 @@ +import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver"; +import VisitPage from "../../../helpers/VisitPage"; +import VerifyIsAlreadyAuthenticatedStage from "../../../helpers/assertions/VerifyIsAlreadyAuthenticatedStage"; +import RegisterAndLoginTwoFactor from "../../../helpers/behaviors/RegisterAndLoginTwoFactor"; + + +export default function() { + describe('When visiting /logout the user is redirected to already logged in page to log out', function() { + before(async function() { + this.driver = await StartDriver(); + await RegisterAndLoginTwoFactor(this.driver, 'john', "password", true); + }); + + after(async function() { + await StopDriver(this.driver); + }); + + it('should redirect the user', async function() { + await VisitPage(this.driver, 'https://login.example.com:8080/logout'); + await VerifyIsAlreadyAuthenticatedStage(this.driver); + }); + }); +} \ No newline at end of file diff --git a/test/suites/minimal/scenarii/RegisterTotp.ts b/test/suites/minimal/scenarii/RegisterTotp.ts index 71162755..636d5f88 100644 --- a/test/suites/minimal/scenarii/RegisterTotp.ts +++ b/test/suites/minimal/scenarii/RegisterTotp.ts @@ -14,7 +14,7 @@ export default function() { beforeEach(async function() { this.driver = await StartDriver(); - await LoginAndRegisterTotp(this.driver, "john", true); + await LoginAndRegisterTotp(this.driver, "john", "password", true); }); afterEach(async function() { diff --git a/test/suites/minimal/scenarii/RequiredTwoFactor.ts b/test/suites/minimal/scenarii/RequiredTwoFactor.ts new file mode 100644 index 00000000..dd48a949 --- /dev/null +++ b/test/suites/minimal/scenarii/RequiredTwoFactor.ts @@ -0,0 +1,30 @@ +import LoginAndRegisterTotp from '../../../helpers/LoginAndRegisterTotp'; +import VerifyUrlIs from '../../../helpers/assertions/VerifyUrlIs'; +import { StartDriver, StopDriver } from '../../../helpers/context/WithDriver'; +import VerifyIsSecondFactorStage from '../../../helpers/assertions/VerifyIsSecondFactorStage'; +import VisitPage from '../../../helpers/VisitPage'; +import FillLoginPageAndClick from '../../../helpers/FillLoginPageAndClick'; + +export default function() { + describe('User tries to access a page protected by second factor while he only passed first factor', function() { + before(async function() { + this.driver = await StartDriver(); + const secret = await LoginAndRegisterTotp(this.driver, "john", "password", true); + if (!secret) throw new Error('No secret!'); + + await VisitPage(this.driver, "https://admin.example.com:8080/secret.html"); + await VerifyUrlIs(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); + await FillLoginPageAndClick(this.driver, "john", "password"); + await VerifyIsSecondFactorStage(this.driver); + }); + + after(async function() { + await StopDriver(this.driver); + }); + + it("should reach second factor page of login portal", async function() { + await VisitPage(this.driver, "https://admin.example.com:8080/secret.html"); + await VerifyIsSecondFactorStage(this.driver); + }); + }); +} \ No newline at end of file diff --git a/test/suites/minimal/scenarii/SimpleAuthentication.ts b/test/suites/minimal/scenarii/SimpleAuthentication.ts new file mode 100644 index 00000000..d88ea9d0 --- /dev/null +++ b/test/suites/minimal/scenarii/SimpleAuthentication.ts @@ -0,0 +1,38 @@ +import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver"; +import RegisterAndLoginTwoFactor from "../../../helpers/behaviors/RegisterAndLoginTwoFactor"; +import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs"; +import VisitPage from "../../../helpers/VisitPage"; +import VisitPageAndWaitUrlIs from "../../../helpers/behaviors/VisitPageAndWaitUrlIs"; + + +export default function() { + describe('The user is redirected to target url upon successful authentication', function() { + before(async function() { + this.driver = await StartDriver(); + await RegisterAndLoginTwoFactor(this.driver, 'john', "password", true, 'https://admin.example.com:8080/secret.html'); + }); + + after(async function() { + await StopDriver(this.driver); + }); + + it('should redirect the user', async function() { + await VerifyUrlIs(this.driver, 'https://admin.example.com:8080/secret.html'); + }); + }); + + describe('The target url is in "rd" parameter of the portal URL', function() { + before(async function() { + this.driver = await StartDriver(); + await VisitPage(this.driver, 'https://admin.example.com:8080/secret.html'); + }); + + after(async function() { + await StopDriver(this.driver); + }); + + it('should redirect the user', async function() { + await VerifyUrlIs(this.driver, 'https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html'); + }); + }) +} \ No newline at end of file diff --git a/test/suites/minimal/scenarii/TOTPValidation.ts b/test/suites/minimal/scenarii/TOTPValidation.ts index 9a9b2815..f5cbbbbd 100644 --- a/test/suites/minimal/scenarii/TOTPValidation.ts +++ b/test/suites/minimal/scenarii/TOTPValidation.ts @@ -17,7 +17,7 @@ export default function() { describe('Successfully pass second factor with TOTP', function() { before(async function() { this.driver = await StartDriver(); - const secret = await LoginAndRegisterTotp(this.driver, "john", true); + const secret = await LoginAndRegisterTotp(this.driver, "john", "password", true); if (!secret) throw new Error('No secret!'); await VisitPageAndWaitUrlIs(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); @@ -46,7 +46,7 @@ export default function() { describe('Fail validation of second factor with TOTP', function() { before(async function() { this.driver = await StartDriver(); - await LoginAndRegisterTotp(this.driver, "john", true); + await LoginAndRegisterTotp(this.driver, "john", "password", true); const BAD_TOKEN = "125478"; await VisitPageAndWaitUrlIs(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html");