Fix inactivity Ãe2e tests.

This commit is contained in:
Clement Michaud 2019-01-30 23:33:14 +01:00
parent 61946929d2
commit d3a790627e
12 changed files with 165 additions and 96 deletions

View File

@ -16,7 +16,7 @@ export interface StateProps {
} }
export interface DispatchProps { export interface DispatchProps {
onAuthenticationRequested(username: string, password: string): void; onAuthenticationRequested(username: string, password: string, rememberMe: boolean): void;
} }
export type Props = StateProps & DispatchProps; export type Props = StateProps & DispatchProps;
@ -131,7 +131,8 @@ class FirstFactorForm extends Component<Props, State> {
private authenticate() { private authenticate() {
this.props.onAuthenticationRequested( this.props.onAuthenticationRequested(
this.state.username, this.state.username,
this.state.password); this.state.password,
this.state.rememberMe);
} }
} }

View File

@ -15,12 +15,12 @@ const mapStateToProps = (state: RootState): StateProps => {
} }
function onAuthenticationRequested(dispatch: Dispatch) { function onAuthenticationRequested(dispatch: Dispatch) {
return async (username: string, password: string) => { return async (username: string, password: string, rememberMe: boolean) => {
let err, res; let err, res;
// Validate first factor // Validate first factor
dispatch(authenticate()); dispatch(authenticate());
[err, res] = await to(AutheliaService.postFirstFactorAuth(username, password)); [err, res] = await to(AutheliaService.postFirstFactorAuth(username, password, rememberMe));
if (err) { if (err) {
await dispatch(authenticateFailure(err.message)); await dispatch(authenticateFailure(err.message));

View File

@ -22,7 +22,8 @@ export async function fetchState() {
}); });
} }
export async function postFirstFactorAuth(username: string, password: string) { export async function postFirstFactorAuth(username: string, password: string,
rememberMe: boolean) {
return fetchSafe('/api/firstfactor', { return fetchSafe('/api/firstfactor', {
method: 'POST', method: 'POST',
headers: { headers: {
@ -32,6 +33,7 @@ export async function postFirstFactorAuth(username: string, password: string) {
body: JSON.stringify({ body: JSON.stringify({
username: username, username: username,
password: password, password: password,
keepMeLoggedIn: rememberMe,
}) })
}); });
} }

View File

@ -3,6 +3,7 @@
var program = require('commander'); var program = require('commander');
var execSync = require('child_process').execSync; var execSync = require('child_process').execSync;
var spawn = require('child_process').spawn; var spawn = require('child_process').spawn;
var fs = require('fs');
program program
.option('-c, --config <config>', 'Configuration file to run Authelia with.') .option('-c, --config <config>', 'Configuration file to run Authelia with.')
@ -30,10 +31,14 @@ else {
server = spawn('/usr/bin/env', ['node', 'dist/server/src/index.js', config]); server = spawn('/usr/bin/env', ['node', 'dist/server/src/index.js', config]);
} }
var logStream = fs.createWriteStream('/tmp/authelia-server.log', {flags: 'a'});
server.stdout.on('data', (data) => { server.stdout.on('data', (data) => {
process.stdout.write(`${data}`); process.stdout.write(`${data}`);
}); });
server.stdout.pipe(logStream);
server.stderr.on('data', (data) => { server.stderr.on('data', (data) => {
process.stderr.write(`${data}`); process.stderr.write(`${data}`);
}); });
server.stderr.pipe(logStream);

View File

@ -15,8 +15,7 @@ export default function (vars: ServerVariables) {
: BluebirdPromise<void> { : BluebirdPromise<void> {
const username: string = req.body.username; const username: string = req.body.username;
const password: string = req.body.password; const password: string = req.body.password;
const keepMeLoggedIn: boolean = req.body.keepMeLoggedIn && const keepMeLoggedIn: boolean = req.body.keepMeLoggedIn;
req.body.keepMeLoggedIn === "true";
let authSession: AuthenticationSession; let authSession: AuthenticationSession;
if (keepMeLoggedIn) { if (keepMeLoggedIn) {

View File

@ -10,8 +10,7 @@ export default async function(
await driver.findElement(SeleniumWebdriver.By.id("username")).sendKeys(username); await driver.findElement(SeleniumWebdriver.By.id("username")).sendKeys(username);
await driver.findElement(SeleniumWebdriver.By.id("password")).sendKeys(password); await driver.findElement(SeleniumWebdriver.By.id("password")).sendKeys(password);
if (keepMeLoggedIn) { if (keepMeLoggedIn) {
await driver.findElement(SeleniumWebdriver.By.id("keep_me_logged_in")).click(); await driver.findElement(SeleniumWebdriver.By.id("remember-checkbox")).click();
return;
} }
await driver.findElement(SeleniumWebdriver.By.tagName("button")).click(); await driver.findElement(SeleniumWebdriver.By.tagName("button")).click();
}; };

View File

@ -8,7 +8,7 @@ export default function() {
beforeEach(function() { beforeEach(function() {
const driver = new SeleniumWebdriver.Builder() const driver = new SeleniumWebdriver.Builder()
.forBrowser("chrome") .forBrowser("chrome")
.setChromeOptions(new chrome.Options().headless()) // .setChromeOptions(new chrome.Options().headless())
.build(); .build();
this.driver = driver; this.driver = driver;
}); });

View File

@ -1,37 +0,0 @@
require("chromedriver");
import Bluebird = require("bluebird");
import Configuration = require("../configuration");
import Environment = require("../environment");
import ChildProcess = require('child_process');
const execAsync = Bluebird.promisify(ChildProcess.exec);
const includes = [
"docker-compose.test.yml",
"example/compose/docker-compose.base.yml",
"example/compose/nginx/minimal/docker-compose.yml",
"example/compose/smtp/docker-compose.yml",
]
before(function() {
this.timeout(20000);
this.environment = new Environment.Environment(includes);
this.configuration = new Configuration.Configuration();
return this.configuration.setup(
"config.minimal.yml",
"config.test.yml",
conf => {
conf.session.inactivity = 2000;
})
.then(() => execAsync("cp users_database.yml users_database.test.yml"))
.then(() => this.environment.setup(2000));
});
after(function() {
this.timeout(30000);
return this.configuration.cleanup()
.then(() => execAsync("rm users_database.test.yml"))
.then(() => this.environment.cleanup());
});

View File

@ -1,48 +0,0 @@
import Bluebird = require("bluebird");
import LoginAndRegisterTotp from "../helpers/LoginAndRegisterTotp";
import VisitPage from "../helpers/VisitPage";
import FillLoginPageWithUserAndPasswordAndClick from "../helpers/FillLoginPageAndClick";
import WithDriver from "../helpers/context/WithDriver";
import ValidateTotp from "../helpers/ValidateTotp";
import WaitRedirected from "../helpers/WaitRedirected";
describe("Keep me logged in", function() {
this.timeout(15000);
WithDriver();
before(function() {
const that = this;
return LoginAndRegisterTotp(this.driver, "john", true)
.then(function(secret: string) {
that.secret = secret;
if(!secret) return Bluebird.reject(new Error("No secret!"));
return Bluebird.resolve();
});
});
it("should disconnect user after inactivity period", function() {
const that = this;
const driver = this.driver;
return VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html")
.then(() => FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'password', false))
.then(() => ValidateTotp(driver, that.secret))
.then(() => WaitRedirected(driver, "https://admin.example.com:8080/secret.html"))
.then(() => VisitPage(driver, "https://home.example.com:8080/"))
.then(() => driver.sleep(3000))
.then(() => driver.get("https://admin.example.com:8080/secret.html"))
.then(() => WaitRedirected(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"))
});
it.only("should keep user logged in after inactivity period", function() {
const that = this;
const driver = this.driver;
return VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html")
.then(() => FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'password', true))
.then(() => ValidateTotp(driver, that.secret))
.then(() => WaitRedirected(driver, "https://admin.example.com:8080/secret.html"))
.then(() => VisitPage(driver, "https://home.example.com:8080/"))
.then(() => driver.sleep(5000))
.then(() => driver.get("https://admin.example.com:8080/secret.html"))
.then(() => WaitRedirected(driver, "https://admin.example.com:8080/secret.html"))
});
});

View File

@ -0,0 +1,107 @@
###############################################################
# Authelia minimal configuration #
###############################################################
port: 9091
logs_level: debug
authentication_backend:
file:
path: ./users_database.yml
session:
secret: unsecure_session_secret
domain: example.com
inactivity: 5000
# Configuration of the storage backend used to store data and secrets. i.e. totp data
storage:
local:
path: /var/lib/authelia
# TOTP Issuer Name
#
# This will be the issuer name displayed in Google Authenticator
# See: https://github.com/google/google-authenticator/wiki/Key-Uri-Format for more info on issuer names
totp:
issuer: example.com
# Access Control
#
# Access control is a set of rules you can use to restrict user access to certain
# resources.
access_control:
# Default policy can either be `bypass`, `one_factor`, `two_factor` or `deny`.
default_policy: deny
rules:
- domain: single_factor.example.com
policy: one_factor
- domain: '*.example.com'
subject: "group:admins"
policy: two_factor
- domain: dev.example.com
resources:
- '^/users/john/.*$'
subject: "user:john"
policy: two_factor
- domain: dev.example.com
resources:
- '^/users/harry/.*$'
subject: "user:harry"
policy: two_factor
- domain: '*.mail.example.com'
subject: "user:bob"
policy: two_factor
- domain: dev.example.com
resources:
- '^/users/bob/.*$'
subject: "user:bob"
policy: two_factor
# Configuration of the authentication regulation mechanism.
regulation:
# Set it to 0 to disable max_retries.
max_retries: 3
# The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window.
find_time: 120
# The length of time before a banned user can login again.
ban_time: 300
# Default redirection URL
#
# Note: this parameter is optional. If not provided, user won't
# be redirected upon successful authentication.
#default_redirection_url: https://authelia.example.domain
notifier:
# For testing purpose, notifications can be sent in a file
# filesystem:
# filename: /tmp/authelia/notification.txt
# Use your email account to send the notifications. You can use an app password.
# List of valid services can be found here: https://nodemailer.com/smtp/well-known/
## email:
## username: user@example.com
## password: yourpassword
## sender: admin@example.com
## service: gmail
# Use a SMTP server for sending notifications
smtp:
username: test
password: password
secure: false
host: 127.0.0.1
port: 1025
sender: admin@example.com

View File

@ -6,10 +6,11 @@ import BadPassword from "./scenarii/BadPassword";
import RegisterTotp from './scenarii/RegisterTotp'; import RegisterTotp from './scenarii/RegisterTotp';
import ResetPassword from './scenarii/ResetPassword'; import ResetPassword from './scenarii/ResetPassword';
import TOTPValidation from './scenarii/TOTPValidation'; import TOTPValidation from './scenarii/TOTPValidation';
import Inactivity from './scenarii/Inactivity';
const execAsync = Bluebird.promisify(ChildProcess.exec); const execAsync = Bluebird.promisify(ChildProcess.exec);
AutheliaSuite('Minimal configuration', 'config.minimal.yml', function() { AutheliaSuite('Minimal configuration', __dirname + '/config.yml', function() {
this.timeout(10000); this.timeout(10000);
beforeEach(function() { beforeEach(function() {
return execAsync("cp users_database.example.yml users_database.yml"); return execAsync("cp users_database.example.yml users_database.yml");
@ -20,4 +21,6 @@ AutheliaSuite('Minimal configuration', 'config.minimal.yml', function() {
describe('TOTP Registration', RegisterTotp); describe('TOTP Registration', RegisterTotp);
describe('TOTP Validation', TOTPValidation); describe('TOTP Validation', TOTPValidation);
describe('Inactivity period', Inactivity);
}); });

View File

@ -0,0 +1,38 @@
import Bluebird = require("bluebird");
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";
export default function(this: Mocha.ISuiteCallbackContext) {
this.timeout(15000);
beforeEach(async function() {
this.secret = await LoginAndRegisterTotp(this.driver, "john", true);
});
it("should disconnect user after inactivity period", async function() {
const driver = this.driver;
await VisitPage(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 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");
});
it("should keep user logged in after inactivity period", async function() {
const driver = this.driver;
await VisitPage(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 driver.sleep(6000);
await driver.get("https://admin.example.com:8080/secret.html");
await WaitRedirected(driver, "https://admin.example.com:8080/secret.html");
});
}