@@ -1,20 +1,12 @@
11package sessions_test
22
33import (
4− "crypto/rand"
54 "encoding/base64"
6− "net/http"
7− "net/http/httptest"
8− "strconv"
9− "strings"
5+ "math/rand"
106 "testing"
117 "time"
128
13− miniredis "github.com/alicebob/miniredis/v2"
149 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
15− sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
16− cookiesapi "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies"
17− "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
1810 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
1911 "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions"
2012 sessionscookie "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie"
@@ -34,341 +26,7 @@ var _ = Describe("NewSessionStore", func() {
3426 var opts *options.SessionOptions
3527 var cookieOpts *options.CookieOptions
3628
37− var request *http.Request
38− var response *httptest.ResponseRecorder
39− var session *sessionsapi.SessionState
40− var ss sessionsapi.SessionStore
41− var mr *miniredis.Miniredis
42−
43− CheckCookieOptions := func() {
44− Context("the cookies returned", func() {
45− var cookies []*http.Cookie
46− BeforeEach(func() {
47− cookies = response.Result().Cookies()
48− })
49−
50− It("have the correct name set", func() {
51− if len(cookies) == 1 {
52− Expect(cookies[0].Name).To(Equal(cookieOpts.Name))
53− } else {
54− for _, cookie := range cookies {
55− Expect(cookie.Name).To(ContainSubstring(cookieOpts.Name))
56− }
57− }
58− })
59−
60− It("have the correct path set", func() {
61− for _, cookie := range cookies {
62− Expect(cookie.Path).To(Equal(cookieOpts.Path))
63− }
64− })
65−
66− It("have the correct domain set", func() {
67− for _, cookie := range cookies {
68− specifiedDomain := ""
69− if len(cookieOpts.Domains) > 0 {
70− specifiedDomain = cookieOpts.Domains[0]
71− }
72− Expect(cookie.Domain).To(Equal(specifiedDomain))
73− }
74− })
75−
76− It("have the correct HTTPOnly set", func() {
77− for _, cookie := range cookies {
78− Expect(cookie.HttpOnly).To(Equal(cookieOpts.HTTPOnly))
79− }
80− })
81−
82− It("have the correct secure set", func() {
83− for _, cookie := range cookies {
84− Expect(cookie.Secure).To(Equal(cookieOpts.Secure))
85− }
86− })
87−
88− It("have the correct SameSite set", func() {
89− for _, cookie := range cookies {
90− Expect(cookie.SameSite).To(Equal(cookiesapi.ParseSameSite(cookieOpts.SameSite)))
91− }
92− })
93−
94− It("have a signature timestamp matching session.CreatedAt", func() {
95− for _, cookie := range cookies {
96− if cookie.Value != "" {
97− parts := strings.Split(cookie.Value, "|")
98− Expect(parts).To(HaveLen(3))
99− Expect(parts[1]).To(Equal(strconv.Itoa(int(session.CreatedAt.Unix()))))
100− }
101− }
102− })
103−
104− })
105− }
106−
107− // The following should only be for server stores
108− PersistentSessionStoreTests := func() {
109− Context("when Clear is called on a persistent store", func() {
110− var resultCookies []*http.Cookie
111−
112− BeforeEach(func() {
113− req := httptest.NewRequest("GET", "http://example.com/", nil)
114− saveResp := httptest.NewRecorder()
115− err := ss.Save(saveResp, req, session)
116− Expect(err).ToNot(HaveOccurred())
117−
118− resultCookies = saveResp.Result().Cookies()
119− for _, c := range resultCookies {
120− request.AddCookie(c)
121− }
122− err = ss.Clear(response, request)
123− Expect(err).ToNot(HaveOccurred())
124− })
125−
126− Context("attempting to Load", func() {
127− var loadedAfterClear *sessionsapi.SessionState
128− var loadErr error
129−
130− BeforeEach(func() {
131− loadReq := httptest.NewRequest("GET", "http://example.com/", nil)
132− for _, c := range resultCookies {
133− loadReq.AddCookie(c)
134− }
135−
136− loadedAfterClear, loadErr = ss.Load(loadReq)
137− })
138−
139− It("returns an empty session", func() {
140− Expect(loadedAfterClear).To(BeNil())
141− })
142−
143− It("returns an error", func() {
144− Expect(loadErr).To(HaveOccurred())
145− })
146− })
147−
148− CheckCookieOptions()
149− })
150− }
151−
152− SessionStoreInterfaceTests := func(persistent bool) {
153− Context("when Save is called", func() {
154− Context("with no existing session", func() {
155− BeforeEach(func() {
156− err := ss.Save(response, request, session)
157− Expect(err).ToNot(HaveOccurred())
158− })
159−
160− It("sets a `set-cookie` header in the response", func() {
161− Expect(response.Header().Get("set-cookie")).ToNot(BeEmpty())
162− })
163−
164− It("Ensures the session CreatedAt is not zero", func() {
165− Expect(session.CreatedAt.IsZero()).To(BeFalse())
166− })
167− })
168−
169− Context("with a broken session", func() {
170− BeforeEach(func() {
171− By("Using a valid cookie with a different providers session encoding")
172− broken := "BrokenSessionFromADifferentSessionImplementation"
173− value := encryption.SignedValue(cookieOpts.Secret, cookieOpts.Name, []byte(broken), time.Now())
174− cookie := cookiesapi.MakeCookieFromOptions(request, cookieOpts.Name, value, cookieOpts, cookieOpts.Expire, time.Now())
175− request.AddCookie(cookie)
176−
177− err := ss.Save(response, request, session)
178− Expect(err).ToNot(HaveOccurred())
179− })
180−
181− It("sets a `set-cookie` header in the response", func() {
182− Expect(response.Header().Get("set-cookie")).ToNot(BeEmpty())
183− })
184−
185− It("Ensures the session CreatedAt is not zero", func() {
186− Expect(session.CreatedAt.IsZero()).To(BeFalse())
187− })
188− })
189−
190− Context("with an expired saved session", func() {
191− var err error
192− BeforeEach(func() {
193− By("saving a session")
194− req := httptest.NewRequest("GET", "http://example.com/", nil)
195− saveResp := httptest.NewRecorder()
196− err = ss.Save(saveResp, req, session)
197− Expect(err).ToNot(HaveOccurred())
198−
199− By("and clearing the session")
200− for _, c := range saveResp.Result().Cookies() {
201− request.AddCookie(c)
202− }
203− clearResp := httptest.NewRecorder()
204− err = ss.Clear(clearResp, request)
205− Expect(err).ToNot(HaveOccurred())
206−
207− By("then saving a request with the cleared session")
208− err = ss.Save(response, request, session)
209− })
210−
211− It("no error should occur", func() {
212− Expect(err).ToNot(HaveOccurred())
213− })
214− })
215−
216− CheckCookieOptions()
217− })
218−
219− Context("when Clear is called", func() {
220− BeforeEach(func() {
221− req := httptest.NewRequest("GET", "http://example.com/", nil)
222− saveResp := httptest.NewRecorder()
223− err := ss.Save(saveResp, req, session)
224− Expect(err).ToNot(HaveOccurred())
225−
226− for _, c := range saveResp.Result().Cookies() {
227− request.AddCookie(c)
228− }
229− err = ss.Clear(response, request)
230− Expect(err).ToNot(HaveOccurred())
231− })
232−
233− It("sets a `set-cookie` header in the response", func() {
234− Expect(response.Header().Get("Set-Cookie")).ToNot(BeEmpty())
235− })
236−
237− CheckCookieOptions()
238− })
239−
240− Context("when Load is called", func() {
241− LoadSessionTests := func() {
242− var loadedSession *sessionsapi.SessionState
243− BeforeEach(func() {
244− var err error
245− loadedSession, err = ss.Load(request)
246− Expect(err).ToNot(HaveOccurred())
247− })
248−
249− It("loads a session equal to the original session", func() {
250− if cookieOpts.Secret == "" {
251− // Only Email and User stored in session when encrypted
252− Expect(loadedSession.Email).To(Equal(session.Email))
253− Expect(loadedSession.User).To(Equal(session.User))
254− } else {
255− // All fields stored in session if encrypted
256−
257− // Can't compare time.Time using Equal() so remove ExpiresOn from sessions
258− l := *loadedSession
259− l.CreatedAt = nil
260− l.ExpiresOn = nil
261− s := *session
262− s.CreatedAt = nil
263− s.ExpiresOn = nil
264− Expect(l).To(Equal(s))
265−
266− // Compare time.Time separately
267− Expect(loadedSession.CreatedAt.Equal(*session.CreatedAt)).To(BeTrue())
268− Expect(loadedSession.ExpiresOn.Equal(*session.ExpiresOn)).To(BeTrue())
269− }
270− })
271− }
272−
273− BeforeEach(func() {
274− req := httptest.NewRequest("GET", "http://example.com/", nil)
275− resp := httptest.NewRecorder()
276− err := ss.Save(resp, req, session)
277− Expect(err).ToNot(HaveOccurred())
278−
279− for _, cookie := range resp.Result().Cookies() {
280− request.AddCookie(cookie)
281− }
282− })
283−
284− Context("before the refresh period", func() {
285− LoadSessionTests()
286− })
287−
288− // Test TTLs and cleanup of persistent session storage
289− // For non-persistent we rely on the browser cookie lifecycle
290− if persistent {
291− Context("after the refresh period, but before the cookie expire period", func() {
292− BeforeEach(func() {
293− switch ss.(type) {
294− case *redis.SessionStore:
295− mr.FastForward(cookieOpts.Refresh + time.Minute)
296− }
297− })
298−
299− LoadSessionTests()
300− })
301−
302− Context("after the cookie expire period", func() {
303− var loadedSession *sessionsapi.SessionState
304− var err error
305−
306− BeforeEach(func() {
307− switch ss.(type) {
308− case *redis.SessionStore:
309− mr.FastForward(cookieOpts.Expire + time.Minute)
310− }
311−
312− loadedSession, err = ss.Load(request)
313− Expect(err).To(HaveOccurred())
314− })
315−
316− It("returns an error loading the session", func() {
317− Expect(err).To(HaveOccurred())
318− })
319−
320− It("returns an empty session", func() {
321− Expect(loadedSession).To(BeNil())
322− })
323− })
324− }
325− })
326−
327− if persistent {
328− PersistentSessionStoreTests()
329− }
330− }
331−
332− RunSessionTests := func(persistent bool) {
333− Context("with default options", func() {
334− BeforeEach(func() {
335− var err error
336− ss, err = sessions.NewSessionStore(opts, cookieOpts)
337− Expect(err).ToNot(HaveOccurred())
338− })
339−
340− SessionStoreInterfaceTests(persistent)
341− })
342−
343− Context("with non-default options", func() {
344− BeforeEach(func() {
345− cookieOpts = &options.CookieOptions{
346− Name: "_cookie_name",
347− Path: "/path",
348− Expire: time.Duration(72) * time.Hour,
349− Refresh: time.Duration(2) * time.Hour,
350− Secure: false,
351− HTTPOnly: false,
352− Domains: []string{"example.com"},
353− SameSite: "strict",
354− }
355−
356− // A secret is required but not defaulted
357− secret := make([]byte, 32)
358− _, err := rand.Read(secret)
359− Expect(err).ToNot(HaveOccurred())
360− cookieOpts.Secret = base64.URLEncoding.EncodeToString(secret)
361−
362− ss, err = sessions.NewSessionStore(opts, cookieOpts)
363− Expect(err).ToNot(HaveOccurred())
364− })
365−
366− SessionStoreInterfaceTests(persistent)
367− })
368− }
369−
37029 BeforeEach(func() {
371− ss = nil
37230 opts = &options.SessionOptions{}
37331
37432 // A secret is required to create a Cipher, validation ensures it is the correct
@@ -388,19 +46,6 @@ var _ = Describe("NewSessionStore", func() {
38846 HTTPOnly: true,
38947 SameSite: "",
39048 }
391−
392− expires := time.Now().Add(1 * time.Hour)
393− session = &sessionsapi.SessionState{
394− AccessToken: "AccessToken",
395− IDToken: "IDToken",
396− ExpiresOn: &expires,
397− RefreshToken: "RefreshToken",
398− Email: "john.doe@example.com",
399− User: "john.doe",
400− }
401−
402− request = httptest.NewRequest("GET", "http://example.com/", nil)
403− response = httptest.NewRecorder()
40449 })
40550
40651 Context("with type 'cookie'", func() {
@@ -413,36 +58,12 @@ var _ = Describe("NewSessionStore", func() {
41358 Expect(err).NotTo(HaveOccurred())
41459 Expect(ss).To(BeAssignableToTypeOf(&sessionscookie.SessionStore{}))
41560 })
416−
417− Context("the cookie.SessionStore", func() {
418− RunSessionTests(false)
419− })
420−
421− Context("with an invalid cookie secret", func() {
422− BeforeEach(func() {
423− cookieOpts.Secret = "invalid"
424− })
425−
426− It("returns an error", func() {
427− ss, err := sessions.NewSessionStore(opts, cookieOpts)
428− Expect(err).To(HaveOccurred())
429− Expect(err.Error()).To(Equal("error initialising cipher: crypto/aes: invalid key size 7"))
430− Expect(ss).To(BeNil())
431− })
432− })
43361 })
43462
43563 Context("with type 'redis'", func() {
43664 BeforeEach(func() {
437− var err error
438− mr, err = miniredis.Run()
439− Expect(err).ToNot(HaveOccurred())
44065 opts.Type = options.RedisSessionStoreType
441− opts.Redis.ConnectionURL = "redis://" + mr.Addr()
442− })
443−
444− AfterEach(func() {
445− mr.Close()
66+ opts.Redis.ConnectionURL = "redis://"
44667 })
44768
44869 It("creates a redis.SessionStore", func() {
@@ -450,23 +71,6 @@ var _ = Describe("NewSessionStore", func() {
45071 Expect(err).NotTo(HaveOccurred())
45172 Expect(ss).To(BeAssignableToTypeOf(&redis.SessionStore{}))
45273 })
453−
454− Context("the redis.SessionStore", func() {
455− RunSessionTests(true)
456− })
457−
458− Context("with an invalid cookie secret", func() {
459− BeforeEach(func() {
460− cookieOpts.Secret = "invalid"
461− })
462−
463− It("returns an error", func() {
464− ss, err := sessions.NewSessionStore(opts, cookieOpts)
465− Expect(err).To(HaveOccurred())
466− Expect(err.Error()).To(Equal("error initialising cipher: crypto/aes: invalid key size 7"))
467− Expect(ss).To(BeNil())
468− })
469− })
47074 })
47175
47276 Context("with an invalid type", func() {