mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
Migrate more Cucumber tests into Mocha.
This commit is contained in:
parent
efceb66ffa
commit
c579355c5b
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -36,3 +36,5 @@ example/ldap/private.ldif
|
|||
Configuration.schema.json
|
||||
|
||||
users_database.test.yml
|
||||
|
||||
.suite
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Dispatch } from "redux";
|
||||
import * as AutheliaService from '../services/AutheliaService';
|
||||
|
||||
export default async function(url: string, dispatch: Dispatch) {
|
||||
export default async function(url: string) {
|
||||
try {
|
||||
// Check the url against the backend before redirecting.
|
||||
await AutheliaService.checkRedirection(url);
|
||||
|
|
|
@ -10,6 +10,10 @@ import Notification from "../../components/Notification/Notification";
|
|||
|
||||
import styles from '../../assets/scss/components/FirstFactorForm/FirstFactorForm.module.scss';
|
||||
|
||||
export interface OwnProps {
|
||||
redirectionUrl: string | null;
|
||||
}
|
||||
|
||||
export interface StateProps {
|
||||
formDisabled: boolean;
|
||||
error: string | null;
|
||||
|
@ -19,7 +23,7 @@ export interface DispatchProps {
|
|||
onAuthenticationRequested(username: string, password: string, rememberMe: boolean): void;
|
||||
}
|
||||
|
||||
export type Props = StateProps & DispatchProps;
|
||||
export type Props = OwnProps & StateProps & DispatchProps;
|
||||
|
||||
interface State {
|
||||
username: string;
|
||||
|
|
|
@ -10,7 +10,7 @@ import Notification from '../Notification/Notification';
|
|||
|
||||
export interface OwnProps {
|
||||
username: string;
|
||||
redirection: string | null;
|
||||
redirectionUrl: string | null;
|
||||
}
|
||||
|
||||
export interface StateProps {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
import { authenticateFailure, authenticateSuccess, authenticate } from '../../../reducers/Portal/FirstFactor/actions';
|
||||
import FirstFactorForm, { StateProps } from '../../../components/FirstFactorForm/FirstFactorForm';
|
||||
import FirstFactorForm, { StateProps, OwnProps } from '../../../components/FirstFactorForm/FirstFactorForm';
|
||||
import { RootState } from '../../../reducers';
|
||||
import * as AutheliaService from '../../../services/AutheliaService';
|
||||
import to from 'await-to-js';
|
||||
import FetchStateBehavior from '../../../behaviors/FetchStateBehavior';
|
||||
import SafelyRedirectBehavior from '../../../behaviors/SafelyRedirectBehavior';
|
||||
|
||||
const mapStateToProps = (state: RootState): StateProps => {
|
||||
return {
|
||||
|
@ -14,13 +15,14 @@ const mapStateToProps = (state: RootState): StateProps => {
|
|||
};
|
||||
}
|
||||
|
||||
function onAuthenticationRequested(dispatch: Dispatch) {
|
||||
function onAuthenticationRequested(dispatch: Dispatch, redirectionUrl: string | null) {
|
||||
return async (username: string, password: string, rememberMe: boolean) => {
|
||||
let err, res;
|
||||
|
||||
// Validate first factor
|
||||
dispatch(authenticate());
|
||||
[err, res] = await to(AutheliaService.postFirstFactorAuth(username, password, rememberMe));
|
||||
[err, res] = await to(AutheliaService.postFirstFactorAuth(
|
||||
username, password, rememberMe, redirectionUrl));
|
||||
|
||||
if (err) {
|
||||
await dispatch(authenticateFailure(err.message));
|
||||
|
@ -32,24 +34,31 @@ function onAuthenticationRequested(dispatch: Dispatch) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (res.status !== 204) {
|
||||
if (res.status === 200) {
|
||||
const json = await res.json();
|
||||
if ('error' in json) {
|
||||
await dispatch(authenticateFailure(json['error']));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(authenticateSuccess());
|
||||
|
||||
// fetch state
|
||||
FetchStateBehavior(dispatch);
|
||||
if ('redirect' in json) {
|
||||
window.location.href = json['redirect'];
|
||||
return;
|
||||
}
|
||||
} else if (res.status === 204) {
|
||||
dispatch(authenticateSuccess());
|
||||
|
||||
// fetch state to move to next stage
|
||||
FetchStateBehavior(dispatch);
|
||||
} else {
|
||||
dispatch(authenticateFailure('Unknown error'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => {
|
||||
const mapDispatchToProps = (dispatch: Dispatch, ownProps: OwnProps) => {
|
||||
return {
|
||||
onAuthenticationRequested: onAuthenticationRequested(dispatch),
|
||||
onAuthenticationRequested: onAuthenticationRequested(dispatch, ownProps.redirectionUrl),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,9 +55,9 @@ async function triggerSecurityKeySigning(dispatch: Dispatch) {
|
|||
|
||||
async function handleSuccess(dispatch: Dispatch, ownProps: OwnProps, duration?: number) {
|
||||
async function handle() {
|
||||
if (ownProps.redirection) {
|
||||
if (ownProps.redirectionUrl) {
|
||||
try {
|
||||
await SafelyRedirectBehavior(ownProps.redirection, dispatch);
|
||||
await SafelyRedirectBehavior(ownProps.redirectionUrl);
|
||||
} catch (e) {
|
||||
await fetchState(dispatch);
|
||||
}
|
||||
|
|
|
@ -37,11 +37,9 @@ const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => {
|
|||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => {
|
||||
const mapDispatchToProps = (dispatch: Dispatch) => {
|
||||
return {
|
||||
onInit: async () => {
|
||||
await FetchStateBehavior(dispatch);
|
||||
}
|
||||
onInit: async () => await FetchStateBehavior(dispatch)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ function getReturnType<R> (f: (...args: any[]) => R): R {
|
|||
}
|
||||
|
||||
const t = getReturnType(PortalReducer)
|
||||
|
||||
export type RootState = StateType<typeof t>;
|
||||
|
||||
export default PortalReducer;
|
|
@ -23,13 +23,20 @@ export async function fetchState() {
|
|||
}
|
||||
|
||||
export async function postFirstFactorAuth(username: string, password: string,
|
||||
rememberMe: boolean) {
|
||||
rememberMe: boolean, redirectionUrl: string | null) {
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
if (redirectionUrl) {
|
||||
headers['X-Target-Url'] = redirectionUrl;
|
||||
}
|
||||
|
||||
return fetchSafe('/api/firstfactor', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
headers: headers,
|
||||
body: JSON.stringify({
|
||||
username: username,
|
||||
password: password,
|
||||
|
|
|
@ -36,12 +36,12 @@ class AuthenticationView extends Component<Props> {
|
|||
if (this.props.stage === Stage.SECOND_FACTOR) {
|
||||
return <SecondFactorForm
|
||||
username={this.props.remoteState.username}
|
||||
redirection={this.props.redirectionUrl} />;
|
||||
redirectionUrl={this.props.redirectionUrl} />;
|
||||
} else if (this.props.stage === Stage.ALREADY_AUTHENTICATED) {
|
||||
return <AlreadyAuthenticated
|
||||
username={this.props.remoteState.username}/>;
|
||||
}
|
||||
return <FirstFactorForm />;
|
||||
return <FirstFactorForm redirectionUrl={this.props.redirectionUrl} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var program = require('commander');
|
||||
var exec = require('child_process').execSync;
|
||||
var spawn = require('child_process').spawn;
|
||||
var chokidar = require('chokidar');
|
||||
var fs = require('fs');
|
||||
|
@ -101,15 +100,39 @@ function reload(path) {
|
|||
reloadServer();
|
||||
}
|
||||
|
||||
fs.writeFileSync(ENVIRONMENT_FILENAME, program.suite);
|
||||
exec('./example/compose/nginx/portal/render.js');
|
||||
exec('./scripts/utils/prepare-environment.sh');
|
||||
function exec(command, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const cmd = spawn(command, args);
|
||||
|
||||
console.log('Start watching');
|
||||
tsWatcher.on('add', reload);
|
||||
tsWatcher.on('remove', reload);
|
||||
tsWatcher.on('change', reload);
|
||||
cmd.stdout.pipe(process.stdout);
|
||||
cmd.stderr.pipe(process.stderr);
|
||||
cmd.on('close', (code) => {
|
||||
if (code == 0) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
reject(new Error('Status code ' + code));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
startServer();
|
||||
startClient();
|
||||
async function main() {
|
||||
console.log(`Create suite file ${ENVIRONMENT_FILENAME}.`);
|
||||
fs.writeFileSync(ENVIRONMENT_FILENAME, program.suite);
|
||||
|
||||
console.log(`Render nginx configuration...`);
|
||||
await exec('./example/compose/nginx/portal/render.js');
|
||||
|
||||
console.log(`Prepare environment with docker-compose...`);
|
||||
await exec('./scripts/utils/prepare-environment.sh');
|
||||
|
||||
console.log('Start watching...');
|
||||
tsWatcher.on('add', reload);
|
||||
tsWatcher.on('remove', reload);
|
||||
tsWatcher.on('change', reload);
|
||||
|
||||
startServer();
|
||||
startClient();
|
||||
}
|
||||
|
||||
main()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/schema/AclConfiguration";
|
||||
import { ACLConfiguration, ACLRule } from "../configuration/schema/AclConfiguration";
|
||||
import { IAuthorizer } from "./IAuthorizer";
|
||||
import { Winston } from "../../../types/Dependencies";
|
||||
import { MultipleDomainMatcher } from "./MultipleDomainMatcher";
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
import Exceptions = require("../../Exceptions");
|
||||
import * as ObjectPath from "object-path";
|
||||
import BluebirdPromise = require("bluebird");
|
||||
import express = require("express");
|
||||
import ErrorReplies = require("../../ErrorReplies");
|
||||
|
@ -9,6 +10,11 @@ import { ServerVariables } from "../../ServerVariables";
|
|||
import { AuthenticationSession } from "../../../../types/AuthenticationSession";
|
||||
import { GroupsAndEmails } from "../../authentication/backends/GroupsAndEmails";
|
||||
import { Level } from "../../authentication/Level";
|
||||
import { Level as AuthorizationLevel } from "../../authorization/Level";
|
||||
import { BelongToDomain } from "../../../../../shared/BelongToDomain";
|
||||
import { URLDecomposer } from "../..//utils/URLDecomposer";
|
||||
import { Object } from "../../../lib/authorization/Object";
|
||||
import { Subject } from "../../../lib/authorization/Subject";
|
||||
|
||||
export default function (vars: ServerVariables) {
|
||||
return function (req: express.Request, res: express.Response)
|
||||
|
@ -54,6 +60,37 @@ export default function (vars: ServerVariables) {
|
|||
|
||||
vars.logger.debug(req, "Mark successful authentication to regulator.");
|
||||
vars.regulator.mark(username, true);
|
||||
})
|
||||
.then(function() {
|
||||
const targetUrl = ObjectPath.get(req, 'headers.x-target-url', null);
|
||||
|
||||
if (!targetUrl) {
|
||||
res.status(204);
|
||||
res.send();
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
||||
|
||||
if (BelongToDomain(targetUrl, vars.config.session.domain)) {
|
||||
const resource = URLDecomposer.fromUrl(targetUrl);
|
||||
const resObject: Object = {
|
||||
domain: resource.domain,
|
||||
resource: resource.path,
|
||||
}
|
||||
|
||||
const subject: Subject = {
|
||||
user: authSession.userid,
|
||||
groups: authSession.groups
|
||||
}
|
||||
|
||||
const authorizationLevel = vars.authorizer.authorization(resObject, subject);
|
||||
if (authorizationLevel <= AuthorizationLevel.ONE_FACTOR) {
|
||||
res.json({
|
||||
redirect: targetUrl
|
||||
});
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
res.status(204);
|
||||
res.send();
|
||||
return BluebirdPromise.resolve();
|
||||
|
|
|
@ -56,8 +56,6 @@ export default function (req: Express.Request, res: Express.Response,
|
|||
|
||||
const originalUrl = ObjectPath.get<Express.Request, string>(
|
||||
req, "headers.x-original-url");
|
||||
const originalUri =
|
||||
ObjectPath.get<Express.Request, string>(req, "headers.x-original-uri");
|
||||
|
||||
const d = URLDecomposer.fromUrl(originalUrl);
|
||||
vars.logger.debug(req, "domain=%s, path=%s, user=%s, groups=%s", d.domain,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Express = require("express");
|
||||
import { DomainExtractor } from "../../../../shared/DomainExtractor";
|
||||
import { BelongToDomain } from "../../../../shared/BelongToDomain";
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// TODO: replace this decompose by third party library.
|
||||
export class URLDecomposer {
|
||||
static fromUrl(url: string): {domain: string, path: string} {
|
||||
if (!url) return;
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
Feature: Headers are correctly forwarded to backend
|
||||
@need-authenticated-user-john
|
||||
Scenario: Custom-Forwarded-User and Custom-Forwarded-Groups are correctly forwarded to protected backend
|
||||
When I visit "https://public.example.com:8080/headers"
|
||||
Then I see header "Custom-Forwarded-User" set to "john"
|
||||
Then I see header "Custom-Forwarded-Groups" set to "dev,admin"
|
||||
|
||||
Scenario: Custom-Forwarded-User and Custom-Forwarded-Groups are correctly forwarded to protected backend when basic auth is used
|
||||
When I request "https://single_factor.example.com:8080/headers" with username "john" and password "password" using basic authentication
|
||||
Then I received header "Custom-Forwarded-User" set to "john"
|
||||
And I received header "Custom-Forwarded-Groups" set to "dev,admin"
|
|
@ -1,14 +0,0 @@
|
|||
import SeleniumWebdriver from "selenium-webdriver";
|
||||
|
||||
export default async function(driver: any) {
|
||||
const content = await driver.wait(
|
||||
SeleniumWebdriver.until.elementLocated(
|
||||
SeleniumWebdriver.By.tagName('body')), 5000).getText();
|
||||
|
||||
if (content.indexOf('This is a very important secret') > - 1) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw new Error('Secret page is not accessible.');
|
||||
}
|
||||
}
|
|
@ -3,6 +3,5 @@ import SeleniumWebdriver, { WebDriver, Locator } from "selenium-webdriver";
|
|||
export default async function(driver: WebDriver, locator: Locator) {
|
||||
const el = await driver.wait(
|
||||
SeleniumWebdriver.until.elementLocated(locator), 5000);
|
||||
|
||||
await el.click();
|
||||
};
|
|
@ -1,13 +1,13 @@
|
|||
import VisitPage from "./VisitPage";
|
||||
import FillLoginPageWithUserAndPasswordAndClick from "./FillLoginPageAndClick";
|
||||
import FillLoginPageAndClick from "./FillLoginPageAndClick";
|
||||
import ValidateTotp from "./ValidateTotp";
|
||||
import WaitRedirected from "./WaitRedirected";
|
||||
import VerifyUrlIs from "./assertions/VerifyUrlIs";
|
||||
import { WebDriver } from "selenium-webdriver";
|
||||
|
||||
// Validate the two factors!
|
||||
export default async function(driver: WebDriver, url: string, user: string, secret: string) {
|
||||
export default async function(driver: WebDriver, user: string, secret: string, url: string) {
|
||||
await VisitPage(driver, `https://login.example.com:8080/?rd=${url}`);
|
||||
await FillLoginPageWithUserAndPasswordAndClick(driver, user, 'password');
|
||||
await FillLoginPageAndClick(driver, user, 'password');
|
||||
await ValidateTotp(driver, secret);
|
||||
await WaitRedirected(driver, "https://admin.example.com:8080/secret.html");
|
||||
await VerifyUrlIs(driver, url);
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import RegisterTotp from './RegisterTotp';
|
||||
import LoginAs from './LoginAs';
|
||||
import { WebDriver } from 'selenium-webdriver';
|
||||
import IsSecondFactorStage from './IsSecondFactorStage';
|
||||
import VerifyIsSecondFactorStage from './assertions/VerifyIsSecondFactorStage';
|
||||
|
||||
export default async function(driver: WebDriver, user: string, email?: boolean) {
|
||||
export default async function(driver: WebDriver, user: string, email: boolean = false) {
|
||||
await LoginAs(driver, user);
|
||||
await IsSecondFactorStage(driver);
|
||||
await VerifyIsSecondFactorStage(driver);
|
||||
return await RegisterTotp(driver, email);
|
||||
}
|
|
@ -2,7 +2,7 @@ import VisitPage from "./VisitPage";
|
|||
import FillLoginPageAndClick from './FillLoginPageAndClick';
|
||||
import { WebDriver } from "selenium-webdriver";
|
||||
|
||||
export default async function(driver: WebDriver, user: string) {
|
||||
export default async function(driver: WebDriver, user: string, password: string = "password") {
|
||||
await VisitPage(driver, "https://login.example.com:8080/");
|
||||
await FillLoginPageAndClick(driver, user, "password");
|
||||
await FillLoginPageAndClick(driver, user, password);
|
||||
}
|
13
test/helpers/assertions/VerifyForwardedHeaderIs.ts
Normal file
13
test/helpers/assertions/VerifyForwardedHeaderIs.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import SeleniumWebDriver, { WebDriver } from "selenium-webdriver";
|
||||
import Util from "util";
|
||||
|
||||
export default async function(driver: WebDriver, header: string, expectedValue: string) {
|
||||
const el = await driver.wait(SeleniumWebDriver.until.elementLocated(SeleniumWebDriver.By.tagName("body")), 5000);
|
||||
const text = await el.getText();
|
||||
|
||||
const expectedLine = Util.format("\"%s\": \"%s\"", header, expectedValue);
|
||||
|
||||
if (text.indexOf(expectedLine) < 0) {
|
||||
throw new Error("Header not found.");
|
||||
}
|
||||
}
|
5
test/helpers/assertions/VerifyUrlIs.ts
Normal file
5
test/helpers/assertions/VerifyUrlIs.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
|
||||
|
||||
export default async function(driver: WebDriver, url: string, timeout: number = 5000) {
|
||||
await driver.wait(SeleniumWebdriver.until.urlIs(url), timeout);
|
||||
}
|
17
test/helpers/behaviors/LoginOneFactor.ts
Normal file
17
test/helpers/behaviors/LoginOneFactor.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { WebDriver } from "selenium-webdriver";
|
||||
import LoginAndRegisterTotp from "../LoginAndRegisterTotp";
|
||||
import FullLogin from "../FullLogin";
|
||||
import VisitPage from "../VisitPage";
|
||||
import FillLoginPageAndClick from "../FillLoginPageAndClick";
|
||||
import VerifyUrlIs from "../assertions/VerifyUrlIs";
|
||||
|
||||
export default async function(
|
||||
driver: WebDriver,
|
||||
username: string,
|
||||
password: string,
|
||||
targetUrl: string) {
|
||||
|
||||
await VisitPage(driver, `https://login.example.com:8080/?rd=${targetUrl}`);
|
||||
await FillLoginPageAndClick(driver, username, password);
|
||||
await VerifyUrlIs(driver, targetUrl);
|
||||
};
|
13
test/helpers/behaviors/RegisterAndLoginTwoFactor.ts
Normal file
13
test/helpers/behaviors/RegisterAndLoginTwoFactor.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { WebDriver } from "selenium-webdriver";
|
||||
import LoginAndRegisterTotp from "../LoginAndRegisterTotp";
|
||||
import FullLogin from "../FullLogin";
|
||||
|
||||
export default async function(
|
||||
driver: WebDriver,
|
||||
username: string,
|
||||
email: boolean = false,
|
||||
targetUrl: string = "https://login.example.com:8080/") {
|
||||
|
||||
const secret = await LoginAndRegisterTotp(driver, username, email);
|
||||
await FullLogin(driver, username, secret, targetUrl);
|
||||
};
|
|
@ -1,31 +1,39 @@
|
|||
require("chromedriver");
|
||||
import chrome from 'selenium-webdriver/chrome';
|
||||
import SeleniumWebdriver from "selenium-webdriver";
|
||||
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
|
||||
|
||||
export default function(forEach: boolean = false) {
|
||||
export async function StartDriver() {
|
||||
let options = new chrome.Options();
|
||||
|
||||
if (process.env['HEADLESS'] == 'y') {
|
||||
options = options.headless();
|
||||
}
|
||||
|
||||
function beforeBlock(this: Mocha.IHookCallbackContext) {
|
||||
const driver = new SeleniumWebdriver.Builder()
|
||||
.forBrowser("chrome")
|
||||
.setChromeOptions(options)
|
||||
.build();
|
||||
this.driver = driver;
|
||||
}
|
||||
const driver = new SeleniumWebdriver.Builder()
|
||||
.forBrowser("chrome")
|
||||
.setChromeOptions(options)
|
||||
.build();
|
||||
return driver;
|
||||
}
|
||||
|
||||
function afterBlock(this: Mocha.IHookCallbackContext) {
|
||||
return this.driver.quit();
|
||||
}
|
||||
export async function StopDriver(driver: WebDriver) {
|
||||
return await driver.quit();
|
||||
}
|
||||
|
||||
export default function(forEach: boolean = false) {
|
||||
if (forEach) {
|
||||
beforeEach(beforeBlock);
|
||||
afterEach(afterBlock);
|
||||
beforeEach(async function() {
|
||||
this.driver = await StartDriver();
|
||||
});
|
||||
afterEach(async function() {
|
||||
await StopDriver(this.driver);
|
||||
});
|
||||
} else {
|
||||
before(beforeBlock);
|
||||
after(afterBlock);
|
||||
before(async function() {
|
||||
this.driver = await StartDriver();
|
||||
});
|
||||
after(async function() {
|
||||
await StopDriver(this.driver)
|
||||
});
|
||||
}
|
||||
}
|
|
@ -2,10 +2,12 @@ import AutheliaSuite from "../../helpers/context/AutheliaSuite";
|
|||
import MongoConnectionRecovery from "./scenarii/MongoConnectionRecovery";
|
||||
import EnforceInternalRedirectionsOnly from "./scenarii/EnforceInternalRedirectionsOnly";
|
||||
import AccessControl from "./scenarii/AccessControl";
|
||||
import CustomHeadersForwarded from "./scenarii/CustomHeadersForwarded";
|
||||
|
||||
AutheliaSuite('Complete configuration', __dirname + '/config.yml', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
describe('Custom headers forwarded to backend', CustomHeadersForwarded);
|
||||
describe('Access control', AccessControl);
|
||||
|
||||
describe('Mongo broken connection recovery', MongoConnectionRecovery);
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import LoginAndRegisterTotp from "../../../helpers/LoginAndRegisterTotp";
|
||||
import VisitPage from "../../../helpers/VisitPage";
|
||||
import ObserveSecret from "../../../helpers/assertions/ObserveSecret";
|
||||
import VerifySecretObserved from "../../../helpers/assertions/VerifySecretObserved";
|
||||
import WithDriver from "../../../helpers/context/WithDriver";
|
||||
import FillLoginPageAndClick from "../../../helpers/FillLoginPageAndClick";
|
||||
import ValidateTotp from "../../../helpers/ValidateTotp";
|
||||
import WaitRedirected from "../../../helpers/WaitRedirected";
|
||||
import VerifyUrlIs from "../../../helpers/assertions/VerifyUrlIs";
|
||||
import Logout from "../../../helpers/Logout";
|
||||
|
||||
async function ShouldHaveAccessTo(url: string) {
|
||||
it('should have access to ' + url, async function() {
|
||||
await VisitPage(this.driver, url);
|
||||
await ObserveSecret(this.driver);
|
||||
await VerifySecretObserved(this.driver);
|
||||
})
|
||||
}
|
||||
|
||||
async function ShouldNotHaveAccessTo(url: string) {
|
||||
it('should not have access to ' + url, async function() {
|
||||
await this.driver.get(url);
|
||||
await WaitRedirected(this.driver, 'https://login.example.com:8080/');
|
||||
await VerifyUrlIs(this.driver, 'https://login.example.com:8080/');
|
||||
})
|
||||
}
|
||||
|
||||
|
|
51
test/suites/complete/scenarii/CustomHeadersForwarded.ts
Normal file
51
test/suites/complete/scenarii/CustomHeadersForwarded.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import Logout from "../../../helpers/Logout";
|
||||
import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver";
|
||||
import RegisterAndLoginWith2FA from "../../../helpers/behaviors/RegisterAndLoginTwoFactor";
|
||||
import VerifyForwardedHeaderIs from "../../../helpers/assertions/VerifyForwardedHeaderIs";
|
||||
import LoginOneFactor from "../../../helpers/behaviors/LoginOneFactor";
|
||||
|
||||
export default function() {
|
||||
describe("Custom-Forwarded-User and Custom-Forwarded-Groups are correctly forwarded to protected backend", function() {
|
||||
this.timeout(10000);
|
||||
|
||||
describe("With single factor", function() {
|
||||
before(async function() {
|
||||
this.driver = await StartDriver();
|
||||
await LoginOneFactor(this.driver, "john", "password", "https://single_factor.example.com:8080/headers");
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
await Logout(this.driver);
|
||||
await StopDriver(this.driver);
|
||||
});
|
||||
|
||||
it("should see header 'Custom-Forwarded-User' set to 'john'", async function() {
|
||||
await VerifyForwardedHeaderIs(this.driver, 'Custom-Forwarded-User', 'john');
|
||||
});
|
||||
|
||||
it("should see header 'Custom-Forwarded-Groups' set to 'dev,admin'", async function() {
|
||||
await VerifyForwardedHeaderIs(this.driver, 'Custom-Forwarded-Groups', 'dev,admin');
|
||||
});
|
||||
});
|
||||
|
||||
describe("With two factors", function() {
|
||||
before(async function() {
|
||||
this.driver = await StartDriver();
|
||||
await RegisterAndLoginWith2FA(this.driver, "john", true, "https://public.example.com:8080/headers");
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
await Logout(this.driver);
|
||||
await StopDriver(this.driver);
|
||||
});
|
||||
|
||||
it("should see header 'Custom-Forwarded-User' set to 'john'", async function() {
|
||||
await VerifyForwardedHeaderIs(this.driver, 'Custom-Forwarded-User', 'john');
|
||||
});
|
||||
|
||||
it("should see header 'Custom-Forwarded-Groups' set to 'dev,admin'", async function() {
|
||||
await VerifyForwardedHeaderIs(this.driver, 'Custom-Forwarded-Groups', 'dev,admin');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -4,8 +4,8 @@ import FillLoginPageWithUserAndPasswordAndClick from '../../../helpers/FillLogin
|
|||
import ValidateTotp from "../../../helpers/ValidateTotp";
|
||||
import Logout from "../../../helpers/Logout";
|
||||
import WaitRedirected from "../../../helpers/WaitRedirected";
|
||||
import IsAlreadyAuthenticatedStage from "../../../helpers/IsAlreadyAuthenticatedStage";
|
||||
import WithDriver from "../../../helpers/context/WithDriver";
|
||||
import IsAlreadyAuthenticatedStage from "../../../helpers/assertions/VerifyIsAlreadyAuthenticatedStage";
|
||||
import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver";
|
||||
|
||||
/*
|
||||
* Authelia should not be vulnerable to open redirection. Otherwise it would aid an
|
||||
|
@ -15,17 +15,18 @@ import WithDriver from "../../../helpers/context/WithDriver";
|
|||
* the URL is pointing to an external domain.
|
||||
*/
|
||||
export default function() {
|
||||
WithDriver(true);
|
||||
describe("Only redirection to a subdomain of the protected domain should be allowed", function() {
|
||||
this.timeout(10000);
|
||||
let secret: string;
|
||||
|
||||
beforeEach(async function() {
|
||||
this.driver = await StartDriver();
|
||||
secret = await LoginAndRegisterTotp(this.driver, "john", true)
|
||||
});
|
||||
|
||||
afterEach(async function() {
|
||||
await Logout(this.driver);
|
||||
await StopDriver(this.driver);
|
||||
})
|
||||
|
||||
function CannotRedirectTo(url: string) {
|
||||
|
|
|
@ -16,6 +16,6 @@ export default function() {
|
|||
|
||||
const secret = await LoginAndRegisterTotp(this.driver, "john", true);
|
||||
child_process.execSync("./scripts/dc-dev.sh restart mongo");
|
||||
await FullLogin(this.driver, "https://admin.example.com:8080/secret.html", "john", secret);
|
||||
await FullLogin(this.driver, "john", secret, "https://admin.example.com:8080/secret.html");
|
||||
});
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import Bluebird = require("bluebird");
|
||||
import LoginAndRegisterTotp from "../../../helpers/LoginAndRegisterTotp";
|
||||
import VisitPage from "../../../helpers/VisitPage";
|
||||
import FillLoginPageWithUserAndPasswordAndClick from "../../../helpers/FillLoginPageAndClick";
|
||||
|
|
|
@ -7,7 +7,7 @@ import WaitRedirected from '../../../helpers/WaitRedirected';
|
|||
import FillField from "../../../helpers/FillField";
|
||||
import {GetLinkFromEmail} from "../../../helpers/GetIdentityLink";
|
||||
import FillLoginPageAndClick from "../../../helpers/FillLoginPageAndClick";
|
||||
import IsSecondFactorStage from "../../../helpers/IsSecondFactorStage";
|
||||
import IsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage";
|
||||
import SeeNotification from '../../../helpers/SeeNotification';
|
||||
|
||||
export default function() {
|
||||
|
|
|
@ -2,7 +2,7 @@ import FillLoginPageWithUserAndPasswordAndClick from '../../../helpers/FillLogin
|
|||
import WaitRedirected from '../../../helpers/WaitRedirected';
|
||||
import VisitPage from '../../../helpers/VisitPage';
|
||||
import ValidateTotp from '../../../helpers/ValidateTotp';
|
||||
import AccessSecret from "../../../helpers/AccessSecret";
|
||||
import VerifySecretObserved from "../../../helpers/assertions/VerifySecretObserved";
|
||||
import LoginAndRegisterTotp from '../../../helpers/LoginAndRegisterTotp';
|
||||
import SeeNotification from '../../../helpers/SeeNotification';
|
||||
import { AUTHENTICATION_TOTP_FAILED } from '../../../../shared/UserMessages';
|
||||
|
@ -25,7 +25,7 @@ export default function() {
|
|||
});
|
||||
|
||||
it("should access the secret", async function() {
|
||||
await AccessSecret(this.driver);
|
||||
await VerifySecretObserved(this.driver);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user