Migrate some tests to mocha.
parent
c5af4498ab
commit
efceb66ffa
|
@ -20,6 +20,10 @@
|
|||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.otpauthContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ class OneTimePasswordRegistrationView extends Component<Props> {
|
|||
<div className={classnames(styles.qrcodeContainer, 'qrcode')}>
|
||||
<QRCode value={secret.otpauth_url} size={180} level="Q"></QRCode>
|
||||
</div>
|
||||
<div className={classnames(styles.otpauthContainer, 'otpauth-secret')}>{secret.otpauth_url}</div>
|
||||
<div className={classnames(styles.base32Container, 'base32-secret')}>{secret.base32_secret}</div>
|
||||
</div>
|
||||
<div className={styles.loginButtonContainer}>
|
||||
|
|
|
@ -358,6 +358,15 @@
|
|||
"integrity": "sha512-J7nx6JzxmtT4zyvfLipYL7jNaxvlCWpyG7JhhCQ4fQyG+AGfovAHoYR55TFx+X8akfkUJYpt5JG6GPeFMjZaCQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node-fetch": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.1.4.tgz",
|
||||
"integrity": "sha512-tR1ekaXUGpmzOcDXWU9BW73YfA2/VW1DF1FH+wlJ82BbCSnWTbdX+JkqWQXWKIGsFPnPsYadbXfNgz28g+ccWg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "10.0.3"
|
||||
}
|
||||
},
|
||||
"@types/nodemailer": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-4.6.0.tgz",
|
||||
|
@ -5251,6 +5260,12 @@
|
|||
"resolved": "https://registry.npmjs.org/nocache/-/nocache-2.0.0.tgz",
|
||||
"integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
|
||||
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==",
|
||||
"dev": true
|
||||
},
|
||||
"nodemailer": {
|
||||
"version": "4.6.4",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.4.tgz",
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
"@types/mockdate": "^2.0.0",
|
||||
"@types/mongodb": "^3.0.9",
|
||||
"@types/nedb": "^1.8.3",
|
||||
"@types/node-fetch": "^2.1.4",
|
||||
"@types/nodemailer": "^4.6.0",
|
||||
"@types/nodemailer-direct-transport": "^1.0.31",
|
||||
"@types/nodemailer-smtp-transport": "^2.7.4",
|
||||
|
@ -98,6 +99,7 @@
|
|||
"jquery": "^3.2.1",
|
||||
"mocha": "^5.2.0",
|
||||
"mockdate": "^2.0.1",
|
||||
"node-fetch": "^2.3.0",
|
||||
"nodemon": "^1.18.9",
|
||||
"nyc": "^13.1.0",
|
||||
"query-string": "^6.0.0",
|
||||
|
|
|
@ -24,8 +24,8 @@ var tsWatcher = chokidar.watch(['server', 'shared/**/*.ts', 'node_modules'], {
|
|||
|
||||
// Properly cleanup server and client if ctrl-c is hit
|
||||
process.on('SIGINT', function() {
|
||||
killServer();
|
||||
killClient();
|
||||
killServer(() => {});
|
||||
killClient(() => {});
|
||||
fs.unlinkSync(ENVIRONMENT_FILENAME);
|
||||
process.exit();
|
||||
});
|
||||
|
@ -60,21 +60,31 @@ function startClient() {
|
|||
|
||||
function killServer(onExit) {
|
||||
if (serverProcess) {
|
||||
process.kill(-serverProcess.pid);
|
||||
serverProcess.on('exit', () => {
|
||||
serverProcess = undefined;
|
||||
onExit();
|
||||
});
|
||||
try {
|
||||
process.kill(-serverProcess.pid);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
onExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function killClient(onExit) {
|
||||
if (clientProcess) {
|
||||
process.kill(-clientProcess.pid);
|
||||
clientProcess.on('exit', () => {
|
||||
clientProcess = undefined;
|
||||
onExit();
|
||||
});
|
||||
try {
|
||||
process.kill(-clientProcess.pid);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
onExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,9 +42,9 @@ export function register(app: Express.Application,
|
|||
vars: ServerVariables) {
|
||||
|
||||
app.post(pre_validation_endpoint,
|
||||
get_start_validation(handler, post_validation_endpoint, vars));
|
||||
post_start_validation(handler, vars));
|
||||
app.post(post_validation_endpoint,
|
||||
get_finish_validation(handler, vars));
|
||||
post_finish_validation(handler, vars));
|
||||
}
|
||||
|
||||
function checkIdentityToken(req: Express.Request, identityToken: string)
|
||||
|
@ -55,7 +55,7 @@ function checkIdentityToken(req: Express.Request, identityToken: string)
|
|||
return BluebirdPromise.resolve();
|
||||
}
|
||||
|
||||
export function get_finish_validation(handler: IdentityValidable,
|
||||
export function post_finish_validation(handler: IdentityValidable,
|
||||
vars: ServerVariables)
|
||||
: Express.RequestHandler {
|
||||
|
||||
|
@ -88,8 +88,7 @@ export function get_finish_validation(handler: IdentityValidable,
|
|||
};
|
||||
}
|
||||
|
||||
export function get_start_validation(handler: IdentityValidable,
|
||||
postValidationEndpoint: string,
|
||||
export function post_start_validation(handler: IdentityValidable,
|
||||
vars: ServerVariables)
|
||||
: Express.RequestHandler {
|
||||
return function (req: Express.Request, res: Express.Response)
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
import { UserDataStore } from "../../../../storage/UserDataStore";
|
||||
import objectPath = require("object-path");
|
||||
import u2f_common = require("../U2FCommon");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
|
|
@ -3,11 +3,8 @@ import objectPath = require("object-path");
|
|||
import u2f_common = require("../U2FCommon");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
import express = require("express");
|
||||
import { UserDataStore } from "../../../../storage/UserDataStore";
|
||||
import { U2FRegistrationDocument } from "../../../../storage/U2FRegistrationDocument";
|
||||
import { Winston } from "../../../../../../types/Dependencies";
|
||||
import U2f = require("u2f");
|
||||
import exceptions = require("../../../../Exceptions");
|
||||
import redirect from "../../redirect";
|
||||
import ErrorReplies = require("../../../../ErrorReplies");
|
||||
import { ServerVariables } from "../../../../ServerVariables";
|
||||
|
|
|
@ -78,6 +78,8 @@ export default function (vars: ServerVariables) {
|
|||
ErrorReplies.replyWithError401(req, res, vars.logger))
|
||||
// The user is not yet authenticated -> 401
|
||||
.catch((err) => {
|
||||
// This redirect parameter is used in Kubernetes to annotate the ingress with
|
||||
// the url to the authentication portal.
|
||||
const redirectUrl = getRedirectParam(req);
|
||||
if (redirectUrl) {
|
||||
ErrorReplies.redirectTo(redirectUrl, req, res, vars.logger)(err);
|
||||
|
|
|
@ -30,15 +30,15 @@ function setupTotp(app: Express.Application, vars: ServerVariables) {
|
|||
RequireValidatedFirstFactor.middleware(vars.logger),
|
||||
TOTPSignGet.default(vars));
|
||||
|
||||
app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
||||
app.post(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_POST,
|
||||
RequireValidatedFirstFactor.middleware(vars.logger));
|
||||
|
||||
app.get(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET,
|
||||
app.post(Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_POST,
|
||||
RequireValidatedFirstFactor.middleware(vars.logger));
|
||||
|
||||
IdentityCheckMiddleware.register(app,
|
||||
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
||||
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET,
|
||||
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_POST,
|
||||
Endpoints.SECOND_FACTOR_TOTP_IDENTITY_FINISH_POST,
|
||||
new TOTPRegistrationIdentityHandler(vars.logger,
|
||||
vars.userDataStore, vars.totpHandler, vars.config.totp),
|
||||
vars);
|
||||
|
|
|
@ -143,7 +143,7 @@ export const SECOND_FACTOR_U2F_IDENTITY_FINISH_POST = "/api/secondfactor/u2f/ide
|
|||
*
|
||||
* @apiDescription Initiates the identity validation
|
||||
*/
|
||||
export const SECOND_FACTOR_TOTP_IDENTITY_START_GET = "/api/secondfactor/totp/identity/start";
|
||||
export const SECOND_FACTOR_TOTP_IDENTITY_START_POST = "/api/secondfactor/totp/identity/start";
|
||||
|
||||
|
||||
|
||||
|
@ -159,7 +159,7 @@ export const SECOND_FACTOR_TOTP_IDENTITY_START_GET = "/api/secondfactor/totp/ide
|
|||
* @apiDescription Serves the TOTP registration page that displays the secret.
|
||||
* The secret is a QRCode and a base32 secret.
|
||||
*/
|
||||
export const SECOND_FACTOR_TOTP_IDENTITY_FINISH_GET = "/api/secondfactor/totp/identity/finish";
|
||||
export const SECOND_FACTOR_TOTP_IDENTITY_FINISH_POST = "/api/secondfactor/totp/identity/finish";
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
Feature: User has access restricted access to domains
|
||||
|
||||
@need-registered-user-john
|
||||
Scenario: User john has admin access
|
||||
When I visit "https://login.example.com:8080?rd=https://home.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"
|
||||
And I'm redirected to "https://home.example.com:8080/"
|
||||
Then I have access to "https://public.example.com:8080/secret.html"
|
||||
And I have access to "https://dev.example.com:8080/groups/admin/secret.html"
|
||||
And I have access to "https://dev.example.com:8080/groups/dev/secret.html"
|
||||
And I have access to "https://dev.example.com:8080/users/john/secret.html"
|
||||
And I have access to "https://dev.example.com:8080/users/harry/secret.html"
|
||||
And I have access to "https://dev.example.com:8080/users/bob/secret.html"
|
||||
And I have access to "https://admin.example.com:8080/secret.html"
|
||||
And I have access to "https://mx1.mail.example.com:8080/secret.html"
|
||||
And I have access to "https://single_factor.example.com:8080/secret.html"
|
||||
And I have no access to "https://mx2.mail.example.com:8080/secret.html"
|
||||
|
||||
@need-registered-user-bob
|
||||
Scenario: User bob has restricted access
|
||||
When I visit "https://login.example.com:8080?rd=https://home.example.com:8080/"
|
||||
And I login with user "bob" 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/"
|
||||
Then I have access to "https://public.example.com:8080/secret.html"
|
||||
And I have no access to "https://dev.example.com:8080/groups/admin/secret.html"
|
||||
And I have access to "https://dev.example.com:8080/groups/dev/secret.html"
|
||||
And I have no access to "https://dev.example.com:8080/users/john/secret.html"
|
||||
And I have no access to "https://dev.example.com:8080/users/harry/secret.html"
|
||||
And I have access to "https://dev.example.com:8080/users/bob/secret.html"
|
||||
And I have no access to "https://admin.example.com:8080/secret.html"
|
||||
And I have access to "https://mx1.mail.example.com:8080/secret.html"
|
||||
And I have access to "https://single_factor.example.com:8080/secret.html"
|
||||
And I have access to "https://mx2.mail.example.com:8080/secret.html"
|
||||
|
||||
@need-registered-user-harry
|
||||
Scenario: User harry has restricted access
|
||||
When I visit "https://login.example.com:8080?rd=https://home.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/"
|
||||
Then I have access to "https://public.example.com:8080/secret.html"
|
||||
And I have no access to "https://dev.example.com:8080/groups/admin/secret.html"
|
||||
And I have no access to "https://dev.example.com:8080/groups/dev/secret.html"
|
||||
And I have no access to "https://dev.example.com:8080/users/john/secret.html"
|
||||
And I have access to "https://dev.example.com:8080/users/harry/secret.html"
|
||||
And I have no access to "https://dev.example.com:8080/users/bob/secret.html"
|
||||
And I have no access to "https://admin.example.com:8080/secret.html"
|
||||
And I have no access to "https://mx1.mail.example.com:8080/secret.html"
|
||||
And I have access to "https://single_factor.example.com:8080/secret.html"
|
||||
And I have no access to "https://mx2.mail.example.com:8080/secret.html"
|
|
@ -1,9 +0,0 @@
|
|||
Feature: Generic tests on Authelia endpoints
|
||||
|
||||
Scenario: /api/verify replies with error when redirect parameter is not provided
|
||||
When I query "https://authelia.example.com:8080/api/verify"
|
||||
Then I get error code 401
|
||||
|
||||
Scenario: /api/verify redirects when redirect parameter is provided
|
||||
When I query "https://authelia.example.com:8080/api/verify?rd=http://login.example.com:8080"
|
||||
Then I get redirected to "http://login.example.com:8080"
|
|
@ -1,38 +1,5 @@
|
|||
Feature: Authentication scenarii
|
||||
|
||||
Scenario: User succeeds first factor
|
||||
Given I visit "https://login.example.com:8080/"
|
||||
When I set field "username" to "bob"
|
||||
And I set field "password" to "password"
|
||||
And I click on "Sign in"
|
||||
Then I'm redirected to "https://login.example.com:8080/secondfactor"
|
||||
|
||||
Scenario: User fails first factor
|
||||
Given I visit "https://login.example.com:8080/"
|
||||
When I set field "username" to "john"
|
||||
And I set field "password" to "bad-password"
|
||||
And I click on "Sign in"
|
||||
Then I get a notification of type "error" with message "Authentication failed. Please check your credentials."
|
||||
|
||||
Scenario: User registers TOTP secret and succeeds authentication
|
||||
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://admin.example.com:8080/secret.html"
|
||||
And I'm redirected to "https://login.example.com:8080/?rd=https://admin.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://admin.example.com:8080/secret.html"
|
||||
|
||||
Scenario: User fails TOTP second factor
|
||||
When I visit "https://admin.example.com:8080/secret.html"
|
||||
And I'm redirected to "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html"
|
||||
And I login with user "john" and password "password"
|
||||
And I use "BADTOKEN" as TOTP token
|
||||
And I click on "Sign in"
|
||||
Then I get a notification of type "error" with message "Authentication failed. Have you already registered your secret?"
|
||||
|
||||
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,14 +0,0 @@
|
|||
Feature: Register secret for second factor
|
||||
|
||||
Scenario: Register a TOTP secret with correct label and issuer
|
||||
Given I visit "https://login.example.com:8080/"
|
||||
And I login with user "john" and password "password"
|
||||
When I register a TOTP secret called "Sec0"
|
||||
Then the otpauth url has label "john" and issuer "authelia.com"
|
||||
|
||||
@needs-totp_issuer-config
|
||||
Scenario: Register a TOTP secret with correct label and custom issuer
|
||||
Given I visit "https://login.example.com:8080/"
|
||||
And I login with user "john" and password "password"
|
||||
When I register a TOTP secret called "Sec0"
|
||||
Then the otpauth url has label "john" and issuer "custom.com"
|
|
@ -1,39 +0,0 @@
|
|||
Feature: User is able to reset his password
|
||||
|
||||
Scenario: User is redirected to password reset page
|
||||
Given I'm on "https://login.example.com:8080"
|
||||
When I click on the link "Forgot password?"
|
||||
Then I'm redirected to "https://login.example.com:8080/password-reset/request"
|
||||
|
||||
Scenario: User get an email with a link to reset password
|
||||
Given I'm on "https://login.example.com:8080/password-reset/request"
|
||||
When I set field "username" to "james"
|
||||
And I click on "Reset Password"
|
||||
Then I get a notification of type "success" with message "An email has been sent to you. Follow the link to change your password."
|
||||
|
||||
Scenario: Request password for unexisting user should behave like existing user
|
||||
Given I'm on "https://login.example.com:8080/password-reset/request"
|
||||
When I set field "username" to "fake_user"
|
||||
And I click on "Reset Password"
|
||||
Then I get a notification of type "success" with message "An email has been sent to you. Follow the link to change your password."
|
||||
|
||||
Scenario: User resets his password
|
||||
Given I'm on "https://login.example.com:8080/password-reset/request"
|
||||
And I set field "username" to "james"
|
||||
And I click on "Reset Password"
|
||||
When I click on the link of the email
|
||||
And I set field "password1" to "newpassword"
|
||||
And I set field "password2" to "newpassword"
|
||||
And I click on "Reset Password"
|
||||
Then I'm redirected to "https://login.example.com:8080/"
|
||||
|
||||
|
||||
Scenario: User does not confirm new password
|
||||
Given I'm on "https://login.example.com:8080/password-reset/request"
|
||||
And I set field "username" to "james"
|
||||
And I click on "Reset Password"
|
||||
When I click on the link of the email
|
||||
And I set field "password1" to "newpassword"
|
||||
And I set field "password2" to "newpassword2"
|
||||
And I click on "Reset Password"
|
||||
Then I get a notification of type "warning" with message "The passwords are different."
|
|
@ -1,16 +0,0 @@
|
|||
Feature: Non authenticated users have no access to certain pages
|
||||
|
||||
Scenario: Anonymous user has no access to protected pages
|
||||
Then I get the following status code when requesting:
|
||||
| url | code | method |
|
||||
| https://login.example.com:8080/secondfactor | 401 | GET |
|
||||
| https://login.example.com:8080/secondfactor/u2f/identity/start | 401 | GET |
|
||||
| https://login.example.com:8080/secondfactor/u2f/identity/finish | 401 | GET |
|
||||
| https://login.example.com:8080/secondfactor/totp/identity/start | 401 | GET |
|
||||
| https://login.example.com:8080/secondfactor/totp/identity/finish | 401 | GET |
|
||||
| https://login.example.com:8080/loggedin | 401 | GET |
|
||||
| https://login.example.com:8080/api/totp | 401 | POST |
|
||||
| https://login.example.com:8080/api/u2f/sign_request | 401 | GET |
|
||||
| https://login.example.com:8080/api/u2f/sign | 401 | POST |
|
||||
| https://login.example.com:8080/api/u2f/register_request | 401 | GET |
|
||||
| https://login.example.com:8080/api/u2f/register | 401 | POST |
|
|
@ -1,20 +0,0 @@
|
|||
@needs-inactivity-config
|
||||
Feature: Session is closed after a certain amount of time
|
||||
|
||||
@need-authenticated-user-john
|
||||
Scenario: An authenticated user is disconnected after a certain inactivity period
|
||||
Given I have access to "https://public.example.com:8080/secret.html"
|
||||
When I sleep for 6 seconds
|
||||
And 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"
|
||||
|
||||
@need-authenticated-user-john
|
||||
Scenario: An authenticated user is disconnected after session expiration period
|
||||
Given I have access to "https://public.example.com:8080/secret.html"
|
||||
When I sleep for 4 seconds
|
||||
And I visit "https://public.example.com:8080/secret.html"
|
||||
And I sleep for 4 seconds
|
||||
And I visit "https://public.example.com:8080/secret.html"
|
||||
And I sleep for 4 seconds
|
||||
And 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"
|
|
@ -1,4 +1,4 @@
|
|||
import SeleniumWebdriver, { ThenableWebDriver, WebDriver } from "selenium-webdriver";
|
||||
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
|
||||
import Assert = require("assert");
|
||||
|
||||
export default async function(driver: WebDriver, type: string, message: string) {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
|
||||
|
||||
// Verify if the current page contains "This is a very important secret!".
|
||||
export default async function(driver: WebDriver, timeout: number = 5000) {
|
||||
const el = await driver.wait(
|
||||
SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.tagName('body')), timeout);
|
||||
|
||||
await driver.wait(
|
||||
SeleniumWebdriver.until.elementTextContains(el, "This is a very important secret!"), timeout);
|
||||
}
|
|
@ -18,7 +18,6 @@ function AutheliaSuiteBase(description: string, configPath: string,
|
|||
}
|
||||
|
||||
return context('Suite: ' + description, function(this: Mocha.ISuiteCallbackContext) {
|
||||
WithDriver.call(this);
|
||||
cb.call(this);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,22 +2,30 @@ require("chromedriver");
|
|||
import chrome from 'selenium-webdriver/chrome';
|
||||
import SeleniumWebdriver from "selenium-webdriver";
|
||||
|
||||
export default function() {
|
||||
export default function(forEach: boolean = false) {
|
||||
let options = new chrome.Options();
|
||||
|
||||
if (process.env['HEADLESS'] == 'y') {
|
||||
options = options.headless();
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
function beforeBlock(this: Mocha.IHookCallbackContext) {
|
||||
const driver = new SeleniumWebdriver.Builder()
|
||||
.forBrowser("chrome")
|
||||
.setChromeOptions(options)
|
||||
.build();
|
||||
this.driver = driver;
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(function() {
|
||||
this.driver.quit();
|
||||
});
|
||||
function afterBlock(this: Mocha.IHookCallbackContext) {
|
||||
return this.driver.quit();
|
||||
}
|
||||
|
||||
if (forEach) {
|
||||
beforeEach(beforeBlock);
|
||||
afterEach(afterBlock);
|
||||
} else {
|
||||
before(beforeBlock);
|
||||
after(afterBlock);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
import Request from 'request-promise';
|
||||
import Fetch from 'node-fetch';
|
||||
import Assert from 'assert';
|
||||
import { StatusCodeError } from 'request-promise/errors';
|
||||
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
||||
|
||||
// Sent a GET request to the url and expect a 401
|
||||
export async function GET_Expect401(url: string) {
|
||||
try {
|
||||
await Request.get(url, {
|
||||
json: true,
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
throw new Error('No response');
|
||||
} catch (e) {
|
||||
if (e instanceof StatusCodeError) {
|
||||
Assert.equal(e.statusCode, 401);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
export async function POST_Expect401(url: string, body?: any) {
|
||||
try {
|
||||
await Request.post(url, {
|
||||
json: true,
|
||||
rejectUnauthorized: false,
|
||||
body
|
||||
});
|
||||
throw new Error('No response');
|
||||
} catch (e) {
|
||||
if (e instanceof StatusCodeError) {
|
||||
Assert.equal(e.statusCode, 401);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
export async function GET_ExpectRedirect(url: string, redirectionUrl: string) {
|
||||
const response = await Fetch(url, {redirect: 'manual'});
|
||||
|
||||
if (response.status == 302) {
|
||||
const body = await response.text();
|
||||
Assert.equal(body, 'Found. Redirecting to ' + redirectionUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error('No redirect');
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
import AutheliaSuite from "../../helpers/context/AutheliaSuite";
|
||||
import MongoConnectionRecovery from "./scenarii/MongoConnectionRecovery";
|
||||
import EnforceInternalRedirectionsOnly from "./scenarii/EnforceInternalRedirectionsOnly";
|
||||
import AccessControl from "./scenarii/AccessControl";
|
||||
|
||||
AutheliaSuite('Complete configuration', __dirname + '/config.yml', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
describe('Access control', AccessControl);
|
||||
|
||||
describe('Mongo broken connection recovery', MongoConnectionRecovery);
|
||||
describe('Enforce internal redirections only', EnforceInternalRedirectionsOnly);
|
||||
});
|
|
@ -0,0 +1,107 @@
|
|||
import LoginAndRegisterTotp from "../../../helpers/LoginAndRegisterTotp";
|
||||
import VisitPage from "../../../helpers/VisitPage";
|
||||
import ObserveSecret from "../../../helpers/assertions/ObserveSecret";
|
||||
import WithDriver from "../../../helpers/context/WithDriver";
|
||||
import FillLoginPageAndClick from "../../../helpers/FillLoginPageAndClick";
|
||||
import ValidateTotp from "../../../helpers/ValidateTotp";
|
||||
import WaitRedirected from "../../../helpers/WaitRedirected";
|
||||
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);
|
||||
})
|
||||
}
|
||||
|
||||
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/');
|
||||
})
|
||||
}
|
||||
|
||||
// we verify that the user has only access to want he is granted to.
|
||||
export default function() {
|
||||
|
||||
// We ensure that bob has access to what he is granted to
|
||||
describe('Permissions of user john', function() {
|
||||
after(async function() {
|
||||
await Logout(this.driver);
|
||||
})
|
||||
|
||||
WithDriver();
|
||||
|
||||
before(async function() {
|
||||
const secret = await LoginAndRegisterTotp(this.driver, "john", true);
|
||||
await VisitPage(this.driver, 'https://login.example.com:8080/');
|
||||
await FillLoginPageAndClick(this.driver, 'john', 'password', false);
|
||||
await ValidateTotp(this.driver, secret);
|
||||
})
|
||||
|
||||
ShouldHaveAccessTo('https://public.example.com:8080/secret.html');
|
||||
ShouldHaveAccessTo('https://dev.example.com:8080/groups/admin/secret.html');
|
||||
ShouldHaveAccessTo('https://dev.example.com:8080/groups/dev/secret.html');
|
||||
ShouldHaveAccessTo('https://dev.example.com:8080/users/john/secret.html');
|
||||
ShouldHaveAccessTo('https://dev.example.com:8080/users/harry/secret.html');
|
||||
ShouldHaveAccessTo('https://dev.example.com:8080/users/bob/secret.html');
|
||||
ShouldHaveAccessTo('https://admin.example.com:8080/secret.html');
|
||||
ShouldHaveAccessTo('https://mx1.mail.example.com:8080/secret.html');
|
||||
ShouldHaveAccessTo('https://single_factor.example.com:8080/secret.html');
|
||||
ShouldNotHaveAccessTo('https://mx2.mail.example.com:8080/secret.html');
|
||||
})
|
||||
|
||||
// We ensure that bob has access to what he is granted to
|
||||
describe('Permissions of user bob', function() {
|
||||
after(async function() {
|
||||
await Logout(this.driver);
|
||||
})
|
||||
|
||||
WithDriver();
|
||||
|
||||
before(async function() {
|
||||
const secret = await LoginAndRegisterTotp(this.driver, "bob", true);
|
||||
await VisitPage(this.driver, 'https://login.example.com:8080/');
|
||||
await FillLoginPageAndClick(this.driver, 'bob', 'password', false);
|
||||
await ValidateTotp(this.driver, secret);
|
||||
})
|
||||
|
||||
ShouldHaveAccessTo('https://public.example.com:8080/secret.html');
|
||||
ShouldNotHaveAccessTo('https://dev.example.com:8080/groups/admin/secret.html');
|
||||
ShouldHaveAccessTo('https://dev.example.com:8080/groups/dev/secret.html');
|
||||
ShouldNotHaveAccessTo('https://dev.example.com:8080/users/john/secret.html');
|
||||
ShouldNotHaveAccessTo('https://dev.example.com:8080/users/harry/secret.html');
|
||||
ShouldHaveAccessTo('https://dev.example.com:8080/users/bob/secret.html');
|
||||
ShouldNotHaveAccessTo('https://admin.example.com:8080/secret.html');
|
||||
ShouldHaveAccessTo('https://mx1.mail.example.com:8080/secret.html');
|
||||
ShouldHaveAccessTo('https://single_factor.example.com:8080/secret.html');
|
||||
ShouldHaveAccessTo('https://mx2.mail.example.com:8080/secret.html');
|
||||
})
|
||||
|
||||
// We ensure that harry has access to what he is granted to
|
||||
describe('Permissions of user harry', function() {
|
||||
after(async function() {
|
||||
await Logout(this.driver);
|
||||
})
|
||||
|
||||
WithDriver();
|
||||
|
||||
before(async function() {
|
||||
const secret = await LoginAndRegisterTotp(this.driver, "harry", true);
|
||||
await VisitPage(this.driver, 'https://login.example.com:8080/');
|
||||
await FillLoginPageAndClick(this.driver, 'harry', 'password', false);
|
||||
await ValidateTotp(this.driver, secret);
|
||||
})
|
||||
|
||||
ShouldHaveAccessTo('https://public.example.com:8080/secret.html');
|
||||
ShouldNotHaveAccessTo('https://dev.example.com:8080/groups/admin/secret.html');
|
||||
ShouldNotHaveAccessTo('https://dev.example.com:8080/groups/dev/secret.html');
|
||||
ShouldNotHaveAccessTo('https://dev.example.com:8080/users/john/secret.html');
|
||||
ShouldHaveAccessTo('https://dev.example.com:8080/users/harry/secret.html');
|
||||
ShouldNotHaveAccessTo('https://dev.example.com:8080/users/bob/secret.html');
|
||||
ShouldNotHaveAccessTo('https://admin.example.com:8080/secret.html');
|
||||
ShouldNotHaveAccessTo('https://mx1.mail.example.com:8080/secret.html');
|
||||
ShouldHaveAccessTo('https://single_factor.example.com:8080/secret.html');
|
||||
ShouldNotHaveAccessTo('https://mx2.mail.example.com:8080/secret.html');
|
||||
})
|
||||
}
|
|
@ -5,6 +5,7 @@ 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";
|
||||
|
||||
/*
|
||||
* Authelia should not be vulnerable to open redirection. Otherwise it would aid an
|
||||
|
@ -14,6 +15,7 @@ import IsAlreadyAuthenticatedStage from "../../../helpers/IsAlreadyAuthenticated
|
|||
* 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;
|
||||
|
@ -44,18 +46,22 @@ export default function() {
|
|||
});
|
||||
}
|
||||
|
||||
describe('blocked redirection', function() {
|
||||
describe('Cannot redirect to https://www.google.fr', function() {
|
||||
// Do not redirect to another domain than example.com
|
||||
CannotRedirectTo("https://www.google.fr");
|
||||
});
|
||||
|
||||
// Do not redirect to rogue domain
|
||||
describe('Cannot redirect to https://public.example.com.a:8080', function() {
|
||||
// Do not redirect to another domain than example.com
|
||||
CannotRedirectTo("https://public.example.com.a:8080");
|
||||
});
|
||||
|
||||
describe('Cannot redirect to http://public.example.com:8080', function() {
|
||||
// Do not redirect to http website
|
||||
CannotRedirectTo("http://public.example.com:8080");
|
||||
});
|
||||
|
||||
describe('allowed redirection', function() {
|
||||
describe('Can redirect to https://public.example.com:8080/', function() {
|
||||
// Can redirect to any subdomain of the domain protected by Authelia.
|
||||
CanRedirectTo("https://public.example.com:8080/");
|
||||
});
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import LoginAndRegisterTotp from "../../../helpers/LoginAndRegisterTotp";
|
||||
import FullLogin from "../../../helpers/FullLogin";
|
||||
import child_process from 'child_process';
|
||||
import WithDriver from "../../../helpers/context/WithDriver";
|
||||
import Logout from "../../../helpers/Logout";
|
||||
|
||||
export default function() {
|
||||
after(async function() {
|
||||
await Logout(this.driver);
|
||||
})
|
||||
|
||||
WithDriver();
|
||||
|
||||
it("should be able to login after mongo restarts", async function() {
|
||||
this.timeout(30000);
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ session:
|
|||
secret: unsecure_session_secret
|
||||
domain: example.com
|
||||
inactivity: 5000
|
||||
expiration: 8000
|
||||
|
||||
# Configuration of the storage backend used to store data and secrets. i.e. totp data
|
||||
storage:
|
||||
|
|
|
@ -7,6 +7,8 @@ import RegisterTotp from './scenarii/RegisterTotp';
|
|||
import ResetPassword from './scenarii/ResetPassword';
|
||||
import TOTPValidation from './scenarii/TOTPValidation';
|
||||
import Inactivity from './scenarii/Inactivity';
|
||||
import BackendProtection from './scenarii/BackendProtection';
|
||||
import VerifyEndpoint from './scenarii/VerifyEndpoint';
|
||||
|
||||
const execAsync = Bluebird.promisify(ChildProcess.exec);
|
||||
|
||||
|
@ -16,6 +18,9 @@ AutheliaSuite('Minimal configuration', __dirname + '/config.yml', function() {
|
|||
return execAsync("cp users_database.example.yml users_database.yml");
|
||||
});
|
||||
|
||||
describe('Backend protection', BackendProtection);
|
||||
describe('Verify API endpoint', VerifyEndpoint);
|
||||
|
||||
describe('Bad password', BadPassword);
|
||||
describe('Reset password', ResetPassword);
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import { POST_Expect401, GET_Expect401 } from "../../../helpers/utils/Requests";
|
||||
|
||||
export default function() {
|
||||
// POST
|
||||
it('should return 401 error when posting to https://login.example.com:8080/api/totp', async function() {
|
||||
await POST_Expect401('https://login.example.com:8080/api/totp', { token: 'MALICIOUS_TOKEN' });
|
||||
});
|
||||
|
||||
it('should return 401 error when posting to https://login.example.com:8080/api/u2f/sign', async function() {
|
||||
await POST_Expect401('https://login.example.com:8080/api/u2f/sign');
|
||||
});
|
||||
|
||||
it('should return 401 error when posting to https://login.example.com:8080/api/u2f/register', async function() {
|
||||
await POST_Expect401('https://login.example.com:8080/api/u2f/register');
|
||||
});
|
||||
|
||||
|
||||
// GET
|
||||
it('should return 401 error on GET to https://login.example.com:8080/api/u2f/sign_request', async function() {
|
||||
await GET_Expect401('https://login.example.com:8080/api/u2f/sign_request');
|
||||
});
|
||||
|
||||
it('should return 401 error on GET to https://login.example.com:8080/api/u2f/register_request', async function() {
|
||||
await GET_Expect401('https://login.example.com:8080/api/u2f/register_request');
|
||||
});
|
||||
|
||||
|
||||
describe('Identity validation endpoints blocked to unauthenticated users', function() {
|
||||
it('should return 401 error on POST to https://login.example.com:8080/api/secondfactor/u2f/identity/start', async function() {
|
||||
await POST_Expect401('https://login.example.com:8080/api/secondfactor/u2f/identity/start');
|
||||
});
|
||||
|
||||
it('should return 401 error on POST to https://login.example.com:8080/api/secondfactor/u2f/identity/finish', async function() {
|
||||
await POST_Expect401('https://login.example.com:8080/api/secondfactor/u2f/identity/finish');
|
||||
});
|
||||
|
||||
it('should return 401 error on POST to https://login.example.com:8080/api/secondfactor/totp/identity/start', async function() {
|
||||
await POST_Expect401('https://login.example.com:8080/api/secondfactor/totp/identity/start');
|
||||
});
|
||||
|
||||
it('should return 401 error on POST to https://login.example.com:8080/api/secondfactor/totp/identity/finish', async function() {
|
||||
await POST_Expect401('https://login.example.com:8080/api/secondfactor/totp/identity/finish');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -4,16 +4,17 @@ import VisitPage from "../../../helpers/VisitPage";
|
|||
import FillLoginPageWithUserAndPasswordAndClick from "../../../helpers/FillLoginPageAndClick";
|
||||
import ValidateTotp from "../../../helpers/ValidateTotp";
|
||||
import WaitRedirected from "../../../helpers/WaitRedirected";
|
||||
import { WebDriver } from "selenium-webdriver";
|
||||
|
||||
export default function(this: Mocha.ISuiteCallbackContext) {
|
||||
this.timeout(15000);
|
||||
this.timeout(20000);
|
||||
|
||||
beforeEach(async function() {
|
||||
this.secret = await LoginAndRegisterTotp(this.driver, "john", true);
|
||||
});
|
||||
|
||||
it("should disconnect user after inactivity period", async function() {
|
||||
const driver = this.driver;
|
||||
const driver = this.driver as WebDriver;
|
||||
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);
|
||||
|
@ -24,15 +25,36 @@ export default function(this: Mocha.ISuiteCallbackContext) {
|
|||
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;
|
||||
it('should disconnect user after cookie expiration', async function() {
|
||||
const driver = this.driver as WebDriver;
|
||||
await VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html");
|
||||
await FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'password', true);
|
||||
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.sleep(4000);
|
||||
await driver.get("https://admin.example.com:8080/secret.html");
|
||||
await WaitRedirected(driver, "https://admin.example.com:8080/secret.html");
|
||||
await driver.sleep(2000);
|
||||
await driver.get("https://admin.example.com:8080/secret.html");
|
||||
|
||||
await driver.sleep(2000);
|
||||
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");
|
||||
|
||||
});
|
||||
|
||||
describe('With remember me checkbox checked', function() {
|
||||
it("should keep user logged in after inactivity period", async function() {
|
||||
const driver = this.driver as WebDriver;
|
||||
await VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html");
|
||||
await 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");
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import SeleniumWebdriver from "selenium-webdriver";
|
||||
import SeleniumWebdriver, { WebDriver } from "selenium-webdriver";
|
||||
import Assert from 'assert';
|
||||
import LoginAndRegisterTotp from '../../../helpers/LoginAndRegisterTotp';
|
||||
|
||||
/**
|
||||
|
@ -26,5 +27,18 @@ export default function() {
|
|||
SeleniumWebdriver.By.className("base32-secret")),
|
||||
5000);
|
||||
});
|
||||
|
||||
it("should have user and issuer in otp url", async function() {
|
||||
// this.timeout(100000);
|
||||
const el = await (this.driver as WebDriver).wait(
|
||||
SeleniumWebdriver.until.elementLocated(
|
||||
SeleniumWebdriver.By.className('otpauth-secret')), 5000);
|
||||
|
||||
const otpauthUrl = await el.getAttribute('innerText');
|
||||
const label = 'john';
|
||||
const issuer = 'example.com';
|
||||
|
||||
Assert(new RegExp(`^otpauth://totp/${label}\\?secret=[A-Z0-9]+&issuer=${issuer}$`).test(otpauthUrl));
|
||||
})
|
||||
});
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ import FillField from "../../../helpers/FillField";
|
|||
import {GetLinkFromEmail} from "../../../helpers/GetIdentityLink";
|
||||
import FillLoginPageAndClick from "../../../helpers/FillLoginPageAndClick";
|
||||
import IsSecondFactorStage from "../../../helpers/IsSecondFactorStage";
|
||||
import SeeNotification from '../../../helpers/SeeNotification';
|
||||
|
||||
export default function() {
|
||||
it("should reset password for john", async function() {
|
||||
|
@ -16,6 +17,7 @@ export default function() {
|
|||
await WaitRedirected(this.driver, "https://login.example.com:8080/forgot-password");
|
||||
await FillField(this.driver, "username", "john");
|
||||
await ClickOn(this.driver, SeleniumWebDriver.By.id('next-button'));
|
||||
await WaitRedirected(this.driver, 'https://login.example.com:8080/confirmation-sent');
|
||||
|
||||
await this.driver.sleep(500); // Simulate the time it takes to receive the e-mail.
|
||||
const link = await GetLinkFromEmail();
|
||||
|
@ -25,6 +27,36 @@ export default function() {
|
|||
await ClickOn(this.driver, SeleniumWebDriver.By.id('reset-button'));
|
||||
await WaitRedirected(this.driver, "https://login.example.com:8080/");
|
||||
await FillLoginPageAndClick(this.driver, "john", "newpass");
|
||||
|
||||
// The user reaches the second factor page using the new password.
|
||||
await IsSecondFactorStage(this.driver);
|
||||
});
|
||||
|
||||
it("should persuade reset password is initiated for unknown user", async function() {
|
||||
await VisitPage(this.driver, "https://login.example.com:8080/");
|
||||
await ClickOnLink(this.driver, "Forgot password\?");
|
||||
await WaitRedirected(this.driver, "https://login.example.com:8080/forgot-password");
|
||||
await FillField(this.driver, "username", "unknown");
|
||||
await ClickOn(this.driver, SeleniumWebDriver.By.id('next-button'));
|
||||
|
||||
// The malicious user thinks the confirmation has been sent.
|
||||
await WaitRedirected(this.driver, 'https://login.example.com:8080/confirmation-sent');
|
||||
});
|
||||
|
||||
it("should notify passwords are different in reset form", async function() {
|
||||
await VisitPage(this.driver, "https://login.example.com:8080/");
|
||||
await ClickOnLink(this.driver, "Forgot password\?");
|
||||
await WaitRedirected(this.driver, "https://login.example.com:8080/forgot-password");
|
||||
await FillField(this.driver, "username", "john");
|
||||
await ClickOn(this.driver, SeleniumWebDriver.By.id('next-button'));
|
||||
await WaitRedirected(this.driver, 'https://login.example.com:8080/confirmation-sent');
|
||||
|
||||
await this.driver.sleep(500); // Simulate the time it takes to receive the e-mail.
|
||||
const link = await GetLinkFromEmail();
|
||||
await VisitPage(this.driver, link);
|
||||
await FillField(this.driver, "password1", "newpass");
|
||||
await FillField(this.driver, "password2", "badpass");
|
||||
await ClickOn(this.driver, SeleniumWebDriver.By.id('reset-button'));
|
||||
await SeeNotification(this.driver, "error", "The passwords are different.");
|
||||
});
|
||||
}
|
||||
|
|
|
@ -39,9 +39,9 @@ export default function() {
|
|||
await LoginAndRegisterTotp(this.driver, "john", true);
|
||||
const BAD_TOKEN = "125478";
|
||||
|
||||
await VisitPage(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html");
|
||||
await FillLoginPageWithUserAndPasswordAndClick(this.driver, 'john', 'password');
|
||||
await ValidateTotp(this.driver, BAD_TOKEN);
|
||||
await VisitPage(this.driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html");
|
||||
await FillLoginPageWithUserAndPasswordAndClick(this.driver, 'john', 'password');
|
||||
await ValidateTotp(this.driver, BAD_TOKEN);
|
||||
});
|
||||
|
||||
it("get a notification message", async function() {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { GET_Expect401, GET_ExpectRedirect } from "../../../helpers/utils/Requests";
|
||||
|
||||
export default function() {
|
||||
describe('Query without authenticated cookie', function() {
|
||||
it('should get a 401 on GET to https://authelia.example.com:8080/api/verify', async function() {
|
||||
await GET_Expect401('https://login.example.com:8080/api/verify');
|
||||
});
|
||||
|
||||
describe('Parameter `rd` required by Kubernetes ingress controller', async function() {
|
||||
it('should redirect to https://login.example.com:8080', async function() {
|
||||
await GET_ExpectRedirect('https://login.example.com:8080/api/verify?rd=https://login.example.com:8080',
|
||||
'https://login.example.com:8080');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue