Fix integration test and package Travis scripts

pull/45/head
Clement Michaud 2017-06-28 15:57:58 +02:00
parent 0414d28e2b
commit e56c2492ed
39 changed files with 585 additions and 260 deletions

4
.gitignore vendored
View File

@ -13,9 +13,7 @@ src/.baseDir.ts
*.swp *.swp
*.sh /config.yml
config.yml
npm-debug.log npm-debug.log

View File

@ -19,14 +19,7 @@ addons:
before_install: npm install -g npm@'>=2.13.5' before_install: npm install -g npm@'>=2.13.5'
script: script:
- grunt build-dist - ./scripts/travis.sh
- grunt docker-build
- docker-compose build
- docker-compose up -d
- sleep 5
- ./scripts/check-services.sh
- npm run int-test
- ./scripts/npm-deployment-test.sh
after_success: after_success:
- ./scripts/docker-publish.sh - ./scripts/docker-publish.sh

View File

@ -10,7 +10,7 @@ COPY dist/src/server /usr/src
ENV PORT=80 ENV PORT=80
EXPOSE 80 EXPOSE 80
VOLUME /etc/auth-server VOLUME /etc/authelia
VOLUME /var/lib/auth-server VOLUME /var/lib/authelia
CMD ["node", "index.js", "/etc/auth-server/config.yml"] CMD ["node", "index.js", "/etc/authelia/config.yml"]

View File

@ -5,12 +5,12 @@ module.exports = function (grunt) {
run: { run: {
options: {}, options: {},
"build": { "build": {
cmd: "npm", cmd: "./node_modules/.bin/tsc",
args: ['run', 'build'] args: ['-p', 'tsconfig.json']
}, },
"tslint": { "tslint": {
cmd: "npm", cmd: "./node_modules/.bin/tslint",
args: ['run', 'tslint'] args: ['-c', 'tslint.json', '-p', 'tsconfig.json']
}, },
"test": { "test": {
cmd: "npm", cmd: "npm",

View File

@ -12,7 +12,7 @@ logs_level: info
# Example: for user john, the DN will be cn=john,ou=users,dc=example,dc=com # Example: for user john, the DN will be cn=john,ou=users,dc=example,dc=com
ldap: ldap:
# The url of the ldap server # The url of the ldap server
url: ldap://ldap url: ldap://openldap-restriction
# The base dn for every entries # The base dn for every entries
base_dn: dc=example,dc=com base_dn: dc=example,dc=com
@ -85,7 +85,7 @@ store_directory: /var/lib/authelia/store
notifier: notifier:
# For testing purpose, notifications can be sent in a file # For testing purpose, notifications can be sent in a file
filesystem: filesystem:
filename: /var/lib/auth-server/notifications/notification.txt filename: /var/lib/authelia/notifications/notification.txt
# Use your gmail account to send the notifications. You can use an app password. # Use your gmail account to send the notifications. You can use an app password.
# gmail: # gmail:

View File

@ -0,0 +1,5 @@
version: '2'
networks:
example-network:
driver: bridge

View File

@ -1,10 +1,10 @@
version: '2' version: '2'
services: services:
auth: authelia:
volumes: volumes:
- ./test:/usr/src/test - ./test:/usr/src/test
- ./dist/src/server:/usr/src - ./dist/src/server:/usr/src
- ./node_modules:/usr/src/node_modules - ./node_modules:/usr/src/node_modules
- ./config.yml:/etc/auth-server/config.yml:ro - ./config.yml:/etc/authelia/config.yml:ro
networks:
- example-network

View File

@ -1,43 +1,11 @@
version: '2' version: '2'
services: services:
auth: authelia:
build: . build: .
restart: always restart: always
volumes: volumes:
- ./config.template.yml:/etc/auth-server/config.yml:ro - ./config.template.yml:/etc/authelia/config.yml:ro
- ./notifications:/var/lib/auth-server/notifications - ./notifications:/var/lib/authelia/notifications
networks:
- example-network
nginx:
image: nginx:alpine
volumes:
- ./example/nginx_conf/nginx.conf:/etc/nginx/nginx.conf
- ./example/nginx_conf/index.html:/usr/share/nginx/html/index.html
- ./example/nginx_conf/secret.html:/usr/share/nginx/html/secret.html
- ./example/nginx_conf/ssl:/etc/ssl
depends_on:
- auth
ports:
- "8080:443"
openldap:
image: clems4ever/openldap
ports:
- "389:389"
environment:
- SLAPD_ORGANISATION=MyCompany
- SLAPD_DOMAIN=example.com
- SLAPD_PASSWORD=password
- SLAPD_CONFIG_PASSWORD=password
- SLAPD_ADDITIONAL_MODULES=memberof
- SLAPD_ADDITIONAL_SCHEMAS=openldap
- SLAPD_FORCE_RECONFIGURE=true
volumes:
- ./example/ldap:/etc/ldap.dist/prepopulate
openldap-admin:
image: osixia/phpldapadmin:0.6.11
ports:
- 9090:80
environment:
- PHPLDAPADMIN_LDAP_HOSTS=openldap
- PHPLDAPADMIN_HTTPS=false

View File

@ -0,0 +1,9 @@
FROM clems4ever/openldap
ENV SLAPD_ORGANISATION=MyCompany
ENV SLAPD_DOMAIN=example.com
ENV SLAPD_PASSWORD=password
ENV SLAPD_CONFIG_PASSWORD=password
ENV SLAPD_ADDITIONAL_MODULES=memberof
ENV SLAPD_ADDITIONAL_SCHEMAS=openldap
ENV SLAPD_FORCE_RECONFIGURE=true

View File

@ -25,7 +25,7 @@ dn: cn=john,ou=users,dc=example,dc=com
cn: john cn: john
objectclass: inetOrgPerson objectclass: inetOrgPerson
objectclass: top objectclass: top
mail: clement.michaud34@gmail.com mail: john.doe@example.com
sn: John Doe sn: John Doe
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g= userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
@ -45,18 +45,3 @@ mail: bob.dylan@example.com
sn: Bob Dylan sn: Bob Dylan
userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g= userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
# dn: uid=jack,ou=users,dc=example,dc=com
# cn: jack
# gidnumber: 501
# givenname: Jack
# homedirectory: /home/jack
# loginshell: /bin/sh
# objectclass: inetOrgPerson
# objectclass: posixAccount
# objectclass: top
# mail: jack.daniels@example.com
# sn: Jack Daniels
# uid: jack
# uidnumber: 1001
# userpassword: {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=
#

View File

@ -0,0 +1,11 @@
version: '2'
services:
openldap-admin:
image: osixia/phpldapadmin:0.6.11
ports:
- 9090:80
environment:
- PHPLDAPADMIN_LDAP_HOSTS=openldap
- PHPLDAPADMIN_HTTPS=false
networks:
- example-network

View File

@ -0,0 +1,10 @@
version: '2'
services:
openldap:
build: ./example/ldap
volumes:
- ./example/ldap/base.ldif:/etc/ldap.dist/prepopulate/base.ldif
- ./example/ldap/access.rules:/etc/ldap.dist/prepopulate/access.rules
networks:
- example-network

View File

@ -0,0 +1,24 @@
version: '2'
services:
nginx:
image: nginx:alpine
volumes:
- ./example/nginx/index.html:/usr/share/nginx/html/index.html
- ./example/nginx/secret.html:/usr/share/nginx/html/secret.html
- ./example/nginx/ssl:/etc/ssl
- ./example/nginx/nginx.conf:/etc/nginx/nginx.conf
ports:
- "8080:443"
depends_on:
- authelia
networks:
example-network:
aliases:
- home.test.local
- secret.test.local
- secret1.test.local
- secret2.test.local
- mx1.mail.test.local
- mx2.mail.test.local
- auth.test.local

View File

@ -36,7 +36,7 @@ http {
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://auth/; proxy_pass http://authelia/;
proxy_intercept_errors on; proxy_intercept_errors on;
@ -68,7 +68,7 @@ http {
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
proxy_pass http://auth/verify; proxy_pass http://authelia/verify;
} }
location = /secret.html { location = /secret.html {

View File

@ -4,6 +4,6 @@
</head> </head>
<body> <body>
This is a very important secret!<br/> This is a very important secret!<br/>
Go back to <a href="https://home.test.local:8080/">home page</a>. Go back to <a href="https://home.test.local/">home page</a>.
</body> </body>
</html> </html>

View File

@ -8,10 +8,7 @@
}, },
"scripts": { "scripts": {
"test": "./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/client test/server", "test": "./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/client test/server",
"int-test": "./node_modules/.bin/mocha --compilers ts:ts-node/register --recursive test/integration",
"cover": "NODE_ENV=test nyc npm t", "cover": "NODE_ENV=test nyc npm t",
"build": "tsc",
"tslint": "tslint -c tslint.json -p tsconfig.json",
"serve": "node dist/server/index.js" "serve": "node dist/server/index.js"
}, },
"repository": { "repository": {
@ -63,7 +60,7 @@
"@types/proxyquire": "^1.3.27", "@types/proxyquire": "^1.3.27",
"@types/query-string": "^4.3.1", "@types/query-string": "^4.3.1",
"@types/randomstring": "^1.1.5", "@types/randomstring": "^1.1.5",
"@types/request": "0.0.43", "@types/request": "0.0.45",
"@types/sinon": "^2.2.1", "@types/sinon": "^2.2.1",
"@types/speakeasy": "^2.0.1", "@types/speakeasy": "^2.0.1",
"@types/tmp": "0.0.33", "@types/tmp": "0.0.33",

View File

@ -0,0 +1,5 @@
#!/bin/bash
set -e
docker-compose -f docker-compose.base.yml -f docker-compose.yml -f example/nginx/docker-compose.yml -f example/ldap/docker-compose.yml $*

View File

@ -0,0 +1,5 @@
#!/bin/bash
set -e
docker-compose -f docker-compose.base.yml -f example/ldap/docker-compose.yml -f test/integration/docker-compose.yml $*

View File

@ -0,0 +1,4 @@
#!/bin/bash
./scripts/dc-example.sh build
./scripts/dc-example.sh up -d

View File

@ -16,6 +16,7 @@ function deploy_on_dockerhub {
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"; docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD";
echo "Docker image $IMAGE_WITH_TAG will be deployed on Dockerhub." echo "Docker image $IMAGE_WITH_TAG will be deployed on Dockerhub."
docker build -t $IMAGE_NAME .
docker tag $IMAGE_NAME $IMAGE_WITH_TAG; docker tag $IMAGE_NAME $IMAGE_WITH_TAG;
docker push $IMAGE_WITH_TAG; docker push $IMAGE_WITH_TAG;
echo "Docker image deployed successfully." echo "Docker image deployed successfully."

View File

@ -1,5 +1,7 @@
#!/bin/bash #!/bin/bash
set -e
NPM_UNPACK_DIR=/tmp/npm-unpack NPM_UNPACK_DIR=/tmp/npm-unpack
echo "--- Packing npm package into a tarball" echo "--- Packing npm package into a tarball"

View File

@ -0,0 +1,22 @@
#!/bin/bash
set -e
echo "Build services images..."
./scripts/dc-test.sh build
echo "Start services..."
./scripts/dc-test.sh up -d authelia nginx openldap
sleep 3
docker ps -a
echo "Display services logs..."
./scripts/dc-test.sh logs authelia
./scripts/dc-test.sh logs nginx
./scripts/dc-test.sh logs openldap
echo "Run integration tests..."
./scripts/dc-test.sh run --rm --name int-test int-test
echo "Shutdown services..."
./scripts/dc-test.sh down

View File

@ -0,0 +1,15 @@
#!/bin/bash
set -e
# Build production environment and set it up
./scripts/dc-example.sh build
./scripts/dc-example.sh up -d
# Wait for services to be running
sleep 5
# Check if services are correctly running
./scripts/check-services.sh
./scripts/dc-example.sh down

21
scripts/travis.sh 100755
View File

@ -0,0 +1,21 @@
#!/bin/bash
set -e
docker --version
docker-compose --version
# Run unit tests
grunt test
# Build the app from Typescript and package
grunt build-dist
# Run integration tests
./scripts/run-int-test.sh
# Test staging environment
./scripts/run-staging.sh
# Test npm deployment before actual deployment
./scripts/npm-deployment-test.sh

View File

@ -0,0 +1,3 @@
#!/bin/bash
./scripts/dc-example.sh down

View File

@ -2,6 +2,8 @@
import * as ObjectPath from "object-path"; import * as ObjectPath from "object-path";
import { AppConfiguration, UserConfiguration, NotifierConfiguration, ACLConfiguration, LdapConfiguration } from "./../../types/Configuration"; import { AppConfiguration, UserConfiguration, NotifierConfiguration, ACLConfiguration, LdapConfiguration } from "./../../types/Configuration";
const LDAP_URL_ENV_VARIABLE = "LDAP_URL";
function get_optional<T>(config: object, path: string, default_value: T): T { function get_optional<T>(config: object, path: string, default_value: T): T {
let entry = default_value; let entry = default_value;
@ -17,26 +19,36 @@ function ensure_key_existence(config: object, path: string): void {
} }
} }
export default class ConfigurationAdapter { function adaptFromUserConfiguration(userConfiguration: UserConfiguration): AppConfiguration {
static adapt(yaml_config: UserConfiguration): AppConfiguration { ensure_key_existence(userConfiguration, "ldap");
ensure_key_existence(yaml_config, "ldap"); ensure_key_existence(userConfiguration, "session.secret");
ensure_key_existence(yaml_config, "session.secret");
const port = ObjectPath.get(yaml_config, "port", 8080); const port = ObjectPath.get(userConfiguration, "port", 8080);
return { return {
port: port, port: port,
ldap: ObjectPath.get<object, LdapConfiguration>(yaml_config, "ldap"), ldap: ObjectPath.get<object, LdapConfiguration>(userConfiguration, "ldap"),
session: { session: {
domain: ObjectPath.get<object, string>(yaml_config, "session.domain"), domain: ObjectPath.get<object, string>(userConfiguration, "session.domain"),
secret: ObjectPath.get<object, string>(yaml_config, "session.secret"), secret: ObjectPath.get<object, string>(userConfiguration, "session.secret"),
expiration: get_optional<number>(yaml_config, "session.expiration", 3600000), // in ms expiration: get_optional<number>(userConfiguration, "session.expiration", 3600000), // in ms
}, },
store_directory: get_optional<string>(yaml_config, "store_directory", undefined), store_directory: get_optional<string>(userConfiguration, "store_directory", undefined),
logs_level: get_optional<string>(yaml_config, "logs_level", "info"), logs_level: get_optional<string>(userConfiguration, "logs_level", "info"),
notifier: ObjectPath.get<object, NotifierConfiguration>(yaml_config, "notifier"), notifier: ObjectPath.get<object, NotifierConfiguration>(userConfiguration, "notifier"),
access_control: ObjectPath.get<object, ACLConfiguration>(yaml_config, "access_control") access_control: ObjectPath.get<object, ACLConfiguration>(userConfiguration, "access_control")
}; };
} }
export default class ConfigurationAdapter {
static adapt(userConfiguration: UserConfiguration): AppConfiguration {
const appConfiguration = adaptFromUserConfiguration(userConfiguration);
const ldapUrl = process.env[LDAP_URL_ENV_VARIABLE];
if (ldapUrl)
appConfiguration.ldap.url = ldapUrl;
return appConfiguration;
}
} }

View File

@ -68,9 +68,10 @@ export class LdapClient {
const that = this; const that = this;
const ldapClient = this.createClient(); const ldapClient = this.createClient();
this.logger.debug("LDAP: Check password for user '%s'", userDN); this.logger.debug("LDAP: Check password by binding user '%s'", userDN);
return ldapClient.bindAsync(userDN, password) return ldapClient.bindAsync(userDN, password)
.then(function () { .then(function () {
that.logger.debug("LDAP: Unbind user '%s'", userDN);
return ldapClient.unbindAsync(); return ldapClient.unbindAsync();
}) })
.error(function (err: Error) { .error(function (err: Error) {

View File

@ -50,6 +50,7 @@ export default class Server {
// by default the level of logs is info // by default the level of logs is info
deps.winston.level = config.logs_level; deps.winston.level = config.logs_level;
console.log("Log level = ", deps.winston.level); console.log("Log level = ", deps.winston.level);
deps.winston.debug("Authelia configuration is %s", JSON.stringify(config, undefined, 2));
ServerVariables.fill(app, config, deps); ServerVariables.fill(app, config, deps);

View File

@ -35,7 +35,7 @@ export class GMailNotifier extends INotifier {
}; };
const mailOptions = { const mailOptions = {
from: "auth-server@open-intent.io", from: "authelia@authelia.com",
to: identity.email, to: identity.email,
subject: subject, subject: subject,
html: ejs.render(email_template, d) html: ejs.render(email_template, d)

View File

@ -0,0 +1,5 @@
FROM node:7-alpine
WORKDIR /usr/src
CMD ["./node_modules/.bin/mocha", "--compilers", "ts:ts-node/register", "--recursive", "test/integration"]

View File

@ -0,0 +1,164 @@
import Request = require("request");
import Assert = require("assert");
import Speakeasy = require("speakeasy");
import BluebirdPromise = require("bluebird");
import Util = require("util");
import Sinon = require("sinon");
import Endpoints = require("../../src/server/endpoints");
const EXEC_PATH = "./dist/src/server/index.js";
const CONFIG_PATH = "./test/integration/config.yml";
const j = Request.jar();
const request: typeof Request = <typeof Request>BluebirdPromise.promisifyAll(Request.defaults({ jar: j }));
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
const DOMAIN = "test.local";
const PORT = 8080;
const HOME_URL = Util.format("https://%s.%s:%d", "home", DOMAIN, PORT);
const SECRET_URL = Util.format("https://%s.%s:%d", "secret", DOMAIN, PORT);
const SECRET1_URL = Util.format("https://%s.%s:%d", "secret1", DOMAIN, PORT);
const SECRET2_URL = Util.format("https://%s.%s:%d", "secret2", DOMAIN, PORT);
const MX1_URL = Util.format("https://%s.%s:%d", "mx1.mail", DOMAIN, PORT);
const MX2_URL = Util.format("https://%s.%s:%d", "mx2.mail", DOMAIN, PORT);
const BASE_AUTH_URL = Util.format("https://%s.%s:%d", "auth", DOMAIN, PORT);
function waitFor(ms: number): BluebirdPromise<{}> {
return new BluebirdPromise(function (resolve, reject) {
setTimeout(function () {
resolve();
}, ms);
});
}
describe("test the server", function () {
let home_page: string;
let login_page: string;
before(function () {
const home_page_promise = getHomePage()
.then(function (data) {
home_page = data.body;
});
const login_page_promise = getLoginPage()
.then(function (data) {
login_page = data.body;
});
return BluebirdPromise.all([home_page_promise,
login_page_promise]);
});
after(function () {
});
function str_contains(str: string, pattern: string) {
return str.indexOf(pattern) != -1;
}
function home_page_contains(pattern: string) {
return str_contains(home_page, pattern);
}
it("should serve a correct home page", function () {
Assert(home_page_contains(BASE_AUTH_URL + Endpoints.LOGOUT_GET + "?redirect=" + HOME_URL + "/"));
Assert(home_page_contains(HOME_URL + "/secret.html"));
Assert(home_page_contains(SECRET_URL + "/secret.html"));
Assert(home_page_contains(SECRET1_URL + "/secret.html"));
Assert(home_page_contains(SECRET2_URL + "/secret.html"));
Assert(home_page_contains(MX1_URL + "/secret.html"));
Assert(home_page_contains(MX2_URL + "/secret.html"));
});
it("should serve the login page", function () {
return getPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_GET)
.then(function (data: Request.RequestResponse) {
Assert.equal(data.statusCode, 200);
});
});
it("should serve the homepage", function () {
return getPromised(HOME_URL + "/")
.then(function (data: Request.RequestResponse) {
Assert.equal(data.statusCode, 200);
});
});
it("should redirect when logout", function () {
return getPromised(BASE_AUTH_URL + Endpoints.LOGOUT_GET + "?redirect=" + HOME_URL)
.then(function (data: Request.RequestResponse) {
Assert.equal(data.statusCode, 200);
Assert.equal(data.body, home_page);
});
});
it("should be redirected to the login page when accessing secret while not authenticated", function () {
return getPromised(HOME_URL + "/secret.html")
.then(function (data: Request.RequestResponse) {
Assert.equal(data.statusCode, 200);
Assert.equal(data.body, login_page);
});
});
it.skip("should fail the first factor", function () {
return postPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_POST, {
form: {
username: "admin",
password: "password",
}
})
.then(function (data: Request.RequestResponse) {
Assert.equal(data.body, "Bad credentials");
});
});
function login_as(username: string, password: string) {
return postPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_POST, {
form: {
username: "john",
password: "password",
}
})
.then(function (data: Request.RequestResponse) {
Assert.equal(data.statusCode, 302);
return BluebirdPromise.resolve();
});
}
it("should succeed the first factor", function () {
return login_as("john", "password");
});
describe("test ldap connection", function () {
it("should not fail after inactivity", function () {
const clock = Sinon.useFakeTimers();
return login_as("john", "password")
.then(function () {
clock.tick(3600000 * 24); // 24 hour
return login_as("john", "password");
})
.then(function () {
clock.restore();
return BluebirdPromise.resolve();
});
});
});
});
function getPromised(url: string) {
return request.getAsync(url);
}
function postPromised(url: string, body: Object) {
return request.postAsync(url, body);
}
function getHomePage(): BluebirdPromise<Request.RequestResponse> {
return getPromised(HOME_URL + "/");
}
function getLoginPage(): BluebirdPromise<Request.RequestResponse> {
return getPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_GET);
}

View File

@ -0,0 +1,94 @@
# The port to listen on
port: 80
# Log level
#
# Level of verbosity for logs
logs_level: debug
# LDAP configuration
#
# Example: for user john, the DN will be cn=john,ou=users,dc=example,dc=com
ldap:
# The url of the ldap server
url: ldap://openldap
# The base dn for every entries
base_dn: dc=example,dc=com
# An additional dn to define the scope to all users
additional_user_dn: ou=users
# The user name attribute of users. Might uid for FreeIPA. 'cn' by default.
user_name_attribute: cn
# An additional dn to define the scope of groups
additional_group_dn: ou=groups
# The group name attribute of group. 'cn' by default.
group_name_attribute: cn
# The username and password of the admin user.
user: cn=admin,dc=example,dc=com
password: password
# Access Control
#
# Access control is a set of rules you can use to restrict the user access.
# Default (anyone), per-user or per-group rules can be defined.
#
# If 'access_control' is not defined, ACL rules are disabled and default policy
# is applied, i.e., access is allowed to anyone. Otherwise restrictions follow
# the rules defined below.
# If no rule is provided, all domains are denied.
#
# '*' means 'any' subdomains and matches any string. It must stand at the
# beginning of the pattern.
access_control:
default:
- home.test.local
groups:
admin:
- '*.test.local'
dev:
- secret.test.local
- secret2.test.local
users:
harry:
- secret1.test.local
bob:
- '*.mail.test.local'
# Configuration of session cookies
#
# _secret_ the secret to encrypt session cookies
# _expiration_ the time before cookies expire
# _domain_ the domain to protect.
# Note: the authenticator must also be in that domain. If empty, the cookie
# is restricted to the subdomain of the issuer.
session:
secret: unsecure_secret
expiration: 3600000
domain: test.local
# The directory where the DB files will be saved
store_directory: /var/lib/authelia/store
# Notifications are sent to users when they require a password reset, a u2f
# registration or a TOTP registration.
# Use only one available configuration: filesystem, gmail
notifier:
# For testing purpose, notifications can be sent in a file
filesystem:
filename: /var/lib/authelia/notifications/notification.txt
# Use your gmail account to send the notifications. You can use an app password.
# gmail:
# username: user
# password: password

View File

@ -0,0 +1,41 @@
version: '2'
services:
authelia:
image: node:7-alpine
command: node /usr/src/dist/src/server/index.js /etc/authelia/config.yml
volumes:
- ./:/usr/src
- ./test/integration/config.yml:/etc/authelia/config.yml:ro
networks:
- example-network
int-test:
build: ./test/integration
volumes:
- ./:/usr/src
networks:
- example-network
nginx:
image: nginx:alpine
volumes:
- ./example/nginx/index.html:/usr/share/nginx/html/index.html
- ./example/nginx/secret.html:/usr/share/nginx/html/secret.html
- ./example/nginx/ssl:/etc/ssl
- ./test/integration/nginx.conf:/etc/nginx/nginx.conf
expose:
- "8080"
depends_on:
- authelia
networks:
example-network:
aliases:
- home.test.local
- secret.test.local
- secret1.test.local
- secret2.test.local
- mx1.mail.test.local
- mx2.mail.test.local
- auth.test.local

View File

@ -0,0 +1,86 @@
# nginx-sso - example nginx config
#
# (c) 2015 by Johannes Gilger <heipei@hackvalue.de>
#
# This is an example config for using nginx with the nginx-sso cookie system.
# For simplicity, this config sets up two fictional vhosts that you can use to
# test against both components of the nginx-sso system: ssoauth & ssologin.
# In a real deployment, these vhosts would be separate hosts.
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
server {
listen 8080 ssl;
server_name auth.test.local localhost;
ssl on;
ssl_certificate /etc/ssl/server.crt;
ssl_certificate_key /etc/ssl/server.key;
location / {
proxy_set_header X-Original-URI $request_uri;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://authelia/;
proxy_intercept_errors on;
error_page 401 = /error/401;
error_page 403 = /error/403;
error_page 404 = /error/404;
}
}
server {
listen 8080 ssl;
root /usr/share/nginx/html;
server_name secret1.test.local secret2.test.local secret.test.local
home.test.local mx1.mail.test.local mx2.mail.test.local;
ssl on;
ssl_certificate /etc/ssl/server.crt;
ssl_certificate_key /etc/ssl/server.key;
error_page 401 = @error401;
location @error401 {
return 302 https://auth.test.local:8080;
}
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 = /secret.html {
auth_request /auth_verify;
auth_request_set $user $upstream_http_x_remote_user;
proxy_set_header X-Forwarded-User $user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-Groups $groups;
auth_request_set $expiry $upstream_http_remote_expiry;
proxy_set_header Remote-Expiry $expiry;
}
}
}

View File

@ -1,157 +0,0 @@
import request_ = require("request");
import assert = require("assert");
import speakeasy = require("speakeasy");
import BluebirdPromise = require("bluebird");
import util = require("util");
import sinon = require("sinon");
import Endpoints = require("../../src/server/endpoints");
const j = request_.jar();
const request: typeof request_ = <typeof request_>BluebirdPromise.promisifyAll(request_.defaults({ jar: j }));
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
const AUTHELIA_HOST = "nginx";
const DOMAIN = "test.local";
const PORT = 8080;
const HOME_URL = util.format("https://%s.%s:%d", "home", DOMAIN, PORT);
const SECRET_URL = util.format("https://%s.%s:%d", "secret", DOMAIN, PORT);
const SECRET1_URL = util.format("https://%s.%s:%d", "secret1", DOMAIN, PORT);
const SECRET2_URL = util.format("https://%s.%s:%d", "secret2", DOMAIN, PORT);
const MX1_URL = util.format("https://%s.%s:%d", "mx1.mail", DOMAIN, PORT);
const MX2_URL = util.format("https://%s.%s:%d", "mx2.mail", DOMAIN, PORT);
const BASE_AUTH_URL = util.format("https://%s.%s:%d", "auth", DOMAIN, PORT);
describe("test the server", function () {
let home_page: string;
let login_page: string;
before(function () {
const home_page_promise = getHomePage()
.then(function (data) {
home_page = data.body;
});
const login_page_promise = getLoginPage()
.then(function (data) {
login_page = data.body;
});
return BluebirdPromise.all([home_page_promise,
login_page_promise]);
});
function str_contains(str: string, pattern: string) {
return str.indexOf(pattern) != -1;
}
function home_page_contains(pattern: string) {
return str_contains(home_page, pattern);
}
it("should serve a correct home page", function () {
assert(home_page_contains(BASE_AUTH_URL + Endpoints.LOGOUT_GET + "?redirect=" + HOME_URL + "/"));
assert(home_page_contains(HOME_URL + "/secret.html"));
assert(home_page_contains(SECRET_URL + "/secret.html"));
assert(home_page_contains(SECRET1_URL + "/secret.html"));
assert(home_page_contains(SECRET2_URL + "/secret.html"));
assert(home_page_contains(MX1_URL + "/secret.html"));
assert(home_page_contains(MX2_URL + "/secret.html"));
});
it("should serve the login page", function (done) {
getPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_GET + "?redirect=/")
.then(function (data: request_.RequestResponse) {
assert.equal(data.statusCode, 200);
done();
});
});
it("should serve the homepage", function (done) {
getPromised(HOME_URL + "/")
.then(function (data: request_.RequestResponse) {
assert.equal(data.statusCode, 200);
done();
});
});
it("should redirect when logout", function (done) {
getPromised(BASE_AUTH_URL + Endpoints.LOGOUT_GET + "?redirect=" + HOME_URL)
.then(function (data: request_.RequestResponse) {
assert.equal(data.statusCode, 200);
assert.equal(data.body, home_page);
done();
});
});
it("should be redirected to the login page when accessing secret while not authenticated", function (done) {
const url = HOME_URL + "/secret.html";
getPromised(url)
.then(function (data: request_.RequestResponse) {
assert.equal(data.statusCode, 200);
assert.equal(data.body, login_page);
done();
});
});
it.skip("should fail the first factor", function (done) {
postPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_POST, {
form: {
username: "admin",
password: "password",
}
})
.then(function (data: request_.RequestResponse) {
assert.equal(data.body, "Bad credentials");
done();
});
});
function login_as(username: string, password: string) {
return postPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_POST, {
form: {
username: "john",
password: "password",
}
})
.then(function (data: request_.RequestResponse) {
assert.equal(data.statusCode, 302);
return BluebirdPromise.resolve();
});
}
it("should succeed the first factor", function () {
return login_as("john", "password");
});
describe("test ldap connection", function () {
it("should not fail after inactivity", function () {
const clock = sinon.useFakeTimers();
return login_as("john", "password")
.then(function () {
clock.tick(3600000 * 24); // 24 hour
return login_as("john", "password");
})
.then(function () {
clock.restore();
return BluebirdPromise.resolve();
});
});
});
});
function getPromised(url: string) {
return request.getAsync(url);
}
function postPromised(url: string, body: Object) {
return request.postAsync(url, body);
}
function getHomePage(): BluebirdPromise<request_.RequestResponse> {
return getPromised(HOME_URL + "/");
}
function getLoginPage(): BluebirdPromise<request_.RequestResponse> {
return getPromised(BASE_AUTH_URL + Endpoints.FIRST_FACTOR_GET);
}