Fix and parallelize integration tests.

pull/474/head
Clement Michaud 2019-11-30 17:49:52 +01:00 committed by Clément Michaud
parent be802cfc7b
commit b89f63e9c1
88 changed files with 1047 additions and 500 deletions

View File

@ -3,7 +3,7 @@ language: go
required: sudo
go:
- '1.13'
- "1.13"
services:
- docker
@ -19,25 +19,73 @@ addons:
- libgif-dev
- google-chrome-stable
install: # Install ChromeDriver (64bits; replace 64 with 32 for 32bits).
install:
- go mod download
before_script:
- export PATH=./cmd/authelia-scripts/:/tmp:$PATH
- source bootstrap.sh
jobs:
include:
- stage: build & test
before_script:
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
- nvm install v12 && nvm use v12
script:
- authelia-scripts --log-level debug ci
# Run all suites in a dedicated container
- &e2e-test
stage: end-to-end suite tests
env:
- SUITE_NAME=BypassAll
before_script:
# Install chrome driver
- wget -N https://chromedriver.storage.googleapis.com/78.0.3904.70/chromedriver_linux64.zip -P ~/
- unzip ~/chromedriver_linux64.zip -d ~/
- rm ~/chromedriver_linux64.zip
- sudo mv -f ~/chromedriver /usr/local/share/
- sudo chmod +x /usr/local/share/chromedriver
- sudo ln -s /usr/local/share/chromedriver /usr/bin/chromedriver
before_script:
- export PATH=./cmd/authelia-scripts/:/tmp:$PATH
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
- nvm install v12 && nvm use v12 && npm i
- source bootstrap.sh
jobs:
include:
- stage: test
script:
- authelia-scripts --log-level debug ci
# Run the suite
- CI=true authelia-scripts --log-level debug suites test $SUITE_NAME --headless
# TODO(c.michaud): check if all suites are listed based on `authelia-scripts suites list` command.
- <<: *e2e-test
env:
- SUITE_NAME=Docker
- <<: *e2e-test
env:
- SUITE_NAME=DuoPush
- <<: *e2e-test
env:
- SUITE_NAME=HighAvailability
- <<: *e2e-test
env:
- SUITE_NAME=Kubernetes
- <<: *e2e-test
env:
- SUITE_NAME=LDAP
- <<: *e2e-test
env:
- SUITE_NAME=Mariadb
- <<: *e2e-test
env:
- SUITE_NAME=NetworkACL
- <<: *e2e-test
env:
- SUITE_NAME=Postgres
- <<: *e2e-test
env:
- SUITE_NAME=ShortTimeouts
- <<: *e2e-test
env:
- SUITE_NAME=Standalone
- <<: *e2e-test
env:
- SUITE_NAME=Traefik
- &build-images
stage: build images
env:
@ -56,9 +104,9 @@ jobs:
- tar -czf authelia-linux-$ARCH.tar.gz authelia-linux-$ARCH public_html
deploy:
provider: releases
api_key: '$GITHUB_API_KEY'
api_key: "$GITHUB_API_KEY"
file_glob: true
file: 'authelia-linux-$ARCH.tar.gz'
file: "authelia-linux-$ARCH.tar.gz"
skip_cleanup: true
on:
tags: true

View File

@ -7,7 +7,14 @@ FROM golang:1.13-alpine AS builder-backend
RUN apk --no-cache add gcc musl-dev
WORKDIR /go/src/app
COPY . .
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download
COPY cmd cmd
COPY internal internal
# CGO_ENABLED=1 is mandatory for building go-sqlite3
RUN cd cmd/authelia && GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -tags netgo -ldflags '-w' -o authelia
@ -16,10 +23,10 @@ RUN cd cmd/authelia && GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -tags netg
# ========================================
# ===== Build image for the frontend =====
# ========================================
FROM node:11-alpine AS builder-frontend
FROM node:12-alpine AS builder-frontend
WORKDIR /node/src/app
COPY client .
COPY web .
# Install the dependencies and build
RUN npm ci && npm run build
@ -41,4 +48,4 @@ EXPOSE 9091
VOLUME /etc/authelia
VOLUME /var/lib/authelia
CMD ["./authelia", "-config", "/etc/authelia/config.yml"]
CMD ["./authelia", "-config", "/etc/authelia/configuration.yml"]

View File

@ -8,7 +8,14 @@ COPY ./qemu-arm-static /usr/bin/qemu-arm-static
RUN apk --no-cache add gcc musl-dev
WORKDIR /go/src/app
COPY . .
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download
COPY cmd cmd
COPY internal internal
# CGO_ENABLED=1 is mandatory for building go-sqlite3
RUN cd cmd/authelia && GOOS=linux GOARCH=arm CGO_ENABLED=1 go build -tags netgo -ldflags '-w' -o authelia
@ -17,10 +24,10 @@ RUN cd cmd/authelia && GOOS=linux GOARCH=arm CGO_ENABLED=1 go build -tags netgo
# ========================================
# ===== Build image for the frontend =====
# ========================================
FROM node:11-alpine AS builder-frontend
FROM node:12-alpine AS builder-frontend
WORKDIR /node/src/app
COPY client .
COPY web .
# Install the dependencies and build
RUN npm ci && npm run build
@ -45,4 +52,4 @@ EXPOSE 9091
VOLUME /etc/authelia
VOLUME /var/lib/authelia
CMD ["./authelia", "-config", "/etc/authelia/config.yml"]
CMD ["./authelia", "-config", "/etc/authelia/configuration.yml"]

View File

@ -8,7 +8,14 @@ COPY ./qemu-aarch64-static /usr/bin/qemu-aarch64-static
RUN apk --no-cache add gcc musl-dev
WORKDIR /go/src/app
COPY . .
COPY go.mod go.mod
COPY go.sum go.sum
RUN go mod download
COPY cmd cmd
COPY internal internal
# CGO_ENABLED=1 is mandatory for building go-sqlite3
RUN cd cmd/authelia && GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build -tags netgo -ldflags '-w' -o authelia
@ -17,10 +24,10 @@ RUN cd cmd/authelia && GOOS=linux GOARCH=arm64 CGO_ENABLED=1 go build -tags netg
# ========================================
# ===== Build image for the frontend =====
# ========================================
FROM node:11-alpine AS builder-frontend
FROM node:12-alpine AS builder-frontend
WORKDIR /node/src/app
COPY client .
COPY web .
# Install the dependencies and build
RUN npm ci && npm run build
@ -45,4 +52,4 @@ EXPOSE 9091
VOLUME /etc/authelia
VOLUME /var/lib/authelia
CMD ["./authelia", "-config", "/etc/authelia/config.yml"]
CMD ["./authelia", "-config", "/etc/authelia/configuration.yml"]

View File

@ -7,6 +7,9 @@ if [ -z "$OLD_PS1" ]; then
export PS1="(authelia) $PS1"
fi
export USER_ID=$(id -u)
export GROUP_ID=$(id -g)
echo "[BOOTSTRAP] Checking if Go is installed..."
if [ ! -x "$(command -v go)" ];

View File

@ -96,15 +96,6 @@ func shell(cmd string) {
runCommand("bash", "-c", cmd)
}
func buildHelperDockerImages() {
shell("docker build -t authelia-example-backend example/compose/nginx/backend")
shell("docker build -t authelia-duo-api example/compose/duo-api")
shell("docker-compose -f docker-compose.yml -f example/compose/kind/docker-compose.yml build")
shell("docker-compose -f docker-compose.yml -f example/compose/authelia/docker-compose.backend.yml build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)")
shell("docker-compose -f docker-compose.yml -f example/compose/authelia/docker-compose.frontend.yml build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g)")
}
func prepareHostsFile() {
contentBytes, err := readHostsFile()
@ -209,9 +200,6 @@ func Bootstrap(cobraCmd *cobra.Command, args []string) {
log.Fatal("GOPATH is not set")
}
bootstrapPrintln("Building development Docker images...")
buildHelperDockerImages()
createTemporaryDirectory()
bootstrapPrintln("Preparing /etc/hosts to serve subdomains of example.com...")

View File

@ -24,7 +24,7 @@ func buildAutheliaBinary() {
func buildFrontend() {
// Install npm dependencies
cmd := utils.CommandWithStdout("npm", "ci")
cmd.Dir = "client"
cmd.Dir = "web"
if err := cmd.Run(); err != nil {
log.Fatal(err)
@ -32,13 +32,13 @@ func buildFrontend() {
// Then build the frontend
cmd = utils.CommandWithStdout("npm", "run", "build")
cmd.Dir = "client"
cmd.Dir = "web"
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
if err := os.Rename("client/build", OutputDir+"/public_html"); err != nil {
if err := os.Rename("web/build", OutputDir+"/public_html"); err != nil {
log.Fatal(err)
}
}

View File

@ -18,18 +18,18 @@ const dockerPullCommandLine = "docker-compose -f docker-compose.yml " +
// RunCI run the CI scripts
func RunCI(cmd *cobra.Command, args []string) {
log.Info("=====> Build stage")
log.Info("=====> Build stage <=====")
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "build").Run(); err != nil {
log.Fatal(err)
}
log.Info("=====> Unit testing stage")
log.Info("=====> Unit testing stage <=====")
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "unittest").Run(); err != nil {
log.Fatal(err)
}
log.Info("=====> End-to-end testing stage")
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "suites", "test", "--headless", "--only-forbidden").Run(); err != nil {
log.Info("=====> Build Docker stage <=====")
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "docker", "build").Run(); err != nil {
log.Fatal(err)
}
}

View File

@ -27,11 +27,9 @@ var ErrNoRunningSuite = errors.New("no running suite")
var runningSuiteFile = ".suite"
var headless bool
var onlyForbidden bool
func init() {
SuitesTestCmd.Flags().BoolVar(&headless, "headless", false, "Run tests in headless mode")
SuitesTestCmd.Flags().BoolVar(&onlyForbidden, "only-forbidden", false, "Mocha 'only' filters are forbidden")
}
// SuitesListCmd Command for listing the available suites
@ -79,17 +77,17 @@ var SuitesTeardownCmd = &cobra.Command{
runningSuite, err := getRunningSuite()
if err != nil {
panic(err)
log.Fatal(err)
}
if runningSuite == "" {
panic(ErrNoRunningSuite)
log.Fatal(ErrNoRunningSuite)
}
suiteName = runningSuite
}
if err := teardownSuite(suiteName); err != nil {
panic(err)
log.Fatal(err)
}
},
Args: cobra.MaximumNArgs(1),
@ -143,6 +141,14 @@ func runSuiteSetupTeardown(command string, suite string) error {
return utils.RunCommandWithTimeout(cmd, s.SetUpTimeout)
}
func runOnSetupTimeout(suite string) error {
cmd := utils.CommandWithStdout("go", "run", "cmd/authelia-suites/main.go", "timeout", suite)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
return utils.RunCommandWithTimeout(cmd, 15*time.Second)
}
func setupSuite(suiteName string) error {
log.Infof("Setup environment for suite %s...", suiteName)
signalChannel := make(chan os.Signal)
@ -156,10 +162,10 @@ func setupSuite(suiteName string) error {
}()
if errSetup := runSuiteSetupTeardown("setup", suiteName); errSetup != nil || interrupted {
err := teardownSuite(suiteName)
if err != nil {
log.Error(err)
if errSetup == utils.ErrTimeoutReached {
runOnSetupTimeout(suiteName)
}
teardownSuite(suiteName)
return errSetup
}
@ -231,7 +237,7 @@ func runSuiteTests(suiteName string, withEnv bool) error {
if suite.TestTimeout > 0 {
timeout = fmt.Sprintf("%ds", int64(suite.TestTimeout/time.Second))
}
testCmdLine := fmt.Sprintf("go test ./internal/suites -timeout %s -run '^(Test%sSuite)$'", timeout, suiteName)
testCmdLine := fmt.Sprintf("go test -v ./internal/suites -timeout %s -run '^(Test%sSuite)$'", timeout, suiteName)
log.Infof("Running tests of suite %s...", suiteName)
log.Debugf("Running tests with command: %s", testCmdLine)

View File

@ -32,6 +32,12 @@ func main() {
Run: setupSuite,
}
setupTimeoutCmd := &cobra.Command{
Use: "timeout [suite]",
Short: "Run the OnSetupTimeout callback when setup times out",
Run: setupTimeoutSuite,
}
stopCmd := &cobra.Command{
Use: "teardown [suite]",
Short: "Teardown the suite environment",
@ -39,6 +45,7 @@ func main() {
}
rootCmd.AddCommand(startCmd)
rootCmd.AddCommand(setupTimeoutCmd)
rootCmd.AddCommand(stopCmd)
rootCmd.Execute()
}
@ -101,6 +108,18 @@ func setupSuite(cmd *cobra.Command, args []string) {
log.Info("Environment is ready!")
}
func setupTimeoutSuite(cmd *cobra.Command, args []string) {
suiteName := args[0]
s := suites.GlobalRegistry.Get(suiteName)
if s.OnSetupTimeout == nil {
return
}
if err := s.OnSetupTimeout(); err != nil {
log.Fatal(err)
}
}
func teardownSuite(cmd *cobra.Command, args []string) {
if os.Getenv("SKIP_TEARDOWN") != "" {
return

View File

@ -99,7 +99,6 @@ authentication_backend:
## file:
## path: ./users_database.yml
# Access Control
#
# Access control is a list of rules defining the authorizations applied for one
@ -156,47 +155,45 @@ access_control:
- domain: singlefactor.example.com
policy: one_factor
# Rules applied to 'admin' group
- domain: 'mx2.mail.example.com'
subject: 'group:admin'
# Rules applied to 'admins' group
- domain: "mx2.mail.example.com"
subject: "groups:admins"
policy: deny
- domain: '*.example.com'
subject: 'group:admin'
- domain: "*.example.com"
subject: "groups:admins"
policy: two_factor
# Rules applied to 'dev' group
- domain: dev.example.com
resources:
- '^/groups/dev/.*$'
subject: 'group:dev'
- "^/groups/dev/.*$"
subject: "group:dev"
policy: two_factor
# Rules applied to user 'john'
- domain: dev.example.com
resources:
- '^/users/john/.*$'
subject: 'user:john'
- "^/users/john/.*$"
subject: "user:john"
policy: two_factor
# Rules applied to user 'harry'
- domain: dev.example.com
resources:
- '^/users/harry/.*$'
subject: 'user:harry'
- "^/users/harry/.*$"
subject: "user:harry"
policy: two_factor
# Rules applied to user 'bob'
- domain: '*.mail.example.com'
subject: 'user:bob'
- domain: "*.mail.example.com"
subject: "user:bob"
policy: two_factor
- domain: 'dev.example.com'
- domain: "dev.example.com"
resources:
- '^/users/bob/.*$'
subject: 'user:bob'
- "^/users/bob/.*$"
subject: "user:bob"
policy: two_factor
# Configuration of session cookies
#
# The session cookies identify the user once logged in.
@ -283,7 +280,6 @@ notifier:
host: 127.0.0.1
port: 1025
sender: admin@example.com
# Sending an email using a Gmail account is as simple as the next section.
# You need to create an app password by following: https://support.google.com/accounts/answer/185833?hl=en
## smtp:

View File

@ -1,8 +1,10 @@
FROM golang:1.13-stretch
FROM golang:1.13-alpine
RUN apk --no-cache add gcc musl-dev
ARG USER_ID
ARG GROUP_ID
RUN groupadd -g ${GROUP_ID} dev && \
useradd -m -u $USER_ID -g $GROUP_ID dev
RUN addgroup --gid ${GROUP_ID} dev && \
adduser --uid ${USER_ID} -G dev -D dev
USER dev

View File

@ -1,9 +1,9 @@
FROM node:11-stretch-slim
FROM node:12-alpine
ARG USER_ID
ARG GROUP_ID
RUN cat /etc/passwd && userdel -rf node && \
groupadd -g ${GROUP_ID} dev && \
useradd -m -u $USER_ID -g $GROUP_ID dev
RUN deluser node && \
addgroup --gid ${GROUP_ID} dev && \
adduser --uid ${USER_ID} -G dev -D dev
USER dev

View File

@ -0,0 +1,14 @@
version: "3"
services:
authelia-backend:
build:
context: .
dockerfile: Dockerfile
volumes:
- "/tmp/authelia:/tmp/authelia"
environment:
- ENVIRONMENT=dev
restart: always
networks:
authelianet:
ipv4_address: 192.168.240.50

View File

@ -4,6 +4,9 @@ services:
build:
context: example/compose/authelia
dockerfile: Dockerfile.backend
args:
USER_ID: ${USER_ID}
GROUP_ID: ${GROUP_ID}
command: /resources/entrypoint.sh
working_dir: /app
volumes:

View File

@ -1,4 +1,4 @@
version: '3'
version: "3"
services:
authelia-frontend:
image: nginx:alpine

View File

@ -1,10 +1,13 @@
version: '3'
version: "3"
services:
authelia-frontend:
build:
context: example/compose/authelia
dockerfile: Dockerfile.frontend
command: npm run start
args:
USER_ID: ${USER_ID}
GROUP_ID: ${GROUP_ID}
command: sh -c 'npm ci && npm run start'
working_dir: /app
volumes:
- "./web:/app"

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
set -x

View File

@ -7,11 +7,11 @@ events {
http {
server {
listen 80;
listen 3000;
location / {
proxy_set_header Host $http_host;
proxy_pass http://authelia-backend;
proxy_pass http://authelia-backend:9091;
}
}
}

View File

@ -1,30 +1,12 @@
#!/bin/bash
#!/bin/sh
set -e
# Retries a command on failure.
# $1 - the max number of attempts
# $2... - the command to run
retry() {
local -r -i max_attempts="$1"; shift
local -r cmd="$@"
local -i attempt_num=1
until $cmd
do
if ((attempt_num==max_attempts))
then
echo "Attempt $attempt_num failed and there are no more attempts left!"
return 1
else
echo "Attempt $attempt_num failed! Trying again in 10 seconds..."
sleep 10
fi
done
}
# Build the binary
go build -o /tmp/authelia/authelia-tmp cmd/authelia/main.go
retry 3 /tmp/authelia/authelia-tmp -config /etc/authelia/configuration.yml
while true;
do
/tmp/authelia/authelia-tmp -config /etc/authelia/configuration.yml
sleep 10
done

View File

@ -1,4 +1,4 @@
FROM node:8.7.0-alpine
FROM node:12-alpine
WORKDIR /usr/app/src

View File

@ -1,6 +1,7 @@
version: '3'
version: "3"
services:
duo-api:
image: authelia-duo-api
build:
context: ./example/compose/duo-api
networks:
- authelianet

View File

@ -16,15 +16,18 @@ let permission = 'allow';
app.post('/allow', (req, res) => {
permission = 'allow';
console.log("set allowed!");
res.send('ALLOWED');
});
app.post('/deny', (req, res) => {
permission = 'deny';
console.log("set denied!");
res.send('DENIED');
});
app.post('/auth/v2/auth', (req, res) => {
setTimeout(() => {
let response;
if (permission == 'allow') {
response = {
@ -45,7 +48,8 @@ app.post('/auth/v2/auth', (req, res) => {
stat: 'OK',
};
}
setTimeout(() => res.json(response), 2000);
res.json(response);
}, 2000);
});
app.listen(port, () => console.log(`Duo API listening on port ${port}!`));

View File

@ -15,8 +15,8 @@ member: cn=bob,ou=users,dc=example,dc=com
objectclass: groupOfNames
objectclass: top
dn: cn=admin,ou=groups,dc=example,dc=com
cn: admin
dn: cn=admins,ou=groups,dc=example,dc=com
cn: admins
member: cn=john,ou=users,dc=example,dc=com
objectclass: groupOfNames
objectclass: top

View File

@ -1,4 +1,4 @@
FROM nginx:alpine
ADD ./html /usr/share/nginx/html
ADD ./nginx.conf /etc/nginx/nginx.conf
ADD html /usr/share/nginx/html
ADD nginx.conf /etc/nginx/nginx.conf

View File

@ -1,10 +1,11 @@
version: '3'
version: "3"
services:
nginx-backend:
image: authelia-example-backend
build:
context: example/compose/nginx/backend
labels:
- traefik.frontend.rule=Host:home.example.com,public.example.com,secure.example.com,admin.example.com,singlefactor.example.com
- traefik.frontend.auth.forward.address=http://192.168.240.1:9091/api/verify?rd=https://login.example.com:8080/%23/
- traefik.frontend.auth.forward.address=http://authelia-backend:9091/api/verify?rd=https://login.example.com:8080/
- traefik.frontend.auth.forward.tls.insecureSkipVerify=true
networks:
- authelianet

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -8,7 +8,8 @@
<body>
<h1>Access the secret</h1>
<span style="font-size: 1.2em; color: red">You need to log in to access the secret!</span><br/><br/> Try to access it using
<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>
@ -59,12 +60,15 @@
</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>.
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 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 in a <strong>fake webmail</strong> at <a href="http://localhost:8085">http://localhost:8085</a>.<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 in a <strong>fake webmail</strong> at <a
href="http://localhost:8085">http://localhost:8085</a>.<br />
It will provide you with the link to complete the registration allowing you to authenticate with 2-factor.
<ul>
@ -86,12 +90,12 @@
- domain: singlefactor.example.com
policy: one_factor
# Rules applied to 'admin' group
# Rules applied to 'admins' group
- domain: 'mx2.mail.example.com'
subject: 'group:admin'
subject: 'groups:admins'
policy: deny
- domain: '*.example.com'
subject: 'group:admin'
subject: 'groups:admins'
policy: two_factor
# Rules applied to 'dev' group

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -1,10 +1,13 @@
<html>
<head>
<title>Secret</title>
<link rel="icon" href="/icon.png" type="image/png" />
</head>
<body>
<body id="secret">
This is a very important secret!<br />
Go back to <a href="https://home.example.com:8080/">home page</a>.
</body>
</html>

View File

@ -19,11 +19,10 @@ spec:
containers:
- name: test-app
imagePullPolicy: Never
image: authelia-example-backend
image: nginx-backend
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
@ -130,4 +129,3 @@ spec:
backend:
serviceName: test-app-service
servicePort: 80

View File

@ -33,47 +33,45 @@ access_control:
- domain: singlefactor.example.com
policy: one_factor
# Rules applied to 'admin' group
- domain: 'mx2.mail.example.com'
subject: 'group:admin'
# Rules applied to 'admins' group
- domain: "mx2.mail.example.com"
subject: "group:admins"
policy: deny
- domain: '*.example.com'
subject: 'group:admin'
- domain: "*.example.com"
subject: "group:admins"
policy: two_factor
# Rules applied to 'dev' group
- domain: dev.example.com
resources:
- '^/groups/dev/.*$'
subject: 'group:dev'
- "^/groups/dev/.*$"
subject: "group:dev"
policy: two_factor
# Rules applied to user 'john'
- domain: dev.example.com
resources:
- '^/users/john/.*$'
subject: 'user:john'
- "^/users/john/.*$"
subject: "user:john"
policy: two_factor
# Rules applied to user 'harry'
- domain: dev.example.com
resources:
- '^/users/harry/.*$'
subject: 'user:harry'
- "^/users/harry/.*$"
subject: "user:harry"
policy: two_factor
# Rules applied to user 'bob'
- domain: '*.mail.example.com'
subject: 'user:bob'
- domain: "*.mail.example.com"
subject: "user:bob"
policy: two_factor
- domain: 'dev.example.com'
- domain: "dev.example.com"
resources:
- '^/users/bob/.*$'
subject: 'user:bob'
- "^/users/bob/.*$"
subject: "user:bob"
policy: two_factor
session:
secret: unsecure_password
expiration: 3600000 # 1 hour
@ -98,6 +96,6 @@ storage:
notifier:
smtp:
host: 'mailcatcher-service'
host: "mailcatcher-service"
port: 1025
sender: admin@example.com

View File

@ -29,5 +29,5 @@ spec:
configMap:
name: authelia-config
items:
- key: config.yml
path: config.yml
- key: configuration.yml
path: configuration.yml

View File

@ -1,7 +1,7 @@
#!/bin/sh
start_authelia() {
kubectl create configmap authelia-config --namespace=authelia --from-file=authelia/configs/config.yml
kubectl create configmap authelia-config --namespace=authelia --from-file=authelia/configs/configuration.yml
kubectl apply -f authelia
}

View File

@ -15,8 +15,8 @@ member: cn=bob,ou=users,dc=example,dc=com
objectclass: groupOfNames
objectclass: top
dn: cn=admin,ou=groups,dc=example,dc=com
cn: admin
dn: cn=admins,ou=groups,dc=example,dc=com
cn: admins
member: cn=john,ou=users,dc=example,dc=com
objectclass: groupOfNames
objectclass: top

View File

@ -1,13 +1,13 @@
version: '3.4'
version: "3.4"
services:
authelia:
image: clems4ever/authelia:latest
# Used for Docker configs
configs:
- source: authelia
target: /etc/authelia/config.yml
uid: '0'
gid: '0'
target: /etc/authelia/configuration.yml
uid: "0"
gid: "0"
mode: 0444
environment:
- NODE_TLS_REJECT_UNAUTHORIZED=0

1
go.mod
View File

@ -7,6 +7,7 @@ require (
github.com/Workiva/go-datastructures v1.0.50
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/cespare/reflex v0.2.0 // indirect
github.com/deckarep/golang-set v1.7.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect

2
go.sum
View File

@ -33,6 +33,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M=

View File

@ -128,7 +128,7 @@ users
john
email: john.doe@authelia.com
groups:
- admin
- admins
- dev
`)

View File

@ -48,44 +48,43 @@ access_control:
- domain: singlefactor.example.com
policy: one_factor
# Rules applied to 'admin' group
- domain: 'mx2.mail.example.com'
subject: 'group:admin'
# Rules applied to 'admins' group
- domain: "mx2.mail.example.com"
subject: "groups:admins"
policy: deny
- domain: '*.example.com'
subject: 'group:admin'
- domain: "*.example.com"
subject: "groups:admins"
policy: two_factor
# Rules applied to 'dev' group
- domain: dev.example.com
resources:
- '^/groups/dev/.*$'
subject: 'group:dev'
- "^/groups/dev/.*$"
subject: "group:dev"
policy: two_factor
# Rules applied to user 'john'
- domain: dev.example.com
resources:
- '^/users/john/.*$'
subject: 'user:john'
- "^/users/john/.*$"
subject: "user:john"
policy: two_factor
# Rules applied to user 'harry'
- domain: dev.example.com
resources:
- '^/users/harry/.*$'
subject: 'user:harry'
- "^/users/harry/.*$"
subject: "user:harry"
policy: two_factor
# Rules applied to user 'bob'
- domain: '*.mail.example.com'
subject: 'user:bob'
- domain: "*.mail.example.com"
subject: "user:bob"
policy: two_factor
- domain: 'dev.example.com'
- domain: "dev.example.com"
resources:
- '^/users/bob/.*$'
subject: 'user:bob'
- "^/users/bob/.*$"
subject: "user:bob"
policy: two_factor
session:

View File

@ -24,10 +24,11 @@ func FirstFactorPost(ctx *middlewares.AutheliaCtx) {
bannedUntil, err := ctx.Providers.Regulator.Regulate(bodyJSON.Username)
if err != nil {
if err == regulation.ErrUserIsBanned {
ctx.Error(fmt.Errorf("User %s is banned until %s", bodyJSON.Username, bannedUntil), userBannedMessage)
return
} else if err != nil {
}
ctx.Error(fmt.Errorf("Unable to regulate authentication: %s", err), authenticationFailedMessage)
return
}
@ -43,6 +44,9 @@ func FirstFactorPost(ctx *middlewares.AutheliaCtx) {
}
if !userPasswordOk {
ctx.Logger.Debugf("Mark authentication attempt made by user %s", bodyJSON.Username)
ctx.Providers.Regulator.Mark(bodyJSON.Username, false)
ctx.ReplyError(fmt.Errorf("Credentials are wrong for user %s", bodyJSON.Username), authenticationFailedMessage)
return
}

View File

@ -162,7 +162,7 @@ func (s *FirstFactorSuite) TestShouldAuthenticateUser() {
GetDetails(gomock.Eq("test")).
Return(&authentication.UserDetails{
Emails: []string{"test@example.com"},
Groups: []string{"dev", "admin"},
Groups: []string{"dev", "admins"},
}, nil)
s.mock.StorageProviderMock.
@ -186,7 +186,7 @@ func (s *FirstFactorSuite) TestShouldAuthenticateUser() {
assert.Equal(s.T(), "test", session.Username)
assert.Equal(s.T(), authentication.OneFactor, session.AuthenticationLevel)
assert.Equal(s.T(), []string{"test@example.com"}, session.Emails)
assert.Equal(s.T(), []string{"dev", "admin"}, session.Groups)
assert.Equal(s.T(), []string{"dev", "admins"}, session.Groups)
}

View File

@ -208,7 +208,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyDefaultPolicy() {
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admin"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(mock.Ctx)
@ -231,7 +231,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfBypassDomain() {
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admin"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(mock.Ctx)
@ -254,7 +254,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfOneFactorDomain() {
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admin"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(mock.Ctx)
@ -277,7 +277,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfTwoFactorDomain() {
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admin"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(mock.Ctx)
@ -300,7 +300,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfDenyDomain() {
GetDetails(gomock.Eq("john")).
Return(&authentication.UserDetails{
Emails: []string{"john@example.com"},
Groups: []string{"dev", "admin"},
Groups: []string{"dev", "admins"},
}, nil)
VerifyGet(mock.Ctx)

View File

@ -3,6 +3,7 @@ package server
import (
"fmt"
"os"
"path"
"github.com/clems4ever/authelia/internal/configuration/schema"
"github.com/clems4ever/authelia/internal/duo"
@ -27,7 +28,6 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi
fmt.Println("Selected public_html directory is ", publicDir)
router.GET("/", fasthttp.FSHandler(publicDir, 0))
router.NotFound = fasthttp.FSHandler(publicDir, 0)
router.ServeFiles("/static/*filepath", publicDir+"/static")
router.GET("/api/state", autheliaMiddleware(handlers.StateGet))
@ -95,6 +95,10 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi
middlewares.RequireFirstFactor(handlers.SecondFactorDuoPost(duoAPI))))
}
router.NotFound = func(ctx *fasthttp.RequestCtx) {
ctx.SendFile(path.Join(publicDir, "index.html"))
}
portPattern := fmt.Sprintf(":%d", configuration.Port)
logging.Logger().Infof("Authelia is listening on %s", portPattern)

View File

@ -0,0 +1,81 @@
###############################################################
# Authelia minimal configuration #
###############################################################
port: 9091
logs_level: debug
default_redirection_url: https://home.example.com:8080/
jwt_secret: very_important_secret
authentication_backend:
file:
path: /var/lib/authelia/users.yml
session:
secret: unsecure_session_secret
domain: example.com
expiration: 3600 # 1 hour
inactivity: 300 # 5 minutes
storage:
local:
path: /tmp/authelia/db.sqlite3
totp:
issuer: example.com
access_control:
default_policy: deny
rules:
- domain: singlefactor.example.com
policy: one_factor
- domain: public.example.com
policy: bypass
- domain: secure.example.com
policy: two_factor
- domain: "*.example.com"
subject: "group:admins"
policy: two_factor
- domain: dev.example.com
resources:
- "^/users/john/.*$"
subject: "user:john"
policy: two_factor
- domain: dev.example.com
resources:
- "^/users/harry/.*$"
subject: "user:harry"
policy: two_factor
- domain: "*.mail.example.com"
subject: "user:bob"
policy: two_factor
- domain: dev.example.com
resources:
- "^/users/bob/.*$"
subject: "user:bob"
policy: two_factor
regulation:
# Set it to 0 to disable max_retries.
max_retries: 3
# The user is banned if the authenticaction failed `max_retries` times in a `find_time` seconds window.
find_time: 300
# The length of time before a banned user can login again.
ban_time: 900
notifier:
smtp:
host: smtp
port: 1025
sender: admin@example.com

View File

@ -0,0 +1,6 @@
version: "3"
services:
authelia-backend:
volumes:
- "./internal/suites/Docker/configuration.yml:/etc/authelia/configuration.yml:ro"
- "./internal/suites/Docker/users.yml:/var/lib/authelia/users.yml"

View File

@ -0,0 +1,20 @@
users:
bob:
password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
email: bob.dylan@authelia.com
groups:
- dev
harry:
password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
email: harry.potter@authelia.com
groups: []
james:
password: "{CRYPT}$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
email: james.dean@authelia.com
groups: []
john:
password: "{CRYPT}$6$rounds=50000$LnfgDsc2WD8F2qNf$0gcCt8jlqAGZRv2ee3mCFsfAr1P4N7kESWEf36Xtw6OjkhAcQuGVOBHXp0lFuZbppa7YlgHk3VD28aSQu9U9S1"
email: john.doe@authelia.com
groups:
- admins
- dev

View File

@ -136,9 +136,9 @@ access_control:
- domain: singlefactor.example.com
policy: one_factor
# Rules applied to 'admin' group
# Rules applied to 'admins' group
- domain: mx2.mail.example.com
subject: "group:admin"
subject: "group:admins"
policy: deny
# Rules applied to user 'john'
@ -147,7 +147,7 @@ access_control:
policy: two_factor
- domain: "*.example.com"
subject: "group:admin"
subject: "group:admins"
policy: two_factor
# Rules applied to 'dev' group

View File

@ -3,3 +3,4 @@ services:
authelia-backend:
volumes:
- "./internal/suites/Mariadb/configuration.yml:/etc/authelia/configuration.yml:ro"
- "./internal/suites/Mariadb/users.yml:/var/lib/authelia/users.yml"

View File

@ -10,7 +10,7 @@ jwt_secret: unsecure_password
authentication_backend:
file:
path: users.yml
path: /var/lib/authelia/users.yml
session:
secret: unsecure_session_secret

View File

@ -4,27 +4,27 @@ import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func (wds *WebDriverSession) doFillLoginPageAndClick(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool) {
usernameElement := wds.WaitElementLocatedByID(ctx, t, "username-textfield")
err := usernameElement.SendKeys(username)
assert.NoError(t, err)
require.NoError(t, err)
passwordElement := wds.WaitElementLocatedByID(ctx, t, "password-textfield")
err = passwordElement.SendKeys(password)
assert.NoError(t, err)
require.NoError(t, err)
if keepMeLoggedIn {
keepMeLoggedInElement := wds.WaitElementLocatedByID(ctx, t, "remember-checkbox")
err = keepMeLoggedInElement.Click()
assert.NoError(t, err)
require.NoError(t, err)
}
buttonElement := wds.WaitElementLocatedByID(ctx, t, "sign-in-button")
err = buttonElement.Click()
assert.NoError(t, err)
require.NoError(t, err)
}
// Login 1FA

View File

@ -33,7 +33,7 @@ func (de *DockerEnvironment) createCommand(cmd string) *exec.Cmd {
// Up spawn a docker environment
func (de *DockerEnvironment) Up() error {
return de.createCommandWithStdout("up -d").Run()
return de.createCommandWithStdout("up --build -d").Run()
}
// Restart restarts a service

View File

@ -5,7 +5,7 @@ import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// DuoPolicy a type of policy
@ -26,10 +26,10 @@ func ConfigureDuo(t *testing.T, allowDeny DuoPolicy) {
}
req, err := http.NewRequest("POST", url, nil)
assert.NoError(t, err)
require.NoError(t, err)
client := NewHTTPClient()
res, err := client.Do(req)
assert.NoError(t, err)
assert.Equal(t, 200, res.StatusCode)
require.NoError(t, err)
require.Equal(t, 200, res.StatusCode)
}

View File

@ -35,31 +35,34 @@ func waitUntilServiceLogDetected(
return err
}
func waitUntilAutheliaIsReady(dockerEnvironment *DockerEnvironment) error {
log.Info("Waiting for Authelia to be ready...")
err := waitUntilServiceLogDetected(
func waitUntilAutheliaBackendIsReady(dockerEnvironment *DockerEnvironment) error {
return waitUntilServiceLogDetected(
5*time.Second,
90*time.Second,
dockerEnvironment,
"authelia-backend",
[]string{"Authelia is listening on"})
if err != nil {
return err
}
err = waitUntilServiceLogDetected(
func waitUntilAutheliaFrontendIsReady(dockerEnvironment *DockerEnvironment) error {
return waitUntilServiceLogDetected(
5*time.Second,
90*time.Second,
dockerEnvironment,
"authelia-frontend",
[]string{"You can now view web in the browser.", "Compiled with warnings", "Compiled successfully!"})
}
if err != nil {
func waitUntilAutheliaIsReady(dockerEnvironment *DockerEnvironment) error {
log.Info("Waiting for Authelia to be ready...")
if err := waitUntilAutheliaBackendIsReady(dockerEnvironment); err != nil {
return err
}
if err := waitUntilAutheliaFrontendIsReady(dockerEnvironment); err != nil {
return err
}
log.Info("Authelia is now ready!")
return nil
}

View File

@ -9,9 +9,12 @@ import (
// Suite the definition of a suite
type Suite struct {
TestTimeout time.Duration
SetUp func(tmpPath string) error
SetUpTimeout time.Duration
OnSetupTimeout func() error
TestTimeout time.Duration
TearDown func(tmpPath string) error
TearDownTimeout time.Duration

View File

@ -2,11 +2,15 @@ package suites
import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
"testing"
"time"
mapset "github.com/deckarep/golang-set"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/tebeka/selenium"
)
@ -59,18 +63,55 @@ func (s *CustomHeadersScenario) TestShouldNotForwardCustomHeaderForUnauthenticat
s.WaitElementTextContains(ctx, s.T(), body, "httpbin:8000")
}
type Headers struct {
ForwardedGroups string `json:"Custom-Forwarded-Groups"`
ForwardedUser string `json:"Custom-Forwarded-User"`
}
type HeadersPayload struct {
Headers Headers `json:"headers"`
}
func (s *CustomHeadersScenario) TestShouldForwardCustomHeaderForAuthenticatedUser() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
expectedGroups := mapset.NewSetWith("dev", "admins")
targetURL := fmt.Sprintf("%s/headers", PublicBaseURL)
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
s.verifyURLIs(ctx, s.T(), targetURL)
err := s.Wait(ctx, func(d selenium.WebDriver) (bool, error) {
body, err := s.WebDriver().FindElement(selenium.ByTagName, "body")
s.Assert().NoError(err)
s.WaitElementTextContains(ctx, s.T(), body, "\"Custom-Forwarded-User\": \"john\"")
s.WaitElementTextContains(ctx, s.T(), body, "\"Custom-Forwarded-Groups\": \"admins,dev\"")
if err != nil {
return false, err
}
if body == nil {
return false, nil
}
content, err := body.Text()
if err != nil {
return false, err
}
payload := HeadersPayload{}
if err := json.Unmarshal([]byte(content), &payload); err != nil {
return false, err
}
groups := strings.Split(payload.Headers.ForwardedGroups, ",")
actualGroups := mapset.NewSet()
for _, group := range groups {
actualGroups.Add(group)
}
return strings.Contains(payload.Headers.ForwardedUser, "john") && expectedGroups.Equal(actualGroups), nil
})
require.NoError(s.T(), err)
}
func TestCustomHeadersScenario(t *testing.T) {

View File

@ -57,7 +57,7 @@ func (s *RegulationScenario) TestShouldBanUserAfterTooManyAttempt() {
for i := 0; i < 3; i++ {
s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
time.Sleep(2 * time.Second)
time.Sleep(1 * time.Second)
}
// Reset password field

View File

@ -47,7 +47,7 @@ func (s *UserPreferencesScenario) SetupTest() {
}
func (s *UserPreferencesScenario) TestShouldRememberLastUsed2FAMethod() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Authenticate

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -24,7 +25,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -34,6 +50,7 @@ func init() {
GlobalRegistry.Register(bypassAllSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TestTimeout: 1 * time.Minute,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,

View File

@ -0,0 +1,57 @@
package suites
import (
"fmt"
"time"
)
func init() {
dockerEnvironment := NewDockerEnvironment([]string{
"docker-compose.yml",
"internal/suites/Docker/docker-compose.yml",
"example/compose/authelia/docker-compose.backend-dist.yml",
"example/compose/authelia/docker-compose.frontend-dist.yml",
"example/compose/nginx/backend/docker-compose.yml",
"example/compose/nginx/portal/docker-compose.yml",
"example/compose/smtp/docker-compose.yml",
})
setup := func(suitePath string) error {
if err := dockerEnvironment.Up(); err != nil {
return err
}
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
return dockerEnvironment.Down()
}
GlobalRegistry.Register("Docker", Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TestTimeout: 1 * time.Minute,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,
Description: `This suite has been created to test the distributable version of Authelia
It's often useful to test this one before the Kube one.`,
})
}

View File

@ -0,0 +1,20 @@
package suites
import (
"testing"
"github.com/stretchr/testify/suite"
)
type DockerSuite struct {
*SeleniumSuite
}
func NewDockerSuite() *DockerSuite {
return &DockerSuite{SeleniumSuite: new(SeleniumSuite)}
}
func TestDockerSuite(t *testing.T) {
suite.Run(t, NewOneFactorScenario())
suite.Run(t, NewTwoFactorScenario())
}

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -22,7 +23,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -32,6 +48,7 @@ func init() {
GlobalRegistry.Register(duoPushSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TestTimeout: 1 * time.Minute,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,

View File

@ -35,15 +35,23 @@ func (s *DuoPushSuite) TearDownSuite() {
}
}
func (s *DuoPushSuite) SetupTest() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
s.doLogout(ctx, s.T())
}
func (s *DuoPushSuite) TearDownTest() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
s.doChangeMethod(ctx, s.T(), "one-time-password")
s.WaitElementLocatedByID(ctx, s.T(), "one-time-password-method")
}
func (s *DuoPushSuite) TestShouldSucceedAuthentication() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
ConfigureDuo(s.T(), Allow)
@ -54,7 +62,7 @@ func (s *DuoPushSuite) TestShouldSucceedAuthentication() {
}
func (s *DuoPushSuite) TestShouldFailAuthentication() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
ConfigureDuo(s.T(), Deny)

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -27,7 +28,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(haDockerEnvironment)
return waitUntilAutheliaBackendIsReady(haDockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := haDockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := haDockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -37,7 +53,8 @@ func init() {
GlobalRegistry.Register(highAvailabilitySuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
TestTimeout: 1 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TestTimeout: 5 * time.Minute,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,
Description: `This suite is made to test Authelia in a *complete*

View File

@ -39,6 +39,15 @@ func (s *HighAvailabilityWebDriverSuite) TearDownSuite() {
}
}
func (s *HighAvailabilityWebDriverSuite) SetupTest() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
s.doLogout(ctx, s.T())
s.doVisit(s.T(), HomeBaseURL)
s.verifyIsHome(ctx, s.T())
}
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserDataInDB() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
@ -151,13 +160,15 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldVerifyAccessControl() {
verifyAuthorization := func(username string) func(t *testing.T) {
return func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
s.doRegisterAndLogin2FA(ctx, t, username, "password", false, "")
for url, authorizations := range expectedAuthorizations {
t.Run(url, func(t *testing.T) {
verifyUserIsAuthorized(ctx, t, username, url, authorizations[username])
})
}
s.doLogout(ctx, t)
@ -165,7 +176,7 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldVerifyAccessControl() {
}
for _, user := range []string{UserJohn, UserBob, UserHarry} {
s.T().Run(fmt.Sprintf("user %s", user), verifyAuthorization(user))
s.T().Run(fmt.Sprintf("%s", user), verifyAuthorization(user))
}
}

View File

@ -15,6 +15,16 @@ func init() {
kubectl := Kubectl{}
setup := func(suitePath string) error {
cmd := utils.Shell("docker-compose -f docker-compose.yml -f example/compose/kind/docker-compose.yml build")
if err := cmd.Run(); err != nil {
return err
}
cmd = utils.Shell("docker build -t nginx-backend example/compose/nginx/backend")
if err := cmd.Run(); err != nil {
return err
}
exists, err := kind.ClusterExists()
if err != nil {
@ -74,23 +84,24 @@ func init() {
}
teardown := func(suitePath string) error {
kubectl.StopDashboard()
kubectl.StopProxy()
return kind.DeleteCluster()
}
GlobalRegistry.Register(kubernetesSuiteName, Suite{
TestTimeout: 60 * time.Second,
SetUp: setup,
SetUpTimeout: 10 * time.Minute,
TestTimeout: 2 * time.Minute,
TearDown: teardown,
TearDownTimeout: 10 * time.Minute,
TearDownTimeout: 2 * time.Minute,
Description: "This suite has been created to test Authelia in a Kubernetes context and using nginx as the ingress controller.",
})
}
func loadDockerImages() error {
kind := Kind{}
images := []string{"authelia:dist", "authelia-example-backend"}
images := []string{"authelia:dist", "nginx-backend"}
for _, image := range images {
err := kind.LoadImage(image)

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -25,7 +26,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -36,6 +52,7 @@ func init() {
GlobalRegistry.Register(ldapSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TestTimeout: 1 * time.Minute,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -24,7 +25,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -34,7 +50,8 @@ func init() {
GlobalRegistry.Register(mariadbSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 3 * time.Minute,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,
})

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -25,7 +26,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -35,6 +51,7 @@ func init() {
GlobalRegistry.Register(networkACLSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TestTimeout: 1 * time.Minute,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,

View File

@ -3,7 +3,6 @@ package suites
import (
"context"
"fmt"
"log"
"testing"
"time"
@ -12,66 +11,26 @@ import (
type NetworkACLSuite struct {
suite.Suite
clients []*WebDriverSession
}
func NewNetworkACLSuite() *NetworkACLSuite {
return &NetworkACLSuite{clients: make([]*WebDriverSession, 3)}
}
func (s *NetworkACLSuite) createClient(idx int) {
wds, err := StartWebDriverWithProxy(fmt.Sprintf("http://proxy-client%d.example.com:3128", idx), 4444+idx)
if err != nil {
log.Fatal(err)
}
s.clients[idx] = wds
}
func (s *NetworkACLSuite) teardownClient(idx int) {
if err := s.clients[idx].Stop(); err != nil {
log.Fatal(err)
}
}
func (s *NetworkACLSuite) SetupSuite() {
wds, err := StartWebDriver()
if err != nil {
log.Fatal(err)
}
s.clients[0] = wds
for i := 1; i <= 2; i++ {
s.createClient(i)
}
}
func (s *NetworkACLSuite) TearDownSuite() {
if err := s.clients[0].Stop(); err != nil {
log.Fatal(err)
}
for i := 1; i <= 2; i++ {
s.teardownClient(i)
}
return &NetworkACLSuite{}
}
func (s *NetworkACLSuite) TestShouldAccessSecretUpon2FA() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
wds, err := StartWebDriver()
s.Require().NoError(err)
defer wds.Stop()
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
secret := s.clients[0].doRegisterThenLogout(ctx, s.T(), "john", "password")
wds.doVisit(s.T(), targetURL)
wds.verifyIsFirstFactorPage(ctx, s.T())
s.clients[0].doVisit(s.T(), targetURL)
s.clients[0].verifyIsFirstFactorPage(ctx, s.T())
s.clients[0].doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
s.clients[0].verifyIsSecondFactorPage(ctx, s.T())
s.clients[0].doValidateTOTP(ctx, s.T(), secret)
s.clients[0].verifySecretAuthorized(ctx, s.T())
wds.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, targetURL)
wds.verifySecretAuthorized(ctx, s.T())
}
// from network 192.168.240.201/32
@ -79,13 +38,17 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon1FA() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
s.clients[1].doVisit(s.T(), targetURL)
s.clients[1].verifyIsFirstFactorPage(ctx, s.T())
wds, err := StartWebDriverWithProxy("http://proxy-client1.example.com:3128", 4444)
s.Require().NoError(err)
defer wds.Stop()
s.clients[1].doLoginOneFactor(ctx, s.T(), "john", "password",
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
wds.doVisit(s.T(), targetURL)
wds.verifyIsFirstFactorPage(ctx, s.T())
wds.doLoginOneFactor(ctx, s.T(), "john", "password",
false, fmt.Sprintf("%s/secret.html", SecureBaseURL))
s.clients[1].verifySecretAuthorized(ctx, s.T())
wds.verifySecretAuthorized(ctx, s.T())
}
// from network 192.168.240.202/32
@ -93,8 +56,12 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon0FA() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
s.clients[2].doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
s.clients[2].verifySecretAuthorized(ctx, s.T())
wds, err := StartWebDriverWithProxy("http://proxy-client2.example.com:3128", 4444)
s.Require().NoError(err)
defer wds.Stop()
wds.doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
wds.verifySecretAuthorized(ctx, s.T())
}
func TestNetworkACLSuite(t *testing.T) {

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -24,7 +25,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -34,7 +50,8 @@ func init() {
GlobalRegistry.Register(postgresSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 3 * time.Minute,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,
})

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -22,7 +23,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -32,7 +48,8 @@ func init() {
GlobalRegistry.Register(shortTimeoutsSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
TestTimeout: 1 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TestTimeout: 3 * time.Minute,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,
Description: `This suite has been created to configure Authelia with short timeouts for sessions expiration

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -24,7 +25,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -35,8 +51,10 @@ func init() {
GlobalRegistry.Register(standaloneSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TearDown: teardown,
TearDownTimeout: 5 * time.Minute,
TestTimeout: 2 * time.Minute,
TearDownTimeout: 2 * time.Minute,
Description: `This suite is used to test Authelia in a standalone
configuration with in-memory sessions and a local sqlite db stored on disk`,
})

View File

@ -1,6 +1,7 @@
package suites
import (
"fmt"
"time"
)
@ -24,7 +25,22 @@ func init() {
return err
}
return waitUntilAutheliaIsReady(dockerEnvironment)
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
}
fmt.Println(backendLogs)
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
if err != nil {
return err
}
fmt.Println(frontendLogs)
return nil
}
teardown := func(suitePath string) error {
@ -35,6 +51,7 @@ func init() {
GlobalRegistry.Register(traefikSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
TearDown: teardown,
TearDownTimeout: 2 * time.Minute,
})

View File

@ -11,8 +11,15 @@ import (
func (wds *WebDriverSession) verifyBodyContains(ctx context.Context, t *testing.T, pattern string) {
err := wds.Wait(ctx, func(wd selenium.WebDriver) (bool, error) {
bodyElement := wds.WaitElementLocatedByTagName(ctx, t, "body")
require.NotNil(t, bodyElement)
bodyElement, err := wds.WebDriver.FindElement(selenium.ByTagName, "body")
if err != nil {
return false, err
}
if bodyElement == nil {
return false, nil
}
content, err := bodyElement.Text()

View File

@ -6,5 +6,5 @@ import (
)
func (wds *WebDriverSession) verifySecretAuthorized(ctx context.Context, t *testing.T) {
wds.verifyBodyContains(ctx, t, "This is a very important secret!")
wds.WaitElementLocatedByID(ctx, t, "secret")
}

View File

@ -0,0 +1,6 @@
package utils
import "errors"
// ErrTimeoutReached error thrown when a timeout is reached
var ErrTimeoutReached = errors.New("timeout reached")

View File

@ -31,10 +31,8 @@ func Command(name string, args ...string) *exec.Cmd {
// CommandWithStdout create a command forwarding stdout and stderr to the OS streams
func CommandWithStdout(name string, args ...string) *exec.Cmd {
cmd := Command(name, args...)
if log.GetLevel() > log.InfoLevel {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
return cmd
}
@ -133,7 +131,7 @@ func RunCommandWithTimeout(cmd *exec.Cmd, timeout time.Duration) error {
if err := cmd.Process.Kill(); err != nil {
return err
}
return fmt.Errorf("timeout of %ds reached", int64(timeout/time.Second))
return ErrTimeoutReached
case err := <-done:
return err
}

View File

@ -1,8 +1,11 @@
import React, { useEffect, Fragment, ReactNode, useState } from "react";
import React, { useEffect, Fragment, ReactNode, useState, useCallback } from "react";
import { Switch, Route, Redirect, useHistory, useLocation } from "react-router";
import FirstFactorForm from "./FirstFactor/FirstFactorForm";
import SecondFactorForm from "./SecondFactor/SecondFactorForm";
import { FirstFactorRoute, SecondFactorRoute, SecondFactorTOTPRoute, SecondFactorPushRoute, SecondFactorU2FRoute, LogoutRoute } from "../../Routes";
import {
FirstFactorRoute, SecondFactorRoute, SecondFactorTOTPRoute,
SecondFactorPushRoute, SecondFactorU2FRoute
} from "../../Routes";
import { useAutheliaState } from "../../hooks/State";
import LoadingPage from "../LoadingPage/LoadingPage";
import { AuthenticationLevel } from "../../services/State";
@ -23,6 +26,8 @@ export default function () {
const [preferences, fetchPreferences, , fetchPreferencesError] = useUserPreferences();
const [configuration, fetchConfiguration, , fetchConfigurationError] = useAutheliaConfiguration();
const redirect = useCallback((url: string) => history.push(url), [history]);
// Fetch the state when portal is mounted.
useEffect(() => { fetchState() }, [fetchState]);
@ -71,19 +76,19 @@ export default function () {
if (state.authentication_level === AuthenticationLevel.Unauthenticated) {
setFirstFactorDisabled(false);
history.push(`${FirstFactorRoute}${redirectionSuffix}`);
redirect(`${FirstFactorRoute}${redirectionSuffix}`);
} else if (state.authentication_level >= AuthenticationLevel.OneFactor && preferences) {
console.log("redirect");
if (preferences.method === SecondFactorMethod.U2F) {
history.push(`${SecondFactorU2FRoute}${redirectionSuffix}`);
redirect(`${SecondFactorU2FRoute}${redirectionSuffix}`);
} else if (preferences.method === SecondFactorMethod.Duo) {
history.push(`${SecondFactorPushRoute}${redirectionSuffix}`);
redirect(`${SecondFactorPushRoute}${redirectionSuffix}`);
} else {
history.push(`${SecondFactorTOTPRoute}${redirectionSuffix}`);
redirect(`${SecondFactorTOTPRoute}${redirectionSuffix}`);
}
}
}
}, [state, redirectionURL, history.push, preferences, setFirstFactorDisabled]);
}, [state, redirectionURL, redirect, preferences, setFirstFactorDisabled]);
const handleFirstFactorSuccess = async (redirectionURL: string | undefined) => {
if (redirectionURL) {

View File

@ -1,4 +1,4 @@
import React, { ReactNode, Fragment } from "react";
import React, { ReactNode } from "react";
import { makeStyles, Typography, Link } from "@material-ui/core";
interface MethodContainerProps {

View File

@ -31,7 +31,7 @@ export default function (props: Props) {
console.error(err);
createErrorNotification("There was an issue signing out");
}
}, [createErrorNotification, setTimedOut]);
}, [createErrorNotification, setTimedOut, mounted]);
useEffect(() => { doSignOut() }, [doSignOut]);