[MISC] Catch and warn on malformed configuration yaml (#1089)
If the configuration yaml is poorly indented or special values are not appropriately escaped Authelia attempts to load said configuration and fails. This attempts to unmarshal the config into an empty interface to catch and warn on malformed yaml. Using the example from issue https://github.com/authelia/authelia/issues/1053#issuecomment-634791662 ```yaml host: 0.0.0.0 port: 9091 log_level: debug jwt_secret: RUtG9TnbXrOl1XLLmDgySw1DGgx9QcrtepIf1uDDBlBVKFZxkVBruYKBi32PvaU default_redirection_url: example.com totp: issuer: example.com period: 30 skew: 1 authentication_backend: file: path: /etc/authelia/users_database.yml access_control: default_policy: deny rules: - domain: example.com policy: bypass - domain: "*.example.com" policy: one_factor session: name: authelia_session secret: TVPMIcDFbBwhnW3kLJzKhdjeHhtqisr7m28FgRY8oLh2A4lwuV2jV2ZGdGbh4aa expiration: 3600 inactivity: 300 domain: example.com regulation: max_retries: 3 find_time: 120 ban_time: 300 storage: mysql: host: example.com port: 3306 database: authelia username: authelia password: example.com notifier: smtp: username: example.com password: example.com host: smtp.gmail.com port: 465 sender: example.com ``` We would actually get a more meaningful error which helps pinpoint the issue: `Error malformed yaml: line 23: did not find expected alphabetic or numeric character`pull/1095/head^2
parent
7bd775f851
commit
a9b0caf4ee
|
@ -10,7 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAuthelia(t *testing.T) {
|
func TestCoverage(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
args []string
|
args []string
|
||||||
)
|
)
|
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -27,10 +26,6 @@ var configPathFlag string
|
||||||
|
|
||||||
//nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting
|
//nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting
|
||||||
func startServer() {
|
func startServer() {
|
||||||
if configPathFlag == "" {
|
|
||||||
log.Fatal(errors.New("No config file path provided"))
|
|
||||||
}
|
|
||||||
|
|
||||||
config, errs := configuration.Read(configPathFlag)
|
config, errs := configuration.Read(configPathFlag)
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
|
@ -38,7 +33,7 @@ func startServer() {
|
||||||
logging.Logger().Error(err)
|
logging.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
panic(errors.New("Some errors have been reported"))
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := logging.InitializeLogger(config.LogFilePath); err != nil {
|
if err := logging.InitializeLogger(config.LogFilePath); err != nil {
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
package configuration
|
package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/authelia/authelia/internal/configuration/schema"
|
"github.com/authelia/authelia/internal/configuration/schema"
|
||||||
"github.com/authelia/authelia/internal/configuration/validator"
|
"github.com/authelia/authelia/internal/configuration/validator"
|
||||||
|
@ -12,6 +16,27 @@ import (
|
||||||
|
|
||||||
// Read a YAML configuration and create a Configuration object out of it.
|
// Read a YAML configuration and create a Configuration object out of it.
|
||||||
func Read(configPath string) (*schema.Configuration, []error) {
|
func Read(configPath string) (*schema.Configuration, []error) {
|
||||||
|
if configPath == "" {
|
||||||
|
return nil, []error{errors.New("No config file path provided")}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, []error{fmt.Errorf("Unable to find config file: %v", configPath)}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := ioutil.ReadFile(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, []error{fmt.Errorf("Failed to %v", err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
var data interface{}
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(file, &data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, []error{fmt.Errorf("Error malformed %v", err)}
|
||||||
|
}
|
||||||
|
|
||||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||||
|
|
||||||
viper.BindEnv("authelia.jwt_secret.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
viper.BindEnv("authelia.jwt_secret.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||||
|
@ -25,11 +50,7 @@ func Read(configPath string) (*schema.Configuration, []error) {
|
||||||
|
|
||||||
viper.SetConfigFile(configPath)
|
viper.SetConfigFile(configPath)
|
||||||
|
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
_ = viper.ReadInConfig()
|
||||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
|
||||||
return nil, []error{fmt.Errorf("unable to find config file %s", configPath)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var configuration schema.Configuration
|
var configuration schema.Configuration
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,39 @@ func setupEnv(t *testing.T) string {
|
||||||
return 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 TestShouldErrorNoConfigFile(t *testing.T) {
|
||||||
|
_, errors := Read("./nonexistent.yml")
|
||||||
|
|
||||||
|
require.Len(t, errors, 1)
|
||||||
|
|
||||||
|
require.EqualError(t, errors[0], "Unable to find config file: ./nonexistent.yml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShouldErrorPermissionsConfigFile(t *testing.T) {
|
||||||
|
_ = ioutil.WriteFile("/tmp/authelia/permissions.yml", []byte{}, 0000) // nolint:gosec
|
||||||
|
_, errors := Read("/tmp/authelia/permissions.yml")
|
||||||
|
|
||||||
|
require.Len(t, errors, 1)
|
||||||
|
|
||||||
|
require.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 23: did not find expected alphabetic or numeric character")
|
||||||
|
}
|
||||||
|
|
||||||
func TestShouldParseConfigFile(t *testing.T) {
|
func TestShouldParseConfigFile(t *testing.T) {
|
||||||
dir := setupEnv(t)
|
dir := setupEnv(t)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
host: 0.0.0.0
|
||||||
|
port: 9091
|
||||||
|
log_level: debug
|
||||||
|
|
||||||
|
jwt_secret: RUtG9TnbXrOl1XLLmDgySw1DGgx9QcrtepIf1uDDBlBVKFZxkVBruYKBi32PvaU
|
||||||
|
|
||||||
|
default_redirection_url: example.com
|
||||||
|
|
||||||
|
totp:
|
||||||
|
issuer: example.com
|
||||||
|
period: 30
|
||||||
|
skew: 1
|
||||||
|
|
||||||
|
authentication_backend:
|
||||||
|
file:
|
||||||
|
path: /etc/authelia/users_database.yml
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
default_policy: deny
|
||||||
|
rules:
|
||||||
|
- domain: example.com
|
||||||
|
policy: bypass
|
||||||
|
- domain: *.example.com
|
||||||
|
policy: one_factor
|
||||||
|
|
||||||
|
session:
|
||||||
|
name: authelia_session
|
||||||
|
secret: TVPMIcDFbBwhnW3kLJzKhdjeHhtqisr7m28FgRY8oLh2A4lwuV2jV2ZGdGbh4aa
|
||||||
|
expiration: 3600
|
||||||
|
inactivity: 300
|
||||||
|
domain: example.com
|
||||||
|
|
||||||
|
regulation:
|
||||||
|
max_retries: 3
|
||||||
|
find_time: 120
|
||||||
|
ban_time: 300
|
||||||
|
|
||||||
|
storage:
|
||||||
|
mysql:
|
||||||
|
host: example.com
|
||||||
|
port: 3306
|
||||||
|
database: authelia
|
||||||
|
username: authelia
|
||||||
|
password: example.com
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
smtp:
|
||||||
|
username: example.com
|
||||||
|
password: example.com
|
||||||
|
host: smtp.gmail.com
|
||||||
|
port: 465
|
||||||
|
sender: example.com
|
Loading…
Reference in New Issue