fix merge problems and test cases
Signed-off-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
		
							parent
							
								
									1d73f140bf
								
							
						
					
					
						commit
						fa2587ac09
					
				
							
								
								
									
										2
									
								
								main.go
								
								
								
								
							
							
						
						
									
										2
									
								
								main.go
								
								
								
								
							|  | @ -70,7 +70,7 @@ func main() { | ||||||
| func loadConfiguration(config, yamlConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) { | func loadConfiguration(config, yamlConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) { | ||||||
| 	opts, err := loadLegacyOptions(config, extraFlags, args) | 	opts, err := loadLegacyOptions(config, extraFlags, args) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("couldn't load legacy options: %w", err) | 		return nil, fmt.Errorf("failed to load legacy options: %w", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if yamlConfig != "" { | 	if yamlConfig != "" { | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								main_test.go
								
								
								
								
							
							
						
						
									
										16
									
								
								main_test.go
								
								
								
								
							|  | @ -22,7 +22,7 @@ var _ = Describe("Configuration Loading Suite", func() { | ||||||
| http_address="127.0.0.1:4180" | http_address="127.0.0.1:4180" | ||||||
| upstreams="http://httpbin" | upstreams="http://httpbin" | ||||||
| set_basic_auth="true" | set_basic_auth="true" | ||||||
| basic_auth_password="super-secret-password" | basic_auth_password="c3VwZXItc2VjcmV0LXBhc3N3b3Jk" | ||||||
| client_id="oauth2-proxy" | client_id="oauth2-proxy" | ||||||
| client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK" | client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK" | ||||||
| ` | ` | ||||||
|  | @ -45,7 +45,7 @@ injectRequestHeaders: | ||||||
|       claim: user |       claim: user | ||||||
|       prefix: "Basic " |       prefix: "Basic " | ||||||
|       basicAuthPassword: |       basicAuthPassword: | ||||||
|         value: super-secret-password |         value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk | ||||||
| - name: X-Forwarded-Groups | - name: X-Forwarded-Groups | ||||||
|   values: |   values: | ||||||
|   - claimSource: |   - claimSource: | ||||||
|  | @ -69,12 +69,12 @@ injectResponseHeaders: | ||||||
|       claim: user |       claim: user | ||||||
|       prefix: "Basic " |       prefix: "Basic " | ||||||
|       basicAuthPassword: |       basicAuthPassword: | ||||||
|         value: super-secret-password |         value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk | ||||||
| server: | server: | ||||||
|   bindAddress: "127.0.0.1:4180" |   bindAddress: "127.0.0.1:4180" | ||||||
| providers: | providers: | ||||||
| - provider: google | - id: google=oauth2-proxy | ||||||
|   ID: google=oauth2-proxy |   provider: google | ||||||
|   clientSecret: b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK |   clientSecret: b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK | ||||||
|   clientID: oauth2-proxy |   clientID: oauth2-proxy | ||||||
|   azureConfig: |   azureConfig: | ||||||
|  | @ -139,7 +139,7 @@ redirect_url="http://localhost:4180/oauth2/callback" | ||||||
| 						Claim:  "user", | 						Claim:  "user", | ||||||
| 						Prefix: "Basic ", | 						Prefix: "Basic ", | ||||||
| 						BasicAuthPassword: &options.SecretSource{ | 						BasicAuthPassword: &options.SecretSource{ | ||||||
| 							Value: []byte("super-secret-password"), | 							Value: []byte("c3VwZXItc2VjcmV0LXBhc3N3b3Jk"), | ||||||
| 						}, | 						}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
|  | @ -248,7 +248,7 @@ redirect_url="http://localhost:4180/oauth2/callback" | ||||||
| 		Entry("with bad legacy configuration", loadConfigurationTableInput{ | 		Entry("with bad legacy configuration", loadConfigurationTableInput{ | ||||||
| 			configContent:   testCoreConfig + "unknown_field=\"something\"", | 			configContent:   testCoreConfig + "unknown_field=\"something\"", | ||||||
| 			expectedOptions: func() *options.Options { return nil }, | 			expectedOptions: func() *options.Options { return nil }, | ||||||
| 			expectedErr:     errors.New("failed to load config: error unmarshalling config: decoding failed due to the following error(s):\n\n'' has invalid keys: unknown_field"), | 			expectedErr:     errors.New("failed to load legacy options: failed to load config: error unmarshalling config: decoding failed due to the following error(s):\n\n'' has invalid keys: unknown_field"), | ||||||
| 		}), | 		}), | ||||||
| 		Entry("with bad alpha configuration", loadConfigurationTableInput{ | 		Entry("with bad alpha configuration", loadConfigurationTableInput{ | ||||||
| 			configContent:      testCoreConfig, | 			configContent:      testCoreConfig, | ||||||
|  | @ -260,7 +260,7 @@ redirect_url="http://localhost:4180/oauth2/callback" | ||||||
| 			configContent:      testCoreConfig + "unknown_field=\"something\"", | 			configContent:      testCoreConfig + "unknown_field=\"something\"", | ||||||
| 			alphaConfigContent: testAlphaConfig, | 			alphaConfigContent: testAlphaConfig, | ||||||
| 			expectedOptions:    func() *options.Options { return nil }, | 			expectedOptions:    func() *options.Options { return nil }, | ||||||
| 			expectedErr:        errors.New("failed to load core options: failed to load config: error unmarshalling config: decoding failed due to the following error(s):\n\n'' has invalid keys: unknown_field"), | 			expectedErr:        errors.New("failed to load legacy options: failed to load config: error unmarshalling config: decoding failed due to the following error(s):\n\n'' has invalid keys: unknown_field"), | ||||||
| 		}), | 		}), | ||||||
| 	) | 	) | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ type Header struct { | ||||||
| 	// should be preserved for the request to the upstream server.
 | 	// should be preserved for the request to the upstream server.
 | ||||||
| 	// This option only applies to injected request headers.
 | 	// This option only applies to injected request headers.
 | ||||||
| 	// Defaults to false (headers that match this header will be stripped).
 | 	// Defaults to false (headers that match this header will be stripped).
 | ||||||
| 	PreserveRequestValue bool `yaml:"preserveRequestValue,omitempty"` | 	PreserveRequestValue bool `yaml:"preserveRequestValue"` | ||||||
| 
 | 
 | ||||||
| 	// Values contains the desired values for this header
 | 	// Values contains the desired values for this header
 | ||||||
| 	Values []HeaderValue `yaml:"values,omitempty"` | 	Values []HeaderValue `yaml:"values,omitempty"` | ||||||
|  |  | ||||||
|  | @ -41,3 +41,17 @@ func toDurationHookFunc() mapstructure.DecodeHookFunc { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // StringToBytesHookFunc returns a DecodeHookFunc that converts string to []byte.
 | ||||||
|  | func stringToBytesHookFunc() mapstructure.DecodeHookFunc { | ||||||
|  | 	return func( | ||||||
|  | 		f reflect.Type, | ||||||
|  | 		t reflect.Type, | ||||||
|  | 		data interface{}, | ||||||
|  | 	) (interface{}, error) { | ||||||
|  | 		if f.Kind() == reflect.String && t == reflect.TypeOf([]byte{}) { | ||||||
|  | 			return []byte(data.(string)), nil | ||||||
|  | 		} | ||||||
|  | 		return data, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -5,7 +5,7 @@ import ( | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestDecode(t *testing.T) { | func TestToDurationHook(t *testing.T) { | ||||||
| 	type result struct { | 	type result struct { | ||||||
| 		Duration time.Duration `yaml:"duration"` | 		Duration time.Duration `yaml:"duration"` | ||||||
| 	} | 	} | ||||||
|  | @ -80,3 +80,17 @@ func TestDecode(t *testing.T) { | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestStringToBytesHook(t *testing.T) { | ||||||
|  | 	var result struct { | ||||||
|  | 		Value []byte `yaml:"value"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := Decode(map[string]interface{}{"value": "hello-world"}, &result); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if string(result.Value) != "hello-world" { | ||||||
|  | 		t.Errorf("expected %q, got %q", "hello-world", string(result.Value)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -89,7 +89,10 @@ func LoadYAML(configFileName string, opts interface{}) error { | ||||||
| // - An error if decoding fails or if there are unmapped keys.
 | // - An error if decoding fails or if there are unmapped keys.
 | ||||||
| func Decode(input interface{}, result interface{}) error { | func Decode(input interface{}, result interface{}) error { | ||||||
| 	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ | 	decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ | ||||||
| 		DecodeHook:           mapstructure.ComposeDecodeHookFunc(toDurationHookFunc()), | 		DecodeHook: mapstructure.ComposeDecodeHookFunc( | ||||||
|  | 			toDurationHookFunc(), | ||||||
|  | 			stringToBytesHookFunc(), | ||||||
|  | 		), | ||||||
| 		Metadata:             nil,    // Don't track any metadata
 | 		Metadata:             nil,    // Don't track any metadata
 | ||||||
| 		Result:               result, // Decode the result into the prefilled options
 | 		Result:               result, // Decode the result into the prefilled options
 | ||||||
| 		TagName:              "yaml", // Parse all fields that use the json tag
 | 		TagName:              "yaml", // Parse all fields that use the json tag
 | ||||||
|  |  | ||||||
|  | @ -155,7 +155,7 @@ var _ = Describe("Load", func() { | ||||||
| 				} | 				} | ||||||
| 				err := Load(configFileName, flagSet, input) | 				err := Load(configFileName, flagSet, input) | ||||||
| 				if o.expectedErr != nil { | 				if o.expectedErr != nil { | ||||||
| 					Expect(err).To(MatchError(o.expectedErr.Error())) | 					Expect(err).To(MatchError(ContainSubstring(o.expectedErr.Error()))) | ||||||
| 				} else { | 				} else { | ||||||
| 					Expect(err).ToNot(HaveOccurred()) | 					Expect(err).ToNot(HaveOccurred()) | ||||||
| 				} | 				} | ||||||
|  | @ -471,7 +471,7 @@ sub: | ||||||
| 				configFile:     []byte(`stringSliceOption: "a"`), | 				configFile:     []byte(`stringSliceOption: "a"`), | ||||||
| 				input:          &TestOptions{}, | 				input:          &TestOptions{}, | ||||||
| 				expectedOutput: &TestOptions{}, | 				expectedOutput: &TestOptions{}, | ||||||
| 				expectedErr:    errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go struct field TestOptions.TestOptionSubStruct.StringSliceOption of type []string"), | 				expectedErr:    errors.New("error decoding config: decoding failed due to the following error(s):\n\n'stringSliceOption' source data must be an array or slice, got string"), | ||||||
| 			}), | 			}), | ||||||
| 			Entry("with a config file containing environment variable references", loadYAMLTableInput{ | 			Entry("with a config file containing environment variable references", loadYAMLTableInput{ | ||||||
| 				configFile: []byte("stringOption: ${TESTUSER}"), | 				configFile: []byte("stringOption: ${TESTUSER}"), | ||||||
|  |  | ||||||
|  | @ -67,7 +67,7 @@ type Provider struct { | ||||||
| 	CAFiles []string `yaml:"caFiles,omitempty"` | 	CAFiles []string `yaml:"caFiles,omitempty"` | ||||||
| 	// UseSystemTrustStore determines if your custom CA files and the system trust store are used
 | 	// UseSystemTrustStore determines if your custom CA files and the system trust store are used
 | ||||||
| 	// If set to true, your custom CA files and the system trust store are used otherwise only your custom CA files.
 | 	// If set to true, your custom CA files and the system trust store are used otherwise only your custom CA files.
 | ||||||
| 	UseSystemTrustStore bool `yaml:"useSystemTrustStore,omitempty"` | 	UseSystemTrustStore bool `yaml:"useSystemTrustStore"` | ||||||
| 	// LoginURL is the authentication endpoint
 | 	// LoginURL is the authentication endpoint
 | ||||||
| 	LoginURL string `yaml:"loginURL,omitempty"` | 	LoginURL string `yaml:"loginURL,omitempty"` | ||||||
| 	// LoginURLParameters defines the parameters that can be passed from the start URL to the IdP login URL
 | 	// LoginURLParameters defines the parameters that can be passed from the start URL to the IdP login URL
 | ||||||
|  | @ -80,7 +80,7 @@ type Provider struct { | ||||||
| 	ProfileURL string `yaml:"profileURL,omitempty"` | 	ProfileURL string `yaml:"profileURL,omitempty"` | ||||||
| 	// SkipClaimsFromProfileURL allows to skip request to Profile URL for resolving claims not present in id_token
 | 	// SkipClaimsFromProfileURL allows to skip request to Profile URL for resolving claims not present in id_token
 | ||||||
| 	// default set to 'false'
 | 	// default set to 'false'
 | ||||||
| 	SkipClaimsFromProfileURL bool `yaml:"skipClaimsFromProfileURL,omitempty"` | 	SkipClaimsFromProfileURL bool `yaml:"skipClaimsFromProfileURL"` | ||||||
| 	// ProtectedResource is the resource that is protected (Azure AD and ADFS only)
 | 	// ProtectedResource is the resource that is protected (Azure AD and ADFS only)
 | ||||||
| 	ProtectedResource string `yaml:"resource,omitempty"` | 	ProtectedResource string `yaml:"resource,omitempty"` | ||||||
| 	// ValidateURL is the access token validation endpoint
 | 	// ValidateURL is the access token validation endpoint
 | ||||||
|  | @ -181,13 +181,13 @@ type MicrosoftEntraIDOptions struct { | ||||||
| 
 | 
 | ||||||
| 	// FederatedTokenAuth enable oAuth2 client authentication with federated token projected
 | 	// FederatedTokenAuth enable oAuth2 client authentication with federated token projected
 | ||||||
| 	// by Entra Workload Identity plugin, instead of client secret.
 | 	// by Entra Workload Identity plugin, instead of client secret.
 | ||||||
| 	FederatedTokenAuth bool `yaml:"federatedTokenAuth,omitempty"` | 	FederatedTokenAuth bool `yaml:"federatedTokenAuth"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ADFSOptions struct { | type ADFSOptions struct { | ||||||
| 	// Skip adding the scope parameter in login request
 | 	// Skip adding the scope parameter in login request
 | ||||||
| 	// Default value is 'false'
 | 	// Default value is 'false'
 | ||||||
| 	SkipScope bool `yaml:"skipScope,omitempty"` | 	SkipScope bool `yaml:"skipScope"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type BitbucketOptions struct { | type BitbucketOptions struct { | ||||||
|  | @ -227,7 +227,7 @@ type GoogleOptions struct { | ||||||
| 	// ServiceAccountJSON is the path to the service account json credentials
 | 	// ServiceAccountJSON is the path to the service account json credentials
 | ||||||
| 	ServiceAccountJSON string `yaml:"serviceAccountJson,omitempty"` | 	ServiceAccountJSON string `yaml:"serviceAccountJson,omitempty"` | ||||||
| 	// UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON
 | 	// UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON
 | ||||||
| 	UseApplicationDefaultCredentials bool `yaml:"useApplicationDefaultCredentials,omitempty"` | 	UseApplicationDefaultCredentials bool `yaml:"useApplicationDefaultCredentials"` | ||||||
| 	// TargetPrincipal is the Google Service Account used for Application Default Credentials
 | 	// TargetPrincipal is the Google Service Account used for Application Default Credentials
 | ||||||
| 	TargetPrincipal string `yaml:"targetPrincipal,omitempty"` | 	TargetPrincipal string `yaml:"targetPrincipal,omitempty"` | ||||||
| } | } | ||||||
|  | @ -250,7 +250,7 @@ type OIDCOptions struct { | ||||||
| 	InsecureSkipNonce bool `yaml:"insecureSkipNonce"` | 	InsecureSkipNonce bool `yaml:"insecureSkipNonce"` | ||||||
| 	// SkipDiscovery allows to skip OIDC discovery and use manually supplied Endpoints
 | 	// SkipDiscovery allows to skip OIDC discovery and use manually supplied Endpoints
 | ||||||
| 	// default set to 'false'
 | 	// default set to 'false'
 | ||||||
| 	SkipDiscovery bool `yaml:"skipDiscovery,omitempty"` | 	SkipDiscovery bool `yaml:"skipDiscovery"` | ||||||
| 	// JwksURL is the OpenID Connect JWKS URL
 | 	// JwksURL is the OpenID Connect JWKS URL
 | ||||||
| 	// eg: https://www.googleapis.com/oauth2/v3/certs
 | 	// eg: https://www.googleapis.com/oauth2/v3/certs
 | ||||||
| 	JwksURL string `yaml:"jwksURL,omitempty"` | 	JwksURL string `yaml:"jwksURL,omitempty"` | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ const ( | ||||||
| type UpstreamConfig struct { | type UpstreamConfig struct { | ||||||
| 	// ProxyRawPath will pass the raw url path to upstream allowing for urls
 | 	// ProxyRawPath will pass the raw url path to upstream allowing for urls
 | ||||||
| 	// like: "/%2F/" which would otherwise be redirected to "/"
 | 	// like: "/%2F/" which would otherwise be redirected to "/"
 | ||||||
| 	ProxyRawPath bool `yaml:"proxyRawPath,omitempty"` | 	ProxyRawPath bool `yaml:"proxyRawPath"` | ||||||
| 
 | 
 | ||||||
| 	// Upstreams represents the configuration for the upstream servers.
 | 	// Upstreams represents the configuration for the upstream servers.
 | ||||||
| 	// Requests will be proxied to this upstream if the path matches the request path.
 | 	// Requests will be proxied to this upstream if the path matches the request path.
 | ||||||
|  | @ -64,13 +64,13 @@ type Upstream struct { | ||||||
| 	// This option is insecure and will allow potential Man-In-The-Middle attacks
 | 	// This option is insecure and will allow potential Man-In-The-Middle attacks
 | ||||||
| 	// between OAuth2 Proxy and the upstream server.
 | 	// between OAuth2 Proxy and the upstream server.
 | ||||||
| 	// Defaults to false.
 | 	// Defaults to false.
 | ||||||
| 	InsecureSkipTLSVerify bool `yaml:"insecureSkipTLSVerify,omitempty"` | 	InsecureSkipTLSVerify bool `yaml:"insecureSkipTLSVerify"` | ||||||
| 
 | 
 | ||||||
| 	// Static will make all requests to this upstream have a static response.
 | 	// Static will make all requests to this upstream have a static response.
 | ||||||
| 	// The response will have a body of "Authenticated" and a response code
 | 	// The response will have a body of "Authenticated" and a response code
 | ||||||
| 	// matching StaticCode.
 | 	// matching StaticCode.
 | ||||||
| 	// If StaticCode is not set, the response will return a 200 response.
 | 	// If StaticCode is not set, the response will return a 200 response.
 | ||||||
| 	Static bool `yaml:"static,omitempty"` | 	Static bool `yaml:"static"` | ||||||
| 
 | 
 | ||||||
| 	// StaticCode determines the response code for the Static response.
 | 	// StaticCode determines the response code for the Static response.
 | ||||||
| 	// This option can only be used with Static enabled.
 | 	// This option can only be used with Static enabled.
 | ||||||
|  | @ -84,11 +84,11 @@ type Upstream struct { | ||||||
| 	// PassHostHeader determines whether the request host header should be proxied
 | 	// PassHostHeader determines whether the request host header should be proxied
 | ||||||
| 	// to the upstream server.
 | 	// to the upstream server.
 | ||||||
| 	// Defaults to true.
 | 	// Defaults to true.
 | ||||||
| 	PassHostHeader *bool `yaml:"passHostHeader,omitempty"` | 	PassHostHeader *bool `yaml:"passHostHeader"` | ||||||
| 
 | 
 | ||||||
| 	// ProxyWebSockets enables proxying of websockets to upstream servers
 | 	// ProxyWebSockets enables proxying of websockets to upstream servers
 | ||||||
| 	// Defaults to true.
 | 	// Defaults to true.
 | ||||||
| 	ProxyWebSockets *bool `yaml:"proxyWebSockets,omitempty"` | 	ProxyWebSockets *bool `yaml:"proxyWebSockets"` | ||||||
| 
 | 
 | ||||||
| 	// Timeout is the maximum duration the server will wait for a response from the upstream server.
 | 	// Timeout is the maximum duration the server will wait for a response from the upstream server.
 | ||||||
| 	// Defaults to 30 seconds.
 | 	// Defaults to 30 seconds.
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue