418 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			418 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
| package header
 | |
| 
 | |
| import (
 | |
| 	"encoding/base64"
 | |
| 	"errors"
 | |
| 	"net/http"
 | |
| 
 | |
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
 | |
| 	sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
 | |
| 	. "github.com/onsi/ginkgo"
 | |
| 	. "github.com/onsi/ginkgo/extensions/table"
 | |
| 	. "github.com/onsi/gomega"
 | |
| )
 | |
| 
 | |
| var _ = Describe("Injector Suite", func() {
 | |
| 	Context("NewInjector", func() {
 | |
| 		type newInjectorTableInput struct {
 | |
| 			headers         []options.Header
 | |
| 			initialHeaders  http.Header
 | |
| 			session         *sessionsapi.SessionState
 | |
| 			expectedHeaders http.Header
 | |
| 			expectedErr     error
 | |
| 		}
 | |
| 
 | |
| 		DescribeTable("creates an injector",
 | |
| 			func(in newInjectorTableInput) {
 | |
| 				injector, err := NewInjector(in.headers)
 | |
| 				if in.expectedErr != nil {
 | |
| 					Expect(err).To(MatchError(in.expectedErr))
 | |
| 					Expect(injector).To(BeNil())
 | |
| 					return
 | |
| 				}
 | |
| 
 | |
| 				Expect(err).ToNot(HaveOccurred())
 | |
| 				Expect(injector).ToNot(BeNil())
 | |
| 
 | |
| 				headers := in.initialHeaders.Clone()
 | |
| 				injector.Inject(headers, in.session)
 | |
| 				Expect(headers).To(Equal(in.expectedHeaders))
 | |
| 			},
 | |
| 			Entry("with no configured headers", newInjectorTableInput{
 | |
| 				headers: []options.Header{},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a static valued header from string", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "Secret",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								SecretSource: &options.SecretSource{
 | |
| 									Value: []byte("super-secret"),
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo":    []string{"bar", "baz"},
 | |
| 					"Secret": []string{"super-secret"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a static valued header from env", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "Secret",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								SecretSource: &options.SecretSource{
 | |
| 									FromEnv: "SECRET_ENV",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo":    []string{"bar", "baz"},
 | |
| 					"Secret": []string{"super-secret-env"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a claim valued header", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "Claim",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "id_token",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{
 | |
| 					IDToken: "IDToken-1234",
 | |
| 				},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo":   []string{"bar", "baz"},
 | |
| 					"Claim": []string{"IDToken-1234"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a claim valued header and a nil session", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "Claim",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "id_token",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: nil,
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a prefixed claim valued header", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "Claim",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim:  "id_token",
 | |
| 									Prefix: "Bearer ",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{
 | |
| 					IDToken: "IDToken-1234",
 | |
| 				},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo":   []string{"bar", "baz"},
 | |
| 					"Claim": []string{"Bearer IDToken-1234"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a prefixed claim valued header missing the claim", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "Claim",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim:  "idToken",
 | |
| 									Prefix: "Bearer ",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a basicAuthPassword and claim valued header", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "X-Auth-Request-Authorization",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "user",
 | |
| 									BasicAuthPassword: &options.SecretSource{
 | |
| 										Value: []byte("basic-password"),
 | |
| 									},
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{
 | |
| 					User: "user-123",
 | |
| 				},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo":                          []string{"bar", "baz"},
 | |
| 					"X-Auth-Request-Authorization": []string{"Basic " + base64.StdEncoding.EncodeToString([]byte("user-123:basic-password"))},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a basicAuthPassword and claim valued header missing the claim", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "X-Auth-Request-Authorization",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "user",
 | |
| 									BasicAuthPassword: &options.SecretSource{
 | |
| 										Value: []byte(base64.StdEncoding.EncodeToString([]byte("basic-password"))),
 | |
| 									},
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a header that already exists", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "X-Auth-Request-User",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "user",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"X-Auth-Request-User": []string{"user"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{
 | |
| 					User: "user-123",
 | |
| 				},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"X-Auth-Request-User": []string{"user", "user-123"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 			Entry("with a claim and secret valued header value", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "Claim",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "id_token",
 | |
| 								},
 | |
| 								SecretSource: &options.SecretSource{
 | |
| 									FromEnv: "SECRET_ENV",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{
 | |
| 					IDToken: "IDToken-1234",
 | |
| 				},
 | |
| 				expectedHeaders: nil,
 | |
| 				expectedErr:     errors.New("error building injector for header \"Claim\": header \"Claim\" value has multiple entries: only one entry per value is allowed"),
 | |
| 			}),
 | |
| 			Entry("with an invalid static valued header", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "Secret",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								SecretSource: &options.SecretSource{
 | |
| 									FromEnv:  "SECRET_ENV",
 | |
| 									FromFile: "secret-file",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session:         &sessionsapi.SessionState{},
 | |
| 				expectedHeaders: nil,
 | |
| 				expectedErr:     errors.New("error building injector for header \"Secret\": error getting secret value: secret source is invalid: exactly one entry required, specify either value, fromEnv or fromFile"),
 | |
| 			}),
 | |
| 			Entry("with an invalid basicAuthPassword claim valued header", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "X-Auth-Request-Authorization",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "user",
 | |
| 									BasicAuthPassword: &options.SecretSource{
 | |
| 										Value:   []byte(base64.StdEncoding.EncodeToString([]byte("basic-password"))),
 | |
| 										FromEnv: "SECRET_ENV",
 | |
| 									},
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{
 | |
| 					User: "user-123",
 | |
| 				},
 | |
| 				expectedHeaders: nil,
 | |
| 				expectedErr:     errors.New("error building injector for header \"X-Auth-Request-Authorization\": error loading basicAuthPassword: secret source is invalid: exactly one entry required, specify either value, fromEnv or fromFile"),
 | |
| 			}),
 | |
| 			Entry("with a mix of configured headers", newInjectorTableInput{
 | |
| 				headers: []options.Header{
 | |
| 					{
 | |
| 						Name: "X-Auth-Request-Authorization",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "user",
 | |
| 									BasicAuthPassword: &options.SecretSource{
 | |
| 										Value: []byte("basic-password"),
 | |
| 									},
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 					{
 | |
| 						Name: "X-Auth-Request-User",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "user",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 					{
 | |
| 						Name: "X-Auth-Request-Email",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								ClaimSource: &options.ClaimSource{
 | |
| 									Claim: "email",
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 					{
 | |
| 						Name: "X-Auth-Request-Version-Info",
 | |
| 						Values: []options.HeaderValue{
 | |
| 							{
 | |
| 								SecretSource: &options.SecretSource{
 | |
| 									Value: []byte("major=1"),
 | |
| 								},
 | |
| 							},
 | |
| 							{
 | |
| 								SecretSource: &options.SecretSource{
 | |
| 									Value: []byte("minor=2"),
 | |
| 								},
 | |
| 							},
 | |
| 							{
 | |
| 								SecretSource: &options.SecretSource{
 | |
| 									Value: []byte("patch=3"),
 | |
| 								},
 | |
| 							},
 | |
| 						},
 | |
| 					},
 | |
| 				},
 | |
| 				initialHeaders: http.Header{
 | |
| 					"foo": []string{"bar", "baz"},
 | |
| 				},
 | |
| 				session: &sessionsapi.SessionState{
 | |
| 					User:  "user-123",
 | |
| 					Email: "user@example.com",
 | |
| 				},
 | |
| 				expectedHeaders: http.Header{
 | |
| 					"foo":                          []string{"bar", "baz"},
 | |
| 					"X-Auth-Request-Authorization": []string{"Basic " + base64.StdEncoding.EncodeToString([]byte("user-123:basic-password"))},
 | |
| 					"X-Auth-Request-User":          []string{"user-123"},
 | |
| 					"X-Auth-Request-Email":         []string{"user@example.com"},
 | |
| 					"X-Auth-Request-Version-Info":  []string{"major=1", "minor=2", "patch=3"},
 | |
| 				},
 | |
| 				expectedErr: nil,
 | |
| 			}),
 | |
| 		)
 | |
| 	})
 | |
| })
 |