You've already forked oauth2-proxy
mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-12-07 23:13:07 +02:00
fix merge problems and test cases
Signed-off-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
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"`
|
||||||
|
|
||||||
// InsecureSkipHeaderNormalization disables normalizing the header name
|
// InsecureSkipHeaderNormalization disables normalizing the header name
|
||||||
// According to RFC 7230 Section 3.2 there aren't any rules about
|
// According to RFC 7230 Section 3.2 there aren't any rules about
|
||||||
|
|||||||
@@ -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"`
|
||||||
// UseOrganizationId indicates whether to use the organization ID as the UserName claim
|
// UseOrganizationId indicates whether to use the organization ID as the UserName claim
|
||||||
@@ -254,7 +254,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.
|
||||||
|
|||||||
Reference in New Issue
Block a user