You've already forked oauth2-proxy
mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-08-08 22:46:33 +02:00
Introduce alpha configuration loading
This commit is contained in:
@ -29,3 +29,11 @@ type AlphaOptions struct {
|
||||
// or from a static secret value.
|
||||
InjectResponseHeaders []Header `json:"injectResponseHeaders,omitempty"`
|
||||
}
|
||||
|
||||
// MergeInto replaces alpha options in the Options struct with the values
|
||||
// from the AlphaOptions
|
||||
func (a *AlphaOptions) MergeInto(opts *Options) {
|
||||
opts.UpstreamServers = a.Upstreams
|
||||
opts.InjectRequestHeaders = a.InjectRequestHeaders
|
||||
opts.InjectResponseHeaders = a.InjectResponseHeaders
|
||||
}
|
||||
|
@ -39,6 +39,15 @@ func NewLegacyOptions() *LegacyOptions {
|
||||
}
|
||||
}
|
||||
|
||||
func NewLegacyFlagSet() *pflag.FlagSet {
|
||||
flagSet := NewFlagSet()
|
||||
|
||||
flagSet.AddFlagSet(legacyUpstreamsFlagSet())
|
||||
flagSet.AddFlagSet(legacyHeadersFlagSet())
|
||||
|
||||
return flagSet
|
||||
}
|
||||
|
||||
func (l *LegacyOptions) ToOptions() (*Options, error) {
|
||||
upstreams, err := l.LegacyUpstreams.convert()
|
||||
if err != nil {
|
||||
|
@ -1,10 +1,13 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
@ -132,3 +135,28 @@ func isUnexported(name string) bool {
|
||||
first := string(name[0])
|
||||
return first == strings.ToLower(first)
|
||||
}
|
||||
|
||||
// LoadYAML will load a YAML based configuration file into the options interface provided.
|
||||
func LoadYAML(configFileName string, into interface{}) error {
|
||||
v := viper.New()
|
||||
v.SetConfigFile(configFileName)
|
||||
v.SetConfigType("yaml")
|
||||
v.SetTypeByDefaultValue(true)
|
||||
|
||||
if configFileName == "" {
|
||||
return errors.New("no configuration file provided")
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(configFileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load config file: %w", err)
|
||||
}
|
||||
|
||||
// UnmarshalStrict will return an error if the config includes options that are
|
||||
// not mapped to felds of the into struct
|
||||
if err := yaml.UnmarshalStrict(data, into, yaml.DisallowUnknownFields); err != nil {
|
||||
return fmt.Errorf("error unmarshalling config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
@ -295,10 +297,199 @@ var _ = Describe("Load", func() {
|
||||
expectedOutput: NewOptions(),
|
||||
}),
|
||||
Entry("with an empty LegacyOptions struct, should return default values", &testOptionsTableInput{
|
||||
flagSet: NewFlagSet,
|
||||
flagSet: NewLegacyFlagSet,
|
||||
input: &LegacyOptions{},
|
||||
expectedOutput: NewLegacyOptions(),
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("LoadYAML", func() {
|
||||
Context("with a testOptions structure", func() {
|
||||
type TestOptionSubStruct struct {
|
||||
StringSliceOption []string `yaml:"stringSliceOption,omitempty"`
|
||||
}
|
||||
|
||||
type TestOptions struct {
|
||||
StringOption string `yaml:"stringOption,omitempty"`
|
||||
Sub TestOptionSubStruct `yaml:"sub,omitempty"`
|
||||
|
||||
// Check that embedded fields can be unmarshalled
|
||||
TestOptionSubStruct `yaml:",inline,squash"`
|
||||
}
|
||||
|
||||
var testOptionsConfigBytesFull = []byte(`
|
||||
stringOption: foo
|
||||
stringSliceOption:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
sub:
|
||||
stringSliceOption:
|
||||
- d
|
||||
- e
|
||||
`)
|
||||
|
||||
type loadYAMLTableInput struct {
|
||||
configFile []byte
|
||||
input interface{}
|
||||
expectedErr error
|
||||
expectedOutput interface{}
|
||||
}
|
||||
|
||||
DescribeTable("LoadYAML",
|
||||
func(in loadYAMLTableInput) {
|
||||
var configFileName string
|
||||
|
||||
if in.configFile != nil {
|
||||
By("Creating a config file")
|
||||
configFile, err := ioutil.TempFile("", "oauth2-proxy-test-config-file")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer configFile.Close()
|
||||
|
||||
_, err = configFile.Write(in.configFile)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.Remove(configFile.Name())
|
||||
|
||||
configFileName = configFile.Name()
|
||||
}
|
||||
|
||||
var input interface{}
|
||||
if in.input != nil {
|
||||
input = in.input
|
||||
} else {
|
||||
input = &TestOptions{}
|
||||
}
|
||||
err := LoadYAML(configFileName, input)
|
||||
if in.expectedErr != nil {
|
||||
Expect(err).To(MatchError(in.expectedErr.Error()))
|
||||
} else {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(input).To(Equal(in.expectedOutput))
|
||||
},
|
||||
Entry("with a valid input", loadYAMLTableInput{
|
||||
configFile: testOptionsConfigBytesFull,
|
||||
input: &TestOptions{},
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "foo",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"d", "e"},
|
||||
},
|
||||
TestOptionSubStruct: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
Entry("with no config file", loadYAMLTableInput{
|
||||
configFile: nil,
|
||||
input: &TestOptions{},
|
||||
expectedOutput: &TestOptions{},
|
||||
expectedErr: errors.New("no configuration file provided"),
|
||||
}),
|
||||
Entry("with invalid YAML", loadYAMLTableInput{
|
||||
configFile: []byte("\tfoo: bar"),
|
||||
input: &TestOptions{},
|
||||
expectedOutput: &TestOptions{},
|
||||
expectedErr: errors.New("error unmarshalling config: error converting YAML to JSON: yaml: found character that cannot start any token"),
|
||||
}),
|
||||
Entry("with extra fields in the YAML", loadYAMLTableInput{
|
||||
configFile: append(testOptionsConfigBytesFull, []byte("foo: bar\n")...),
|
||||
input: &TestOptions{},
|
||||
expectedOutput: &TestOptions{
|
||||
StringOption: "foo",
|
||||
Sub: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"d", "e"},
|
||||
},
|
||||
TestOptionSubStruct: TestOptionSubStruct{
|
||||
StringSliceOption: []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
|
||||
}),
|
||||
Entry("with an incorrect type for a string field", loadYAMLTableInput{
|
||||
configFile: []byte(`stringOption: ["a", "b"]`),
|
||||
input: &TestOptions{},
|
||||
expectedOutput: &TestOptions{},
|
||||
expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go struct field TestOptions.StringOption of type string"),
|
||||
}),
|
||||
Entry("with an incorrect type for an array field", loadYAMLTableInput{
|
||||
configFile: []byte(`stringSliceOption: "a"`),
|
||||
input: &TestOptions{},
|
||||
expectedOutput: &TestOptions{},
|
||||
expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go struct field TestOptions.StringSliceOption of type []string"),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
It("should load a full example AlphaOptions", func() {
|
||||
config := []byte(`
|
||||
upstreams:
|
||||
- id: httpbin
|
||||
path: /
|
||||
uri: http://httpbin
|
||||
flushInterval: 500ms
|
||||
injectRequestHeaders:
|
||||
- name: X-Forwarded-User
|
||||
values:
|
||||
- claim: user
|
||||
injectResponseHeaders:
|
||||
- name: X-Secret
|
||||
values:
|
||||
- value: c2VjcmV0
|
||||
`)
|
||||
|
||||
By("Creating a config file")
|
||||
configFile, err := ioutil.TempFile("", "oauth2-proxy-test-alpha-config-file")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer configFile.Close()
|
||||
|
||||
_, err = configFile.Write(config)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.Remove(configFile.Name())
|
||||
|
||||
configFileName := configFile.Name()
|
||||
|
||||
By("Loading the example config")
|
||||
into := &AlphaOptions{}
|
||||
Expect(LoadYAML(configFileName, into)).To(Succeed())
|
||||
|
||||
flushInterval := Duration(500 * time.Millisecond)
|
||||
|
||||
Expect(into).To(Equal(&AlphaOptions{
|
||||
Upstreams: []Upstream{
|
||||
{
|
||||
ID: "httpbin",
|
||||
Path: "/",
|
||||
URI: "http://httpbin",
|
||||
FlushInterval: &flushInterval,
|
||||
},
|
||||
},
|
||||
InjectRequestHeaders: []Header{
|
||||
{
|
||||
Name: "X-Forwarded-User",
|
||||
Values: []HeaderValue{
|
||||
{
|
||||
ClaimSource: &ClaimSource{
|
||||
Claim: "user",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
InjectResponseHeaders: []Header{
|
||||
{
|
||||
Name: "X-Secret",
|
||||
Values: []HeaderValue{
|
||||
{
|
||||
SecretSource: &SecretSource{
|
||||
Value: []byte("secret"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
@ -246,8 +246,6 @@ func NewFlagSet() *pflag.FlagSet {
|
||||
|
||||
flagSet.AddFlagSet(cookieFlagSet())
|
||||
flagSet.AddFlagSet(loggingFlagSet())
|
||||
flagSet.AddFlagSet(legacyUpstreamsFlagSet())
|
||||
flagSet.AddFlagSet(legacyHeadersFlagSet())
|
||||
|
||||
return flagSet
|
||||
}
|
||||
|
Reference in New Issue
Block a user