mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
Fix inactivity Ãe2e tests.
This commit is contained in:
parent
61946929d2
commit
d3a790627e
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
};
|
};
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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());
|
|
||||||
});
|
|
|
@ -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"))
|
|
||||||
});
|
|
||||||
});
|
|
107
test/suites/minimal-config/config.yml
Normal file
107
test/suites/minimal-config/config.yml
Normal 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
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
38
test/suites/minimal-config/scenarii/Inactivity.ts
Normal file
38
test/suites/minimal-config/scenarii/Inactivity.ts
Normal 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");
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user