From 595ee97182b126f2cde4189d38f4fc95e6fdb496 Mon Sep 17 00:00:00 2001 From: Clement Michaud Date: Tue, 12 Feb 2019 23:41:19 +0100 Subject: [PATCH] Add test behavior VisitPageAndWaitUrlIs. --- test/helpers/FullLogin.ts | 4 +-- test/helpers/LoginAs.ts | 4 +-- test/helpers/VisitPage.ts | 3 +- test/helpers/behaviors/LoginOneFactor.ts | 6 ++-- .../behaviors/VisitPageAndWaitUrlIs.ts | 8 +++++ test/suites/complete/index.ts | 2 ++ .../suites/complete/scenarii/AccessControl.ts | 11 +++--- .../EnforceInternalRedirectionsOnly.ts | 14 ++++---- .../scenarii/SingleFactorAuthentication.ts | 34 +++++++++++++++++++ test/suites/minimal/scenarii/BadPassword.ts | 4 +-- test/suites/minimal/scenarii/Inactivity.ts | 14 ++++---- test/suites/minimal/scenarii/ResetPassword.ts | 12 +++---- .../suites/minimal/scenarii/TOTPValidation.ts | 6 ++-- 13 files changed, 82 insertions(+), 40 deletions(-) create mode 100644 test/helpers/behaviors/VisitPageAndWaitUrlIs.ts create mode 100644 test/suites/complete/scenarii/SingleFactorAuthentication.ts diff --git a/test/helpers/FullLogin.ts b/test/helpers/FullLogin.ts index 72ee4dcc4..2cdb5df9c 100644 --- a/test/helpers/FullLogin.ts +++ b/test/helpers/FullLogin.ts @@ -1,12 +1,12 @@ -import VisitPage from "./VisitPage"; import FillLoginPageAndClick from "./FillLoginPageAndClick"; import ValidateTotp from "./ValidateTotp"; import VerifyUrlIs from "./assertions/VerifyUrlIs"; import { WebDriver } from "selenium-webdriver"; +import VisitPageAndWaitUrlIs from "./behaviors/VisitPageAndWaitUrlIs"; // Validate the two factors! export default async function(driver: WebDriver, user: string, secret: string, url: string) { - await VisitPage(driver, `https://login.example.com:8080/?rd=${url}`); + await VisitPageAndWaitUrlIs(driver, `https://login.example.com:8080/?rd=${url}`); await FillLoginPageAndClick(driver, user, 'password'); await ValidateTotp(driver, secret); await VerifyUrlIs(driver, url); diff --git a/test/helpers/LoginAs.ts b/test/helpers/LoginAs.ts index 30381b4f9..f2765d6fe 100644 --- a/test/helpers/LoginAs.ts +++ b/test/helpers/LoginAs.ts @@ -1,8 +1,8 @@ -import VisitPage from "./VisitPage"; 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 VisitPage(driver, "https://login.example.com:8080/"); + await VisitPageAndWaitUrlIs(driver, "https://login.example.com:8080/"); await FillLoginPageAndClick(driver, user, password); } \ No newline at end of file diff --git a/test/helpers/VisitPage.ts b/test/helpers/VisitPage.ts index 8bf36a1be..f8dea0fea 100644 --- a/test/helpers/VisitPage.ts +++ b/test/helpers/VisitPage.ts @@ -1,6 +1,5 @@ import SeleniumWebdriver, { WebDriver } from "selenium-webdriver"; export default async function(driver: WebDriver, url: string, timeout: number = 5000) { - await driver.get(url) - await driver.wait(SeleniumWebdriver.until.urlIs(url), timeout); + await driver.get(url); } \ No newline at end of file diff --git a/test/helpers/behaviors/LoginOneFactor.ts b/test/helpers/behaviors/LoginOneFactor.ts index eede3cbef..ead8e13fc 100644 --- a/test/helpers/behaviors/LoginOneFactor.ts +++ b/test/helpers/behaviors/LoginOneFactor.ts @@ -1,9 +1,7 @@ import { WebDriver } from "selenium-webdriver"; -import LoginAndRegisterTotp from "../LoginAndRegisterTotp"; -import FullLogin from "../FullLogin"; -import VisitPage from "../VisitPage"; import FillLoginPageAndClick from "../FillLoginPageAndClick"; import VerifyUrlIs from "../assertions/VerifyUrlIs"; +import VisitPageAndWaitUrlIs from "./VisitPageAndWaitUrlIs"; export default async function( driver: WebDriver, @@ -11,7 +9,7 @@ export default async function( password: string, targetUrl: string) { - await VisitPage(driver, `https://login.example.com:8080/?rd=${targetUrl}`); + await VisitPageAndWaitUrlIs(driver, `https://login.example.com:8080/?rd=${targetUrl}`); await FillLoginPageAndClick(driver, username, password); await VerifyUrlIs(driver, targetUrl); }; \ No newline at end of file diff --git a/test/helpers/behaviors/VisitPageAndWaitUrlIs.ts b/test/helpers/behaviors/VisitPageAndWaitUrlIs.ts new file mode 100644 index 000000000..6ff8229aa --- /dev/null +++ b/test/helpers/behaviors/VisitPageAndWaitUrlIs.ts @@ -0,0 +1,8 @@ +import { WebDriver } from "selenium-webdriver"; +import VerifyUrlIs from "../assertions/VerifyUrlIs"; +import VisitPage from "../VisitPage"; + +export default async function(driver: WebDriver, url: string, timeout: number = 5000) { + await VisitPage(driver, url); + await VerifyUrlIs(driver, url); +} \ No newline at end of file diff --git a/test/suites/complete/index.ts b/test/suites/complete/index.ts index 5dd9a6892..6e0126b1e 100644 --- a/test/suites/complete/index.ts +++ b/test/suites/complete/index.ts @@ -3,6 +3,7 @@ import MongoConnectionRecovery from "./scenarii/MongoConnectionRecovery"; import EnforceInternalRedirectionsOnly from "./scenarii/EnforceInternalRedirectionsOnly"; import AccessControl from "./scenarii/AccessControl"; import CustomHeadersForwarded from "./scenarii/CustomHeadersForwarded"; +import SingleFactorAuthentication from "./scenarii/SingleFactorAuthentication"; AutheliaSuite('Complete configuration', __dirname + '/config.yml', function() { this.timeout(10000); @@ -12,4 +13,5 @@ AutheliaSuite('Complete configuration', __dirname + '/config.yml', function() { describe('Mongo broken connection recovery', MongoConnectionRecovery); describe('Enforce internal redirections only', EnforceInternalRedirectionsOnly); + describe('Single factor authentication', SingleFactorAuthentication); }); \ No newline at end of file diff --git a/test/suites/complete/scenarii/AccessControl.ts b/test/suites/complete/scenarii/AccessControl.ts index 66766e2e0..8743b38a2 100644 --- a/test/suites/complete/scenarii/AccessControl.ts +++ b/test/suites/complete/scenarii/AccessControl.ts @@ -6,17 +6,18 @@ import FillLoginPageAndClick from "../../../helpers/FillLoginPageAndClick"; import ValidateTotp from "../../../helpers/ValidateTotp"; import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs"; import Logout from "../../../helpers/Logout"; +import VisitPageAndWaitUrlIs from "../../../helpers/behaviors/VisitPageAndWaitUrlIs"; async function ShouldHaveAccessTo(url: string) { it('should have access to ' + url, async function() { - await VisitPage(this.driver, url); + await VisitPageAndWaitUrlIs(this.driver, url); await VerifySecretObserved(this.driver); }) } async function ShouldNotHaveAccessTo(url: string) { it('should not have access to ' + url, async function() { - await this.driver.get(url); + await VisitPage(this.driver, url); await VerifyUrlIs(this.driver, 'https://login.example.com:8080/'); }) } @@ -34,7 +35,7 @@ export default function() { before(async function() { const secret = await LoginAndRegisterTotp(this.driver, "john", true); - await VisitPage(this.driver, 'https://login.example.com:8080/'); + await VisitPageAndWaitUrlIs(this.driver, 'https://login.example.com:8080/'); await FillLoginPageAndClick(this.driver, 'john', 'password', false); await ValidateTotp(this.driver, secret); }) @@ -61,7 +62,7 @@ export default function() { before(async function() { const secret = await LoginAndRegisterTotp(this.driver, "bob", true); - await VisitPage(this.driver, 'https://login.example.com:8080/'); + await VisitPageAndWaitUrlIs(this.driver, 'https://login.example.com:8080/'); await FillLoginPageAndClick(this.driver, 'bob', 'password', false); await ValidateTotp(this.driver, secret); }) @@ -88,7 +89,7 @@ export default function() { before(async function() { const secret = await LoginAndRegisterTotp(this.driver, "harry", true); - await VisitPage(this.driver, 'https://login.example.com:8080/'); + 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/EnforceInternalRedirectionsOnly.ts b/test/suites/complete/scenarii/EnforceInternalRedirectionsOnly.ts index cd33d24ab..855be335e 100644 --- a/test/suites/complete/scenarii/EnforceInternalRedirectionsOnly.ts +++ b/test/suites/complete/scenarii/EnforceInternalRedirectionsOnly.ts @@ -1,11 +1,11 @@ import LoginAndRegisterTotp from "../../../helpers/LoginAndRegisterTotp"; -import VisitPage from "../../../helpers/VisitPage"; import FillLoginPageWithUserAndPasswordAndClick from '../../../helpers/FillLoginPageAndClick'; import ValidateTotp from "../../../helpers/ValidateTotp"; import Logout from "../../../helpers/Logout"; -import WaitRedirected from "../../../helpers/WaitRedirected"; -import IsAlreadyAuthenticatedStage from "../../../helpers/assertions/VerifyIsAlreadyAuthenticatedStage"; +import VerifyIsAlreadyAuthenticatedStage from "../../../helpers/assertions/VerifyIsAlreadyAuthenticatedStage"; import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver"; +import VisitPageAndWaitUrlIs from "../../../helpers/behaviors/VisitPageAndWaitUrlIs"; +import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs"; /* * Authelia should not be vulnerable to open redirection. Otherwise it would aid an @@ -31,19 +31,19 @@ export default function() { function CannotRedirectTo(url: string) { it(`should redirect to already authenticated page when requesting ${url}`, async function() { - await VisitPage(this.driver, `https://login.example.com:8080/?rd=${url}`); + await VisitPageAndWaitUrlIs(this.driver, `https://login.example.com:8080/?rd=${url}`); await FillLoginPageWithUserAndPasswordAndClick(this.driver, 'john', 'password'); await ValidateTotp(this.driver, secret); - await IsAlreadyAuthenticatedStage(this.driver); + await VerifyIsAlreadyAuthenticatedStage(this.driver); }); } function CanRedirectTo(url: string) { it(`should redirect to ${url}`, async function() { - await VisitPage(this.driver, `https://login.example.com:8080/?rd=${url}`); + await VisitPageAndWaitUrlIs(this.driver, `https://login.example.com:8080/?rd=${url}`); await FillLoginPageWithUserAndPasswordAndClick(this.driver, 'john', 'password'); await ValidateTotp(this.driver, secret); - await WaitRedirected(this.driver, url); + await VerifyUrlIs(this.driver, url); }); } diff --git a/test/suites/complete/scenarii/SingleFactorAuthentication.ts b/test/suites/complete/scenarii/SingleFactorAuthentication.ts new file mode 100644 index 000000000..92d4807cc --- /dev/null +++ b/test/suites/complete/scenarii/SingleFactorAuthentication.ts @@ -0,0 +1,34 @@ +import Logout from "../../../helpers/Logout"; +import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver"; +import LoginOneFactor from "../../../helpers/behaviors/LoginOneFactor"; +import VerifySecretObserved from "../../../helpers/assertions/VerifySecretObserved"; +import VisitPage from "../../../helpers/VisitPage"; +import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs"; +import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; + +export default function() { + beforeEach(async function() { + this.driver = await StartDriver(); + }); + + afterEach(async function() { + await Logout(this.driver); + await StopDriver(this.driver); + }); + + it("should redirect user after first stage", async function() { + await LoginOneFactor(this.driver, "john", "password", "https://single_factor.example.com:8080/secret.html"); + await VerifySecretObserved(this.driver); + }); + + it("should redirect to portal if not enough authorized", async function() { + await LoginOneFactor(this.driver, "john", "password", "https://single_factor.example.com:8080/secret.html"); + await VisitPage(this.driver, "https://admin.example.com:8080/secret.html"); + + // the url should be the one from the portal. + await VerifyUrlIs(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); + + // And the user should end up on the second factor page. + await VerifyIsSecondFactorStage(this.driver); + }) +} \ No newline at end of file diff --git a/test/suites/minimal/scenarii/BadPassword.ts b/test/suites/minimal/scenarii/BadPassword.ts index 0186cad1a..47be13f4e 100644 --- a/test/suites/minimal/scenarii/BadPassword.ts +++ b/test/suites/minimal/scenarii/BadPassword.ts @@ -1,7 +1,7 @@ import FillLoginPageWithUserAndPasswordAndClick from '../../../helpers/FillLoginPageAndClick'; -import VisitPage from '../../../helpers/VisitPage'; import SeeNotification from '../../../helpers/SeeNotification'; import {AUTHENTICATION_FAILED} from '../../../../shared/UserMessages'; +import VisitPageAndWaitUrlIs from '../../../helpers/behaviors/VisitPageAndWaitUrlIs'; export default function() { /** @@ -11,7 +11,7 @@ export default function() { describe('failed login as john in first factor', function() { beforeEach(async function() { this.timeout(10000); - await VisitPage(this.driver, "https://login.example.com:8080/") + await VisitPageAndWaitUrlIs(this.driver, "https://login.example.com:8080/") await FillLoginPageWithUserAndPasswordAndClick(this.driver, 'john', 'bad_password'); }); diff --git a/test/suites/minimal/scenarii/Inactivity.ts b/test/suites/minimal/scenarii/Inactivity.ts index 9f4726480..5d3bdb123 100644 --- a/test/suites/minimal/scenarii/Inactivity.ts +++ b/test/suites/minimal/scenarii/Inactivity.ts @@ -1,9 +1,9 @@ import LoginAndRegisterTotp from "../../../helpers/LoginAndRegisterTotp"; -import VisitPage from "../../../helpers/VisitPage"; import FillLoginPageWithUserAndPasswordAndClick from "../../../helpers/FillLoginPageAndClick"; import ValidateTotp from "../../../helpers/ValidateTotp"; import WaitRedirected from "../../../helpers/WaitRedirected"; import { WebDriver } from "selenium-webdriver"; +import VisitPageAndWaitUrlIs from "../../../helpers/behaviors/VisitPageAndWaitUrlIs"; export default function(this: Mocha.ISuiteCallbackContext) { this.timeout(20000); @@ -14,11 +14,11 @@ export default function(this: Mocha.ISuiteCallbackContext) { it("should disconnect user after inactivity period", async function() { const driver = this.driver as WebDriver; - await VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); + await VisitPageAndWaitUrlIs(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); await FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'password', false); await ValidateTotp(driver, this.secret); await WaitRedirected(driver, "https://admin.example.com:8080/secret.html"); - await VisitPage(driver, "https://home.example.com:8080/"); + await VisitPageAndWaitUrlIs(driver, "https://home.example.com:8080/"); await driver.sleep(6000); await driver.get("https://admin.example.com:8080/secret.html"); await WaitRedirected(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); @@ -26,11 +26,11 @@ export default function(this: Mocha.ISuiteCallbackContext) { it('should disconnect user after cookie expiration', async function() { const driver = this.driver as WebDriver; - await VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); + await VisitPageAndWaitUrlIs(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); await FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'password', false); await ValidateTotp(driver, this.secret); await WaitRedirected(driver, "https://admin.example.com:8080/secret.html"); - await VisitPage(driver, "https://home.example.com:8080/"); + await VisitPageAndWaitUrlIs(driver, "https://home.example.com:8080/"); await driver.sleep(4000); await driver.get("https://admin.example.com:8080/secret.html"); @@ -46,11 +46,11 @@ export default function(this: Mocha.ISuiteCallbackContext) { describe('With remember me checkbox checked', function() { it("should keep user logged in after inactivity period", async function() { const driver = this.driver as WebDriver; - await VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); + await VisitPageAndWaitUrlIs(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); await FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'password', true); await ValidateTotp(driver, this.secret); await WaitRedirected(driver, "https://admin.example.com:8080/secret.html"); - await VisitPage(driver, "https://home.example.com:8080/"); + await VisitPageAndWaitUrlIs(driver, "https://home.example.com:8080/"); await driver.sleep(6000); await driver.get("https://admin.example.com:8080/secret.html"); await WaitRedirected(driver, "https://admin.example.com:8080/secret.html"); diff --git a/test/suites/minimal/scenarii/ResetPassword.ts b/test/suites/minimal/scenarii/ResetPassword.ts index 7e21ce752..d83054fd4 100644 --- a/test/suites/minimal/scenarii/ResetPassword.ts +++ b/test/suites/minimal/scenarii/ResetPassword.ts @@ -1,6 +1,5 @@ import SeleniumWebDriver from 'selenium-webdriver'; -import VisitPage from '../../../helpers/VisitPage'; import ClickOnLink from '../../../helpers/ClickOnLink'; import ClickOn from '../../../helpers/ClickOn'; import WaitRedirected from '../../../helpers/WaitRedirected'; @@ -9,10 +8,11 @@ import {GetLinkFromEmail} from "../../../helpers/GetIdentityLink"; import FillLoginPageAndClick from "../../../helpers/FillLoginPageAndClick"; import IsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage"; import SeeNotification from '../../../helpers/SeeNotification'; +import VisitPageAndWaitUrlIs from '../../../helpers/behaviors/VisitPageAndWaitUrlIs'; export default function() { it("should reset password for john", async function() { - await VisitPage(this.driver, "https://login.example.com:8080/"); + await VisitPageAndWaitUrlIs(this.driver, "https://login.example.com:8080/"); await ClickOnLink(this.driver, "Forgot password\?"); await WaitRedirected(this.driver, "https://login.example.com:8080/forgot-password"); await FillField(this.driver, "username", "john"); @@ -21,7 +21,7 @@ export default function() { await this.driver.sleep(500); // Simulate the time it takes to receive the e-mail. const link = await GetLinkFromEmail(); - await VisitPage(this.driver, link); + await VisitPageAndWaitUrlIs(this.driver, link); await FillField(this.driver, "password1", "newpass"); await FillField(this.driver, "password2", "newpass"); await ClickOn(this.driver, SeleniumWebDriver.By.id('reset-button')); @@ -33,7 +33,7 @@ export default function() { }); it("should persuade reset password is initiated for unknown user", async function() { - await VisitPage(this.driver, "https://login.example.com:8080/"); + await VisitPageAndWaitUrlIs(this.driver, "https://login.example.com:8080/"); await ClickOnLink(this.driver, "Forgot password\?"); await WaitRedirected(this.driver, "https://login.example.com:8080/forgot-password"); await FillField(this.driver, "username", "unknown"); @@ -44,7 +44,7 @@ export default function() { }); it("should notify passwords are different in reset form", async function() { - await VisitPage(this.driver, "https://login.example.com:8080/"); + await VisitPageAndWaitUrlIs(this.driver, "https://login.example.com:8080/"); await ClickOnLink(this.driver, "Forgot password\?"); await WaitRedirected(this.driver, "https://login.example.com:8080/forgot-password"); await FillField(this.driver, "username", "john"); @@ -53,7 +53,7 @@ export default function() { await this.driver.sleep(500); // Simulate the time it takes to receive the e-mail. const link = await GetLinkFromEmail(); - await VisitPage(this.driver, link); + await VisitPageAndWaitUrlIs(this.driver, link); await FillField(this.driver, "password1", "newpass"); await FillField(this.driver, "password2", "badpass"); await ClickOn(this.driver, SeleniumWebDriver.By.id('reset-button')); diff --git a/test/suites/minimal/scenarii/TOTPValidation.ts b/test/suites/minimal/scenarii/TOTPValidation.ts index 8bc69c976..371da0465 100644 --- a/test/suites/minimal/scenarii/TOTPValidation.ts +++ b/test/suites/minimal/scenarii/TOTPValidation.ts @@ -1,11 +1,11 @@ import FillLoginPageWithUserAndPasswordAndClick from '../../../helpers/FillLoginPageAndClick'; import WaitRedirected from '../../../helpers/WaitRedirected'; -import VisitPage from '../../../helpers/VisitPage'; import ValidateTotp from '../../../helpers/ValidateTotp'; import VerifySecretObserved from "../../../helpers/assertions/VerifySecretObserved"; import LoginAndRegisterTotp from '../../../helpers/LoginAndRegisterTotp'; import SeeNotification from '../../../helpers/SeeNotification'; import { AUTHENTICATION_TOTP_FAILED } from '../../../../shared/UserMessages'; +import VisitPageAndWaitUrlIs from '../../../helpers/behaviors/VisitPageAndWaitUrlIs'; export default function() { /** @@ -18,7 +18,7 @@ export default function() { const secret = await LoginAndRegisterTotp(this.driver, "john", true); if (!secret) throw new Error('No secret!'); - await VisitPage(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); + await VisitPageAndWaitUrlIs(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); await FillLoginPageWithUserAndPasswordAndClick(this.driver, 'john', 'password'); await ValidateTotp(this.driver, secret); await WaitRedirected(this.driver, "https://admin.example.com:8080/secret.html"); @@ -39,7 +39,7 @@ export default function() { await LoginAndRegisterTotp(this.driver, "john", true); const BAD_TOKEN = "125478"; - await VisitPage(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); + await VisitPageAndWaitUrlIs(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"); await FillLoginPageWithUserAndPasswordAndClick(this.driver, 'john', 'password'); await ValidateTotp(this.driver, BAD_TOKEN); });