mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
2c42464fc8
* refactor: logging config key to log This refactors the recent pre-release change adding log options to their own configuration section in favor of a log section (from logging). * docs: add step to getting started to get the latest tagged commit This is so we avoid issues with changes on master having differences that don't work on the latest docker tag. * test: adjust tests * docs: adjust doc strings
283 lines
13 KiB
Go
283 lines
13 KiB
Go
package configuration
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"runtime"
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/authelia/authelia/internal/authentication"
|
|
"github.com/authelia/authelia/internal/utils"
|
|
)
|
|
|
|
func createTestingTempFile(t *testing.T, dir, name, content string) {
|
|
err := ioutil.WriteFile(path.Join(dir, name), []byte(content), 0600)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func resetEnv() {
|
|
_ = os.Unsetenv("AUTHELIA_JWT_SECRET_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_DUO_API_SECRET_KEY_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_SESSION_SECRET_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_SESSION_SECRET_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_SESSION_REDIS_PASSWORD_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE")
|
|
_ = os.Unsetenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE")
|
|
}
|
|
|
|
func setupEnv(t *testing.T) string {
|
|
resetEnv()
|
|
|
|
dirEnv := os.Getenv("AUTHELIA_TESTING_DIR")
|
|
if dirEnv != "" {
|
|
return dirEnv
|
|
}
|
|
|
|
dir := "/tmp/authelia" + utils.RandomString(10, authentication.HashingPossibleSaltCharacters) + "/"
|
|
err := os.MkdirAll(dir, 0700)
|
|
require.NoError(t, err)
|
|
|
|
createTestingTempFile(t, dir, "jwt", "secret_from_env")
|
|
createTestingTempFile(t, dir, "duo", "duo_secret_from_env")
|
|
createTestingTempFile(t, dir, "session", "session_secret_from_env")
|
|
createTestingTempFile(t, dir, "authentication", "ldap_secret_from_env")
|
|
createTestingTempFile(t, dir, "notifier", "smtp_secret_from_env")
|
|
createTestingTempFile(t, dir, "redis", "redis_secret_from_env")
|
|
createTestingTempFile(t, dir, "redis-sentinel", "redis-sentinel_secret_from_env")
|
|
createTestingTempFile(t, dir, "mysql", "mysql_secret_from_env")
|
|
createTestingTempFile(t, dir, "postgres", "postgres_secret_from_env")
|
|
|
|
require.NoError(t, os.Setenv("AUTHELIA_TESTING_DIR", dir))
|
|
|
|
return dir
|
|
}
|
|
|
|
func TestShouldErrorNoConfigPath(t *testing.T) {
|
|
_, errors := Read("")
|
|
|
|
require.Len(t, errors, 1)
|
|
|
|
require.EqualError(t, errors[0], "No config file path provided")
|
|
}
|
|
|
|
func TestShouldErrorSecretNotExist(t *testing.T) {
|
|
dir := "/path/not/exist"
|
|
|
|
require.NoError(t, os.Setenv("AUTHELIA_JWT_SECRET_FILE", dir+"jwt"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_DUO_API_SECRET_KEY_FILE", dir+"duo"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_SECRET_FILE", dir+"session"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE", dir+"authentication"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE", dir+"notifier"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_PASSWORD_FILE", dir+"redis"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE", dir+"redis-sentinel"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE", dir+"mysql"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE", dir+"postgres"))
|
|
|
|
_, errors := Read("./test_resources/config.yml")
|
|
|
|
require.Len(t, errors, 12)
|
|
|
|
if runtime.GOOS == windows {
|
|
assert.EqualError(t, errors[0], "error loading secret file (jwt_secret): open /path/not/existjwt: The system cannot find the path specified.")
|
|
assert.EqualError(t, errors[1], "error loading secret file (session.secret): open /path/not/existsession: The system cannot find the path specified.")
|
|
assert.EqualError(t, errors[2], "error loading secret file (duo_api.secret_key): open /path/not/existduo: The system cannot find the path specified.")
|
|
assert.EqualError(t, errors[3], "error loading secret file (session.redis.password): open /path/not/existredis: The system cannot find the path specified.")
|
|
assert.EqualError(t, errors[4], "error loading secret file (session.redis.high_availability.sentinel_password): open /path/not/existredis-sentinel: The system cannot find the path specified.")
|
|
assert.EqualError(t, errors[5], "error loading secret file (authentication_backend.ldap.password): open /path/not/existauthentication: The system cannot find the path specified.")
|
|
assert.EqualError(t, errors[6], "error loading secret file (notifier.smtp.password): open /path/not/existnotifier: The system cannot find the path specified.")
|
|
assert.EqualError(t, errors[7], "error loading secret file (storage.mysql.password): open /path/not/existmysql: The system cannot find the path specified.")
|
|
} else {
|
|
assert.EqualError(t, errors[0], "error loading secret file (jwt_secret): open /path/not/existjwt: no such file or directory")
|
|
assert.EqualError(t, errors[1], "error loading secret file (session.secret): open /path/not/existsession: no such file or directory")
|
|
assert.EqualError(t, errors[2], "error loading secret file (duo_api.secret_key): open /path/not/existduo: no such file or directory")
|
|
assert.EqualError(t, errors[3], "error loading secret file (session.redis.password): open /path/not/existredis: no such file or directory")
|
|
assert.EqualError(t, errors[4], "error loading secret file (session.redis.high_availability.sentinel_password): open /path/not/existredis-sentinel: no such file or directory")
|
|
assert.EqualError(t, errors[5], "error loading secret file (authentication_backend.ldap.password): open /path/not/existauthentication: no such file or directory")
|
|
assert.EqualError(t, errors[6], "error loading secret file (notifier.smtp.password): open /path/not/existnotifier: no such file or directory")
|
|
assert.EqualError(t, errors[7], "error loading secret file (storage.mysql.password): open /path/not/existmysql: no such file or directory")
|
|
}
|
|
|
|
assert.EqualError(t, errors[8], "Provide a JWT secret using \"jwt_secret\" key")
|
|
assert.EqualError(t, errors[9], "Please provide a password to connect to the LDAP server")
|
|
assert.EqualError(t, errors[10], "The session secret must be set when using the redis sentinel session provider")
|
|
assert.EqualError(t, errors[11], "the SQL username and password must be provided")
|
|
}
|
|
|
|
func TestShouldErrorPermissionsOnLocalFS(t *testing.T) {
|
|
if runtime.GOOS == windows {
|
|
t.Skip("skipping test due to being on windows")
|
|
}
|
|
|
|
resetEnv()
|
|
|
|
_ = os.Mkdir("/tmp/noperms/", 0000)
|
|
_, errors := Read("/tmp/noperms/configuration.yml")
|
|
|
|
require.Len(t, errors, 3)
|
|
|
|
require.EqualError(t, errors[0], "Unable to find config file: /tmp/noperms/configuration.yml")
|
|
require.EqualError(t, errors[1], "Generating config file: /tmp/noperms/configuration.yml")
|
|
require.EqualError(t, errors[2], "Unable to generate /tmp/noperms/configuration.yml: open /tmp/noperms/configuration.yml: permission denied")
|
|
}
|
|
|
|
func TestShouldErrorAndGenerateConfigFile(t *testing.T) {
|
|
_, errors := Read("./nonexistent.yml")
|
|
_ = os.Remove("./nonexistent.yml")
|
|
|
|
require.Len(t, errors, 3)
|
|
|
|
require.EqualError(t, errors[0], "Unable to find config file: ./nonexistent.yml")
|
|
require.EqualError(t, errors[1], "Generating config file: ./nonexistent.yml")
|
|
require.EqualError(t, errors[2], "Generated configuration at: ./nonexistent.yml")
|
|
}
|
|
|
|
func TestShouldErrorPermissionsConfigFile(t *testing.T) {
|
|
resetEnv()
|
|
|
|
_ = ioutil.WriteFile("/tmp/authelia/permissions.yml", []byte{}, 0000) // nolint:gosec
|
|
_, errors := Read("/tmp/authelia/permissions.yml")
|
|
|
|
if runtime.GOOS == windows {
|
|
require.Len(t, errors, 5)
|
|
assert.EqualError(t, errors[0], "Provide a JWT secret using \"jwt_secret\" key")
|
|
assert.EqualError(t, errors[1], "Please provide `ldap` or `file` object in `authentication_backend`")
|
|
assert.EqualError(t, errors[2], "Set domain of the session object")
|
|
assert.EqualError(t, errors[3], "A storage configuration must be provided. It could be 'local', 'mysql' or 'postgres'")
|
|
assert.EqualError(t, errors[4], "A notifier configuration must be provided")
|
|
} else {
|
|
require.Len(t, errors, 1)
|
|
|
|
assert.EqualError(t, errors[0], "Failed to open /tmp/authelia/permissions.yml: permission denied")
|
|
}
|
|
}
|
|
|
|
func TestShouldErrorParseBadConfigFile(t *testing.T) {
|
|
_, errors := Read("./test_resources/config_bad_quoting.yml")
|
|
|
|
require.Len(t, errors, 1)
|
|
|
|
require.EqualError(t, errors[0], "Error malformed yaml: line 26: did not find expected alphabetic or numeric character")
|
|
}
|
|
|
|
func TestShouldParseConfigFile(t *testing.T) {
|
|
dir := setupEnv(t)
|
|
|
|
require.NoError(t, os.Setenv("AUTHELIA_JWT_SECRET_FILE", dir+"jwt"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_DUO_API_SECRET_KEY_FILE", dir+"duo"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_SECRET_FILE", dir+"session"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE", dir+"authentication"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE", dir+"notifier"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_PASSWORD_FILE", dir+"redis"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE", dir+"redis-sentinel"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE", dir+"mysql"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE", dir+"postgres"))
|
|
|
|
config, errors := Read("./test_resources/config.yml")
|
|
|
|
require.Len(t, errors, 0)
|
|
|
|
assert.Equal(t, 9091, config.Port)
|
|
assert.Equal(t, "debug", config.Logging.Level)
|
|
assert.Equal(t, "https://home.example.com:8080/", config.DefaultRedirectionURL)
|
|
assert.Equal(t, "authelia.com", config.TOTP.Issuer)
|
|
assert.Equal(t, "secret_from_env", config.JWTSecret)
|
|
|
|
assert.Equal(t, "api-123456789.example.com", config.DuoAPI.Hostname)
|
|
assert.Equal(t, "ABCDEF", config.DuoAPI.IntegrationKey)
|
|
assert.Equal(t, "duo_secret_from_env", config.DuoAPI.SecretKey)
|
|
|
|
assert.Equal(t, "session_secret_from_env", config.Session.Secret)
|
|
assert.Equal(t, "ldap_secret_from_env", config.AuthenticationBackend.LDAP.Password)
|
|
assert.Equal(t, "smtp_secret_from_env", config.Notifier.SMTP.Password)
|
|
assert.Equal(t, "redis_secret_from_env", config.Session.Redis.Password)
|
|
assert.Equal(t, "redis-sentinel_secret_from_env", config.Session.Redis.HighAvailability.SentinelPassword)
|
|
assert.Equal(t, "mysql_secret_from_env", config.Storage.MySQL.Password)
|
|
|
|
assert.Equal(t, "deny", config.AccessControl.DefaultPolicy)
|
|
assert.Len(t, config.AccessControl.Rules, 12)
|
|
|
|
require.NotNil(t, config.Session)
|
|
require.NotNil(t, config.Session.Redis)
|
|
require.NotNil(t, config.Session.Redis.HighAvailability)
|
|
}
|
|
|
|
func TestShouldParseAltConfigFile(t *testing.T) {
|
|
dir := setupEnv(t)
|
|
|
|
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE", dir+"postgres"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE", dir+"authentication"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_JWT_SECRET_FILE", dir+"jwt"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_SECRET_FILE", dir+"session"))
|
|
|
|
config, errors := Read("./test_resources/config_alt.yml")
|
|
require.Len(t, errors, 0)
|
|
|
|
assert.Equal(t, 9091, config.Port)
|
|
assert.Equal(t, "debug", config.Logging.Level)
|
|
assert.Equal(t, "https://home.example.com:8080/", config.DefaultRedirectionURL)
|
|
assert.Equal(t, "authelia.com", config.TOTP.Issuer)
|
|
assert.Equal(t, "secret_from_env", config.JWTSecret)
|
|
|
|
assert.Equal(t, "api-123456789.example.com", config.DuoAPI.Hostname)
|
|
assert.Equal(t, "ABCDEF", config.DuoAPI.IntegrationKey)
|
|
assert.Equal(t, "postgres_secret_from_env", config.Storage.PostgreSQL.Password)
|
|
|
|
assert.Equal(t, "deny", config.AccessControl.DefaultPolicy)
|
|
assert.Len(t, config.AccessControl.Rules, 12)
|
|
}
|
|
|
|
func TestShouldNotParseConfigFileWithOldOrUnexpectedKeys(t *testing.T) {
|
|
dir := setupEnv(t)
|
|
|
|
require.NoError(t, os.Setenv("AUTHELIA_JWT_SECRET_FILE", dir+"jwt"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_DUO_API_SECRET_KEY_FILE", dir+"duo"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_SECRET_FILE", dir+"session"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE", dir+"authentication"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE", dir+"notifier"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_PASSWORD_FILE", dir+"redis"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE", dir+"mysql"))
|
|
|
|
_, errors := Read("./test_resources/config_bad_keys.yml")
|
|
require.Len(t, errors, 2)
|
|
|
|
// Sort error slice to prevent shenanigans that somehow occur
|
|
sort.Slice(errors, func(i, j int) bool {
|
|
return errors[i].Error() < errors[j].Error()
|
|
})
|
|
assert.EqualError(t, errors[0], "config key not expected: loggy_file")
|
|
assert.EqualError(t, errors[1], "invalid configuration key 'logs_level' was replaced by 'log.level'")
|
|
}
|
|
|
|
func TestShouldValidateConfigurationTemplate(t *testing.T) {
|
|
resetEnv()
|
|
|
|
_, errors := Read("../../config.template.yml")
|
|
assert.Len(t, errors, 0)
|
|
}
|
|
|
|
func TestShouldOnlyAllowEnvOrConfig(t *testing.T) {
|
|
dir := setupEnv(t)
|
|
|
|
resetEnv()
|
|
require.NoError(t, os.Setenv("AUTHELIA_JWT_SECRET_FILE", dir+"jwt"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_DUO_API_SECRET_KEY_FILE", dir+"duo"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_SECRET_FILE", dir+"session"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE", dir+"authentication"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE", dir+"notifier"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_PASSWORD_FILE", dir+"redis"))
|
|
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE", dir+"mysql"))
|
|
|
|
_, errors := Read("./test_resources/config_with_secret.yml")
|
|
|
|
require.Len(t, errors, 1)
|
|
require.EqualError(t, errors[0], "error loading secret (jwt_secret): it's already defined in the config file")
|
|
}
|