Add tests for minimal configuration
parent
21653bc7e3
commit
6d6162f26c
|
@ -22,6 +22,7 @@ addons:
|
||||||
- mx2.mail.example.com
|
- mx2.mail.example.com
|
||||||
- public.example.com
|
- public.example.com
|
||||||
- authelia.example.com
|
- authelia.example.com
|
||||||
|
- admin.example.com
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- npm install -g npm@'>=2.13.5'
|
- npm install -g npm@'>=2.13.5'
|
||||||
|
|
|
@ -12,8 +12,7 @@ RUN apk --update add --no-cache --virtual \
|
||||||
COPY dist/server /usr/src/server
|
COPY dist/server /usr/src/server
|
||||||
COPY dist/shared /usr/src/shared
|
COPY dist/shared /usr/src/shared
|
||||||
|
|
||||||
ENV PORT=80
|
EXPOSE 8080
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
VOLUME /etc/authelia
|
VOLUME /etc/authelia
|
||||||
VOLUME /var/lib/authelia
|
VOLUME /var/lib/authelia
|
||||||
|
|
|
@ -41,10 +41,14 @@ module.exports = function (grunt) {
|
||||||
cmd: "./node_modules/.bin/mocha",
|
cmd: "./node_modules/.bin/mocha",
|
||||||
args: ['--colors', '--require', 'ts-node/register', 'client/test/**/*.test.ts']
|
args: ['--colors', '--require', 'ts-node/register', 'client/test/**/*.test.ts']
|
||||||
},
|
},
|
||||||
"test-int": {
|
"test-cucumber": {
|
||||||
cmd: "./scripts/run-cucumber.sh",
|
cmd: "./scripts/run-cucumber.sh",
|
||||||
args: ["./test/features"]
|
args: ["./test/features"]
|
||||||
},
|
},
|
||||||
|
"test-minimal-config": {
|
||||||
|
cmd: "./node_modules/.bin/mocha",
|
||||||
|
args: ['--colors', '--require', 'ts-node/register', 'test/minimal-config/**/*.ts']
|
||||||
|
},
|
||||||
"docker-build": {
|
"docker-build": {
|
||||||
cmd: "docker",
|
cmd: "docker",
|
||||||
args: ['build', '-t', 'clems4ever/authelia', '.']
|
args: ['build', '-t', 'clems4ever/authelia', '.']
|
||||||
|
@ -183,7 +187,7 @@ module.exports = function (grunt) {
|
||||||
grunt.registerTask('test-server', ['env:env-test-server-unit', 'run:test-server-unit'])
|
grunt.registerTask('test-server', ['env:env-test-server-unit', 'run:test-server-unit'])
|
||||||
grunt.registerTask('test-client', ['env:env-test-client-unit', 'run:test-client-unit'])
|
grunt.registerTask('test-client', ['env:env-test-client-unit', 'run:test-client-unit'])
|
||||||
grunt.registerTask('test-unit', ['test-server', 'test-client']);
|
grunt.registerTask('test-unit', ['test-server', 'test-client']);
|
||||||
grunt.registerTask('test-int', ['run:test-int']);
|
grunt.registerTask('test-int', ['run:test-cucumber', 'run:test-minimal-config']);
|
||||||
|
|
||||||
grunt.registerTask('copy-resources', ['copy:resources', 'copy:views', 'copy:images', 'copy:thirdparties', 'concat:css']);
|
grunt.registerTask('copy-resources', ['copy:resources', 'copy:views', 'copy:images', 'copy:thirdparties', 'concat:css']);
|
||||||
grunt.registerTask('generate-config-schema', ['run:generate-config-schema', 'copy:schema']);
|
grunt.registerTask('generate-config-schema', ['run:generate-config-schema', 'copy:schema']);
|
||||||
|
|
|
@ -15,10 +15,9 @@ ldap:
|
||||||
password: password
|
password: password
|
||||||
|
|
||||||
session:
|
session:
|
||||||
# The secret to encrypt the session cookies.
|
# The secret to encrypt the session cookies with.
|
||||||
secret: unsecure_session_secret
|
secret: unsecure_session_secret
|
||||||
|
|
||||||
# The domain to protect.
|
# The domain to protect.
|
||||||
# Note: the authenticator must also be in that domain. If empty, the cookie
|
# Note: Authelia must also be served by that domain.
|
||||||
# is restricted to the subdomain of the issuer.
|
|
||||||
domain: example.com
|
domain: example.com
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
###############################################################
|
###############################################################
|
||||||
|
|
||||||
# The port to listen on
|
# The port to listen on
|
||||||
port: 80
|
port: 8080
|
||||||
|
|
||||||
# Log level
|
# Log level
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
authelia:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./server:/usr/src/server
|
||||||
|
- ./dist/server/src/public_html:/usr/src/server/src/public_html
|
||||||
|
- ./client:/usr/src/client
|
||||||
|
- ./shared:/usr/src/shared
|
||||||
|
- ./node_modules:/usr/src/node_modules
|
||||||
|
- ./config.minimal.yml:/etc/authelia/config.yml:ro
|
||||||
|
- /tmp/authelia:/tmp/authelia
|
||||||
|
environment:
|
||||||
|
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- example-network
|
||||||
|
command:
|
||||||
|
- "./node_modules/.bin/ts-node"
|
||||||
|
- "-P"
|
||||||
|
- "server/tsconfig.json"
|
||||||
|
- "server/src/index.ts"
|
||||||
|
- "/etc/authelia/config.yml"
|
|
@ -10,4 +10,4 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
networks:
|
networks:
|
||||||
- example-network
|
- example-network
|
|
@ -0,0 +1,12 @@
|
||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
authelia:
|
||||||
|
build: .
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./config.minimal.yml:/etc/authelia/config.yml:ro
|
||||||
|
- /tmp/authelia:/tmp/authelia
|
||||||
|
environment:
|
||||||
|
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
|
networks:
|
||||||
|
- example-network
|
|
@ -9,5 +9,6 @@ services:
|
||||||
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis
|
- redis
|
||||||
|
- mongo
|
||||||
networks:
|
networks:
|
||||||
- example-network
|
- example-network
|
|
@ -1,8 +0,0 @@
|
||||||
version: '2'
|
|
||||||
services:
|
|
||||||
authelia:
|
|
||||||
volumes:
|
|
||||||
- ./dist/server:/usr/src/server
|
|
||||||
- ./dist/shared:/usr/src/shared
|
|
||||||
networks:
|
|
||||||
- example-network
|
|
|
@ -1,8 +0,0 @@
|
||||||
version: '2'
|
|
||||||
services:
|
|
||||||
nginx-authelia:
|
|
||||||
image: nginx:alpine
|
|
||||||
volumes:
|
|
||||||
- ./example/compose/nginx/authelia/nginx.conf:/etc/nginx/nginx.conf
|
|
||||||
networks:
|
|
||||||
- example-network
|
|
|
@ -1,22 +0,0 @@
|
||||||
worker_processes 1;
|
|
||||||
|
|
||||||
events {
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
http {
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
|
|
||||||
resolver 127.0.0.11 ipv6=off;
|
|
||||||
set $upstream_endpoint http://authelia;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_set_header Host $http_host;
|
|
||||||
|
|
||||||
proxy_pass $upstream_endpoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
version: '2'
|
||||||
|
services:
|
||||||
|
nginx-portal:
|
||||||
|
image: nginx:alpine
|
||||||
|
volumes:
|
||||||
|
- ./example/compose/nginx/minimal/nginx.conf:/etc/nginx/nginx.conf
|
||||||
|
- ./example/compose/nginx/minimal/html:/usr/share/nginx/html
|
||||||
|
- ./example/compose/nginx/minimal/ssl:/etc/ssl
|
||||||
|
ports:
|
||||||
|
- "8080:443"
|
||||||
|
networks:
|
||||||
|
- example-network
|
|
@ -0,0 +1,11 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Secret</title>
|
||||||
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Secret</h1>
|
||||||
|
This is a very important secret!<br/>
|
||||||
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<!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
|
||||||
|
the following links to test Authelia.<br/>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
admin.example.com <a href="https://admin.example.com:8080/secret.html"> / secret.html</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
You can also log off by visiting the following <a href="https://login.example.com:8080/logout?rd=https://home.example.com:8080/">link</a>.
|
||||||
|
|
||||||
|
<h1>List of users</h1>
|
||||||
|
Here is the list of credentials you can log in with.<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 stored in a <strong>local file</strong> called <strong>/tmp/authelia/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>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,99 @@
|
||||||
|
worker_processes 1;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name login.example.com;
|
||||||
|
|
||||||
|
resolver 127.0.0.11 ipv6=off;
|
||||||
|
set $upstream_endpoint http://authelia:8080;
|
||||||
|
|
||||||
|
ssl on;
|
||||||
|
ssl_certificate /etc/ssl/server.crt;
|
||||||
|
ssl_certificate_key /etc/ssl/server.key;
|
||||||
|
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Original-URI $request_uri;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_intercept_errors on;
|
||||||
|
|
||||||
|
proxy_pass $upstream_endpoint;
|
||||||
|
|
||||||
|
if ($request_method !~ ^(POST)$){
|
||||||
|
error_page 401 = /error/401;
|
||||||
|
error_page 403 = /error/403;
|
||||||
|
error_page 404 = /error/404;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name home.example.com;
|
||||||
|
|
||||||
|
resolver 127.0.0.11 ipv6=off;
|
||||||
|
set $upstream_endpoint http://nginx-backend;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html/home;
|
||||||
|
|
||||||
|
ssl on;
|
||||||
|
ssl_certificate /etc/ssl/server.crt;
|
||||||
|
ssl_certificate_key /etc/ssl/server.key;
|
||||||
|
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN";
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl;
|
||||||
|
server_name admin.example.com;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html/admin;
|
||||||
|
|
||||||
|
resolver 127.0.0.11 ipv6=off;
|
||||||
|
set $upstream_verify http://authelia:8080/api/verify;
|
||||||
|
|
||||||
|
ssl on;
|
||||||
|
ssl_certificate /etc/ssl/server.crt;
|
||||||
|
ssl_certificate_key /etc/ssl/server.key;
|
||||||
|
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN";
|
||||||
|
|
||||||
|
location /auth_verify {
|
||||||
|
internal;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
|
||||||
|
proxy_set_header X-Original-URI $request_uri;
|
||||||
|
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_pass_request_body off;
|
||||||
|
proxy_set_header Content-Length "";
|
||||||
|
|
||||||
|
proxy_pass $upstream_verify;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
auth_request /auth_verify;
|
||||||
|
|
||||||
|
auth_request_set $redirect $upstream_http_redirect;
|
||||||
|
auth_request_set $user $upstream_http_remote_user;
|
||||||
|
auth_request_set $groups $upstream_http_remote_groups;
|
||||||
|
|
||||||
|
error_page 401 =302 https://login.example.com:8080?rd=$redirect;
|
||||||
|
error_page 403 = https://login.example.com:8080/error/403;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICATCCAWoCCQCvH2RvyOshNzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
|
||||||
|
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
||||||
|
cyBQdHkgTHRkMB4XDTE3MDExNzIzMTc0M1oXDTE4MDExNzIzMTc0M1owRTELMAkG
|
||||||
|
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
|
||||||
|
IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzZaE
|
||||||
|
4XE1QyFNbrHBHRhSA53anAsJ5mBeG7Om6SdQcZAYahlDWEbtdoY4hy0gPNGcITcW
|
||||||
|
eE+WA+PvNRr7PczKEhneIyUUgV+nrz010fM5JnECPxLTe1oFzl4U8dyYiBpTziNz
|
||||||
|
hiUfq733PRYjcd9BQtcKcN4LdmQvjUHnnQ73TysCAwEAATANBgkqhkiG9w0BAQsF
|
||||||
|
AAOBgQAUFICtbuqXgL4HBRAg7yGbwokoH8Ar1QKZGe+F2WTR8vaDLOYUL7VsltLE
|
||||||
|
EJIGrcfs31nItHOBcLJuflrS8y0CQqes5puRw33LL2usSvO8z2q7JhCx+DSBi6yN
|
||||||
|
RbhcrGOllIdjsrbmd/zAMBVTUyxSisq3Nmk1cZayDvKg+GSAEA==
|
||||||
|
-----END CERTIFICATE-----
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBhDCB7gIBADBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
|
||||||
|
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4GNADCBiQKBgQDNloThcTVDIU1uscEdGFIDndqcCwnmYF4bs6bpJ1BxkBhq
|
||||||
|
GUNYRu12hjiHLSA80ZwhNxZ4T5YD4+81Gvs9zMoSGd4jJRSBX6evPTXR8zkmcQI/
|
||||||
|
EtN7WgXOXhTx3JiIGlPOI3OGJR+rvfc9FiNx30FC1wpw3gt2ZC+NQeedDvdPKwID
|
||||||
|
AQABoAAwDQYJKoZIhvcNAQELBQADgYEAmCX60kspIw1Zfb79AQOarFW5Q2K2h5Vx
|
||||||
|
/cRbDyHlKtbmG77EtICccULyqf76B1gNRw5Zq3lSotSUcLzsWcdesXCFDC7k87Qf
|
||||||
|
mpQKPj6GdTYJvdWf8aDwt32tAqWuBIRoAbdx5WbFPPWVfDcm7zDJefBrhNUDH0Qd
|
||||||
|
vcnxjvPMmOM=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,15 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXQIBAAKBgQDNloThcTVDIU1uscEdGFIDndqcCwnmYF4bs6bpJ1BxkBhqGUNY
|
||||||
|
Ru12hjiHLSA80ZwhNxZ4T5YD4+81Gvs9zMoSGd4jJRSBX6evPTXR8zkmcQI/EtN7
|
||||||
|
WgXOXhTx3JiIGlPOI3OGJR+rvfc9FiNx30FC1wpw3gt2ZC+NQeedDvdPKwIDAQAB
|
||||||
|
AoGBAIwGcfkO30UawJ+daDeF4g5ejI/toM+NYWuiwBNbWJoQl+Bj1o+gt4obvxKq
|
||||||
|
tKNX7OxelepZ4oZB0CIuf2LHQfU6cVGdu//or7nfS2FLBYStopZyL6KorZbkqsj1
|
||||||
|
ikQN4GosJQqaYkexnwjItMFaHaRRX6YnIXp42Jl1glitO3+5AkEA+thn/vwFo24I
|
||||||
|
fC+7ORpmLi+BVAkTuhMm+C6TIV6s64B+A5oQ82OBCYK9YCOWmS6JHHFDrxJla+3M
|
||||||
|
2U9KXky63wJBANHQCFCirfuT6esSjbqpCeqtmZG5LWHtL12V9DF7yjHPjmHL9uRu
|
||||||
|
e9W+Uz33IJbqd82gtZ/ARfpYEjD0JEieQTUCQFo872xzDTQ1qSfDo/5u2MNUo5mv
|
||||||
|
ikEuEp7FYnhmrp4poyt4iRCFgy4Ask+bfdmtO/XXaRnZ7FJfQYoLVB2ITNECQQCN
|
||||||
|
gOiauZztl4yj5heAVJFDnWF9To61BOp1C7VtyjdL8NfuTUluNrV+KqapnAp2vhue
|
||||||
|
q0zTOTH47X0XVxFBiLohAkBuQzPey5I3Ui8inE4sDt/fqX8r/GMhBTxIb9KlV/H6
|
||||||
|
jKZNs/83n5/ohaX36er8svW9PB4pcqENZ+kBpvDtKVwS
|
||||||
|
-----END RSA PRIVATE KEY-----
|
|
@ -10,7 +10,7 @@ http {
|
||||||
server_name login.example.com;
|
server_name login.example.com;
|
||||||
|
|
||||||
resolver 127.0.0.11 ipv6=off;
|
resolver 127.0.0.11 ipv6=off;
|
||||||
set $upstream_endpoint http://nginx-authelia;
|
set $upstream_endpoint http://authelia:8080;
|
||||||
|
|
||||||
ssl on;
|
ssl on;
|
||||||
ssl_certificate /etc/ssl/server.crt;
|
ssl_certificate /etc/ssl/server.crt;
|
||||||
|
@ -61,7 +61,7 @@ http {
|
||||||
server_name public.example.com;
|
server_name public.example.com;
|
||||||
|
|
||||||
resolver 127.0.0.11 ipv6=off;
|
resolver 127.0.0.11 ipv6=off;
|
||||||
set $upstream_verify http://nginx-authelia/api/verify;
|
set $upstream_verify http://authelia:8080/api/verify;
|
||||||
set $upstream_endpoint http://nginx-backend;
|
set $upstream_endpoint http://nginx-backend;
|
||||||
set $upstream_headers http://httpbin:8000/headers;
|
set $upstream_headers http://httpbin:8000/headers;
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ http {
|
||||||
server_name admin.example.com;
|
server_name admin.example.com;
|
||||||
|
|
||||||
resolver 127.0.0.11 ipv6=off;
|
resolver 127.0.0.11 ipv6=off;
|
||||||
set $upstream_verify http://nginx-authelia/api/verify;
|
set $upstream_verify http://authelia:8080/api/verify;
|
||||||
set $upstream_endpoint http://nginx-backend;
|
set $upstream_endpoint http://nginx-backend;
|
||||||
|
|
||||||
ssl on;
|
ssl on;
|
||||||
|
@ -179,7 +179,7 @@ http {
|
||||||
server_name dev.example.com;
|
server_name dev.example.com;
|
||||||
|
|
||||||
resolver 127.0.0.11 ipv6=off;
|
resolver 127.0.0.11 ipv6=off;
|
||||||
set $upstream_verify http://nginx-authelia/api/verify;
|
set $upstream_verify http://authelia:8080/api/verify;
|
||||||
set $upstream_endpoint http://nginx-backend;
|
set $upstream_endpoint http://nginx-backend;
|
||||||
|
|
||||||
ssl on;
|
ssl on;
|
||||||
|
@ -229,7 +229,7 @@ http {
|
||||||
server_name mx1.mail.example.com mx2.mail.example.com;
|
server_name mx1.mail.example.com mx2.mail.example.com;
|
||||||
|
|
||||||
resolver 127.0.0.11 ipv6=off;
|
resolver 127.0.0.11 ipv6=off;
|
||||||
set $upstream_verify http://nginx-authelia/api/verify;
|
set $upstream_verify http://authelia:8080/api/verify;
|
||||||
set $upstream_endpoint http://nginx-backend;
|
set $upstream_endpoint http://nginx-backend;
|
||||||
|
|
||||||
ssl on;
|
ssl on;
|
||||||
|
@ -279,7 +279,7 @@ http {
|
||||||
server_name single_factor.example.com;
|
server_name single_factor.example.com;
|
||||||
|
|
||||||
resolver 127.0.0.11 ipv6=off;
|
resolver 127.0.0.11 ipv6=off;
|
||||||
set $upstream_verify http://nginx-authelia/api/verify;
|
set $upstream_verify http://authelia:8080/api/verify;
|
||||||
set $upstream_endpoint http://nginx-backend;
|
set $upstream_endpoint http://nginx-backend;
|
||||||
set $upstream_headers http://httpbin:8000/headers;
|
set $upstream_headers http://httpbin:8000/headers;
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ http {
|
||||||
server_name authelia.example.com;
|
server_name authelia.example.com;
|
||||||
|
|
||||||
resolver 127.0.0.11 ipv6=off;
|
resolver 127.0.0.11 ipv6=off;
|
||||||
set $upstream_endpoint http://authelia;
|
set $upstream_endpoint http://authelia:8080;
|
||||||
|
|
||||||
ssl on;
|
ssl on;
|
||||||
ssl_certificate /etc/ssl/server.crt;
|
ssl_certificate /etc/ssl/server.crt;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
npm i
|
|
||||||
grunt schema
|
|
||||||
grunt build
|
|
|
@ -3,12 +3,10 @@
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
docker-compose \
|
docker-compose \
|
||||||
-f docker-compose.yml \
|
-f docker-compose.dev.yml \
|
||||||
-f example/compose/docker-compose.base.yml \
|
-f example/compose/docker-compose.base.yml \
|
||||||
-f example/compose/authelia/docker-compose.dev.yml \
|
|
||||||
-f example/compose/mongo/docker-compose.yml \
|
-f example/compose/mongo/docker-compose.yml \
|
||||||
-f example/compose/redis/docker-compose.yml \
|
-f example/compose/redis/docker-compose.yml \
|
||||||
-f example/compose/nginx/authelia/docker-compose.yml \
|
|
||||||
-f example/compose/nginx/backend/docker-compose.yml \
|
-f example/compose/nginx/backend/docker-compose.yml \
|
||||||
-f example/compose/nginx/portal/docker-compose.yml \
|
-f example/compose/nginx/portal/docker-compose.yml \
|
||||||
-f example/compose/smtp/docker-compose.yml \
|
-f example/compose/smtp/docker-compose.yml \
|
||||||
|
|
|
@ -7,7 +7,6 @@ docker-compose \
|
||||||
-f example/compose/docker-compose.base.yml \
|
-f example/compose/docker-compose.base.yml \
|
||||||
-f example/compose/mongo/docker-compose.yml \
|
-f example/compose/mongo/docker-compose.yml \
|
||||||
-f example/compose/redis/docker-compose.yml \
|
-f example/compose/redis/docker-compose.yml \
|
||||||
-f example/compose/nginx/authelia/docker-compose.yml \
|
|
||||||
-f example/compose/nginx/backend/docker-compose.yml \
|
-f example/compose/nginx/backend/docker-compose.yml \
|
||||||
-f example/compose/nginx/portal/docker-compose.yml \
|
-f example/compose/nginx/portal/docker-compose.yml \
|
||||||
-f example/compose/smtp/docker-compose.yml \
|
-f example/compose/smtp/docker-compose.yml \
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
DC_SCRIPT=./scripts/example-commit/dc-example.sh
|
DC_SCRIPT=./scripts/example-commit/dc-example.sh
|
||||||
|
|
||||||
$DC_SCRIPT build
|
$DC_SCRIPT build
|
||||||
$DC_SCRIPT up -d httpbin mongo redis openldap authelia smtp nginx-authelia nginx-portal nginx-backend
|
$DC_SCRIPT up -d httpbin mongo redis openldap authelia smtp nginx-portal nginx-backend
|
||||||
|
|
|
@ -7,7 +7,6 @@ docker-compose \
|
||||||
-f example/compose/docker-compose.base.yml \
|
-f example/compose/docker-compose.base.yml \
|
||||||
-f example/compose/mongo/docker-compose.yml \
|
-f example/compose/mongo/docker-compose.yml \
|
||||||
-f example/compose/redis/docker-compose.yml \
|
-f example/compose/redis/docker-compose.yml \
|
||||||
-f example/compose/nginx/authelia/docker-compose.yml \
|
|
||||||
-f example/compose/nginx/backend/docker-compose.yml \
|
-f example/compose/nginx/backend/docker-compose.yml \
|
||||||
-f example/compose/nginx/portal/docker-compose.yml \
|
-f example/compose/nginx/portal/docker-compose.yml \
|
||||||
-f example/compose/smtp/docker-compose.yml \
|
-f example/compose/smtp/docker-compose.yml \
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
DC_SCRIPT=./scripts/example-dockerhub/dc-example.sh
|
DC_SCRIPT=./scripts/example-dockerhub/dc-example.sh
|
||||||
|
|
||||||
#$DC_SCRIPT build
|
#$DC_SCRIPT build
|
||||||
$DC_SCRIPT up -d httpbin mongo redis openldap authelia smtp nginx-authelia nginx-portal nginx-backend
|
$DC_SCRIPT up -d httpbin mongo redis openldap authelia smtp nginx-portal nginx-backend
|
||||||
|
|
|
@ -1,25 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DC_SCRIPT=./scripts/example-commit/dc-example.sh
|
DC_SCRIPT=./scripts/example-commit/dc-example.sh
|
||||||
EXPECTED_SERVICES_COUNT=9
|
EXPECTED_SERVICES_COUNT=8
|
||||||
|
|
||||||
build_services() {
|
build_services() {
|
||||||
$DC_SCRIPT build authelia
|
$DC_SCRIPT build authelia
|
||||||
}
|
}
|
||||||
|
|
||||||
start_services() {
|
|
||||||
$DC_SCRIPT up -d httpbin mongo redis openldap authelia smtp nginx-authelia nginx-portal nginx-backend
|
|
||||||
sleep 3
|
|
||||||
}
|
|
||||||
|
|
||||||
shut_services() {
|
|
||||||
containers_exist=`docker ps -aq | wc -l`
|
|
||||||
if [ "$containers_exist" -ne "0" ]
|
|
||||||
then
|
|
||||||
docker rm -f $(docker ps -aq)
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
expect_services_count() {
|
expect_services_count() {
|
||||||
EXPECTED_COUNT=$1
|
EXPECTED_COUNT=$1
|
||||||
service_count=`docker ps -a | grep "Up " | wc -l`
|
service_count=`docker ps -a | grep "Up " | wc -l`
|
||||||
|
@ -35,19 +22,11 @@ expect_services_count() {
|
||||||
}
|
}
|
||||||
|
|
||||||
run_integration_tests() {
|
run_integration_tests() {
|
||||||
echo "Start services..."
|
./node_modules/.bin/grunt test-int
|
||||||
start_services
|
|
||||||
expect_services_count $EXPECTED_SERVICES_COUNT
|
|
||||||
|
|
||||||
sleep 5
|
|
||||||
./node_modules/.bin/grunt run:test-int
|
|
||||||
shut_services
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run_other_tests() {
|
run_other_tests() {
|
||||||
echo "Test dev environment deployment (commands in README)"
|
echo "Test dev environment deployment (commands in README)"
|
||||||
# rm -rf node_modules
|
|
||||||
# ./scripts/build-dev.sh
|
|
||||||
./scripts/example-commit/deploy-example.sh
|
./scripts/example-commit/deploy-example.sh
|
||||||
expect_services_count $EXPECTED_SERVICES_COUNT
|
expect_services_count $EXPECTED_SERVICES_COUNT
|
||||||
./scripts/example-commit/undeploy-example.sh
|
./scripts/example-commit/undeploy-example.sh
|
||||||
|
@ -60,18 +39,14 @@ run_other_tests_docker() {
|
||||||
./scripts/example-dockerhub/undeploy-example.sh
|
./scripts/example-dockerhub/undeploy-example.sh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "Make sure services are not already running"
|
|
||||||
shut_services
|
|
||||||
|
|
||||||
# Build the container
|
# Build the container
|
||||||
build_services
|
build_services
|
||||||
|
|
||||||
|
# Pull all images
|
||||||
|
$DC_SCRIPT pull
|
||||||
|
|
||||||
# Prepare & test example from end user perspective
|
# Prepare & test example from end user perspective
|
||||||
run_integration_tests
|
run_integration_tests
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ set -e
|
||||||
|
|
||||||
docker --version
|
docker --version
|
||||||
docker-compose --version
|
docker-compose --version
|
||||||
|
echo "node `node -v`"
|
||||||
|
echo "npm `npm -v`"
|
||||||
|
|
||||||
# Generate configuration schema
|
# Generate configuration schema
|
||||||
grunt schema
|
grunt schema
|
||||||
|
|
|
@ -22,23 +22,36 @@ export interface Configuration {
|
||||||
totp?: TotpConfiguration;
|
totp?: TotpConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function complete(configuration: Configuration): [Configuration, string[]] {
|
export function complete(
|
||||||
const newConfiguration: Configuration = JSON.parse(JSON.stringify(configuration));
|
configuration: Configuration):
|
||||||
|
[Configuration, string[]] {
|
||||||
|
|
||||||
|
const newConfiguration: Configuration = JSON.parse(
|
||||||
|
JSON.stringify(configuration));
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
|
|
||||||
newConfiguration.access_control = AclConfigurationComplete(newConfiguration.access_control);
|
newConfiguration.access_control = AclConfigurationComplete(
|
||||||
newConfiguration.ldap = LdapConfigurationComplete(newConfiguration.ldap);
|
newConfiguration.access_control);
|
||||||
|
newConfiguration.ldap = LdapConfigurationComplete(
|
||||||
|
newConfiguration.ldap);
|
||||||
|
|
||||||
newConfiguration.authentication_methods = AuthenticationMethodsConfigurationComplete(newConfiguration.authentication_methods);
|
newConfiguration.authentication_methods =
|
||||||
|
AuthenticationMethodsConfigurationComplete(
|
||||||
|
newConfiguration.authentication_methods);
|
||||||
|
|
||||||
if (!newConfiguration.logs_level) {
|
if (!newConfiguration.logs_level) {
|
||||||
newConfiguration.logs_level = "info";
|
newConfiguration.logs_level = "info";
|
||||||
}
|
}
|
||||||
|
|
||||||
// In single factor mode, notifier section is optional.
|
// In single factor mode, notifier section is optional.
|
||||||
if (!MethodCalculator.isSingleFactorOnlyMode(newConfiguration.authentication_methods)) {
|
if (!MethodCalculator.isSingleFactorOnlyMode(
|
||||||
const [notifier, error] = NotifierConfigurationComplete(newConfiguration.notifier);
|
newConfiguration.authentication_methods) ||
|
||||||
|
newConfiguration.notifier) {
|
||||||
|
|
||||||
|
const [notifier, error] = NotifierConfigurationComplete(
|
||||||
|
newConfiguration.notifier);
|
||||||
newConfiguration.notifier = notifier;
|
newConfiguration.notifier = notifier;
|
||||||
|
|
||||||
if (error) errors.push(error);
|
if (error) errors.push(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,10 +59,14 @@ export function complete(configuration: Configuration): [Configuration, string[]
|
||||||
newConfiguration.port = 8080;
|
newConfiguration.port = 8080;
|
||||||
}
|
}
|
||||||
|
|
||||||
newConfiguration.regulation = RegulationConfigurationComplete(newConfiguration.regulation);
|
newConfiguration.regulation = RegulationConfigurationComplete(
|
||||||
newConfiguration.session = SessionConfigurationComplete(newConfiguration.session);
|
newConfiguration.regulation);
|
||||||
newConfiguration.storage = StorageConfigurationComplete(newConfiguration.storage);
|
newConfiguration.session = SessionConfigurationComplete(
|
||||||
newConfiguration.totp = TotpConfigurationComplete(newConfiguration.totp);
|
newConfiguration.session);
|
||||||
|
newConfiguration.storage = StorageConfigurationComplete(
|
||||||
|
newConfiguration.storage);
|
||||||
|
newConfiguration.totp = TotpConfigurationComplete(
|
||||||
|
newConfiguration.totp);
|
||||||
|
|
||||||
return [newConfiguration, errors];
|
return [newConfiguration, errors];
|
||||||
}
|
}
|
|
@ -2,10 +2,19 @@ import Assert = require("assert");
|
||||||
import { NotifierConfiguration, complete } from "./NotifierConfiguration";
|
import { NotifierConfiguration, complete } from "./NotifierConfiguration";
|
||||||
|
|
||||||
describe("configuration/schema/NotifierConfiguration", function() {
|
describe("configuration/schema/NotifierConfiguration", function() {
|
||||||
it("should ensure at least one key is provided", function() {
|
it("should use a default notifier when none is provided", function() {
|
||||||
const configuration: NotifierConfiguration = {};
|
const configuration: NotifierConfiguration = {};
|
||||||
const [newConfiguration, error] = complete(configuration);
|
const [newConfiguration, error] = complete(configuration);
|
||||||
|
|
||||||
|
Assert.deepEqual(newConfiguration.filesystem, {filename: "/tmp/authelia/notification.txt"})
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should ensure correct key is provided", function() {
|
||||||
|
const configuration = {
|
||||||
|
abc: 'badvalue'
|
||||||
|
};
|
||||||
|
const [newConfiguration, error] = complete(configuration as any);
|
||||||
|
|
||||||
Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'");
|
Assert.equal(error, "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export function complete(configuration: NotifierConfiguration): [NotifierConfigu
|
||||||
const newConfiguration: NotifierConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
|
const newConfiguration: NotifierConfiguration = (configuration) ? JSON.parse(JSON.stringify(configuration)) : {};
|
||||||
|
|
||||||
if (Object.keys(newConfiguration).length == 0)
|
if (Object.keys(newConfiguration).length == 0)
|
||||||
newConfiguration.filesystem = { filename: '/tmp/authelia-notification.txt' };
|
newConfiguration.filesystem = { filename: "/tmp/authelia/notification.txt" };
|
||||||
|
|
||||||
const ERROR = "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'";
|
const ERROR = "Notifier must have one of the following keys: 'filesystem', 'email' or 'smtp'";
|
||||||
|
|
||||||
|
@ -42,4 +42,4 @@ export function complete(configuration: NotifierConfiguration): [NotifierConfigu
|
||||||
return [newConfiguration, ERROR];
|
return [newConfiguration, ERROR];
|
||||||
|
|
||||||
return [newConfiguration, undefined];
|
return [newConfiguration, undefined];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
import Bluebird = require("bluebird");
|
||||||
|
|
||||||
|
function docker_compose(includes: string[]) {
|
||||||
|
const compose_args = includes.map((dc: string) => `-f ${dc}`).join(' ');
|
||||||
|
return `docker-compose ${compose_args}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setup(includes: string[], setupTime: number = 2000): Bluebird<void> {
|
||||||
|
const command = docker_compose(includes) + ' up -d'
|
||||||
|
console.log('Starting up environment.');
|
||||||
|
console.log('Running: %s', command);
|
||||||
|
|
||||||
|
return new Bluebird<void>(function(resolve, reject) {
|
||||||
|
exec(command, function(err, stdout, stderr) {
|
||||||
|
if(err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout(function() {
|
||||||
|
resolve();
|
||||||
|
}, setupTime);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cleanup(includes: string[]): Bluebird<void> {
|
||||||
|
const command = docker_compose(includes) + ' down';
|
||||||
|
console.log('Shutting down environment.');
|
||||||
|
console.log('Running: %s', command);
|
||||||
|
|
||||||
|
return new Bluebird<void>(function(resolve, reject) {
|
||||||
|
exec(command, function(err, stdout, stderr) {
|
||||||
|
if(err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ Feature: User is correctly redirected
|
||||||
And I login with user "john" and password "badpassword"
|
And I login with user "john" and password "badpassword"
|
||||||
And I wait for notification to disappear
|
And I wait for notification to disappear
|
||||||
And I clear field "username"
|
And I clear field "username"
|
||||||
|
And I clear field "password"
|
||||||
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 "Sign in"
|
And I click on "Sign in"
|
||||||
|
|
|
@ -8,7 +8,6 @@ When("I query {string}", function (url: string) {
|
||||||
const that = this;
|
const that = this;
|
||||||
return Request(url, { followRedirect: false })
|
return Request(url, { followRedirect: false })
|
||||||
.then(function(response) {
|
.then(function(response) {
|
||||||
console.log(response);
|
|
||||||
that.response = response;
|
that.response = response;
|
||||||
})
|
})
|
||||||
.catch(function(err: Error) {
|
.catch(function(err: Error) {
|
||||||
|
@ -26,7 +25,7 @@ Then("I get error code 401", function() {
|
||||||
if(that.response)
|
if(that.response)
|
||||||
reject(new Error("No error thrown"));
|
reject(new Error("No error thrown"));
|
||||||
else if(that.error.statusCode != 401)
|
else if(that.error.statusCode != 401)
|
||||||
reject(new Error("Error code != 401"));
|
reject(new Error(`Error code (${that.error.statusCode}) != 401`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {setDefaultTimeout, After, Before} from "cucumber";
|
import {setDefaultTimeout, After, Before, BeforeAll, AfterAll} from "cucumber";
|
||||||
import fs = require("fs");
|
import fs = require("fs");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import ChildProcess = require("child_process");
|
import ChildProcess = require("child_process");
|
||||||
|
@ -10,11 +10,32 @@ import { TotpHandler } from "../../../server/src/lib/authentication/totp/TotpHan
|
||||||
import Speakeasy = require("speakeasy");
|
import Speakeasy = require("speakeasy");
|
||||||
import Request = require("request-promise");
|
import Request = require("request-promise");
|
||||||
import { TOTPSecret } from "../../../server/types/TOTPSecret";
|
import { TOTPSecret } from "../../../server/types/TOTPSecret";
|
||||||
|
import Environment = require("../../environment");
|
||||||
|
|
||||||
setDefaultTimeout(20 * 1000);
|
setDefaultTimeout(30 * 1000);
|
||||||
|
|
||||||
const exec = BluebirdPromise.promisify<any, any>(ChildProcess.exec);
|
const exec = BluebirdPromise.promisify<any, any>(ChildProcess.exec);
|
||||||
|
|
||||||
|
const includes = [
|
||||||
|
"docker-compose.yml",
|
||||||
|
"example/compose/docker-compose.base.yml",
|
||||||
|
"example/compose/mongo/docker-compose.yml",
|
||||||
|
"example/compose/redis/docker-compose.yml",
|
||||||
|
"example/compose/nginx/backend/docker-compose.yml",
|
||||||
|
"example/compose/nginx/portal/docker-compose.yml",
|
||||||
|
"example/compose/smtp/docker-compose.yml",
|
||||||
|
"example/compose/httpbin/docker-compose.yml",
|
||||||
|
"example/compose/ldap/docker-compose.yml"
|
||||||
|
]
|
||||||
|
|
||||||
|
BeforeAll(function() {
|
||||||
|
return Environment.setup(includes, 10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
AfterAll(function() {
|
||||||
|
return Environment.cleanup(includes)
|
||||||
|
});
|
||||||
|
|
||||||
Before(function () {
|
Before(function () {
|
||||||
this.jar = Request.jar();
|
this.jar = Request.jar();
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
import Bluebird = require("bluebird");
|
||||||
|
|
||||||
|
export default function(driver: any) {
|
||||||
|
return driver.findElement(
|
||||||
|
SeleniumWebdriver.By.tagName('h1')).getText()
|
||||||
|
.then(function(content: string) {
|
||||||
|
return (content.indexOf('Secret') > -1)
|
||||||
|
? Bluebird.resolve()
|
||||||
|
: Bluebird.reject(new Error("Secret is not accessible."));
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
|
||||||
|
export default function(driver: any, buttonText: string) {
|
||||||
|
return driver.wait(
|
||||||
|
SeleniumWebdriver.until.elementLocated(
|
||||||
|
SeleniumWebdriver.By.tagName("button")), 5000)
|
||||||
|
.then(function () {
|
||||||
|
return driver
|
||||||
|
.findElement(SeleniumWebdriver.By.tagName("button"))
|
||||||
|
.findElement(SeleniumWebdriver.By.xpath("//button[contains(.,'" + buttonText + "')]"))
|
||||||
|
.click();
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,17 @@
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
|
||||||
|
export default function(driver: any, username: string, password: string) {
|
||||||
|
return driver.wait(SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.id("username")), 5000)
|
||||||
|
.then(function () {
|
||||||
|
return driver.findElement(SeleniumWebdriver.By.id("username"))
|
||||||
|
.sendKeys(username);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return driver.findElement(SeleniumWebdriver.By.id("password"))
|
||||||
|
.sendKeys(password);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return driver.findElement(SeleniumWebdriver.By.tagName("button"))
|
||||||
|
.click();
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
import VisitPage from "./visit-page";
|
||||||
|
import FillLoginPageAndClick from './fill-login-page-and-click';
|
||||||
|
import RegisterTotp from './register-totp';
|
||||||
|
import WaitRedirected from './wait-redirected';
|
||||||
|
|
||||||
|
export default function(driver: any, user: string) {
|
||||||
|
return VisitPage(driver, "https://login.example.com:8080/")
|
||||||
|
.then(() => FillLoginPageAndClick(driver, user, "password"))
|
||||||
|
.then(() => WaitRedirected(driver, "https://login.example.com:8080/secondfactor"))
|
||||||
|
.then(() => RegisterTotp(driver));
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import Bluebird = require("bluebird");
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
import Fs = require("fs");
|
||||||
|
|
||||||
|
function retrieveValidationLinkFromNotificationFile(): Bluebird<string> {
|
||||||
|
return Bluebird.promisify(Fs.readFile)("/tmp/authelia/notification.txt")
|
||||||
|
.then(function (data: any) {
|
||||||
|
const regexp = new RegExp(/Link: (.+)/);
|
||||||
|
const match = regexp.exec(data);
|
||||||
|
const link = match[1];
|
||||||
|
return Bluebird.resolve(link);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function(driver: any): Bluebird<string> {
|
||||||
|
return driver.wait(SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.className("register-totp")), 5000)
|
||||||
|
.then(function () {
|
||||||
|
return driver.findElement(SeleniumWebdriver.By.className("register-totp")).click();
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return retrieveValidationLinkFromNotificationFile();
|
||||||
|
})
|
||||||
|
.then(function (link: string) {
|
||||||
|
return driver.get(link);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return driver.wait(SeleniumWebdriver.until.elementLocated(SeleniumWebdriver.By.id("secret")), 5000);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return driver.findElement(SeleniumWebdriver.By.id("secret")).getText();
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
import Assert = require("assert");
|
||||||
|
|
||||||
|
export default function(driver: any, type: string, message: string) {
|
||||||
|
const notificationEl = driver.findElement(SeleniumWebdriver.By.className("notification"));
|
||||||
|
return driver.wait(SeleniumWebdriver.until.elementIsVisible(notificationEl), 5000)
|
||||||
|
.then(function () {
|
||||||
|
return notificationEl.getText();
|
||||||
|
})
|
||||||
|
.then(function (txt: string) {
|
||||||
|
Assert.equal(message, txt);
|
||||||
|
return notificationEl.getAttribute("class");
|
||||||
|
})
|
||||||
|
.then(function (classes: string) {
|
||||||
|
Assert(classes.indexOf(type) > -1, "Class '" + type + "' not found in notification element.");
|
||||||
|
return driver.sleep(500);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
import Speakeasy = require("speakeasy");
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
import ClickOnButton from "./click-on-button";
|
||||||
|
|
||||||
|
export default function(driver: any, secret: string) {
|
||||||
|
const token = Speakeasy.totp({
|
||||||
|
secret: secret,
|
||||||
|
encoding: "base32"
|
||||||
|
});
|
||||||
|
return driver.wait(
|
||||||
|
SeleniumWebdriver.until.elementLocated(
|
||||||
|
SeleniumWebdriver.By.id("token")), 5000)
|
||||||
|
.then(function () {
|
||||||
|
return driver.findElement(SeleniumWebdriver.By.id("token"))
|
||||||
|
.sendKeys(token);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return ClickOnButton(driver, "Sign in");
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
|
||||||
|
export default function(driver: any, url: string, timeout: number = 5000) {
|
||||||
|
return driver.get(url)
|
||||||
|
.then(function () {
|
||||||
|
return driver.wait(SeleniumWebdriver.until.urlIs(url), timeout);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
|
||||||
|
export default function(driver: any, url: string, timeout: number = 5000) {
|
||||||
|
return driver.wait(SeleniumWebdriver.until.urlIs(url), timeout);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
before(function() {
|
||||||
|
this.driver = new SeleniumWebdriver.Builder()
|
||||||
|
.forBrowser("chrome")
|
||||||
|
.build();
|
||||||
|
})
|
||||||
|
|
||||||
|
after(function() {
|
||||||
|
this.driver.quit();
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
require("chromedriver");
|
||||||
|
import Environment = require('../environment');
|
||||||
|
|
||||||
|
const includes = [
|
||||||
|
"docker-compose.minimal.yml",
|
||||||
|
"example/compose/docker-compose.base.yml",
|
||||||
|
"example/compose/nginx/minimal/docker-compose.yml",
|
||||||
|
"example/compose/ldap/docker-compose.yml"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
this.timeout(20000);
|
||||||
|
return Environment.setup(includes);
|
||||||
|
});
|
||||||
|
|
||||||
|
after(function() {
|
||||||
|
this.timeout(30000);
|
||||||
|
return Environment.cleanup(includes);
|
||||||
|
});
|
|
@ -0,0 +1,34 @@
|
||||||
|
import Bluebird = require("bluebird");
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
import Fs = require("fs");
|
||||||
|
import Speakeasy = require("speakeasy");
|
||||||
|
import WithDriver from '../helpers/with-driver';
|
||||||
|
import FillLoginPageWithUserAndPasswordAndClick from '../helpers/fill-login-page-and-click';
|
||||||
|
import WaitRedirected from '../helpers/wait-redirected';
|
||||||
|
import VisitPage from '../helpers/visit-page';
|
||||||
|
import SeeNotification from '../helpers/see-notification';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When user provides bad password,
|
||||||
|
* Then he gets a notification message.
|
||||||
|
*/
|
||||||
|
describe("Provide bad password", function() {
|
||||||
|
WithDriver();
|
||||||
|
|
||||||
|
describe('failed login as john', function() {
|
||||||
|
before(function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
const driver = this.driver;
|
||||||
|
return VisitPage(driver, "https://login.example.com:8080/")
|
||||||
|
.then(function() {
|
||||||
|
return FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'bad_password');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get a notification message', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
return SeeNotification(this.driver, "error", "Authentication failed. Please check your credentials.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,46 @@
|
||||||
|
require("chromedriver");
|
||||||
|
import Bluebird = require("bluebird");
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
import Fs = require("fs");
|
||||||
|
import Speakeasy = require("speakeasy");
|
||||||
|
import WithDriver from '../helpers/with-driver';
|
||||||
|
import FillLoginPageWithUserAndPasswordAndClick from '../helpers/fill-login-page-and-click';
|
||||||
|
import WaitRedirected from '../helpers/wait-redirected';
|
||||||
|
import VisitPage from '../helpers/visit-page';
|
||||||
|
import RegisterTotp from '../helpers/register-totp';
|
||||||
|
import ValidateTotp from '../helpers/validate-totp';
|
||||||
|
import AccessSecret from "../helpers/access-secret";
|
||||||
|
import LoginAndRegisterTotp from '../helpers/login-and-register-totp';
|
||||||
|
import seeNotification from "../helpers/see-notification";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given john has registered a TOTP secret,
|
||||||
|
* When he fails the TOTP challenge,
|
||||||
|
* Then he gets a notification message.
|
||||||
|
*/
|
||||||
|
describe('Fail TOTP challenge', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
WithDriver();
|
||||||
|
|
||||||
|
describe('successfully login as john', function() {
|
||||||
|
before(function() {
|
||||||
|
const that = this;
|
||||||
|
return LoginAndRegisterTotp(this.driver, "john");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fail second factor', function() {
|
||||||
|
before(function() {
|
||||||
|
const BAD_TOKEN = "125478";
|
||||||
|
const driver = this.driver;
|
||||||
|
|
||||||
|
return VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html")
|
||||||
|
.then(() => FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'password'))
|
||||||
|
.then(() => ValidateTotp(driver, BAD_TOKEN));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("get a notification message", function() {
|
||||||
|
return seeNotification(this.driver, "error", "Authentication failed. Have you already registered your secret?");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,32 @@
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
import WithDriver from '../helpers/with-driver';
|
||||||
|
import LoginAndRegisterTotp from '../helpers/login-and-register-totp';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the user logs in as john,
|
||||||
|
* When he register a TOTP token,
|
||||||
|
* Then he reach a page containing the secret as string an qrcode
|
||||||
|
*/
|
||||||
|
describe('Registering TOTP', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
WithDriver();
|
||||||
|
|
||||||
|
describe('successfully login as john', function() {
|
||||||
|
before('register successfully', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
return LoginAndRegisterTotp(this.driver, "john");
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should see generated qrcode", function() {
|
||||||
|
this.driver.findElement(
|
||||||
|
SeleniumWebdriver.By.id("qrcode"),
|
||||||
|
5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should see generated secret", function() {
|
||||||
|
this.driver.findElement(
|
||||||
|
SeleniumWebdriver.By.id("secret"),
|
||||||
|
5000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,56 @@
|
||||||
|
require("chromedriver");
|
||||||
|
import Bluebird = require("bluebird");
|
||||||
|
import SeleniumWebdriver = require("selenium-webdriver");
|
||||||
|
import Fs = require("fs");
|
||||||
|
import Speakeasy = require("speakeasy");
|
||||||
|
import WithDriver from '../helpers/with-driver';
|
||||||
|
import FillLoginPageWithUserAndPasswordAndClick from '../helpers/fill-login-page-and-click';
|
||||||
|
import WaitRedirected from '../helpers/wait-redirected';
|
||||||
|
import VisitPage from '../helpers/visit-page';
|
||||||
|
import RegisterTotp from '../helpers/register-totp';
|
||||||
|
import ValidateTotp from '../helpers/validate-totp';
|
||||||
|
import AccessSecret from "../helpers/access-secret";
|
||||||
|
import LoginAndRegisterTotp from '../helpers/login-and-register-totp';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given john has registered a TOTP secret,
|
||||||
|
* When he validates the TOTP second factor,
|
||||||
|
* Then he has access to secret page.
|
||||||
|
*/
|
||||||
|
describe('Validate TOTP factor', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
WithDriver();
|
||||||
|
|
||||||
|
describe('successfully login as john', function() {
|
||||||
|
before(function() {
|
||||||
|
const that = this;
|
||||||
|
return LoginAndRegisterTotp(this.driver, "john")
|
||||||
|
.then(function(secret: string) {
|
||||||
|
that.secret = secret;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('validate second factor', function() {
|
||||||
|
before(function() {
|
||||||
|
const secret = this.secret;
|
||||||
|
if(!secret) return Bluebird.reject(new Error("No secret!"));
|
||||||
|
const driver = this.driver;
|
||||||
|
|
||||||
|
return VisitPage(driver, "https://login.example.com:8080/?rd=https://admin.example.com:8080/secret.html")
|
||||||
|
.then(function() {
|
||||||
|
return FillLoginPageWithUserAndPasswordAndClick(driver, 'john', 'password');
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return ValidateTotp(driver, secret);
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
return WaitRedirected(driver, "https://admin.example.com:8080/secret.html")
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should access the secret", function() {
|
||||||
|
return AccessSecret(this.driver);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue