Fix and parallelize integration tests.
parent
be802cfc7b
commit
b89f63e9c1
78
.travis.yml
78
.travis.yml
|
@ -3,7 +3,7 @@ language: go
|
||||||
required: sudo
|
required: sudo
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- '1.13'
|
- "1.13"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- docker
|
- docker
|
||||||
|
@ -19,25 +19,73 @@ addons:
|
||||||
- libgif-dev
|
- libgif-dev
|
||||||
- google-chrome-stable
|
- 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 ~/
|
- wget -N https://chromedriver.storage.googleapis.com/78.0.3904.70/chromedriver_linux64.zip -P ~/
|
||||||
- unzip ~/chromedriver_linux64.zip -d ~/
|
- unzip ~/chromedriver_linux64.zip -d ~/
|
||||||
- rm ~/chromedriver_linux64.zip
|
- rm ~/chromedriver_linux64.zip
|
||||||
- sudo mv -f ~/chromedriver /usr/local/share/
|
- sudo mv -f ~/chromedriver /usr/local/share/
|
||||||
- sudo chmod +x /usr/local/share/chromedriver
|
- sudo chmod +x /usr/local/share/chromedriver
|
||||||
- sudo ln -s /usr/local/share/chromedriver /usr/bin/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:
|
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
|
- &build-images
|
||||||
stage: build images
|
stage: build images
|
||||||
env:
|
env:
|
||||||
|
@ -56,9 +104,9 @@ jobs:
|
||||||
- tar -czf authelia-linux-$ARCH.tar.gz authelia-linux-$ARCH public_html
|
- tar -czf authelia-linux-$ARCH.tar.gz authelia-linux-$ARCH public_html
|
||||||
deploy:
|
deploy:
|
||||||
provider: releases
|
provider: releases
|
||||||
api_key: '$GITHUB_API_KEY'
|
api_key: "$GITHUB_API_KEY"
|
||||||
file_glob: true
|
file_glob: true
|
||||||
file: 'authelia-linux-$ARCH.tar.gz'
|
file: "authelia-linux-$ARCH.tar.gz"
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
on:
|
on:
|
||||||
tags: true
|
tags: true
|
||||||
|
|
15
Dockerfile
15
Dockerfile
|
@ -7,7 +7,14 @@ FROM golang:1.13-alpine AS builder-backend
|
||||||
RUN apk --no-cache add gcc musl-dev
|
RUN apk --no-cache add gcc musl-dev
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
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
|
# 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
|
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 =====
|
# ===== Build image for the frontend =====
|
||||||
# ========================================
|
# ========================================
|
||||||
FROM node:11-alpine AS builder-frontend
|
FROM node:12-alpine AS builder-frontend
|
||||||
|
|
||||||
WORKDIR /node/src/app
|
WORKDIR /node/src/app
|
||||||
COPY client .
|
COPY web .
|
||||||
|
|
||||||
# Install the dependencies and build
|
# Install the dependencies and build
|
||||||
RUN npm ci && npm run build
|
RUN npm ci && npm run build
|
||||||
|
@ -41,4 +48,4 @@ EXPOSE 9091
|
||||||
VOLUME /etc/authelia
|
VOLUME /etc/authelia
|
||||||
VOLUME /var/lib/authelia
|
VOLUME /var/lib/authelia
|
||||||
|
|
||||||
CMD ["./authelia", "-config", "/etc/authelia/config.yml"]
|
CMD ["./authelia", "-config", "/etc/authelia/configuration.yml"]
|
||||||
|
|
|
@ -8,7 +8,14 @@ COPY ./qemu-arm-static /usr/bin/qemu-arm-static
|
||||||
RUN apk --no-cache add gcc musl-dev
|
RUN apk --no-cache add gcc musl-dev
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
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
|
# 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
|
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 =====
|
# ===== Build image for the frontend =====
|
||||||
# ========================================
|
# ========================================
|
||||||
FROM node:11-alpine AS builder-frontend
|
FROM node:12-alpine AS builder-frontend
|
||||||
|
|
||||||
WORKDIR /node/src/app
|
WORKDIR /node/src/app
|
||||||
COPY client .
|
COPY web .
|
||||||
|
|
||||||
# Install the dependencies and build
|
# Install the dependencies and build
|
||||||
RUN npm ci && npm run build
|
RUN npm ci && npm run build
|
||||||
|
@ -45,4 +52,4 @@ EXPOSE 9091
|
||||||
VOLUME /etc/authelia
|
VOLUME /etc/authelia
|
||||||
VOLUME /var/lib/authelia
|
VOLUME /var/lib/authelia
|
||||||
|
|
||||||
CMD ["./authelia", "-config", "/etc/authelia/config.yml"]
|
CMD ["./authelia", "-config", "/etc/authelia/configuration.yml"]
|
||||||
|
|
|
@ -8,7 +8,14 @@ COPY ./qemu-aarch64-static /usr/bin/qemu-aarch64-static
|
||||||
RUN apk --no-cache add gcc musl-dev
|
RUN apk --no-cache add gcc musl-dev
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
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
|
# 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
|
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 =====
|
# ===== Build image for the frontend =====
|
||||||
# ========================================
|
# ========================================
|
||||||
FROM node:11-alpine AS builder-frontend
|
FROM node:12-alpine AS builder-frontend
|
||||||
|
|
||||||
WORKDIR /node/src/app
|
WORKDIR /node/src/app
|
||||||
COPY client .
|
COPY web .
|
||||||
|
|
||||||
# Install the dependencies and build
|
# Install the dependencies and build
|
||||||
RUN npm ci && npm run build
|
RUN npm ci && npm run build
|
||||||
|
@ -45,4 +52,4 @@ EXPOSE 9091
|
||||||
VOLUME /etc/authelia
|
VOLUME /etc/authelia
|
||||||
VOLUME /var/lib/authelia
|
VOLUME /var/lib/authelia
|
||||||
|
|
||||||
CMD ["./authelia", "-config", "/etc/authelia/config.yml"]
|
CMD ["./authelia", "-config", "/etc/authelia/configuration.yml"]
|
||||||
|
|
|
@ -7,6 +7,9 @@ if [ -z "$OLD_PS1" ]; then
|
||||||
export PS1="(authelia) $PS1"
|
export PS1="(authelia) $PS1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
export USER_ID=$(id -u)
|
||||||
|
export GROUP_ID=$(id -g)
|
||||||
|
|
||||||
|
|
||||||
echo "[BOOTSTRAP] Checking if Go is installed..."
|
echo "[BOOTSTRAP] Checking if Go is installed..."
|
||||||
if [ ! -x "$(command -v go)" ];
|
if [ ! -x "$(command -v go)" ];
|
||||||
|
|
|
@ -96,15 +96,6 @@ func shell(cmd string) {
|
||||||
runCommand("bash", "-c", cmd)
|
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() {
|
func prepareHostsFile() {
|
||||||
contentBytes, err := readHostsFile()
|
contentBytes, err := readHostsFile()
|
||||||
|
|
||||||
|
@ -209,9 +200,6 @@ func Bootstrap(cobraCmd *cobra.Command, args []string) {
|
||||||
log.Fatal("GOPATH is not set")
|
log.Fatal("GOPATH is not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrapPrintln("Building development Docker images...")
|
|
||||||
buildHelperDockerImages()
|
|
||||||
|
|
||||||
createTemporaryDirectory()
|
createTemporaryDirectory()
|
||||||
|
|
||||||
bootstrapPrintln("Preparing /etc/hosts to serve subdomains of example.com...")
|
bootstrapPrintln("Preparing /etc/hosts to serve subdomains of example.com...")
|
||||||
|
|
|
@ -24,7 +24,7 @@ func buildAutheliaBinary() {
|
||||||
func buildFrontend() {
|
func buildFrontend() {
|
||||||
// Install npm dependencies
|
// Install npm dependencies
|
||||||
cmd := utils.CommandWithStdout("npm", "ci")
|
cmd := utils.CommandWithStdout("npm", "ci")
|
||||||
cmd.Dir = "client"
|
cmd.Dir = "web"
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -32,13 +32,13 @@ func buildFrontend() {
|
||||||
|
|
||||||
// Then build the frontend
|
// Then build the frontend
|
||||||
cmd = utils.CommandWithStdout("npm", "run", "build")
|
cmd = utils.CommandWithStdout("npm", "run", "build")
|
||||||
cmd.Dir = "client"
|
cmd.Dir = "web"
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
log.Fatal(err)
|
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)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,18 @@ const dockerPullCommandLine = "docker-compose -f docker-compose.yml " +
|
||||||
|
|
||||||
// RunCI run the CI scripts
|
// RunCI run the CI scripts
|
||||||
func RunCI(cmd *cobra.Command, args []string) {
|
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 {
|
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "build").Run(); err != nil {
|
||||||
log.Fatal(err)
|
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 {
|
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "unittest").Run(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("=====> End-to-end testing stage")
|
log.Info("=====> Build Docker stage <=====")
|
||||||
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "suites", "test", "--headless", "--only-forbidden").Run(); err != nil {
|
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "docker", "build").Run(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,11 +27,9 @@ var ErrNoRunningSuite = errors.New("no running suite")
|
||||||
var runningSuiteFile = ".suite"
|
var runningSuiteFile = ".suite"
|
||||||
|
|
||||||
var headless bool
|
var headless bool
|
||||||
var onlyForbidden bool
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
SuitesTestCmd.Flags().BoolVar(&headless, "headless", false, "Run tests in headless mode")
|
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
|
// SuitesListCmd Command for listing the available suites
|
||||||
|
@ -79,17 +77,17 @@ var SuitesTeardownCmd = &cobra.Command{
|
||||||
runningSuite, err := getRunningSuite()
|
runningSuite, err := getRunningSuite()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if runningSuite == "" {
|
if runningSuite == "" {
|
||||||
panic(ErrNoRunningSuite)
|
log.Fatal(ErrNoRunningSuite)
|
||||||
}
|
}
|
||||||
suiteName = runningSuite
|
suiteName = runningSuite
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := teardownSuite(suiteName); err != nil {
|
if err := teardownSuite(suiteName); err != nil {
|
||||||
panic(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
|
@ -143,6 +141,14 @@ func runSuiteSetupTeardown(command string, suite string) error {
|
||||||
return utils.RunCommandWithTimeout(cmd, s.SetUpTimeout)
|
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 {
|
func setupSuite(suiteName string) error {
|
||||||
log.Infof("Setup environment for suite %s...", suiteName)
|
log.Infof("Setup environment for suite %s...", suiteName)
|
||||||
signalChannel := make(chan os.Signal)
|
signalChannel := make(chan os.Signal)
|
||||||
|
@ -156,10 +162,10 @@ func setupSuite(suiteName string) error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if errSetup := runSuiteSetupTeardown("setup", suiteName); errSetup != nil || interrupted {
|
if errSetup := runSuiteSetupTeardown("setup", suiteName); errSetup != nil || interrupted {
|
||||||
err := teardownSuite(suiteName)
|
if errSetup == utils.ErrTimeoutReached {
|
||||||
if err != nil {
|
runOnSetupTimeout(suiteName)
|
||||||
log.Error(err)
|
|
||||||
}
|
}
|
||||||
|
teardownSuite(suiteName)
|
||||||
return errSetup
|
return errSetup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +237,7 @@ func runSuiteTests(suiteName string, withEnv bool) error {
|
||||||
if suite.TestTimeout > 0 {
|
if suite.TestTimeout > 0 {
|
||||||
timeout = fmt.Sprintf("%ds", int64(suite.TestTimeout/time.Second))
|
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.Infof("Running tests of suite %s...", suiteName)
|
||||||
log.Debugf("Running tests with command: %s", testCmdLine)
|
log.Debugf("Running tests with command: %s", testCmdLine)
|
||||||
|
|
|
@ -32,6 +32,12 @@ func main() {
|
||||||
Run: setupSuite,
|
Run: setupSuite,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupTimeoutCmd := &cobra.Command{
|
||||||
|
Use: "timeout [suite]",
|
||||||
|
Short: "Run the OnSetupTimeout callback when setup times out",
|
||||||
|
Run: setupTimeoutSuite,
|
||||||
|
}
|
||||||
|
|
||||||
stopCmd := &cobra.Command{
|
stopCmd := &cobra.Command{
|
||||||
Use: "teardown [suite]",
|
Use: "teardown [suite]",
|
||||||
Short: "Teardown the suite environment",
|
Short: "Teardown the suite environment",
|
||||||
|
@ -39,6 +45,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
rootCmd.AddCommand(startCmd)
|
rootCmd.AddCommand(startCmd)
|
||||||
|
rootCmd.AddCommand(setupTimeoutCmd)
|
||||||
rootCmd.AddCommand(stopCmd)
|
rootCmd.AddCommand(stopCmd)
|
||||||
rootCmd.Execute()
|
rootCmd.Execute()
|
||||||
}
|
}
|
||||||
|
@ -101,6 +108,18 @@ func setupSuite(cmd *cobra.Command, args []string) {
|
||||||
log.Info("Environment is ready!")
|
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) {
|
func teardownSuite(cmd *cobra.Command, args []string) {
|
||||||
if os.Getenv("SKIP_TEARDOWN") != "" {
|
if os.Getenv("SKIP_TEARDOWN") != "" {
|
||||||
return
|
return
|
||||||
|
|
|
@ -99,7 +99,6 @@ authentication_backend:
|
||||||
## file:
|
## file:
|
||||||
## path: ./users_database.yml
|
## path: ./users_database.yml
|
||||||
|
|
||||||
|
|
||||||
# Access Control
|
# Access Control
|
||||||
#
|
#
|
||||||
# Access control is a list of rules defining the authorizations applied for one
|
# Access control is a list of rules defining the authorizations applied for one
|
||||||
|
@ -156,47 +155,45 @@ access_control:
|
||||||
- domain: singlefactor.example.com
|
- domain: singlefactor.example.com
|
||||||
policy: one_factor
|
policy: one_factor
|
||||||
|
|
||||||
# Rules applied to 'admin' group
|
# Rules applied to 'admins' group
|
||||||
- domain: 'mx2.mail.example.com'
|
- domain: "mx2.mail.example.com"
|
||||||
subject: 'group:admin'
|
subject: "groups:admins"
|
||||||
policy: deny
|
policy: deny
|
||||||
- domain: '*.example.com'
|
- domain: "*.example.com"
|
||||||
subject: 'group:admin'
|
subject: "groups:admins"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to 'dev' group
|
# Rules applied to 'dev' group
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/groups/dev/.*$'
|
- "^/groups/dev/.*$"
|
||||||
subject: 'group:dev'
|
subject: "group:dev"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to user 'john'
|
# Rules applied to user 'john'
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/users/john/.*$'
|
- "^/users/john/.*$"
|
||||||
subject: 'user:john'
|
subject: "user:john"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
|
|
||||||
# Rules applied to user 'harry'
|
# Rules applied to user 'harry'
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/users/harry/.*$'
|
- "^/users/harry/.*$"
|
||||||
subject: 'user:harry'
|
subject: "user:harry"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to user 'bob'
|
# Rules applied to user 'bob'
|
||||||
- domain: '*.mail.example.com'
|
- domain: "*.mail.example.com"
|
||||||
subject: 'user:bob'
|
subject: "user:bob"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
- domain: 'dev.example.com'
|
- domain: "dev.example.com"
|
||||||
resources:
|
resources:
|
||||||
- '^/users/bob/.*$'
|
- "^/users/bob/.*$"
|
||||||
subject: 'user:bob'
|
subject: "user:bob"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
|
|
||||||
# Configuration of session cookies
|
# Configuration of session cookies
|
||||||
#
|
#
|
||||||
# The session cookies identify the user once logged in.
|
# The session cookies identify the user once logged in.
|
||||||
|
@ -283,7 +280,6 @@ notifier:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 1025
|
port: 1025
|
||||||
sender: admin@example.com
|
sender: admin@example.com
|
||||||
|
|
||||||
# Sending an email using a Gmail account is as simple as the next section.
|
# 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
|
# You need to create an app password by following: https://support.google.com/accounts/answer/185833?hl=en
|
||||||
## smtp:
|
## smtp:
|
||||||
|
|
|
@ -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 USER_ID
|
||||||
ARG GROUP_ID
|
ARG GROUP_ID
|
||||||
|
|
||||||
RUN groupadd -g ${GROUP_ID} dev && \
|
RUN addgroup --gid ${GROUP_ID} dev && \
|
||||||
useradd -m -u $USER_ID -g $GROUP_ID dev
|
adduser --uid ${USER_ID} -G dev -D dev
|
||||||
USER dev
|
USER dev
|
|
@ -1,9 +1,9 @@
|
||||||
FROM node:11-stretch-slim
|
FROM node:12-alpine
|
||||||
|
|
||||||
ARG USER_ID
|
ARG USER_ID
|
||||||
ARG GROUP_ID
|
ARG GROUP_ID
|
||||||
|
|
||||||
RUN cat /etc/passwd && userdel -rf node && \
|
RUN deluser node && \
|
||||||
groupadd -g ${GROUP_ID} dev && \
|
addgroup --gid ${GROUP_ID} dev && \
|
||||||
useradd -m -u $USER_ID -g $GROUP_ID dev
|
adduser --uid ${USER_ID} -G dev -D dev
|
||||||
USER dev
|
USER dev
|
|
@ -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
|
|
@ -4,6 +4,9 @@ services:
|
||||||
build:
|
build:
|
||||||
context: example/compose/authelia
|
context: example/compose/authelia
|
||||||
dockerfile: Dockerfile.backend
|
dockerfile: Dockerfile.backend
|
||||||
|
args:
|
||||||
|
USER_ID: ${USER_ID}
|
||||||
|
GROUP_ID: ${GROUP_ID}
|
||||||
command: /resources/entrypoint.sh
|
command: /resources/entrypoint.sh
|
||||||
working_dir: /app
|
working_dir: /app
|
||||||
volumes:
|
volumes:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
version: '3'
|
version: "3"
|
||||||
services:
|
services:
|
||||||
authelia-frontend:
|
authelia-frontend:
|
||||||
image: nginx:alpine
|
image: nginx:alpine
|
|
@ -1,10 +1,13 @@
|
||||||
version: '3'
|
version: "3"
|
||||||
services:
|
services:
|
||||||
authelia-frontend:
|
authelia-frontend:
|
||||||
build:
|
build:
|
||||||
context: example/compose/authelia
|
context: example/compose/authelia
|
||||||
dockerfile: Dockerfile.frontend
|
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
|
working_dir: /app
|
||||||
volumes:
|
volumes:
|
||||||
- "./web:/app"
|
- "./web:/app"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ events {
|
||||||
|
|
||||||
http {
|
http {
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 3000;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
proxy_pass http://authelia-backend;
|
proxy_pass http://authelia-backend:9091;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,30 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
|
|
||||||
set -e
|
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
|
# Build the binary
|
||||||
go build -o /tmp/authelia/authelia-tmp cmd/authelia/main.go
|
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
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:8.7.0-alpine
|
FROM node:12-alpine
|
||||||
|
|
||||||
WORKDIR /usr/app/src
|
WORKDIR /usr/app/src
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
version: '3'
|
version: "3"
|
||||||
services:
|
services:
|
||||||
duo-api:
|
duo-api:
|
||||||
image: authelia-duo-api
|
build:
|
||||||
|
context: ./example/compose/duo-api
|
||||||
networks:
|
networks:
|
||||||
- authelianet
|
- authelianet
|
||||||
|
|
|
@ -16,15 +16,18 @@ let permission = 'allow';
|
||||||
|
|
||||||
app.post('/allow', (req, res) => {
|
app.post('/allow', (req, res) => {
|
||||||
permission = 'allow';
|
permission = 'allow';
|
||||||
|
console.log("set allowed!");
|
||||||
res.send('ALLOWED');
|
res.send('ALLOWED');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/deny', (req, res) => {
|
app.post('/deny', (req, res) => {
|
||||||
permission = 'deny';
|
permission = 'deny';
|
||||||
|
console.log("set denied!");
|
||||||
res.send('DENIED');
|
res.send('DENIED');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/auth/v2/auth', (req, res) => {
|
app.post('/auth/v2/auth', (req, res) => {
|
||||||
|
setTimeout(() => {
|
||||||
let response;
|
let response;
|
||||||
if (permission == 'allow') {
|
if (permission == 'allow') {
|
||||||
response = {
|
response = {
|
||||||
|
@ -45,7 +48,8 @@ app.post('/auth/v2/auth', (req, res) => {
|
||||||
stat: 'OK',
|
stat: 'OK',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
setTimeout(() => res.json(response), 2000);
|
res.json(response);
|
||||||
|
}, 2000);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.listen(port, () => console.log(`Duo API listening on port ${port}!`));
|
app.listen(port, () => console.log(`Duo API listening on port ${port}!`));
|
||||||
|
|
|
@ -15,8 +15,8 @@ member: cn=bob,ou=users,dc=example,dc=com
|
||||||
objectclass: groupOfNames
|
objectclass: groupOfNames
|
||||||
objectclass: top
|
objectclass: top
|
||||||
|
|
||||||
dn: cn=admin,ou=groups,dc=example,dc=com
|
dn: cn=admins,ou=groups,dc=example,dc=com
|
||||||
cn: admin
|
cn: admins
|
||||||
member: cn=john,ou=users,dc=example,dc=com
|
member: cn=john,ou=users,dc=example,dc=com
|
||||||
objectclass: groupOfNames
|
objectclass: groupOfNames
|
||||||
objectclass: top
|
objectclass: top
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM nginx:alpine
|
FROM nginx:alpine
|
||||||
|
|
||||||
ADD ./html /usr/share/nginx/html
|
ADD html /usr/share/nginx/html
|
||||||
ADD ./nginx.conf /etc/nginx/nginx.conf
|
ADD nginx.conf /etc/nginx/nginx.conf
|
|
@ -1,10 +1,11 @@
|
||||||
version: '3'
|
version: "3"
|
||||||
services:
|
services:
|
||||||
nginx-backend:
|
nginx-backend:
|
||||||
image: authelia-example-backend
|
build:
|
||||||
|
context: example/compose/nginx/backend
|
||||||
labels:
|
labels:
|
||||||
- traefik.frontend.rule=Host:home.example.com,public.example.com,secure.example.com,admin.example.com,singlefactor.example.com
|
- 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
|
- traefik.frontend.auth.forward.tls.insecureSkipVerify=true
|
||||||
networks:
|
networks:
|
||||||
- authelianet
|
- authelianet
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -8,7 +8,8 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Access the secret</h1>
|
<h1>Access the secret</h1>
|
||||||
<span style="font-size: 1.2em; color: red">You need to log in to access the secret!</span><br/><br/> 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 />
|
one of the following links to test access control powered by Authelia.<br />
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
@ -59,12 +60,15 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</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>
|
<h1>List of users</h1>
|
||||||
Here is the list of credentials you can log in with to test access control.<br />
|
Here is the list of credentials you can log in with to test access control.<br />
|
||||||
<br/> Once first factor is passed, you will need to follow the links to register a secret for the second factor.<br/> Authelia
|
<br /> Once first factor is passed, you will need to follow the links to register a secret for the second
|
||||||
will send you a fictituous email in a <strong>fake webmail</strong> at <a href="http://localhost:8085">http://localhost:8085</a>.<br/>
|
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.
|
It will provide you with the link to complete the registration allowing you to authenticate with 2-factor.
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -86,12 +90,12 @@
|
||||||
- domain: singlefactor.example.com
|
- domain: singlefactor.example.com
|
||||||
policy: one_factor
|
policy: one_factor
|
||||||
|
|
||||||
# Rules applied to 'admin' group
|
# Rules applied to 'admins' group
|
||||||
- domain: 'mx2.mail.example.com'
|
- domain: 'mx2.mail.example.com'
|
||||||
subject: 'group:admin'
|
subject: 'groups:admins'
|
||||||
policy: deny
|
policy: deny
|
||||||
- domain: '*.example.com'
|
- domain: '*.example.com'
|
||||||
subject: 'group:admin'
|
subject: 'groups:admins'
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to 'dev' group
|
# Rules applied to 'dev' group
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,10 +1,13 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Secret</title>
|
<title>Secret</title>
|
||||||
<link rel="icon" href="/icon.png" type="image/png" />
|
<link rel="icon" href="/icon.png" type="image/png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
|
<body id="secret">
|
||||||
This is a very important secret!<br />
|
This is a very important secret!<br />
|
||||||
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
Go back to <a href="https://home.example.com:8080/">home page</a>.
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -19,11 +19,10 @@ spec:
|
||||||
containers:
|
containers:
|
||||||
- name: test-app
|
- name: test-app
|
||||||
imagePullPolicy: Never
|
imagePullPolicy: Never
|
||||||
image: authelia-example-backend
|
image: nginx-backend
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
|
@ -130,4 +129,3 @@ spec:
|
||||||
backend:
|
backend:
|
||||||
serviceName: test-app-service
|
serviceName: test-app-service
|
||||||
servicePort: 80
|
servicePort: 80
|
||||||
|
|
||||||
|
|
|
@ -33,47 +33,45 @@ access_control:
|
||||||
- domain: singlefactor.example.com
|
- domain: singlefactor.example.com
|
||||||
policy: one_factor
|
policy: one_factor
|
||||||
|
|
||||||
# Rules applied to 'admin' group
|
# Rules applied to 'admins' group
|
||||||
- domain: 'mx2.mail.example.com'
|
- domain: "mx2.mail.example.com"
|
||||||
subject: 'group:admin'
|
subject: "group:admins"
|
||||||
policy: deny
|
policy: deny
|
||||||
- domain: '*.example.com'
|
- domain: "*.example.com"
|
||||||
subject: 'group:admin'
|
subject: "group:admins"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to 'dev' group
|
# Rules applied to 'dev' group
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/groups/dev/.*$'
|
- "^/groups/dev/.*$"
|
||||||
subject: 'group:dev'
|
subject: "group:dev"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to user 'john'
|
# Rules applied to user 'john'
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/users/john/.*$'
|
- "^/users/john/.*$"
|
||||||
subject: 'user:john'
|
subject: "user:john"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
|
|
||||||
# Rules applied to user 'harry'
|
# Rules applied to user 'harry'
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/users/harry/.*$'
|
- "^/users/harry/.*$"
|
||||||
subject: 'user:harry'
|
subject: "user:harry"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to user 'bob'
|
# Rules applied to user 'bob'
|
||||||
- domain: '*.mail.example.com'
|
- domain: "*.mail.example.com"
|
||||||
subject: 'user:bob'
|
subject: "user:bob"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
- domain: 'dev.example.com'
|
- domain: "dev.example.com"
|
||||||
resources:
|
resources:
|
||||||
- '^/users/bob/.*$'
|
- "^/users/bob/.*$"
|
||||||
subject: 'user:bob'
|
subject: "user:bob"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
|
|
||||||
session:
|
session:
|
||||||
secret: unsecure_password
|
secret: unsecure_password
|
||||||
expiration: 3600000 # 1 hour
|
expiration: 3600000 # 1 hour
|
||||||
|
@ -98,6 +96,6 @@ storage:
|
||||||
|
|
||||||
notifier:
|
notifier:
|
||||||
smtp:
|
smtp:
|
||||||
host: 'mailcatcher-service'
|
host: "mailcatcher-service"
|
||||||
port: 1025
|
port: 1025
|
||||||
sender: admin@example.com
|
sender: admin@example.com
|
|
@ -29,5 +29,5 @@ spec:
|
||||||
configMap:
|
configMap:
|
||||||
name: authelia-config
|
name: authelia-config
|
||||||
items:
|
items:
|
||||||
- key: config.yml
|
- key: configuration.yml
|
||||||
path: config.yml
|
path: configuration.yml
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
start_authelia() {
|
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
|
kubectl apply -f authelia
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@ member: cn=bob,ou=users,dc=example,dc=com
|
||||||
objectclass: groupOfNames
|
objectclass: groupOfNames
|
||||||
objectclass: top
|
objectclass: top
|
||||||
|
|
||||||
dn: cn=admin,ou=groups,dc=example,dc=com
|
dn: cn=admins,ou=groups,dc=example,dc=com
|
||||||
cn: admin
|
cn: admins
|
||||||
member: cn=john,ou=users,dc=example,dc=com
|
member: cn=john,ou=users,dc=example,dc=com
|
||||||
objectclass: groupOfNames
|
objectclass: groupOfNames
|
||||||
objectclass: top
|
objectclass: top
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
version: '3.4'
|
version: "3.4"
|
||||||
services:
|
services:
|
||||||
authelia:
|
authelia:
|
||||||
image: clems4ever/authelia:latest
|
image: clems4ever/authelia:latest
|
||||||
# Used for Docker configs
|
# Used for Docker configs
|
||||||
configs:
|
configs:
|
||||||
- source: authelia
|
- source: authelia
|
||||||
target: /etc/authelia/config.yml
|
target: /etc/authelia/configuration.yml
|
||||||
uid: '0'
|
uid: "0"
|
||||||
gid: '0'
|
gid: "0"
|
||||||
mode: 0444
|
mode: 0444
|
||||||
environment:
|
environment:
|
||||||
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
- NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -7,6 +7,7 @@ require (
|
||||||
github.com/Workiva/go-datastructures v1.0.50
|
github.com/Workiva/go-datastructures v1.0.50
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
|
||||||
github.com/cespare/reflex v0.2.0 // indirect
|
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/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74
|
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74
|
||||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
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=
|
github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M=
|
||||||
|
|
|
@ -128,7 +128,7 @@ users
|
||||||
john
|
john
|
||||||
email: john.doe@authelia.com
|
email: john.doe@authelia.com
|
||||||
groups:
|
groups:
|
||||||
- admin
|
- admins
|
||||||
- dev
|
- dev
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
|
@ -48,44 +48,43 @@ access_control:
|
||||||
- domain: singlefactor.example.com
|
- domain: singlefactor.example.com
|
||||||
policy: one_factor
|
policy: one_factor
|
||||||
|
|
||||||
# Rules applied to 'admin' group
|
# Rules applied to 'admins' group
|
||||||
- domain: 'mx2.mail.example.com'
|
- domain: "mx2.mail.example.com"
|
||||||
subject: 'group:admin'
|
subject: "groups:admins"
|
||||||
policy: deny
|
policy: deny
|
||||||
- domain: '*.example.com'
|
- domain: "*.example.com"
|
||||||
subject: 'group:admin'
|
subject: "groups:admins"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to 'dev' group
|
# Rules applied to 'dev' group
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/groups/dev/.*$'
|
- "^/groups/dev/.*$"
|
||||||
subject: 'group:dev'
|
subject: "group:dev"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to user 'john'
|
# Rules applied to user 'john'
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/users/john/.*$'
|
- "^/users/john/.*$"
|
||||||
subject: 'user:john'
|
subject: "user:john"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
|
|
||||||
# Rules applied to user 'harry'
|
# Rules applied to user 'harry'
|
||||||
- domain: dev.example.com
|
- domain: dev.example.com
|
||||||
resources:
|
resources:
|
||||||
- '^/users/harry/.*$'
|
- "^/users/harry/.*$"
|
||||||
subject: 'user:harry'
|
subject: "user:harry"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to user 'bob'
|
# Rules applied to user 'bob'
|
||||||
- domain: '*.mail.example.com'
|
- domain: "*.mail.example.com"
|
||||||
subject: 'user:bob'
|
subject: "user:bob"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
- domain: 'dev.example.com'
|
- domain: "dev.example.com"
|
||||||
resources:
|
resources:
|
||||||
- '^/users/bob/.*$'
|
- "^/users/bob/.*$"
|
||||||
subject: 'user:bob'
|
subject: "user:bob"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
session:
|
session:
|
||||||
|
|
|
@ -24,10 +24,11 @@ func FirstFactorPost(ctx *middlewares.AutheliaCtx) {
|
||||||
|
|
||||||
bannedUntil, err := ctx.Providers.Regulator.Regulate(bodyJSON.Username)
|
bannedUntil, err := ctx.Providers.Regulator.Regulate(bodyJSON.Username)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
if err == regulation.ErrUserIsBanned {
|
if err == regulation.ErrUserIsBanned {
|
||||||
ctx.Error(fmt.Errorf("User %s is banned until %s", bodyJSON.Username, bannedUntil), userBannedMessage)
|
ctx.Error(fmt.Errorf("User %s is banned until %s", bodyJSON.Username, bannedUntil), userBannedMessage)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
}
|
||||||
ctx.Error(fmt.Errorf("Unable to regulate authentication: %s", err), authenticationFailedMessage)
|
ctx.Error(fmt.Errorf("Unable to regulate authentication: %s", err), authenticationFailedMessage)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -43,6 +44,9 @@ func FirstFactorPost(ctx *middlewares.AutheliaCtx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !userPasswordOk {
|
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)
|
ctx.ReplyError(fmt.Errorf("Credentials are wrong for user %s", bodyJSON.Username), authenticationFailedMessage)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ func (s *FirstFactorSuite) TestShouldAuthenticateUser() {
|
||||||
GetDetails(gomock.Eq("test")).
|
GetDetails(gomock.Eq("test")).
|
||||||
Return(&authentication.UserDetails{
|
Return(&authentication.UserDetails{
|
||||||
Emails: []string{"test@example.com"},
|
Emails: []string{"test@example.com"},
|
||||||
Groups: []string{"dev", "admin"},
|
Groups: []string{"dev", "admins"},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
s.mock.StorageProviderMock.
|
s.mock.StorageProviderMock.
|
||||||
|
@ -186,7 +186,7 @@ func (s *FirstFactorSuite) TestShouldAuthenticateUser() {
|
||||||
assert.Equal(s.T(), "test", session.Username)
|
assert.Equal(s.T(), "test", session.Username)
|
||||||
assert.Equal(s.T(), authentication.OneFactor, session.AuthenticationLevel)
|
assert.Equal(s.T(), authentication.OneFactor, session.AuthenticationLevel)
|
||||||
assert.Equal(s.T(), []string{"test@example.com"}, session.Emails)
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,7 +208,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyDefaultPolicy() {
|
||||||
GetDetails(gomock.Eq("john")).
|
GetDetails(gomock.Eq("john")).
|
||||||
Return(&authentication.UserDetails{
|
Return(&authentication.UserDetails{
|
||||||
Emails: []string{"john@example.com"},
|
Emails: []string{"john@example.com"},
|
||||||
Groups: []string{"dev", "admin"},
|
Groups: []string{"dev", "admins"},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
@ -231,7 +231,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfBypassDomain() {
|
||||||
GetDetails(gomock.Eq("john")).
|
GetDetails(gomock.Eq("john")).
|
||||||
Return(&authentication.UserDetails{
|
Return(&authentication.UserDetails{
|
||||||
Emails: []string{"john@example.com"},
|
Emails: []string{"john@example.com"},
|
||||||
Groups: []string{"dev", "admin"},
|
Groups: []string{"dev", "admins"},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
@ -254,7 +254,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfOneFactorDomain() {
|
||||||
GetDetails(gomock.Eq("john")).
|
GetDetails(gomock.Eq("john")).
|
||||||
Return(&authentication.UserDetails{
|
Return(&authentication.UserDetails{
|
||||||
Emails: []string{"john@example.com"},
|
Emails: []string{"john@example.com"},
|
||||||
Groups: []string{"dev", "admin"},
|
Groups: []string{"dev", "admins"},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
@ -277,7 +277,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfTwoFactorDomain() {
|
||||||
GetDetails(gomock.Eq("john")).
|
GetDetails(gomock.Eq("john")).
|
||||||
Return(&authentication.UserDetails{
|
Return(&authentication.UserDetails{
|
||||||
Emails: []string{"john@example.com"},
|
Emails: []string{"john@example.com"},
|
||||||
Groups: []string{"dev", "admin"},
|
Groups: []string{"dev", "admins"},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
@ -300,7 +300,7 @@ func (s *BasicAuthorizationSuite) TestShouldApplyPolicyOfDenyDomain() {
|
||||||
GetDetails(gomock.Eq("john")).
|
GetDetails(gomock.Eq("john")).
|
||||||
Return(&authentication.UserDetails{
|
Return(&authentication.UserDetails{
|
||||||
Emails: []string{"john@example.com"},
|
Emails: []string{"john@example.com"},
|
||||||
Groups: []string{"dev", "admin"},
|
Groups: []string{"dev", "admins"},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package server
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/clems4ever/authelia/internal/configuration/schema"
|
"github.com/clems4ever/authelia/internal/configuration/schema"
|
||||||
"github.com/clems4ever/authelia/internal/duo"
|
"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)
|
fmt.Println("Selected public_html directory is ", publicDir)
|
||||||
|
|
||||||
router.GET("/", fasthttp.FSHandler(publicDir, 0))
|
router.GET("/", fasthttp.FSHandler(publicDir, 0))
|
||||||
router.NotFound = fasthttp.FSHandler(publicDir, 0)
|
|
||||||
router.ServeFiles("/static/*filepath", publicDir+"/static")
|
router.ServeFiles("/static/*filepath", publicDir+"/static")
|
||||||
|
|
||||||
router.GET("/api/state", autheliaMiddleware(handlers.StateGet))
|
router.GET("/api/state", autheliaMiddleware(handlers.StateGet))
|
||||||
|
@ -95,6 +95,10 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi
|
||||||
middlewares.RequireFirstFactor(handlers.SecondFactorDuoPost(duoAPI))))
|
middlewares.RequireFirstFactor(handlers.SecondFactorDuoPost(duoAPI))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
router.NotFound = func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.SendFile(path.Join(publicDir, "index.html"))
|
||||||
|
}
|
||||||
|
|
||||||
portPattern := fmt.Sprintf(":%d", configuration.Port)
|
portPattern := fmt.Sprintf(":%d", configuration.Port)
|
||||||
logging.Logger().Infof("Authelia is listening on %s", portPattern)
|
logging.Logger().Infof("Authelia is listening on %s", portPattern)
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -136,9 +136,9 @@ access_control:
|
||||||
- domain: singlefactor.example.com
|
- domain: singlefactor.example.com
|
||||||
policy: one_factor
|
policy: one_factor
|
||||||
|
|
||||||
# Rules applied to 'admin' group
|
# Rules applied to 'admins' group
|
||||||
- domain: mx2.mail.example.com
|
- domain: mx2.mail.example.com
|
||||||
subject: "group:admin"
|
subject: "group:admins"
|
||||||
policy: deny
|
policy: deny
|
||||||
|
|
||||||
# Rules applied to user 'john'
|
# Rules applied to user 'john'
|
||||||
|
@ -147,7 +147,7 @@ access_control:
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
- domain: "*.example.com"
|
- domain: "*.example.com"
|
||||||
subject: "group:admin"
|
subject: "group:admins"
|
||||||
policy: two_factor
|
policy: two_factor
|
||||||
|
|
||||||
# Rules applied to 'dev' group
|
# Rules applied to 'dev' group
|
||||||
|
|
|
@ -3,3 +3,4 @@ services:
|
||||||
authelia-backend:
|
authelia-backend:
|
||||||
volumes:
|
volumes:
|
||||||
- "./internal/suites/Mariadb/configuration.yml:/etc/authelia/configuration.yml:ro"
|
- "./internal/suites/Mariadb/configuration.yml:/etc/authelia/configuration.yml:ro"
|
||||||
|
- "./internal/suites/Mariadb/users.yml:/var/lib/authelia/users.yml"
|
||||||
|
|
|
@ -10,7 +10,7 @@ jwt_secret: unsecure_password
|
||||||
|
|
||||||
authentication_backend:
|
authentication_backend:
|
||||||
file:
|
file:
|
||||||
path: users.yml
|
path: /var/lib/authelia/users.yml
|
||||||
|
|
||||||
session:
|
session:
|
||||||
secret: unsecure_session_secret
|
secret: unsecure_session_secret
|
||||||
|
|
|
@ -4,27 +4,27 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"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) {
|
func (wds *WebDriverSession) doFillLoginPageAndClick(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool) {
|
||||||
usernameElement := wds.WaitElementLocatedByID(ctx, t, "username-textfield")
|
usernameElement := wds.WaitElementLocatedByID(ctx, t, "username-textfield")
|
||||||
err := usernameElement.SendKeys(username)
|
err := usernameElement.SendKeys(username)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
passwordElement := wds.WaitElementLocatedByID(ctx, t, "password-textfield")
|
passwordElement := wds.WaitElementLocatedByID(ctx, t, "password-textfield")
|
||||||
err = passwordElement.SendKeys(password)
|
err = passwordElement.SendKeys(password)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if keepMeLoggedIn {
|
if keepMeLoggedIn {
|
||||||
keepMeLoggedInElement := wds.WaitElementLocatedByID(ctx, t, "remember-checkbox")
|
keepMeLoggedInElement := wds.WaitElementLocatedByID(ctx, t, "remember-checkbox")
|
||||||
err = keepMeLoggedInElement.Click()
|
err = keepMeLoggedInElement.Click()
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonElement := wds.WaitElementLocatedByID(ctx, t, "sign-in-button")
|
buttonElement := wds.WaitElementLocatedByID(ctx, t, "sign-in-button")
|
||||||
err = buttonElement.Click()
|
err = buttonElement.Click()
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login 1FA
|
// Login 1FA
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (de *DockerEnvironment) createCommand(cmd string) *exec.Cmd {
|
||||||
|
|
||||||
// Up spawn a docker environment
|
// Up spawn a docker environment
|
||||||
func (de *DockerEnvironment) Up() error {
|
func (de *DockerEnvironment) Up() error {
|
||||||
return de.createCommandWithStdout("up -d").Run()
|
return de.createCommandWithStdout("up --build -d").Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart restarts a service
|
// Restart restarts a service
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DuoPolicy a type of policy
|
// DuoPolicy a type of policy
|
||||||
|
@ -26,10 +26,10 @@ func ConfigureDuo(t *testing.T, allowDeny DuoPolicy) {
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, nil)
|
req, err := http.NewRequest("POST", url, nil)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
client := NewHTTPClient()
|
client := NewHTTPClient()
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, 200, res.StatusCode)
|
require.Equal(t, 200, res.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,31 +35,34 @@ func waitUntilServiceLogDetected(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitUntilAutheliaIsReady(dockerEnvironment *DockerEnvironment) error {
|
func waitUntilAutheliaBackendIsReady(dockerEnvironment *DockerEnvironment) error {
|
||||||
log.Info("Waiting for Authelia to be ready...")
|
return waitUntilServiceLogDetected(
|
||||||
|
|
||||||
err := waitUntilServiceLogDetected(
|
|
||||||
5*time.Second,
|
5*time.Second,
|
||||||
90*time.Second,
|
90*time.Second,
|
||||||
dockerEnvironment,
|
dockerEnvironment,
|
||||||
"authelia-backend",
|
"authelia-backend",
|
||||||
[]string{"Authelia is listening on"})
|
[]string{"Authelia is listening on"})
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = waitUntilServiceLogDetected(
|
func waitUntilAutheliaFrontendIsReady(dockerEnvironment *DockerEnvironment) error {
|
||||||
|
return waitUntilServiceLogDetected(
|
||||||
5*time.Second,
|
5*time.Second,
|
||||||
90*time.Second,
|
90*time.Second,
|
||||||
dockerEnvironment,
|
dockerEnvironment,
|
||||||
"authelia-frontend",
|
"authelia-frontend",
|
||||||
[]string{"You can now view web in the browser.", "Compiled with warnings", "Compiled successfully!"})
|
[]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
|
return err
|
||||||
}
|
}
|
||||||
log.Info("Authelia is now ready!")
|
log.Info("Authelia is now ready!")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,12 @@ import (
|
||||||
|
|
||||||
// Suite the definition of a suite
|
// Suite the definition of a suite
|
||||||
type Suite struct {
|
type Suite struct {
|
||||||
TestTimeout time.Duration
|
|
||||||
SetUp func(tmpPath string) error
|
SetUp func(tmpPath string) error
|
||||||
SetUpTimeout time.Duration
|
SetUpTimeout time.Duration
|
||||||
|
OnSetupTimeout func() error
|
||||||
|
|
||||||
|
TestTimeout time.Duration
|
||||||
|
|
||||||
TearDown func(tmpPath string) error
|
TearDown func(tmpPath string) error
|
||||||
TearDownTimeout time.Duration
|
TearDownTimeout time.Duration
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,15 @@ package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
mapset "github.com/deckarep/golang-set"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/tebeka/selenium"
|
"github.com/tebeka/selenium"
|
||||||
)
|
)
|
||||||
|
@ -59,18 +63,55 @@ func (s *CustomHeadersScenario) TestShouldNotForwardCustomHeaderForUnauthenticat
|
||||||
s.WaitElementTextContains(ctx, s.T(), body, "httpbin:8000")
|
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() {
|
func (s *CustomHeadersScenario) TestShouldForwardCustomHeaderForAuthenticatedUser() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
expectedGroups := mapset.NewSetWith("dev", "admins")
|
||||||
|
|
||||||
targetURL := fmt.Sprintf("%s/headers", PublicBaseURL)
|
targetURL := fmt.Sprintf("%s/headers", PublicBaseURL)
|
||||||
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
|
s.doLoginOneFactor(ctx, s.T(), "john", "password", false, targetURL)
|
||||||
s.verifyURLIs(ctx, s.T(), 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")
|
body, err := s.WebDriver().FindElement(selenium.ByTagName, "body")
|
||||||
s.Assert().NoError(err)
|
if err != nil {
|
||||||
s.WaitElementTextContains(ctx, s.T(), body, "\"Custom-Forwarded-User\": \"john\"")
|
return false, err
|
||||||
s.WaitElementTextContains(ctx, s.T(), body, "\"Custom-Forwarded-Groups\": \"admins,dev\"")
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestCustomHeadersScenario(t *testing.T) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (s *RegulationScenario) TestShouldBanUserAfterTooManyAttempt() {
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
|
s.WaitElementLocatedByID(ctx, s.T(), "sign-in-button").Click()
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset password field
|
// Reset password field
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (s *UserPreferencesScenario) SetupTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UserPreferencesScenario) TestShouldRememberLastUsed2FAMethod() {
|
func (s *UserPreferencesScenario) TestShouldRememberLastUsed2FAMethod() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Authenticate
|
// Authenticate
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +25,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -34,6 +50,7 @@ func init() {
|
||||||
GlobalRegistry.Register(bypassAllSuiteName, Suite{
|
GlobalRegistry.Register(bypassAllSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 5 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
TestTimeout: 1 * time.Minute,
|
TestTimeout: 1 * time.Minute,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
|
|
|
@ -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.`,
|
||||||
|
})
|
||||||
|
}
|
|
@ -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())
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,7 +23,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -32,6 +48,7 @@ func init() {
|
||||||
GlobalRegistry.Register(duoPushSuiteName, Suite{
|
GlobalRegistry.Register(duoPushSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 5 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
TestTimeout: 1 * time.Minute,
|
TestTimeout: 1 * time.Minute,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
|
|
|
@ -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() {
|
func (s *DuoPushSuite) TearDownTest() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
s.doChangeMethod(ctx, s.T(), "one-time-password")
|
s.doChangeMethod(ctx, s.T(), "one-time-password")
|
||||||
|
s.WaitElementLocatedByID(ctx, s.T(), "one-time-password-method")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DuoPushSuite) TestShouldSucceedAuthentication() {
|
func (s *DuoPushSuite) TestShouldSucceedAuthentication() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
ConfigureDuo(s.T(), Allow)
|
ConfigureDuo(s.T(), Allow)
|
||||||
|
@ -54,7 +62,7 @@ func (s *DuoPushSuite) TestShouldSucceedAuthentication() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DuoPushSuite) TestShouldFailAuthentication() {
|
func (s *DuoPushSuite) TestShouldFailAuthentication() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
ConfigureDuo(s.T(), Deny)
|
ConfigureDuo(s.T(), Deny)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,7 +28,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -37,7 +53,8 @@ func init() {
|
||||||
GlobalRegistry.Register(highAvailabilitySuiteName, Suite{
|
GlobalRegistry.Register(highAvailabilitySuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 5 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
TestTimeout: 1 * time.Minute,
|
OnSetupTimeout: onSetupTimeout,
|
||||||
|
TestTimeout: 5 * time.Minute,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
Description: `This suite is made to test Authelia in a *complete*
|
Description: `This suite is made to test Authelia in a *complete*
|
||||||
|
|
|
@ -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() {
|
func (s *HighAvailabilityWebDriverSuite) TestShouldKeepUserDataInDB() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -151,13 +160,15 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldVerifyAccessControl() {
|
||||||
|
|
||||||
verifyAuthorization := func(username string) func(t *testing.T) {
|
verifyAuthorization := func(username string) func(t *testing.T) {
|
||||||
return 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()
|
defer cancel()
|
||||||
|
|
||||||
s.doRegisterAndLogin2FA(ctx, t, username, "password", false, "")
|
s.doRegisterAndLogin2FA(ctx, t, username, "password", false, "")
|
||||||
|
|
||||||
for url, authorizations := range expectedAuthorizations {
|
for url, authorizations := range expectedAuthorizations {
|
||||||
|
t.Run(url, func(t *testing.T) {
|
||||||
verifyUserIsAuthorized(ctx, t, username, url, authorizations[username])
|
verifyUserIsAuthorized(ctx, t, username, url, authorizations[username])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
s.doLogout(ctx, t)
|
s.doLogout(ctx, t)
|
||||||
|
@ -165,7 +176,7 @@ func (s *HighAvailabilityWebDriverSuite) TestShouldVerifyAccessControl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, user := range []string{UserJohn, UserBob, UserHarry} {
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,16 @@ func init() {
|
||||||
kubectl := Kubectl{}
|
kubectl := Kubectl{}
|
||||||
|
|
||||||
setup := func(suitePath string) error {
|
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()
|
exists, err := kind.ClusterExists()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -74,23 +84,24 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
teardown := func(suitePath string) error {
|
teardown := func(suitePath string) error {
|
||||||
|
kubectl.StopDashboard()
|
||||||
kubectl.StopProxy()
|
kubectl.StopProxy()
|
||||||
return kind.DeleteCluster()
|
return kind.DeleteCluster()
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalRegistry.Register(kubernetesSuiteName, Suite{
|
GlobalRegistry.Register(kubernetesSuiteName, Suite{
|
||||||
TestTimeout: 60 * time.Second,
|
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 10 * time.Minute,
|
SetUpTimeout: 10 * time.Minute,
|
||||||
|
TestTimeout: 2 * time.Minute,
|
||||||
TearDown: teardown,
|
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.",
|
Description: "This suite has been created to test Authelia in a Kubernetes context and using nginx as the ingress controller.",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadDockerImages() error {
|
func loadDockerImages() error {
|
||||||
kind := Kind{}
|
kind := Kind{}
|
||||||
images := []string{"authelia:dist", "authelia-example-backend"}
|
images := []string{"authelia:dist", "nginx-backend"}
|
||||||
|
|
||||||
for _, image := range images {
|
for _, image := range images {
|
||||||
err := kind.LoadImage(image)
|
err := kind.LoadImage(image)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +26,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -36,6 +52,7 @@ func init() {
|
||||||
GlobalRegistry.Register(ldapSuiteName, Suite{
|
GlobalRegistry.Register(ldapSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 5 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
TestTimeout: 1 * time.Minute,
|
TestTimeout: 1 * time.Minute,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +25,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -34,7 +50,8 @@ func init() {
|
||||||
|
|
||||||
GlobalRegistry.Register(mariadbSuiteName, Suite{
|
GlobalRegistry.Register(mariadbSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 3 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +26,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -35,6 +51,7 @@ func init() {
|
||||||
GlobalRegistry.Register(networkACLSuiteName, Suite{
|
GlobalRegistry.Register(networkACLSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 5 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
TestTimeout: 1 * time.Minute,
|
TestTimeout: 1 * time.Minute,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
|
|
|
@ -3,7 +3,6 @@ package suites
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -12,66 +11,26 @@ import (
|
||||||
|
|
||||||
type NetworkACLSuite struct {
|
type NetworkACLSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
clients []*WebDriverSession
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNetworkACLSuite() *NetworkACLSuite {
|
func NewNetworkACLSuite() *NetworkACLSuite {
|
||||||
return &NetworkACLSuite{clients: make([]*WebDriverSession, 3)}
|
return &NetworkACLSuite{}
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NetworkACLSuite) TestShouldAccessSecretUpon2FA() {
|
func (s *NetworkACLSuite) TestShouldAccessSecretUpon2FA() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
wds, err := StartWebDriver()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer wds.Stop()
|
||||||
|
|
||||||
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
|
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)
|
wds.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, targetURL)
|
||||||
s.clients[0].verifyIsFirstFactorPage(ctx, s.T())
|
wds.verifySecretAuthorized(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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// from network 192.168.240.201/32
|
// from network 192.168.240.201/32
|
||||||
|
@ -79,13 +38,17 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon1FA() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
targetURL := fmt.Sprintf("%s/secret.html", SecureBaseURL)
|
wds, err := StartWebDriverWithProxy("http://proxy-client1.example.com:3128", 4444)
|
||||||
s.clients[1].doVisit(s.T(), targetURL)
|
s.Require().NoError(err)
|
||||||
s.clients[1].verifyIsFirstFactorPage(ctx, s.T())
|
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))
|
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
|
// from network 192.168.240.202/32
|
||||||
|
@ -93,8 +56,12 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon0FA() {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
s.clients[2].doVisit(s.T(), fmt.Sprintf("%s/secret.html", SecureBaseURL))
|
wds, err := StartWebDriverWithProxy("http://proxy-client2.example.com:3128", 4444)
|
||||||
s.clients[2].verifySecretAuthorized(ctx, s.T())
|
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) {
|
func TestNetworkACLSuite(t *testing.T) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +25,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -34,7 +50,8 @@ func init() {
|
||||||
|
|
||||||
GlobalRegistry.Register(postgresSuiteName, Suite{
|
GlobalRegistry.Register(postgresSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 3 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,7 +23,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -32,7 +48,8 @@ func init() {
|
||||||
GlobalRegistry.Register(shortTimeoutsSuiteName, Suite{
|
GlobalRegistry.Register(shortTimeoutsSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 5 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
TestTimeout: 1 * time.Minute,
|
OnSetupTimeout: onSetupTimeout,
|
||||||
|
TestTimeout: 3 * time.Minute,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
Description: `This suite has been created to configure Authelia with short timeouts for sessions expiration
|
Description: `This suite has been created to configure Authelia with short timeouts for sessions expiration
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +25,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -35,8 +51,10 @@ func init() {
|
||||||
GlobalRegistry.Register(standaloneSuiteName, Suite{
|
GlobalRegistry.Register(standaloneSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 5 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
TearDown: teardown,
|
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
|
Description: `This suite is used to test Authelia in a standalone
|
||||||
configuration with in-memory sessions and a local sqlite db stored on disk`,
|
configuration with in-memory sessions and a local sqlite db stored on disk`,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package suites
|
package suites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,7 +25,22 @@ func init() {
|
||||||
return err
|
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 {
|
teardown := func(suitePath string) error {
|
||||||
|
@ -35,6 +51,7 @@ func init() {
|
||||||
GlobalRegistry.Register(traefikSuiteName, Suite{
|
GlobalRegistry.Register(traefikSuiteName, Suite{
|
||||||
SetUp: setup,
|
SetUp: setup,
|
||||||
SetUpTimeout: 5 * time.Minute,
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
TearDown: teardown,
|
TearDown: teardown,
|
||||||
TearDownTimeout: 2 * time.Minute,
|
TearDownTimeout: 2 * time.Minute,
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,8 +11,15 @@ import (
|
||||||
|
|
||||||
func (wds *WebDriverSession) verifyBodyContains(ctx context.Context, t *testing.T, pattern string) {
|
func (wds *WebDriverSession) verifyBodyContains(ctx context.Context, t *testing.T, pattern string) {
|
||||||
err := wds.Wait(ctx, func(wd selenium.WebDriver) (bool, error) {
|
err := wds.Wait(ctx, func(wd selenium.WebDriver) (bool, error) {
|
||||||
bodyElement := wds.WaitElementLocatedByTagName(ctx, t, "body")
|
bodyElement, err := wds.WebDriver.FindElement(selenium.ByTagName, "body")
|
||||||
require.NotNil(t, bodyElement)
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bodyElement == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
content, err := bodyElement.Text()
|
content, err := bodyElement.Text()
|
||||||
|
|
||||||
|
|
|
@ -6,5 +6,5 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (wds *WebDriverSession) verifySecretAuthorized(ctx context.Context, t *testing.T) {
|
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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// ErrTimeoutReached error thrown when a timeout is reached
|
||||||
|
var ErrTimeoutReached = errors.New("timeout reached")
|
|
@ -31,10 +31,8 @@ func Command(name string, args ...string) *exec.Cmd {
|
||||||
// CommandWithStdout create a command forwarding stdout and stderr to the OS streams
|
// CommandWithStdout create a command forwarding stdout and stderr to the OS streams
|
||||||
func CommandWithStdout(name string, args ...string) *exec.Cmd {
|
func CommandWithStdout(name string, args ...string) *exec.Cmd {
|
||||||
cmd := Command(name, args...)
|
cmd := Command(name, args...)
|
||||||
if log.GetLevel() > log.InfoLevel {
|
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
}
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +131,7 @@ func RunCommandWithTimeout(cmd *exec.Cmd, timeout time.Duration) error {
|
||||||
if err := cmd.Process.Kill(); err != nil {
|
if err := cmd.Process.Kill(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fmt.Errorf("timeout of %ds reached", int64(timeout/time.Second))
|
return ErrTimeoutReached
|
||||||
case err := <-done:
|
case err := <-done:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { Switch, Route, Redirect, useHistory, useLocation } from "react-router";
|
||||||
import FirstFactorForm from "./FirstFactor/FirstFactorForm";
|
import FirstFactorForm from "./FirstFactor/FirstFactorForm";
|
||||||
import SecondFactorForm from "./SecondFactor/SecondFactorForm";
|
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 { useAutheliaState } from "../../hooks/State";
|
||||||
import LoadingPage from "../LoadingPage/LoadingPage";
|
import LoadingPage from "../LoadingPage/LoadingPage";
|
||||||
import { AuthenticationLevel } from "../../services/State";
|
import { AuthenticationLevel } from "../../services/State";
|
||||||
|
@ -23,6 +26,8 @@ export default function () {
|
||||||
const [preferences, fetchPreferences, , fetchPreferencesError] = useUserPreferences();
|
const [preferences, fetchPreferences, , fetchPreferencesError] = useUserPreferences();
|
||||||
const [configuration, fetchConfiguration, , fetchConfigurationError] = useAutheliaConfiguration();
|
const [configuration, fetchConfiguration, , fetchConfigurationError] = useAutheliaConfiguration();
|
||||||
|
|
||||||
|
const redirect = useCallback((url: string) => history.push(url), [history]);
|
||||||
|
|
||||||
// Fetch the state when portal is mounted.
|
// Fetch the state when portal is mounted.
|
||||||
useEffect(() => { fetchState() }, [fetchState]);
|
useEffect(() => { fetchState() }, [fetchState]);
|
||||||
|
|
||||||
|
@ -71,19 +76,19 @@ export default function () {
|
||||||
|
|
||||||
if (state.authentication_level === AuthenticationLevel.Unauthenticated) {
|
if (state.authentication_level === AuthenticationLevel.Unauthenticated) {
|
||||||
setFirstFactorDisabled(false);
|
setFirstFactorDisabled(false);
|
||||||
history.push(`${FirstFactorRoute}${redirectionSuffix}`);
|
redirect(`${FirstFactorRoute}${redirectionSuffix}`);
|
||||||
} else if (state.authentication_level >= AuthenticationLevel.OneFactor && preferences) {
|
} else if (state.authentication_level >= AuthenticationLevel.OneFactor && preferences) {
|
||||||
console.log("redirect");
|
console.log("redirect");
|
||||||
if (preferences.method === SecondFactorMethod.U2F) {
|
if (preferences.method === SecondFactorMethod.U2F) {
|
||||||
history.push(`${SecondFactorU2FRoute}${redirectionSuffix}`);
|
redirect(`${SecondFactorU2FRoute}${redirectionSuffix}`);
|
||||||
} else if (preferences.method === SecondFactorMethod.Duo) {
|
} else if (preferences.method === SecondFactorMethod.Duo) {
|
||||||
history.push(`${SecondFactorPushRoute}${redirectionSuffix}`);
|
redirect(`${SecondFactorPushRoute}${redirectionSuffix}`);
|
||||||
} else {
|
} 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) => {
|
const handleFirstFactorSuccess = async (redirectionURL: string | undefined) => {
|
||||||
if (redirectionURL) {
|
if (redirectionURL) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { ReactNode, Fragment } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { makeStyles, Typography, Link } from "@material-ui/core";
|
import { makeStyles, Typography, Link } from "@material-ui/core";
|
||||||
|
|
||||||
interface MethodContainerProps {
|
interface MethodContainerProps {
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default function (props: Props) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
createErrorNotification("There was an issue signing out");
|
createErrorNotification("There was an issue signing out");
|
||||||
}
|
}
|
||||||
}, [createErrorNotification, setTimedOut]);
|
}, [createErrorNotification, setTimedOut, mounted]);
|
||||||
|
|
||||||
useEffect(() => { doSignOut() }, [doSignOut]);
|
useEffect(() => { doSignOut() }, [doSignOut]);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue