/
config.go
144 lines (124 loc) · 3.82 KB
/
config.go
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package generic
import (
"encoding/base64"
"fmt"
"net/url"
"regexp"
"strings"
"text/template"
"github.com/cyberark/secretless-broker/pkg/secretless/plugin/connector"
validation "github.com/go-ozzo/ozzo-validation"
)
type config struct {
CredentialPatterns map[string]*regexp.Regexp
Headers map[string]*template.Template
OAuth1Secrets map[string]*template.Template
QueryParams map[string]*template.Template
ForceSSL bool
}
// validate validates that the given creds satisfy the CredentialValidations of
// the config.
func (c *config) validate(credsByID connector.CredentialValuesByID) error {
for requiredCred, pattern := range c.CredentialPatterns {
credVal, ok := credsByID[requiredCred]
if !ok {
return fmt.Errorf("missing required credential: %q", requiredCred)
}
if !pattern.Match(credVal) {
return fmt.Errorf(
"credential %q doesn't match pattern %q", requiredCred, pattern,
)
}
}
return nil
}
// renderTemplates returns the config's templates filled in with the
// given credentialValues.
func renderTemplates(
template map[string]*template.Template,
credsByID connector.CredentialValuesByID,
) (map[string]string, error) {
errs := validation.Errors{}
args := make(map[string]string)
// Creds must be strings to work with templates
credStringsByID := make(map[string]string)
for credName, credBytes := range credsByID {
credStringsByID[credName] = string(credBytes)
}
for arg, tmpl := range template {
builder := &strings.Builder{}
if err := tmpl.Execute(builder, credStringsByID); err != nil {
errs[arg] = fmt.Errorf("couldn't render template: %q", err)
continue
}
args[arg] = builder.String()
}
if err := errs.Filter(); err != nil {
return nil, err
}
return args, nil
}
// newConfig takes a ConfigYAML, validates it, and converts it into a
// generic.config struct -- which is what our application wants to work with.
func newConfig(cfgYAML *ConfigYAML) (*config, error) {
errs := validation.Errors{}
cfg := &config{
CredentialPatterns: make(map[string]*regexp.Regexp),
ForceSSL: cfgYAML.ForceSSL,
}
// Validate and save regexps
for cred, reStr := range cfgYAML.CredentialValidations {
re, err := regexp.Compile(reStr)
if err != nil {
errs[cred] = fmt.Errorf("invalid regex: %q", err)
continue
}
cfg.CredentialPatterns[cred] = re
}
cfg.Headers, errs = stringsToTemplates(cfgYAML.Headers, errs)
cfg.QueryParams, errs = stringsToTemplates(cfgYAML.QueryParams, errs)
cfg.OAuth1Secrets, errs = stringsToTemplates(cfgYAML.OAuth1Secrets, errs)
if err := errs.Filter(); err != nil {
return nil, err
}
return cfg, nil
}
func stringsToTemplates(
templates map[string]string,
errs validation.Errors,
) (map[string]*template.Template, validation.Errors) {
parsedTemplates := make(map[string]*template.Template)
// Validate and save template strings
for tmplName, tmplStr := range templates {
tmpl := newHTTPTemplate(tmplName)
// Ignore pointer to receiver returned by Parse(): it's just "tmpl".
_, err := tmpl.Parse(tmplStr)
if err != nil {
errs[tmplName] = fmt.Errorf("invalid template: %q", err)
continue
}
parsedTemplates[tmplName] = tmpl
}
return parsedTemplates, errs
}
func appendQueryParams(URL url.URL, params map[string]string) string {
query := url.Values{}
if len(URL.RawQuery) > 0 {
query = URL.Query()
}
for key, value := range params {
query.Add(key, value)
}
return query.Encode()
}
// templateFuncs is a map holding the custom functions available for use within
// a header template string. We can easily add new functions as needed or
// requested.
var templateFuncs = template.FuncMap{
"base64": func(str string) string {
return base64.StdEncoding.EncodeToString([]byte(str))
},
}
func newHTTPTemplate(name string) *template.Template {
return template.New(name).Funcs(templateFuncs)
}