introduce mapstructure decoder for yaml parsing
remove color output in tests for better readability in github actions bugfix: remove google as default provider for alpha options fix conversion flow for toml to yaml revert ginkgo color deactivation revert claim- and secret source back to pointers regenerate alpha config
This commit is contained in:
		
							parent
							
								
									8afb047e01
								
							
						
					
					
						commit
						31a4c34726
					
				|  | @ -37,7 +37,7 @@ linters: | |||
|       - linters: | ||||
|           - revive | ||||
|         path: _test\.go | ||||
|         text: 'dot-imports:' | ||||
|         text: "dot-imports:" | ||||
|       # # If we have tests in shared test folders, these can be less strictly linted | ||||
|       - linters: | ||||
|           - bodyclose | ||||
|  |  | |||
|  | @ -204,16 +204,6 @@ ClaimSource allows loading a header value from a claim within the session | |||
| | `prefix` | _string_ | Prefix is an optional prefix that will be prepended to the value of the<br/>claim if it is non-empty. | | ||||
| | `basicAuthPassword` | _[SecretSource](#secretsource)_ | BasicAuthPassword converts this claim into a basic auth header.<br/>Note the value of claim will become the basic auth username and the<br/>basicAuthPassword will be used as the password value. | | ||||
| 
 | ||||
| ### Duration | ||||
| #### (`string` alias) | ||||
| 
 | ||||
| (**Appears on:** [Upstream](#upstream)) | ||||
| 
 | ||||
| Duration is as string representation of a period of time. | ||||
| A duration string is a is a possibly signed sequence of decimal numbers, | ||||
| each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". | ||||
| Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". | ||||
| 
 | ||||
| ### GitHubOptions | ||||
| 
 | ||||
| (**Appears on:** [Provider](#provider)) | ||||
|  | @ -275,7 +265,7 @@ make up the header value | |||
| 
 | ||||
| | Field | Type | Description | | ||||
| | ----- | ---- | ----------- | | ||||
| | `value` | _[]byte_ | Value expects a base64 encoded string value. | | ||||
| | `value` | _string_ | Value expects a base64 encoded string value. | | ||||
| | `fromEnv` | _string_ | FromEnv expects the name of an environment variable. | | ||||
| | `fromFile` | _string_ | FromFile expects a path to a file containing the secret value. | | ||||
| | `claim` | _string_ | Claim is the name of the claim in the session that the value should be<br/>loaded from. Available claims: `access_token` `id_token` `created_at`<br/>`expires_on` `refresh_token` `email` `user` `groups` `preferred_username`. | | ||||
|  | @ -487,7 +477,7 @@ Only one source within the struct should be defined at any time. | |||
| 
 | ||||
| | Field | Type | Description | | ||||
| | ----- | ---- | ----------- | | ||||
| | `value` | _[]byte_ | Value expects a base64 encoded string value. | | ||||
| | `value` | _string_ | Value expects a base64 encoded string value. | | ||||
| | `fromEnv` | _string_ | FromEnv expects the name of an environment variable. | | ||||
| | `fromFile` | _string_ | FromFile expects a path to a file containing the secret value. | | ||||
| 
 | ||||
|  | @ -547,10 +537,10 @@ Requests will be proxied to this upstream if the path matches the request path. | |||
| | `insecureSkipTLSVerify` | _bool_ | InsecureSkipTLSVerify will skip TLS verification of upstream HTTPS hosts.<br/>This option is insecure and will allow potential Man-In-The-Middle attacks<br/>between OAuth2 Proxy and the upstream server.<br/>Defaults to false. | | ||||
| | `static` | _bool_ | Static will make all requests to this upstream have a static response.<br/>The response will have a body of "Authenticated" and a response code<br/>matching StaticCode.<br/>If StaticCode is not set, the response will return a 200 response. | | ||||
| | `staticCode` | _int_ | StaticCode determines the response code for the Static response.<br/>This option can only be used with Static enabled. | | ||||
| | `flushInterval` | _[Duration](#duration)_ | FlushInterval is the period between flushing the response buffer when<br/>streaming response from the upstream.<br/>Defaults to 1 second. | | ||||
| | `flushInterval` | _duration_ | FlushInterval is the period between flushing the response buffer when<br/>streaming response from the upstream.<br/>Defaults to 1 second. | | ||||
| | `passHostHeader` | _bool_ | PassHostHeader determines whether the request host header should be proxied<br/>to the upstream server.<br/>Defaults to true. | | ||||
| | `proxyWebSockets` | _bool_ | ProxyWebSockets enables proxying of websockets to upstream servers<br/>Defaults to true. | | ||||
| | `timeout` | _[Duration](#duration)_ | Timeout is the maximum duration the server will wait for a response from the upstream server.<br/>Defaults to 30 seconds. | | ||||
| | `timeout` | _duration_ | Timeout is the maximum duration the server will wait for a response from the upstream server.<br/>Defaults to 30 seconds. | | ||||
| | `disableKeepAlives` | _bool_ | DisableKeepAlives disables HTTP keep-alive connections to the upstream server.<br/>Defaults to false. | | ||||
| 
 | ||||
| ### UpstreamConfig | ||||
|  |  | |||
							
								
								
									
										35
									
								
								main.go
								
								
								
								
							
							
						
						
									
										35
									
								
								main.go
								
								
								
								
							|  | @ -67,12 +67,17 @@ func main() { | |||
| // loadConfiguration will load in the user's configuration.
 | ||||
| // It will either load the alpha configuration (if alphaConfig is given)
 | ||||
| // or the legacy configuration.
 | ||||
| func loadConfiguration(config, alphaConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) { | ||||
| 	if alphaConfig != "" { | ||||
| 		logger.Printf("WARNING: You are using alpha configuration. The structure in this configuration file may change without notice. You MUST remove conflicting options from your existing configuration.") | ||||
| 		return loadAlphaOptions(config, alphaConfig, extraFlags, args) | ||||
| func loadConfiguration(config, yamlConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) { | ||||
| 	opts, err := loadLegacyOptions(config, extraFlags, args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return loadLegacyOptions(config, extraFlags, args) | ||||
| 
 | ||||
| 	if yamlConfig != "" { | ||||
| 		logger.Printf("WARNING: You are using alpha configuration. The structure in this configuration file may change without notice. You MUST remove conflicting options from your existing configuration.") | ||||
| 		return loadYamlOptions(yamlConfig, config, extraFlags, args) | ||||
| 	} | ||||
| 	return opts, err | ||||
| } | ||||
| 
 | ||||
| // loadLegacyOptions loads the old toml options using the legacy flagset
 | ||||
|  | @ -97,17 +102,17 @@ func loadLegacyOptions(config string, extraFlags *pflag.FlagSet, args []string) | |||
| 	return opts, nil | ||||
| } | ||||
| 
 | ||||
| // loadAlphaOptions loads the old style config excluding options converted to
 | ||||
| // loadYamlOptions loads the old style config excluding options converted to
 | ||||
| // the new alpha format, then merges the alpha options, loaded from YAML,
 | ||||
| // into the core configuration.
 | ||||
| func loadAlphaOptions(config, alphaConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) { | ||||
| func loadYamlOptions(yamlConfig, config string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) { | ||||
| 	opts, err := loadOptions(config, extraFlags, args) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to load core options: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	alphaOpts := &options.AlphaOptions{} | ||||
| 	if err := options.LoadYAML(alphaConfig, alphaOpts); err != nil { | ||||
| 	alphaOpts := options.NewAlphaOptions(opts) | ||||
| 	if err := options.LoadYAML(yamlConfig, alphaOpts); err != nil { | ||||
| 		return nil, fmt.Errorf("failed to load alpha options: %v", err) | ||||
| 	} | ||||
| 
 | ||||
|  | @ -137,10 +142,16 @@ func loadOptions(config string, extraFlags *pflag.FlagSet, args []string) (*opti | |||
| // printConvertedConfig extracts alpha options from the loaded configuration
 | ||||
| // and renders these to stdout in YAML format.
 | ||||
| func printConvertedConfig(opts *options.Options) error { | ||||
| 	alphaConfig := &options.AlphaOptions{} | ||||
| 	alphaConfig.ExtractFrom(opts) | ||||
| 	alphaConfig := options.NewAlphaOptions(opts) | ||||
| 
 | ||||
| 	data, err := yaml.Marshal(alphaConfig) | ||||
| 	// Generic interface for loading arbitrary yaml structure
 | ||||
| 	var buffer map[string]interface{} | ||||
| 
 | ||||
| 	if err := options.Decode(alphaConfig, &buffer); err != nil { | ||||
| 		return fmt.Errorf("unable to decode alpha config into interface: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	data, err := yaml.Marshal(buffer) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("unable to marshal config: %v", err) | ||||
| 	} | ||||
|  |  | |||
							
								
								
									
										31
									
								
								main_test.go
								
								
								
								
							
							
						
						
									
										31
									
								
								main_test.go
								
								
								
								
							|  | @ -43,29 +43,35 @@ upstreamConfig: | |||
| injectRequestHeaders: | ||||
| - name: Authorization | ||||
|   values: | ||||
|   - claim: user | ||||
|   - claimSource: | ||||
|       claim: user | ||||
|       prefix: "Basic " | ||||
|       basicAuthPassword: | ||||
|       value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk | ||||
|         value: super-secret-password | ||||
| - name: X-Forwarded-Groups | ||||
|   values: | ||||
|   - claim: groups | ||||
|   - claimSource: | ||||
|       claim: groups | ||||
| - name: X-Forwarded-User | ||||
|   values: | ||||
|   - claim: user | ||||
|   - claimSource: | ||||
|       claim: user | ||||
| - name: X-Forwarded-Email | ||||
|   values: | ||||
|   - claim: email | ||||
|   - claimSource: | ||||
|       claim: email | ||||
| - name: X-Forwarded-Preferred-Username | ||||
|   values: | ||||
|   - claim: preferred_username | ||||
|   - claimSource: | ||||
|       claim: preferred_username | ||||
| injectResponseHeaders: | ||||
| - name: Authorization | ||||
|   values: | ||||
|   - claim: user | ||||
|   - claimSource: | ||||
|       claim: user | ||||
|       prefix: "Basic " | ||||
|       basicAuthPassword: | ||||
|       value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk | ||||
|         value: super-secret-password | ||||
| server: | ||||
|   bindAddress: "127.0.0.1:4180" | ||||
| providers: | ||||
|  | @ -100,9 +106,8 @@ redirect_url="http://localhost:4180/oauth2/callback" | |||
| 		return &b | ||||
| 	} | ||||
| 
 | ||||
| 	durationPtr := func(d time.Duration) *options.Duration { | ||||
| 		du := options.Duration(d) | ||||
| 		return &du | ||||
| 	durationPtr := func(d time.Duration) *time.Duration { | ||||
| 		return &d | ||||
| 	} | ||||
| 
 | ||||
| 	testExpectedOptions := func() *options.Options { | ||||
|  | @ -136,7 +141,7 @@ redirect_url="http://localhost:4180/oauth2/callback" | |||
| 						Claim:  "user", | ||||
| 						Prefix: "Basic ", | ||||
| 						BasicAuthPassword: &options.SecretSource{ | ||||
| 							Value: []byte("super-secret-password"), | ||||
| 							Value: "super-secret-password", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
|  | @ -226,7 +231,7 @@ redirect_url="http://localhost:4180/oauth2/callback" | |||
| 
 | ||||
| 			opts, err := loadConfiguration(configFileName, alphaConfigFileName, extraFlags, in.args) | ||||
| 			if in.expectedErr != nil { | ||||
| 				Expect(err).To(MatchError(in.expectedErr.Error())) | ||||
| 				Expect(err).To(MatchError(ContainSubstring(in.expectedErr.Error()))) | ||||
| 			} else { | ||||
| 				Expect(err).ToNot(HaveOccurred()) | ||||
| 			} | ||||
|  |  | |||
|  | @ -215,7 +215,7 @@ func TestBasicAuthPassword(t *testing.T) { | |||
| 					ClaimSource: &options.ClaimSource{ | ||||
| 						Claim: "email", | ||||
| 						BasicAuthPassword: &options.SecretSource{ | ||||
| 							Value: []byte(basicAuthPassword), | ||||
| 							Value: basicAuthPassword, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
|  | @ -1282,7 +1282,7 @@ func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) { | |||
| 					ClaimSource: &options.ClaimSource{ | ||||
| 						Claim: "user", | ||||
| 						BasicAuthPassword: &options.SecretSource{ | ||||
| 							Value: []byte("This is a secure password"), | ||||
| 							Value: "This is a secure password", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
|  | @ -2044,7 +2044,7 @@ func baseTestOptions() *options.Options { | |||
| 					ClaimSource: &options.ClaimSource{ | ||||
| 						Claim: "user", | ||||
| 						BasicAuthPassword: &options.SecretSource{ | ||||
| 							Value: []byte(base64.StdEncoding.EncodeToString([]byte("This is a secure password"))), | ||||
| 							Value: base64.StdEncoding.EncodeToString([]byte("This is a secure password")), | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
|  |  | |||
|  | @ -47,15 +47,11 @@ type AlphaOptions struct { | |||
| 	Providers Providers `json:"providers,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // MergeInto replaces alpha options in the Options struct with the values
 | ||||
| // from the AlphaOptions
 | ||||
| func (a *AlphaOptions) MergeInto(opts *Options) { | ||||
| 	opts.UpstreamServers = a.UpstreamConfig | ||||
| 	opts.InjectRequestHeaders = a.InjectRequestHeaders | ||||
| 	opts.InjectResponseHeaders = a.InjectResponseHeaders | ||||
| 	opts.Server = a.Server | ||||
| 	opts.MetricsServer = a.MetricsServer | ||||
| 	opts.Providers = a.Providers | ||||
| // Initialize alpha options with default values and settings of the core options
 | ||||
| func NewAlphaOptions(opts *Options) *AlphaOptions { | ||||
| 	aOpts := &AlphaOptions{} | ||||
| 	aOpts.ExtractFrom(opts) | ||||
| 	return aOpts | ||||
| } | ||||
| 
 | ||||
| // ExtractFrom populates the fields in the AlphaOptions with the values from
 | ||||
|  | @ -68,3 +64,14 @@ func (a *AlphaOptions) ExtractFrom(opts *Options) { | |||
| 	a.MetricsServer = opts.MetricsServer | ||||
| 	a.Providers = opts.Providers | ||||
| } | ||||
| 
 | ||||
| // MergeInto replaces alpha options in the Options struct with the values
 | ||||
| // from the AlphaOptions
 | ||||
| func (a *AlphaOptions) MergeInto(opts *Options) { | ||||
| 	opts.UpstreamServers = a.UpstreamConfig | ||||
| 	opts.InjectRequestHeaders = a.InjectRequestHeaders | ||||
| 	opts.InjectResponseHeaders = a.InjectResponseHeaders | ||||
| 	opts.Server = a.Server | ||||
| 	opts.MetricsServer = a.MetricsServer | ||||
| 	opts.Providers = a.Providers | ||||
| } | ||||
|  |  | |||
|  | @ -1,63 +0,0 @@ | |||
| package options | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // SecretSource references an individual secret value.
 | ||||
| // Only one source within the struct should be defined at any time.
 | ||||
| type SecretSource struct { | ||||
| 	// Value expects a base64 encoded string value.
 | ||||
| 	Value []byte `json:"value,omitempty"` | ||||
| 
 | ||||
| 	// FromEnv expects the name of an environment variable.
 | ||||
| 	FromEnv string `json:"fromEnv,omitempty"` | ||||
| 
 | ||||
| 	// FromFile expects a path to a file containing the secret value.
 | ||||
| 	FromFile string `json:"fromFile,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // Duration is an alias for time.Duration so that we can ensure the marshalling
 | ||||
| // and unmarshalling of string durations is done as users expect.
 | ||||
| // Intentional blank line below to keep this first part of the comment out of
 | ||||
| // any generated references.
 | ||||
| 
 | ||||
| // Duration is as string representation of a period of time.
 | ||||
| // A duration string is a is a possibly signed sequence of decimal numbers,
 | ||||
| // each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
 | ||||
| // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
 | ||||
| // +reference-gen:alias-name=string
 | ||||
| type Duration time.Duration | ||||
| 
 | ||||
| // UnmarshalJSON parses the duration string and sets the value of duration
 | ||||
| // to the value of the duration string.
 | ||||
| func (d *Duration) UnmarshalJSON(data []byte) error { | ||||
| 	input := string(data) | ||||
| 	if unquoted, err := strconv.Unquote(input); err == nil { | ||||
| 		input = unquoted | ||||
| 	} | ||||
| 
 | ||||
| 	du, err := time.ParseDuration(input) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*d = Duration(du) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // MarshalJSON ensures that when the string is marshalled to JSON as a human
 | ||||
| // readable string.
 | ||||
| func (d *Duration) MarshalJSON() ([]byte, error) { | ||||
| 	dStr := fmt.Sprintf("%q", d.Duration().String()) | ||||
| 	return []byte(dStr), nil | ||||
| } | ||||
| 
 | ||||
| // Duration returns the time.Duration version of this Duration
 | ||||
| func (d *Duration) Duration() time.Duration { | ||||
| 	if d == nil { | ||||
| 		return time.Duration(0) | ||||
| 	} | ||||
| 	return time.Duration(*d) | ||||
| } | ||||
|  | @ -0,0 +1,43 @@ | |||
| package options | ||||
| 
 | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/mitchellh/mapstructure" | ||||
| ) | ||||
| 
 | ||||
| // Duration is an alias for time.Duration so that we can ensure the marshalling
 | ||||
| // and unmarshalling of string durations is done as users expect.
 | ||||
| // Intentional blank line below to keep this first part of the comment out of
 | ||||
| // any generated references.
 | ||||
| 
 | ||||
| // Duration is as string representation of a period of time.
 | ||||
| // A duration string is a is a possibly signed sequence of decimal numbers,
 | ||||
| // each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
 | ||||
| // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
 | ||||
| 
 | ||||
| // Conversion from string or floating point to golang duration type
 | ||||
| // This way floating points will be converted to seconds and strings
 | ||||
| // of type 3s or 5m will be parsed with time.ParseDuration
 | ||||
| func toDurationHookFunc() mapstructure.DecodeHookFunc { | ||||
| 	return func( | ||||
| 		f reflect.Type, | ||||
| 		t reflect.Type, | ||||
| 		data interface{}) (interface{}, error) { | ||||
| 		if t != reflect.TypeOf(time.Duration(0)) { | ||||
| 			return data, nil | ||||
| 		} | ||||
| 
 | ||||
| 		switch f.Kind() { | ||||
| 		case reflect.String: | ||||
| 			return time.ParseDuration(data.(string)) | ||||
| 		case reflect.Float64: | ||||
| 			return time.Duration(data.(float64) * float64(time.Second)), nil | ||||
| 		case reflect.Int64: | ||||
| 			return time.Duration(data.(int64)), nil | ||||
| 		default: | ||||
| 			return data, nil | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -21,10 +21,10 @@ type Header struct { | |||
| // make up the header value
 | ||||
| type HeaderValue struct { | ||||
| 	// Allow users to load the value from a secret source
 | ||||
| 	*SecretSource `json:",omitempty"` | ||||
| 	*SecretSource `json:"secretSource,omitempty"` | ||||
| 
 | ||||
| 	// Allow users to load the value from a session claim
 | ||||
| 	*ClaimSource `json:",omitempty"` | ||||
| 	*ClaimSource `json:"claimSource,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // ClaimSource allows loading a header value from a claim within the session
 | ||||
|  |  | |||
|  | @ -136,8 +136,8 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) { | |||
| 			u.Path = "/" | ||||
| 		} | ||||
| 
 | ||||
| 		flushInterval := Duration(l.FlushInterval) | ||||
| 		timeout := Duration(l.Timeout) | ||||
| 		flushInterval := l.FlushInterval | ||||
| 		timeout := l.Timeout | ||||
| 		upstream := Upstream{ | ||||
| 			ID:                    u.Path, | ||||
| 			Path:                  u.Path, | ||||
|  | @ -294,7 +294,7 @@ func getBasicAuthHeader(preferEmailToUser bool, basicAuthPassword string) Header | |||
| 					Claim:  claim, | ||||
| 					Prefix: "Basic ", | ||||
| 					BasicAuthPassword: &SecretSource{ | ||||
| 						Value: []byte(basicAuthPassword), | ||||
| 						Value: basicAuthPassword, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
|  |  | |||
|  | @ -15,8 +15,8 @@ var _ = Describe("Legacy Options", func() { | |||
| 			legacyOpts := NewLegacyOptions() | ||||
| 
 | ||||
| 			// Set upstreams and related options to test their conversion
 | ||||
| 			flushInterval := Duration(5 * time.Second) | ||||
| 			timeout := Duration(5 * time.Second) | ||||
| 			flushInterval := 5 * time.Second | ||||
| 			timeout := 5 * time.Second | ||||
| 			legacyOpts.LegacyUpstreams.FlushInterval = time.Duration(flushInterval) | ||||
| 			legacyOpts.LegacyUpstreams.Timeout = time.Duration(timeout) | ||||
| 			legacyOpts.LegacyUpstreams.PassHostHeader = true | ||||
|  | @ -147,8 +147,8 @@ var _ = Describe("Legacy Options", func() { | |||
| 		skipVerify := true | ||||
| 		passHostHeader := false | ||||
| 		proxyWebSockets := true | ||||
| 		flushInterval := Duration(5 * time.Second) | ||||
| 		timeout := Duration(5 * time.Second) | ||||
| 		flushInterval := 5 * time.Second | ||||
| 		timeout := 5 * time.Second | ||||
| 		disableKeepAlives := true | ||||
| 
 | ||||
| 		// Test cases and expected outcomes
 | ||||
|  | @ -369,7 +369,7 @@ var _ = Describe("Legacy Options", func() { | |||
| 						Claim:  "user", | ||||
| 						Prefix: "Basic ", | ||||
| 						BasicAuthPassword: &SecretSource{ | ||||
| 							Value: []byte(basicAuthSecret), | ||||
| 							Value: basicAuthSecret, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
|  | @ -409,7 +409,7 @@ var _ = Describe("Legacy Options", func() { | |||
| 						Claim:  "email", | ||||
| 						Prefix: "Basic ", | ||||
| 						BasicAuthPassword: &SecretSource{ | ||||
| 							Value: []byte(basicAuthSecret), | ||||
| 							Value: basicAuthSecret, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
|  |  | |||
|  | @ -55,6 +55,76 @@ func Load(configFileName string, flagSet *pflag.FlagSet, into interface{}) error | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // LoadYAML will load a YAML based configuration file into the options interface provided.
 | ||||
| func LoadYAML(configFileName string, opts interface{}) error { | ||||
| 	buffer, err := loadAndSubstituteEnvs(configFileName) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// Generic interface for loading arbitrary yaml structure
 | ||||
| 	var intermediate map[string]interface{} | ||||
| 
 | ||||
| 	if err := yaml.Unmarshal(buffer, &intermediate); err != nil { | ||||
| 		return fmt.Errorf("error unmarshalling config: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return Decode(intermediate, opts) | ||||
| } | ||||
| 
 | ||||
| func Decode(input interface{}, result interface{}) error { | ||||
| 	// Using mapstructure to decode arbitrary yaml structure into options and
 | ||||
| 	// merge with existing values instead of overwriting everything. This is especially
 | ||||
| 	// important as we have a lot of default values for boolean which are supposed to be
 | ||||
| 	// true by default. Normally by just parsing through yaml all booleans that aren't
 | ||||
| 	// referenced in the config file would be parsed as false and we cannot identify after
 | ||||
| 	// the fact if they have been explicitly set to false or have not been referenced.
 | ||||
| 	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ | ||||
| 		DecodeHook:           mapstructure.ComposeDecodeHookFunc(toDurationHookFunc()), | ||||
| 		Metadata:             nil,    // Don't track any metadata
 | ||||
| 		Result:               result, // Decode the result into the prefilled options
 | ||||
| 		TagName:              "json", // Parse all fields that use the yaml tag
 | ||||
| 		ZeroFields:           false,  // Don't clean the default values from the result map (options)
 | ||||
| 		ErrorUnused:          true,   // Throw an error if keys have been used that aren't mapped to any struct fields
 | ||||
| 		IgnoreUntaggedFields: true,   // Ignore fields in structures that aren't tagged with yaml
 | ||||
| 	}) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("error creating decoder for config: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if err := decoder.Decode(input); err != nil { | ||||
| 		return fmt.Errorf("error decoding config: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // loadAndSubstituteEnvs reads the yaml config into a generic byte buffer and
 | ||||
| // substitute env references
 | ||||
| func loadAndSubstituteEnvs(configFileName string) ([]byte, error) { | ||||
| 	if configFileName == "" { | ||||
| 		return nil, errors.New("no configuration file provided") | ||||
| 	} | ||||
| 
 | ||||
| 	unparsedBuffer, err := os.ReadFile(configFileName) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to load config file: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	modifiedBuffer, err := normalizeSubstitution(unparsedBuffer) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error normalizing substitution string : %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	buffer, err := envsubst.Bytes(modifiedBuffer) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error in substituting env variables : %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return buffer, nil | ||||
| } | ||||
| 
 | ||||
| // registerFlags uses `cfg` and `flag` tags to associate flags in the flagSet
 | ||||
| // to the fields in the options interface provided.
 | ||||
| // Each exported field in the options must have a `cfg` tag otherwise an error will occur.
 | ||||
|  | @ -140,47 +210,6 @@ func isUnexported(name string) bool { | |||
| 	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 { | ||||
| 	buffer, err := loadAndParseYaml(configFileName) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// UnmarshalStrict will return an error if the config includes options that are
 | ||||
| 	// not mapped to fields of the into struct
 | ||||
| 	if err := yaml.UnmarshalStrict(buffer, into, yaml.DisallowUnknownFields); err != nil { | ||||
| 		return fmt.Errorf("error unmarshalling config: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // loadAndParseYaml reads the config from the filesystem and
 | ||||
| // execute the environment variable substitution
 | ||||
| func loadAndParseYaml(configFileName string) ([]byte, error) { | ||||
| 	if configFileName == "" { | ||||
| 		return nil, errors.New("no configuration file provided") | ||||
| 	} | ||||
| 
 | ||||
| 	unparsedBuffer, err := os.ReadFile(configFileName) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("unable to load config file: %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	modifiedBuffer, err := normalizeSubstitution(unparsedBuffer) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error normalizing substitution string : %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	buffer, err := envsubst.Bytes(modifiedBuffer) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error in substituting env variables : %w", err) | ||||
| 	} | ||||
| 
 | ||||
| 	return buffer, nil | ||||
| } | ||||
| 
 | ||||
| // normalizeSubstitution normalizes dollar signs ($) with numerals like
 | ||||
| // $1 or $2 properly by correctly escaping them
 | ||||
| func normalizeSubstitution(unparsedBuffer []byte) ([]byte, error) { | ||||
|  |  | |||
|  | @ -355,15 +355,15 @@ var _ = Describe("Load", func() { | |||
| var _ = Describe("LoadYAML", func() { | ||||
| 	Context("with a testOptions structure", func() { | ||||
| 		type TestOptionSubStruct struct { | ||||
| 			StringSliceOption []string `yaml:"stringSliceOption,omitempty"` | ||||
| 			StringSliceOption []string `json:"stringSliceOption,omitempty"` | ||||
| 		} | ||||
| 
 | ||||
| 		type TestOptions struct { | ||||
| 			StringOption string              `yaml:"stringOption,omitempty"` | ||||
| 			Sub          TestOptionSubStruct `yaml:"sub,omitempty"` | ||||
| 			StringOption string              `json:"stringOption,omitempty"` | ||||
| 			Sub          TestOptionSubStruct `json:"sub,omitempty"` | ||||
| 
 | ||||
| 			// Check that embedded fields can be unmarshalled
 | ||||
| 			TestOptionSubStruct `yaml:",inline,squash"` | ||||
| 			TestOptionSubStruct `json:",inline,squash"` | ||||
| 		} | ||||
| 
 | ||||
| 		var testOptionsConfigBytesFull = []byte(` | ||||
|  | @ -416,7 +416,7 @@ sub: | |||
| 
 | ||||
| 				err := LoadYAML(configFileName, input) | ||||
| 				if in.expectedErr != nil { | ||||
| 					Expect(err).To(MatchError(in.expectedErr.Error())) | ||||
| 					Expect(err).To(MatchError(ContainSubstring(in.expectedErr.Error()))) | ||||
| 				} else { | ||||
| 					Expect(err).ToNot(HaveOccurred()) | ||||
| 				} | ||||
|  | @ -459,13 +459,13 @@ sub: | |||
| 						StringSliceOption: []string{"a", "b", "c"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 				expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""), | ||||
| 				expectedErr: errors.New("has invalid keys: 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"), | ||||
| 				expectedErr:    errors.New("'stringOption' expected type 'string', got unconvertible type"), | ||||
| 			}), | ||||
| 			Entry("with an incorrect type for an array field", loadYAMLTableInput{ | ||||
| 				configFile:     []byte(`stringSliceOption: "a"`), | ||||
|  | @ -526,11 +526,13 @@ upstreamConfig: | |||
| injectRequestHeaders: | ||||
| - name: X-Forwarded-User | ||||
|   values: | ||||
|   - claim: user | ||||
|   - claimSource: | ||||
|       claim: user | ||||
| injectResponseHeaders: | ||||
| - name: X-Secret | ||||
|   values: | ||||
|   - value: c2VjcmV0 | ||||
|   - secretSource: | ||||
|       value: secret | ||||
| `) | ||||
| 
 | ||||
| 		By("Creating a config file") | ||||
|  | @ -548,7 +550,7 @@ injectResponseHeaders: | |||
| 		into := &AlphaOptions{} | ||||
| 		Expect(LoadYAML(configFileName, into)).To(Succeed()) | ||||
| 
 | ||||
| 		flushInterval := Duration(500 * time.Millisecond) | ||||
| 		flushInterval := 500 * time.Millisecond | ||||
| 
 | ||||
| 		Expect(into).To(Equal(&AlphaOptions{ | ||||
| 			UpstreamConfig: UpstreamConfig{ | ||||
|  | @ -579,7 +581,7 @@ injectResponseHeaders: | |||
| 					Values: []HeaderValue{ | ||||
| 						{ | ||||
| 							SecretSource: &SecretSource{ | ||||
| 								Value: []byte("secret"), | ||||
| 								Value: "secret", | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
|  |  | |||
|  | @ -238,16 +238,16 @@ type OIDCOptions struct { | |||
| 	IssuerURL string `json:"issuerURL,omitempty"` | ||||
| 	// InsecureAllowUnverifiedEmail prevents failures if an email address in an id_token is not verified
 | ||||
| 	// default set to 'false'
 | ||||
| 	InsecureAllowUnverifiedEmail bool `json:"insecureAllowUnverifiedEmail,omitempty"` | ||||
| 	InsecureAllowUnverifiedEmail bool `json:"insecureAllowUnverifiedEmail"` | ||||
| 	// InsecureSkipIssuerVerification skips verification of ID token issuers. When false, ID Token Issuers must match the OIDC discovery URL
 | ||||
| 	// default set to 'false'
 | ||||
| 	InsecureSkipIssuerVerification bool `json:"insecureSkipIssuerVerification,omitempty"` | ||||
| 	InsecureSkipIssuerVerification bool `json:"insecureSkipIssuerVerification"` | ||||
| 	// InsecureSkipNonce skips verifying the ID Token's nonce claim that must match
 | ||||
| 	// the random nonce sent in the initial OAuth flow. Otherwise, the nonce is checked
 | ||||
| 	// after the initial OAuth redeem & subsequent token refreshes.
 | ||||
| 	// default set to 'true'
 | ||||
| 	// Warning: In a future release, this will change to 'false' by default for enhanced security.
 | ||||
| 	InsecureSkipNonce bool `json:"insecureSkipNonce,omitempty"` | ||||
| 	InsecureSkipNonce bool `json:"insecureSkipNonce"` | ||||
| 	// SkipDiscovery allows to skip OIDC discovery and use manually supplied Endpoints
 | ||||
| 	// default set to 'false'
 | ||||
| 	SkipDiscovery bool `json:"skipDiscovery,omitempty"` | ||||
|  |  | |||
|  | @ -0,0 +1,14 @@ | |||
| package options | ||||
| 
 | ||||
| // SecretSource references an individual secret value.
 | ||||
| // Only one source within the struct should be defined at any time.
 | ||||
| type SecretSource struct { | ||||
| 	// Value expects a base64 encoded string value.
 | ||||
| 	Value string `json:"value,omitempty"` | ||||
| 
 | ||||
| 	// FromEnv expects the name of an environment variable.
 | ||||
| 	FromEnv string `json:"fromEnv,omitempty"` | ||||
| 
 | ||||
| 	// FromFile expects a path to a file containing the secret value.
 | ||||
| 	FromFile string `json:"fromFile,omitempty"` | ||||
| } | ||||
|  | @ -79,7 +79,7 @@ type Upstream struct { | |||
| 	// FlushInterval is the period between flushing the response buffer when
 | ||||
| 	// streaming response from the upstream.
 | ||||
| 	// Defaults to 1 second.
 | ||||
| 	FlushInterval *Duration `json:"flushInterval,omitempty"` | ||||
| 	FlushInterval *time.Duration `json:"flushInterval,omitempty"` | ||||
| 
 | ||||
| 	// PassHostHeader determines whether the request host header should be proxied
 | ||||
| 	// to the upstream server.
 | ||||
|  | @ -92,7 +92,7 @@ type Upstream struct { | |||
| 
 | ||||
| 	// Timeout is the maximum duration the server will wait for a response from the upstream server.
 | ||||
| 	// Defaults to 30 seconds.
 | ||||
| 	Timeout *Duration `json:"timeout,omitempty"` | ||||
| 	Timeout *time.Duration `json:"timeout,omitempty"` | ||||
| 
 | ||||
| 	// DisableKeepAlives disables HTTP keep-alive connections to the upstream server.
 | ||||
| 	// Defaults to false.
 | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ import ( | |||
| func GetSecretValue(source *options.SecretSource) ([]byte, error) { | ||||
| 	switch { | ||||
| 	case len(source.Value) > 0 && source.FromEnv == "" && source.FromFile == "": | ||||
| 		return source.Value, nil | ||||
| 		return []byte(source.Value), nil | ||||
| 	case len(source.Value) == 0 && source.FromEnv != "" && source.FromFile == "": | ||||
| 		return []byte(os.Getenv(source.FromEnv)), nil | ||||
| 	case len(source.Value) == 0 && source.FromEnv == "" && source.FromFile != "": | ||||
|  |  | |||
|  | @ -31,7 +31,7 @@ var _ = Describe("GetSecretValue", func() { | |||
| 
 | ||||
| 	It("returns the correct value from the string value", func() { | ||||
| 		value, err := GetSecretValue(&options.SecretSource{ | ||||
| 			Value: []byte("secret-value-1"), | ||||
| 			Value: "secret-value-1", | ||||
| 		}) | ||||
| 		Expect(err).ToNot(HaveOccurred()) | ||||
| 		Expect(string(value)).To(Equal("secret-value-1")) | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ var _ = Describe("Injector Suite", func() { | |||
| 						Values: []options.HeaderValue{ | ||||
| 							{ | ||||
| 								SecretSource: &options.SecretSource{ | ||||
| 									Value: []byte("super-secret"), | ||||
| 									Value: "super-secret", | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
|  | @ -199,7 +199,7 @@ var _ = Describe("Injector Suite", func() { | |||
| 								ClaimSource: &options.ClaimSource{ | ||||
| 									Claim: "user", | ||||
| 									BasicAuthPassword: &options.SecretSource{ | ||||
| 										Value: []byte("basic-password"), | ||||
| 										Value: "basic-password", | ||||
| 									}, | ||||
| 								}, | ||||
| 							}, | ||||
|  | @ -227,7 +227,7 @@ var _ = Describe("Injector Suite", func() { | |||
| 								ClaimSource: &options.ClaimSource{ | ||||
| 									Claim: "user", | ||||
| 									BasicAuthPassword: &options.SecretSource{ | ||||
| 										Value: []byte(base64.StdEncoding.EncodeToString([]byte("basic-password"))), | ||||
| 										Value: base64.StdEncoding.EncodeToString([]byte("basic-password")), | ||||
| 									}, | ||||
| 								}, | ||||
| 							}, | ||||
|  | @ -322,7 +322,7 @@ var _ = Describe("Injector Suite", func() { | |||
| 								ClaimSource: &options.ClaimSource{ | ||||
| 									Claim: "user", | ||||
| 									BasicAuthPassword: &options.SecretSource{ | ||||
| 										Value:   []byte(base64.StdEncoding.EncodeToString([]byte("basic-password"))), | ||||
| 										Value:   base64.StdEncoding.EncodeToString([]byte("basic-password")), | ||||
| 										FromEnv: "SECRET_ENV", | ||||
| 									}, | ||||
| 								}, | ||||
|  | @ -348,7 +348,7 @@ var _ = Describe("Injector Suite", func() { | |||
| 								ClaimSource: &options.ClaimSource{ | ||||
| 									Claim: "user", | ||||
| 									BasicAuthPassword: &options.SecretSource{ | ||||
| 										Value: []byte("basic-password"), | ||||
| 										Value: "basic-password", | ||||
| 									}, | ||||
| 								}, | ||||
| 							}, | ||||
|  | @ -379,17 +379,17 @@ var _ = Describe("Injector Suite", func() { | |||
| 						Values: []options.HeaderValue{ | ||||
| 							{ | ||||
| 								SecretSource: &options.SecretSource{ | ||||
| 									Value: []byte("major=1"), | ||||
| 									Value: "major=1", | ||||
| 								}, | ||||
| 							}, | ||||
| 							{ | ||||
| 								SecretSource: &options.SecretSource{ | ||||
| 									Value: []byte("minor=2"), | ||||
| 									Value: "minor=2", | ||||
| 								}, | ||||
| 							}, | ||||
| 							{ | ||||
| 								SecretSource: &options.SecretSource{ | ||||
| 									Value: []byte("patch=3"), | ||||
| 									Value: "patch=3", | ||||
| 								}, | ||||
| 							}, | ||||
| 						}, | ||||
|  |  | |||
|  | @ -48,10 +48,10 @@ var _ = BeforeSuite(func() { | |||
| 
 | ||||
| 		certOut := new(bytes.Buffer) | ||||
| 		Expect(pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})).To(Succeed()) | ||||
| 		ipv4CertDataSource.Value = certOut.Bytes() | ||||
| 		ipv4CertDataSource.Value = certOut.String() | ||||
| 		keyOut := new(bytes.Buffer) | ||||
| 		Expect(pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})).To(Succeed()) | ||||
| 		ipv4KeyDataSource.Value = keyOut.Bytes() | ||||
| 		ipv4KeyDataSource.Value = keyOut.String() | ||||
| 	}) | ||||
| 
 | ||||
| 	By("Generating a ipv6 self-signed cert for TLS tests", func() { | ||||
|  | @ -61,16 +61,16 @@ var _ = BeforeSuite(func() { | |||
| 
 | ||||
| 		certOut := new(bytes.Buffer) | ||||
| 		Expect(pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certBytes})).To(Succeed()) | ||||
| 		ipv6CertDataSource.Value = certOut.Bytes() | ||||
| 		ipv6CertDataSource.Value = certOut.String() | ||||
| 		keyOut := new(bytes.Buffer) | ||||
| 		Expect(pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})).To(Succeed()) | ||||
| 		ipv6KeyDataSource.Value = keyOut.Bytes() | ||||
| 		ipv6KeyDataSource.Value = keyOut.String() | ||||
| 	}) | ||||
| 
 | ||||
| 	By("Setting up a http client", func() { | ||||
| 		ipv4cert, err := tls.X509KeyPair(ipv4CertDataSource.Value, ipv4KeyDataSource.Value) | ||||
| 		ipv4cert, err := tls.X509KeyPair([]byte(ipv4CertDataSource.Value), []byte(ipv4KeyDataSource.Value)) | ||||
| 		Expect(err).ToNot(HaveOccurred()) | ||||
| 		ipv6cert, err := tls.X509KeyPair(ipv6CertDataSource.Value, ipv6KeyDataSource.Value) | ||||
| 		ipv6cert, err := tls.X509KeyPair([]byte(ipv6CertDataSource.Value), []byte(ipv6KeyDataSource.Value)) | ||||
| 		Expect(err).ToNot(HaveOccurred()) | ||||
| 
 | ||||
| 		ipv4certificate, err := x509.ParseCertificate(ipv4cert.Certificate[0]) | ||||
|  |  | |||
|  | @ -234,7 +234,7 @@ var _ = Describe("Server", func() { | |||
| 					SecureBindAddress: "127.0.0.1:0", | ||||
| 					TLS: &options.TLS{ | ||||
| 						Key: &options.SecretSource{ | ||||
| 							Value: []byte("invalid"), | ||||
| 							Value: "invalid", | ||||
| 						}, | ||||
| 						Cert: &ipv4CertDataSource, | ||||
| 					}, | ||||
|  | @ -250,7 +250,7 @@ var _ = Describe("Server", func() { | |||
| 					TLS: &options.TLS{ | ||||
| 						Key: &ipv4KeyDataSource, | ||||
| 						Cert: &options.SecretSource{ | ||||
| 							Value: []byte("invalid"), | ||||
| 							Value: "invalid", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
|  | @ -506,7 +506,7 @@ var _ = Describe("Server", func() { | |||
| 					SecureBindAddress: "[::1]:0", | ||||
| 					TLS: &options.TLS{ | ||||
| 						Key: &options.SecretSource{ | ||||
| 							Value: []byte("invalid"), | ||||
| 							Value: "invalid", | ||||
| 						}, | ||||
| 						Cert: &ipv6CertDataSource, | ||||
| 					}, | ||||
|  | @ -523,7 +523,7 @@ var _ = Describe("Server", func() { | |||
| 					TLS: &options.TLS{ | ||||
| 						Key: &ipv6KeyDataSource, | ||||
| 						Cert: &options.SecretSource{ | ||||
| 							Value: []byte("invalid"), | ||||
| 							Value: "invalid", | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
|  |  | |||
|  | @ -188,7 +188,7 @@ var _ = Describe("Headers Suite", func() { | |||
| 							ClaimSource: &options.ClaimSource{ | ||||
| 								Claim: "user", | ||||
| 								BasicAuthPassword: &options.SecretSource{ | ||||
| 									Value:   []byte(base64.StdEncoding.EncodeToString([]byte("basic-password"))), | ||||
| 									Value:   base64.StdEncoding.EncodeToString([]byte("basic-password")), | ||||
| 									FromEnv: "SECRET_ENV", | ||||
| 								}, | ||||
| 							}, | ||||
|  | @ -260,7 +260,7 @@ var _ = Describe("Headers Suite", func() { | |||
| 					Values: []options.HeaderValue{ | ||||
| 						{ | ||||
| 							SecretSource: &options.SecretSource{ | ||||
| 								Value: []byte("_oauth2_proxy=ey123123123"), | ||||
| 								Value: "_oauth2_proxy=ey123123123", | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
|  | @ -270,7 +270,7 @@ var _ = Describe("Headers Suite", func() { | |||
| 					Values: []options.HeaderValue{ | ||||
| 						{ | ||||
| 							SecretSource: &options.SecretSource{ | ||||
| 								Value: []byte("oauth_user"), | ||||
| 								Value: "oauth_user", | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
|  | @ -416,7 +416,7 @@ var _ = Describe("Headers Suite", func() { | |||
| 							ClaimSource: &options.ClaimSource{ | ||||
| 								Claim: "user", | ||||
| 								BasicAuthPassword: &options.SecretSource{ | ||||
| 									Value:   []byte(base64.StdEncoding.EncodeToString([]byte("basic-password"))), | ||||
| 									Value:   base64.StdEncoding.EncodeToString([]byte("basic-password")), | ||||
| 									FromEnv: "SECRET_ENV", | ||||
| 								}, | ||||
| 							}, | ||||
|  |  | |||
|  | @ -137,12 +137,12 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr | |||
| 
 | ||||
| 	// Change default duration for waiting for an upstream response
 | ||||
| 	if upstream.Timeout != nil { | ||||
| 		transport.ResponseHeaderTimeout = upstream.Timeout.Duration() | ||||
| 		transport.ResponseHeaderTimeout = *upstream.Timeout | ||||
| 	} | ||||
| 
 | ||||
| 	// Configure options on the SingleHostReverseProxy
 | ||||
| 	if upstream.FlushInterval != nil { | ||||
| 		proxy.FlushInterval = upstream.FlushInterval.Duration() | ||||
| 		proxy.FlushInterval = *upstream.FlushInterval | ||||
| 	} else { | ||||
| 		proxy.FlushInterval = options.DefaultUpstreamFlushInterval | ||||
| 	} | ||||
|  |  | |||
|  | @ -21,8 +21,8 @@ import ( | |||
| ) | ||||
| 
 | ||||
| var _ = Describe("HTTP Upstream Suite", func() { | ||||
| 	defaultFlushInterval := options.Duration(options.DefaultUpstreamFlushInterval) | ||||
| 	defaultTimeout := options.Duration(options.DefaultUpstreamTimeout) | ||||
| 	defaultFlushInterval := options.DefaultUpstreamFlushInterval | ||||
| 	defaultTimeout := options.DefaultUpstreamTimeout | ||||
| 	truth := true | ||||
| 	falsum := false | ||||
| 
 | ||||
|  | @ -57,9 +57,9 @@ var _ = Describe("HTTP Upstream Suite", func() { | |||
| 			req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{}) | ||||
| 			rw := httptest.NewRecorder() | ||||
| 
 | ||||
| 			flush := options.Duration(1 * time.Second) | ||||
| 			flush := 1 * time.Second | ||||
| 
 | ||||
| 			timeout := options.Duration(options.DefaultUpstreamTimeout) | ||||
| 			timeout := options.DefaultUpstreamTimeout | ||||
| 
 | ||||
| 			upstream := options.Upstream{ | ||||
| 				ID:                    in.id, | ||||
|  | @ -373,11 +373,11 @@ var _ = Describe("HTTP Upstream Suite", func() { | |||
| 
 | ||||
| 	type newUpstreamTableInput struct { | ||||
| 		proxyWebSockets   bool | ||||
| 		flushInterval     options.Duration | ||||
| 		flushInterval     time.Duration | ||||
| 		skipVerify        bool | ||||
| 		sigData           *options.SignatureData | ||||
| 		errorHandler      func(http.ResponseWriter, *http.Request, error) | ||||
| 		timeout           options.Duration | ||||
| 		timeout           time.Duration | ||||
| 		disableKeepAlives bool | ||||
| 	} | ||||
| 
 | ||||
|  | @ -406,10 +406,10 @@ var _ = Describe("HTTP Upstream Suite", func() { | |||
| 
 | ||||
| 			proxy, ok := upstreamProxy.handler.(*httputil.ReverseProxy) | ||||
| 			Expect(ok).To(BeTrue()) | ||||
| 			Expect(proxy.FlushInterval).To(Equal(in.flushInterval.Duration())) | ||||
| 			Expect(proxy.FlushInterval).To(Equal(in.flushInterval)) | ||||
| 			transport, ok := proxy.Transport.(*http.Transport) | ||||
| 			Expect(ok).To(BeTrue()) | ||||
| 			Expect(transport.ResponseHeaderTimeout).To(Equal(in.timeout.Duration())) | ||||
| 			Expect(transport.ResponseHeaderTimeout).To(Equal(in.timeout)) | ||||
| 			Expect(proxy.ErrorHandler != nil).To(Equal(in.errorHandler != nil)) | ||||
| 			if in.skipVerify { | ||||
| 				Expect(transport.TLSClientConfig.InsecureSkipVerify).To(Equal(true)) | ||||
|  | @ -428,7 +428,7 @@ var _ = Describe("HTTP Upstream Suite", func() { | |||
| 		}), | ||||
| 		Entry("with a non standard flush interval", &newUpstreamTableInput{ | ||||
| 			proxyWebSockets: false, | ||||
| 			flushInterval:   options.Duration(5 * time.Second), | ||||
| 			flushInterval:   5 * time.Second, | ||||
| 			skipVerify:      false, | ||||
| 			sigData:         nil, | ||||
| 			errorHandler:    nil, | ||||
|  | @ -466,7 +466,7 @@ var _ = Describe("HTTP Upstream Suite", func() { | |||
| 			skipVerify:      false, | ||||
| 			sigData:         nil, | ||||
| 			errorHandler:    nil, | ||||
| 			timeout:         options.Duration(5 * time.Second), | ||||
| 			timeout:         5 * time.Second, | ||||
| 		}), | ||||
| 		Entry("with a DisableKeepAlives", &newUpstreamTableInput{ | ||||
| 			proxyWebSockets:   false, | ||||
|  | @ -483,8 +483,8 @@ var _ = Describe("HTTP Upstream Suite", func() { | |||
| 		var proxyServer *httptest.Server | ||||
| 
 | ||||
| 		BeforeEach(func() { | ||||
| 			flush := options.Duration(1 * time.Second) | ||||
| 			timeout := options.Duration(options.DefaultUpstreamTimeout) | ||||
| 			flush := 1 * time.Second | ||||
| 			timeout := options.DefaultUpstreamTimeout | ||||
| 			upstream := options.Upstream{ | ||||
| 				ID:                    "websocketProxy", | ||||
| 				PassHostHeader:        &truth, | ||||
|  |  | |||
|  | @ -9,12 +9,12 @@ import ( | |||
| ) | ||||
| 
 | ||||
| var _ = Describe("Common", func() { | ||||
| 	var validSecretSourceValue []byte | ||||
| 	var validSecretSourceValue string | ||||
| 	const validSecretSourceEnv = "OAUTH2_PROXY_TEST_SECRET_SOURCE_ENV" | ||||
| 	var validSecretSourceFile string | ||||
| 
 | ||||
| 	BeforeEach(func() { | ||||
| 		validSecretSourceValue = []byte("This is a secret source value") | ||||
| 		validSecretSourceValue = "This is a secret source value" | ||||
| 		Expect(os.Setenv(validSecretSourceEnv, "This is a secret source env")).To(Succeed()) | ||||
| 		tmp, err := os.CreateTemp("", "oauth2-proxy-secret-source-test") | ||||
| 		Expect(err).ToNot(HaveOccurred()) | ||||
|  |  | |||
|  | @ -51,11 +51,9 @@ func validateHeaderValue(_ string, value options.HeaderValue) []string { | |||
| 
 | ||||
| func validateHeaderValueClaimSource(claim options.ClaimSource) []string { | ||||
| 	msgs := []string{} | ||||
| 
 | ||||
| 	if claim.Claim == "" { | ||||
| 		msgs = append(msgs, "claim should not be empty") | ||||
| 	} | ||||
| 
 | ||||
| 	if claim.BasicAuthPassword != nil { | ||||
| 		msgs = append(msgs, prefixValues("invalid basicAuthPassword: ", validateSecretSource(*claim.BasicAuthPassword))...) | ||||
| 	} | ||||
|  |  | |||
|  | @ -30,7 +30,7 @@ var _ = Describe("Headers", func() { | |||
| 		Values: []options.HeaderValue{ | ||||
| 			{ | ||||
| 				SecretSource: &options.SecretSource{ | ||||
| 					Value: []byte(base64.StdEncoding.EncodeToString([]byte("secret"))), | ||||
| 					Value: base64.StdEncoding.EncodeToString([]byte("secret")), | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
|  | @ -43,7 +43,7 @@ var _ = Describe("Headers", func() { | |||
| 				ClaimSource: &options.ClaimSource{ | ||||
| 					Claim: "email", | ||||
| 					BasicAuthPassword: &options.SecretSource{ | ||||
| 						Value: []byte(base64.StdEncoding.EncodeToString([]byte("secret"))), | ||||
| 						Value: base64.StdEncoding.EncodeToString([]byte("secret")), | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ func validateStaticUpstream(upstream options.Upstream) []string { | |||
| 	if upstream.InsecureSkipTLSVerify { | ||||
| 		msgs = append(msgs, fmt.Sprintf("upstream %q has insecureSkipTLSVerify, but is a static upstream, this will have no effect.", upstream.ID)) | ||||
| 	} | ||||
| 	if upstream.FlushInterval != nil && upstream.FlushInterval.Duration() != options.DefaultUpstreamFlushInterval { | ||||
| 	if upstream.FlushInterval != nil && *upstream.FlushInterval != options.DefaultUpstreamFlushInterval { | ||||
| 		msgs = append(msgs, fmt.Sprintf("upstream %q has flushInterval, but is a static upstream, this will have no effect.", upstream.ID)) | ||||
| 	} | ||||
| 	if upstream.PassHostHeader != nil { | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ var _ = Describe("Upstreams", func() { | |||
| 		errStrings []string | ||||
| 	} | ||||
| 
 | ||||
| 	flushInterval := options.Duration(5 * time.Second) | ||||
| 	flushInterval := 5 * time.Second | ||||
| 	staticCode200 := 200 | ||||
| 	truth := true | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue