[CI] Add godot linter (#958)
* [CI] Add godot linter * Implement godot recommendationspull/964/head
parent
ce5f5e9214
commit
e67f63ee44
|
@ -4,12 +4,15 @@ run:
|
||||||
linters-settings:
|
linters-settings:
|
||||||
gocyclo:
|
gocyclo:
|
||||||
min-complexity: 15
|
min-complexity: 15
|
||||||
|
godot:
|
||||||
|
check-all: true
|
||||||
goimports:
|
goimports:
|
||||||
local-prefixes: github.com/authelia/authelia
|
local-prefixes: github.com/authelia/authelia
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
- gocyclo
|
- gocyclo
|
||||||
|
- godot
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
- goimports
|
||||||
- golint
|
- golint
|
||||||
|
|
|
@ -13,16 +13,16 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HostEntry represents an entry in /etc/hosts
|
// HostEntry represents an entry in /etc/hosts.
|
||||||
type HostEntry struct {
|
type HostEntry struct {
|
||||||
Domain string
|
Domain string
|
||||||
IP string
|
IP string
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostEntries = []HostEntry{
|
var hostEntries = []HostEntry{
|
||||||
// For authelia backend
|
// For authelia backend.
|
||||||
{Domain: "authelia.example.com", IP: "192.168.240.50"},
|
{Domain: "authelia.example.com", IP: "192.168.240.50"},
|
||||||
// For common tests
|
// For common tests.
|
||||||
{Domain: "login.example.com", IP: "192.168.240.100"},
|
{Domain: "login.example.com", IP: "192.168.240.100"},
|
||||||
{Domain: "admin.example.com", IP: "192.168.240.100"},
|
{Domain: "admin.example.com", IP: "192.168.240.100"},
|
||||||
{Domain: "singlefactor.example.com", IP: "192.168.240.100"},
|
{Domain: "singlefactor.example.com", IP: "192.168.240.100"},
|
||||||
|
@ -34,19 +34,15 @@ var hostEntries = []HostEntry{
|
||||||
{Domain: "secure.example.com", IP: "192.168.240.100"},
|
{Domain: "secure.example.com", IP: "192.168.240.100"},
|
||||||
{Domain: "mail.example.com", IP: "192.168.240.100"},
|
{Domain: "mail.example.com", IP: "192.168.240.100"},
|
||||||
{Domain: "duo.example.com", IP: "192.168.240.100"},
|
{Domain: "duo.example.com", IP: "192.168.240.100"},
|
||||||
|
// For Traefik suite.
|
||||||
// For Traefik suite
|
|
||||||
{Domain: "traefik.example.com", IP: "192.168.240.100"},
|
{Domain: "traefik.example.com", IP: "192.168.240.100"},
|
||||||
|
// For HAProxy suite.
|
||||||
// For HAProxy suite
|
|
||||||
{Domain: "haproxy.example.com", IP: "192.168.240.100"},
|
{Domain: "haproxy.example.com", IP: "192.168.240.100"},
|
||||||
|
// For testing network ACLs.
|
||||||
// For testing network ACLs
|
|
||||||
{Domain: "proxy-client1.example.com", IP: "192.168.240.201"},
|
{Domain: "proxy-client1.example.com", IP: "192.168.240.201"},
|
||||||
{Domain: "proxy-client2.example.com", IP: "192.168.240.202"},
|
{Domain: "proxy-client2.example.com", IP: "192.168.240.202"},
|
||||||
{Domain: "proxy-client3.example.com", IP: "192.168.240.203"},
|
{Domain: "proxy-client3.example.com", IP: "192.168.240.203"},
|
||||||
|
// Kubernetes dashboard.
|
||||||
// Kubernetes dashboard
|
|
||||||
{Domain: "kubernetes.example.com", IP: "192.168.240.110"},
|
{Domain: "kubernetes.example.com", IP: "192.168.240.110"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +166,7 @@ func readVersions() {
|
||||||
readVersion("docker-compose", "--version")
|
readVersion("docker-compose", "--version")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bootstrap bootstrap authelia dev environment
|
// Bootstrap bootstrap authelia dev environment.
|
||||||
func Bootstrap(cobraCmd *cobra.Command, args []string) {
|
func Bootstrap(cobraCmd *cobra.Command, args []string) {
|
||||||
bootstrapPrintln("Checking command installation...")
|
bootstrapPrintln("Checking command installation...")
|
||||||
checkCommandExist("node")
|
checkCommandExist("node")
|
||||||
|
|
|
@ -23,7 +23,7 @@ func buildAutheliaBinary() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildFrontend() {
|
func buildFrontend() {
|
||||||
// Install npm dependencies
|
// Install npm dependencies.
|
||||||
cmd := utils.CommandWithStdout("yarn", "install")
|
cmd := utils.CommandWithStdout("yarn", "install")
|
||||||
cmd.Dir = "web"
|
cmd.Dir = "web"
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ func buildFrontend() {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then build the frontend
|
// Then build the frontend.
|
||||||
cmd = utils.CommandWithStdout("yarn", "build")
|
cmd = utils.CommandWithStdout("yarn", "build")
|
||||||
cmd.Dir = "web"
|
cmd.Dir = "web"
|
||||||
cmd.Env = append(os.Environ(), "INLINE_RUNTIME_CHUNK=false")
|
cmd.Env = append(os.Environ(), "INLINE_RUNTIME_CHUNK=false")
|
||||||
|
@ -68,7 +68,7 @@ func generateEmbeddedAssets() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build build Authelia
|
// Build build Authelia.
|
||||||
func Build(cobraCmd *cobra.Command, args []string) {
|
func Build(cobraCmd *cobra.Command, args []string) {
|
||||||
log.Info("Building Authelia...")
|
log.Info("Building Authelia...")
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Clean artifacts built and installed by authelia-scripts
|
// Clean artifacts built and installed by authelia-scripts.
|
||||||
func Clean(cobraCmd *cobra.Command, args []string) {
|
func Clean(cobraCmd *cobra.Command, args []string) {
|
||||||
log.Debug("Removing `" + OutputDir + "` directory")
|
log.Debug("Removing `" + OutputDir + "` directory")
|
||||||
err := os.RemoveAll(OutputDir)
|
err := os.RemoveAll(OutputDir)
|
||||||
|
|
|
@ -42,12 +42,12 @@ func checkArchIsSupported(arch string) {
|
||||||
|
|
||||||
func dockerBuildOfficialImage(arch string) error {
|
func dockerBuildOfficialImage(arch string) error {
|
||||||
docker := &Docker{}
|
docker := &Docker{}
|
||||||
// Set default Architecture Dockerfile to amd64
|
// Set default Architecture Dockerfile to amd64.
|
||||||
dockerfile := "Dockerfile"
|
dockerfile := "Dockerfile"
|
||||||
// Set version of QEMU
|
// Set version of QEMU.
|
||||||
qemuversion := "v4.2.0-7"
|
qemuversion := "v4.2.0-7"
|
||||||
|
|
||||||
// If not the default value
|
// If not the default value.
|
||||||
if arch != defaultArch {
|
if arch != defaultArch {
|
||||||
dockerfile = fmt.Sprintf("%s.%s", dockerfile, arch)
|
dockerfile = fmt.Sprintf("%s.%s", dockerfile, arch)
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ var DockerBuildCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// DockerPushCmd Command for pushing Authelia docker image to Docker Hub
|
// DockerPushCmd Command for pushing Authelia docker image to DockerHub.
|
||||||
var DockerPushCmd = &cobra.Command{
|
var DockerPushCmd = &cobra.Command{
|
||||||
Use: "push-image",
|
Use: "push-image",
|
||||||
Short: "Publish Authelia docker image to Docker Hub",
|
Short: "Publish Authelia docker image to Docker Hub",
|
||||||
|
@ -131,7 +131,7 @@ var DockerPushCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// DockerManifestCmd Command for pushing Authelia docker manifest to Docker Hub
|
// DockerManifestCmd Command for pushing Authelia docker manifest to DockerHub.
|
||||||
var DockerManifestCmd = &cobra.Command{
|
var DockerManifestCmd = &cobra.Command{
|
||||||
Use: "push-manifest",
|
Use: "push-manifest",
|
||||||
Short: "Publish Authelia docker manifest to Docker Hub",
|
Short: "Publish Authelia docker manifest to Docker Hub",
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServeCmd serve authelia with the provided configuration
|
// ServeCmd serve Authelia with the provided configuration.
|
||||||
func ServeCmd(cobraCmd *cobra.Command, args []string) {
|
func ServeCmd(cobraCmd *cobra.Command, args []string) {
|
||||||
log.Infof("Running Authelia with config %s...", args[0])
|
log.Infof("Running Authelia with config %s...", args[0])
|
||||||
cmd := utils.CommandWithStdout(OutputDir+"/authelia", "--config", args[0])
|
cmd := utils.CommandWithStdout(OutputDir+"/authelia", "--config", args[0])
|
||||||
|
|
|
@ -21,10 +21,10 @@ import (
|
||||||
// ErrNotAvailableSuite error raised when suite is not available.
|
// ErrNotAvailableSuite error raised when suite is not available.
|
||||||
var ErrNotAvailableSuite = errors.New("unavailable suite")
|
var ErrNotAvailableSuite = errors.New("unavailable suite")
|
||||||
|
|
||||||
// ErrNoRunningSuite error raised when no suite is running
|
// ErrNoRunningSuite error raised when no suite is running.
|
||||||
var ErrNoRunningSuite = errors.New("no running suite")
|
var ErrNoRunningSuite = errors.New("no running suite")
|
||||||
|
|
||||||
// runningSuiteFile name of the file containing the currently running suite
|
// runningSuiteFile name of the file containing the currently running suite.
|
||||||
var runningSuiteFile = ".suite"
|
var runningSuiteFile = ".suite"
|
||||||
|
|
||||||
var headless bool
|
var headless bool
|
||||||
|
@ -68,7 +68,7 @@ var SuitesSetupCmd = &cobra.Command{
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuitesTeardownCmd Command for tearing down a suite environment
|
// SuitesTeardownCmd Command for tearing down a suite environment.
|
||||||
var SuitesTeardownCmd = &cobra.Command{
|
var SuitesTeardownCmd = &cobra.Command{
|
||||||
Use: "teardown [suite]",
|
Use: "teardown [suite]",
|
||||||
Short: "Teardown a Go suite environment. Suites can be listed using the list command.",
|
Short: "Teardown a Go suite environment. Suites can be listed using the list command.",
|
||||||
|
@ -96,7 +96,7 @@ var SuitesTeardownCmd = &cobra.Command{
|
||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuitesTestCmd Command for testing a suite
|
// SuitesTestCmd Command for testing a suite.
|
||||||
var SuitesTestCmd = &cobra.Command{
|
var SuitesTestCmd = &cobra.Command{
|
||||||
Use: "test [suite]",
|
Use: "test [suite]",
|
||||||
Short: "Test a suite. Suites can be listed using the list command.",
|
Short: "Test a suite. Suites can be listed using the list command.",
|
||||||
|
@ -192,7 +192,7 @@ func testSuite(cmd *cobra.Command, args []string) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If suite(s) are provided as argument
|
// If suite(s) are provided as argument.
|
||||||
if len(args) >= 1 {
|
if len(args) >= 1 {
|
||||||
suiteArg := args[0]
|
suiteArg := args[0]
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ func runSuiteTests(suiteName string, withEnv bool) error {
|
||||||
|
|
||||||
suite := suites.GlobalRegistry.Get(suiteName)
|
suite := suites.GlobalRegistry.Get(suiteName)
|
||||||
|
|
||||||
// Default value is 1 minute
|
// Default value is 1 minute.
|
||||||
timeout := "60s"
|
timeout := "60s"
|
||||||
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))
|
||||||
|
@ -279,7 +279,7 @@ func runSuiteTests(suiteName string, withEnv bool) error {
|
||||||
|
|
||||||
if withEnv {
|
if withEnv {
|
||||||
if err := teardownSuite(suiteName); err != nil {
|
if err := teardownSuite(suiteName); err != nil {
|
||||||
// Do not return this error to return the test error instead
|
// Do not return this error to return the test error instead.
|
||||||
log.Errorf("Error running teardown: %v", err)
|
log.Errorf("Error running teardown: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunUnitTest run the unit tests
|
// RunUnitTest run the unit tests.
|
||||||
func RunUnitTest(cobraCmd *cobra.Command, args []string) {
|
func RunUnitTest(cobraCmd *cobra.Command, args []string) {
|
||||||
log.SetLevel(log.TraceLevel)
|
log.SetLevel(log.TraceLevel)
|
||||||
if err := utils.Shell("go test $(go list ./... | grep -v suites)").Run(); err != nil {
|
if err := utils.Shell("go test $(go list ./... | grep -v suites)").Run(); err != nil {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
// OutputDir the output directory where the built version of Authelia is located
|
// OutputDir the output directory where the built version of Authelia is located.
|
||||||
var OutputDir = "dist"
|
var OutputDir = "dist"
|
||||||
|
|
||||||
// DockerImageName the official name of authelia docker image
|
// DockerImageName the official name of Authelia docker image.
|
||||||
var DockerImageName = "authelia/authelia"
|
var DockerImageName = "authelia/authelia"
|
||||||
|
|
||||||
// IntermediateDockerImageName local name of the docker image
|
// IntermediateDockerImageName local name of the docker image.
|
||||||
var IntermediateDockerImageName = "authelia:dist"
|
var IntermediateDockerImageName = "authelia:dist"
|
||||||
|
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Docker a docker object
|
// Docker a docker object.
|
||||||
type Docker struct{}
|
type Docker struct{}
|
||||||
|
|
||||||
// Build build a docker image
|
// Build build a docker image.
|
||||||
func (d *Docker) Build(tag, dockerfile, target, gitTag, gitCommit string) error {
|
func (d *Docker) Build(tag, dockerfile, target, gitTag, gitCommit string) error {
|
||||||
return utils.CommandWithStdout(
|
return utils.CommandWithStdout(
|
||||||
"docker", "build", "-t", tag, "-f", dockerfile, "--build-arg",
|
"docker", "build", "-t", tag, "-f", dockerfile, "--build-arg",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//usr/bin/env go run "$0" "$@"; exit
|
//usr/bin/env go run "$0" "$@"; exit
|
||||||
|
//nolint:godot
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
@ -23,10 +24,10 @@ type AutheliaCommandDefinition struct {
|
||||||
SubCommands []*cobra.Command
|
SubCommands []*cobra.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
// CobraCommands list of cobra commands
|
// CobraCommands list of cobra commands.
|
||||||
type CobraCommands = []*cobra.Command
|
type CobraCommands = []*cobra.Command
|
||||||
|
|
||||||
// Commands is the list of commands of authelia-scripts
|
// Commands is the list of commands of authelia-scripts.
|
||||||
var Commands = []AutheliaCommandDefinition{
|
var Commands = []AutheliaCommandDefinition{
|
||||||
{
|
{
|
||||||
Name: "bootstrap",
|
Name: "bootstrap",
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
|
|
||||||
var tmpDirectory = "/tmp/authelia/suites/"
|
var tmpDirectory = "/tmp/authelia/suites/"
|
||||||
|
|
||||||
// runningSuiteFile name of the file containing the currently running suite
|
// runningSuiteFile name of the file containing the currently running suite.
|
||||||
var runningSuiteFile = ".suite"
|
var runningSuiteFile = ".suite"
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -21,7 +21,7 @@ const (
|
||||||
Push = "mobile_push"
|
Push = "mobile_push"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PossibleMethods is the set of all possible 2FA methods
|
// PossibleMethods is the set of all possible 2FA methods.
|
||||||
var PossibleMethods = []string{TOTP, U2F, Push}
|
var PossibleMethods = []string{TOTP, U2F, Push}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -285,7 +285,7 @@ groups:
|
||||||
- dev
|
- dev
|
||||||
`)
|
`)
|
||||||
|
|
||||||
// The YAML is valid but the root key is user instead of users
|
// The YAML is valid but the root key is user instead of users.
|
||||||
var BadSchemaUserDatabaseContent = []byte(`
|
var BadSchemaUserDatabaseContent = []byte(`
|
||||||
user:
|
user:
|
||||||
john:
|
john:
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ********************* CONNECTION *********************
|
// ********************* CONNECTION *********************.
|
||||||
|
|
||||||
// LDAPConnection interface representing a connection to the ldap.
|
// LDAPConnection interface representing a connection to the ldap.
|
||||||
type LDAPConnection interface {
|
type LDAPConnection interface {
|
||||||
|
@ -47,7 +47,7 @@ func (lc *LDAPConnectionImpl) Modify(modifyRequest *ldap.ModifyRequest) error {
|
||||||
return lc.conn.Modify(modifyRequest)
|
return lc.conn.Modify(modifyRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ********************* FACTORY ***********************
|
// ********************* FACTORY ***********************.
|
||||||
|
|
||||||
// LDAPConnectionFactory an interface of factory of ldap connections.
|
// LDAPConnectionFactory an interface of factory of ldap connections.
|
||||||
type LDAPConnectionFactory interface {
|
type LDAPConnectionFactory interface {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// PasswordHash represents all characteristics of a password hash.
|
// PasswordHash represents all characteristics of a password hash.
|
||||||
// Authelia only supports salted SHA512 or salted argon2id method, i.e., $6$ mode or $argon2id$ mode
|
// Authelia only supports salted SHA512 or salted argon2id method, i.e., $6$ mode or $argon2id$ mode.
|
||||||
type PasswordHash struct {
|
type PasswordHash struct {
|
||||||
Algorithm string
|
Algorithm string
|
||||||
Iterations int
|
Iterations int
|
||||||
|
@ -23,11 +23,11 @@ type PasswordHash struct {
|
||||||
Parallelism int
|
Parallelism int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseHash extracts all characteristics of a hash given its string representation
|
// ParseHash extracts all characteristics of a hash given its string representation.
|
||||||
func ParseHash(hash string) (passwordHash *PasswordHash, err error) {
|
func ParseHash(hash string) (passwordHash *PasswordHash, err error) {
|
||||||
parts := strings.Split(hash, "$")
|
parts := strings.Split(hash, "$")
|
||||||
|
|
||||||
// This error can be ignored as it's always nil
|
// This error can be ignored as it's always nil.
|
||||||
code, parameters, salt, key, _ := crypt.DecodeSettings(hash)
|
code, parameters, salt, key, _ := crypt.DecodeSettings(hash)
|
||||||
h := &PasswordHash{}
|
h := &PasswordHash{}
|
||||||
|
|
||||||
|
@ -81,8 +81,8 @@ func ParseHash(hash string) (passwordHash *PasswordHash, err error) {
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashPassword generate a salt and hash the password with the salt and a constant number of rounds
|
// HashPassword generate a salt and hash the password with the salt and a constant number of rounds.
|
||||||
//nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting
|
//nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting.
|
||||||
func HashPassword(password, salt, algorithm string, iterations, memory, parallelism, keyLength, saltLength int) (hash string, err error) {
|
func HashPassword(password, salt, algorithm string, iterations, memory, parallelism, keyLength, saltLength int) (hash string, err error) {
|
||||||
var settings string
|
var settings string
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ func HashPassword(password, salt, algorithm string, iterations, memory, parallel
|
||||||
}
|
}
|
||||||
|
|
||||||
if algorithm == HashingAlgorithmArgon2id {
|
if algorithm == HashingAlgorithmArgon2id {
|
||||||
// Caution: Increasing any of the values in the below block has a high chance in old passwords that cannot be verified
|
// Caution: Increasing any of the values in the below block has a high chance in old passwords that cannot be verified.
|
||||||
if memory < 8 {
|
if memory < 8 {
|
||||||
return "", fmt.Errorf("Memory (argon2id) input of %d is invalid, it must be 8 or higher", memory)
|
return "", fmt.Errorf("Memory (argon2id) input of %d is invalid, it must be 8 or higher", memory)
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ func HashPassword(password, salt, algorithm string, iterations, memory, parallel
|
||||||
if iterations < 1 {
|
if iterations < 1 {
|
||||||
return "", fmt.Errorf("Iterations (argon2id) input of %d is invalid, it must be 1 or more", iterations)
|
return "", fmt.Errorf("Iterations (argon2id) input of %d is invalid, it must be 1 or more", iterations)
|
||||||
}
|
}
|
||||||
// Caution: Increasing any of the values in the above block has a high chance in old passwords that cannot be verified
|
// Caution: Increasing any of the values in the above block has a high chance in old passwords that cannot be verified.
|
||||||
}
|
}
|
||||||
|
|
||||||
if salt == "" {
|
if salt == "" {
|
||||||
|
@ -129,12 +129,12 @@ func HashPassword(password, salt, algorithm string, iterations, memory, parallel
|
||||||
}
|
}
|
||||||
settings = getCryptSettings(salt, algorithm, iterations, memory, parallelism, keyLength)
|
settings = getCryptSettings(salt, algorithm, iterations, memory, parallelism, keyLength)
|
||||||
|
|
||||||
// This error can be ignored because we check for it before a user gets here
|
// This error can be ignored because we check for it before a user gets here.
|
||||||
hash, _ = crypt.Crypt(password, settings)
|
hash, _ = crypt.Crypt(password, settings)
|
||||||
return hash, nil
|
return hash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckPassword check a password against a hash
|
// CheckPassword check a password against a hash.
|
||||||
func CheckPassword(password, hash string) (ok bool, err error) {
|
func CheckPassword(password, hash string) (ok bool, err error) {
|
||||||
passwordHash, err := ParseHash(hash)
|
passwordHash, err := ParseHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -45,7 +45,7 @@ func TestShouldHashArgon2idPassword(t *testing.T) {
|
||||||
assert.Equal(t, schema.DefaultCIPasswordConfiguration.KeyLength, parameters.GetInt("k", HashingDefaultArgon2idKeyLength))
|
assert.Equal(t, schema.DefaultCIPasswordConfiguration.KeyLength, parameters.GetInt("k", HashingDefaultArgon2idKeyLength))
|
||||||
}
|
}
|
||||||
|
|
||||||
// This checks the method of hashing (for argon2id) supports all the characters we allow in Authelia's hash function
|
// This checks the method of hashing (for argon2id) supports all the characters we allow in Authelia's hash function.
|
||||||
func TestArgon2idHashSaltValidValues(t *testing.T) {
|
func TestArgon2idHashSaltValidValues(t *testing.T) {
|
||||||
data := string(HashingPossibleSaltCharacters)
|
data := string(HashingPossibleSaltCharacters)
|
||||||
datas := utils.SliceString(data, 16)
|
datas := utils.SliceString(data, 16)
|
||||||
|
@ -58,7 +58,7 @@ func TestArgon2idHashSaltValidValues(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This checks the method of hashing (for sha512) supports all the characters we allow in Authelia's hash function
|
// This checks the method of hashing (for sha512) supports all the characters we allow in Authelia's hash function.
|
||||||
func TestSHA512HashSaltValidValues(t *testing.T) {
|
func TestSHA512HashSaltValidValues(t *testing.T) {
|
||||||
data := string(HashingPossibleSaltCharacters)
|
data := string(HashingPossibleSaltCharacters)
|
||||||
datas := utils.SliceString(data, 16)
|
datas := utils.SliceString(data, 16)
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (s Subject) String() string {
|
||||||
return fmt.Sprintf("username=%s groups=%s ip=%s", s.Username, strings.Join(s.Groups, ","), s.IP.String())
|
return fmt.Sprintf("username=%s groups=%s ip=%s", s.Username, strings.Join(s.Groups, ","), s.IP.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object object to check access control for
|
// Object object to check access control for.
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Domain string
|
Domain string
|
||||||
Path string
|
Path string
|
||||||
|
|
|
@ -19,7 +19,7 @@ type SessionConfiguration struct {
|
||||||
Redis *RedisSessionConfiguration `mapstructure:"redis"`
|
Redis *RedisSessionConfiguration `mapstructure:"redis"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultSessionConfiguration is the default session configuration
|
// DefaultSessionConfiguration is the default session configuration.
|
||||||
var DefaultSessionConfiguration = SessionConfiguration{
|
var DefaultSessionConfiguration = SessionConfiguration{
|
||||||
Name: "authelia_session",
|
Name: "authelia_session",
|
||||||
Expiration: "1h",
|
Expiration: "1h",
|
||||||
|
|
|
@ -5,7 +5,7 @@ type LocalStorageConfiguration struct {
|
||||||
Path string `mapstructure:"path"`
|
Path string `mapstructure:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SQLStorageConfiguration represents the configuration of the SQL database
|
// SQLStorageConfiguration represents the configuration of the SQL database.
|
||||||
type SQLStorageConfiguration struct {
|
type SQLStorageConfiguration struct {
|
||||||
Host string `mapstructure:"host"`
|
Host string `mapstructure:"host"`
|
||||||
Port int `mapstructure:"port"`
|
Port int `mapstructure:"port"`
|
||||||
|
@ -14,12 +14,12 @@ type SQLStorageConfiguration struct {
|
||||||
Password string `mapstructure:"password"`
|
Password string `mapstructure:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MySQLStorageConfiguration represents the configuration of a MySQL database
|
// MySQLStorageConfiguration represents the configuration of a MySQL database.
|
||||||
type MySQLStorageConfiguration struct {
|
type MySQLStorageConfiguration struct {
|
||||||
SQLStorageConfiguration `mapstructure:",squash"`
|
SQLStorageConfiguration `mapstructure:",squash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostgreSQLStorageConfiguration represents the configuration of a Postgres database
|
// PostgreSQLStorageConfiguration represents the configuration of a Postgres database.
|
||||||
type PostgreSQLStorageConfiguration struct {
|
type PostgreSQLStorageConfiguration struct {
|
||||||
SQLStorageConfiguration `mapstructure:",squash"`
|
SQLStorageConfiguration `mapstructure:",squash"`
|
||||||
SSLMode string `mapstructure:"sslmode"`
|
SSLMode string `mapstructure:"sslmode"`
|
||||||
|
|
|
@ -7,19 +7,19 @@ import (
|
||||||
"github.com/Workiva/go-datastructures/queue"
|
"github.com/Workiva/go-datastructures/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorContainer represents a container where we can add errors and retrieve them
|
// ErrorContainer represents a container where we can add errors and retrieve them.
|
||||||
type ErrorContainer interface {
|
type ErrorContainer interface {
|
||||||
Push(err error)
|
Push(err error)
|
||||||
HasErrors() bool
|
HasErrors() bool
|
||||||
Errors() []error
|
Errors() []error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validator represents the validator interface
|
// Validator represents the validator interface.
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
errors map[string][]error
|
errors map[string][]error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValidator create a validator
|
// NewValidator create a validator.
|
||||||
func NewValidator() *Validator {
|
func NewValidator() *Validator {
|
||||||
validator := new(Validator)
|
validator := new(Validator)
|
||||||
validator.errors = make(map[string][]error)
|
validator.errors = make(map[string][]error)
|
||||||
|
@ -67,7 +67,7 @@ func (v *Validator) validateOne(item QueueItem, q *queue.Queue) error { //nolint
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validate a struct
|
// Validate validate a struct.
|
||||||
func (v *Validator) Validate(s interface{}) error {
|
func (v *Validator) Validate(s interface{}) error {
|
||||||
q := queue.New(40)
|
q := queue.New(40)
|
||||||
q.Put(QueueItem{value: reflect.ValueOf(s), path: "root"}) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
q.Put(QueueItem{value: reflect.ValueOf(s), path: "root"}) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||||
|
@ -86,7 +86,7 @@ func (v *Validator) Validate(s interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintErrors display the errors thrown during validation
|
// PrintErrors display the errors thrown during validation.
|
||||||
func (v *Validator) PrintErrors() {
|
func (v *Validator) PrintErrors() {
|
||||||
for path, errs := range v.errors {
|
for path, errs := range v.errors {
|
||||||
fmt.Printf("Errors at %s:\n", path)
|
fmt.Printf("Errors at %s:\n", path)
|
||||||
|
@ -96,17 +96,17 @@ func (v *Validator) PrintErrors() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errors return the errors thrown during validation
|
// Errors return the errors thrown during validation.
|
||||||
func (v *Validator) Errors() map[string][]error {
|
func (v *Validator) Errors() map[string][]error {
|
||||||
return v.errors
|
return v.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// StructValidator is a validator for structs
|
// StructValidator is a validator for structs.
|
||||||
type StructValidator struct {
|
type StructValidator struct {
|
||||||
errors []error
|
errors []error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStructValidator is a constructor of struct validator
|
// NewStructValidator is a constructor of struct validator.
|
||||||
func NewStructValidator() *StructValidator {
|
func NewStructValidator() *StructValidator {
|
||||||
val := new(StructValidator)
|
val := new(StructValidator)
|
||||||
val.errors = make([]error, 0)
|
val.errors = make([]error, 0)
|
||||||
|
@ -128,7 +128,7 @@ func (v *StructValidator) Errors() []error {
|
||||||
return v.errors
|
return v.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear errors
|
// Clear errors.
|
||||||
func (v *StructValidator) Clear() {
|
func (v *StructValidator) Clear() {
|
||||||
v.errors = []error{}
|
v.errors = []error{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,14 +9,14 @@ import (
|
||||||
"github.com/authelia/authelia/internal/middlewares"
|
"github.com/authelia/authelia/internal/middlewares"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewDuoAPI create duo API instance
|
// NewDuoAPI create duo API instance.
|
||||||
func NewDuoAPI(duoAPI *duoapi.DuoApi) *APIImpl {
|
func NewDuoAPI(duoAPI *duoapi.DuoApi) *APIImpl {
|
||||||
api := new(APIImpl)
|
api := new(APIImpl)
|
||||||
api.DuoApi = duoAPI
|
api.DuoApi = duoAPI
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call call to the DuoAPI
|
// Call call to the DuoAPI.
|
||||||
func (d *APIImpl) Call(values url.Values, ctx *middlewares.AutheliaCtx) (*Response, error) {
|
func (d *APIImpl) Call(values url.Values, ctx *middlewares.AutheliaCtx) (*Response, error) {
|
||||||
_, responseBytes, err := d.DuoApi.SignedCall("POST", "/auth/v2/auth", values)
|
_, responseBytes, err := d.DuoApi.SignedCall("POST", "/auth/v2/auth", values)
|
||||||
|
|
||||||
|
|
|
@ -8,17 +8,17 @@ import (
|
||||||
"github.com/authelia/authelia/internal/middlewares"
|
"github.com/authelia/authelia/internal/middlewares"
|
||||||
)
|
)
|
||||||
|
|
||||||
// API interface wrapping duo api library for testing purpose
|
// API interface wrapping duo api library for testing purpose.
|
||||||
type API interface {
|
type API interface {
|
||||||
Call(values url.Values, ctx *middlewares.AutheliaCtx) (*Response, error)
|
Call(values url.Values, ctx *middlewares.AutheliaCtx) (*Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIImpl implementation of DuoAPI interface
|
// APIImpl implementation of DuoAPI interface.
|
||||||
type APIImpl struct {
|
type APIImpl struct {
|
||||||
*duoapi.DuoApi
|
*duoapi.DuoApi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response response coming from Duo API
|
// Response response coming from Duo API.
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Response struct {
|
Response struct {
|
||||||
Result string `json:"result"`
|
Result string `json:"result"`
|
||||||
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"github.com/authelia/authelia/internal/middlewares"
|
"github.com/authelia/authelia/internal/middlewares"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtendedConfigurationBody the content returned by extended configuration endpoint
|
// ExtendedConfigurationBody the content returned by extended configuration endpoint.
|
||||||
type ExtendedConfigurationBody struct {
|
type ExtendedConfigurationBody struct {
|
||||||
AvailableMethods MethodList `json:"available_methods"`
|
AvailableMethods MethodList `json:"available_methods"`
|
||||||
SecondFactorEnabled bool `json:"second_factor_enabled"` // whether second factor is enabled or not
|
SecondFactorEnabled bool `json:"second_factor_enabled"` // whether second factor is enabled or not.
|
||||||
TOTPPeriod int `json:"totp_period"`
|
TOTPPeriod int `json:"totp_period"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ func secondFactorTOTPIdentityFinish(ctx *middlewares.AutheliaCtx, username strin
|
||||||
ctx.SetJSONBody(response) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
ctx.SetJSONBody(response) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecondFactorTOTPIdentityFinish the handler for finishing the identity validation
|
// SecondFactorTOTPIdentityFinish the handler for finishing the identity validation.
|
||||||
var SecondFactorTOTPIdentityFinish = middlewares.IdentityVerificationFinish(
|
var SecondFactorTOTPIdentityFinish = middlewares.IdentityVerificationFinish(
|
||||||
middlewares.IdentityVerificationFinishArgs{
|
middlewares.IdentityVerificationFinishArgs{
|
||||||
ActionClaim: TOTPRegistrationAction,
|
ActionClaim: TOTPRegistrationAction,
|
||||||
|
|
|
@ -58,7 +58,7 @@ func secondFactorU2FIdentityFinish(ctx *middlewares.AutheliaCtx, username string
|
||||||
ctx.SetJSONBody(u2f.NewWebRegisterRequest(challenge, []u2f.Registration{})) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
ctx.SetJSONBody(u2f.NewWebRegisterRequest(challenge, []u2f.Registration{})) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecondFactorU2FIdentityFinish the handler for finishing the identity validation
|
// SecondFactorU2FIdentityFinish the handler for finishing the identity validation.
|
||||||
var SecondFactorU2FIdentityFinish = middlewares.IdentityVerificationFinish(
|
var SecondFactorU2FIdentityFinish = middlewares.IdentityVerificationFinish(
|
||||||
middlewares.IdentityVerificationFinishArgs{
|
middlewares.IdentityVerificationFinishArgs{
|
||||||
ActionClaim: U2FRegistrationAction,
|
ActionClaim: U2FRegistrationAction,
|
||||||
|
|
|
@ -6,13 +6,13 @@ import (
|
||||||
"github.com/authelia/authelia/internal/middlewares"
|
"github.com/authelia/authelia/internal/middlewares"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResetPasswordPost handler for resetting passwords
|
// ResetPasswordPost handler for resetting passwords.
|
||||||
func ResetPasswordPost(ctx *middlewares.AutheliaCtx) {
|
func ResetPasswordPost(ctx *middlewares.AutheliaCtx) {
|
||||||
userSession := ctx.GetSession()
|
userSession := ctx.GetSession()
|
||||||
|
|
||||||
// Those checks unsure that the identity verification process has been initiated and completed successfully
|
// Those checks unsure that the identity verification process has been initiated and completed successfully
|
||||||
// otherwise PasswordReset would not be set to true. We can improve the security of this check by making the
|
// otherwise PasswordReset would not be set to true. We can improve the security of this check by making the
|
||||||
// request expire at some point because here it only expires when the cookie expires...
|
// request expire at some point because here it only expires when the cookie expires.
|
||||||
if userSession.PasswordResetUsername == nil {
|
if userSession.PasswordResetUsername == nil {
|
||||||
ctx.Error(fmt.Errorf("No identity verification process has been initiated"), unableToResetPasswordMessage)
|
ctx.Error(fmt.Errorf("No identity verification process has been initiated"), unableToResetPasswordMessage)
|
||||||
return
|
return
|
||||||
|
|
|
@ -26,7 +26,7 @@ func isSchemeWSS(url *url.URL) bool {
|
||||||
return url.Scheme == "wss"
|
return url.Scheme == "wss"
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOriginalURL extract the URL from the request headers (X-Original-URI or X-Forwarded-* headers)
|
// getOriginalURL extract the URL from the request headers (X-Original-URI or X-Forwarded-* headers).
|
||||||
func getOriginalURL(ctx *middlewares.AutheliaCtx) (*url.URL, error) {
|
func getOriginalURL(ctx *middlewares.AutheliaCtx) (*url.URL, error) {
|
||||||
originalURL := ctx.XOriginalURL()
|
originalURL := ctx.XOriginalURL()
|
||||||
if originalURL != nil {
|
if originalURL != nil {
|
||||||
|
@ -64,8 +64,8 @@ func getOriginalURL(ctx *middlewares.AutheliaCtx) (*url.URL, error) {
|
||||||
return url, nil
|
return url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseBasicAuth parses an HTTP Basic Authentication string
|
// parseBasicAuth parses an HTTP Basic Authentication string.
|
||||||
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true)
|
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
|
||||||
func parseBasicAuth(auth string) (username, password string, err error) {
|
func parseBasicAuth(auth string) (username, password string, err error) {
|
||||||
if !strings.HasPrefix(auth, authPrefix) {
|
if !strings.HasPrefix(auth, authPrefix) {
|
||||||
return "", "", fmt.Errorf("%s prefix not found in %s header", strings.Trim(authPrefix, " "), AuthorizationHeader)
|
return "", "", fmt.Errorf("%s prefix not found in %s header", strings.Trim(authPrefix, " "), AuthorizationHeader)
|
||||||
|
@ -82,7 +82,7 @@ func parseBasicAuth(auth string) (username, password string, err error) {
|
||||||
return cs[:s], cs[s+1:], nil
|
return cs[:s], cs[s+1:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isTargetURLAuthorized check whether the given user is authorized to access the resource
|
// isTargetURLAuthorized check whether the given user is authorized to access the resource.
|
||||||
func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL url.URL,
|
func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL url.URL,
|
||||||
username string, userGroups []string, clientIP net.IP, authLevel authentication.Level) authorizationMatching {
|
username string, userGroups []string, clientIP net.IP, authLevel authentication.Level) authorizationMatching {
|
||||||
level := authorizer.GetRequiredLevel(authorization.Subject{
|
level := authorizer.GetRequiredLevel(authorization.Subject{
|
||||||
|
@ -114,7 +114,7 @@ func isTargetURLAuthorized(authorizer *authorization.Authorizer, targetURL url.U
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyBasicAuth verify that the provided username and password are correct and
|
// verifyBasicAuth verify that the provided username and password are correct and
|
||||||
// that the user is authorized to target the resource
|
// that the user is authorized to target the resource.
|
||||||
func verifyBasicAuth(auth []byte, targetURL url.URL, ctx *middlewares.AutheliaCtx) (username string, groups []string, authLevel authentication.Level, err error) { //nolint:unparam
|
func verifyBasicAuth(auth []byte, targetURL url.URL, ctx *middlewares.AutheliaCtx) (username string, groups []string, authLevel authentication.Level, err error) { //nolint:unparam
|
||||||
username, password, err := parseBasicAuth(string(auth))
|
username, password, err := parseBasicAuth(string(auth))
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ func verifyBasicAuth(auth []byte, targetURL url.URL, ctx *middlewares.AutheliaCt
|
||||||
return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to check credentials extracted from %s header: %s", AuthorizationHeader, err)
|
return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to check credentials extracted from %s header: %s", AuthorizationHeader, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user is not correctly authenticated, send a 401
|
// If the user is not correctly authenticated, send a 401.
|
||||||
if !authenticated {
|
if !authenticated {
|
||||||
// Request Basic Authentication otherwise
|
// Request Basic Authentication otherwise
|
||||||
return "", nil, authentication.NotAuthenticated, fmt.Errorf("User %s is not authenticated", username)
|
return "", nil, authentication.NotAuthenticated, fmt.Errorf("User %s is not authenticated", username)
|
||||||
|
@ -143,7 +143,7 @@ func verifyBasicAuth(auth []byte, targetURL url.URL, ctx *middlewares.AutheliaCt
|
||||||
return username, details.Groups, authentication.OneFactor, nil
|
return username, details.Groups, authentication.OneFactor, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setForwardedHeaders set the forwarded User and Groups headers
|
// setForwardedHeaders set the forwarded User and Groups headers.
|
||||||
func setForwardedHeaders(headers *fasthttp.ResponseHeader, username string, groups []string) {
|
func setForwardedHeaders(headers *fasthttp.ResponseHeader, username string, groups []string) {
|
||||||
if username != "" {
|
if username != "" {
|
||||||
headers.Set(remoteUserHeader, username)
|
headers.Set(remoteUserHeader, username)
|
||||||
|
@ -151,7 +151,7 @@ func setForwardedHeaders(headers *fasthttp.ResponseHeader, username string, grou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasUserBeenInactiveLongEnough check whether the user has been inactive for too long
|
// hasUserBeenInactiveLongEnough check whether the user has been inactive for too long.
|
||||||
func hasUserBeenInactiveLongEnough(ctx *middlewares.AutheliaCtx) (bool, error) { //nolint:unparam
|
func hasUserBeenInactiveLongEnough(ctx *middlewares.AutheliaCtx) (bool, error) { //nolint:unparam
|
||||||
maxInactivityPeriod := int64(ctx.Providers.SessionProvider.Inactivity.Seconds())
|
maxInactivityPeriod := int64(ctx.Providers.SessionProvider.Inactivity.Seconds())
|
||||||
if maxInactivityPeriod == 0 {
|
if maxInactivityPeriod == 0 {
|
||||||
|
@ -171,10 +171,10 @@ func hasUserBeenInactiveLongEnough(ctx *middlewares.AutheliaCtx) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyFromSessionCookie verify if a user identified by a cookie is allowed to access target URL
|
// verifyFromSessionCookie verify if a user identified by a cookie is allowed to access target URL.
|
||||||
func verifyFromSessionCookie(targetURL url.URL, ctx *middlewares.AutheliaCtx) (username string, groups []string, authLevel authentication.Level, err error) { //nolint:unparam
|
func verifyFromSessionCookie(targetURL url.URL, ctx *middlewares.AutheliaCtx) (username string, groups []string, authLevel authentication.Level, err error) { //nolint:unparam
|
||||||
userSession := ctx.GetSession()
|
userSession := ctx.GetSession()
|
||||||
// No username in the session means the user is anonymous
|
// No username in the session means the user is anonymous.
|
||||||
isUserAnonymous := userSession.Username == ""
|
isUserAnonymous := userSession.Username == ""
|
||||||
|
|
||||||
if isUserAnonymous && userSession.AuthenticationLevel != authentication.NotAuthenticated {
|
if isUserAnonymous && userSession.AuthenticationLevel != authentication.NotAuthenticated {
|
||||||
|
@ -188,7 +188,7 @@ func verifyFromSessionCookie(targetURL url.URL, ctx *middlewares.AutheliaCtx) (u
|
||||||
}
|
}
|
||||||
|
|
||||||
if inactiveLongEnough {
|
if inactiveLongEnough {
|
||||||
// Destroy the session a new one will be regenerated on next request
|
// Destroy the session a new one will be regenerated on next request.
|
||||||
err := ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
|
err := ctx.Providers.SessionProvider.DestroySession(ctx.RequestCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to destroy user session after long inactivity: %s", err)
|
return "", nil, authentication.NotAuthenticated, fmt.Errorf("Unable to destroy user session after long inactivity: %s", err)
|
||||||
|
@ -203,7 +203,7 @@ func verifyFromSessionCookie(targetURL url.URL, ctx *middlewares.AutheliaCtx) (u
|
||||||
func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, username string) {
|
func handleUnauthorized(ctx *middlewares.AutheliaCtx, targetURL fmt.Stringer, username string) {
|
||||||
// Kubernetes ingress controller and Traefik use the rd parameter of the verify
|
// Kubernetes ingress controller and Traefik use the rd parameter of the verify
|
||||||
// endpoint to provide the URL of the login portal. The target URL of the user
|
// endpoint to provide the URL of the login portal. The target URL of the user
|
||||||
// is computed from X-Fowarded-* headers or X-Original-URL
|
// is computed from X-Fowarded-* headers or X-Original-URL.
|
||||||
rd := string(ctx.QueryArgs().Peek("rd"))
|
rd := string(ctx.QueryArgs().Peek("rd"))
|
||||||
if rd != "" {
|
if rd != "" {
|
||||||
redirectionURL := fmt.Sprintf("%s?rd=%s", rd, url.QueryEscape(targetURL.String()))
|
redirectionURL := fmt.Sprintf("%s?rd=%s", rd, url.QueryEscape(targetURL.String()))
|
||||||
|
@ -230,12 +230,12 @@ func updateActivityTimestamp(ctx *middlewares.AutheliaCtx, isBasicAuth bool, use
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark current activity
|
// Mark current activity.
|
||||||
userSession.LastActivity = ctx.Clock.Now().Unix()
|
userSession.LastActivity = ctx.Clock.Now().Unix()
|
||||||
return ctx.SaveSession(userSession)
|
return ctx.SaveSession(userSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyGet is the handler verifying if a request is allowed to go through
|
// VerifyGet is the handler verifying if a request is allowed to go through.
|
||||||
func VerifyGet(ctx *middlewares.AutheliaCtx) {
|
func VerifyGet(ctx *middlewares.AutheliaCtx) {
|
||||||
ctx.Logger.Tracef("Headers=%s", ctx.Request.Header.String())
|
ctx.Logger.Tracef("Headers=%s", ctx.Request.Header.String())
|
||||||
targetURL, err := getOriginalURL(ctx)
|
targetURL, err := getOriginalURL(ctx)
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"github.com/authelia/authelia/internal/session"
|
"github.com/authelia/authelia/internal/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test getOriginalURL
|
// Test getOriginalURL.
|
||||||
func TestShouldGetOriginalURLFromOriginalURLHeader(t *testing.T) {
|
func TestShouldGetOriginalURLFromOriginalURLHeader(t *testing.T) {
|
||||||
mock := mocks.NewMockAutheliaCtx(t)
|
mock := mocks.NewMockAutheliaCtx(t)
|
||||||
defer mock.Close()
|
defer mock.Close()
|
||||||
|
@ -110,7 +110,7 @@ func TestShouldRaiseWhenXForwardedURIIsNotParseable(t *testing.T) {
|
||||||
assert.Equal(t, "Unable to parse URL https://myhost.local!:;;:,: parse https://myhost.local!:;;:,: invalid port \":,\" after host", err.Error())
|
assert.Equal(t, "Unable to parse URL https://myhost.local!:;;:,: parse https://myhost.local!:;;:,: invalid port \":,\" after host", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test parseBasicAuth
|
// Test parseBasicAuth.
|
||||||
func TestShouldRaiseWhenHeaderDoesNotContainBasicPrefix(t *testing.T) {
|
func TestShouldRaiseWhenHeaderDoesNotContainBasicPrefix(t *testing.T) {
|
||||||
_, _, err := parseBasicAuth("alzefzlfzemjfej==")
|
_, _, err := parseBasicAuth("alzefzlfzemjfej==")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
@ -138,7 +138,7 @@ func TestShouldReturnUsernameAndPassword(t *testing.T) {
|
||||||
assert.Equal(t, "password", password)
|
assert.Equal(t, "password", password)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test isTargetURLAuthorized
|
// Test isTargetURLAuthorized.
|
||||||
func TestShouldCheckAuthorizationMatching(t *testing.T) {
|
func TestShouldCheckAuthorizationMatching(t *testing.T) {
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
Policy string
|
Policy string
|
||||||
|
@ -185,7 +185,7 @@ func TestShouldCheckAuthorizationMatching(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test verifyBasicAuth
|
// Test verifyBasicAuth.
|
||||||
func TestShouldVerifyWrongCredentials(t *testing.T) {
|
func TestShouldVerifyWrongCredentials(t *testing.T) {
|
||||||
mock := mocks.NewMockAutheliaCtx(t)
|
mock := mocks.NewMockAutheliaCtx(t)
|
||||||
defer mock.Close()
|
defer mock.Close()
|
||||||
|
@ -473,7 +473,7 @@ func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) {
|
||||||
past := clock.Now().Add(-1 * time.Hour)
|
past := clock.Now().Add(-1 * time.Hour)
|
||||||
|
|
||||||
mock.Ctx.Configuration.Session.Inactivity = "10"
|
mock.Ctx.Configuration.Session.Inactivity = "10"
|
||||||
// Reload the session provider since the configuration is indirect
|
// Reload the session provider since the configuration is indirect.
|
||||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session)
|
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session)
|
||||||
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
||||||
|
|
||||||
|
@ -487,7 +487,7 @@ func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) {
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
|
||||||
// The session has been destroyed
|
// The session has been destroyed.
|
||||||
newUserSession := mock.Ctx.GetSession()
|
newUserSession := mock.Ctx.GetSession()
|
||||||
assert.Equal(t, "", newUserSession.Username)
|
assert.Equal(t, "", newUserSession.Username)
|
||||||
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
|
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
|
||||||
|
@ -504,7 +504,7 @@ func TestShouldDestroySessionWhenInactiveForTooLongUsingDurationNotation(t *test
|
||||||
clock.Set(time.Now())
|
clock.Set(time.Now())
|
||||||
|
|
||||||
mock.Ctx.Configuration.Session.Inactivity = "10s"
|
mock.Ctx.Configuration.Session.Inactivity = "10s"
|
||||||
// Reload the session provider since the configuration is indirect
|
// Reload the session provider since the configuration is indirect.
|
||||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session)
|
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session)
|
||||||
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
||||||
|
|
||||||
|
@ -518,7 +518,7 @@ func TestShouldDestroySessionWhenInactiveForTooLongUsingDurationNotation(t *test
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
|
||||||
// The session has been destroyed
|
// The session has been destroyed.
|
||||||
newUserSession := mock.Ctx.GetSession()
|
newUserSession := mock.Ctx.GetSession()
|
||||||
assert.Equal(t, "", newUserSession.Username)
|
assert.Equal(t, "", newUserSession.Username)
|
||||||
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
|
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
|
||||||
|
@ -544,7 +544,7 @@ func TestShouldKeepSessionWhenUserCheckedRememberMeAndIsInactiveForTooLong(t *te
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
|
||||||
// The session has been destroyed
|
// The session has been destroyed.
|
||||||
newUserSession := mock.Ctx.GetSession()
|
newUserSession := mock.Ctx.GetSession()
|
||||||
assert.Equal(t, "john", newUserSession.Username)
|
assert.Equal(t, "john", newUserSession.Username)
|
||||||
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
|
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
|
||||||
|
@ -574,7 +574,7 @@ func TestShouldKeepSessionWhenInactivityTimeoutHasNotBeenExceeded(t *testing.T)
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
|
||||||
// The session has been destroyed
|
// The session has been destroyed.
|
||||||
newUserSession := mock.Ctx.GetSession()
|
newUserSession := mock.Ctx.GetSession()
|
||||||
assert.Equal(t, "john", newUserSession.Username)
|
assert.Equal(t, "john", newUserSession.Username)
|
||||||
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
|
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
|
||||||
|
@ -593,7 +593,7 @@ func TestShouldRedirectWhenSessionInactiveForTooLongAndRDParamProvided(t *testin
|
||||||
clock.Set(time.Now())
|
clock.Set(time.Now())
|
||||||
|
|
||||||
mock.Ctx.Configuration.Session.Inactivity = "10"
|
mock.Ctx.Configuration.Session.Inactivity = "10"
|
||||||
// Reload the session provider since the configuration is indirect
|
// Reload the session provider since the configuration is indirect.
|
||||||
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session)
|
mock.Ctx.Providers.SessionProvider = session.NewProvider(mock.Ctx.Configuration.Session)
|
||||||
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
||||||
|
|
||||||
|
@ -640,7 +640,7 @@ func TestShouldUpdateInactivityTimestampEvenWhenHittingForbiddenResources(t *tes
|
||||||
|
|
||||||
VerifyGet(mock.Ctx)
|
VerifyGet(mock.Ctx)
|
||||||
|
|
||||||
// The resource if forbidden
|
// The resource if forbidden.
|
||||||
assert.Equal(t, 403, mock.Ctx.Response.StatusCode())
|
assert.Equal(t, 403, mock.Ctx.Response.StatusCode())
|
||||||
|
|
||||||
// Check the inactivity timestamp has been updated to current time in the new session.
|
// Check the inactivity timestamp has been updated to current time in the new session.
|
||||||
|
@ -683,8 +683,8 @@ func TestIsDomainProtected(t *testing.T) {
|
||||||
assert.True(t, isURLUnderProtectedDomain(
|
assert.True(t, isURLUnderProtectedDomain(
|
||||||
GetURL("https://mytest.example.com/abc/?query=abc"), "example.com"))
|
GetURL("https://mytest.example.com/abc/?query=abc"), "example.com"))
|
||||||
|
|
||||||
// cookies readable by a service on a machine is also readable by a service on the same machine
|
// Cookies readable by a service on a machine is also readable by a service on the same machine
|
||||||
// with a different port as mentioned in https://tools.ietf.org/html/rfc6265#section-8.5
|
// with a different port as mentioned in https://tools.ietf.org/html/rfc6265#section-8.5.
|
||||||
assert.True(t, isURLUnderProtectedDomain(
|
assert.True(t, isURLUnderProtectedDomain(
|
||||||
GetURL("https://mytest.example.com:8080/abc/?query=abc"), "example.com"))
|
GetURL("https://mytest.example.com:8080/abc/?query=abc"), "example.com"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handle1FAResponse handle the redirection upon 1FA authentication
|
// Handle1FAResponse handle the redirection upon 1FA authentication.
|
||||||
func Handle1FAResponse(ctx *middlewares.AutheliaCtx, targetURI string, username string, groups []string) {
|
func Handle1FAResponse(ctx *middlewares.AutheliaCtx, targetURI string, username string, groups []string) {
|
||||||
if targetURI == "" {
|
if targetURI == "" {
|
||||||
if !ctx.Providers.Authorizer.IsSecondFactorEnabled() && ctx.Configuration.DefaultRedirectionURL != "" {
|
if !ctx.Providers.Authorizer.IsSecondFactorEnabled() && ctx.Configuration.DefaultRedirectionURL != "" {
|
||||||
|
@ -56,7 +56,7 @@ func Handle1FAResponse(ctx *middlewares.AutheliaCtx, targetURI string, username
|
||||||
ctx.SetJSONBody(response) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
ctx.SetJSONBody(response) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle2FAResponse handle the redirection upon 2FA authentication
|
// Handle2FAResponse handle the redirection upon 2FA authentication.
|
||||||
func Handle2FAResponse(ctx *middlewares.AutheliaCtx, targetURI string) {
|
func Handle2FAResponse(ctx *middlewares.AutheliaCtx, targetURI string) {
|
||||||
if targetURI == "" {
|
if targetURI == "" {
|
||||||
if ctx.Configuration.DefaultRedirectionURL != "" {
|
if ctx.Configuration.DefaultRedirectionURL != "" {
|
||||||
|
|
|
@ -17,7 +17,7 @@ func SetLevel(level logrus.Level) {
|
||||||
logrus.SetLevel(level)
|
logrus.SetLevel(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitializeLogger initialize logger
|
// InitializeLogger initialize logger.
|
||||||
func InitializeLogger(filename string) error {
|
func InitializeLogger(filename string) error {
|
||||||
callerLevels := []logrus.Level{}
|
callerLevels := []logrus.Level{}
|
||||||
stackLevels := []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel}
|
stackLevels := []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel}
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (c *AutheliaCtx) Error(err error, message string) {
|
||||||
c.Logger.Error(err)
|
c.Logger.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplyError reply with an error but does not display any stack trace in the logs
|
// ReplyError reply with an error but does not display any stack trace in the logs.
|
||||||
func (c *AutheliaCtx) ReplyError(err error, message string) {
|
func (c *AutheliaCtx) ReplyError(err error, message string) {
|
||||||
b, marshalErr := json.Marshal(ErrorResponse{Status: "KO", Message: message})
|
b, marshalErr := json.Marshal(ErrorResponse{Status: "KO", Message: message})
|
||||||
|
|
||||||
|
@ -75,33 +75,33 @@ func (c *AutheliaCtx) ReplyError(err error, message string) {
|
||||||
c.Logger.Debug(err)
|
c.Logger.Debug(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplyUnauthorized response sent when user is unauthorized
|
// ReplyUnauthorized response sent when user is unauthorized.
|
||||||
func (c *AutheliaCtx) ReplyUnauthorized() {
|
func (c *AutheliaCtx) ReplyUnauthorized() {
|
||||||
c.RequestCtx.Error(fasthttp.StatusMessage(fasthttp.StatusUnauthorized), fasthttp.StatusUnauthorized)
|
c.RequestCtx.Error(fasthttp.StatusMessage(fasthttp.StatusUnauthorized), fasthttp.StatusUnauthorized)
|
||||||
// c.Response.Header.Set("WWW-Authenticate", "Basic realm=Restricted")
|
// c.Response.Header.Set("WWW-Authenticate", "Basic realm=Restricted")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplyForbidden response sent when access is forbidden to user
|
// ReplyForbidden response sent when access is forbidden to user.
|
||||||
func (c *AutheliaCtx) ReplyForbidden() {
|
func (c *AutheliaCtx) ReplyForbidden() {
|
||||||
c.RequestCtx.Error(fasthttp.StatusMessage(fasthttp.StatusForbidden), fasthttp.StatusForbidden)
|
c.RequestCtx.Error(fasthttp.StatusMessage(fasthttp.StatusForbidden), fasthttp.StatusForbidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XForwardedProto return the content of the header X-Forwarded-Proto
|
// XForwardedProto return the content of the header X-Forwarded-Proto.
|
||||||
func (c *AutheliaCtx) XForwardedProto() []byte {
|
func (c *AutheliaCtx) XForwardedProto() []byte {
|
||||||
return c.RequestCtx.Request.Header.Peek(xForwardedProtoHeader)
|
return c.RequestCtx.Request.Header.Peek(xForwardedProtoHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XForwardedHost return the content of the header X-Forwarded-Host
|
// XForwardedHost return the content of the header X-Forwarded-Host.
|
||||||
func (c *AutheliaCtx) XForwardedHost() []byte {
|
func (c *AutheliaCtx) XForwardedHost() []byte {
|
||||||
return c.RequestCtx.Request.Header.Peek(xForwardedHostHeader)
|
return c.RequestCtx.Request.Header.Peek(xForwardedHostHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XForwardedURI return the content of the header X-Forwarded-URI
|
// XForwardedURI return the content of the header X-Forwarded-URI.
|
||||||
func (c *AutheliaCtx) XForwardedURI() []byte {
|
func (c *AutheliaCtx) XForwardedURI() []byte {
|
||||||
return c.RequestCtx.Request.Header.Peek(xForwardedURIHeader)
|
return c.RequestCtx.Request.Header.Peek(xForwardedURIHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XOriginalURL return the content of the header X-Original-URL
|
// XOriginalURL return the content of the header X-Original-URL.
|
||||||
func (c *AutheliaCtx) XOriginalURL() []byte {
|
func (c *AutheliaCtx) XOriginalURL() []byte {
|
||||||
return c.RequestCtx.Request.Header.Peek(xOriginalURLHeader)
|
return c.RequestCtx.Request.Header.Peek(xOriginalURLHeader)
|
||||||
}
|
}
|
||||||
|
@ -121,13 +121,13 @@ func (c *AutheliaCtx) SaveSession(userSession session.UserSession) error {
|
||||||
return c.Providers.SessionProvider.SaveSession(c.RequestCtx, userSession)
|
return c.Providers.SessionProvider.SaveSession(c.RequestCtx, userSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplyOK is a helper method to reply ok
|
// ReplyOK is a helper method to reply ok.
|
||||||
func (c *AutheliaCtx) ReplyOK() {
|
func (c *AutheliaCtx) ReplyOK() {
|
||||||
c.SetContentType(applicationJSONContentType)
|
c.SetContentType(applicationJSONContentType)
|
||||||
c.SetBody(okMessageBytes)
|
c.SetBody(okMessageBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseBody parse the request body into the type of value
|
// ParseBody parse the request body into the type of value.
|
||||||
func (c *AutheliaCtx) ParseBody(value interface{}) error {
|
func (c *AutheliaCtx) ParseBody(value interface{}) error {
|
||||||
err := json.Unmarshal(c.PostBody(), &value)
|
err := json.Unmarshal(c.PostBody(), &value)
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ func (c *AutheliaCtx) ParseBody(value interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetJSONBody Set json body
|
// SetJSONBody Set json body.
|
||||||
func (c *AutheliaCtx) SetJSONBody(value interface{}) error {
|
func (c *AutheliaCtx) SetJSONBody(value interface{}) error {
|
||||||
b, err := json.Marshal(OKResponse{Status: "OK", Data: value})
|
b, err := json.Marshal(OKResponse{Status: "OK", Data: value})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package middlewares
|
package middlewares
|
||||||
|
|
||||||
// JWTIssuer is
|
// JWTIssuer is.
|
||||||
const jwtIssuer = "Authelia"
|
const jwtIssuer = "Authelia"
|
||||||
|
|
||||||
const xForwardedProtoHeader = "X-Forwarded-Proto"
|
const xForwardedProtoHeader = "X-Forwarded-Proto"
|
||||||
|
|
|
@ -144,8 +144,7 @@ func TestShouldSucceedIdentityVerificationStartProcess(t *testing.T) {
|
||||||
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
|
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test Finish process
|
// Test Finish process.
|
||||||
|
|
||||||
type IdentityVerificationFinishProcess struct {
|
type IdentityVerificationFinishProcess struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
|
|
|
@ -54,23 +54,23 @@ type IdentityVerificationStartArgs struct {
|
||||||
// is completed successfully.
|
// is completed successfully.
|
||||||
TargetEndpoint string
|
TargetEndpoint string
|
||||||
|
|
||||||
// The action claim that will be stored in the JWT token
|
// The action claim that will be stored in the JWT token.
|
||||||
ActionClaim string
|
ActionClaim string
|
||||||
|
|
||||||
// The function retrieving the identity to who the email will be sent.
|
// The function retrieving the identity to who the email will be sent.
|
||||||
IdentityRetrieverFunc func(ctx *AutheliaCtx) (*session.Identity, error)
|
IdentityRetrieverFunc func(ctx *AutheliaCtx) (*session.Identity, error)
|
||||||
|
|
||||||
// The function for checking the user in the token is valid for the current action
|
// The function for checking the user in the token is valid for the current action.
|
||||||
IsTokenUserValidFunc func(ctx *AutheliaCtx, username string) bool
|
IsTokenUserValidFunc func(ctx *AutheliaCtx, username string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// IdentityVerificationFinishArgs represent the arguments used to customize the finishing phase
|
// IdentityVerificationFinishArgs represent the arguments used to customize the finishing phase
|
||||||
// of the identity verification process.
|
// of the identity verification process.
|
||||||
type IdentityVerificationFinishArgs struct {
|
type IdentityVerificationFinishArgs struct {
|
||||||
// The action claim that should be in the token to consider the action legitimate
|
// The action claim that should be in the token to consider the action legitimate.
|
||||||
ActionClaim string
|
ActionClaim string
|
||||||
|
|
||||||
// The function for checking the user in the token is valid for the current action
|
// The function for checking the user in the token is valid for the current action.
|
||||||
IsTokenUserValidFunc func(ctx *AutheliaCtx, username string) bool
|
IsTokenUserValidFunc func(ctx *AutheliaCtx, username string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,13 +90,13 @@ type IdentityVerificationFinishBody struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OKResponse model of a status OK response
|
// OKResponse model of a status OK response.
|
||||||
type OKResponse struct {
|
type OKResponse struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Data interface{} `json:"data,omitempty"`
|
Data interface{} `json:"data,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorResponse model of an error response
|
// ErrorResponse model of an error response.
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
|
|
@ -22,7 +22,7 @@ func NewFileNotifier(configuration schema.FileSystemNotifierConfiguration) *File
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartupCheck checks the file provider can write to the specified file
|
// StartupCheck checks the file provider can write to the specified file.
|
||||||
func (n *FileNotifier) StartupCheck() (bool, error) {
|
func (n *FileNotifier) StartupCheck() (bool, error) {
|
||||||
dir := filepath.Dir(n.path)
|
dir := filepath.Dir(n.path)
|
||||||
if _, err := os.Stat(dir); err != nil {
|
if _, err := os.Stat(dir); err != nil {
|
||||||
|
|
|
@ -2,5 +2,5 @@ package regulation
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// ErrUserIsBanned user is banned error message
|
// ErrUserIsBanned user is banned error message.
|
||||||
var ErrUserIsBanned = fmt.Errorf("User is banned")
|
var ErrUserIsBanned = fmt.Errorf("User is banned")
|
||||||
|
|
|
@ -38,7 +38,7 @@ func NewRegulator(configuration *schema.RegulationConfiguration, provider storag
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark mark an authentication attempt.
|
// Mark mark an authentication attempt.
|
||||||
// We split Mark and Regulate in order to avoid timing attacks since if
|
// We split Mark and Regulate in order to avoid timing attacks.
|
||||||
func (r *Regulator) Mark(username string, successful bool) error {
|
func (r *Regulator) Mark(username string, successful bool) error {
|
||||||
return r.storageProvider.AppendAuthenticationLog(models.AuthenticationAttempt{
|
return r.storageProvider.AppendAuthenticationLog(models.AuthenticationAttempt{
|
||||||
Username: username,
|
Username: username,
|
||||||
|
|
|
@ -14,13 +14,13 @@ type EncryptingSerializer struct {
|
||||||
key [32]byte
|
key [32]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEncryptingSerializer return new encrypt instance
|
// NewEncryptingSerializer return new encrypt instance.
|
||||||
func NewEncryptingSerializer(secret string) *EncryptingSerializer {
|
func NewEncryptingSerializer(secret string) *EncryptingSerializer {
|
||||||
key := sha256.Sum256([]byte(secret))
|
key := sha256.Sum256([]byte(secret))
|
||||||
return &EncryptingSerializer{key}
|
return &EncryptingSerializer{key}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encode and encrypt session
|
// Encode encode and encrypt session.
|
||||||
func (e *EncryptingSerializer) Encode(src session.Dict) ([]byte, error) {
|
func (e *EncryptingSerializer) Encode(src session.Dict) ([]byte, error) {
|
||||||
if len(src.D) == 0 {
|
if len(src.D) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -39,7 +39,7 @@ func (e *EncryptingSerializer) Encode(src session.Dict) ([]byte, error) {
|
||||||
return encryptedDst, nil
|
return encryptedDst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decrypt and decode session
|
// Decode decrypt and decode session.
|
||||||
func (e *EncryptingSerializer) Decode(dst *session.Dict, src []byte) error {
|
func (e *EncryptingSerializer) Decode(dst *session.Dict, src []byte) error {
|
||||||
if len(src) == 0 {
|
if len(src) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -44,7 +44,7 @@ func NewProvider(configuration schema.SessionConfiguration) *Provider {
|
||||||
return provider
|
return provider
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSession return the user session from a request
|
// GetSession return the user session from a request.
|
||||||
func (p *Provider) GetSession(ctx *fasthttp.RequestCtx) (UserSession, error) {
|
func (p *Provider) GetSession(ctx *fasthttp.RequestCtx) (UserSession, error) {
|
||||||
store, err := p.sessionHolder.Get(ctx)
|
store, err := p.sessionHolder.Get(ctx)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewProviderConfig creates a configuration for creating the session provider
|
// NewProviderConfig creates a configuration for creating the session provider.
|
||||||
func NewProviderConfig(configuration schema.SessionConfiguration) ProviderConfig {
|
func NewProviderConfig(configuration schema.SessionConfiguration) ProviderConfig {
|
||||||
config := session.NewDefaultConfig()
|
config := session.NewDefaultConfig()
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ func NewProviderConfig(configuration schema.SessionConfiguration) ProviderConfig
|
||||||
// Only serve the header over HTTPS.
|
// Only serve the header over HTTPS.
|
||||||
config.Secure = true
|
config.Secure = true
|
||||||
|
|
||||||
// Ignore the error as it will be handled by validator
|
// Ignore the error as it will be handled by validator.
|
||||||
config.Expires, _ = utils.ParseDurationString(configuration.Expiration)
|
config.Expires, _ = utils.ParseDurationString(configuration.Expiration)
|
||||||
|
|
||||||
// TODO(c.michaud): Make this configurable by giving the list of IPs that are trustable.
|
// TODO(c.michaud): Make this configurable by giving the list of IPs that are trustable.
|
||||||
|
@ -42,7 +42,7 @@ func NewProviderConfig(configuration schema.SessionConfiguration) ProviderConfig
|
||||||
Host: configuration.Redis.Host,
|
Host: configuration.Redis.Host,
|
||||||
Port: configuration.Redis.Port,
|
Port: configuration.Redis.Port,
|
||||||
Password: configuration.Redis.Password,
|
Password: configuration.Redis.Password,
|
||||||
// DbNumber is the fasthttp/session property for the Redis DB Index
|
// DbNumber is the fasthttp/session property for the Redis DB Index.
|
||||||
DbNumber: configuration.Redis.DatabaseIndex,
|
DbNumber: configuration.Redis.DatabaseIndex,
|
||||||
PoolSize: 8,
|
PoolSize: 8,
|
||||||
IdleTimeout: 300,
|
IdleTimeout: 300,
|
||||||
|
|
|
@ -14,7 +14,7 @@ type ProviderConfig struct {
|
||||||
providerConfig session.ProviderConfig
|
providerConfig session.ProviderConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// U2FRegistration is a serializable version of a U2F registration
|
// U2FRegistration is a serializable version of a U2F registration.
|
||||||
type U2FRegistration struct {
|
type U2FRegistration struct {
|
||||||
KeyHandle []byte
|
KeyHandle []byte
|
||||||
PublicKey []byte
|
PublicKey []byte
|
||||||
|
|
|
@ -9,24 +9,24 @@ var totpSecretsTableName = "totp_secrets"
|
||||||
var u2fDeviceHandlesTableName = "u2f_devices"
|
var u2fDeviceHandlesTableName = "u2f_devices"
|
||||||
var authenticationLogsTableName = "authentication_logs"
|
var authenticationLogsTableName = "authentication_logs"
|
||||||
|
|
||||||
// SQLCreateUserPreferencesTable common SQL query to create user_preferences table
|
// SQLCreateUserPreferencesTable common SQL query to create user_preferences table.
|
||||||
var SQLCreateUserPreferencesTable = fmt.Sprintf(`
|
var SQLCreateUserPreferencesTable = fmt.Sprintf(`
|
||||||
CREATE TABLE IF NOT EXISTS %s (
|
CREATE TABLE IF NOT EXISTS %s (
|
||||||
username VARCHAR(100) PRIMARY KEY,
|
username VARCHAR(100) PRIMARY KEY,
|
||||||
second_factor_method VARCHAR(11)
|
second_factor_method VARCHAR(11)
|
||||||
)`, preferencesTableName)
|
)`, preferencesTableName)
|
||||||
|
|
||||||
// SQLCreateIdentityVerificationTokensTable common SQL query to create identity_verification_tokens table
|
// SQLCreateIdentityVerificationTokensTable common SQL query to create identity_verification_tokens table.
|
||||||
var SQLCreateIdentityVerificationTokensTable = fmt.Sprintf(`
|
var SQLCreateIdentityVerificationTokensTable = fmt.Sprintf(`
|
||||||
CREATE TABLE IF NOT EXISTS %s (token VARCHAR(512))
|
CREATE TABLE IF NOT EXISTS %s (token VARCHAR(512))
|
||||||
`, identityVerificationTokensTableName)
|
`, identityVerificationTokensTableName)
|
||||||
|
|
||||||
// SQLCreateTOTPSecretsTable common SQL query to create totp_secrets table
|
// SQLCreateTOTPSecretsTable common SQL query to create totp_secrets table.
|
||||||
var SQLCreateTOTPSecretsTable = fmt.Sprintf(`
|
var SQLCreateTOTPSecretsTable = fmt.Sprintf(`
|
||||||
CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100) PRIMARY KEY, secret VARCHAR(64))
|
CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100) PRIMARY KEY, secret VARCHAR(64))
|
||||||
`, totpSecretsTableName)
|
`, totpSecretsTableName)
|
||||||
|
|
||||||
// SQLCreateU2FDeviceHandlesTable common SQL query to create u2f_device_handles table
|
// SQLCreateU2FDeviceHandlesTable common SQL query to create u2f_device_handles table.
|
||||||
var SQLCreateU2FDeviceHandlesTable = fmt.Sprintf(`
|
var SQLCreateU2FDeviceHandlesTable = fmt.Sprintf(`
|
||||||
CREATE TABLE IF NOT EXISTS %s (
|
CREATE TABLE IF NOT EXISTS %s (
|
||||||
username VARCHAR(100) PRIMARY KEY,
|
username VARCHAR(100) PRIMARY KEY,
|
||||||
|
@ -34,7 +34,7 @@ CREATE TABLE IF NOT EXISTS %s (
|
||||||
publicKey TEXT
|
publicKey TEXT
|
||||||
)`, u2fDeviceHandlesTableName)
|
)`, u2fDeviceHandlesTableName)
|
||||||
|
|
||||||
// SQLCreateAuthenticationLogsTable common SQL query to create authentication_logs table
|
// SQLCreateAuthenticationLogsTable common SQL query to create authentication_logs table.
|
||||||
var SQLCreateAuthenticationLogsTable = fmt.Sprintf(`
|
var SQLCreateAuthenticationLogsTable = fmt.Sprintf(`
|
||||||
CREATE TABLE IF NOT EXISTS %s (
|
CREATE TABLE IF NOT EXISTS %s (
|
||||||
username VARCHAR(100),
|
username VARCHAR(100),
|
||||||
|
|
|
@ -10,12 +10,12 @@ import (
|
||||||
"github.com/authelia/authelia/internal/logging"
|
"github.com/authelia/authelia/internal/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MySQLProvider is a MySQL provider
|
// MySQLProvider is a MySQL provider.
|
||||||
type MySQLProvider struct {
|
type MySQLProvider struct {
|
||||||
SQLProvider
|
SQLProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMySQLProvider a MySQL provider
|
// NewMySQLProvider a MySQL provider.
|
||||||
func NewMySQLProvider(configuration schema.MySQLStorageConfiguration) *MySQLProvider {
|
func NewMySQLProvider(configuration schema.MySQLStorageConfiguration) *MySQLProvider {
|
||||||
connectionString := configuration.Username
|
connectionString := configuration.Username
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,12 @@ import (
|
||||||
"github.com/authelia/authelia/internal/logging"
|
"github.com/authelia/authelia/internal/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PostgreSQLProvider is a Postrgres provider
|
// PostgreSQLProvider is a PostgreSQL provider.
|
||||||
type PostgreSQLProvider struct {
|
type PostgreSQLProvider struct {
|
||||||
SQLProvider
|
SQLProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPostgreSQLProvider a SQL provider
|
// NewPostgreSQLProvider a PostgreSQL provider.
|
||||||
func NewPostgreSQLProvider(configuration schema.PostgreSQLStorageConfiguration) *PostgreSQLProvider {
|
func NewPostgreSQLProvider(configuration schema.PostgreSQLStorageConfiguration) *PostgreSQLProvider {
|
||||||
args := make([]string, 0)
|
args := make([]string, 0)
|
||||||
if configuration.Username != "" {
|
if configuration.Username != "" {
|
||||||
|
|
|
@ -9,12 +9,12 @@ import (
|
||||||
"github.com/authelia/authelia/internal/logging"
|
"github.com/authelia/authelia/internal/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SQLiteProvider is a sqlite3 provider
|
// SQLiteProvider is a SQLite3 provider.
|
||||||
type SQLiteProvider struct {
|
type SQLiteProvider struct {
|
||||||
SQLProvider
|
SQLProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSQLiteProvider construct a sqlite provider.
|
// NewSQLiteProvider constructs a SQLite provider.
|
||||||
func NewSQLiteProvider(path string) *SQLiteProvider {
|
func NewSQLiteProvider(path string) *SQLiteProvider {
|
||||||
db, err := sql.Open("sqlite3", path)
|
db, err := sql.Open("sqlite3", path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -28,13 +28,13 @@ func (wds *WebDriverSession) doFillLoginPageAndClick(ctx context.Context, t *tes
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login 1FA
|
// Login 1FA.
|
||||||
func (wds *WebDriverSession) doLoginOneFactor(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, targetURL string) {
|
func (wds *WebDriverSession) doLoginOneFactor(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, targetURL string) {
|
||||||
wds.doVisitLoginPage(ctx, t, targetURL)
|
wds.doVisitLoginPage(ctx, t, targetURL)
|
||||||
wds.doFillLoginPageAndClick(ctx, t, username, password, keepMeLoggedIn)
|
wds.doFillLoginPageAndClick(ctx, t, username, password, keepMeLoggedIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login 1FA and 2FA subsequently (must already be registered)
|
// Login 1FA and 2FA subsequently (must already be registered).
|
||||||
func (wds *WebDriverSession) doLoginTwoFactor(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, otpSecret, targetURL string) {
|
func (wds *WebDriverSession) doLoginTwoFactor(ctx context.Context, t *testing.T, username, password string, keepMeLoggedIn bool, otpSecret, targetURL string) {
|
||||||
wds.doLoginOneFactor(ctx, t, username, password, keepMeLoggedIn, targetURL)
|
wds.doLoginOneFactor(ctx, t, username, password, keepMeLoggedIn, targetURL)
|
||||||
wds.verifyIsSecondFactorPage(ctx, t)
|
wds.verifyIsSecondFactorPage(ctx, t)
|
||||||
|
|
|
@ -2,41 +2,41 @@ package suites
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// BaseDomain the base domain
|
// BaseDomain the base domain.
|
||||||
var BaseDomain = "example.com:8080"
|
var BaseDomain = "example.com:8080"
|
||||||
|
|
||||||
// LoginBaseURL the base URL of the login portal
|
// LoginBaseURL the base URL of the login portal.
|
||||||
var LoginBaseURL = fmt.Sprintf("https://login.%s", BaseDomain)
|
var LoginBaseURL = fmt.Sprintf("https://login.%s", BaseDomain)
|
||||||
|
|
||||||
// SingleFactorBaseURL the base URL of the singlefactor domain
|
// SingleFactorBaseURL the base URL of the singlefactor domain.
|
||||||
var SingleFactorBaseURL = fmt.Sprintf("https://singlefactor.%s", BaseDomain)
|
var SingleFactorBaseURL = fmt.Sprintf("https://singlefactor.%s", BaseDomain)
|
||||||
|
|
||||||
// AdminBaseURL the base URL of the admin domain
|
// AdminBaseURL the base URL of the admin domain.
|
||||||
var AdminBaseURL = fmt.Sprintf("https://admin.%s", BaseDomain)
|
var AdminBaseURL = fmt.Sprintf("https://admin.%s", BaseDomain)
|
||||||
|
|
||||||
// MailBaseURL the base URL of the mail domain
|
// MailBaseURL the base URL of the mail domain.
|
||||||
var MailBaseURL = fmt.Sprintf("https://mail.%s", BaseDomain)
|
var MailBaseURL = fmt.Sprintf("https://mail.%s", BaseDomain)
|
||||||
|
|
||||||
// HomeBaseURL the base URL of the home domain
|
// HomeBaseURL the base URL of the home domain.
|
||||||
var HomeBaseURL = fmt.Sprintf("https://home.%s", BaseDomain)
|
var HomeBaseURL = fmt.Sprintf("https://home.%s", BaseDomain)
|
||||||
|
|
||||||
// PublicBaseURL the base URL of the public domain
|
// PublicBaseURL the base URL of the public domain.
|
||||||
var PublicBaseURL = fmt.Sprintf("https://public.%s", BaseDomain)
|
var PublicBaseURL = fmt.Sprintf("https://public.%s", BaseDomain)
|
||||||
|
|
||||||
// SecureBaseURL the base URL of the secure domain
|
// SecureBaseURL the base URL of the secure domain.
|
||||||
var SecureBaseURL = fmt.Sprintf("https://secure.%s", BaseDomain)
|
var SecureBaseURL = fmt.Sprintf("https://secure.%s", BaseDomain)
|
||||||
|
|
||||||
// DevBaseURL the base URL of the dev domain
|
// DevBaseURL the base URL of the dev domain.
|
||||||
var DevBaseURL = fmt.Sprintf("https://dev.%s", BaseDomain)
|
var DevBaseURL = fmt.Sprintf("https://dev.%s", BaseDomain)
|
||||||
|
|
||||||
// MX1MailBaseURL the base URL of the mx1.mail domain
|
// MX1MailBaseURL the base URL of the mx1.mail domain.
|
||||||
var MX1MailBaseURL = fmt.Sprintf("https://mx1.mail.%s", BaseDomain)
|
var MX1MailBaseURL = fmt.Sprintf("https://mx1.mail.%s", BaseDomain)
|
||||||
|
|
||||||
// MX2MailBaseURL the base URL of the mx2.mail domain
|
// MX2MailBaseURL the base URL of the mx2.mail domain.
|
||||||
var MX2MailBaseURL = fmt.Sprintf("https://mx2.mail.%s", BaseDomain)
|
var MX2MailBaseURL = fmt.Sprintf("https://mx2.mail.%s", BaseDomain)
|
||||||
|
|
||||||
// DuoBaseURL the base URL of the Duo configuration API
|
// DuoBaseURL the base URL of the Duo configuration API.
|
||||||
var DuoBaseURL = "https://duo.example.com"
|
var DuoBaseURL = "https://duo.example.com"
|
||||||
|
|
||||||
// AutheliaBaseURL the base URL of Authelia service
|
// AutheliaBaseURL the base URL of Authelia service.
|
||||||
var AutheliaBaseURL = "https://authelia.example.com:9091"
|
var AutheliaBaseURL = "https://authelia.example.com:9091"
|
||||||
|
|
|
@ -11,12 +11,12 @@ import (
|
||||||
"github.com/authelia/authelia/internal/utils"
|
"github.com/authelia/authelia/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DockerEnvironment represent a docker environment
|
// DockerEnvironment represent a docker environment.
|
||||||
type DockerEnvironment struct {
|
type DockerEnvironment struct {
|
||||||
dockerComposeFiles []string
|
dockerComposeFiles []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDockerEnvironment create a new docker environment
|
// NewDockerEnvironment create a new docker environment.
|
||||||
func NewDockerEnvironment(files []string) *DockerEnvironment {
|
func NewDockerEnvironment(files []string) *DockerEnvironment {
|
||||||
if os.Getenv("CI") == "true" {
|
if os.Getenv("CI") == "true" {
|
||||||
for i := range files {
|
for i := range files {
|
||||||
|
@ -42,22 +42,22 @@ func (de *DockerEnvironment) createCommand(cmd string) *exec.Cmd {
|
||||||
return utils.Command("bash", "-c", dockerCmdLine)
|
return utils.Command("bash", "-c", dockerCmdLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Up spawn a docker environment
|
// Up spawn a docker environment.
|
||||||
func (de *DockerEnvironment) Up() error {
|
func (de *DockerEnvironment) Up() error {
|
||||||
return de.createCommandWithStdout("up --build -d").Run()
|
return de.createCommandWithStdout("up --build -d").Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart restarts a service
|
// Restart restarts a service.
|
||||||
func (de *DockerEnvironment) Restart(service string) error {
|
func (de *DockerEnvironment) Restart(service string) error {
|
||||||
return de.createCommandWithStdout(fmt.Sprintf("restart %s", service)).Run()
|
return de.createCommandWithStdout(fmt.Sprintf("restart %s", service)).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Down spawn a docker environment
|
// Down spawn a docker environment.
|
||||||
func (de *DockerEnvironment) Down() error {
|
func (de *DockerEnvironment) Down() error {
|
||||||
return de.createCommandWithStdout("down -v").Run()
|
return de.createCommandWithStdout("down -v").Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logs get logs of a given service of the environment
|
// Logs get logs of a given service of the environment.
|
||||||
func (de *DockerEnvironment) Logs(service string, flags []string) (string, error) {
|
func (de *DockerEnvironment) Logs(service string, flags []string) (string, error) {
|
||||||
cmd := de.createCommand(fmt.Sprintf("logs %s %s", strings.Join(flags, " "), service))
|
cmd := de.createCommand(fmt.Sprintf("logs %s %s", strings.Join(flags, " "), service))
|
||||||
content, err := cmd.Output()
|
content, err := cmd.Output()
|
||||||
|
|
|
@ -8,17 +8,17 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DuoPolicy a type of policy
|
// DuoPolicy a type of policy.
|
||||||
type DuoPolicy int32
|
type DuoPolicy int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Deny deny policy
|
// Deny deny policy.
|
||||||
Deny DuoPolicy = iota
|
Deny DuoPolicy = iota
|
||||||
// Allow allow policy
|
// Allow allow policy.
|
||||||
Allow DuoPolicy = iota
|
Allow DuoPolicy = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigureDuo configure duo api to allow or block auth requests
|
// ConfigureDuo configure duo api to allow or block auth requests.
|
||||||
func ConfigureDuo(t *testing.T, allowDeny DuoPolicy) {
|
func ConfigureDuo(t *testing.T, allowDeny DuoPolicy) {
|
||||||
url := fmt.Sprintf("%s/allow", DuoBaseURL)
|
url := fmt.Sprintf("%s/allow", DuoBaseURL)
|
||||||
if allowDeny == Deny {
|
if allowDeny == Deny {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewHTTPClient create a new client skipping TLS verification and not redirecting
|
// NewHTTPClient create a new client skipping TLS verification and not redirecting.
|
||||||
func NewHTTPClient() *http.Client {
|
func NewHTTPClient() *http.Client {
|
||||||
tr := &http.Transport{
|
tr := &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
var kindImageName = "authelia-kind-proxy"
|
var kindImageName = "authelia-kind-proxy"
|
||||||
var dockerCmdLine = fmt.Sprintf("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/kind/docker-compose.yml run --rm %s", kindImageName)
|
var dockerCmdLine = fmt.Sprintf("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/kind/docker-compose.yml run --rm %s", kindImageName)
|
||||||
|
|
||||||
// Kind used for running kind commands
|
// Kind used for running kind commands.
|
||||||
type Kind struct{}
|
type Kind struct{}
|
||||||
|
|
||||||
func kindCommand(cmdline string) *exec.Cmd {
|
func kindCommand(cmdline string) *exec.Cmd {
|
||||||
|
@ -20,7 +20,7 @@ func kindCommand(cmdline string) *exec.Cmd {
|
||||||
return utils.Shell(cmd)
|
return utils.Shell(cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCluster create a new Kubernetes cluster
|
// CreateCluster create a new Kubernetes cluster.
|
||||||
func (k Kind) CreateCluster() error {
|
func (k Kind) CreateCluster() error {
|
||||||
cmd := kindCommand("kind create cluster --config /etc/kind/config.yml")
|
cmd := kindCommand("kind create cluster --config /etc/kind/config.yml")
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
|
@ -32,7 +32,7 @@ func (k Kind) CreateCluster() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// This command is necessary to fix the coredns loop detected when using user-defined docker network
|
// This command is necessary to fix the coredns loop detected when using user-defined docker network.
|
||||||
// In that case /etc/resolv.conf use 127.0.0.11 as DNS and CoreDNS thinks it is talking to itself which is wrong.
|
// In that case /etc/resolv.conf use 127.0.0.11 as DNS and CoreDNS thinks it is talking to itself which is wrong.
|
||||||
// This IP is the docker internal DNS so it is safe to disable the loop check.
|
// This IP is the docker internal DNS so it is safe to disable the loop check.
|
||||||
cmd = kindCommand("sh -c 'kubectl -n kube-system get configmap/coredns -o yaml | grep -v loop | kubectl replace -f -'")
|
cmd = kindCommand("sh -c 'kubectl -n kube-system get configmap/coredns -o yaml | grep -v loop | kubectl replace -f -'")
|
||||||
|
@ -42,13 +42,13 @@ func (k Kind) CreateCluster() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCluster delete a Kubernetes cluster
|
// DeleteCluster delete a Kubernetes cluster.
|
||||||
func (k Kind) DeleteCluster() error {
|
func (k Kind) DeleteCluster() error {
|
||||||
cmd := kindCommand("kind delete cluster")
|
cmd := kindCommand("kind delete cluster")
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClusterExists check whether a cluster exists
|
// ClusterExists check whether a cluster exists.
|
||||||
func (k Kind) ClusterExists() (bool, error) {
|
func (k Kind) ClusterExists() (bool, error) {
|
||||||
cmd := kindCommand("kind get clusters")
|
cmd := kindCommand("kind get clusters")
|
||||||
cmd.Stdout = nil
|
cmd.Stdout = nil
|
||||||
|
@ -62,28 +62,28 @@ func (k Kind) ClusterExists() (bool, error) {
|
||||||
return strings.Contains(string(output), "kind"), nil
|
return strings.Contains(string(output), "kind"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadImage load an image in the Kubernetes container
|
// LoadImage load an image in the Kubernetes container.
|
||||||
func (k Kind) LoadImage(imageName string) error {
|
func (k Kind) LoadImage(imageName string) error {
|
||||||
cmd := kindCommand(fmt.Sprintf("kind load docker-image %s", imageName))
|
cmd := kindCommand(fmt.Sprintf("kind load docker-image %s", imageName))
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kubectl used for running kubectl commands
|
// Kubectl used for running kubectl commands.
|
||||||
type Kubectl struct{}
|
type Kubectl struct{}
|
||||||
|
|
||||||
// StartProxy start a proxy
|
// StartProxy start a proxy.
|
||||||
func (k Kubectl) StartProxy() error {
|
func (k Kubectl) StartProxy() error {
|
||||||
cmd := utils.Shell("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/kind/docker-compose.yml up -d authelia-kind-proxy")
|
cmd := utils.Shell("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/kind/docker-compose.yml up -d authelia-kind-proxy")
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopProxy stop a proxy
|
// StopProxy stop a proxy.
|
||||||
func (k Kubectl) StopProxy() error {
|
func (k Kubectl) StopProxy() error {
|
||||||
cmd := utils.Shell("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/kind/docker-compose.yml rm -s -f authelia-kind-proxy")
|
cmd := utils.Shell("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/kind/docker-compose.yml rm -s -f authelia-kind-proxy")
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartDashboard start Kube dashboard
|
// StartDashboard start Kube dashboard.
|
||||||
func (k Kubectl) StartDashboard() error {
|
func (k Kubectl) StartDashboard() error {
|
||||||
if err := kindCommand("sh -c 'cd /authelia && ./bootstrap-dashboard.sh'").Run(); err != nil {
|
if err := kindCommand("sh -c 'cd /authelia && ./bootstrap-dashboard.sh'").Run(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -95,25 +95,25 @@ func (k Kubectl) StartDashboard() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopDashboard stop kube dashboard
|
// StopDashboard stop kube dashboard.
|
||||||
func (k Kubectl) StopDashboard() error {
|
func (k Kubectl) StopDashboard() error {
|
||||||
cmd := utils.Shell("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/kind/docker-compose.yml rm -s -f kube-dashboard")
|
cmd := utils.Shell("docker-compose -p authelia -f internal/suites/docker-compose.yml -f internal/suites/example/compose/kind/docker-compose.yml rm -s -f kube-dashboard")
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeployThirdparties deploy thirdparty services (ldap, db, ingress controllers, etc...)
|
// DeployThirdparties deploy thirdparty services (ldap, db, ingress controllers, etc...).
|
||||||
func (k Kubectl) DeployThirdparties() error {
|
func (k Kubectl) DeployThirdparties() error {
|
||||||
cmd := kindCommand("sh -c 'cd /authelia && ./bootstrap.sh'")
|
cmd := kindCommand("sh -c 'cd /authelia && ./bootstrap.sh'")
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeployAuthelia deploy Authelia application
|
// DeployAuthelia deploy Authelia application.
|
||||||
func (k Kubectl) DeployAuthelia() error {
|
func (k Kubectl) DeployAuthelia() error {
|
||||||
cmd := kindCommand("sh -c 'cd /authelia && ./bootstrap-authelia.sh'")
|
cmd := kindCommand("sh -c 'cd /authelia && ./bootstrap-authelia.sh'")
|
||||||
return cmd.Run()
|
return cmd.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitPodsReady wait for all pods to be ready
|
// WaitPodsReady wait for all pods to be ready.
|
||||||
func (k Kubectl) WaitPodsReady(timeout time.Duration) error {
|
func (k Kubectl) WaitPodsReady(timeout time.Duration) error {
|
||||||
return utils.CheckUntil(5*time.Second, timeout, func() (bool, error) {
|
return utils.CheckUntil(5*time.Second, timeout, func() (bool, error) {
|
||||||
cmd := kindCommand("kubectl get -n authelia pods --no-headers")
|
cmd := kindCommand("kubectl get -n authelia pods --no-headers")
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Suite the definition of a suite
|
// Suite the definition of a suite.
|
||||||
type Suite struct {
|
type Suite struct {
|
||||||
SetUp func(tmpPath string) error
|
SetUp func(tmpPath string) error
|
||||||
SetUpTimeout time.Duration
|
SetUpTimeout time.Duration
|
||||||
|
@ -15,7 +15,7 @@ type Suite struct {
|
||||||
// Callback called when an error occur during setup phase.
|
// Callback called when an error occur during setup phase.
|
||||||
OnSetupTimeout func() error
|
OnSetupTimeout func() error
|
||||||
|
|
||||||
// Callback called when at least one test fail
|
// Callback called when at least one test fail.
|
||||||
OnError func() error
|
OnError func() error
|
||||||
|
|
||||||
TestTimeout time.Duration
|
TestTimeout time.Duration
|
||||||
|
@ -27,24 +27,24 @@ type Suite struct {
|
||||||
Description string
|
Description string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registry represent a registry of suite by name
|
// Registry represent a registry of suite by name.
|
||||||
type Registry struct {
|
type Registry struct {
|
||||||
registry map[string]Suite
|
registry map[string]Suite
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalRegistry a global registry used by Authelia tooling
|
// GlobalRegistry a global registry used by Authelia tooling.
|
||||||
var GlobalRegistry *Registry
|
var GlobalRegistry *Registry
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
GlobalRegistry = NewSuitesRegistry()
|
GlobalRegistry = NewSuitesRegistry()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSuitesRegistry create a suites registry
|
// NewSuitesRegistry create a suites registry.
|
||||||
func NewSuitesRegistry() *Registry {
|
func NewSuitesRegistry() *Registry {
|
||||||
return &Registry{make(map[string]Suite)}
|
return &Registry{make(map[string]Suite)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register register a suite by name
|
// Register register a suite by name.
|
||||||
func (sr *Registry) Register(name string, suite Suite) {
|
func (sr *Registry) Register(name string, suite Suite) {
|
||||||
if _, found := sr.registry[name]; found {
|
if _, found := sr.registry[name]; found {
|
||||||
log.Fatal(fmt.Sprintf("Trying to register the suite %s multiple times", name))
|
log.Fatal(fmt.Sprintf("Trying to register the suite %s multiple times", name))
|
||||||
|
@ -52,7 +52,7 @@ func (sr *Registry) Register(name string, suite Suite) {
|
||||||
sr.registry[name] = suite
|
sr.registry[name] = suite
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get return a suite by name
|
// Get return a suite by name.
|
||||||
func (sr *Registry) Get(name string) Suite {
|
func (sr *Registry) Get(name string) Suite {
|
||||||
s, found := sr.registry[name]
|
s, found := sr.registry[name]
|
||||||
if !found {
|
if !found {
|
||||||
|
@ -61,7 +61,7 @@ func (sr *Registry) Get(name string) Suite {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suites list available suites
|
// Suites list available suites.
|
||||||
func (sr *Registry) Suites() []string {
|
func (sr *Registry) Suites() []string {
|
||||||
suites := make([]string, 0)
|
suites := make([]string, 0)
|
||||||
for k := range sr.registry {
|
for k := range sr.registry {
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon2FA() {
|
||||||
wds.verifySecretAuthorized(ctx, s.T())
|
wds.verifySecretAuthorized(ctx, s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
// from network 192.168.240.201/32
|
// from network 192.168.240.201/32.
|
||||||
func (s *NetworkACLSuite) TestShouldAccessSecretUpon1FA() {
|
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()
|
||||||
|
@ -51,7 +51,7 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon1FA() {
|
||||||
wds.verifySecretAuthorized(ctx, s.T())
|
wds.verifySecretAuthorized(ctx, s.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
// from network 192.168.240.202/32
|
// from network 192.168.240.202/32.
|
||||||
func (s *NetworkACLSuite) TestShouldAccessSecretUpon0FA() {
|
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()
|
||||||
|
|
|
@ -57,11 +57,11 @@ func (s *StandaloneWebDriverSuite) TestShouldLetUserKnowHeIsAlreadyAuthenticated
|
||||||
|
|
||||||
_ = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
|
_ = s.doRegisterAndLogin2FA(ctx, s.T(), "john", "password", false, "")
|
||||||
|
|
||||||
// Visit home page to change context
|
// Visit home page to change context.
|
||||||
s.doVisit(s.T(), HomeBaseURL)
|
s.doVisit(s.T(), HomeBaseURL)
|
||||||
s.verifyIsHome(ctx, s.T())
|
s.verifyIsHome(ctx, s.T())
|
||||||
|
|
||||||
// Visit the login page and wait for redirection to 2FA page with success icon displayed
|
// Visit the login page and wait for redirection to 2FA page with success icon displayed.
|
||||||
s.doVisit(s.T(), LoginBaseURL)
|
s.doVisit(s.T(), LoginBaseURL)
|
||||||
s.verifyIsAuthenticatedPage(ctx, s.T())
|
s.verifyIsAuthenticatedPage(ctx, s.T())
|
||||||
}
|
}
|
||||||
|
@ -73,22 +73,22 @@ func (s *StandaloneWebDriverSuite) TestShouldCheckUserIsAskedToRegisterDevice()
|
||||||
username := "john"
|
username := "john"
|
||||||
password := "password"
|
password := "password"
|
||||||
|
|
||||||
// Clean up any TOTP secret already in DB
|
// Clean up any TOTP secret already in DB.
|
||||||
provider := storage.NewSQLiteProvider("/tmp/db.sqlite3")
|
provider := storage.NewSQLiteProvider("/tmp/db.sqlite3")
|
||||||
require.NoError(s.T(), provider.DeleteTOTPSecret(username))
|
require.NoError(s.T(), provider.DeleteTOTPSecret(username))
|
||||||
|
|
||||||
// Login one factor
|
// Login one factor.
|
||||||
s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
|
s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
|
||||||
|
|
||||||
// Check the user is asked to register a new device
|
// Check the user is asked to register a new device.
|
||||||
s.WaitElementLocatedByClassName(ctx, s.T(), "state-not-registered")
|
s.WaitElementLocatedByClassName(ctx, s.T(), "state-not-registered")
|
||||||
|
|
||||||
// Then register the TOTP factor
|
// Then register the TOTP factor.
|
||||||
s.doRegisterTOTP(ctx, s.T())
|
s.doRegisterTOTP(ctx, s.T())
|
||||||
// And logout
|
// And logout.
|
||||||
s.doLogout(ctx, s.T())
|
s.doLogout(ctx, s.T())
|
||||||
|
|
||||||
// Login one factor again
|
// Login one factor again.
|
||||||
s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
|
s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
|
||||||
|
|
||||||
// now the user should be asked to perform 2FA
|
// now the user should be asked to perform 2FA
|
||||||
|
@ -103,7 +103,7 @@ func NewStandaloneSuite() *StandaloneSuite {
|
||||||
return &StandaloneSuite{}
|
return &StandaloneSuite{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard case using nginx
|
// Standard case using nginx.
|
||||||
func (s *StandaloneSuite) TestShouldVerifyAPIVerifyUnauthorize() {
|
func (s *StandaloneSuite) TestShouldVerifyAPIVerifyUnauthorize() {
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/verify", AutheliaBaseURL), nil)
|
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/verify", AutheliaBaseURL), nil)
|
||||||
s.Assert().NoError(err)
|
s.Assert().NoError(err)
|
||||||
|
@ -119,7 +119,7 @@ func (s *StandaloneSuite) TestShouldVerifyAPIVerifyUnauthorize() {
|
||||||
s.Assert().Equal(string(body), "Unauthorized")
|
s.Assert().Equal(string(body), "Unauthorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard case using Kubernetes
|
// Standard case using Kubernetes.
|
||||||
func (s *StandaloneSuite) TestShouldVerifyAPIVerifyRedirectFromXOriginalURL() {
|
func (s *StandaloneSuite) TestShouldVerifyAPIVerifyRedirectFromXOriginalURL() {
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/verify?rd=%s", AutheliaBaseURL, LoginBaseURL), nil)
|
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/verify?rd=%s", AutheliaBaseURL, LoginBaseURL), nil)
|
||||||
s.Assert().NoError(err)
|
s.Assert().NoError(err)
|
||||||
|
|
|
@ -5,14 +5,14 @@ import (
|
||||||
"github.com/tebeka/selenium"
|
"github.com/tebeka/selenium"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SeleniumSuite is a selenium suite
|
// SeleniumSuite is a selenium suite.
|
||||||
type SeleniumSuite struct {
|
type SeleniumSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
|
|
||||||
*WebDriverSession
|
*WebDriverSession
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebDriver return the webdriver of the suite
|
// WebDriver return the webdriver of the suite.
|
||||||
func (s *SeleniumSuite) WebDriver() selenium.WebDriver {
|
func (s *SeleniumSuite) WebDriver() selenium.WebDriver {
|
||||||
return s.WebDriverSession.WebDriver
|
return s.WebDriverSession.WebDriver
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ type WebDriverSession struct {
|
||||||
WebDriver selenium.WebDriver
|
WebDriver selenium.WebDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartWebDriverWithProxy create a selenium session
|
// StartWebDriverWithProxy create a selenium session.
|
||||||
func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error) {
|
func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error) {
|
||||||
service, err := selenium.NewChromeDriverService("/usr/bin/chromedriver", port)
|
service, err := selenium.NewChromeDriverService("/usr/bin/chromedriver", port)
|
||||||
|
|
||||||
|
@ -62,12 +62,12 @@ func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error)
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartWebDriver create a selenium session
|
// StartWebDriver create a selenium session.
|
||||||
func StartWebDriver() (*WebDriverSession, error) {
|
func StartWebDriver() (*WebDriverSession, error) {
|
||||||
return StartWebDriverWithProxy("", 4444)
|
return StartWebDriverWithProxy("", 4444)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stop the selenium session
|
// Stop stop the selenium session.
|
||||||
func (wds *WebDriverSession) Stop() error {
|
func (wds *WebDriverSession) Stop() error {
|
||||||
err := wds.WebDriver.Quit()
|
err := wds.WebDriver.Quit()
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ func (wds *WebDriverSession) Stop() error {
|
||||||
return wds.service.Stop()
|
return wds.service.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithWebdriver run some actions against a webdriver
|
// WithWebdriver run some actions against a webdriver.
|
||||||
func WithWebdriver(fn func(webdriver selenium.WebDriver) error) error {
|
func WithWebdriver(fn func(webdriver selenium.WebDriver) error) error {
|
||||||
wds, err := StartWebDriver()
|
wds, err := StartWebDriver()
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ func WithWebdriver(fn func(webdriver selenium.WebDriver) error) error {
|
||||||
return fn(wds.WebDriver)
|
return fn(wds.WebDriver)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait wait until condition holds true
|
// Wait wait until condition holds true.
|
||||||
func (wds *WebDriverSession) Wait(ctx context.Context, condition selenium.Condition) error {
|
func (wds *WebDriverSession) Wait(ctx context.Context, condition selenium.Condition) error {
|
||||||
done := make(chan error, 1)
|
done := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -148,37 +148,37 @@ func (wds *WebDriverSession) waitElementsLocated(ctx context.Context, t *testing
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitElementLocatedByID wait an element is located by id
|
// WaitElementLocatedByID wait an element is located by id.
|
||||||
func (wds *WebDriverSession) WaitElementLocatedByID(ctx context.Context, t *testing.T, id string) selenium.WebElement {
|
func (wds *WebDriverSession) WaitElementLocatedByID(ctx context.Context, t *testing.T, id string) selenium.WebElement {
|
||||||
return wds.waitElementLocated(ctx, t, selenium.ByID, id)
|
return wds.waitElementLocated(ctx, t, selenium.ByID, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitElementLocatedByTagName wait an element is located by tag name
|
// WaitElementLocatedByTagName wait an element is located by tag name.
|
||||||
func (wds *WebDriverSession) WaitElementLocatedByTagName(ctx context.Context, t *testing.T, tagName string) selenium.WebElement {
|
func (wds *WebDriverSession) WaitElementLocatedByTagName(ctx context.Context, t *testing.T, tagName string) selenium.WebElement {
|
||||||
return wds.waitElementLocated(ctx, t, selenium.ByTagName, tagName)
|
return wds.waitElementLocated(ctx, t, selenium.ByTagName, tagName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitElementLocatedByClassName wait an element is located by class name
|
// WaitElementLocatedByClassName wait an element is located by class name.
|
||||||
func (wds *WebDriverSession) WaitElementLocatedByClassName(ctx context.Context, t *testing.T, className string) selenium.WebElement {
|
func (wds *WebDriverSession) WaitElementLocatedByClassName(ctx context.Context, t *testing.T, className string) selenium.WebElement {
|
||||||
return wds.waitElementLocated(ctx, t, selenium.ByClassName, className)
|
return wds.waitElementLocated(ctx, t, selenium.ByClassName, className)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitElementLocatedByLinkText wait an element is located by link text
|
// WaitElementLocatedByLinkText wait an element is located by link text.
|
||||||
func (wds *WebDriverSession) WaitElementLocatedByLinkText(ctx context.Context, t *testing.T, linkText string) selenium.WebElement {
|
func (wds *WebDriverSession) WaitElementLocatedByLinkText(ctx context.Context, t *testing.T, linkText string) selenium.WebElement {
|
||||||
return wds.waitElementLocated(ctx, t, selenium.ByLinkText, linkText)
|
return wds.waitElementLocated(ctx, t, selenium.ByLinkText, linkText)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitElementLocatedByCSSSelector wait an element is located by class name
|
// WaitElementLocatedByCSSSelector wait an element is located by class name.
|
||||||
func (wds *WebDriverSession) WaitElementLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) selenium.WebElement {
|
func (wds *WebDriverSession) WaitElementLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) selenium.WebElement {
|
||||||
return wds.waitElementLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
|
return wds.waitElementLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitElementsLocatedByCSSSelector wait an element is located by CSS selector
|
// WaitElementsLocatedByCSSSelector wait an element is located by CSS selector.
|
||||||
func (wds *WebDriverSession) WaitElementsLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) []selenium.WebElement {
|
func (wds *WebDriverSession) WaitElementsLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) []selenium.WebElement {
|
||||||
return wds.waitElementsLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
|
return wds.waitElementsLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitElementTextContains wait the text of an element contains a pattern
|
// WaitElementTextContains wait the text of an element contains a pattern.
|
||||||
func (wds *WebDriverSession) WaitElementTextContains(ctx context.Context, t *testing.T, element selenium.WebElement, pattern string) {
|
func (wds *WebDriverSession) WaitElementTextContains(ctx context.Context, t *testing.T, element selenium.WebElement, pattern string) {
|
||||||
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
||||||
text, err := element.Text()
|
text, err := element.Text()
|
||||||
|
|
|
@ -2,21 +2,21 @@ package utils
|
||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
// Clock is an interface for a clock
|
// Clock is an interface for a clock.
|
||||||
type Clock interface {
|
type Clock interface {
|
||||||
Now() time.Time
|
Now() time.Time
|
||||||
After(d time.Duration) <-chan time.Time
|
After(d time.Duration) <-chan time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// RealClock is the implementation of a clock for production code
|
// RealClock is the implementation of a clock for production code.
|
||||||
type RealClock struct{}
|
type RealClock struct{}
|
||||||
|
|
||||||
// Now return the current time
|
// Now return the current time.
|
||||||
func (RealClock) Now() time.Time {
|
func (RealClock) Now() time.Time {
|
||||||
return time.Now()
|
return time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// After return a channel receiving the time after the defined duration
|
// After return a channel receiving the time after the defined duration.
|
||||||
func (RealClock) After(d time.Duration) <-chan time.Time {
|
func (RealClock) After(d time.Duration) <-chan time.Time {
|
||||||
return time.After(d)
|
return time.After(d)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,17 +10,17 @@ import (
|
||||||
var ErrTimeoutReached = errors.New("timeout reached")
|
var ErrTimeoutReached = errors.New("timeout reached")
|
||||||
var parseDurationRegexp = regexp.MustCompile(`^(?P<Duration>[1-9]\d*?)(?P<Unit>[smhdwMy])?$`)
|
var parseDurationRegexp = regexp.MustCompile(`^(?P<Duration>[1-9]\d*?)(?P<Unit>[smhdwMy])?$`)
|
||||||
|
|
||||||
// Hour is an int based representation of the time unit
|
// Hour is an int based representation of the time unit.
|
||||||
const Hour = time.Minute * 60
|
const Hour = time.Minute * 60
|
||||||
|
|
||||||
// Day is an int based representation of the time unit
|
// Day is an int based representation of the time unit.
|
||||||
const Day = Hour * 24
|
const Day = Hour * 24
|
||||||
|
|
||||||
// Week is an int based representation of the time unit
|
// Week is an int based representation of the time unit.
|
||||||
const Week = Day * 7
|
const Week = Day * 7
|
||||||
|
|
||||||
// Year is an int based representation of the time unit
|
// Year is an int based representation of the time unit.
|
||||||
const Year = Day * 365
|
const Year = Day * 365
|
||||||
|
|
||||||
// Month is an int based representation of the time unit
|
// Month is an int based representation of the time unit.
|
||||||
const Month = Year / 12
|
const Month = Year / 12
|
||||||
|
|
|
@ -15,11 +15,11 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Command create a command at the project root
|
// Command create a command at the project root.
|
||||||
func Command(name string, args ...string) *exec.Cmd {
|
func Command(name string, args ...string) *exec.Cmd {
|
||||||
cmd := exec.Command(name, args...)
|
cmd := exec.Command(name, args...)
|
||||||
|
|
||||||
// By default set the working directory to the project root directory
|
// By default set the working directory to the project root directory.
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
for !strings.HasSuffix(wd, "authelia") {
|
for !strings.HasSuffix(wd, "authelia") {
|
||||||
wd = filepath.Dir(wd)
|
wd = filepath.Dir(wd)
|
||||||
|
@ -28,7 +28,7 @@ func Command(name string, args ...string) *exec.Cmd {
|
||||||
return cmd
|
return 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...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
|
@ -36,12 +36,12 @@ func CommandWithStdout(name string, args ...string) *exec.Cmd {
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shell create a shell command
|
// Shell create a shell command.
|
||||||
func Shell(command string) *exec.Cmd {
|
func Shell(command string) *exec.Cmd {
|
||||||
return CommandWithStdout("bash", "-c", command)
|
return CommandWithStdout("bash", "-c", command)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunCommandUntilCtrlC run a command until ctrl-c is hit
|
// RunCommandUntilCtrlC run a command until ctrl-c is hit.
|
||||||
func RunCommandUntilCtrlC(cmd *exec.Cmd) {
|
func RunCommandUntilCtrlC(cmd *exec.Cmd) {
|
||||||
mutex := sync.Mutex{}
|
mutex := sync.Mutex{}
|
||||||
cond := sync.NewCond(&mutex)
|
cond := sync.NewCond(&mutex)
|
||||||
|
@ -74,7 +74,7 @@ func RunCommandUntilCtrlC(cmd *exec.Cmd) {
|
||||||
cond.Wait()
|
cond.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunFuncUntilCtrlC run a function until ctrl-c is hit
|
// RunFuncUntilCtrlC run a function until ctrl-c is hit.
|
||||||
func RunFuncUntilCtrlC(fn func() error) error {
|
func RunFuncUntilCtrlC(fn func() error) error {
|
||||||
mutex := sync.Mutex{}
|
mutex := sync.Mutex{}
|
||||||
cond := sync.NewCond(&mutex)
|
cond := sync.NewCond(&mutex)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileExists returns whether the given file or directory exists
|
// FileExists returns whether the given file or directory exists.
|
||||||
func FileExists(path string) (bool, error) {
|
func FileExists(path string) (bool, error) {
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
// ParseDurationString parses a string to a duration
|
// ParseDurationString parses a string to a duration
|
||||||
// Duration notations are an integer followed by a unit
|
// Duration notations are an integer followed by a unit
|
||||||
// Units are s = second, m = minute, d = day, w = week, M = month, y = year
|
// Units are s = second, m = minute, d = day, w = week, M = month, y = year
|
||||||
// Example 1y is the same as 1 year
|
// Example 1y is the same as 1 year.
|
||||||
func ParseDurationString(input string) (time.Duration, error) {
|
func ParseDurationString(input string) (time.Duration, error) {
|
||||||
var duration time.Duration
|
var duration time.Duration
|
||||||
matches := parseDurationRegexp.FindStringSubmatch(input)
|
matches := parseDurationRegexp.FindStringSubmatch(input)
|
||||||
|
|
Loading…
Reference in New Issue