Finish migrating integration tests to mocha.
parent
85d3adc3e3
commit
50d4ab1368
|
@ -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"
|
|
|
@ -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/"
|
|
|
@ -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/"
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
|
@ -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.")));
|
|
||||||
});
|
|
||||||
})
|
|
|
@ -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<any, any>(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<void> {
|
|
||||||
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<void> {
|
|
||||||
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<void> {
|
|
||||||
return exec("\
|
|
||||||
cat config.template.yml | \
|
|
||||||
sed 's/default_method: two_factor/default_method: single_factor/' > config.test.yml \
|
|
||||||
");
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCustomTotpIssuerConfiguration(): BluebirdPromise<void> {
|
|
||||||
return exec("\
|
|
||||||
cat config.template.yml | \
|
|
||||||
sed 's/issuer: authelia.com/issuer: custom.com/' > config.test.yml \
|
|
||||||
");
|
|
||||||
}
|
|
||||||
|
|
||||||
function declareNeedsConfiguration(tag: string, cb: () => BluebirdPromise<void>) {
|
|
||||||
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<void> {
|
|
||||||
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);
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
|
@ -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));
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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");
|
|
||||||
});
|
|
|
@ -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<void>[] = [];
|
|
||||||
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
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
|
@ -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.")));
|
|
||||||
});
|
|
|
@ -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(/<a href="(.+)" class="button">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);
|
|
|
@ -1,6 +1,5 @@
|
||||||
import FillLoginPageAndClick from "./FillLoginPageAndClick";
|
import FillLoginPageAndClick from "./FillLoginPageAndClick";
|
||||||
import ValidateTotp from "./ValidateTotp";
|
import ValidateTotp from "./ValidateTotp";
|
||||||
import VerifyUrlIs from "./assertions/VerifyUrlIs";
|
|
||||||
import { WebDriver } from "selenium-webdriver";
|
import { WebDriver } from "selenium-webdriver";
|
||||||
import VisitPageAndWaitUrlIs from "./behaviors/VisitPageAndWaitUrlIs";
|
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 VisitPageAndWaitUrlIs(driver, `https://login.example.com:8080/?rd=${url}`);
|
||||||
await FillLoginPageAndClick(driver, user, 'password');
|
await FillLoginPageAndClick(driver, user, 'password');
|
||||||
await ValidateTotp(driver, secret);
|
await ValidateTotp(driver, secret);
|
||||||
await VerifyUrlIs(driver, url);
|
|
||||||
}
|
}
|
|
@ -3,8 +3,8 @@ import LoginAs from './LoginAs';
|
||||||
import { WebDriver } from 'selenium-webdriver';
|
import { WebDriver } from 'selenium-webdriver';
|
||||||
import VerifyIsSecondFactorStage from './assertions/VerifyIsSecondFactorStage';
|
import VerifyIsSecondFactorStage from './assertions/VerifyIsSecondFactorStage';
|
||||||
|
|
||||||
export default async function(driver: WebDriver, user: string, email: boolean = false) {
|
export default async function(driver: WebDriver, user: string, password: string, email: boolean = false) {
|
||||||
await LoginAs(driver, user);
|
await LoginAs(driver, user, password);
|
||||||
await VerifyIsSecondFactorStage(driver);
|
await VerifyIsSecondFactorStage(driver);
|
||||||
return await RegisterTotp(driver, email);
|
return await RegisterTotp(driver, email);
|
||||||
}
|
}
|
|
@ -2,7 +2,8 @@ import FillLoginPageAndClick from './FillLoginPageAndClick';
|
||||||
import { WebDriver } from "selenium-webdriver";
|
import { WebDriver } from "selenium-webdriver";
|
||||||
import VisitPageAndWaitUrlIs from "./behaviors/VisitPageAndWaitUrlIs";
|
import VisitPageAndWaitUrlIs from "./behaviors/VisitPageAndWaitUrlIs";
|
||||||
|
|
||||||
export default async function(driver: WebDriver, user: string, password: string = "password") {
|
export default async function(driver: WebDriver, user: string, password: string, targetUrl?: string) {
|
||||||
await VisitPageAndWaitUrlIs(driver, "https://login.example.com:8080/");
|
const urlExt = (targetUrl) ? ('rd=' + targetUrl) : '';
|
||||||
|
await VisitPageAndWaitUrlIs(driver, "https://login.example.com:8080/" + urlExt);
|
||||||
await FillLoginPageAndClick(driver, user, password);
|
await FillLoginPageAndClick(driver, user, password);
|
||||||
}
|
}
|
|
@ -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) {
|
export default async function(driver: WebDriver, url: string, timeout: number = 5000) {
|
||||||
await driver.get(url);
|
await driver.get(url);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import SeleniumWebDriver, { WebDriver } from "selenium-webdriver";
|
import SeleniumWebDriver, { WebDriver } from "selenium-webdriver";
|
||||||
import Util from "util";
|
import Util from "util";
|
||||||
|
|
||||||
export default async function(driver: WebDriver, header: string, expectedValue: string) {
|
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")), 5000);
|
const el = await driver.wait(SeleniumWebDriver.until.elementLocated(
|
||||||
|
SeleniumWebDriver.By.tagName("body")), timeout);
|
||||||
const text = await el.getText();
|
const text = await el.getText();
|
||||||
|
|
||||||
const expectedLine = Util.format("\"%s\": \"%s\"", header, expectedValue);
|
const expectedLine = Util.format("\"%s\": \"%s\"", header, expectedValue);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import SeleniumWebDriver, { WebDriver } from "selenium-webdriver";
|
import SeleniumWebDriver, { WebDriver } from "selenium-webdriver";
|
||||||
|
|
||||||
export default async function(driver: WebDriver) {
|
export default async function(driver: WebDriver, timeout: number = 5000) {
|
||||||
await driver.wait(SeleniumWebDriver.until.elementLocated(SeleniumWebDriver.By.className('already-authenticated-step')));
|
await driver.wait(SeleniumWebDriver.until.elementLocated(
|
||||||
|
SeleniumWebDriver.By.className('already-authenticated-step')), timeout);
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
import SeleniumWebDriver, { WebDriver } from "selenium-webdriver";
|
import SeleniumWebDriver, { WebDriver } from "selenium-webdriver";
|
||||||
|
|
||||||
export default async function(driver: WebDriver) {
|
export default async function(driver: WebDriver, timeout: number = 5000) {
|
||||||
await driver.wait(SeleniumWebDriver.until.elementLocated(SeleniumWebDriver.By.className('second-factor-step')));
|
await driver.wait(SeleniumWebDriver.until.elementLocated(
|
||||||
|
SeleniumWebDriver.By.className('second-factor-step')), timeout);
|
||||||
}
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
|
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
|
||||||
import Assert = require("assert");
|
import Assert = require("assert");
|
||||||
|
|
||||||
export default async function(driver: WebDriver, message: string) {
|
export default async function(driver: WebDriver, message: string, timeout: number = 5000) {
|
||||||
await driver.wait(SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.className("notification")), 5000)
|
await driver.wait(SeleniumWebdriver.until.elementLocated(
|
||||||
|
SeleniumWebdriver.By.className("notification")), timeout)
|
||||||
const notificationEl = driver.findElement(SeleniumWebdriver.By.className("notification"));
|
const notificationEl = driver.findElement(SeleniumWebdriver.By.className("notification"));
|
||||||
const txt = await notificationEl.getText();
|
const txt = await notificationEl.getText();
|
||||||
Assert.equal(message, txt);
|
Assert.equal(message, txt);
|
||||||
|
|
|
@ -5,9 +5,11 @@ import FullLogin from "../FullLogin";
|
||||||
export default async function(
|
export default async function(
|
||||||
driver: WebDriver,
|
driver: WebDriver,
|
||||||
username: string,
|
username: string,
|
||||||
|
password: string,
|
||||||
email: boolean = false,
|
email: boolean = false,
|
||||||
targetUrl: string = "https://login.example.com:8080/") {
|
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);
|
await FullLogin(driver, username, secret, targetUrl);
|
||||||
|
return secret;
|
||||||
};
|
};
|
|
@ -34,7 +34,7 @@ export default function() {
|
||||||
WithDriver();
|
WithDriver();
|
||||||
|
|
||||||
before(async function() {
|
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 VisitPageAndWaitUrlIs(this.driver, 'https://login.example.com:8080/');
|
||||||
await FillLoginPageAndClick(this.driver, 'john', 'password', false);
|
await FillLoginPageAndClick(this.driver, 'john', 'password', false);
|
||||||
await ValidateTotp(this.driver, secret);
|
await ValidateTotp(this.driver, secret);
|
||||||
|
@ -61,7 +61,7 @@ export default function() {
|
||||||
WithDriver();
|
WithDriver();
|
||||||
|
|
||||||
before(async function() {
|
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 VisitPageAndWaitUrlIs(this.driver, 'https://login.example.com:8080/');
|
||||||
await FillLoginPageAndClick(this.driver, 'bob', 'password', false);
|
await FillLoginPageAndClick(this.driver, 'bob', 'password', false);
|
||||||
await ValidateTotp(this.driver, secret);
|
await ValidateTotp(this.driver, secret);
|
||||||
|
@ -88,7 +88,7 @@ export default function() {
|
||||||
WithDriver();
|
WithDriver();
|
||||||
|
|
||||||
before(async function() {
|
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 VisitPageAndWaitUrlIs(this.driver, 'https://login.example.com:8080/');
|
||||||
await FillLoginPageAndClick(this.driver, 'harry', 'password', false);
|
await FillLoginPageAndClick(this.driver, 'harry', 'password', false);
|
||||||
await ValidateTotp(this.driver, secret);
|
await ValidateTotp(this.driver, secret);
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default function() {
|
||||||
ChildProcess.execSync('rm -f .authelia-interrupt');
|
ChildProcess.execSync('rm -f .authelia-interrupt');
|
||||||
|
|
||||||
this.driver = await StartDriver();
|
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/');
|
await VisitPageAndWaitUrlIs(this.driver, 'https://home.example.com:8080/');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export default function() {
|
||||||
ChildProcess.execSync('rm -f .authelia-interrupt');
|
ChildProcess.execSync('rm -f .authelia-interrupt');
|
||||||
|
|
||||||
this.driver = await StartDriver();
|
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);
|
await Logout(this.driver);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,31 +3,14 @@ import LoginAs from "../../../helpers/LoginAs";
|
||||||
import VerifyNotificationDisplayed from "../../../helpers/assertions/VerifyNotificationDisplayed";
|
import VerifyNotificationDisplayed from "../../../helpers/assertions/VerifyNotificationDisplayed";
|
||||||
import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage";
|
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() {
|
export default function() {
|
||||||
describe('Authelia regulates authentications when a hacker is brute forcing', function() {
|
describe('Authelia regulates authentications when a hacker is brute forcing', function() {
|
||||||
this.timeout(15000);
|
this.timeout(15000);
|
||||||
before(async function() {
|
beforeEach(async function() {
|
||||||
this.driver = await StartDriver();
|
this.driver = await StartDriver();
|
||||||
});
|
});
|
||||||
|
|
||||||
after(async function() {
|
afterEach(async function() {
|
||||||
await StopDriver(this.driver);
|
await StopDriver(this.driver);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default function() {
|
||||||
describe("With two factors", function() {
|
describe("With two factors", function() {
|
||||||
before(async function() {
|
before(async function() {
|
||||||
this.driver = await StartDriver();
|
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() {
|
after(async function() {
|
||||||
|
|
|
@ -21,7 +21,7 @@ export default function() {
|
||||||
|
|
||||||
beforeEach(async function() {
|
beforeEach(async function() {
|
||||||
this.driver = await StartDriver();
|
this.driver = await StartDriver();
|
||||||
secret = await LoginAndRegisterTotp(this.driver, "john", true)
|
secret = await LoginAndRegisterTotp(this.driver, "john", "password", true)
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async function() {
|
afterEach(async function() {
|
||||||
|
|
|
@ -9,6 +9,9 @@ import TOTPValidation from './scenarii/TOTPValidation';
|
||||||
import Inactivity from './scenarii/Inactivity';
|
import Inactivity from './scenarii/Inactivity';
|
||||||
import BackendProtection from './scenarii/BackendProtection';
|
import BackendProtection from './scenarii/BackendProtection';
|
||||||
import VerifyEndpoint from './scenarii/VerifyEndpoint';
|
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);
|
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");
|
return execAsync("cp users_database.example.yml users_database.yml");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Simple authentication', SimpleAuthentication);
|
||||||
describe('Backend protection', BackendProtection);
|
describe('Backend protection', BackendProtection);
|
||||||
describe('Verify API endpoint', VerifyEndpoint);
|
describe('Verify API endpoint', VerifyEndpoint);
|
||||||
|
|
||||||
describe('Bad password', BadPassword);
|
describe('Bad password', BadPassword);
|
||||||
describe('Reset password', ResetPassword);
|
describe('Reset password', ResetPassword);
|
||||||
|
|
||||||
describe('TOTP Registration', RegisterTotp);
|
describe('TOTP Registration', RegisterTotp);
|
||||||
describe('TOTP Validation', TOTPValidation);
|
describe('TOTP Validation', TOTPValidation);
|
||||||
|
|
||||||
describe('Inactivity period', Inactivity);
|
describe('Inactivity period', Inactivity);
|
||||||
|
describe('Required two factor', RequiredTwoFactor);
|
||||||
|
describe('Logout endpoint redirect to already logged in page', LogoutRedirectToAlreadyLoggedIn);
|
||||||
});
|
});
|
|
@ -12,7 +12,7 @@ export default function(this: Mocha.ISuiteCallbackContext) {
|
||||||
|
|
||||||
beforeEach(async function() {
|
beforeEach(async function() {
|
||||||
this.driver = await StartDriver();
|
this.driver = await StartDriver();
|
||||||
this.secret = await LoginAndRegisterTotp(this.driver, "john", true);
|
this.secret = await LoginAndRegisterTotp(this.driver, "john", "password", true);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async function() {
|
afterEach(async function() {
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ export default function() {
|
||||||
|
|
||||||
beforeEach(async function() {
|
beforeEach(async function() {
|
||||||
this.driver = await StartDriver();
|
this.driver = await StartDriver();
|
||||||
await LoginAndRegisterTotp(this.driver, "john", true);
|
await LoginAndRegisterTotp(this.driver, "john", "password", true);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async function() {
|
afterEach(async function() {
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -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');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ export default function() {
|
||||||
describe('Successfully pass second factor with TOTP', function() {
|
describe('Successfully pass second factor with TOTP', function() {
|
||||||
before(async function() {
|
before(async function() {
|
||||||
this.driver = await StartDriver();
|
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!');
|
if (!secret) throw new Error('No secret!');
|
||||||
|
|
||||||
await VisitPageAndWaitUrlIs(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");
|
||||||
|
@ -46,7 +46,7 @@ export default function() {
|
||||||
describe('Fail validation of second factor with TOTP', function() {
|
describe('Fail validation of second factor with TOTP', function() {
|
||||||
before(async function() {
|
before(async function() {
|
||||||
this.driver = await StartDriver();
|
this.driver = await StartDriver();
|
||||||
await LoginAndRegisterTotp(this.driver, "john", true);
|
await LoginAndRegisterTotp(this.driver, "john", "password", true);
|
||||||
const BAD_TOKEN = "125478";
|
const BAD_TOKEN = "125478";
|
||||||
|
|
||||||
await VisitPageAndWaitUrlIs(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");
|
||||||
|
|
Loading…
Reference in New Issue