authelia/internal/configuration/validator/keys.go

118 lines
2.4 KiB
Go
Raw Normal View History

package validator
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/utils"
)
// ValidateKeys determines if all provided keys are valid.
func ValidateKeys(keys []string, prefix string, validator *schema.StructValidator) {
var errStrings []string
var patterns []*regexp.Regexp
for _, key := range schema.Keys {
pattern, _ := NewKeyPattern(key)
switch {
case pattern == nil:
continue
default:
patterns = append(patterns, pattern)
}
}
KEYS:
for _, key := range keys {
expectedKey := reKeyReplacer.ReplaceAllString(key, "[]")
if utils.IsStringInSlice(expectedKey, schema.Keys) {
continue
}
if newKey, ok := replacedKeys[expectedKey]; ok {
validator.Push(fmt.Errorf(errFmtReplacedConfigurationKey, key, newKey))
continue
}
for _, p := range patterns {
if p.MatchString(expectedKey) {
continue KEYS
}
}
if err, ok := specificErrorKeys[expectedKey]; ok {
if !utils.IsStringInSlice(err, errStrings) {
errStrings = append(errStrings, err)
}
} else {
if strings.HasPrefix(key, prefix) {
validator.PushWarning(fmt.Errorf("configuration environment variable not expected: %s", key))
} else {
validator.Push(fmt.Errorf("configuration key not expected: %s", key))
}
}
}
for _, err := range errStrings {
validator.Push(errors.New(err))
}
}
// NewKeyPattern returns patterns which are required to match key patterns.
func NewKeyPattern(key string) (pattern *regexp.Regexp, err error) {
switch {
case reIsMapKey.MatchString(key):
return NewKeyMapPattern(key)
default:
return nil, nil
}
}
var reIsMapKey = regexp.MustCompile(`\.\*(\[]|\.)`)
// NewKeyMapPattern returns a pattern required to match map keys.
func NewKeyMapPattern(key string) (pattern *regexp.Regexp, err error) {
parts := strings.Split(key, ".*")
buf := &strings.Builder{}
buf.WriteString("^")
n := len(parts) - 1
for i, part := range parts {
if i != 0 && !strings.HasPrefix(part, "[]") {
buf.WriteString("\\.")
}
for j, r := range part {
// Skip prefixed period.
if j == 0 && r == '.' {
continue
}
switch r {
case '[', ']', '.', '{', '}':
buf.WriteRune('\\')
fallthrough
default:
buf.WriteRune(r)
}
}
if i < n {
buf.WriteString("\\.[a-z0-9](([a-z0-9-_]+)?[a-z0-9])?")
}
}
buf.WriteString("$")
return regexp.Compile(buf.String())
}