mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
Disable second factor for certain subdomain
This commit is contained in:
parent
1636fc27e5
commit
4cbf6efa42
|
@ -15,13 +15,14 @@ addons:
|
|||
- libgif-dev
|
||||
- google-chrome-stable
|
||||
hosts:
|
||||
- auth.test.local
|
||||
- home.test.local
|
||||
- public.test.local
|
||||
- admin.test.local
|
||||
- auth.test.local
|
||||
- basicauth.test.local
|
||||
- dev.test.local
|
||||
- home.test.local
|
||||
- mx1.mail.test.local
|
||||
- mx2.mail.test.local
|
||||
- public.test.local
|
||||
|
||||
before_install:
|
||||
- npm install -g npm@'>=2.13.5'
|
||||
|
|
|
@ -26,7 +26,7 @@ module.exports = function (grunt) {
|
|||
},
|
||||
"docker-restart": {
|
||||
cmd: "./scripts/dc-dev.sh",
|
||||
args: ['up', '-d']
|
||||
args: ['restart', 'authelia']
|
||||
},
|
||||
"minify": {
|
||||
cmd: "./node_modules/.bin/uglifyjs",
|
||||
|
|
|
@ -5,6 +5,6 @@ services:
|
|||
- ./test:/usr/src/test
|
||||
- ./dist/src/server:/usr/src
|
||||
- ./node_modules:/usr/src/node_modules
|
||||
- ./config.yml:/etc/authelia/config.yml:ro
|
||||
- ./config.template.yml:/etc/authelia/config.yml:ro
|
||||
networks:
|
||||
- example-network
|
||||
|
|
10
example/nginx/html/basicauth.test.local/secret.html
Normal file
10
example/nginx/html/basicauth.test.local/secret.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Secret</title>
|
||||
<link rel="icon" href="/icon.png" type="image/png" />
|
||||
</head>
|
||||
<body>
|
||||
This is a very important secret!<br/>
|
||||
Go back to <a href="https://home.test.local:8080/">home page</a>.
|
||||
</body>
|
||||
</html>
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
<body>
|
||||
<h1>Access the secret</h1>
|
||||
<span style="font-size: 1.2em; color: red">You need to log in to access the secret!</span><br/><br/>
|
||||
Try to access it using one of the following links to test access control powered by Authelia.<br/>
|
||||
<span style="font-size: 1.2em; color: red">You need to log in to access the secret!</span><br/><br/> Try to access it using
|
||||
one of the following links to test access control powered by Authelia.<br/>
|
||||
<ul>
|
||||
<li>
|
||||
public.test.local <a href="https://public.test.local:8080/"> / index.html</a>
|
||||
|
@ -51,17 +51,19 @@
|
|||
<li>
|
||||
mx2.main.test.local <a href="https://mx2.mail.test.local:8080/secret.html"> / secret.html</a>
|
||||
</li>
|
||||
<li>
|
||||
basicauth.test.local <a href="https://basicauth.test.local:8080/secret.html"> / secret.html</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
You can also log off by visiting the following <a href="https://auth.test.local:8080/logout?redirect=https://home.test.local:8080/">link</a>.
|
||||
|
||||
<h1>List of users</h1>
|
||||
Here is the list of credentials you can log in with to test access control.<br/>
|
||||
<br/>
|
||||
Once first factor is passed, you will need to follow the links to register a secret for the second factor.<br/>
|
||||
Authelia will send you a fictituous email that will be in the file
|
||||
<strong>/tmp/notifications/notification.txt</strong>.<br/>
|
||||
It will provide you with the link to complete the registration allowing you to authenticate with 2-factor.
|
||||
<br/> Once first factor is passed, you will need to follow the links to register a secret for the second factor.<br/> Authelia
|
||||
will send you a fictituous email that will be in the file
|
||||
<strong>/tmp/notifications/notification.txt</strong>.<br/> It will provide you with the link to complete the registration
|
||||
allowing you to authenticate with 2-factor.
|
||||
|
||||
<ul>
|
||||
<li><strong>john / password</strong>: belongs to <em>admin</em> and <em>dev</em> groups.</li>
|
||||
|
@ -129,4 +131,5 @@ users:
|
|||
resources:
|
||||
- '^/users/harry/.*$'</pre>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -203,5 +203,42 @@ http {
|
|||
error_page 403 = https://auth.test.local:8080/error/403;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
root /usr/share/nginx/html/basicauth.test.local;
|
||||
|
||||
server_name basicauth.test.local;
|
||||
|
||||
ssl on;
|
||||
ssl_certificate /etc/ssl/server.crt;
|
||||
ssl_certificate_key /etc/ssl/server.key;
|
||||
|
||||
location /auth_verify {
|
||||
internal;
|
||||
proxy_set_header X-Original-URI $request_uri;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header Content-Length "";
|
||||
|
||||
proxy_pass http://authelia/verify?only_basic_auth=true;
|
||||
}
|
||||
|
||||
location / {
|
||||
auth_request /auth_verify;
|
||||
|
||||
auth_request_set $redirect $upstream_http_redirect;
|
||||
proxy_set_header Redirect $redirect;
|
||||
|
||||
auth_request_set $user $upstream_http_remote_user;
|
||||
proxy_set_header X-Forwarded-User $user;
|
||||
|
||||
auth_request_set $groups $upstream_http_remote_groups;
|
||||
proxy_set_header Remote-Groups $groups;
|
||||
|
||||
error_page 401 =302 https://auth.test.local:8080?redirect=$redirect&only_basic_auth=true;
|
||||
error_page 403 = https://auth.test.local:8080/error/403;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,24 @@
|
|||
|
||||
import BluebirdPromise = require("bluebird");
|
||||
import Endpoints = require("../../../server/endpoints");
|
||||
import Constants = require("../../../server/constants");
|
||||
|
||||
export function validate(username: string, password: string, $: JQueryStatic): BluebirdPromise<void> {
|
||||
return new BluebirdPromise<void>(function (resolve, reject) {
|
||||
$.post(Endpoints.FIRST_FACTOR_POST, {
|
||||
export function validate(username: string, password: string,
|
||||
redirectUrl: string, onlyBasicAuth: boolean, $: JQueryStatic): BluebirdPromise<string> {
|
||||
return new BluebirdPromise<string>(function (resolve, reject) {
|
||||
const url = Endpoints.FIRST_FACTOR_POST + "?" + Constants.REDIRECT_QUERY_PARAM + "=" + redirectUrl
|
||||
+ "&" + Constants.ONLY_BASIC_AUTH_QUERY_PARAM + "=" + onlyBasicAuth;
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: url,
|
||||
data: {
|
||||
username: username,
|
||||
password: password,
|
||||
}
|
||||
})
|
||||
.done(function () {
|
||||
resolve();
|
||||
.done(function (data: { redirect: string }) {
|
||||
resolve(data.redirect);
|
||||
})
|
||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
||||
reject(new Error("Authetication failed. Please check your credentials."));
|
||||
|
|
|
@ -3,7 +3,7 @@ import JSLogger = require("js-logger");
|
|||
import UISelectors = require("./UISelectors");
|
||||
import { Notifier } from "../Notifier";
|
||||
import { QueryParametersRetriever } from "../QueryParametersRetriever";
|
||||
|
||||
import Constants = require("../../../server/constants");
|
||||
import Endpoints = require("../../../server/endpoints");
|
||||
|
||||
export default function (window: Window, $: JQueryStatic,
|
||||
|
@ -15,20 +15,17 @@ export default function (window: Window, $: JQueryStatic,
|
|||
const username: string = $(UISelectors.USERNAME_FIELD_ID).val();
|
||||
const password: string = $(UISelectors.PASSWORD_FIELD_ID).val();
|
||||
$(UISelectors.PASSWORD_FIELD_ID).val("");
|
||||
jslogger.debug("Form submitted");
|
||||
firstFactorValidator.validate(username, password, $)
|
||||
|
||||
const redirectUrl = QueryParametersRetriever.get(Constants.REDIRECT_QUERY_PARAM);
|
||||
const onlyBasicAuth = QueryParametersRetriever.get(Constants.ONLY_BASIC_AUTH_QUERY_PARAM) ? true : false;
|
||||
firstFactorValidator.validate(username, password, redirectUrl, onlyBasicAuth, $)
|
||||
.then(onFirstFactorSuccess, onFirstFactorFailure);
|
||||
return false;
|
||||
}
|
||||
|
||||
function onFirstFactorSuccess() {
|
||||
function onFirstFactorSuccess(redirectUrl: string) {
|
||||
jslogger.debug("First factor validated.");
|
||||
const redirectUrl = QueryParametersRetriever.get("redirect");
|
||||
if (redirectUrl)
|
||||
window.location.href = Endpoints.SECOND_FACTOR_GET + "?redirect="
|
||||
+ encodeURIComponent(redirectUrl);
|
||||
else
|
||||
window.location.href = Endpoints.SECOND_FACTOR_GET;
|
||||
window.location.href = redirectUrl;
|
||||
}
|
||||
|
||||
function onFirstFactorFailure(err: Error) {
|
||||
|
|
|
@ -8,22 +8,23 @@ import Endpoints = require("../../../server/endpoints");
|
|||
import Constants = require("./constants");
|
||||
import { Notifier } from "../Notifier";
|
||||
import { QueryParametersRetriever } from "../QueryParametersRetriever";
|
||||
import ServerConstants = require("../../../server/constants");
|
||||
|
||||
|
||||
export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi) {
|
||||
const notifierTotp = new Notifier(".notification-totp", $);
|
||||
const notifierU2f = new Notifier(".notification-u2f", $);
|
||||
|
||||
function onAuthenticationSuccess(data: any) {
|
||||
const redirectUrl = QueryParametersRetriever.get("redirect");
|
||||
function onAuthenticationSuccess(data: any, notifier: Notifier) {
|
||||
const redirectUrl = QueryParametersRetriever.get(ServerConstants.REDIRECT_QUERY_PARAM);
|
||||
if (redirectUrl)
|
||||
window.location.href = redirectUrl;
|
||||
else
|
||||
window.location.href = Endpoints.FIRST_FACTOR_GET;
|
||||
notifier.success("Authentication succeeded. You can now access your services.");
|
||||
}
|
||||
|
||||
function onSecondFactorTotpSuccess(data: any) {
|
||||
onAuthenticationSuccess(data);
|
||||
onAuthenticationSuccess(data, notifierTotp);
|
||||
}
|
||||
|
||||
function onSecondFactorTotpFailure(err: Error) {
|
||||
|
@ -31,7 +32,7 @@ export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi)
|
|||
}
|
||||
|
||||
function onU2fAuthenticationSuccess(data: any) {
|
||||
onAuthenticationSuccess(data);
|
||||
onAuthenticationSuccess(data, notifierU2f);
|
||||
}
|
||||
|
||||
function onU2fAuthenticationFailure() {
|
||||
|
|
4
src/server/constants.ts
Normal file
4
src/server/constants.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
export const ONLY_BASIC_AUTH_QUERY_PARAM = "only_basic_auth";
|
||||
export const REDIRECT_QUERY_PARAM = "redirect";
|
|
@ -4,7 +4,7 @@ import BluebirdPromise = require("bluebird");
|
|||
|
||||
function replyWithError(res: express.Response, code: number, logger: Winston): (err: Error) => void {
|
||||
return function (err: Error): void {
|
||||
logger.error("Reply with error %d: %s", code, err);
|
||||
logger.error("Reply with error %d: %s", code, err.stack);
|
||||
res.status(code);
|
||||
res.send();
|
||||
};
|
||||
|
|
|
@ -93,6 +93,8 @@ export class AccessController implements IAccessController {
|
|||
}
|
||||
|
||||
isAccessAllowed(domain: string, resource: string, user: string, groups: string[]): boolean {
|
||||
if (!this.configuration) return true;
|
||||
|
||||
const allRules = this.getMatchingAllRules(domain, resource);
|
||||
const groupRules = this.getMatchingGroupRules(groups, domain, resource);
|
||||
const userRules = this.getMatchingUserRules(user, domain, resource);
|
||||
|
|
|
@ -188,7 +188,7 @@ export class Client implements IClient {
|
|||
this.logger.debug("LDAP: update password of user '%s'", username);
|
||||
return this.searchUserDn(username)
|
||||
.then(function (userDN: string) {
|
||||
this.client.modifyAsync(userDN, change);
|
||||
that.client.modifyAsync(userDN, change);
|
||||
})
|
||||
.then(function () {
|
||||
return that.client.unbindAsync();
|
||||
|
|
|
@ -12,22 +12,9 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
|
||||
logger.debug("First factor: headers are %s", JSON.stringify(req.headers));
|
||||
|
||||
return AuthenticationValidator.validate(req)
|
||||
.then(function () {
|
||||
const redirectUrl = req.query.redirect;
|
||||
if (redirectUrl) {
|
||||
res.redirect(redirectUrl);
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
||||
else {
|
||||
res.render("already-logged-in", { logout_endpoint: Endpoints.LOGOUT_GET });
|
||||
return BluebirdPromise.resolve();
|
||||
}
|
||||
}, function () {
|
||||
res.render("firstfactor", {
|
||||
first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST,
|
||||
reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET
|
||||
});
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
}
|
|
@ -10,6 +10,7 @@ import Endpoint = require("../../../endpoints");
|
|||
import ErrorReplies = require("../../ErrorReplies");
|
||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../AuthenticationSession");
|
||||
import Constants = require("../../../constants");
|
||||
|
||||
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const username: string = req.body.username;
|
||||
|
@ -47,6 +48,8 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
JSON.stringify(groupsAndEmails));
|
||||
authSession.userid = username;
|
||||
authSession.first_factor = true;
|
||||
const redirectUrl = req.query[Constants.REDIRECT_QUERY_PARAM];
|
||||
const onlyBasicAuth = req.query[Constants.ONLY_BASIC_AUTH_QUERY_PARAM] === "true";
|
||||
|
||||
const emails: string[] = groupsAndEmails.emails;
|
||||
const groups: string[] = groupsAndEmails.groups;
|
||||
|
@ -63,8 +66,25 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
logger.debug("1st factor: Mark successful authentication to regulator.");
|
||||
regulator.mark(username, true);
|
||||
|
||||
res.status(204);
|
||||
res.send();
|
||||
logger.debug("1st factor: Redirect URL is %s", redirectUrl);
|
||||
logger.debug("1st factor: %s? %s", Constants.ONLY_BASIC_AUTH_QUERY_PARAM, onlyBasicAuth);
|
||||
|
||||
if (onlyBasicAuth) {
|
||||
res.send({
|
||||
redirect: redirectUrl
|
||||
});
|
||||
logger.debug("1st factor: redirect to '%s'", redirectUrl);
|
||||
}
|
||||
else {
|
||||
let newRedirectUrl = Endpoint.SECOND_FACTOR_GET;
|
||||
if (redirectUrl !== "undefined") {
|
||||
newRedirectUrl += "?redirect=" + encodeURIComponent(redirectUrl);
|
||||
}
|
||||
logger.debug("1st factor: redirect to '%s'", newRedirectUrl, typeof redirectUrl);
|
||||
res.send({
|
||||
redirect: newRedirectUrl
|
||||
});
|
||||
}
|
||||
return BluebirdPromise.resolve();
|
||||
})
|
||||
.catch(exceptions.LdapSearchError, ErrorReplies.replyWithError500(res, logger))
|
||||
|
|
|
@ -16,7 +16,7 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
const newPassword = objectPath.get<express.Request, string>(req, "body.password");
|
||||
|
||||
return AuthenticationSession.get(req)
|
||||
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
|
||||
.then(function (_authSession) {
|
||||
authSession = _authSession;
|
||||
logger.info("POST reset-password: User %s wants to reset his/her password.",
|
||||
authSession.identity_check.userid);
|
||||
|
@ -27,7 +27,6 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
|||
res.send();
|
||||
return BluebirdPromise.reject(new Error("Bad challenge."));
|
||||
}
|
||||
|
||||
return ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
|
||||
})
|
||||
.then(function () {
|
||||
|
|
|
@ -8,35 +8,38 @@ import AuthenticationValidator = require("../../AuthenticationValidator");
|
|||
import ErrorReplies = require("../../ErrorReplies");
|
||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../AuthenticationSession");
|
||||
import Constants = require("../../../constants");
|
||||
|
||||
function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||
const accessController = ServerVariablesHandler.getAccessController(req.app);
|
||||
let authSession: AuthenticationSession.AuthenticationSession;
|
||||
|
||||
return AuthenticationSession.get(req)
|
||||
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
|
||||
authSession = _authSession;
|
||||
.then(function (authSession) {
|
||||
logger.debug("Verify: headers are %s", JSON.stringify(req.headers));
|
||||
res.set("Redirect", encodeURIComponent("https://" + req.headers["host"] + req.headers["x-original-uri"]));
|
||||
|
||||
return AuthenticationValidator.validate(req);
|
||||
})
|
||||
.then(function () {
|
||||
const username = authSession.userid;
|
||||
const groups = authSession.groups;
|
||||
const onlyBasicAuth = req.query[Constants.ONLY_BASIC_AUTH_QUERY_PARAM] === "true";
|
||||
logger.debug("Verify: %s=%s", Constants.ONLY_BASIC_AUTH_QUERY_PARAM, onlyBasicAuth);
|
||||
|
||||
const host = objectPath.get<express.Request, string>(req, "headers.host");
|
||||
const path = objectPath.get<express.Request, string>(req, "headers.x-original-uri");
|
||||
|
||||
const domain = host.split(":")[0];
|
||||
logger.debug("Verify: domain=%s, path=%s", domain, path);
|
||||
logger.debug("Verify: user=%s, groups=%s", username, groups.join(","));
|
||||
|
||||
if (!authSession.first_factor)
|
||||
return BluebirdPromise.reject(new exceptions.AccessDeniedError("First factor not validated."));
|
||||
|
||||
const isAllowed = accessController.isAccessAllowed(domain, path, username, groups);
|
||||
if (!isAllowed) return BluebirdPromise.reject(
|
||||
new exceptions.DomainAccessDenied("User '" + username + "' does not have access to " + domain));
|
||||
|
||||
if (!authSession.first_factor || !authSession.second_factor)
|
||||
return BluebirdPromise.reject(new exceptions.AccessDeniedError("First or second factor not validated"));
|
||||
if (!onlyBasicAuth && !authSession.second_factor)
|
||||
return BluebirdPromise.reject(new exceptions.AccessDeniedError("Second factor not validated."));
|
||||
|
||||
res.setHeader("Remote-User", username);
|
||||
res.setHeader("Remote-Groups", groups.join(","));
|
||||
|
|
|
@ -16,6 +16,7 @@ Feature: User has access restricted access to domains
|
|||
| https://dev.test.local:8080/users/bob/secret.html |
|
||||
| https://admin.test.local:8080/secret.html |
|
||||
| https://mx1.mail.test.local:8080/secret.html |
|
||||
| https://basicauth.test.local:8080/secret.html |
|
||||
And I have no access to:
|
||||
| url |
|
||||
| https://mx2.mail.test.local:8080/secret.html |
|
||||
|
@ -39,6 +40,7 @@ Feature: User has access restricted access to domains
|
|||
| https://admin.test.local:8080/secret.html |
|
||||
| https://dev.test.local:8080/users/john/secret.html |
|
||||
| https://dev.test.local:8080/users/harry/secret.html |
|
||||
| https://basicauth.test.local:8080/secret.html |
|
||||
|
||||
@need-registered-user-harry
|
||||
Scenario: User harry has restricted access
|
||||
|
@ -59,3 +61,4 @@ Feature: User has access restricted access to domains
|
|||
| https://dev.test.local:8080/users/john/secret.html |
|
||||
| https://mx1.mail.test.local:8080/secret.html |
|
||||
| https://mx2.mail.test.local:8080/secret.html |
|
||||
| https://basicauth.test.local:8080/secret.html |
|
||||
|
|
19
test/features/basic-auth.feature
Normal file
19
test/features/basic-auth.feature
Normal file
|
@ -0,0 +1,19 @@
|
|||
Feature: User can access certain subdomains with basic auth
|
||||
|
||||
@need-registered-user-john
|
||||
Scenario: User is redirected to service after first factor if allowed
|
||||
When I visit "https://auth.test.local:8080/?redirect=https%3A%2F%2Fbasicauth.test.local%3A8080%2Fsecret.html&only_basic_auth=true"
|
||||
And I login with user "john" and password "password"
|
||||
Then I'm redirected to "https://basicauth.test.local:8080/secret.html"
|
||||
|
||||
@need-registered-user-john
|
||||
Scenario: Redirection after first factor fails if basic_auth not allowed. It redirects user to first factor.
|
||||
When I visit "https://auth.test.local:8080/?redirect=https%3A%2F%2Fadmin.test.local%3A8080%2Fsecret.html&only_basic_auth=true"
|
||||
And I login with user "john" and password "password"
|
||||
Then I'm redirected to "https://auth.test.local:8080/?redirect=https%3A%2F%2Fadmin.test.local%3A8080%2Fsecret.html"
|
||||
|
||||
@need-registered-user-john
|
||||
Scenario: User is redirected to second factor after first factor
|
||||
When I visit "https://auth.test.local:8080/?redirect=https%3A%2F%2Fadmin.test.local%3A8080%2Fsecret.html"
|
||||
And I login with user "john" and password "password"
|
||||
Then I'm redirected to "https://auth.test.local:8080/secondfactor?redirect=https%3A%2F%2Fadmin.test.local%3A8080%2Fsecret.html"
|
|
@ -2,7 +2,7 @@ Feature: User is correctly redirected
|
|||
|
||||
Scenario: User is redirected to authelia when he is not authenticated
|
||||
When I visit "https://public.test.local:8080"
|
||||
Then I'm redirected to "https://auth.test.local:8080/"
|
||||
Then I'm redirected to "https://auth.test.local:8080/?redirect=https%3A%2F%2Fpublic.test.local%3A8080%2F"
|
||||
|
||||
@need-registered-user-john
|
||||
Scenario: User is redirected to home page after several authentication tries
|
||||
|
|
|
@ -14,6 +14,4 @@ Feature: Authelia keeps user sessions despite the application restart
|
|||
And I login with user "john" and password "password"
|
||||
And I use "REGISTERED" as TOTP token handle
|
||||
And I click on "TOTP"
|
||||
Then I have access to:
|
||||
| url |
|
||||
| https://admin.test.local:8080/secret.html |
|
||||
Then I'm redirected to "https://admin.test.local:8080/secret.html"
|
|
@ -6,7 +6,7 @@ import Speakeasy = require("speakeasy");
|
|||
import CustomWorld = require("../support/world");
|
||||
|
||||
Cucumber.defineSupportCode(function ({ Given, When, Then }) {
|
||||
When(/^I visit "(https:\/\/[a-zA-Z0-9:%.\/=?-]+)"$/, function (link: string) {
|
||||
When(/^I visit "(https:\/\/[a-zA-Z0-9:%&._\/=?-]+)"$/, function (link: string) {
|
||||
return this.visit(link);
|
||||
});
|
||||
|
||||
|
@ -66,10 +66,7 @@ and I use TOTP token handle {stringInDoubleQuotes}",
|
|||
function hasAccessToSecret(link: string, that: any) {
|
||||
return that.driver.get(link)
|
||||
.then(function () {
|
||||
return that.driver.findElement(seleniumWebdriver.By.tagName("body")).getText()
|
||||
.then(function (body: string) {
|
||||
Assert(body.indexOf("This is a very important secret!") > -1, body);
|
||||
});
|
||||
return that.waitUntilUrlContains(link);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,6 @@ Cucumber.defineSupportCode(function ({ Given, When, Then }) {
|
|||
});
|
||||
|
||||
Then("I'm redirected to {stringInDoubleQuotes}", function (link: string) {
|
||||
return this.driver.wait(seleniumWebdriver.until.urlContains(link), 15000);
|
||||
return this.waitUntilUrlContains(link);
|
||||
});
|
||||
});
|
|
@ -55,6 +55,10 @@ function CustomWorld() {
|
|||
});
|
||||
};
|
||||
|
||||
this.waitUntilUrlContains = function(url: string) {
|
||||
return this.driver.wait(seleniumWebdriver.until.urlIs(url), 15000);
|
||||
};
|
||||
|
||||
this.loginWithUserPassword = function (username: string, password: string) {
|
||||
return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id("username")), 4000)
|
||||
.then(function () {
|
||||
|
|
|
@ -7,13 +7,13 @@ import Assert = require("assert");
|
|||
describe("test FirstFactorValidator", function () {
|
||||
it("should validate first factor successfully", () => {
|
||||
const postPromise = JQueryMock.JQueryDeferredMock();
|
||||
postPromise.done.yields();
|
||||
postPromise.done.yields({ redirect: "http://redirect" });
|
||||
postPromise.done.returns(postPromise);
|
||||
|
||||
const jqueryMock = JQueryMock.JQueryMock();
|
||||
jqueryMock.jquery.post.returns(postPromise);
|
||||
jqueryMock.jquery.ajax.returns(postPromise);
|
||||
|
||||
return FirstFactorValidator.validate("username", "password", jqueryMock.jquery as any);
|
||||
return FirstFactorValidator.validate("username", "password", "http://redirect", false, jqueryMock.jquery as any);
|
||||
});
|
||||
|
||||
function should_fail_first_factor_validation(errorMessage: string) {
|
||||
|
@ -25,9 +25,9 @@ describe("test FirstFactorValidator", function () {
|
|||
postPromise.done.returns(postPromise);
|
||||
|
||||
const jqueryMock = JQueryMock.JQueryMock();
|
||||
jqueryMock.jquery.post.returns(postPromise);
|
||||
jqueryMock.jquery.ajax.returns(postPromise);
|
||||
|
||||
return FirstFactorValidator.validate("username", "password", jqueryMock.jquery as any)
|
||||
return FirstFactorValidator.validate("username", "password", "http://redirect", false, jqueryMock.jquery as any)
|
||||
.then(function () {
|
||||
return BluebirdPromise.reject(new Error("First factor validation successfully finished while it should have not."));
|
||||
}, function (err: Error) {
|
||||
|
|
|
@ -8,6 +8,19 @@ describe("test access control manager", function () {
|
|||
let accessController: AccessController;
|
||||
let configuration: ACLConfiguration;
|
||||
|
||||
describe("configuration is null", function() {
|
||||
it("should allow access to anything, anywhere for anybody", function() {
|
||||
configuration = undefined;
|
||||
accessController = new AccessController(configuration, winston);
|
||||
|
||||
Assert(accessController.isAccessAllowed("home.test.local", "/", "user1", ["group1", "group2"]));
|
||||
Assert(accessController.isAccessAllowed("home.test.local", "/abc", "user1", ["group1", "group2"]));
|
||||
Assert(accessController.isAccessAllowed("home.test.local", "/", "user2", ["group1", "group2"]));
|
||||
Assert(accessController.isAccessAllowed("admin.test.local", "/", "user3", ["group3"]));
|
||||
});
|
||||
});
|
||||
|
||||
describe("configuration is not null", function () {
|
||||
beforeEach(function () {
|
||||
configuration = {
|
||||
default_policy: "deny",
|
||||
|
@ -351,3 +364,4 @@ describe("test access control manager", function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import sinon = require("sinon");
|
||||
import Sinon = require("sinon");
|
||||
import express = require("express");
|
||||
import winston = require("winston");
|
||||
import { UserDataStoreStub } from "./storage/UserDataStoreStub";
|
||||
import { VARIABLES_KEY } from "../../../../src/server/lib/ServerVariablesHandler";
|
||||
import { VARIABLES_KEY } from "../../../../src/server/lib/ServerVariablesHandler";
|
||||
|
||||
export interface ServerVariablesMock {
|
||||
logger: any;
|
||||
|
@ -22,19 +22,19 @@ export interface ServerVariablesMock {
|
|||
|
||||
export function mock(app: express.Application): ServerVariablesMock {
|
||||
const mocks: ServerVariablesMock = {
|
||||
accessController: sinon.stub(),
|
||||
config: sinon.stub(),
|
||||
ldapAuthenticator: sinon.stub() as any,
|
||||
ldapEmailsRetriever: sinon.stub() as any,
|
||||
ldapPasswordUpdater: sinon.stub() as any,
|
||||
accessController: Sinon.stub(),
|
||||
config: Sinon.stub(),
|
||||
ldapAuthenticator: Sinon.stub() as any,
|
||||
ldapEmailsRetriever: Sinon.stub() as any,
|
||||
ldapPasswordUpdater: Sinon.stub() as any,
|
||||
logger: winston,
|
||||
notifier: sinon.stub(),
|
||||
regulator: sinon.stub(),
|
||||
totpGenerator: sinon.stub(),
|
||||
totpValidator: sinon.stub(),
|
||||
u2f: sinon.stub(),
|
||||
notifier: Sinon.stub(),
|
||||
regulator: Sinon.stub(),
|
||||
totpGenerator: Sinon.stub(),
|
||||
totpValidator: Sinon.stub(),
|
||||
u2f: Sinon.stub(),
|
||||
userDataStore: new UserDataStoreStub()
|
||||
};
|
||||
app.get = sinon.stub().withArgs(VARIABLES_KEY).returns(mocks);
|
||||
app.get = Sinon.stub().withArgs(VARIABLES_KEY).returns(mocks);
|
||||
return mocks;
|
||||
}
|
|
@ -51,6 +51,9 @@ describe("test the first factor validation route", function () {
|
|||
username: "username",
|
||||
password: "password"
|
||||
},
|
||||
query: {
|
||||
redirect: "http://redirect.url"
|
||||
},
|
||||
session: {
|
||||
},
|
||||
headers: {
|
||||
|
@ -87,7 +90,6 @@ describe("test the first factor validation route", function () {
|
|||
.then(function () {
|
||||
assert.equal("username", authSession.userid);
|
||||
assert(res.send.calledOnce);
|
||||
assert(res.status.calledWith(204));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
import assert = require("assert");
|
||||
import Assert = require("assert");
|
||||
import VerifyGet = require("../../../../../src/server/lib/routes/verify/get");
|
||||
import AuthenticationSession = require("../../../../../src/server/lib/AuthenticationSession");
|
||||
|
||||
import sinon = require("sinon");
|
||||
import Sinon = require("sinon");
|
||||
import winston = require("winston");
|
||||
import BluebirdPromise = require("bluebird");
|
||||
|
||||
|
@ -24,10 +24,13 @@ describe("test authentication token verification", function () {
|
|||
|
||||
req = ExpressMock.RequestMock();
|
||||
res = ExpressMock.ResponseMock();
|
||||
req.app = {
|
||||
get: sinon.stub().returns({ logger: winston })
|
||||
};
|
||||
req.session = {};
|
||||
req.query = {
|
||||
redirect: "http://redirect.url"
|
||||
};
|
||||
req.app = {
|
||||
get: Sinon.stub().returns({ logger: winston })
|
||||
};
|
||||
AuthenticationSession.reset(req as any);
|
||||
req.headers = {};
|
||||
req.headers.host = "secret.example.com";
|
||||
|
@ -49,17 +52,20 @@ describe("test authentication token verification", function () {
|
|||
return VerifyGet.default(req as express.Request, res as any);
|
||||
})
|
||||
.then(function () {
|
||||
sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
||||
sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
||||
assert.equal(204, res.status.getCall(0).args[0]);
|
||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
||||
Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
||||
Assert.equal(204, res.status.getCall(0).args[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("given different cases of session", function () {
|
||||
function test_session(auth_session: AuthenticationSession.AuthenticationSession, status_code: number) {
|
||||
return VerifyGet.default(req as express.Request, res as any)
|
||||
function test_session(_authSession: AuthenticationSession.AuthenticationSession, status_code: number) {
|
||||
return AuthenticationSession.get(req as any)
|
||||
.then(function (authSession) {
|
||||
authSession = _authSession;
|
||||
return VerifyGet.default(req as express.Request, res as any);
|
||||
})
|
||||
.then(function () {
|
||||
assert.equal(status_code, res.status.getCall(0).args[0]);
|
||||
Assert.equal(status_code, res.status.getCall(0).args[0]);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -75,6 +81,8 @@ describe("test authentication token verification", function () {
|
|||
return test_session(auth_session, 204);
|
||||
}
|
||||
|
||||
describe("given user tries to access a 2-factor endpoint", function () {
|
||||
describe("given different cases of session", function () {
|
||||
it("should not be authenticated when second factor is missing", function () {
|
||||
return test_non_authenticated_401({
|
||||
userid: "user",
|
||||
|
@ -143,3 +151,36 @@ describe("test authentication token verification", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("given user tries to access a basic auth endpoint", function () {
|
||||
beforeEach(function () {
|
||||
req.query = {
|
||||
redirect: "http://redirect.url",
|
||||
only_basic_auth: "true"
|
||||
};
|
||||
});
|
||||
|
||||
it("should be authenticated when first factor is validated and not second factor", function () {
|
||||
return AuthenticationSession.get(req as any)
|
||||
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
|
||||
authSession.first_factor = true;
|
||||
return VerifyGet.default(req as express.Request, res as any);
|
||||
})
|
||||
.then(function () {
|
||||
Assert(res.status.calledWith(204));
|
||||
Assert(res.send.calledOnce);
|
||||
});
|
||||
});
|
||||
|
||||
it("should be rejected with 401 when first factor is not validated", function () {
|
||||
return AuthenticationSession.get(req as any)
|
||||
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
|
||||
authSession.first_factor = false;
|
||||
return VerifyGet.default(req as express.Request, res as any);
|
||||
})
|
||||
.then(function () {
|
||||
Assert(res.status.calledWith(401));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user