summaryrefslogtreecommitdiff
path: root/internal/configuration/validator/keys.go
blob: ee2179135c28815c90f47085cc33c69ece5505d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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, remapped []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 utils.IsStringInSlice(expectedKey, remapped) {
			continue
		}

		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, "[]") {
			if len(parts) == i+1 && strings.HasSuffix(key, ".*") {
				continue
			}

			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-zA-Z0-9](([a-zA-Z0-9/_-]+)?[a-zA-Z0-9])?")
		}
	}

	buf.WriteString("$")

	return regexp.Compile(buf.String())
}