Merge pull request #98 from clems4ever/disable-second-factor
Allow basic auth for certain subdomainspull/105/head
commit
89de19bb35
15
.travis.yml
15
.travis.yml
|
@ -15,13 +15,14 @@ addons:
|
||||||
- libgif-dev
|
- libgif-dev
|
||||||
- google-chrome-stable
|
- google-chrome-stable
|
||||||
hosts:
|
hosts:
|
||||||
- auth.test.local
|
- admin.test.local
|
||||||
- home.test.local
|
- auth.test.local
|
||||||
- public.test.local
|
- basicauth.test.local
|
||||||
- admin.test.local
|
- dev.test.local
|
||||||
- dev.test.local
|
- home.test.local
|
||||||
- mx1.mail.test.local
|
- mx1.mail.test.local
|
||||||
- mx2.mail.test.local
|
- mx2.mail.test.local
|
||||||
|
- public.test.local
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- npm install -g npm@'>=2.13.5'
|
- npm install -g npm@'>=2.13.5'
|
||||||
|
|
|
@ -26,7 +26,7 @@ module.exports = function (grunt) {
|
||||||
},
|
},
|
||||||
"docker-restart": {
|
"docker-restart": {
|
||||||
cmd: "./scripts/dc-dev.sh",
|
cmd: "./scripts/dc-dev.sh",
|
||||||
args: ['up', '-d']
|
args: ['restart', 'authelia']
|
||||||
},
|
},
|
||||||
"minify": {
|
"minify": {
|
||||||
cmd: "./node_modules/.bin/uglifyjs",
|
cmd: "./node_modules/.bin/uglifyjs",
|
||||||
|
|
|
@ -5,6 +5,6 @@ services:
|
||||||
- ./test:/usr/src/test
|
- ./test:/usr/src/test
|
||||||
- ./dist/src/server:/usr/src
|
- ./dist/src/server:/usr/src
|
||||||
- ./node_modules:/usr/src/node_modules
|
- ./node_modules:/usr/src/node_modules
|
||||||
- ./config.yml:/etc/authelia/config.yml:ro
|
- ./config.template.yml:/etc/authelia/config.yml:ro
|
||||||
networks:
|
networks:
|
||||||
- example-network
|
- example-network
|
||||||
|
|
|
@ -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>
|
<body>
|
||||||
<h1>Access the secret</h1>
|
<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/>
|
<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
|
||||||
Try to access it using one of the following links to test access control powered by Authelia.<br/>
|
one of the following links to test access control powered by Authelia.<br/>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
public.test.local <a href="https://public.test.local:8080/"> / index.html</a>
|
public.test.local <a href="https://public.test.local:8080/"> / index.html</a>
|
||||||
|
@ -51,17 +51,19 @@
|
||||||
<li>
|
<li>
|
||||||
mx2.main.test.local <a href="https://mx2.mail.test.local:8080/secret.html"> / secret.html</a>
|
mx2.main.test.local <a href="https://mx2.mail.test.local:8080/secret.html"> / secret.html</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
basicauth.test.local <a href="https://basicauth.test.local:8080/secret.html"> / secret.html</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</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>.
|
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>
|
<h1>List of users</h1>
|
||||||
Here is the list of credentials you can log in with to test access control.<br/>
|
Here is the list of credentials you can log in with to test access control.<br/>
|
||||||
<br/>
|
<br/> Once first factor is passed, you will need to follow the links to register a secret for the second factor.<br/> Authelia
|
||||||
Once first factor is passed, you will need to follow the links to register a secret for the second factor.<br/>
|
will send you a fictituous email that will be in the file
|
||||||
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
|
||||||
<strong>/tmp/notifications/notification.txt</strong>.<br/>
|
allowing you to authenticate with 2-factor.
|
||||||
It will provide you with the link to complete the registration allowing you to authenticate with 2-factor.
|
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>john / password</strong>: belongs to <em>admin</em> and <em>dev</em> groups.</li>
|
<li><strong>john / password</strong>: belongs to <em>admin</em> and <em>dev</em> groups.</li>
|
||||||
|
@ -70,7 +72,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h1>Access control rules</h1>
|
<h1>Access control rules</h1>
|
||||||
<p></p>These rules are extracted from the configuration file
|
<p></p>These rules are extracted from the configuration file
|
||||||
<a href="https://github.com/clems4ever/authelia/blob/master/config.template.yml">config.template.yml</a>.</p>
|
<a href="https://github.com/clems4ever/authelia/blob/master/config.template.yml">config.template.yml</a>.</p>
|
||||||
<pre id="rules" style="border: 1px grey solid; padding: 20px; display: inline-block;">
|
<pre id="rules" style="border: 1px grey solid; padding: 20px; display: inline-block;">
|
||||||
# Default policy can either be `allow` or `deny`.
|
# Default policy can either be `allow` or `deny`.
|
||||||
|
@ -129,4 +131,5 @@ users:
|
||||||
resources:
|
resources:
|
||||||
- '^/users/harry/.*$'</pre>
|
- '^/users/harry/.*$'</pre>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -203,5 +203,42 @@ http {
|
||||||
error_page 403 = https://auth.test.local:8080/error/403;
|
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,18 +1,27 @@
|
||||||
|
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import Endpoints = require("../../../server/endpoints");
|
import Endpoints = require("../../../server/endpoints");
|
||||||
|
import Constants = require("../../../server/constants");
|
||||||
|
|
||||||
export function validate(username: string, password: string, $: JQueryStatic): BluebirdPromise<void> {
|
export function validate(username: string, password: string,
|
||||||
return new BluebirdPromise<void>(function (resolve, reject) {
|
redirectUrl: string, onlyBasicAuth: boolean, $: JQueryStatic): BluebirdPromise<string> {
|
||||||
$.post(Endpoints.FIRST_FACTOR_POST, {
|
return new BluebirdPromise<string>(function (resolve, reject) {
|
||||||
username: username,
|
const url = Endpoints.FIRST_FACTOR_POST + "?" + Constants.REDIRECT_QUERY_PARAM + "=" + redirectUrl
|
||||||
password: password,
|
+ "&" + Constants.ONLY_BASIC_AUTH_QUERY_PARAM + "=" + onlyBasicAuth;
|
||||||
})
|
|
||||||
.done(function () {
|
$.ajax({
|
||||||
resolve();
|
method: "POST",
|
||||||
})
|
url: url,
|
||||||
.fail(function (xhr: JQueryXHR, textStatus: string) {
|
data: {
|
||||||
reject(new Error("Authetication failed. Please check your credentials."));
|
username: username,
|
||||||
});
|
password: password,
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
.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 UISelectors = require("./UISelectors");
|
||||||
import { Notifier } from "../Notifier";
|
import { Notifier } from "../Notifier";
|
||||||
import { QueryParametersRetriever } from "../QueryParametersRetriever";
|
import { QueryParametersRetriever } from "../QueryParametersRetriever";
|
||||||
|
import Constants = require("../../../server/constants");
|
||||||
import Endpoints = require("../../../server/endpoints");
|
import Endpoints = require("../../../server/endpoints");
|
||||||
|
|
||||||
export default function (window: Window, $: JQueryStatic,
|
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 username: string = $(UISelectors.USERNAME_FIELD_ID).val();
|
||||||
const password: string = $(UISelectors.PASSWORD_FIELD_ID).val();
|
const password: string = $(UISelectors.PASSWORD_FIELD_ID).val();
|
||||||
$(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);
|
.then(onFirstFactorSuccess, onFirstFactorFailure);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFirstFactorSuccess() {
|
function onFirstFactorSuccess(redirectUrl: string) {
|
||||||
jslogger.debug("First factor validated.");
|
jslogger.debug("First factor validated.");
|
||||||
const redirectUrl = QueryParametersRetriever.get("redirect");
|
window.location.href = redirectUrl;
|
||||||
if (redirectUrl)
|
|
||||||
window.location.href = Endpoints.SECOND_FACTOR_GET + "?redirect="
|
|
||||||
+ encodeURIComponent(redirectUrl);
|
|
||||||
else
|
|
||||||
window.location.href = Endpoints.SECOND_FACTOR_GET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFirstFactorFailure(err: Error) {
|
function onFirstFactorFailure(err: Error) {
|
||||||
|
|
|
@ -8,22 +8,23 @@ import Endpoints = require("../../../server/endpoints");
|
||||||
import Constants = require("./constants");
|
import Constants = require("./constants");
|
||||||
import { Notifier } from "../Notifier";
|
import { Notifier } from "../Notifier";
|
||||||
import { QueryParametersRetriever } from "../QueryParametersRetriever";
|
import { QueryParametersRetriever } from "../QueryParametersRetriever";
|
||||||
|
import ServerConstants = require("../../../server/constants");
|
||||||
|
|
||||||
|
|
||||||
export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi) {
|
export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi) {
|
||||||
const notifierTotp = new Notifier(".notification-totp", $);
|
const notifierTotp = new Notifier(".notification-totp", $);
|
||||||
const notifierU2f = new Notifier(".notification-u2f", $);
|
const notifierU2f = new Notifier(".notification-u2f", $);
|
||||||
|
|
||||||
function onAuthenticationSuccess(data: any) {
|
function onAuthenticationSuccess(data: any, notifier: Notifier) {
|
||||||
const redirectUrl = QueryParametersRetriever.get("redirect");
|
const redirectUrl = QueryParametersRetriever.get(ServerConstants.REDIRECT_QUERY_PARAM);
|
||||||
if (redirectUrl)
|
if (redirectUrl)
|
||||||
window.location.href = redirectUrl;
|
window.location.href = redirectUrl;
|
||||||
else
|
else
|
||||||
window.location.href = Endpoints.FIRST_FACTOR_GET;
|
notifier.success("Authentication succeeded. You can now access your services.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSecondFactorTotpSuccess(data: any) {
|
function onSecondFactorTotpSuccess(data: any) {
|
||||||
onAuthenticationSuccess(data);
|
onAuthenticationSuccess(data, notifierTotp);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSecondFactorTotpFailure(err: Error) {
|
function onSecondFactorTotpFailure(err: Error) {
|
||||||
|
@ -31,7 +32,7 @@ export default function (window: Window, $: JQueryStatic, u2fApi: typeof U2fApi)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onU2fAuthenticationSuccess(data: any) {
|
function onU2fAuthenticationSuccess(data: any) {
|
||||||
onAuthenticationSuccess(data);
|
onAuthenticationSuccess(data, notifierU2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onU2fAuthenticationFailure() {
|
function onU2fAuthenticationFailure() {
|
||||||
|
|
|
@ -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 {
|
function replyWithError(res: express.Response, code: number, logger: Winston): (err: Error) => void {
|
||||||
return function (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.status(code);
|
||||||
res.send();
|
res.send();
|
||||||
};
|
};
|
||||||
|
|
|
@ -93,6 +93,8 @@ export class AccessController implements IAccessController {
|
||||||
}
|
}
|
||||||
|
|
||||||
isAccessAllowed(domain: string, resource: string, user: string, groups: string[]): boolean {
|
isAccessAllowed(domain: string, resource: string, user: string, groups: string[]): boolean {
|
||||||
|
if (!this.configuration) return true;
|
||||||
|
|
||||||
const allRules = this.getMatchingAllRules(domain, resource);
|
const allRules = this.getMatchingAllRules(domain, resource);
|
||||||
const groupRules = this.getMatchingGroupRules(groups, domain, resource);
|
const groupRules = this.getMatchingGroupRules(groups, domain, resource);
|
||||||
const userRules = this.getMatchingUserRules(user, 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);
|
this.logger.debug("LDAP: update password of user '%s'", username);
|
||||||
return this.searchUserDn(username)
|
return this.searchUserDn(username)
|
||||||
.then(function (userDN: string) {
|
.then(function (userDN: string) {
|
||||||
this.client.modifyAsync(userDN, change);
|
that.client.modifyAsync(userDN, change);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return that.client.unbindAsync();
|
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));
|
logger.debug("First factor: headers are %s", JSON.stringify(req.headers));
|
||||||
|
|
||||||
return AuthenticationValidator.validate(req)
|
res.render("firstfactor", {
|
||||||
.then(function () {
|
first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST,
|
||||||
const redirectUrl = req.query.redirect;
|
reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET
|
||||||
if (redirectUrl) {
|
});
|
||||||
res.redirect(redirectUrl);
|
return BluebirdPromise.resolve();
|
||||||
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 ErrorReplies = require("../../ErrorReplies");
|
||||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||||
import AuthenticationSession = require("../../AuthenticationSession");
|
import AuthenticationSession = require("../../AuthenticationSession");
|
||||||
|
import Constants = require("../../../constants");
|
||||||
|
|
||||||
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
const username: string = req.body.username;
|
const username: string = req.body.username;
|
||||||
|
@ -47,6 +48,8 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
||||||
JSON.stringify(groupsAndEmails));
|
JSON.stringify(groupsAndEmails));
|
||||||
authSession.userid = username;
|
authSession.userid = username;
|
||||||
authSession.first_factor = true;
|
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 emails: string[] = groupsAndEmails.emails;
|
||||||
const groups: string[] = groupsAndEmails.groups;
|
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.");
|
logger.debug("1st factor: Mark successful authentication to regulator.");
|
||||||
regulator.mark(username, true);
|
regulator.mark(username, true);
|
||||||
|
|
||||||
res.status(204);
|
logger.debug("1st factor: Redirect URL is %s", redirectUrl);
|
||||||
res.send();
|
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();
|
return BluebirdPromise.resolve();
|
||||||
})
|
})
|
||||||
.catch(exceptions.LdapSearchError, ErrorReplies.replyWithError500(res, logger))
|
.catch(exceptions.LdapSearchError, ErrorReplies.replyWithError500(res, logger))
|
||||||
|
|
|
@ -10,33 +10,32 @@ import ErrorReplies = require("../../../ErrorReplies");
|
||||||
import Constants = require("./../constants");
|
import Constants = require("./../constants");
|
||||||
|
|
||||||
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||||
const ldapPasswordUpdater = ServerVariablesHandler.getLdapPasswordUpdater(req.app);
|
const ldapPasswordUpdater = ServerVariablesHandler.getLdapPasswordUpdater(req.app);
|
||||||
let authSession: AuthenticationSession.AuthenticationSession;
|
let authSession: AuthenticationSession.AuthenticationSession;
|
||||||
const newPassword = objectPath.get<express.Request, string>(req, "body.password");
|
const newPassword = objectPath.get<express.Request, string>(req, "body.password");
|
||||||
|
|
||||||
return AuthenticationSession.get(req)
|
return AuthenticationSession.get(req)
|
||||||
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
|
.then(function (_authSession) {
|
||||||
authSession = _authSession;
|
authSession = _authSession;
|
||||||
logger.info("POST reset-password: User %s wants to reset his/her password.",
|
logger.info("POST reset-password: User %s wants to reset his/her password.",
|
||||||
authSession.identity_check.userid);
|
authSession.identity_check.userid);
|
||||||
logger.info("POST reset-password: Challenge %s", authSession.identity_check.challenge);
|
logger.info("POST reset-password: Challenge %s", authSession.identity_check.challenge);
|
||||||
|
|
||||||
if (authSession.identity_check.challenge != Constants.CHALLENGE) {
|
if (authSession.identity_check.challenge != Constants.CHALLENGE) {
|
||||||
res.status(403);
|
res.status(403);
|
||||||
res.send();
|
res.send();
|
||||||
return BluebirdPromise.reject(new Error("Bad challenge."));
|
return BluebirdPromise.reject(new Error("Bad challenge."));
|
||||||
}
|
}
|
||||||
|
return ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
|
||||||
return ldapPasswordUpdater.updatePassword(authSession.identity_check.userid, newPassword);
|
})
|
||||||
})
|
.then(function () {
|
||||||
.then(function () {
|
logger.info("POST reset-password: Password reset for user '%s'",
|
||||||
logger.info("POST reset-password: Password reset for user '%s'",
|
authSession.identity_check.userid);
|
||||||
authSession.identity_check.userid);
|
AuthenticationSession.reset(req);
|
||||||
AuthenticationSession.reset(req);
|
res.status(204);
|
||||||
res.status(204);
|
res.send();
|
||||||
res.send();
|
return BluebirdPromise.resolve();
|
||||||
return BluebirdPromise.resolve();
|
})
|
||||||
})
|
.catch(ErrorReplies.replyWithError500(res, logger));
|
||||||
.catch(ErrorReplies.replyWithError500(res, logger));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,35 +8,38 @@ import AuthenticationValidator = require("../../AuthenticationValidator");
|
||||||
import ErrorReplies = require("../../ErrorReplies");
|
import ErrorReplies = require("../../ErrorReplies");
|
||||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||||
import AuthenticationSession = require("../../AuthenticationSession");
|
import AuthenticationSession = require("../../AuthenticationSession");
|
||||||
|
import Constants = require("../../../constants");
|
||||||
|
|
||||||
function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
const logger = ServerVariablesHandler.getLogger(req.app);
|
const logger = ServerVariablesHandler.getLogger(req.app);
|
||||||
const accessController = ServerVariablesHandler.getAccessController(req.app);
|
const accessController = ServerVariablesHandler.getAccessController(req.app);
|
||||||
let authSession: AuthenticationSession.AuthenticationSession;
|
|
||||||
|
|
||||||
return AuthenticationSession.get(req)
|
return AuthenticationSession.get(req)
|
||||||
.then(function (_authSession: AuthenticationSession.AuthenticationSession) {
|
.then(function (authSession) {
|
||||||
authSession = _authSession;
|
|
||||||
logger.debug("Verify: headers are %s", JSON.stringify(req.headers));
|
logger.debug("Verify: headers are %s", JSON.stringify(req.headers));
|
||||||
res.set("Redirect", encodeURIComponent("https://" + req.headers["host"] + req.headers["x-original-uri"]));
|
res.set("Redirect", encodeURIComponent("https://" + req.headers["host"] + req.headers["x-original-uri"]));
|
||||||
|
|
||||||
return AuthenticationValidator.validate(req);
|
|
||||||
})
|
|
||||||
.then(function () {
|
|
||||||
const username = authSession.userid;
|
const username = authSession.userid;
|
||||||
const groups = authSession.groups;
|
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 host = objectPath.get<express.Request, string>(req, "headers.host");
|
||||||
const path = objectPath.get<express.Request, string>(req, "headers.x-original-uri");
|
const path = objectPath.get<express.Request, string>(req, "headers.x-original-uri");
|
||||||
|
|
||||||
const domain = host.split(":")[0];
|
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);
|
const isAllowed = accessController.isAccessAllowed(domain, path, username, groups);
|
||||||
if (!isAllowed) return BluebirdPromise.reject(
|
if (!isAllowed) return BluebirdPromise.reject(
|
||||||
new exceptions.DomainAccessDenied("User '" + username + "' does not have access to " + domain));
|
new exceptions.DomainAccessDenied("User '" + username + "' does not have access to " + domain));
|
||||||
|
|
||||||
if (!authSession.first_factor || !authSession.second_factor)
|
if (!onlyBasicAuth && !authSession.second_factor)
|
||||||
return BluebirdPromise.reject(new exceptions.AccessDeniedError("First or second factor not validated"));
|
return BluebirdPromise.reject(new exceptions.AccessDeniedError("Second factor not validated."));
|
||||||
|
|
||||||
res.setHeader("Remote-User", username);
|
res.setHeader("Remote-User", username);
|
||||||
res.setHeader("Remote-Groups", groups.join(","));
|
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://dev.test.local:8080/users/bob/secret.html |
|
||||||
| https://admin.test.local:8080/secret.html |
|
| https://admin.test.local:8080/secret.html |
|
||||||
| https://mx1.mail.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:
|
And I have no access to:
|
||||||
| url |
|
| url |
|
||||||
| https://mx2.mail.test.local:8080/secret.html |
|
| 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://admin.test.local:8080/secret.html |
|
||||||
| https://dev.test.local:8080/users/john/secret.html |
|
| https://dev.test.local:8080/users/john/secret.html |
|
||||||
| https://dev.test.local:8080/users/harry/secret.html |
|
| https://dev.test.local:8080/users/harry/secret.html |
|
||||||
|
| https://basicauth.test.local:8080/secret.html |
|
||||||
|
|
||||||
@need-registered-user-harry
|
@need-registered-user-harry
|
||||||
Scenario: User harry has restricted access
|
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://dev.test.local:8080/users/john/secret.html |
|
||||||
| https://mx1.mail.test.local:8080/secret.html |
|
| https://mx1.mail.test.local:8080/secret.html |
|
||||||
| https://mx2.mail.test.local:8080/secret.html |
|
| https://mx2.mail.test.local:8080/secret.html |
|
||||||
|
| https://basicauth.test.local:8080/secret.html |
|
||||||
|
|
|
@ -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
|
Scenario: User is redirected to authelia when he is not authenticated
|
||||||
When I visit "https://public.test.local:8080"
|
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
|
@need-registered-user-john
|
||||||
Scenario: User is redirected to home page after several authentication tries
|
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 login with user "john" and password "password"
|
||||||
And I use "REGISTERED" as TOTP token handle
|
And I use "REGISTERED" as TOTP token handle
|
||||||
And I click on "TOTP"
|
And I click on "TOTP"
|
||||||
Then I have access to:
|
Then I'm redirected to "https://admin.test.local:8080/secret.html"
|
||||||
| url |
|
|
||||||
| https://admin.test.local:8080/secret.html |
|
|
|
@ -6,7 +6,7 @@ import Speakeasy = require("speakeasy");
|
||||||
import CustomWorld = require("../support/world");
|
import CustomWorld = require("../support/world");
|
||||||
|
|
||||||
Cucumber.defineSupportCode(function ({ Given, When, Then }) {
|
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);
|
return this.visit(link);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,10 +66,7 @@ and I use TOTP token handle {stringInDoubleQuotes}",
|
||||||
function hasAccessToSecret(link: string, that: any) {
|
function hasAccessToSecret(link: string, that: any) {
|
||||||
return that.driver.get(link)
|
return that.driver.get(link)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return that.driver.findElement(seleniumWebdriver.By.tagName("body")).getText()
|
return that.waitUntilUrlContains(link);
|
||||||
.then(function (body: string) {
|
|
||||||
Assert(body.indexOf("This is a very important secret!") > -1, body);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,6 @@ Cucumber.defineSupportCode(function ({ Given, When, Then }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
Then("I'm redirected to {stringInDoubleQuotes}", function (link: string) {
|
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) {
|
this.loginWithUserPassword = function (username: string, password: string) {
|
||||||
return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id("username")), 4000)
|
return that.driver.wait(seleniumWebdriver.until.elementLocated(seleniumWebdriver.By.id("username")), 4000)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
|
|
@ -7,13 +7,13 @@ import Assert = require("assert");
|
||||||
describe("test FirstFactorValidator", function () {
|
describe("test FirstFactorValidator", function () {
|
||||||
it("should validate first factor successfully", () => {
|
it("should validate first factor successfully", () => {
|
||||||
const postPromise = JQueryMock.JQueryDeferredMock();
|
const postPromise = JQueryMock.JQueryDeferredMock();
|
||||||
postPromise.done.yields();
|
postPromise.done.yields({ redirect: "http://redirect" });
|
||||||
postPromise.done.returns(postPromise);
|
postPromise.done.returns(postPromise);
|
||||||
|
|
||||||
const jqueryMock = JQueryMock.JQueryMock();
|
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) {
|
function should_fail_first_factor_validation(errorMessage: string) {
|
||||||
|
@ -25,9 +25,9 @@ describe("test FirstFactorValidator", function () {
|
||||||
postPromise.done.returns(postPromise);
|
postPromise.done.returns(postPromise);
|
||||||
|
|
||||||
const jqueryMock = JQueryMock.JQueryMock();
|
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 () {
|
.then(function () {
|
||||||
return BluebirdPromise.reject(new Error("First factor validation successfully finished while it should have not."));
|
return BluebirdPromise.reject(new Error("First factor validation successfully finished while it should have not."));
|
||||||
}, function (err: Error) {
|
}, function (err: Error) {
|
||||||
|
|
|
@ -8,346 +8,360 @@ describe("test access control manager", function () {
|
||||||
let accessController: AccessController;
|
let accessController: AccessController;
|
||||||
let configuration: ACLConfiguration;
|
let configuration: ACLConfiguration;
|
||||||
|
|
||||||
beforeEach(function () {
|
describe("configuration is null", function() {
|
||||||
configuration = {
|
it("should allow access to anything, anywhere for anybody", function() {
|
||||||
default_policy: "deny",
|
configuration = undefined;
|
||||||
any: [],
|
accessController = new AccessController(configuration, winston);
|
||||||
users: {},
|
|
||||||
groups: {}
|
Assert(accessController.isAccessAllowed("home.test.local", "/", "user1", ["group1", "group2"]));
|
||||||
};
|
Assert(accessController.isAccessAllowed("home.test.local", "/abc", "user1", ["group1", "group2"]));
|
||||||
accessController = new AccessController(configuration, winston);
|
Assert(accessController.isAccessAllowed("home.test.local", "/", "user2", ["group1", "group2"]));
|
||||||
|
Assert(accessController.isAccessAllowed("admin.test.local", "/", "user3", ["group3"]));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("check access control with default policy to deny", function () {
|
describe("configuration is not null", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
configuration.default_policy = "deny";
|
configuration = {
|
||||||
|
default_policy: "deny",
|
||||||
|
any: [],
|
||||||
|
users: {},
|
||||||
|
groups: {}
|
||||||
|
};
|
||||||
|
accessController = new AccessController(configuration, winston);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should deny access when no rule is provided", function () {
|
describe("check access control with default policy to deny", function () {
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
beforeEach(function () {
|
||||||
});
|
configuration.default_policy = "deny";
|
||||||
|
|
||||||
it("should control access when multiple domain matcher is provided", function () {
|
|
||||||
configuration.users["user1"] = [{
|
|
||||||
domain: "*.mail.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: [".*"]
|
|
||||||
}];
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(accessController.isAccessAllowed("mx1.mail.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(accessController.isAccessAllowed("mx1.server.mail.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("mail.example.com", "/", "user1", ["group1"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow access to all resources when resources is not provided", function () {
|
|
||||||
configuration.users["user1"] = [{
|
|
||||||
domain: "*.mail.example.com",
|
|
||||||
policy: "allow"
|
|
||||||
}];
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(accessController.isAccessAllowed("mx1.mail.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(accessController.isAccessAllowed("mx1.server.mail.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("mail.example.com", "/", "user1", ["group1"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("check user rules", function () {
|
|
||||||
it("should allow access when user has a matching allowing rule", function () {
|
|
||||||
configuration.users["user1"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: [".*"]
|
|
||||||
}];
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/another/resource", "user1", ["group1"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("another.home.example.com", "/", "user1", ["group1"]));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should deny to other users", function () {
|
it("should deny access when no rule is provided", function () {
|
||||||
configuration.users["user1"] = [{
|
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: [".*"]
|
|
||||||
}];
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user2", ["group1"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/another/resource", "user2", ["group1"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("another.home.example.com", "/", "user2", ["group1"]));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should allow user access only to specific resources", function () {
|
it("should control access when multiple domain matcher is provided", function () {
|
||||||
configuration.users["user1"] = [{
|
configuration.users["user1"] = [{
|
||||||
domain: "home.example.com",
|
domain: "*.mail.example.com",
|
||||||
policy: "allow",
|
policy: "allow",
|
||||||
resources: ["/private/.*", "^/begin", "/end$"]
|
resources: [".*"]
|
||||||
}];
|
}];
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/private", "user1", ["group1"]));
|
Assert(accessController.isAccessAllowed("mx1.mail.example.com", "/", "user1", ["group1"]));
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/private/class", "user1", ["group1"]));
|
Assert(accessController.isAccessAllowed("mx1.server.mail.example.com", "/", "user1", ["group1"]));
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/middle/private/class", "user1", ["group1"]));
|
Assert(!accessController.isAccessAllowed("mail.example.com", "/", "user1", ["group1"]));
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/begin", "user1", ["group1"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/not/begin", "user1", ["group1"]));
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/abc/end", "user1", ["group1"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/abc/end/x", "user1", ["group1"]));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should allow access to multiple domains", function () {
|
it("should allow access to all resources when resources is not provided", function () {
|
||||||
configuration.users["user1"] = [{
|
configuration.users["user1"] = [{
|
||||||
domain: "home.example.com",
|
domain: "*.mail.example.com",
|
||||||
policy: "allow",
|
policy: "allow"
|
||||||
resources: [".*"]
|
|
||||||
}, {
|
|
||||||
domain: "home1.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: [".*"]
|
|
||||||
}, {
|
|
||||||
domain: "home2.example.com",
|
|
||||||
policy: "deny",
|
|
||||||
resources: [".*"]
|
|
||||||
}];
|
}];
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
Assert(accessController.isAccessAllowed("home1.example.com", "/", "user1", ["group1"]));
|
Assert(accessController.isAccessAllowed("mx1.mail.example.com", "/", "user1", ["group1"]));
|
||||||
Assert(!accessController.isAccessAllowed("home2.example.com", "/", "user1", ["group1"]));
|
Assert(accessController.isAccessAllowed("mx1.server.mail.example.com", "/", "user1", ["group1"]));
|
||||||
Assert(!accessController.isAccessAllowed("home3.example.com", "/", "user1", ["group1"]));
|
Assert(!accessController.isAccessAllowed("mail.example.com", "/", "user1", ["group1"]));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should always apply latest rule", function () {
|
describe("check user rules", function () {
|
||||||
configuration.users["user1"] = [{
|
it("should allow access when user has a matching allowing rule", function () {
|
||||||
domain: "home.example.com",
|
configuration.users["user1"] = [{
|
||||||
policy: "allow",
|
domain: "home.example.com",
|
||||||
resources: ["^/my/.*"]
|
policy: "allow",
|
||||||
}, {
|
resources: [".*"]
|
||||||
domain: "home.example.com",
|
}];
|
||||||
policy: "deny",
|
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
resources: ["^/my/private/.*"]
|
Assert(accessController.isAccessAllowed("home.example.com", "/another/resource", "user1", ["group1"]));
|
||||||
}, {
|
Assert(!accessController.isAccessAllowed("another.home.example.com", "/", "user1", ["group1"]));
|
||||||
domain: "home.example.com",
|
});
|
||||||
policy: "allow",
|
|
||||||
resources: ["/my/private/resource"]
|
|
||||||
}];
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/my/poney", "user1", ["group1"]));
|
it("should deny to other users", function () {
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/my/private/duck", "user1", ["group1"]));
|
configuration.users["user1"] = [{
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/my/private/resource", "user1", ["group1"]));
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: [".*"]
|
||||||
|
}];
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user2", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/another/resource", "user2", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("another.home.example.com", "/", "user2", ["group1"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow user access only to specific resources", function () {
|
||||||
|
configuration.users["user1"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["/private/.*", "^/begin", "/end$"]
|
||||||
|
}];
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/private", "user1", ["group1"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/private/class", "user1", ["group1"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/middle/private/class", "user1", ["group1"]));
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/begin", "user1", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/not/begin", "user1", ["group1"]));
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/abc/end", "user1", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/abc/end/x", "user1", ["group1"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow access to multiple domains", function () {
|
||||||
|
configuration.users["user1"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: [".*"]
|
||||||
|
}, {
|
||||||
|
domain: "home1.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: [".*"]
|
||||||
|
}, {
|
||||||
|
domain: "home2.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: [".*"]
|
||||||
|
}];
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home1.example.com", "/", "user1", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home2.example.com", "/", "user1", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home3.example.com", "/", "user1", ["group1"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should always apply latest rule", function () {
|
||||||
|
configuration.users["user1"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/my/.*"]
|
||||||
|
}, {
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: ["^/my/private/.*"]
|
||||||
|
}, {
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["/my/private/resource"]
|
||||||
|
}];
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/my/poney", "user1", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/my/private/duck", "user1", ["group1"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/my/private/resource", "user1", ["group1"]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("check group rules", function () {
|
||||||
|
it("should allow access when user is in group having a matching allowing rule", function () {
|
||||||
|
configuration.groups["group1"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/$"]
|
||||||
|
}];
|
||||||
|
configuration.groups["group2"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/test$"]
|
||||||
|
}, {
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: ["^/private$"]
|
||||||
|
}];
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1",
|
||||||
|
["group1", "group2", "group3"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/test", "user1",
|
||||||
|
["group1", "group2", "group3"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/private", "user1",
|
||||||
|
["group1", "group2", "group3"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("another.home.example.com", "/", "user1",
|
||||||
|
["group1", "group2", "group3"]));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("check group rules", function () {
|
describe("check all rules", function () {
|
||||||
it("should allow access when user is in group having a matching allowing rule", function () {
|
it("should control access when all rules are defined", function () {
|
||||||
configuration.groups["group1"] = [{
|
configuration.any = [{
|
||||||
domain: "home.example.com",
|
domain: "home.example.com",
|
||||||
policy: "allow",
|
policy: "allow",
|
||||||
resources: ["^/$"]
|
resources: ["^/public$"]
|
||||||
}];
|
|
||||||
configuration.groups["group2"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/test$"]
|
|
||||||
}, {
|
}, {
|
||||||
domain: "home.example.com",
|
domain: "home.example.com",
|
||||||
policy: "deny",
|
policy: "deny",
|
||||||
resources: ["^/private$"]
|
resources: ["^/private$"]
|
||||||
}];
|
}];
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1",
|
Assert(accessController.isAccessAllowed("home.example.com", "/public", "user1",
|
||||||
["group1", "group2", "group3"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/test", "user1",
|
|
||||||
["group1", "group2", "group3"]));
|
["group1", "group2", "group3"]));
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/private", "user1",
|
Assert(!accessController.isAccessAllowed("home.example.com", "/private", "user1",
|
||||||
["group1", "group2", "group3"]));
|
["group1", "group2", "group3"]));
|
||||||
Assert(!accessController.isAccessAllowed("another.home.example.com", "/", "user1",
|
Assert(accessController.isAccessAllowed("home.example.com", "/public", "user4",
|
||||||
["group1", "group2", "group3"]));
|
["group5"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/private", "user4",
|
||||||
|
["group5"]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("check access control with default policy to allow", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
configuration.default_policy = "allow";
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should allow access to anything when no rule is provided", function () {
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/test", "user1", ["group1"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "user1", ["group1"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deny access to one resource when defined", function () {
|
||||||
|
configuration.users["user1"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: ["/test"]
|
||||||
|
}];
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/test", "user1", ["group1"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "user1", ["group1"]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("check access control with complete use case", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
configuration.default_policy = "deny";
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should control access of multiple user (real use case)", function () {
|
||||||
|
// Let say we have three users: admin, john, harry.
|
||||||
|
// admin is in groups ["admins"]
|
||||||
|
// john is in groups ["dev", "admin-private"]
|
||||||
|
// harry is in groups ["dev"]
|
||||||
|
configuration.any = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/public$", "^/$"]
|
||||||
|
}];
|
||||||
|
configuration.groups["dev"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/dev/?.*$"]
|
||||||
|
}];
|
||||||
|
configuration.groups["admins"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: [".*"]
|
||||||
|
}];
|
||||||
|
configuration.groups["admin-private"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/private/?.*"]
|
||||||
|
}];
|
||||||
|
configuration.users["john"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/private/john$"]
|
||||||
|
}];
|
||||||
|
configuration.users["harry"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/private/harry"]
|
||||||
|
}, {
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: ["^/dev/b.*$"]
|
||||||
|
}];
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/", "admin", ["admins"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/public", "admin", ["admins"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "admin", ["admins"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev/bob", "admin", ["admins"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/admin", "admin", ["admins"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/private/josh", "admin", ["admins"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/private/john", "admin", ["admins"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/private/harry", "admin", ["admins"]));
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/", "john", ["dev", "admin-private"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/public", "john", ["dev", "admin-private"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "john", ["dev", "admin-private"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev", "admin-private"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/admin", "john", ["dev", "admin-private"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/private/josh", "john", ["dev", "admin-private"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/private/john", "john", ["dev", "admin-private"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/private/harry", "john", ["dev", "admin-private"]));
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/", "harry", ["dev"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/public", "harry", ["dev"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "harry", ["dev"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "harry", ["dev"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/admin", "harry", ["dev"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/private/josh", "harry", ["dev"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/private/john", "harry", ["dev"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/private/harry", "harry", ["dev"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should control access when allowed at group level and denied at user level", function () {
|
||||||
|
configuration.groups["dev"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/dev/?.*$"]
|
||||||
|
}];
|
||||||
|
configuration.users["john"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: ["^/dev/bob$"]
|
||||||
|
}];
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev/john", "john", ["dev"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should control access when allowed at all level and denied at user level", function () {
|
||||||
|
configuration.any = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/dev/?.*$"]
|
||||||
|
}];
|
||||||
|
configuration.users["john"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: ["^/dev/bob$"]
|
||||||
|
}];
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev/john", "john", ["dev"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should control access when allowed at all level and denied at group level", function () {
|
||||||
|
configuration.any = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/dev/?.*$"]
|
||||||
|
}];
|
||||||
|
configuration.groups["dev"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: ["^/dev/bob$"]
|
||||||
|
}];
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev/john", "john", ["dev"]));
|
||||||
|
Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"]));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should respect rules precedence", function () {
|
||||||
|
// the priority from least to most is 'default_policy', 'all', 'group', 'user'
|
||||||
|
// and the first rules in each category as a lower priority than the latest.
|
||||||
|
// You can think of it that way: they override themselves inside each category.
|
||||||
|
configuration.any = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/dev/?.*$"]
|
||||||
|
}];
|
||||||
|
configuration.groups["dev"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "deny",
|
||||||
|
resources: ["^/dev/bob$"]
|
||||||
|
}];
|
||||||
|
configuration.users["john"] = [{
|
||||||
|
domain: "home.example.com",
|
||||||
|
policy: "allow",
|
||||||
|
resources: ["^/dev/?.*$"]
|
||||||
|
}];
|
||||||
|
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev/john", "john", ["dev"]));
|
||||||
|
Assert(accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"]));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("check all rules", function () {
|
|
||||||
it("should control access when all rules are defined", function () {
|
|
||||||
configuration.any = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/public$"]
|
|
||||||
}, {
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "deny",
|
|
||||||
resources: ["^/private$"]
|
|
||||||
}];
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/public", "user1",
|
|
||||||
["group1", "group2", "group3"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/private", "user1",
|
|
||||||
["group1", "group2", "group3"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/public", "user4",
|
|
||||||
["group5"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/private", "user4",
|
|
||||||
["group5"]));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("check access control with default policy to allow", function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
configuration.default_policy = "allow";
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow access to anything when no rule is provided", function () {
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/test", "user1", ["group1"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "user1", ["group1"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should deny access to one resource when defined", function () {
|
|
||||||
configuration.users["user1"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "deny",
|
|
||||||
resources: ["/test"]
|
|
||||||
}];
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/test", "user1", ["group1"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "user1", ["group1"]));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("check access control with complete use case", function () {
|
|
||||||
beforeEach(function () {
|
|
||||||
configuration.default_policy = "deny";
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should control access of multiple user (real use case)", function () {
|
|
||||||
// Let say we have three users: admin, john, harry.
|
|
||||||
// admin is in groups ["admins"]
|
|
||||||
// john is in groups ["dev", "admin-private"]
|
|
||||||
// harry is in groups ["dev"]
|
|
||||||
configuration.any = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/public$", "^/$"]
|
|
||||||
}];
|
|
||||||
configuration.groups["dev"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/dev/?.*$"]
|
|
||||||
}];
|
|
||||||
configuration.groups["admins"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: [".*"]
|
|
||||||
}];
|
|
||||||
configuration.groups["admin-private"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/private/?.*"]
|
|
||||||
}];
|
|
||||||
configuration.users["john"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/private/john$"]
|
|
||||||
}];
|
|
||||||
configuration.users["harry"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/private/harry"]
|
|
||||||
}, {
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "deny",
|
|
||||||
resources: ["^/dev/b.*$"]
|
|
||||||
}];
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/", "admin", ["admins"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/public", "admin", ["admins"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "admin", ["admins"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev/bob", "admin", ["admins"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/admin", "admin", ["admins"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/private/josh", "admin", ["admins"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/private/john", "admin", ["admins"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/private/harry", "admin", ["admins"]));
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/", "john", ["dev", "admin-private"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/public", "john", ["dev", "admin-private"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "john", ["dev", "admin-private"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev", "admin-private"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/admin", "john", ["dev", "admin-private"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/private/josh", "john", ["dev", "admin-private"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/private/john", "john", ["dev", "admin-private"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/private/harry", "john", ["dev", "admin-private"]));
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/", "harry", ["dev"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/public", "harry", ["dev"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev", "harry", ["dev"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "harry", ["dev"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/admin", "harry", ["dev"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/private/josh", "harry", ["dev"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/private/john", "harry", ["dev"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/private/harry", "harry", ["dev"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should control access when allowed at group level and denied at user level", function () {
|
|
||||||
configuration.groups["dev"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/dev/?.*$"]
|
|
||||||
}];
|
|
||||||
configuration.users["john"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "deny",
|
|
||||||
resources: ["^/dev/bob$"]
|
|
||||||
}];
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev/john", "john", ["dev"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should control access when allowed at all level and denied at user level", function () {
|
|
||||||
configuration.any = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/dev/?.*$"]
|
|
||||||
}];
|
|
||||||
configuration.users["john"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "deny",
|
|
||||||
resources: ["^/dev/bob$"]
|
|
||||||
}];
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev/john", "john", ["dev"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should control access when allowed at all level and denied at group level", function () {
|
|
||||||
configuration.any = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/dev/?.*$"]
|
|
||||||
}];
|
|
||||||
configuration.groups["dev"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "deny",
|
|
||||||
resources: ["^/dev/bob$"]
|
|
||||||
}];
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev/john", "john", ["dev"]));
|
|
||||||
Assert(!accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should respect rules precedence", function () {
|
|
||||||
// the priority from least to most is 'default_policy', 'all', 'group', 'user'
|
|
||||||
// and the first rules in each category as a lower priority than the latest.
|
|
||||||
// You can think of it that way: they override themselves inside each category.
|
|
||||||
configuration.any = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/dev/?.*$"]
|
|
||||||
}];
|
|
||||||
configuration.groups["dev"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "deny",
|
|
||||||
resources: ["^/dev/bob$"]
|
|
||||||
}];
|
|
||||||
configuration.users["john"] = [{
|
|
||||||
domain: "home.example.com",
|
|
||||||
policy: "allow",
|
|
||||||
resources: ["^/dev/?.*$"]
|
|
||||||
}];
|
|
||||||
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev/john", "john", ["dev"]));
|
|
||||||
Assert(accessController.isAccessAllowed("home.example.com", "/dev/bob", "john", ["dev"]));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
import sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import { UserDataStoreStub } from "./storage/UserDataStoreStub";
|
import { UserDataStoreStub } from "./storage/UserDataStoreStub";
|
||||||
import { VARIABLES_KEY } from "../../../../src/server/lib/ServerVariablesHandler";
|
import { VARIABLES_KEY } from "../../../../src/server/lib/ServerVariablesHandler";
|
||||||
|
|
||||||
export interface ServerVariablesMock {
|
export interface ServerVariablesMock {
|
||||||
logger: any;
|
logger: any;
|
||||||
ldapAuthenticator: any;
|
ldapAuthenticator: any;
|
||||||
ldapEmailsRetriever: any;
|
ldapEmailsRetriever: any;
|
||||||
ldapPasswordUpdater: any;
|
ldapPasswordUpdater: any;
|
||||||
totpValidator: any;
|
totpValidator: any;
|
||||||
totpGenerator: any;
|
totpGenerator: any;
|
||||||
u2f: any;
|
u2f: any;
|
||||||
userDataStore: UserDataStoreStub;
|
userDataStore: UserDataStoreStub;
|
||||||
notifier: any;
|
notifier: any;
|
||||||
regulator: any;
|
regulator: any;
|
||||||
config: any;
|
config: any;
|
||||||
accessController: any;
|
accessController: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function mock(app: express.Application): ServerVariablesMock {
|
export function mock(app: express.Application): ServerVariablesMock {
|
||||||
const mocks: ServerVariablesMock = {
|
const mocks: ServerVariablesMock = {
|
||||||
accessController: sinon.stub(),
|
accessController: Sinon.stub(),
|
||||||
config: sinon.stub(),
|
config: Sinon.stub(),
|
||||||
ldapAuthenticator: sinon.stub() as any,
|
ldapAuthenticator: Sinon.stub() as any,
|
||||||
ldapEmailsRetriever: sinon.stub() as any,
|
ldapEmailsRetriever: Sinon.stub() as any,
|
||||||
ldapPasswordUpdater: sinon.stub() as any,
|
ldapPasswordUpdater: Sinon.stub() as any,
|
||||||
logger: winston,
|
logger: winston,
|
||||||
notifier: sinon.stub(),
|
notifier: Sinon.stub(),
|
||||||
regulator: sinon.stub(),
|
regulator: Sinon.stub(),
|
||||||
totpGenerator: sinon.stub(),
|
totpGenerator: Sinon.stub(),
|
||||||
totpValidator: sinon.stub(),
|
totpValidator: Sinon.stub(),
|
||||||
u2f: sinon.stub(),
|
u2f: Sinon.stub(),
|
||||||
userDataStore: new UserDataStoreStub()
|
userDataStore: new UserDataStoreStub()
|
||||||
};
|
};
|
||||||
app.get = sinon.stub().withArgs(VARIABLES_KEY).returns(mocks);
|
app.get = Sinon.stub().withArgs(VARIABLES_KEY).returns(mocks);
|
||||||
return mocks;
|
return mocks;
|
||||||
}
|
}
|
|
@ -51,6 +51,9 @@ describe("test the first factor validation route", function () {
|
||||||
username: "username",
|
username: "username",
|
||||||
password: "password"
|
password: "password"
|
||||||
},
|
},
|
||||||
|
query: {
|
||||||
|
redirect: "http://redirect.url"
|
||||||
|
},
|
||||||
session: {
|
session: {
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -87,7 +90,6 @@ describe("test the first factor validation route", function () {
|
||||||
.then(function () {
|
.then(function () {
|
||||||
assert.equal("username", authSession.userid);
|
assert.equal("username", authSession.userid);
|
||||||
assert(res.send.calledOnce);
|
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 VerifyGet = require("../../../../../src/server/lib/routes/verify/get");
|
||||||
import AuthenticationSession = require("../../../../../src/server/lib/AuthenticationSession");
|
import AuthenticationSession = require("../../../../../src/server/lib/AuthenticationSession");
|
||||||
|
|
||||||
import sinon = require("sinon");
|
import Sinon = require("sinon");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
|
||||||
|
@ -24,10 +24,13 @@ describe("test authentication token verification", function () {
|
||||||
|
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
req.app = {
|
|
||||||
get: sinon.stub().returns({ logger: winston })
|
|
||||||
};
|
|
||||||
req.session = {};
|
req.session = {};
|
||||||
|
req.query = {
|
||||||
|
redirect: "http://redirect.url"
|
||||||
|
};
|
||||||
|
req.app = {
|
||||||
|
get: Sinon.stub().returns({ logger: winston })
|
||||||
|
};
|
||||||
AuthenticationSession.reset(req as any);
|
AuthenticationSession.reset(req as any);
|
||||||
req.headers = {};
|
req.headers = {};
|
||||||
req.headers.host = "secret.example.com";
|
req.headers.host = "secret.example.com";
|
||||||
|
@ -49,95 +52,133 @@ describe("test authentication token verification", function () {
|
||||||
return VerifyGet.default(req as express.Request, res as any);
|
return VerifyGet.default(req as express.Request, res as any);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
Sinon.assert.calledWithExactly(res.setHeader, "Remote-User", "myuser");
|
||||||
sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
Sinon.assert.calledWithExactly(res.setHeader, "Remote-Groups", "mygroup,othergroup");
|
||||||
assert.equal(204, res.status.getCall(0).args[0]);
|
Assert.equal(204, res.status.getCall(0).args[0]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("given different cases of session", function () {
|
function test_session(_authSession: AuthenticationSession.AuthenticationSession, status_code: number) {
|
||||||
function test_session(auth_session: AuthenticationSession.AuthenticationSession, status_code: number) {
|
return AuthenticationSession.get(req as any)
|
||||||
return VerifyGet.default(req as express.Request, res as any)
|
.then(function (authSession) {
|
||||||
.then(function () {
|
authSession = _authSession;
|
||||||
assert.equal(status_code, res.status.getCall(0).args[0]);
|
return VerifyGet.default(req as express.Request, res as any);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
Assert.equal(status_code, res.status.getCall(0).args[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_non_authenticated_401(auth_session: AuthenticationSession.AuthenticationSession) {
|
||||||
|
return test_session(auth_session, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_unauthorized_403(auth_session: AuthenticationSession.AuthenticationSession) {
|
||||||
|
return test_session(auth_session, 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_authorized(auth_session: AuthenticationSession.AuthenticationSession) {
|
||||||
|
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",
|
||||||
|
first_factor: true,
|
||||||
|
second_factor: false,
|
||||||
|
email: undefined,
|
||||||
|
groups: [],
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
function test_non_authenticated_401(auth_session: AuthenticationSession.AuthenticationSession) {
|
it("should not be authenticated when first factor is missing", function () {
|
||||||
return test_session(auth_session, 401);
|
return test_non_authenticated_401({
|
||||||
}
|
userid: "user",
|
||||||
|
first_factor: false,
|
||||||
|
second_factor: true,
|
||||||
|
email: undefined,
|
||||||
|
groups: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function test_unauthorized_403(auth_session: AuthenticationSession.AuthenticationSession) {
|
it("should not be authenticated when userid is missing", function () {
|
||||||
return test_session(auth_session, 403);
|
return test_non_authenticated_401({
|
||||||
}
|
userid: undefined,
|
||||||
|
first_factor: true,
|
||||||
|
second_factor: false,
|
||||||
|
email: undefined,
|
||||||
|
groups: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function test_authorized(auth_session: AuthenticationSession.AuthenticationSession) {
|
it("should not be authenticated when first and second factor are missing", function () {
|
||||||
return test_session(auth_session, 204);
|
return test_non_authenticated_401({
|
||||||
}
|
userid: "user",
|
||||||
|
first_factor: false,
|
||||||
|
second_factor: false,
|
||||||
|
email: undefined,
|
||||||
|
groups: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should not be authenticated when second factor is missing", function () {
|
it("should not be authenticated when session has not be initiated", function () {
|
||||||
return test_non_authenticated_401({
|
return test_non_authenticated_401(undefined);
|
||||||
userid: "user",
|
});
|
||||||
first_factor: true,
|
|
||||||
second_factor: false,
|
it("should not be authenticated when domain is not allowed for user", function () {
|
||||||
email: undefined,
|
return AuthenticationSession.get(req as any)
|
||||||
groups: [],
|
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
|
||||||
|
authSession.first_factor = true;
|
||||||
|
authSession.second_factor = true;
|
||||||
|
authSession.userid = "myuser";
|
||||||
|
|
||||||
|
req.headers.host = "test.example.com";
|
||||||
|
|
||||||
|
accessController.isAccessAllowedMock.returns(false);
|
||||||
|
accessController.isAccessAllowedMock.withArgs("test.example.com", "user", ["group1", "group2"]).returns(true);
|
||||||
|
|
||||||
|
return test_unauthorized_403({
|
||||||
|
first_factor: true,
|
||||||
|
second_factor: true,
|
||||||
|
userid: "user",
|
||||||
|
groups: ["group1", "group2"],
|
||||||
|
email: undefined
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("should not be authenticated when first factor is missing", function () {
|
describe("given user tries to access a basic auth endpoint", function () {
|
||||||
return test_non_authenticated_401({
|
beforeEach(function () {
|
||||||
userid: "user",
|
req.query = {
|
||||||
first_factor: false,
|
redirect: "http://redirect.url",
|
||||||
second_factor: true,
|
only_basic_auth: "true"
|
||||||
email: undefined,
|
};
|
||||||
groups: [],
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not be authenticated when userid is missing", function () {
|
it("should be authenticated when first factor is validated and not second factor", function () {
|
||||||
return test_non_authenticated_401({
|
|
||||||
userid: undefined,
|
|
||||||
first_factor: true,
|
|
||||||
second_factor: false,
|
|
||||||
email: undefined,
|
|
||||||
groups: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not be authenticated when first and second factor are missing", function () {
|
|
||||||
return test_non_authenticated_401({
|
|
||||||
userid: "user",
|
|
||||||
first_factor: false,
|
|
||||||
second_factor: false,
|
|
||||||
email: undefined,
|
|
||||||
groups: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not be authenticated when session has not be initiated", function () {
|
|
||||||
return test_non_authenticated_401(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not be authenticated when domain is not allowed for user", function () {
|
|
||||||
return AuthenticationSession.get(req as any)
|
return AuthenticationSession.get(req as any)
|
||||||
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
|
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
|
||||||
authSession.first_factor = true;
|
authSession.first_factor = true;
|
||||||
authSession.second_factor = true;
|
return VerifyGet.default(req as express.Request, res as any);
|
||||||
authSession.userid = "myuser";
|
})
|
||||||
|
.then(function () {
|
||||||
|
Assert(res.status.calledWith(204));
|
||||||
|
Assert(res.send.calledOnce);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
req.headers.host = "test.example.com";
|
it("should be rejected with 401 when first factor is not validated", function () {
|
||||||
|
return AuthenticationSession.get(req as any)
|
||||||
accessController.isAccessAllowedMock.returns(false);
|
.then(function (authSession: AuthenticationSession.AuthenticationSession) {
|
||||||
accessController.isAccessAllowedMock.withArgs("test.example.com", "user", ["group1", "group2"]).returns(true);
|
authSession.first_factor = false;
|
||||||
|
return VerifyGet.default(req as express.Request, res as any);
|
||||||
return test_unauthorized_403({
|
})
|
||||||
first_factor: true,
|
.then(function () {
|
||||||
second_factor: true,
|
Assert(res.status.calledWith(401));
|
||||||
userid: "user",
|
|
||||||
groups: ["group1", "group2"],
|
|
||||||
email: undefined
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue