[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:
|
||||
gocyclo:
|
||||
min-complexity: 15
|
||||
godot:
|
||||
check-all: true
|
||||
goimports:
|
||||
local-prefixes: github.com/authelia/authelia
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- gocyclo
|
||||
- godot
|
||||
- gofmt
|
||||
- goimports
|
||||
- golint
|
||||
|
|
|
@ -13,16 +13,16 @@ import (
|
|||
"github.com/authelia/authelia/internal/utils"
|
||||
)
|
||||
|
||||
// HostEntry represents an entry in /etc/hosts
|
||||
// HostEntry represents an entry in /etc/hosts.
|
||||
type HostEntry struct {
|
||||
Domain string
|
||||
IP string
|
||||
}
|
||||
|
||||
var hostEntries = []HostEntry{
|
||||
// For authelia backend
|
||||
// For authelia backend.
|
||||
{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: "admin.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: "mail.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"},
|
||||
|
||||
// For HAProxy suite
|
||||
// For HAProxy suite.
|
||||
{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-client2.example.com", IP: "192.168.240.202"},
|
||||
{Domain: "proxy-client3.example.com", IP: "192.168.240.203"},
|
||||
|
||||
// Kubernetes dashboard
|
||||
// Kubernetes dashboard.
|
||||
{Domain: "kubernetes.example.com", IP: "192.168.240.110"},
|
||||
}
|
||||
|
||||
|
@ -170,7 +166,7 @@ func readVersions() {
|
|||
readVersion("docker-compose", "--version")
|
||||
}
|
||||
|
||||
// Bootstrap bootstrap authelia dev environment
|
||||
// Bootstrap bootstrap authelia dev environment.
|
||||
func Bootstrap(cobraCmd *cobra.Command, args []string) {
|
||||
bootstrapPrintln("Checking command installation...")
|
||||
checkCommandExist("node")
|
||||
|
|
|
@ -23,7 +23,7 @@ func buildAutheliaBinary() {
|
|||
}
|
||||
|
||||
func buildFrontend() {
|
||||
// Install npm dependencies
|
||||
// Install npm dependencies.
|
||||
cmd := utils.CommandWithStdout("yarn", "install")
|
||||
cmd.Dir = "web"
|
||||
|
||||
|
@ -31,7 +31,7 @@ func buildFrontend() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Then build the frontend
|
||||
// Then build the frontend.
|
||||
cmd = utils.CommandWithStdout("yarn", "build")
|
||||
cmd.Dir = "web"
|
||||
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) {
|
||||
log.Info("Building Authelia...")
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/authelia/authelia/internal/utils"
|
||||
)
|
||||
|
||||
// RunCI run the CI scripts
|
||||
// RunCI run the CI scripts.
|
||||
func RunCI(cmd *cobra.Command, args []string) {
|
||||
log.Info("=====> Build stage <=====")
|
||||
if err := utils.CommandWithStdout("authelia-scripts", "--log-level", "debug", "build").Run(); err != nil {
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"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) {
|
||||
log.Debug("Removing `" + OutputDir + "` directory")
|
||||
err := os.RemoveAll(OutputDir)
|
||||
|
|
|
@ -42,12 +42,12 @@ func checkArchIsSupported(arch string) {
|
|||
|
||||
func dockerBuildOfficialImage(arch string) error {
|
||||
docker := &Docker{}
|
||||
// Set default Architecture Dockerfile to amd64
|
||||
// Set default Architecture Dockerfile to amd64.
|
||||
dockerfile := "Dockerfile"
|
||||
// Set version of QEMU
|
||||
// Set version of QEMU.
|
||||
qemuversion := "v4.2.0-7"
|
||||
|
||||
// If not the default value
|
||||
// If not the default value.
|
||||
if arch != defaultArch {
|
||||
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{
|
||||
Use: "push-image",
|
||||
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{
|
||||
Use: "push-manifest",
|
||||
Short: "Publish Authelia docker manifest to Docker Hub",
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"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) {
|
||||
log.Infof("Running Authelia with config %s...", args[0])
|
||||
cmd := utils.CommandWithStdout(OutputDir+"/authelia", "--config", args[0])
|
||||
|
|
|
@ -21,10 +21,10 @@ import (
|
|||
// ErrNotAvailableSuite error raised when suite is not available.
|
||||
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")
|
||||
|
||||
// runningSuiteFile name of the file containing the currently running suite
|
||||
// runningSuiteFile name of the file containing the currently running suite.
|
||||
var runningSuiteFile = ".suite"
|
||||
|
||||
var headless bool
|
||||
|
@ -68,7 +68,7 @@ var SuitesSetupCmd = &cobra.Command{
|
|||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
|
||||
// SuitesTeardownCmd Command for tearing down a suite environment
|
||||
// SuitesTeardownCmd Command for tearing down a suite environment.
|
||||
var SuitesTeardownCmd = &cobra.Command{
|
||||
Use: "teardown [suite]",
|
||||
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),
|
||||
}
|
||||
|
||||
// SuitesTestCmd Command for testing a suite
|
||||
// SuitesTestCmd Command for testing a suite.
|
||||
var SuitesTestCmd = &cobra.Command{
|
||||
Use: "test [suite]",
|
||||
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)
|
||||
}
|
||||
|
||||
// If suite(s) are provided as argument
|
||||
// If suite(s) are provided as argument.
|
||||
if len(args) >= 1 {
|
||||
suiteArg := args[0]
|
||||
|
||||
|
@ -242,7 +242,7 @@ func runSuiteTests(suiteName string, withEnv bool) error {
|
|||
|
||||
suite := suites.GlobalRegistry.Get(suiteName)
|
||||
|
||||
// Default value is 1 minute
|
||||
// Default value is 1 minute.
|
||||
timeout := "60s"
|
||||
if suite.TestTimeout > 0 {
|
||||
timeout = fmt.Sprintf("%ds", int64(suite.TestTimeout/time.Second))
|
||||
|
@ -279,7 +279,7 @@ func runSuiteTests(suiteName string, withEnv bool) error {
|
|||
|
||||
if withEnv {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/authelia/authelia/internal/utils"
|
||||
)
|
||||
|
||||
// RunUnitTest run the unit tests
|
||||
// RunUnitTest run the unit tests.
|
||||
func RunUnitTest(cobraCmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.TraceLevel)
|
||||
if err := utils.Shell("go test $(go list ./... | grep -v suites)").Run(); err != nil {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
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"
|
||||
|
||||
// DockerImageName the official name of authelia docker image
|
||||
// DockerImageName the official name of Authelia docker image.
|
||||
var DockerImageName = "authelia/authelia"
|
||||
|
||||
// IntermediateDockerImageName local name of the docker image
|
||||
// IntermediateDockerImageName local name of the docker image.
|
||||
var IntermediateDockerImageName = "authelia:dist"
|
||||
|
|
|
@ -4,10 +4,10 @@ import (
|
|||
"github.com/authelia/authelia/internal/utils"
|
||||
)
|
||||
|
||||
// Docker a docker object
|
||||
// Docker a docker object.
|
||||
type Docker struct{}
|
||||
|
||||
// Build build a docker image
|
||||
// Build build a docker image.
|
||||
func (d *Docker) Build(tag, dockerfile, target, gitTag, gitCommit string) error {
|
||||
return utils.CommandWithStdout(
|
||||
"docker", "build", "-t", tag, "-f", dockerfile, "--build-arg",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//usr/bin/env go run "$0" "$@"; exit
|
||||
//nolint:godot
|
||||
|
||||
package main
|
||||
|
||||
|
@ -23,10 +24,10 @@ type AutheliaCommandDefinition struct {
|
|||
SubCommands []*cobra.Command
|
||||
}
|
||||
|
||||
// CobraCommands list of cobra commands
|
||||
// CobraCommands list of cobra commands.
|
||||
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{
|
||||
{
|
||||
Name: "bootstrap",
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
|
||||
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"
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -21,7 +21,7 @@ const (
|
|||
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}
|
||||
|
||||
const (
|
||||
|
|
|
@ -285,7 +285,7 @@ groups:
|
|||
- 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(`
|
||||
user:
|
||||
john:
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
// ********************* CONNECTION *********************
|
||||
// ********************* CONNECTION *********************.
|
||||
|
||||
// LDAPConnection interface representing a connection to the ldap.
|
||||
type LDAPConnection interface {
|
||||
|
@ -47,7 +47,7 @@ func (lc *LDAPConnectionImpl) Modify(modifyRequest *ldap.ModifyRequest) error {
|
|||
return lc.conn.Modify(modifyRequest)
|
||||
}
|
||||
|
||||
// ********************* FACTORY ***********************
|
||||
// ********************* FACTORY ***********************.
|
||||
|
||||
// LDAPConnectionFactory an interface of factory of ldap connections.
|
||||
type LDAPConnectionFactory interface {
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
// 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 {
|
||||
Algorithm string
|
||||
Iterations int
|
||||
|
@ -23,11 +23,11 @@ type PasswordHash struct {
|
|||
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) {
|
||||
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)
|
||||
h := &PasswordHash{}
|
||||
|
||||
|
@ -81,8 +81,8 @@ func ParseHash(hash string) (passwordHash *PasswordHash, err error) {
|
|||
return h, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
func HashPassword(password, salt, algorithm string, iterations, memory, parallelism, keyLength, saltLength int) (hash string, err error) {
|
||||
var settings string
|
||||
|
||||
|
@ -105,7 +105,7 @@ func HashPassword(password, salt, algorithm string, iterations, memory, parallel
|
|||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 == "" {
|
||||
|
@ -129,12 +129,12 @@ func HashPassword(password, salt, algorithm string, iterations, memory, parallel
|
|||
}
|
||||
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)
|
||||
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) {
|
||||
passwordHash, err := ParseHash(hash)
|
||||
if err != nil {
|
||||
|
|
|
@ -45,7 +45,7 @@ func TestShouldHashArgon2idPassword(t *testing.T) {
|
|||
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) {
|
||||
data := string(HashingPossibleSaltCharacters)
|
||||
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) {
|
||||
data := string(HashingPossibleSaltCharacters)
|
||||
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())
|
||||
}
|
||||
|
||||
// Object object to check access control for
|
||||
// Object object to check access control for.
|
||||
type Object struct {
|
||||
Domain string
|
||||
Path string
|
||||
|
|
|
@ -19,7 +19,7 @@ type SessionConfiguration struct {
|
|||
Redis *RedisSessionConfiguration `mapstructure:"redis"`
|
||||
}
|
||||
|
||||
// DefaultSessionConfiguration is the default session configuration
|
||||
// DefaultSessionConfiguration is the default session configuration.
|
||||
var DefaultSessionConfiguration = SessionConfiguration{
|
||||
Name: "authelia_session",
|
||||
Expiration: "1h",
|
||||
|
|
|
@ -5,7 +5,7 @@ type LocalStorageConfiguration struct {
|
|||
Path string `mapstructure:"path"`
|
||||
}
|
||||
|
||||
// SQLStorageConfiguration represents the configuration of the SQL database
|
||||
// SQLStorageConfiguration represents the configuration of the SQL database.
|
||||
type SQLStorageConfiguration struct {
|
||||
Host string `mapstructure:"host"`
|
||||
Port int `mapstructure:"port"`
|
||||
|
@ -14,12 +14,12 @@ type SQLStorageConfiguration struct {
|
|||
Password string `mapstructure:"password"`
|
||||
}
|
||||
|
||||
// MySQLStorageConfiguration represents the configuration of a MySQL database
|
||||
// MySQLStorageConfiguration represents the configuration of a MySQL database.
|
||||
type MySQLStorageConfiguration struct {
|
||||
SQLStorageConfiguration `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
// PostgreSQLStorageConfiguration represents the configuration of a Postgres database
|
||||
// PostgreSQLStorageConfiguration represents the configuration of a Postgres database.
|
||||
type PostgreSQLStorageConfiguration struct {
|
||||
SQLStorageConfiguration `mapstructure:",squash"`
|
||||
SSLMode string `mapstructure:"sslmode"`
|
||||
|
|
|
@ -7,19 +7,19 @@ import (
|
|||
"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 {
|
||||
Push(err error)
|
||||
HasErrors() bool
|
||||
Errors() []error
|
||||
}
|
||||
|
||||
// Validator represents the validator interface
|
||||
// Validator represents the validator interface.
|
||||
type Validator struct {
|
||||
errors map[string][]error
|
||||
}
|
||||
|
||||
// NewValidator create a validator
|
||||
// NewValidator create a validator.
|
||||
func NewValidator() *Validator {
|
||||
validator := new(Validator)
|
||||
validator.errors = make(map[string][]error)
|
||||
|
@ -67,7 +67,7 @@ func (v *Validator) validateOne(item QueueItem, q *queue.Queue) error { //nolint
|
|||
return nil
|
||||
}
|
||||
|
||||
// Validate validate a struct
|
||||
// Validate validate a struct.
|
||||
func (v *Validator) Validate(s interface{}) error {
|
||||
q := queue.New(40)
|
||||
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
|
||||
}
|
||||
|
||||
// PrintErrors display the errors thrown during validation
|
||||
// PrintErrors display the errors thrown during validation.
|
||||
func (v *Validator) PrintErrors() {
|
||||
for path, errs := range v.errors {
|
||||
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 {
|
||||
return v.errors
|
||||
}
|
||||
|
||||
// StructValidator is a validator for structs
|
||||
// StructValidator is a validator for structs.
|
||||
type StructValidator struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
// NewStructValidator is a constructor of struct validator
|
||||
// NewStructValidator is a constructor of struct validator.
|
||||
func NewStructValidator() *StructValidator {
|
||||
val := new(StructValidator)
|
||||
val.errors = make([]error, 0)
|
||||
|
@ -128,7 +128,7 @@ func (v *StructValidator) Errors() []error {
|
|||
return v.errors
|
||||
}
|
||||
|
||||
// Clear errors
|
||||
// Clear errors.
|
||||
func (v *StructValidator) Clear() {
|
||||
v.errors = []error{}
|
||||
}
|
||||
|
|
|
@ -9,14 +9,14 @@ import (
|
|||
"github.com/authelia/authelia/internal/middlewares"
|
||||
)
|
||||
|
||||
// NewDuoAPI create duo API instance
|
||||
// NewDuoAPI create duo API instance.
|
||||
func NewDuoAPI(duoAPI *duoapi.DuoApi) *APIImpl {
|
||||
api := new(APIImpl)
|
||||
api.DuoApi = duoAPI
|
||||
return api
|
||||
}
|
||||
|
||||
// Call call to the DuoAPI
|
||||
// Call call to the DuoAPI.
|
||||
func (d *APIImpl) Call(values url.Values, ctx *middlewares.AutheliaCtx) (*Response, error) {
|
||||
_, responseBytes, err := d.DuoApi.SignedCall("POST", "/auth/v2/auth", values)
|
||||
|
||||
|
|
|
@ -8,17 +8,17 @@ import (
|
|||
"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 {
|
||||
Call(values url.Values, ctx *middlewares.AutheliaCtx) (*Response, error)
|
||||
}
|
||||
|
||||
// APIImpl implementation of DuoAPI interface
|
||||
// APIImpl implementation of DuoAPI interface.
|
||||
type APIImpl struct {
|
||||
*duoapi.DuoApi
|
||||
}
|
||||
|
||||
// Response response coming from Duo API
|
||||
// Response response coming from Duo API.
|
||||
type Response struct {
|
||||
Response struct {
|
||||
Result string `json:"result"`
|
||||
|
|
|
@ -5,10 +5,10 @@ import (
|
|||
"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 {
|
||||
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"`
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ func secondFactorTOTPIdentityFinish(ctx *middlewares.AutheliaCtx, username strin
|
|||
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(
|
||||
middlewares.IdentityVerificationFinishArgs{
|
||||
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.
|
||||
}
|
||||
|
||||
// SecondFactorU2FIdentityFinish the handler for finishing the identity validation
|
||||
// SecondFactorU2FIdentityFinish the handler for finishing the identity validation.
|
||||
var SecondFactorU2FIdentityFinish = middlewares.IdentityVerificationFinish(
|
||||
middlewares.IdentityVerificationFinishArgs{
|
||||
ActionClaim: U2FRegistrationAction,
|
||||
|
|
|
@ -6,13 +6,13 @@ import (
|
|||
"github.com/authelia/authelia/internal/middlewares"
|
||||
)
|
||||
|
||||
// ResetPasswordPost handler for resetting passwords
|
||||
// ResetPasswordPost handler for resetting passwords.
|
||||
func ResetPasswordPost(ctx *middlewares.AutheliaCtx) {
|
||||
userSession := ctx.GetSession()
|
||||
|
||||
// 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
|
||||
// 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 {
|
||||
ctx.Error(fmt.Errorf("No identity verification process has been initiated"), unableToResetPasswordMessage)
|
||||
return
|
||||
|
|
|
@ -26,7 +26,7 @@ func isSchemeWSS(url *url.URL) bool {
|
|||
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) {
|
||||
originalURL := ctx.XOriginalURL()
|
||||
if originalURL != nil {
|
||||
|
@ -64,8 +64,8 @@ func getOriginalURL(ctx *middlewares.AutheliaCtx) (*url.URL, error) {
|
|||
return url, nil
|
||||
}
|
||||
|
||||
// parseBasicAuth parses an HTTP Basic Authentication string
|
||||
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true)
|
||||
// parseBasicAuth parses an HTTP Basic Authentication string.
|
||||
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
|
||||
func parseBasicAuth(auth string) (username, password string, err error) {
|
||||
if !strings.HasPrefix(auth, authPrefix) {
|
||||
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
|
||||
}
|
||||
|
||||
// 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,
|
||||
username string, userGroups []string, clientIP net.IP, authLevel authentication.Level) authorizationMatching {
|
||||
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
|
||||
// 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
|
||||
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)
|
||||
}
|
||||
|
||||
// If the user is not correctly authenticated, send a 401
|
||||
// If the user is not correctly authenticated, send a 401.
|
||||
if !authenticated {
|
||||
// Request Basic Authentication otherwise
|
||||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if 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
|
||||
maxInactivityPeriod := int64(ctx.Providers.SessionProvider.Inactivity.Seconds())
|
||||
if maxInactivityPeriod == 0 {
|
||||
|
@ -171,10 +171,10 @@ func hasUserBeenInactiveLongEnough(ctx *middlewares.AutheliaCtx) (bool, error) {
|
|||
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
|
||||
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 == ""
|
||||
|
||||
if isUserAnonymous && userSession.AuthenticationLevel != authentication.NotAuthenticated {
|
||||
|
@ -188,7 +188,7 @@ func verifyFromSessionCookie(targetURL url.URL, ctx *middlewares.AutheliaCtx) (u
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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) {
|
||||
// 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
|
||||
// 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"))
|
||||
if rd != "" {
|
||||
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
|
||||
}
|
||||
|
||||
// Mark current activity
|
||||
// Mark current activity.
|
||||
userSession.LastActivity = ctx.Clock.Now().Unix()
|
||||
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) {
|
||||
ctx.Logger.Tracef("Headers=%s", ctx.Request.Header.String())
|
||||
targetURL, err := getOriginalURL(ctx)
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"github.com/authelia/authelia/internal/session"
|
||||
)
|
||||
|
||||
// Test getOriginalURL
|
||||
// Test getOriginalURL.
|
||||
func TestShouldGetOriginalURLFromOriginalURLHeader(t *testing.T) {
|
||||
mock := mocks.NewMockAutheliaCtx(t)
|
||||
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())
|
||||
}
|
||||
|
||||
// Test parseBasicAuth
|
||||
// Test parseBasicAuth.
|
||||
func TestShouldRaiseWhenHeaderDoesNotContainBasicPrefix(t *testing.T) {
|
||||
_, _, err := parseBasicAuth("alzefzlfzemjfej==")
|
||||
assert.Error(t, err)
|
||||
|
@ -138,7 +138,7 @@ func TestShouldReturnUsernameAndPassword(t *testing.T) {
|
|||
assert.Equal(t, "password", password)
|
||||
}
|
||||
|
||||
// Test isTargetURLAuthorized
|
||||
// Test isTargetURLAuthorized.
|
||||
func TestShouldCheckAuthorizationMatching(t *testing.T) {
|
||||
type Rule struct {
|
||||
Policy string
|
||||
|
@ -185,7 +185,7 @@ func TestShouldCheckAuthorizationMatching(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test verifyBasicAuth
|
||||
// Test verifyBasicAuth.
|
||||
func TestShouldVerifyWrongCredentials(t *testing.T) {
|
||||
mock := mocks.NewMockAutheliaCtx(t)
|
||||
defer mock.Close()
|
||||
|
@ -473,7 +473,7 @@ func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) {
|
|||
past := clock.Now().Add(-1 * time.Hour)
|
||||
|
||||
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)
|
||||
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
||||
|
||||
|
@ -487,7 +487,7 @@ func TestShouldDestroySessionWhenInactiveForTooLong(t *testing.T) {
|
|||
|
||||
VerifyGet(mock.Ctx)
|
||||
|
||||
// The session has been destroyed
|
||||
// The session has been destroyed.
|
||||
newUserSession := mock.Ctx.GetSession()
|
||||
assert.Equal(t, "", newUserSession.Username)
|
||||
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
|
||||
|
@ -504,7 +504,7 @@ func TestShouldDestroySessionWhenInactiveForTooLongUsingDurationNotation(t *test
|
|||
clock.Set(time.Now())
|
||||
|
||||
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)
|
||||
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
||||
|
||||
|
@ -518,7 +518,7 @@ func TestShouldDestroySessionWhenInactiveForTooLongUsingDurationNotation(t *test
|
|||
|
||||
VerifyGet(mock.Ctx)
|
||||
|
||||
// The session has been destroyed
|
||||
// The session has been destroyed.
|
||||
newUserSession := mock.Ctx.GetSession()
|
||||
assert.Equal(t, "", newUserSession.Username)
|
||||
assert.Equal(t, authentication.NotAuthenticated, newUserSession.AuthenticationLevel)
|
||||
|
@ -544,7 +544,7 @@ func TestShouldKeepSessionWhenUserCheckedRememberMeAndIsInactiveForTooLong(t *te
|
|||
|
||||
VerifyGet(mock.Ctx)
|
||||
|
||||
// The session has been destroyed
|
||||
// The session has been destroyed.
|
||||
newUserSession := mock.Ctx.GetSession()
|
||||
assert.Equal(t, "john", newUserSession.Username)
|
||||
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
|
||||
|
@ -574,7 +574,7 @@ func TestShouldKeepSessionWhenInactivityTimeoutHasNotBeenExceeded(t *testing.T)
|
|||
|
||||
VerifyGet(mock.Ctx)
|
||||
|
||||
// The session has been destroyed
|
||||
// The session has been destroyed.
|
||||
newUserSession := mock.Ctx.GetSession()
|
||||
assert.Equal(t, "john", newUserSession.Username)
|
||||
assert.Equal(t, authentication.TwoFactor, newUserSession.AuthenticationLevel)
|
||||
|
@ -593,7 +593,7 @@ func TestShouldRedirectWhenSessionInactiveForTooLongAndRDParamProvided(t *testin
|
|||
clock.Set(time.Now())
|
||||
|
||||
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)
|
||||
assert.Equal(t, time.Second*10, mock.Ctx.Providers.SessionProvider.Inactivity)
|
||||
|
||||
|
@ -640,7 +640,7 @@ func TestShouldUpdateInactivityTimestampEvenWhenHittingForbiddenResources(t *tes
|
|||
|
||||
VerifyGet(mock.Ctx)
|
||||
|
||||
// The resource if forbidden
|
||||
// The resource if forbidden.
|
||||
assert.Equal(t, 403, mock.Ctx.Response.StatusCode())
|
||||
|
||||
// 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(
|
||||
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
|
||||
// with a different port as mentioned in https://tools.ietf.org/html/rfc6265#section-8.5
|
||||
// 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.
|
||||
assert.True(t, isURLUnderProtectedDomain(
|
||||
GetURL("https://mytest.example.com:8080/abc/?query=abc"), "example.com"))
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"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) {
|
||||
if targetURI == "" {
|
||||
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.
|
||||
}
|
||||
|
||||
// Handle2FAResponse handle the redirection upon 2FA authentication
|
||||
// Handle2FAResponse handle the redirection upon 2FA authentication.
|
||||
func Handle2FAResponse(ctx *middlewares.AutheliaCtx, targetURI string) {
|
||||
if targetURI == "" {
|
||||
if ctx.Configuration.DefaultRedirectionURL != "" {
|
||||
|
|
|
@ -17,7 +17,7 @@ func SetLevel(level logrus.Level) {
|
|||
logrus.SetLevel(level)
|
||||
}
|
||||
|
||||
// InitializeLogger initialize logger
|
||||
// InitializeLogger initialize logger.
|
||||
func InitializeLogger(filename string) error {
|
||||
callerLevels := []logrus.Level{}
|
||||
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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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)
|
||||
}
|
||||
|
||||
// ReplyUnauthorized response sent when user is unauthorized
|
||||
// ReplyUnauthorized response sent when user is unauthorized.
|
||||
func (c *AutheliaCtx) ReplyUnauthorized() {
|
||||
c.RequestCtx.Error(fasthttp.StatusMessage(fasthttp.StatusUnauthorized), fasthttp.StatusUnauthorized)
|
||||
// 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() {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
// ReplyOK is a helper method to reply ok
|
||||
// ReplyOK is a helper method to reply ok.
|
||||
func (c *AutheliaCtx) ReplyOK() {
|
||||
c.SetContentType(applicationJSONContentType)
|
||||
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 {
|
||||
err := json.Unmarshal(c.PostBody(), &value)
|
||||
|
||||
|
@ -147,7 +147,7 @@ func (c *AutheliaCtx) ParseBody(value interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetJSONBody Set json body
|
||||
// SetJSONBody Set json body.
|
||||
func (c *AutheliaCtx) SetJSONBody(value interface{}) error {
|
||||
b, err := json.Marshal(OKResponse{Status: "OK", Data: value})
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package middlewares
|
||||
|
||||
// JWTIssuer is
|
||||
// JWTIssuer is.
|
||||
const jwtIssuer = "Authelia"
|
||||
|
||||
const xForwardedProtoHeader = "X-Forwarded-Proto"
|
||||
|
|
|
@ -144,8 +144,7 @@ func TestShouldSucceedIdentityVerificationStartProcess(t *testing.T) {
|
|||
assert.Equal(t, 200, mock.Ctx.Response.StatusCode())
|
||||
}
|
||||
|
||||
// Test Finish process
|
||||
|
||||
// Test Finish process.
|
||||
type IdentityVerificationFinishProcess struct {
|
||||
suite.Suite
|
||||
|
||||
|
|
|
@ -54,23 +54,23 @@ type IdentityVerificationStartArgs struct {
|
|||
// is completed successfully.
|
||||
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
|
||||
|
||||
// The function retrieving the identity to who the email will be sent.
|
||||
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
|
||||
}
|
||||
|
||||
// IdentityVerificationFinishArgs represent the arguments used to customize the finishing phase
|
||||
// of the identity verification process.
|
||||
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
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -90,13 +90,13 @@ type IdentityVerificationFinishBody struct {
|
|||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// OKResponse model of a status OK response
|
||||
// OKResponse model of a status OK response.
|
||||
type OKResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// ErrorResponse model of an error response
|
||||
// ErrorResponse model of an error response.
|
||||
type ErrorResponse struct {
|
||||
Status string `json:"status"`
|
||||
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) {
|
||||
dir := filepath.Dir(n.path)
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
|
|
|
@ -2,5 +2,5 @@ package regulation
|
|||
|
||||
import "fmt"
|
||||
|
||||
// ErrUserIsBanned user is banned error message
|
||||
// ErrUserIsBanned user is banned error message.
|
||||
var ErrUserIsBanned = fmt.Errorf("User is banned")
|
||||
|
|
|
@ -38,7 +38,7 @@ func NewRegulator(configuration *schema.RegulationConfiguration, provider storag
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return r.storageProvider.AppendAuthenticationLog(models.AuthenticationAttempt{
|
||||
Username: username,
|
||||
|
|
|
@ -14,13 +14,13 @@ type EncryptingSerializer struct {
|
|||
key [32]byte
|
||||
}
|
||||
|
||||
// NewEncryptingSerializer return new encrypt instance
|
||||
// NewEncryptingSerializer return new encrypt instance.
|
||||
func NewEncryptingSerializer(secret string) *EncryptingSerializer {
|
||||
key := sha256.Sum256([]byte(secret))
|
||||
return &EncryptingSerializer{key}
|
||||
}
|
||||
|
||||
// Encode encode and encrypt session
|
||||
// Encode encode and encrypt session.
|
||||
func (e *EncryptingSerializer) Encode(src session.Dict) ([]byte, error) {
|
||||
if len(src.D) == 0 {
|
||||
return nil, nil
|
||||
|
@ -39,7 +39,7 @@ func (e *EncryptingSerializer) Encode(src session.Dict) ([]byte, error) {
|
|||
return encryptedDst, nil
|
||||
}
|
||||
|
||||
// Decode decrypt and decode session
|
||||
// Decode decrypt and decode session.
|
||||
func (e *EncryptingSerializer) Decode(dst *session.Dict, src []byte) error {
|
||||
if len(src) == 0 {
|
||||
return nil
|
||||
|
|
|
@ -44,7 +44,7 @@ func NewProvider(configuration schema.SessionConfiguration) *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) {
|
||||
store, err := p.sessionHolder.Get(ctx)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"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 {
|
||||
config := session.NewDefaultConfig()
|
||||
|
||||
|
@ -23,7 +23,7 @@ func NewProviderConfig(configuration schema.SessionConfiguration) ProviderConfig
|
|||
// Only serve the header over HTTPS.
|
||||
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)
|
||||
|
||||
// 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,
|
||||
Port: configuration.Redis.Port,
|
||||
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,
|
||||
PoolSize: 8,
|
||||
IdleTimeout: 300,
|
||||
|
|
|
@ -14,7 +14,7 @@ type ProviderConfig struct {
|
|||
providerConfig session.ProviderConfig
|
||||
}
|
||||
|
||||
// U2FRegistration is a serializable version of a U2F registration
|
||||
// U2FRegistration is a serializable version of a U2F registration.
|
||||
type U2FRegistration struct {
|
||||
KeyHandle []byte
|
||||
PublicKey []byte
|
||||
|
|
|
@ -9,24 +9,24 @@ var totpSecretsTableName = "totp_secrets"
|
|||
var u2fDeviceHandlesTableName = "u2f_devices"
|
||||
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(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
username VARCHAR(100) PRIMARY KEY,
|
||||
second_factor_method VARCHAR(11)
|
||||
)`, 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(`
|
||||
CREATE TABLE IF NOT EXISTS %s (token VARCHAR(512))
|
||||
`, identityVerificationTokensTableName)
|
||||
|
||||
// SQLCreateTOTPSecretsTable common SQL query to create totp_secrets table
|
||||
// SQLCreateTOTPSecretsTable common SQL query to create totp_secrets table.
|
||||
var SQLCreateTOTPSecretsTable = fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100) PRIMARY KEY, secret VARCHAR(64))
|
||||
`, 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(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
username VARCHAR(100) PRIMARY KEY,
|
||||
|
@ -34,7 +34,7 @@ CREATE TABLE IF NOT EXISTS %s (
|
|||
publicKey TEXT
|
||||
)`, u2fDeviceHandlesTableName)
|
||||
|
||||
// SQLCreateAuthenticationLogsTable common SQL query to create authentication_logs table
|
||||
// SQLCreateAuthenticationLogsTable common SQL query to create authentication_logs table.
|
||||
var SQLCreateAuthenticationLogsTable = fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
username VARCHAR(100),
|
||||
|
|
|
@ -10,12 +10,12 @@ import (
|
|||
"github.com/authelia/authelia/internal/logging"
|
||||
)
|
||||
|
||||
// MySQLProvider is a MySQL provider
|
||||
// MySQLProvider is a MySQL provider.
|
||||
type MySQLProvider struct {
|
||||
SQLProvider
|
||||
}
|
||||
|
||||
// NewMySQLProvider a MySQL provider
|
||||
// NewMySQLProvider a MySQL provider.
|
||||
func NewMySQLProvider(configuration schema.MySQLStorageConfiguration) *MySQLProvider {
|
||||
connectionString := configuration.Username
|
||||
|
||||
|
|
|
@ -11,12 +11,12 @@ import (
|
|||
"github.com/authelia/authelia/internal/logging"
|
||||
)
|
||||
|
||||
// PostgreSQLProvider is a Postrgres provider
|
||||
// PostgreSQLProvider is a PostgreSQL provider.
|
||||
type PostgreSQLProvider struct {
|
||||
SQLProvider
|
||||
}
|
||||
|
||||
// NewPostgreSQLProvider a SQL provider
|
||||
// NewPostgreSQLProvider a PostgreSQL provider.
|
||||
func NewPostgreSQLProvider(configuration schema.PostgreSQLStorageConfiguration) *PostgreSQLProvider {
|
||||
args := make([]string, 0)
|
||||
if configuration.Username != "" {
|
||||
|
|
|
@ -9,12 +9,12 @@ import (
|
|||
"github.com/authelia/authelia/internal/logging"
|
||||
)
|
||||
|
||||
// SQLiteProvider is a sqlite3 provider
|
||||
// SQLiteProvider is a SQLite3 provider.
|
||||
type SQLiteProvider struct {
|
||||
SQLProvider
|
||||
}
|
||||
|
||||
// NewSQLiteProvider construct a sqlite provider.
|
||||
// NewSQLiteProvider constructs a SQLite provider.
|
||||
func NewSQLiteProvider(path string) *SQLiteProvider {
|
||||
db, err := sql.Open("sqlite3", path)
|
||||
if err != nil {
|
||||
|
|
|
@ -28,13 +28,13 @@ func (wds *WebDriverSession) doFillLoginPageAndClick(ctx context.Context, t *tes
|
|||
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) {
|
||||
wds.doVisitLoginPage(ctx, t, targetURL)
|
||||
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) {
|
||||
wds.doLoginOneFactor(ctx, t, username, password, keepMeLoggedIn, targetURL)
|
||||
wds.verifyIsSecondFactorPage(ctx, t)
|
||||
|
|
|
@ -2,41 +2,41 @@ package suites
|
|||
|
||||
import "fmt"
|
||||
|
||||
// BaseDomain the base domain
|
||||
// BaseDomain the base domain.
|
||||
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)
|
||||
|
||||
// SingleFactorBaseURL the base URL of the singlefactor domain
|
||||
// SingleFactorBaseURL the base URL of the singlefactor domain.
|
||||
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)
|
||||
|
||||
// MailBaseURL the base URL of the mail domain
|
||||
// MailBaseURL the base URL of the mail domain.
|
||||
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)
|
||||
|
||||
// PublicBaseURL the base URL of the public domain
|
||||
// PublicBaseURL the base URL of the public domain.
|
||||
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)
|
||||
|
||||
// DevBaseURL the base URL of the dev domain
|
||||
// DevBaseURL the base URL of the dev domain.
|
||||
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)
|
||||
|
||||
// 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)
|
||||
|
||||
// DuoBaseURL the base URL of the Duo configuration API
|
||||
// DuoBaseURL the base URL of the Duo configuration API.
|
||||
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"
|
||||
|
|
|
@ -11,12 +11,12 @@ import (
|
|||
"github.com/authelia/authelia/internal/utils"
|
||||
)
|
||||
|
||||
// DockerEnvironment represent a docker environment
|
||||
// DockerEnvironment represent a docker environment.
|
||||
type DockerEnvironment struct {
|
||||
dockerComposeFiles []string
|
||||
}
|
||||
|
||||
// NewDockerEnvironment create a new docker environment
|
||||
// NewDockerEnvironment create a new docker environment.
|
||||
func NewDockerEnvironment(files []string) *DockerEnvironment {
|
||||
if os.Getenv("CI") == "true" {
|
||||
for i := range files {
|
||||
|
@ -42,22 +42,22 @@ func (de *DockerEnvironment) createCommand(cmd string) *exec.Cmd {
|
|||
return utils.Command("bash", "-c", dockerCmdLine)
|
||||
}
|
||||
|
||||
// Up spawn a docker environment
|
||||
// Up spawn a docker environment.
|
||||
func (de *DockerEnvironment) Up() error {
|
||||
return de.createCommandWithStdout("up --build -d").Run()
|
||||
}
|
||||
|
||||
// Restart restarts a service
|
||||
// Restart restarts a service.
|
||||
func (de *DockerEnvironment) Restart(service string) error {
|
||||
return de.createCommandWithStdout(fmt.Sprintf("restart %s", service)).Run()
|
||||
}
|
||||
|
||||
// Down spawn a docker environment
|
||||
// Down spawn a docker environment.
|
||||
func (de *DockerEnvironment) Down() error {
|
||||
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) {
|
||||
cmd := de.createCommand(fmt.Sprintf("logs %s %s", strings.Join(flags, " "), service))
|
||||
content, err := cmd.Output()
|
||||
|
|
|
@ -8,17 +8,17 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// DuoPolicy a type of policy
|
||||
// DuoPolicy a type of policy.
|
||||
type DuoPolicy int32
|
||||
|
||||
const (
|
||||
// Deny deny policy
|
||||
// Deny deny policy.
|
||||
Deny DuoPolicy = iota
|
||||
// Allow allow policy
|
||||
// Allow allow policy.
|
||||
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) {
|
||||
url := fmt.Sprintf("%s/allow", DuoBaseURL)
|
||||
if allowDeny == Deny {
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"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 {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
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)
|
||||
|
||||
// Kind used for running kind commands
|
||||
// Kind used for running kind commands.
|
||||
type Kind struct{}
|
||||
|
||||
func kindCommand(cmdline string) *exec.Cmd {
|
||||
|
@ -20,7 +20,7 @@ func kindCommand(cmdline string) *exec.Cmd {
|
|||
return utils.Shell(cmd)
|
||||
}
|
||||
|
||||
// CreateCluster create a new Kubernetes cluster
|
||||
// CreateCluster create a new Kubernetes cluster.
|
||||
func (k Kind) CreateCluster() error {
|
||||
cmd := kindCommand("kind create cluster --config /etc/kind/config.yml")
|
||||
if err := cmd.Run(); err != nil {
|
||||
|
@ -32,7 +32,7 @@ func (k Kind) CreateCluster() error {
|
|||
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.
|
||||
// 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 -'")
|
||||
|
@ -42,13 +42,13 @@ func (k Kind) CreateCluster() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeleteCluster delete a Kubernetes cluster
|
||||
// DeleteCluster delete a Kubernetes cluster.
|
||||
func (k Kind) DeleteCluster() error {
|
||||
cmd := kindCommand("kind delete cluster")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// ClusterExists check whether a cluster exists
|
||||
// ClusterExists check whether a cluster exists.
|
||||
func (k Kind) ClusterExists() (bool, error) {
|
||||
cmd := kindCommand("kind get clusters")
|
||||
cmd.Stdout = nil
|
||||
|
@ -62,28 +62,28 @@ func (k Kind) ClusterExists() (bool, error) {
|
|||
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 {
|
||||
cmd := kindCommand(fmt.Sprintf("kind load docker-image %s", imageName))
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Kubectl used for running kubectl commands
|
||||
// Kubectl used for running kubectl commands.
|
||||
type Kubectl struct{}
|
||||
|
||||
// StartProxy start a proxy
|
||||
// StartProxy start a proxy.
|
||||
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")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// StopProxy stop a proxy
|
||||
// StopProxy stop a proxy.
|
||||
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")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// StartDashboard start Kube dashboard
|
||||
// StartDashboard start Kube dashboard.
|
||||
func (k Kubectl) StartDashboard() error {
|
||||
if err := kindCommand("sh -c 'cd /authelia && ./bootstrap-dashboard.sh'").Run(); err != nil {
|
||||
return err
|
||||
|
@ -95,25 +95,25 @@ func (k Kubectl) StartDashboard() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// StopDashboard stop kube dashboard
|
||||
// StopDashboard stop kube dashboard.
|
||||
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")
|
||||
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 {
|
||||
cmd := kindCommand("sh -c 'cd /authelia && ./bootstrap.sh'")
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// DeployAuthelia deploy Authelia application
|
||||
// DeployAuthelia deploy Authelia application.
|
||||
func (k Kubectl) DeployAuthelia() error {
|
||||
cmd := kindCommand("sh -c 'cd /authelia && ./bootstrap-authelia.sh'")
|
||||
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 {
|
||||
return utils.CheckUntil(5*time.Second, timeout, func() (bool, error) {
|
||||
cmd := kindCommand("kubectl get -n authelia pods --no-headers")
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Suite the definition of a suite
|
||||
// Suite the definition of a suite.
|
||||
type Suite struct {
|
||||
SetUp func(tmpPath string) error
|
||||
SetUpTimeout time.Duration
|
||||
|
@ -15,7 +15,7 @@ type Suite struct {
|
|||
// Callback called when an error occur during setup phase.
|
||||
OnSetupTimeout func() error
|
||||
|
||||
// Callback called when at least one test fail
|
||||
// Callback called when at least one test fail.
|
||||
OnError func() error
|
||||
|
||||
TestTimeout time.Duration
|
||||
|
@ -27,24 +27,24 @@ type Suite struct {
|
|||
Description string
|
||||
}
|
||||
|
||||
// Registry represent a registry of suite by name
|
||||
// Registry represent a registry of suite by name.
|
||||
type Registry struct {
|
||||
registry map[string]Suite
|
||||
}
|
||||
|
||||
// GlobalRegistry a global registry used by Authelia tooling
|
||||
// GlobalRegistry a global registry used by Authelia tooling.
|
||||
var GlobalRegistry *Registry
|
||||
|
||||
func init() {
|
||||
GlobalRegistry = NewSuitesRegistry()
|
||||
}
|
||||
|
||||
// NewSuitesRegistry create a suites registry
|
||||
// NewSuitesRegistry create a suites registry.
|
||||
func NewSuitesRegistry() *Registry {
|
||||
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) {
|
||||
if _, found := sr.registry[name]; found {
|
||||
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
|
||||
}
|
||||
|
||||
// Get return a suite by name
|
||||
// Get return a suite by name.
|
||||
func (sr *Registry) Get(name string) Suite {
|
||||
s, found := sr.registry[name]
|
||||
if !found {
|
||||
|
@ -61,7 +61,7 @@ func (sr *Registry) Get(name string) Suite {
|
|||
return s
|
||||
}
|
||||
|
||||
// Suites list available suites
|
||||
// Suites list available suites.
|
||||
func (sr *Registry) Suites() []string {
|
||||
suites := make([]string, 0)
|
||||
for k := range sr.registry {
|
||||
|
|
|
@ -33,7 +33,7 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon2FA() {
|
|||
wds.verifySecretAuthorized(ctx, s.T())
|
||||
}
|
||||
|
||||
// from network 192.168.240.201/32
|
||||
// from network 192.168.240.201/32.
|
||||
func (s *NetworkACLSuite) TestShouldAccessSecretUpon1FA() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
@ -51,7 +51,7 @@ func (s *NetworkACLSuite) TestShouldAccessSecretUpon1FA() {
|
|||
wds.verifySecretAuthorized(ctx, s.T())
|
||||
}
|
||||
|
||||
// from network 192.168.240.202/32
|
||||
// from network 192.168.240.202/32.
|
||||
func (s *NetworkACLSuite) TestShouldAccessSecretUpon0FA() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
|
|
@ -57,11 +57,11 @@ func (s *StandaloneWebDriverSuite) TestShouldLetUserKnowHeIsAlreadyAuthenticated
|
|||
|
||||
_ = 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.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.verifyIsAuthenticatedPage(ctx, s.T())
|
||||
}
|
||||
|
@ -73,22 +73,22 @@ func (s *StandaloneWebDriverSuite) TestShouldCheckUserIsAskedToRegisterDevice()
|
|||
username := "john"
|
||||
password := "password"
|
||||
|
||||
// Clean up any TOTP secret already in DB
|
||||
// Clean up any TOTP secret already in DB.
|
||||
provider := storage.NewSQLiteProvider("/tmp/db.sqlite3")
|
||||
require.NoError(s.T(), provider.DeleteTOTPSecret(username))
|
||||
|
||||
// Login one factor
|
||||
// Login one factor.
|
||||
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")
|
||||
|
||||
// Then register the TOTP factor
|
||||
// Then register the TOTP factor.
|
||||
s.doRegisterTOTP(ctx, s.T())
|
||||
// And logout
|
||||
// And logout.
|
||||
s.doLogout(ctx, s.T())
|
||||
|
||||
// Login one factor again
|
||||
// Login one factor again.
|
||||
s.doLoginOneFactor(ctx, s.T(), username, password, false, "")
|
||||
|
||||
// now the user should be asked to perform 2FA
|
||||
|
@ -103,7 +103,7 @@ func NewStandaloneSuite() *StandaloneSuite {
|
|||
return &StandaloneSuite{}
|
||||
}
|
||||
|
||||
// Standard case using nginx
|
||||
// Standard case using nginx.
|
||||
func (s *StandaloneSuite) TestShouldVerifyAPIVerifyUnauthorize() {
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/verify", AutheliaBaseURL), nil)
|
||||
s.Assert().NoError(err)
|
||||
|
@ -119,7 +119,7 @@ func (s *StandaloneSuite) TestShouldVerifyAPIVerifyUnauthorize() {
|
|||
s.Assert().Equal(string(body), "Unauthorized")
|
||||
}
|
||||
|
||||
// Standard case using Kubernetes
|
||||
// Standard case using Kubernetes.
|
||||
func (s *StandaloneSuite) TestShouldVerifyAPIVerifyRedirectFromXOriginalURL() {
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/verify?rd=%s", AutheliaBaseURL, LoginBaseURL), nil)
|
||||
s.Assert().NoError(err)
|
||||
|
|
|
@ -5,14 +5,14 @@ import (
|
|||
"github.com/tebeka/selenium"
|
||||
)
|
||||
|
||||
// SeleniumSuite is a selenium suite
|
||||
// SeleniumSuite is a selenium suite.
|
||||
type SeleniumSuite struct {
|
||||
suite.Suite
|
||||
|
||||
*WebDriverSession
|
||||
}
|
||||
|
||||
// WebDriver return the webdriver of the suite
|
||||
// WebDriver return the webdriver of the suite.
|
||||
func (s *SeleniumSuite) WebDriver() selenium.WebDriver {
|
||||
return s.WebDriverSession.WebDriver
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ type WebDriverSession struct {
|
|||
WebDriver selenium.WebDriver
|
||||
}
|
||||
|
||||
// StartWebDriverWithProxy create a selenium session
|
||||
// StartWebDriverWithProxy create a selenium session.
|
||||
func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error) {
|
||||
service, err := selenium.NewChromeDriverService("/usr/bin/chromedriver", port)
|
||||
|
||||
|
@ -62,12 +62,12 @@ func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error)
|
|||
}, nil
|
||||
}
|
||||
|
||||
// StartWebDriver create a selenium session
|
||||
// StartWebDriver create a selenium session.
|
||||
func StartWebDriver() (*WebDriverSession, error) {
|
||||
return StartWebDriverWithProxy("", 4444)
|
||||
}
|
||||
|
||||
// Stop stop the selenium session
|
||||
// Stop stop the selenium session.
|
||||
func (wds *WebDriverSession) Stop() error {
|
||||
err := wds.WebDriver.Quit()
|
||||
|
||||
|
@ -78,7 +78,7 @@ func (wds *WebDriverSession) Stop() error {
|
|||
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 {
|
||||
wds, err := StartWebDriver()
|
||||
|
||||
|
@ -91,7 +91,7 @@ func WithWebdriver(fn func(webdriver selenium.WebDriver) error) error {
|
|||
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 {
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
|
@ -148,37 +148,37 @@ func (wds *WebDriverSession) waitElementsLocated(ctx context.Context, t *testing
|
|||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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) {
|
||||
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
||||
text, err := element.Text()
|
||||
|
|
|
@ -2,21 +2,21 @@ package utils
|
|||
|
||||
import "time"
|
||||
|
||||
// Clock is an interface for a clock
|
||||
// Clock is an interface for a clock.
|
||||
type Clock interface {
|
||||
Now() 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{}
|
||||
|
||||
// Now return the current time
|
||||
// Now return the current time.
|
||||
func (RealClock) Now() time.Time {
|
||||
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 {
|
||||
return time.After(d)
|
||||
}
|
||||
|
|
|
@ -10,17 +10,17 @@ import (
|
|||
var ErrTimeoutReached = errors.New("timeout reached")
|
||||
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
|
||||
|
||||
// Day is an int based representation of the time unit
|
||||
// Day is an int based representation of the time unit.
|
||||
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
|
||||
|
||||
// Year is an int based representation of the time unit
|
||||
// Year is an int based representation of the time unit.
|
||||
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
|
||||
|
|
|
@ -15,11 +15,11 @@ import (
|
|||
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 {
|
||||
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()
|
||||
for !strings.HasSuffix(wd, "authelia") {
|
||||
wd = filepath.Dir(wd)
|
||||
|
@ -28,7 +28,7 @@ func Command(name string, args ...string) *exec.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 {
|
||||
cmd := Command(name, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
|
@ -36,12 +36,12 @@ func CommandWithStdout(name string, args ...string) *exec.Cmd {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// Shell create a shell command
|
||||
// Shell create a shell command.
|
||||
func Shell(command string) *exec.Cmd {
|
||||
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) {
|
||||
mutex := sync.Mutex{}
|
||||
cond := sync.NewCond(&mutex)
|
||||
|
@ -74,7 +74,7 @@ func RunCommandUntilCtrlC(cmd *exec.Cmd) {
|
|||
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 {
|
||||
mutex := sync.Mutex{}
|
||||
cond := sync.NewCond(&mutex)
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"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) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
// ParseDurationString parses a string to a duration
|
||||
// Duration notations are an integer followed by a unit
|
||||
// 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) {
|
||||
var duration time.Duration
|
||||
matches := parseDurationRegexp.FindStringSubmatch(input)
|
||||
|
|
Loading…
Reference in New Issue