@@ -0,0 +1,405 @@
9+ middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
10+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
11+ sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
12+ . "github.com/onsi/ginkgo"
13+ . "github.com/onsi/ginkgo/extensions/table"
14+ . "github.com/onsi/gomega"
17+var _ = Describe("Headers Suite", func() {
18+ type headersTableInput struct {
19+ headers []options.Header
20+ initialHeaders http.Header
21+ session *sessionsapi.SessionState
22+ expectedHeaders http.Header
26+ DescribeTable("the request header injector",
27+ func(in headersTableInput) {
28+ scope := &middlewareapi.RequestScope{
32+ // Set up the request with a request scope
33+ req := httptest.NewRequest("", "/", nil)
34+ contextWithScope := context.WithValue(req.Context(), requestScopeKey, scope)
35+ req = req.WithContext(contextWithScope)
36+ req.Header = in.initialHeaders.Clone()
38+ rw := httptest.NewRecorder()
40+ // Create the handler with a next handler that will capture the headers
42+ var gotHeaders http.Header
43+ injector, err := NewRequestHeaderInjector(in.headers)
44+ if in.expectedErr != "" {
45+ Expect(err).To(MatchError(in.expectedErr))
48+ Expect(err).ToNot(HaveOccurred())
50+ handler := injector(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
51+ gotHeaders = r.Header.Clone()
53+ handler.ServeHTTP(rw, req)
55+ Expect(gotHeaders).To(Equal(in.expectedHeaders))
57+ Entry("with no configured headers", headersTableInput{
58+ headers: []options.Header{},
59+ initialHeaders: http.Header{
60+ "foo": []string{"bar", "baz"},
62+ session: &sessionsapi.SessionState{},
63+ expectedHeaders: http.Header{
64+ "foo": []string{"bar", "baz"},
68+ Entry("with a claim valued header", headersTableInput{
69+ headers: []options.Header{
72+ Values: []options.HeaderValue{
74+ ClaimSource: &options.ClaimSource{
81+ initialHeaders: http.Header{
82+ "foo": []string{"bar", "baz"},
84+ session: &sessionsapi.SessionState{
85+ IDToken: "IDToken-1234",
87+ expectedHeaders: http.Header{
88+ "foo": []string{"bar", "baz"},
89+ "Claim": []string{"IDToken-1234"},
93+ Entry("with a claim valued header (without preservation)", headersTableInput{
94+ headers: []options.Header{
97+ Values: []options.HeaderValue{
99+ ClaimSource: &options.ClaimSource{
106+ initialHeaders: http.Header{
107+ "Claim": []string{"bar", "baz"},
109+ session: &sessionsapi.SessionState{
110+ IDToken: "IDToken-1234",
112+ expectedHeaders: http.Header{
113+ "Claim": []string{"IDToken-1234"},
117+ Entry("with a claim valued header (with preservation)", headersTableInput{
118+ headers: []options.Header{
121+ PreserveRequestValue: true,
122+ Values: []options.HeaderValue{
124+ ClaimSource: &options.ClaimSource{
131+ initialHeaders: http.Header{
132+ "Claim": []string{"bar", "baz"},
134+ session: &sessionsapi.SessionState{
135+ IDToken: "IDToken-1234",
137+ expectedHeaders: http.Header{
138+ "Claim": []string{"bar", "baz", "IDToken-1234"},
142+ Entry("with a claim valued header that's not present (without preservation)", headersTableInput{
143+ headers: []options.Header{
146+ Values: []options.HeaderValue{
148+ ClaimSource: &options.ClaimSource{
155+ initialHeaders: http.Header{
156+ "Claim": []string{"bar", "baz"},
159+ expectedHeaders: http.Header{},
162+ Entry("with a claim valued header that's not present (with preservation)", headersTableInput{
163+ headers: []options.Header{
166+ PreserveRequestValue: true,
167+ Values: []options.HeaderValue{
169+ ClaimSource: &options.ClaimSource{
176+ initialHeaders: http.Header{
177+ "Claim": []string{"bar", "baz"},
180+ expectedHeaders: http.Header{
181+ "Claim": []string{"bar", "baz"},
185+ Entry("with an invalid basicAuthPassword claim valued header", headersTableInput{
186+ headers: []options.Header{
188+ Name: "X-Auth-Request-Authorization",
189+ Values: []options.HeaderValue{
191+ ClaimSource: &options.ClaimSource{
193+ BasicAuthPassword: &options.SecretSource{
194+ Value: []byte(base64.StdEncoding.EncodeToString([]byte("basic-password"))),
195+ FromEnv: "SECRET_ENV",
202+ initialHeaders: http.Header{
203+ "foo": []string{"bar", "baz"},
205+ session: &sessionsapi.SessionState{
208+ expectedHeaders: nil,
209+ expectedErr: "error building request header injector: error building request injector: 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",
213+ DescribeTable("the response header injector",
214+ func(in headersTableInput) {
215+ scope := &middlewareapi.RequestScope{
216+ Session: in.session,
219+ // Set up the request with a request scope
220+ req := httptest.NewRequest("", "/", nil)
221+ contextWithScope := context.WithValue(req.Context(), requestScopeKey, scope)
222+ req = req.WithContext(contextWithScope)
224+ rw := httptest.NewRecorder()
225+ for key, values := range in.initialHeaders {
226+ for _, value := range values {
227+ rw.Header().Add(key, value)
231+ // Create the handler with a next handler that will capture the headers
233+ var gotHeaders http.Header
234+ injector, err := NewResponseHeaderInjector(in.headers)
235+ if in.expectedErr != "" {
236+ Expect(err).To(MatchError(in.expectedErr))
239+ Expect(err).ToNot(HaveOccurred())
241+ handler := injector(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
242+ gotHeaders = w.Header().Clone()
244+ handler.ServeHTTP(rw, req)
246+ Expect(gotHeaders).To(Equal(in.expectedHeaders))
248+ Entry("with no configured headers", headersTableInput{
249+ headers: []options.Header{},
250+ initialHeaders: http.Header{
251+ "Foo": []string{"bar", "baz"},
253+ session: &sessionsapi.SessionState{},
254+ expectedHeaders: http.Header{
255+ "Foo": []string{"bar", "baz"},
259+ Entry("with a claim valued header", headersTableInput{
260+ headers: []options.Header{
263+ Values: []options.HeaderValue{
265+ ClaimSource: &options.ClaimSource{
272+ initialHeaders: http.Header{
273+ "Foo": []string{"bar", "baz"},
275+ session: &sessionsapi.SessionState{
276+ IDToken: "IDToken-1234",
278+ expectedHeaders: http.Header{
279+ "Foo": []string{"bar", "baz"},
280+ "Claim": []string{"IDToken-1234"},
284+ Entry("with a claim valued header (without preservation)", headersTableInput{
285+ headers: []options.Header{
288+ Values: []options.HeaderValue{
290+ ClaimSource: &options.ClaimSource{
297+ initialHeaders: http.Header{
298+ "Claim": []string{"bar", "baz"},
300+ session: &sessionsapi.SessionState{
301+ IDToken: "IDToken-1234",
303+ expectedHeaders: http.Header{
304+ "Claim": []string{"bar", "baz", "IDToken-1234"},
308+ Entry("with a claim valued header (with preservation)", headersTableInput{
309+ headers: []options.Header{
312+ PreserveRequestValue: true,
313+ Values: []options.HeaderValue{
315+ ClaimSource: &options.ClaimSource{
322+ initialHeaders: http.Header{
323+ "Claim": []string{"bar", "baz"},
325+ session: &sessionsapi.SessionState{
326+ IDToken: "IDToken-1234",
328+ expectedHeaders: http.Header{
329+ "Claim": []string{"bar", "baz", "IDToken-1234"},
333+ Entry("with a claim valued header that's not present (without preservation)", headersTableInput{
334+ headers: []options.Header{
337+ Values: []options.HeaderValue{
339+ ClaimSource: &options.ClaimSource{
346+ initialHeaders: http.Header{
347+ "Claim": []string{"bar", "baz"},
350+ expectedHeaders: http.Header{
351+ "Claim": []string{"bar", "baz"},
355+ Entry("with a claim valued header that's not present (with preservation)", headersTableInput{
356+ headers: []options.Header{
359+ PreserveRequestValue: true,
360+ Values: []options.HeaderValue{
362+ ClaimSource: &options.ClaimSource{
369+ initialHeaders: http.Header{
370+ "Claim": []string{"bar", "baz"},
373+ expectedHeaders: http.Header{
374+ "Claim": []string{"bar", "baz"},
378+ Entry("with an invalid basicAuthPassword claim valued header", headersTableInput{
379+ headers: []options.Header{
381+ Name: "X-Auth-Request-Authorization",
382+ Values: []options.HeaderValue{
384+ ClaimSource: &options.ClaimSource{
386+ BasicAuthPassword: &options.SecretSource{
387+ Value: []byte(base64.StdEncoding.EncodeToString([]byte("basic-password"))),
388+ FromEnv: "SECRET_ENV",
395+ initialHeaders: http.Header{
396+ "foo": []string{"bar", "baz"},
398+ session: &sessionsapi.SessionState{
401+ expectedHeaders: nil,
402+ expectedErr: "error building response header injector: error building response injector: 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",