mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
Merge pull request #95 from clems4ever/acl-by-resources
Refine access control with per resource ACLs
This commit is contained in:
commit
7a2b45a66f
|
@ -18,9 +18,8 @@ addons:
|
||||||
- auth.test.local
|
- auth.test.local
|
||||||
- home.test.local
|
- home.test.local
|
||||||
- public.test.local
|
- public.test.local
|
||||||
- secret.test.local
|
- admin.test.local
|
||||||
- secret1.test.local
|
- dev.test.local
|
||||||
- secret2.test.local
|
|
||||||
- mx1.mail.test.local
|
- mx1.mail.test.local
|
||||||
- mx2.mail.test.local
|
- mx2.mail.test.local
|
||||||
|
|
||||||
|
|
61
README.md
61
README.md
|
@ -48,7 +48,7 @@ without even configure anything.
|
||||||
|
|
||||||
Otherwise here are the available steps to deploy **Authelia** on your machine given
|
Otherwise here are the available steps to deploy **Authelia** on your machine given
|
||||||
your configuration file is **/path/to/your/config.yml**. Note that you can create your
|
your configuration file is **/path/to/your/config.yml**. Note that you can create your
|
||||||
own the configuration file from **config.template.yml** at the root of the repo.
|
own the configuration file from [config.template.yml] at the root of the repo.
|
||||||
|
|
||||||
### With NPM
|
### With NPM
|
||||||
|
|
||||||
|
@ -91,11 +91,10 @@ Make sure you don't have anything listening on port 8080.
|
||||||
|
|
||||||
Add the following lines to your **/etc/hosts** to alias multiple subdomains so that nginx can redirect request to the correct virtual host.
|
Add the following lines to your **/etc/hosts** to alias multiple subdomains so that nginx can redirect request to the correct virtual host.
|
||||||
|
|
||||||
127.0.0.1 public.test.local
|
|
||||||
127.0.0.1 secret.test.local
|
|
||||||
127.0.0.1 secret1.test.local
|
|
||||||
127.0.0.1 secret2.test.local
|
|
||||||
127.0.0.1 home.test.local
|
127.0.0.1 home.test.local
|
||||||
|
127.0.0.1 public.test.local
|
||||||
|
127.0.0.1 dev.test.local
|
||||||
|
127.0.0.1 admin.test.local
|
||||||
127.0.0.1 mx1.mail.test.local
|
127.0.0.1 mx1.mail.test.local
|
||||||
127.0.0.1 mx2.mail.test.local
|
127.0.0.1 mx2.mail.test.local
|
||||||
127.0.0.1 auth.test.local
|
127.0.0.1 auth.test.local
|
||||||
|
@ -119,7 +118,7 @@ After few seconds the services should be running and you should be able to visit
|
||||||
|
|
||||||
When accessing the login page, a self-signed certificate exception should appear,
|
When accessing the login page, a self-signed certificate exception should appear,
|
||||||
it has to be trusted before you can get to the target page. The certificate
|
it has to be trusted before you can get to the target page. The certificate
|
||||||
must be trusted for each subdomain, therefore it is normal to see the exception
|
must also be trusted for each subdomain, therefore it is normal to see the exception
|
||||||
several times.
|
several times.
|
||||||
|
|
||||||
Below is what the login page looks like:
|
Below is what the login page looks like:
|
||||||
|
@ -128,37 +127,24 @@ Below is what the login page looks like:
|
||||||
|
|
||||||
## Features in details
|
## Features in details
|
||||||
|
|
||||||
### First factor with LDAP and ACL
|
### First factor using an LDAP server
|
||||||
An LDAP server has been deployed for you with the following credentials and
|
**Authelia** uses an LDAP server as the backend for storing credentials.
|
||||||
access control list:
|
When authentication is needed, the user is redirected to the login page which
|
||||||
|
corresponds to the first factor. Authelia tries to bind the username and password
|
||||||
- **john / password** is in the admin group and has access to the secret from
|
against the configured LDAP backend.
|
||||||
any subdomain.
|
|
||||||
- **bob / password** is in the dev group and has access to the secret from
|
|
||||||
- [secret.test.local](https://secret.test.local:8080/secret.html)
|
|
||||||
- [secret2.test.local](https://secret2.test.local:8080/secret.html)
|
|
||||||
- [home.test.local](https://home.test.local:8080/secret.html)
|
|
||||||
- [\*.mail.test.local](https://mx1.mail.test.local:8080/secret.html)
|
|
||||||
- **harry / password** is not in a group but has rules giving him has access to
|
|
||||||
the secret from
|
|
||||||
- [secret1.test.local](https://secret1.test.local:8080/secret.html)
|
|
||||||
- [home.test.local](https://home.test.local:8080/secret.html)
|
|
||||||
|
|
||||||
You can use them in the login page. If everything is ok, the second factor
|
|
||||||
page should appear as shown below. Otherwise you'll get an error message notifying
|
|
||||||
your credentials are wrong.
|
|
||||||
|
|
||||||
|
You can find an example of the configuration of the LDAP backend in [config.template.yml].
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/clems4ever/authelia/master/images/second_factor.png" width="400">
|
<img src="https://raw.githubusercontent.com/clems4ever/authelia/master/images/second_factor.png" width="400">
|
||||||
|
|
||||||
|
|
||||||
### Second factor with TOTP
|
### Second factor with TOTP
|
||||||
In **Authelia**, you need to register a per user TOTP (Time-Based One Time Password) secret before
|
In **Authelia**, you can register a per user TOTP (Time-Based One Time Password) secret before
|
||||||
authenticating. To do that, you need to click on the register button. It will
|
authenticating. To do that, you need to click on the register button. It will
|
||||||
send a link to the user email address. Since this is an example, no email will
|
send a link to the user email address defined in the LDAP.
|
||||||
be sent, the link is rather delivered in the file
|
Since this is an example, no email will be sent, the link is rather delivered in the file
|
||||||
**/tmp/notifications/notification.txt**. Paste the link in your browser and you'll get
|
**/tmp/notifications/notification.txt**. Paste the link in your browser and you'll get
|
||||||
your secret in QRCode and Base32 formats. You can use
|
your secret in QRCode and Base32 format. You can use
|
||||||
[Google Authenticator]
|
[Google Authenticator]
|
||||||
to store them and get the generated tokens with the app.
|
to store them and get the generated tokens with the app.
|
||||||
|
|
||||||
|
@ -169,8 +155,8 @@ to store them and get the generated tokens with the app.
|
||||||
USB security keys. U2F is one of the most secure authentication protocol and is
|
USB security keys. U2F is one of the most secure authentication protocol and is
|
||||||
already available for Google, Facebook, Github accounts and more.
|
already available for Google, Facebook, Github accounts and more.
|
||||||
|
|
||||||
Like TOTP, U2F requires you register your security key before authenticating.
|
Like TOTP, U2F requires you register a security key before authenticating.
|
||||||
To do so, click on the register button. This will send a link to the
|
To do so, click on the register link. This will send a link to the
|
||||||
user email address. Since this is an example, no email will be sent, the
|
user email address. Since this is an example, no email will be sent, the
|
||||||
link is rather delivered in the file **/tmp/notifications/notification.txt**. Paste
|
link is rather delivered in the file **/tmp/notifications/notification.txt**. Paste
|
||||||
the link in your browser and you'll be asking to touch the token of your device
|
the link in your browser and you'll be asking to touch the token of your device
|
||||||
|
@ -190,18 +176,18 @@ Paste the link in your browser and you should be able to reset the password.
|
||||||
<img src="https://raw.githubusercontent.com/clems4ever/authelia/master/images/reset_password.png" width="400">
|
<img src="https://raw.githubusercontent.com/clems4ever/authelia/master/images/reset_password.png" width="400">
|
||||||
|
|
||||||
### Access Control
|
### Access Control
|
||||||
With **Authelia**, you can define your own access control rules for restricting
|
With **Authelia**, you can define your own access control rules for finely restricting
|
||||||
the user access to some subdomains. Those rules are defined in the
|
user access to some resources and subdomains. Those rules are defined and fully documented
|
||||||
configuration file and can be set either for everyone, per-user or per-group policies.
|
in the configuration file. They can apply to users, groups or everyone.
|
||||||
Check out the *config.template.yml* to see how they are defined.
|
Check out [config.template.yml] to see how they are defined.
|
||||||
|
|
||||||
### Session management with Redis
|
### Session management with Redis
|
||||||
When your users authenticate against Authelia, sessions are stored in a Redis key/value store. You can specify your own Redis instance in the [configuration file](#authelia-configuration).
|
When your users authenticate against Authelia, sessions are stored in a Redis key/value store. You can specify your own Redis instance in [config.template.yml].
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
### Authelia configuration
|
### Authelia configuration
|
||||||
The configuration of the server is defined in the file
|
The configuration of the server is defined in the file
|
||||||
**configuration.template.yml**. All the details are documented there.
|
[config.template.yml]. All the details are documented there.
|
||||||
You can specify another configuration file by giving it as first argument of
|
You can specify another configuration file by giving it as first argument of
|
||||||
**Authelia**.
|
**Authelia**.
|
||||||
|
|
||||||
|
@ -246,4 +232,5 @@ Follow [contributing](CONTRIBUTORS.md) file.
|
||||||
[Yubikey]: https://www.yubico.com/products/yubikey-hardware/yubikey4/
|
[Yubikey]: https://www.yubico.com/products/yubikey-hardware/yubikey4/
|
||||||
[auth_request]: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html
|
[auth_request]: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html
|
||||||
[Google Authenticator]: https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en
|
[Google Authenticator]: https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en
|
||||||
|
[config.template.yml]: https://github.com/clems4ever/authelia/blob/master/config.template.yml
|
||||||
|
|
||||||
|
|
|
@ -49,38 +49,84 @@ ldap:
|
||||||
|
|
||||||
# Access Control
|
# Access Control
|
||||||
#
|
#
|
||||||
# Access control is a set of rules you can use to restrict the user access.
|
# Access control is a set of rules you can use to restrict user access to certain
|
||||||
# Default (anyone), per-user or per-group rules can be defined.
|
# resources.
|
||||||
|
# Any (apply to anyone), per-user or per-group rules can be defined.
|
||||||
#
|
#
|
||||||
# If 'access_control' is not defined, ACL rules are disabled and a default policy
|
# If 'access_control' is not defined, ACL rules are disabled and the `allow` default
|
||||||
# is applied, i.e., access is allowed to anyone. Otherwise restrictions follow
|
# policy is applied, i.e., access is allowed to anyone. Otherwise restrictions follow
|
||||||
# the rules defined below.
|
# the rules defined.
|
||||||
# If no rule is provided, all domains are denied.
|
#
|
||||||
|
# Note: One can use the wildcard * to match any subdomain.
|
||||||
|
# It must stand at the beginning of the pattern. (example: *.mydomain.com)
|
||||||
|
#
|
||||||
|
# Note: You must put the pattern in simple quotes when using the wildcard for the YAML
|
||||||
|
# to be syntaxically correct.
|
||||||
|
#
|
||||||
|
# Definition: A `rule` is an object with the following keys: `domain`, `policy`
|
||||||
|
# and `resources`.
|
||||||
|
# - `domain` defines which domain or set of domains the rule applies to.
|
||||||
|
# - `policy` is the policy to apply to resources. It must be either `allow` or `deny`.
|
||||||
|
# - `resources` is a list of regular expressions that matches a set of resources to
|
||||||
|
# apply the policy to.
|
||||||
|
#
|
||||||
|
# Note: Rules follow an order of priority defined as follows:
|
||||||
|
# In each category (`any`, `groups`, `users`), the latest rules have the highest
|
||||||
|
# priority. In other words, it means that if a given resource matches two rules in the
|
||||||
|
# same category, the latest one overrides the first one.
|
||||||
|
# Each category has also its own priority. That is, `users` has the highest priority, then
|
||||||
|
# `groups` and `any` has the lowest priority. It means if two rules in different categories
|
||||||
|
# match a given resource, the one in the category with the highest priority overrides the
|
||||||
|
# other one.
|
||||||
#
|
#
|
||||||
# One can use the wildcard * to match any subdomain.
|
|
||||||
# Note 1: It must stand at the beginning of the pattern. (example: *.mydomain.com)
|
|
||||||
# Note 2: You must put the pattern in simple quotes when using the wildcard.
|
|
||||||
access_control:
|
access_control:
|
||||||
# The default policy. Applies to any user
|
# Default policy can either be `allow` or `deny`.
|
||||||
default:
|
# It is the policy applied to any resource if it has not been overriden
|
||||||
- public.test.local
|
# in the `any`, `groups` or `users` category.
|
||||||
|
default_policy: deny
|
||||||
|
|
||||||
# Group based policies. The key is a group name and the value
|
# The rules that apply to anyone.
|
||||||
# is the domain to allow access to.
|
# The value is a list of rules.
|
||||||
|
any:
|
||||||
|
- domain: public.test.local
|
||||||
|
policy: allow
|
||||||
|
|
||||||
|
# Group-based rules. The key is a group name and the value
|
||||||
|
# is a list of rules.
|
||||||
groups:
|
groups:
|
||||||
admin:
|
admin:
|
||||||
- '*.test.local'
|
# All resources in all domains
|
||||||
|
- domain: '*.test.local'
|
||||||
|
policy: allow
|
||||||
|
# Except mx2.mail.test.local (it restricts the first rule)
|
||||||
|
- domain: 'mx2.mail.test.local'
|
||||||
|
policy: deny
|
||||||
dev:
|
dev:
|
||||||
- secret.test.local
|
- domain: dev.test.local
|
||||||
- secret2.test.local
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/groups/dev/.*$'
|
||||||
|
|
||||||
# Group based policies. The key is a group name and the value
|
# User-based rules. The key is a user name and the value
|
||||||
# is the domain to allow access to.
|
# is a list of rules.
|
||||||
users:
|
users:
|
||||||
|
john:
|
||||||
|
- domain: dev.test.local
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/john/.*$'
|
||||||
harry:
|
harry:
|
||||||
- secret1.test.local
|
- domain: dev.test.local
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/harry/.*$'
|
||||||
bob:
|
bob:
|
||||||
- '*.mail.test.local'
|
- domain: '*.mail.test.local'
|
||||||
|
policy: allow
|
||||||
|
- domain: 'dev.test.local'
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/bob/.*$'
|
||||||
|
|
||||||
|
|
||||||
# Configuration of session cookies
|
# Configuration of session cookies
|
||||||
|
|
|
@ -61,27 +61,53 @@ ldap:
|
||||||
# Note 1: It must stand at the beginning of the pattern. (example: *.mydomain.com)
|
# Note 1: It must stand at the beginning of the pattern. (example: *.mydomain.com)
|
||||||
# Note 2: You must put the pattern in simple quotes when using the wildcard.
|
# Note 2: You must put the pattern in simple quotes when using the wildcard.
|
||||||
access_control:
|
access_control:
|
||||||
# The default policy. Applies to any user
|
# Default policy can either be `allow` or `deny`.
|
||||||
default:
|
# It is the policy applied to any resource if it has not been overriden
|
||||||
- public.test.local
|
# in the `any`, `groups` or `users` category.
|
||||||
|
default_policy: deny
|
||||||
|
|
||||||
# Group based policies. The key is a group name and the value
|
# The rules that apply to anyone.
|
||||||
# is the domain to allow access to.
|
# The value is a list of rules.
|
||||||
|
any:
|
||||||
|
- domain: public.test.local
|
||||||
|
policy: allow
|
||||||
|
|
||||||
|
# Group-based rules. The key is a group name and the value
|
||||||
|
# is a list of rules.
|
||||||
groups:
|
groups:
|
||||||
admin:
|
admin:
|
||||||
- '*.test.local'
|
# All resources in all domains
|
||||||
|
- domain: '*.test.local'
|
||||||
|
policy: allow
|
||||||
|
# Except mx2.mail.test.local (it restricts the first rule)
|
||||||
|
- domain: 'mx2.mail.test.local'
|
||||||
|
policy: deny
|
||||||
dev:
|
dev:
|
||||||
- secret.test.local
|
- domain: dev.test.local
|
||||||
- secret2.test.local
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/groups/dev/.*$'
|
||||||
|
|
||||||
# Group based policies. The key is a group name and the value
|
# User-based rules. The key is a user name and the value
|
||||||
# is the domain to allow access to.
|
# is a list of rules.
|
||||||
users:
|
users:
|
||||||
|
john:
|
||||||
|
- domain: dev.test.local
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/john/.*$'
|
||||||
harry:
|
harry:
|
||||||
- secret1.test.local
|
- domain: dev.test.local
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/harry/.*$'
|
||||||
bob:
|
bob:
|
||||||
- '*.mail.test.local'
|
- domain: '*.mail.test.local'
|
||||||
|
policy: allow
|
||||||
|
- domain: 'dev.test.local'
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/bob/.*$'
|
||||||
|
|
||||||
# Configuration of session cookies
|
# Configuration of session cookies
|
||||||
#
|
#
|
||||||
|
|
|
@ -14,10 +14,8 @@ services:
|
||||||
- example-network
|
- example-network
|
||||||
# aliases:
|
# aliases:
|
||||||
# - home.test.local
|
# - home.test.local
|
||||||
# - secret.test.local
|
# - public.test.local
|
||||||
# - secret1.test.local
|
# - admin.test.local
|
||||||
# - secret2.test.local
|
# - dev.test.local
|
||||||
# - mx1.mail.test.local
|
|
||||||
# - mx2.mail.test.local
|
|
||||||
# - auth.test.local
|
# - auth.test.local
|
||||||
|
|
||||||
|
|
10
example/nginx/html/dev.test.local/groups/admin/secret.html
Normal file
10
example/nginx/html/dev.test.local/groups/admin/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>
|
10
example/nginx/html/dev.test.local/groups/dev/secret.html
Normal file
10
example/nginx/html/dev.test.local/groups/dev/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>
|
10
example/nginx/html/dev.test.local/users/bob/secret.html
Normal file
10
example/nginx/html/dev.test.local/users/bob/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>
|
10
example/nginx/html/dev.test.local/users/harry/secret.html
Normal file
10
example/nginx/html/dev.test.local/users/harry/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>
|
10
example/nginx/html/dev.test.local/users/john/secret.html
Normal file
10
example/nginx/html/dev.test.local/users/john/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>
|
132
example/nginx/html/home.test.local/index.html
Normal file
132
example/nginx/html/home.test.local/index.html
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
<!DOCTYPE>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Home page</title>
|
||||||
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<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/>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
public.test.local <a href="https://public.test.local:8080/"> / index.html</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
secret.test.local
|
||||||
|
<ul>
|
||||||
|
<li>Groups
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://dev.test.local:8080/groups/admin/secret.html"> / groups / admins / secret.html</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://dev.test.local:8080/groups/dev/secret.html"> / groups / dev / secret.html</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>Users
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="https://dev.test.local:8080/users/john/secret.html"> / users / john / secret.html</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://dev.test.local:8080/users/harry/secret.html"> / users / harry / secret.html</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://dev.test.local:8080/users/bob/secret.html"> / users / bob / secret.html</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
admin.test.local <a href="https://admin.test.local:8080/secret.html"> / secret.html</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
mx1.main.test.local <a href="https://mx1.mail.test.local:8080/secret.html"> / secret.html</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
mx2.main.test.local <a href="https://mx2.mail.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.
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><strong>john / password</strong>: belongs to <em>admin</em> and <em>dev</em> groups.</li>
|
||||||
|
<li><strong>bob / password</strong>: belongs to <em>dev</em> group only.</li>
|
||||||
|
<li><strong>harry / password</strong>: does not belong to any group.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1>Access control rules</h1>
|
||||||
|
<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>
|
||||||
|
<pre id="rules" style="border: 1px grey solid; padding: 20px; display: inline-block;">
|
||||||
|
# Default policy can either be `allow` or `deny`.
|
||||||
|
# It is the policy applied to any resource if it has not been overriden
|
||||||
|
# in the `any`, `groups` or `users` category.
|
||||||
|
|
||||||
|
default_policy: deny
|
||||||
|
|
||||||
|
# The rules that apply to anyone.
|
||||||
|
# The value is a list of rules.
|
||||||
|
|
||||||
|
any:
|
||||||
|
- domain: public.test.local
|
||||||
|
policy: allow
|
||||||
|
|
||||||
|
# Group-based rules. The key is a group name and the value
|
||||||
|
# is a list of rules.
|
||||||
|
|
||||||
|
groups:
|
||||||
|
admin:
|
||||||
|
# All resources in all domains
|
||||||
|
- domain: '*.test.local'
|
||||||
|
policy: allow
|
||||||
|
# Except mx2.mail.test.local (it restricts the first rule)
|
||||||
|
- domain: 'mx2.mail.test.local'
|
||||||
|
policy: deny
|
||||||
|
dev:
|
||||||
|
- domain: dev.test.local
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/groups/dev/.*$'
|
||||||
|
|
||||||
|
# User-based rules. The key is a user name and the value
|
||||||
|
# is a list of rules.
|
||||||
|
|
||||||
|
users:
|
||||||
|
john:
|
||||||
|
- domain: dev.test.local
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/john/.*$'
|
||||||
|
harry:
|
||||||
|
- domain: dev.test.local
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/harry/.*$'
|
||||||
|
bob:
|
||||||
|
- domain: '*.mail.test.local'
|
||||||
|
policy: allow
|
||||||
|
- domain: 'dev.test.local'
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/bob/.*$'
|
||||||
|
- domain: 'dev.test.local'
|
||||||
|
policy: allow
|
||||||
|
resources:
|
||||||
|
- '^/users/harry/.*$'</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,82 +0,0 @@
|
||||||
<!DOCTYPE>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Home page</title>
|
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Access the secret</h1>
|
|
||||||
You need to log in to access the secret!<br/><br/>
|
|
||||||
Try to access it via one of the following links.<br/>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="https://public.test.local:8080/secret.html">public.test.local</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://secret.test.local:8080/secret.html">secret.test.local</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://secret1.test.local:8080/secret.html">secret1.test.local</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://secret2.test.local:8080/secret.html">secret2.test.local</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://mx1.mail.test.local:8080/secret.html">mx1.mail.test.local</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://mx2.mail.test.local:8080/secret.html">mx2.mail.test.local</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.
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><strong>john / password</strong>: belongs to <em>admin</em> and <em>dev</em> groups.</li>
|
|
||||||
<li><strong>bob / password</strong>: belongs to <em>dev</em> group only.</li>
|
|
||||||
<li><strong>harry / password</strong>: does not belong to any group.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h1>Access control rules</h1>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li><strong>Default policy</strong>
|
|
||||||
<ul>
|
|
||||||
<li>public.test.local</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><strong>Groups policy</strong>
|
|
||||||
<ul>
|
|
||||||
<li>admin
|
|
||||||
<ul>
|
|
||||||
<li>*.test.local</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>dev
|
|
||||||
<ul>
|
|
||||||
<li>secret.test.local</li>
|
|
||||||
<li>secret2.test.local</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><strong>Users policy</strong>
|
|
||||||
<ul>
|
|
||||||
<li>harry
|
|
||||||
<ul>
|
|
||||||
<li>secret1.test.local</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li>bob
|
|
||||||
<ul>
|
|
||||||
<li>*.mail.test.local</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
10
example/nginx/html/mail.test.local/secret.html
Normal file
10
example/nginx/html/mail.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>
|
13
example/nginx/html/public.test.local/index.html
Normal file
13
example/nginx/html/public.test.local/index.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Public resource</title>
|
||||||
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Public resource</h1>
|
||||||
|
<p>This is a public resource.<br/>
|
||||||
|
Go back to <a href="https://home.test.local:8080/">home page</a>.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
example/nginx/html/public.test.local/secret.html
Normal file
10
example/nginx/html/public.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>
|
|
@ -50,11 +50,128 @@ http {
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html/home.test.local;
|
||||||
|
|
||||||
server_name secret1.test.local secret2.test.local secret.test.local
|
server_name home.test.local;
|
||||||
home.test.local mx1.mail.test.local mx2.mail.test.local
|
|
||||||
public.test.local;
|
ssl on;
|
||||||
|
ssl_certificate /etc/ssl/server.crt;
|
||||||
|
ssl_certificate_key /etc/ssl/server.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
root /usr/share/nginx/html/public.test.local;
|
||||||
|
|
||||||
|
server_name public.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_pass http://authelia/verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
error_page 403 = https://auth.test.local:8080/error/403;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
root /usr/share/nginx/html/admin.test.local;
|
||||||
|
|
||||||
|
server_name admin.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_pass http://authelia/verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
error_page 403 = https://auth.test.local:8080/error/403;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
root /usr/share/nginx/html/dev.test.local;
|
||||||
|
|
||||||
|
server_name dev.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_pass http://authelia/verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
error_page 403 = https://auth.test.local:8080/error/403;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
root /usr/share/nginx/html/mail.test.local;
|
||||||
|
|
||||||
|
server_name mx1.mail.test.local mx2.mail.test.local;
|
||||||
|
|
||||||
ssl on;
|
ssl on;
|
||||||
ssl_certificate /etc/ssl/server.crt;
|
ssl_certificate /etc/ssl/server.crt;
|
||||||
|
@ -70,7 +187,7 @@ http {
|
||||||
proxy_pass http://authelia/verify;
|
proxy_pass http://authelia/verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
location = /secret.html {
|
location / {
|
||||||
auth_request /auth_verify;
|
auth_request /auth_verify;
|
||||||
|
|
||||||
auth_request_set $redirect $upstream_http_redirect;
|
auth_request_set $redirect $upstream_http_redirect;
|
||||||
|
|
|
@ -1,35 +1,109 @@
|
||||||
|
|
||||||
import { ACLConfiguration } from "../configuration/Configuration";
|
import { ACLConfiguration, ACLPolicy, ACLRule } from "../configuration/Configuration";
|
||||||
import PatternBuilder from "./PatternBuilder";
|
import { IAccessController } from "./IAccessController";
|
||||||
import { Winston } from "../../../types/Dependencies";
|
import { Winston } from "../../../types/Dependencies";
|
||||||
|
import { DomainMatcher } from "./DomainMatcher";
|
||||||
|
|
||||||
export class AccessController {
|
|
||||||
private logger: Winston;
|
|
||||||
private patternBuilder: PatternBuilder;
|
|
||||||
|
|
||||||
constructor(configuration: ACLConfiguration, logger_: Winston) {
|
enum AccessReturn {
|
||||||
this.logger = logger_;
|
NO_MATCHING_RULES,
|
||||||
this.patternBuilder = new PatternBuilder(configuration, logger_);
|
MATCHING_RULES_AND_ACCESS,
|
||||||
}
|
MATCHING_RULES_AND_NO_ACCESS
|
||||||
|
}
|
||||||
isDomainAllowedForUser(domain: string, user: string, groups: string[]): boolean {
|
|
||||||
const allowed_domains = this.patternBuilder.getAllowedDomains(user, groups);
|
function AllowedRule(rule: ACLRule) {
|
||||||
|
return rule.policy == "allow";
|
||||||
// Allow all matcher
|
}
|
||||||
if (allowed_domains.length == 1 && allowed_domains[0] == "*") return true;
|
|
||||||
|
function MatchDomain(actualDomain: string) {
|
||||||
this.logger.debug("ACL: trying to match %s with %s", domain,
|
return function (rule: ACLRule): boolean {
|
||||||
JSON.stringify(allowed_domains));
|
return DomainMatcher.match(actualDomain, rule.domain);
|
||||||
for (let i = 0; i < allowed_domains.length; ++i) {
|
};
|
||||||
const allowed_domain = allowed_domains[i];
|
}
|
||||||
if (allowed_domain.startsWith("*") &&
|
|
||||||
domain.endsWith(allowed_domain.substr(1))) {
|
function MatchResource(actualResource: string) {
|
||||||
return true;
|
return function (rule: ACLRule): boolean {
|
||||||
}
|
// If resources key is not provided, the rule applies to all resources.
|
||||||
else if (domain == allowed_domain) {
|
if (!rule.resources) return true;
|
||||||
return true;
|
|
||||||
}
|
for (let i = 0; i < rule.resources.length; ++i) {
|
||||||
}
|
const regexp = new RegExp(rule.resources[i]);
|
||||||
return false;
|
if (regexp.test(actualResource)) return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectPolicy(rule: ACLRule): ("allow" | "deny") {
|
||||||
|
return rule.policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AccessController implements IAccessController {
|
||||||
|
private logger: Winston;
|
||||||
|
private readonly configuration: ACLConfiguration;
|
||||||
|
|
||||||
|
constructor(configuration: ACLConfiguration, logger_: Winston) {
|
||||||
|
this.logger = logger_;
|
||||||
|
this.configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isAccessAllowedInRules(rules: ACLRule[], domain: string, resource: string): AccessReturn {
|
||||||
|
if (!rules)
|
||||||
|
return AccessReturn.NO_MATCHING_RULES;
|
||||||
|
|
||||||
|
const policies = rules.map(SelectPolicy);
|
||||||
|
|
||||||
|
if (rules.length > 0) {
|
||||||
|
if (policies[0] == "allow") {
|
||||||
|
return AccessReturn.MATCHING_RULES_AND_ACCESS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return AccessReturn.MATCHING_RULES_AND_NO_ACCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AccessReturn.NO_MATCHING_RULES;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMatchingUserRules(user: string, domain: string, resource: string): ACLRule[] {
|
||||||
|
const userRules = this.configuration.users[user];
|
||||||
|
if (!userRules) return [];
|
||||||
|
return userRules.filter(MatchDomain(domain)).filter(MatchResource(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMatchingGroupRules(groups: string[], domain: string, resource: string): ACLRule[] {
|
||||||
|
const that = this;
|
||||||
|
// There is no ordering between group rules. That is, when a user belongs to 2 groups, there is no
|
||||||
|
// guarantee one set of rules has precedence on the other one.
|
||||||
|
const groupRules = groups.reduce(function (rules: ACLRule[], group: string) {
|
||||||
|
const groupRules = that.configuration.groups[group];
|
||||||
|
if (groupRules) rules = rules.concat(groupRules);
|
||||||
|
return rules;
|
||||||
|
}, []);
|
||||||
|
return groupRules.filter(MatchDomain(domain)).filter(MatchResource(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
private getMatchingAllRules(domain: string, resource: string): ACLRule[] {
|
||||||
|
const rules = this.configuration.any;
|
||||||
|
if (!rules) return [];
|
||||||
|
return rules.filter(MatchDomain(domain)).filter(MatchResource(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
private isAccessAllowedDefaultPolicy(): boolean {
|
||||||
|
return this.configuration.default_policy == "allow";
|
||||||
|
}
|
||||||
|
|
||||||
|
isAccessAllowed(domain: string, resource: string, user: string, groups: string[]): boolean {
|
||||||
|
const allRules = this.getMatchingAllRules(domain, resource);
|
||||||
|
const groupRules = this.getMatchingGroupRules(groups, domain, resource);
|
||||||
|
const userRules = this.getMatchingUserRules(user, domain, resource);
|
||||||
|
const rules = allRules.concat(groupRules).concat(userRules).reverse();
|
||||||
|
|
||||||
|
const access = this.isAccessAllowedInRules(rules, domain, resource);
|
||||||
|
if (access == AccessReturn.MATCHING_RULES_AND_ACCESS)
|
||||||
|
return true;
|
||||||
|
else if (access == AccessReturn.MATCHING_RULES_AND_NO_ACCESS)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return this.isAccessAllowedDefaultPolicy();
|
||||||
|
}
|
||||||
}
|
}
|
12
src/server/lib/access_control/DomainMatcher.ts
Normal file
12
src/server/lib/access_control/DomainMatcher.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
export class DomainMatcher {
|
||||||
|
static match(domain: string, allowedDomain: string): boolean {
|
||||||
|
if (allowedDomain.startsWith("*") &&
|
||||||
|
domain.endsWith(allowedDomain.substr(1))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (domain == allowedDomain) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
src/server/lib/access_control/IAccessController.ts
Normal file
4
src/server/lib/access_control/IAccessController.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
export interface IAccessController {
|
||||||
|
isAccessAllowed(domain: string, resource: string, user: string, groups: string[]): boolean;
|
||||||
|
}
|
|
@ -1,61 +0,0 @@
|
||||||
|
|
||||||
import { Winston } from "../../../types/Dependencies";
|
|
||||||
import { ACLConfiguration, ACLGroupsRules, ACLUsersRules, ACLDefaultRules } from "../configuration/Configuration";
|
|
||||||
import objectPath = require("object-path");
|
|
||||||
|
|
||||||
export default class AccessControlPatternBuilder {
|
|
||||||
logger: Winston;
|
|
||||||
configuration: ACLConfiguration;
|
|
||||||
|
|
||||||
constructor(configuration: ACLConfiguration | undefined, logger_: Winston) {
|
|
||||||
this.configuration = configuration;
|
|
||||||
this.logger = logger_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildFromGroups(groups: string[]): string[] {
|
|
||||||
let allowed_domains: string[] = [];
|
|
||||||
const groups_policy = objectPath.get<ACLConfiguration, ACLGroupsRules>(this.configuration, "groups");
|
|
||||||
if (groups_policy) {
|
|
||||||
for (let i = 0; i < groups.length; ++i) {
|
|
||||||
const group = groups[i];
|
|
||||||
if (group in groups_policy) {
|
|
||||||
const group_policy: string[] = groups_policy[group];
|
|
||||||
allowed_domains = allowed_domains.concat(groups_policy[group]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allowed_domains;
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildFromUser(user: string): string[] {
|
|
||||||
let allowed_domains: string[] = [];
|
|
||||||
const users_policy = objectPath.get<ACLConfiguration, ACLUsersRules>(this.configuration, "users");
|
|
||||||
if (users_policy) {
|
|
||||||
if (user in users_policy) {
|
|
||||||
allowed_domains = allowed_domains.concat(users_policy[user]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allowed_domains;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllowedDomains(user: string, groups: string[]): string[] {
|
|
||||||
if (!this.configuration) {
|
|
||||||
this.logger.debug("No access control rules found." +
|
|
||||||
"Default policy to allow all.");
|
|
||||||
return ["*"]; // No configuration means, no restrictions.
|
|
||||||
}
|
|
||||||
|
|
||||||
let allowed_domains: string[] = [];
|
|
||||||
const default_policy = objectPath.get<ACLConfiguration, ACLDefaultRules>(this.configuration, "default");
|
|
||||||
if (default_policy) {
|
|
||||||
allowed_domains = allowed_domains.concat(default_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
allowed_domains = allowed_domains.concat(this.buildFromGroups(groups));
|
|
||||||
allowed_domains = allowed_domains.concat(this.buildFromUser(user));
|
|
||||||
|
|
||||||
this.logger.debug("ACL: user \'%s\' is allowed access to %s", user,
|
|
||||||
JSON.stringify(allowed_domains));
|
|
||||||
return allowed_domains;
|
|
||||||
}
|
|
||||||
}
|
|
17
src/server/lib/configuration/Configuration.d.ts
vendored
17
src/server/lib/configuration/Configuration.d.ts
vendored
|
@ -35,12 +35,21 @@ type UserName = string;
|
||||||
type GroupName = string;
|
type GroupName = string;
|
||||||
type DomainPattern = string;
|
type DomainPattern = string;
|
||||||
|
|
||||||
export type ACLDefaultRules = DomainPattern[];
|
export type ACLPolicy = 'deny' | 'allow';
|
||||||
export type ACLGroupsRules = { [group: string]: string[]; };
|
|
||||||
export type ACLUsersRules = { [user: string]: string[]; };
|
export type ACLRule = {
|
||||||
|
domain: string;
|
||||||
|
policy: ACLPolicy;
|
||||||
|
resources?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ACLDefaultRules = ACLRule[];
|
||||||
|
export type ACLGroupsRules = { [group: string]: ACLRule[]; };
|
||||||
|
export type ACLUsersRules = { [user: string]: ACLRule[]; };
|
||||||
|
|
||||||
export interface ACLConfiguration {
|
export interface ACLConfiguration {
|
||||||
default: ACLDefaultRules;
|
default_policy: ACLPolicy;
|
||||||
|
any: ACLDefaultRules;
|
||||||
groups: ACLGroupsRules;
|
groups: ACLGroupsRules;
|
||||||
users: ACLUsersRules;
|
users: ACLUsersRules;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import exceptions = require("../../Exceptions");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import AuthenticationValidator = require("../../AuthenticationValidator");
|
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");
|
||||||
|
|
||||||
function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
function verify_filter(req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
|
@ -27,10 +27,11 @@ function verify_filter(req: express.Request, res: express.Response): BluebirdPro
|
||||||
const groups = authSession.groups;
|
const groups = authSession.groups;
|
||||||
|
|
||||||
const host = objectPath.get<express.Request, string>(req, "headers.host");
|
const host = objectPath.get<express.Request, string>(req, "headers.host");
|
||||||
const domain = host.split(":")[0];
|
const path = objectPath.get<express.Request, string>(req, "headers.x-original-uri");
|
||||||
console.log(domain);
|
|
||||||
|
|
||||||
const isAllowed = accessController.isDomainAllowedForUser(domain, username, groups);
|
const domain = host.split(":")[0];
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,18 @@ Feature: User has access restricted access to domains
|
||||||
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 have access to:
|
||||||
| url |
|
| url |
|
||||||
| https://public.test.local:8080/secret.html |
|
| https://public.test.local:8080/secret.html |
|
||||||
| https://secret.test.local:8080/secret.html |
|
| https://dev.test.local:8080/groups/admin/secret.html |
|
||||||
| https://secret1.test.local:8080/secret.html |
|
| https://dev.test.local:8080/groups/dev/secret.html |
|
||||||
| https://secret2.test.local:8080/secret.html |
|
| https://dev.test.local:8080/users/john/secret.html |
|
||||||
| https://mx1.mail.test.local:8080/secret.html |
|
| https://dev.test.local:8080/users/harry/secret.html |
|
||||||
| https://mx2.mail.test.local:8080/secret.html |
|
| https://dev.test.local:8080/users/bob/secret.html |
|
||||||
|
| https://admin.test.local:8080/secret.html |
|
||||||
|
| https://mx1.mail.test.local:8080/secret.html |
|
||||||
|
And I have no access to:
|
||||||
|
| url |
|
||||||
|
| https://mx2.mail.test.local:8080/secret.html |
|
||||||
|
|
||||||
@need-registered-user-bob
|
@need-registered-user-bob
|
||||||
Scenario: User bob has restricted access
|
Scenario: User bob has restricted access
|
||||||
|
@ -22,15 +27,18 @@ Feature: User has access restricted access to domains
|
||||||
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 have access to:
|
||||||
| url |
|
| url |
|
||||||
| https://public.test.local:8080/secret.html |
|
| https://public.test.local:8080/secret.html |
|
||||||
| https://secret.test.local:8080/secret.html |
|
| https://dev.test.local:8080/groups/dev/secret.html |
|
||||||
| https://secret2.test.local:8080/secret.html |
|
| https://dev.test.local:8080/users/bob/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 |
|
||||||
And I have no access to:
|
And I have no access to:
|
||||||
| url |
|
| url |
|
||||||
| https://secret1.test.local:8080/secret.html |
|
| https://dev.test.local:8080/groups/admin/secret.html |
|
||||||
|
| 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 |
|
||||||
|
|
||||||
@need-registered-user-harry
|
@need-registered-user-harry
|
||||||
Scenario: User harry has restricted access
|
Scenario: User harry has restricted access
|
||||||
|
@ -39,12 +47,15 @@ Feature: User has access restricted access to domains
|
||||||
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 have access to:
|
||||||
| url |
|
| url |
|
||||||
| https://public.test.local:8080/secret.html |
|
| https://public.test.local:8080/secret.html |
|
||||||
| https://secret1.test.local:8080/secret.html |
|
| https://dev.test.local:8080/users/harry/secret.html |
|
||||||
And I have no access to:
|
And I have no access to:
|
||||||
| url |
|
| url |
|
||||||
| https://secret.test.local:8080/secret.html |
|
| https://dev.test.local:8080/groups/dev/secret.html |
|
||||||
| https://secret2.test.local:8080/secret.html |
|
| https://dev.test.local:8080/users/bob/secret.html |
|
||||||
| https://mx1.mail.test.local:8080/secret.html |
|
| https://dev.test.local:8080/groups/admin/secret.html |
|
||||||
| https://mx2.mail.test.local:8080/secret.html |
|
| https://admin.test.local:8080/secret.html |
|
||||||
|
| 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 |
|
||||||
|
|
|
@ -18,19 +18,19 @@ Feature: User validate first factor
|
||||||
Given I visit "https://auth.test.local:8080/"
|
Given I visit "https://auth.test.local:8080/"
|
||||||
And I login with user "john" and password "password"
|
And I login with user "john" and password "password"
|
||||||
And I register a TOTP secret called "Sec0"
|
And I register a TOTP secret called "Sec0"
|
||||||
When I visit "https://secret.test.local:8080/secret.html" and get redirected "https://auth.test.local:8080/?redirect=https%3A%2F%2Fsecret.test.local%3A8080%2Fsecret.html"
|
When I visit "https://admin.test.local:8080/secret.html" and get redirected "https://auth.test.local:8080/?redirect=https%3A%2F%2Fadmin.test.local%3A8080%2Fsecret.html"
|
||||||
And I login with user "john" and password "password"
|
And I login with user "john" and password "password"
|
||||||
And I use "Sec0" as TOTP token handle
|
And I use "Sec0" as TOTP token handle
|
||||||
And I click on "TOTP"
|
And I click on "TOTP"
|
||||||
Then I'm redirected to "https://secret.test.local:8080/secret.html"
|
Then I'm redirected to "https://admin.test.local:8080/secret.html"
|
||||||
|
|
||||||
Scenario: User fails TOTP second factor
|
Scenario: User fails TOTP second factor
|
||||||
When I visit "https://secret.test.local:8080/secret.html" and get redirected "https://auth.test.local:8080/?redirect=https%3A%2F%2Fsecret.test.local%3A8080%2Fsecret.html"
|
When I visit "https://admin.test.local:8080/secret.html" and get redirected "https://auth.test.local:8080/?redirect=https%3A%2F%2Fadmin.test.local%3A8080%2Fsecret.html"
|
||||||
And I login with user "john" and password "password"
|
And I login with user "john" and password "password"
|
||||||
And I use "BADTOKEN" as TOTP token
|
And I use "BADTOKEN" as TOTP token
|
||||||
And I click on "TOTP"
|
And I click on "TOTP"
|
||||||
Then I get a notification of type "error" with message "Problem with TOTP validation."
|
Then I get a notification of type "error" with message "Problem with TOTP validation."
|
||||||
|
|
||||||
Scenario: Logout redirects user to redirect URL given in parameter
|
Scenario: Logout redirects user to redirect URL given in parameter
|
||||||
When I visit "https://auth.test.local:8080/logout?redirect=https://www.google.fr"
|
When I visit "https://auth.test.local:8080/logout?redirect=https://home.test.local:8080/"
|
||||||
Then I'm redirected to "https://www.google.fr"
|
Then I'm redirected to "https://home.test.local:8080/"
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
Feature: User is correctly redirected
|
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
|
||||||
Given I'm on https://home.test.local:8080
|
When I visit "https://public.test.local:8080"
|
||||||
When I click on the link to secret.test.local
|
|
||||||
Then I'm redirected to "https://auth.test.local:8080/"
|
Then I'm redirected to "https://auth.test.local:8080/"
|
||||||
|
|
||||||
@need-registered-user-john
|
@need-registered-user-john
|
||||||
|
@ -15,9 +14,9 @@ Feature: User is correctly redirected
|
||||||
And I click on "TOTP"
|
And I click on "TOTP"
|
||||||
Then I'm redirected to "https://public.test.local:8080/secret.html"
|
Then I'm redirected to "https://public.test.local:8080/secret.html"
|
||||||
|
|
||||||
Scenario: User Harry does not have access to https://secret.test.local:8080/secret.html and thus he must get an error 403
|
Scenario: User Harry does not have access to admin domain and thus he must get an error 403
|
||||||
When I register TOTP and login with user "harry" and password "password"
|
When I register TOTP and login with user "harry" and password "password"
|
||||||
And I visit "https://secret.test.local:8080/secret.html"
|
And I visit "https://admin.test.local:8080/secret.html"
|
||||||
Then I get an error 403
|
Then I get an error 403
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,15 @@ Feature: Authelia keeps user sessions despite the application restart
|
||||||
When the application restarts
|
When the application restarts
|
||||||
Then I have access to:
|
Then I have access to:
|
||||||
| url |
|
| url |
|
||||||
| https://secret.test.local:8080/secret.html |
|
| https://admin.test.local:8080/secret.html |
|
||||||
|
|
||||||
@need-registered-user-john
|
@need-registered-user-john
|
||||||
Scenario: Secrets are stored even when Authelia restarts
|
Scenario: Secrets are stored even when Authelia restarts
|
||||||
When the application restarts
|
When the application restarts
|
||||||
And I visit "https://secret.test.local:8080/secret.html" and get redirected "https://auth.test.local:8080/?redirect=https%3A%2F%2Fsecret.test.local%3A8080%2Fsecret.html"
|
And I visit "https://admin.test.local:8080/secret.html" and get redirected "https://auth.test.local:8080/?redirect=https%3A%2F%2Fadmin.test.local%3A8080%2Fsecret.html"
|
||||||
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 have access to:
|
||||||
| url |
|
| url |
|
||||||
| https://secret.test.local:8080/secret.html |
|
| https://admin.test.local:8080/secret.html |
|
|
@ -81,7 +81,7 @@ and I use TOTP token handle {stringInDoubleQuotes}",
|
||||||
}
|
}
|
||||||
|
|
||||||
Then("I have access to:", function (dataTable: Cucumber.TableDefinition) {
|
Then("I have access to:", function (dataTable: Cucumber.TableDefinition) {
|
||||||
const promises = [];
|
const promises: any = [];
|
||||||
for (let i = 0; i < dataTable.rows().length; i++) {
|
for (let i = 0; i < dataTable.rows().length; i++) {
|
||||||
const url = (dataTable.hashes() as any)[i].url;
|
const url = (dataTable.hashes() as any)[i].url;
|
||||||
promises.push(hasAccessToSecret(url, this));
|
promises.push(hasAccessToSecret(url, this));
|
||||||
|
|
|
@ -47,6 +47,9 @@ function CustomWorld() {
|
||||||
.findElement(seleniumWebdriver.By.tagName("button"))
|
.findElement(seleniumWebdriver.By.tagName("button"))
|
||||||
.findElement(seleniumWebdriver.By.xpath("//button[contains(.,'" + buttonText + "')]"))
|
.findElement(seleniumWebdriver.By.xpath("//button[contains(.,'" + buttonText + "')]"))
|
||||||
.click();
|
.click();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return that.driver.sleep(500);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ describe("test session configuration builder", function () {
|
||||||
it("should return session options without redis options", function () {
|
it("should return session options without redis options", function () {
|
||||||
const configuration: AppConfiguration = {
|
const configuration: AppConfiguration = {
|
||||||
access_control: {
|
access_control: {
|
||||||
default: [],
|
default_policy: "deny",
|
||||||
|
any: [],
|
||||||
users: {},
|
users: {},
|
||||||
groups: {}
|
groups: {}
|
||||||
},
|
},
|
||||||
|
@ -81,7 +82,8 @@ describe("test session configuration builder", function () {
|
||||||
it("should return session options with redis options", function () {
|
it("should return session options with redis options", function () {
|
||||||
const configuration: AppConfiguration = {
|
const configuration: AppConfiguration = {
|
||||||
access_control: {
|
access_control: {
|
||||||
default: [],
|
default_policy: "deny",
|
||||||
|
any: [],
|
||||||
users: {},
|
users: {},
|
||||||
groups: {}
|
groups: {}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,53 +1,353 @@
|
||||||
|
|
||||||
import assert = require("assert");
|
import Assert = require("assert");
|
||||||
import winston = require("winston");
|
import winston = require("winston");
|
||||||
import { AccessController } from "../../../../src/server/lib/access_control/AccessController";
|
import { AccessController } from "../../../../src/server/lib/access_control/AccessController";
|
||||||
import { ACLConfiguration } from "../../../../src/server/lib/configuration/Configuration";
|
import { ACLConfiguration, ACLRule } from "../../../../src/server/lib/configuration/Configuration";
|
||||||
|
|
||||||
describe("test access control manager", function () {
|
describe("test access control manager", function () {
|
||||||
let accessController: AccessController;
|
let accessController: AccessController;
|
||||||
let configuration: ACLConfiguration;
|
let configuration: ACLConfiguration;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
configuration = {
|
||||||
|
default_policy: "deny",
|
||||||
|
any: [],
|
||||||
|
users: {},
|
||||||
|
groups: {}
|
||||||
|
};
|
||||||
|
accessController = new AccessController(configuration, winston);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("check access control with default policy to deny", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
configuration = {
|
configuration.default_policy = "deny";
|
||||||
default: [],
|
|
||||||
users: {},
|
|
||||||
groups: {}
|
|
||||||
};
|
|
||||||
accessController = new AccessController(configuration, winston);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("check access control matching", function () {
|
it("should deny access when no rule is provided", function () {
|
||||||
beforeEach(function () {
|
Assert(!accessController.isAccessAllowed("home.example.com", "/", "user1", ["group1"]));
|
||||||
configuration.default = ["home.example.com", "*.public.example.com"];
|
|
||||||
configuration.users = {
|
|
||||||
user1: ["user1.example.com", "user1.mail.example.com"]
|
|
||||||
};
|
|
||||||
configuration.groups = {
|
|
||||||
group1: ["secret2.example.com"],
|
|
||||||
group2: ["secret.example.com", "secret1.example.com"]
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow access to secret.example.com", function () {
|
|
||||||
assert(accessController.isDomainAllowedForUser("secret.example.com", "user", ["group1", "group2"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should deny access to secret3.example.com", function () {
|
|
||||||
assert(!accessController.isDomainAllowedForUser("secret3.example.com", "user", ["group1", "group2"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow access to home.example.com", function () {
|
|
||||||
assert(accessController.isDomainAllowedForUser("home.example.com", "user", ["group1", "group2"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow access to user1.example.com", function () {
|
|
||||||
assert(accessController.isDomainAllowedForUser("user1.example.com", "user1", ["group1", "group2"]));
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow access *.public.example.com", function () {
|
|
||||||
assert(accessController.isDomainAllowedForUser("user.public.example.com", "nouser", []));
|
|
||||||
assert(accessController.isDomainAllowedForUser("test.public.example.com", "nouser", []));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 () {
|
||||||
|
configuration.users["user1"] = [{
|
||||||
|
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 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,120 +0,0 @@
|
||||||
|
|
||||||
import assert = require("assert");
|
|
||||||
import winston = require("winston");
|
|
||||||
|
|
||||||
import PatternBuilder from "../../../../src/server/lib/access_control/PatternBuilder";
|
|
||||||
import { ACLConfiguration } from "../../../../src/server/lib/configuration/Configuration";
|
|
||||||
|
|
||||||
describe("test access control manager", function () {
|
|
||||||
describe("test access control pattern builder when no configuration is provided", () => {
|
|
||||||
it("should allow access to the user", () => {
|
|
||||||
const patternBuilder = new PatternBuilder(undefined, winston);
|
|
||||||
|
|
||||||
const allowed_domains = patternBuilder.getAllowedDomains("user", ["group1"]);
|
|
||||||
assert.deepEqual(allowed_domains, ["*"]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("test access control pattern builder", function () {
|
|
||||||
let patternBuilder: PatternBuilder;
|
|
||||||
let configuration: ACLConfiguration;
|
|
||||||
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
configuration = {
|
|
||||||
default: [],
|
|
||||||
users: {},
|
|
||||||
groups: {}
|
|
||||||
};
|
|
||||||
patternBuilder = new PatternBuilder(configuration, winston);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should deny all if nothing is defined in the config", function () {
|
|
||||||
const allowed_domains = patternBuilder.getAllowedDomains("user", ["group1", "group2"]);
|
|
||||||
assert.deepEqual(allowed_domains, []);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow domain test.example.com to all users if defined in" +
|
|
||||||
" default policy", function () {
|
|
||||||
configuration.default = ["test.example.com"];
|
|
||||||
const allowed_domains = patternBuilder.getAllowedDomains("user", ["group1", "group2"]);
|
|
||||||
assert.deepEqual(allowed_domains, ["test.example.com"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow domain test.example.com to all users in group mygroup", function () {
|
|
||||||
const allowed_domains0 = patternBuilder.getAllowedDomains("user", ["group1", "group1"]);
|
|
||||||
assert.deepEqual(allowed_domains0, []);
|
|
||||||
|
|
||||||
configuration.groups = {
|
|
||||||
mygroup: ["test.example.com"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const allowed_domains1 = patternBuilder.getAllowedDomains("user", ["group1", "group2"]);
|
|
||||||
assert.deepEqual(allowed_domains1, []);
|
|
||||||
|
|
||||||
const allowed_domains2 = patternBuilder.getAllowedDomains("user", ["group1", "mygroup"]);
|
|
||||||
assert.deepEqual(allowed_domains2, ["test.example.com"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow domain test.example.com based on per user config", function () {
|
|
||||||
const allowed_domains0 = patternBuilder.getAllowedDomains("user", ["group1"]);
|
|
||||||
assert.deepEqual(allowed_domains0, []);
|
|
||||||
|
|
||||||
configuration.users = {
|
|
||||||
user1: ["test.example.com"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const allowed_domains1 = patternBuilder.getAllowedDomains("user", ["group1", "mygroup"]);
|
|
||||||
assert.deepEqual(allowed_domains1, []);
|
|
||||||
|
|
||||||
const allowed_domains2 = patternBuilder.getAllowedDomains("user1", ["group1", "mygroup"]);
|
|
||||||
assert.deepEqual(allowed_domains2, ["test.example.com"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow domains from user and groups", function () {
|
|
||||||
configuration.groups = {
|
|
||||||
group2: ["secret.example.com", "secret1.example.com"]
|
|
||||||
};
|
|
||||||
configuration.users = {
|
|
||||||
user: ["test.example.com"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const allowed_domains0 = patternBuilder.getAllowedDomains("user", ["group1", "group2"]);
|
|
||||||
assert.deepEqual(allowed_domains0, [
|
|
||||||
"secret.example.com",
|
|
||||||
"secret1.example.com",
|
|
||||||
"test.example.com",
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow domains from several groups", function () {
|
|
||||||
configuration.groups = {
|
|
||||||
group1: ["secret2.example.com"],
|
|
||||||
group2: ["secret.example.com", "secret1.example.com"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const allowed_domains0 = patternBuilder.getAllowedDomains("user", ["group1", "group2"]);
|
|
||||||
assert.deepEqual(allowed_domains0, [
|
|
||||||
"secret2.example.com",
|
|
||||||
"secret.example.com",
|
|
||||||
"secret1.example.com",
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow domains from several groups and default policy", function () {
|
|
||||||
configuration.default = ["home.example.com"];
|
|
||||||
configuration.groups = {
|
|
||||||
group1: ["secret2.example.com"],
|
|
||||||
group2: ["secret.example.com", "secret1.example.com"]
|
|
||||||
};
|
|
||||||
|
|
||||||
const allowed_domains0 = patternBuilder.getAllowedDomains("user", ["group1", "group2"]);
|
|
||||||
assert.deepEqual(allowed_domains0, [
|
|
||||||
"home.example.com",
|
|
||||||
"secret2.example.com",
|
|
||||||
"secret.example.com",
|
|
||||||
"secret1.example.com",
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,5 +1,8 @@
|
||||||
import * as Assert from "assert";
|
import * as Assert from "assert";
|
||||||
import { UserConfiguration, LdapConfiguration } from "../../../../src/server/lib/configuration/Configuration";
|
import {
|
||||||
|
UserConfiguration,
|
||||||
|
LdapConfiguration, ACLConfiguration
|
||||||
|
} from "../../../../src/server/lib/configuration/Configuration";
|
||||||
import { ConfigurationAdapter } from "../../../../src/server/lib/configuration/ConfigurationAdapter";
|
import { ConfigurationAdapter } from "../../../../src/server/lib/configuration/ConfigurationAdapter";
|
||||||
|
|
||||||
describe("test config adapter", function () {
|
describe("test config adapter", function () {
|
||||||
|
@ -94,15 +97,17 @@ describe("test config adapter", function () {
|
||||||
it("should get the access_control config", function () {
|
it("should get the access_control config", function () {
|
||||||
const yaml_config = build_yaml_config();
|
const yaml_config = build_yaml_config();
|
||||||
yaml_config.access_control = {
|
yaml_config.access_control = {
|
||||||
default: [],
|
default_policy: "deny",
|
||||||
|
any: [],
|
||||||
users: {},
|
users: {},
|
||||||
groups: {}
|
groups: {}
|
||||||
};
|
};
|
||||||
const config = ConfigurationAdapter.adapt(yaml_config);
|
const config = ConfigurationAdapter.adapt(yaml_config);
|
||||||
Assert.deepEqual(config.access_control, {
|
Assert.deepEqual(config.access_control, {
|
||||||
default: [],
|
default_policy: "deny",
|
||||||
|
any: [],
|
||||||
users: {},
|
users: {},
|
||||||
groups: {}
|
groups: {}
|
||||||
});
|
} as ACLConfiguration);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
|
|
||||||
import sinon = require("sinon");
|
|
||||||
|
|
||||||
export interface AccessControllerMock {
|
|
||||||
isDomainAllowedForUser: sinon.SinonStub;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AccessControllerMock() {
|
|
||||||
return {
|
|
||||||
isDomainAllowedForUser: sinon.stub()
|
|
||||||
};
|
|
||||||
}
|
|
15
test/unit/server/mocks/AccessControllerStub.ts
Normal file
15
test/unit/server/mocks/AccessControllerStub.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
import Sinon = require("sinon");
|
||||||
|
import { IAccessController } from "../../../../src/server/lib/access_control/IAccessController";
|
||||||
|
|
||||||
|
export class AccessControllerStub implements IAccessController {
|
||||||
|
isAccessAllowedMock: Sinon.SinonStub;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.isAccessAllowedMock = Sinon.stub();
|
||||||
|
}
|
||||||
|
|
||||||
|
isAccessAllowed(domain: string, resource: string, user: string, groups: string[]): boolean {
|
||||||
|
return this.isAccessAllowedMock(domain, resource, user, groups);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import AuthenticationSession = require("../../../../../src/server/lib/Authentica
|
||||||
import Endpoints = require("../../../../../src/server/endpoints");
|
import Endpoints = require("../../../../../src/server/endpoints");
|
||||||
|
|
||||||
import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator");
|
import AuthenticationRegulatorMock = require("../../mocks/AuthenticationRegulator");
|
||||||
import AccessControllerMock = require("../../mocks/AccessController");
|
import { AccessControllerStub } from "../../mocks/AccessControllerStub";
|
||||||
import ExpressMock = require("../../mocks/express");
|
import ExpressMock = require("../../mocks/express");
|
||||||
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
||||||
import { ServerVariables } from "../../../../../src/server/lib/ServerVariablesHandler";
|
import { ServerVariables } from "../../../../../src/server/lib/ServerVariablesHandler";
|
||||||
|
@ -22,7 +22,7 @@ describe("test the first factor validation route", function () {
|
||||||
let groups: string[];
|
let groups: string[];
|
||||||
let configuration;
|
let configuration;
|
||||||
let regulator: AuthenticationRegulatorMock.AuthenticationRegulatorMock;
|
let regulator: AuthenticationRegulatorMock.AuthenticationRegulatorMock;
|
||||||
let accessController: AccessControllerMock.AccessControllerMock;
|
let accessController: AccessControllerStub;
|
||||||
let serverVariables: ServerVariables;
|
let serverVariables: ServerVariables;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
@ -36,8 +36,8 @@ describe("test the first factor validation route", function () {
|
||||||
emails = ["test_ok@example.com"];
|
emails = ["test_ok@example.com"];
|
||||||
groups = ["group1", "group2" ];
|
groups = ["group1", "group2" ];
|
||||||
|
|
||||||
accessController = AccessControllerMock.AccessControllerMock();
|
accessController = new AccessControllerStub();
|
||||||
accessController.isDomainAllowedForUser.returns(true);
|
accessController.isAccessAllowedMock.returns(true);
|
||||||
|
|
||||||
regulator = AuthenticationRegulatorMock.AuthenticationRegulatorMock();
|
regulator = AuthenticationRegulatorMock.AuthenticationRegulatorMock();
|
||||||
regulator.regulate.returns(BluebirdPromise.resolve());
|
regulator.regulate.returns(BluebirdPromise.resolve());
|
||||||
|
|
|
@ -10,17 +10,17 @@ import BluebirdPromise = require("bluebird");
|
||||||
import express = require("express");
|
import express = require("express");
|
||||||
|
|
||||||
import ExpressMock = require("../../mocks/express");
|
import ExpressMock = require("../../mocks/express");
|
||||||
import AccessControllerMock = require("../../mocks/AccessController");
|
import { AccessControllerStub } from "../../mocks/AccessControllerStub";
|
||||||
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
import ServerVariablesMock = require("../../mocks/ServerVariablesMock");
|
||||||
|
|
||||||
describe("test authentication token verification", function () {
|
describe("test authentication token verification", function () {
|
||||||
let req: ExpressMock.RequestMock;
|
let req: ExpressMock.RequestMock;
|
||||||
let res: ExpressMock.ResponseMock;
|
let res: ExpressMock.ResponseMock;
|
||||||
let accessController: AccessControllerMock.AccessControllerMock;
|
let accessController: AccessControllerStub;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
accessController = AccessControllerMock.AccessControllerMock();
|
accessController = new AccessControllerStub();
|
||||||
accessController.isDomainAllowedForUser.returns(true);
|
accessController.isAccessAllowedMock.returns(true);
|
||||||
|
|
||||||
req = ExpressMock.RequestMock();
|
req = ExpressMock.RequestMock();
|
||||||
res = ExpressMock.ResponseMock();
|
res = ExpressMock.ResponseMock();
|
||||||
|
@ -128,8 +128,8 @@ describe("test authentication token verification", function () {
|
||||||
|
|
||||||
req.headers.host = "test.example.com";
|
req.headers.host = "test.example.com";
|
||||||
|
|
||||||
accessController.isDomainAllowedForUser.returns(false);
|
accessController.isAccessAllowedMock.returns(false);
|
||||||
accessController.isDomainAllowedForUser.withArgs("test.example.com", "user", ["group1", "group2"]).returns(true);
|
accessController.isAccessAllowedMock.withArgs("test.example.com", "user", ["group1", "group2"]).returns(true);
|
||||||
|
|
||||||
return test_unauthorized_403({
|
return test_unauthorized_403({
|
||||||
first_factor: true,
|
first_factor: true,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user