2019-04-25 04:52:08 +07:00
package configuration
import (
2020-05-08 08:01:57 +07:00
"io/ioutil"
2020-01-22 03:56:44 +07:00
"os"
2020-05-08 08:01:57 +07:00
"path"
2021-03-22 16:04:09 +07:00
"runtime"
2020-04-23 08:47:27 +07:00
"sort"
2019-04-25 04:52:08 +07:00
"testing"
"github.com/stretchr/testify/assert"
2020-01-22 03:56:44 +07:00
"github.com/stretchr/testify/require"
2020-05-08 08:01:57 +07:00
"github.com/authelia/authelia/internal/authentication"
"github.com/authelia/authelia/internal/utils"
2019-04-25 04:52:08 +07:00
)
2020-05-08 08:01:57 +07:00
func createTestingTempFile ( t * testing . T , dir , name , content string ) {
err := ioutil . WriteFile ( path . Join ( dir , name ) , [ ] byte ( content ) , 0600 )
require . NoError ( t , err )
}
2020-04-23 08:11:32 +07:00
func resetEnv ( ) {
2020-05-08 08:01:57 +07:00
_ = 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" )
2021-03-22 16:04:09 +07:00
_ = os . Unsetenv ( "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE" )
2020-05-08 08:01:57 +07:00
_ = 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" )
2021-03-22 16:04:09 +07:00
createTestingTempFile ( t , dir , "redis-sentinel" , "redis-sentinel_secret_from_env" )
2020-05-08 08:01:57 +07:00
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
2020-04-23 08:11:32 +07:00
}
2020-06-09 05:22:41 +07:00
func TestShouldErrorNoConfigPath ( t * testing . T ) {
_ , errors := Read ( "" )
require . Len ( t , errors , 1 )
require . EqualError ( t , errors [ 0 ] , "No config file path provided" )
}
2021-03-22 16:04:09 +07:00
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" )
}
2020-06-17 13:25:35 +07:00
func TestShouldErrorPermissionsOnLocalFS ( t * testing . T ) {
2021-03-22 16:04:09 +07:00
if runtime . GOOS == windows {
t . Skip ( "skipping test due to being on windows" )
}
resetEnv ( )
2020-06-17 13:25:35 +07:00
_ = 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 )
2020-06-09 05:22:41 +07:00
require . EqualError ( t , errors [ 0 ] , "Unable to find config file: ./nonexistent.yml" )
2020-06-17 13:25:35 +07:00
require . EqualError ( t , errors [ 1 ] , "Generating config file: ./nonexistent.yml" )
require . EqualError ( t , errors [ 2 ] , "Generated configuration at: ./nonexistent.yml" )
2020-06-09 05:22:41 +07:00
}
func TestShouldErrorPermissionsConfigFile ( t * testing . T ) {
2021-03-22 16:04:09 +07:00
resetEnv ( )
2020-06-09 05:22:41 +07:00
_ = ioutil . WriteFile ( "/tmp/authelia/permissions.yml" , [ ] byte { } , 0000 ) // nolint:gosec
_ , errors := Read ( "/tmp/authelia/permissions.yml" )
2021-03-22 16:04:09 +07:00
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" )
}
2020-06-09 05:22:41 +07:00
}
func TestShouldErrorParseBadConfigFile ( t * testing . T ) {
_ , errors := Read ( "./test_resources/config_bad_quoting.yml" )
require . Len ( t , errors , 1 )
2021-04-11 03:51:00 +07:00
require . EqualError ( t , errors [ 0 ] , "Error malformed yaml: line 24: did not find expected alphabetic or numeric character" )
2020-06-09 05:22:41 +07:00
}
2019-04-25 04:52:08 +07:00
func TestShouldParseConfigFile ( t * testing . T ) {
2020-05-08 08:01:57 +07:00
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" ) )
2021-03-22 16:04:09 +07:00
require . NoError ( t , os . Setenv ( "AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE" , dir + "redis-sentinel" ) )
2020-05-08 08:01:57 +07:00
require . NoError ( t , os . Setenv ( "AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE" , dir + "mysql" ) )
require . NoError ( t , os . Setenv ( "AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE" , dir + "postgres" ) )
2020-01-22 03:56:44 +07:00
2019-11-30 21:27:39 +07:00
config , errors := Read ( "./test_resources/config.yml" )
2019-04-25 04:52:08 +07:00
2020-01-22 03:56:44 +07:00
require . Len ( t , errors , 0 )
2019-04-25 04:52:08 +07:00
assert . Equal ( t , 9091 , config . Port )
2020-03-10 02:57:53 +07:00
assert . Equal ( t , "debug" , config . LogLevel )
2019-04-25 04:52:08 +07:00
assert . Equal ( t , "https://home.example.com:8080/" , config . DefaultRedirectionURL )
assert . Equal ( t , "authelia.com" , config . TOTP . Issuer )
2020-01-22 03:56:44 +07:00
assert . Equal ( t , "secret_from_env" , config . JWTSecret )
2019-04-25 04:52:08 +07:00
assert . Equal ( t , "api-123456789.example.com" , config . DuoAPI . Hostname )
assert . Equal ( t , "ABCDEF" , config . DuoAPI . IntegrationKey )
2020-01-22 03:56:44 +07:00
assert . Equal ( t , "duo_secret_from_env" , config . DuoAPI . SecretKey )
2020-01-22 05:02:03 +07:00
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 )
2021-03-22 16:04:09 +07:00
assert . Equal ( t , "redis-sentinel_secret_from_env" , config . Session . Redis . HighAvailability . SentinelPassword )
2020-01-22 05:02:03 +07:00
assert . Equal ( t , "mysql_secret_from_env" , config . Storage . MySQL . Password )
2020-04-23 08:11:32 +07:00
assert . Equal ( t , "deny" , config . AccessControl . DefaultPolicy )
assert . Len ( t , config . AccessControl . Rules , 12 )
2021-03-22 16:04:09 +07:00
require . NotNil ( t , config . Session )
require . NotNil ( t , config . Session . Redis )
require . NotNil ( t , config . Session . Redis . HighAvailability )
2020-04-23 08:11:32 +07:00
}
func TestShouldParseAltConfigFile ( t * testing . T ) {
2020-05-08 08:01:57 +07:00
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" ) )
2020-05-06 02:35:32 +07:00
2020-04-23 08:11:32 +07:00
config , errors := Read ( "./test_resources/config_alt.yml" )
require . Len ( t , errors , 0 )
assert . Equal ( t , 9091 , config . Port )
assert . Equal ( t , "debug" , config . LogLevel )
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 )
2020-01-22 05:02:03 +07:00
assert . Equal ( t , "postgres_secret_from_env" , config . Storage . PostgreSQL . Password )
2020-01-22 03:56:44 +07:00
assert . Equal ( t , "deny" , config . AccessControl . DefaultPolicy )
2020-04-16 07:18:11 +07:00
assert . Len ( t , config . AccessControl . Rules , 12 )
2019-04-25 04:52:08 +07:00
}
2020-04-23 08:11:32 +07:00
2020-04-23 08:47:27 +07:00
func TestShouldNotParseConfigFileWithOldOrUnexpectedKeys ( t * testing . T ) {
2020-05-08 08:01:57 +07:00
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" ) )
2020-04-23 08:47:27 +07:00
_ , 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 ] , "config key replaced: logs_level is now log_level" )
}
func TestShouldValidateConfigurationTemplate ( t * testing . T ) {
resetEnv ( )
2020-05-06 02:35:32 +07:00
2020-04-23 08:47:27 +07:00
_ , errors := Read ( "../../config.template.yml" )
assert . Len ( t , errors , 0 )
}
2020-04-23 08:11:32 +07:00
func TestShouldOnlyAllowEnvOrConfig ( t * testing . T ) {
2020-05-08 08:01:57 +07:00
dir := setupEnv ( t )
2020-04-23 08:11:32 +07:00
resetEnv ( )
2020-05-08 08:01:57 +07:00
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" ) )
2020-05-06 02:35:32 +07:00
2020-04-23 08:11:32 +07:00
_ , 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" )
}