Implement SaveSession in Cookie SessionStore
This commit is contained in:
parent
15a2cf8b9e
commit
b965f25c10
@ -15,6 +15,12 @@ import (
|
||||
"github.com/pusher/oauth2_proxy/pkg/sessions/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// Cookies are limited to 4kb including the length of the cookie name,
|
||||
// the cookie name can be up to 256 bytes
|
||||
maxCookieLength = 3840
|
||||
)
|
||||
|
||||
// Ensure CookieSessionStore implements the interface
|
||||
var _ sessions.SessionStore = &SessionStore{}
|
||||
|
||||
@ -34,7 +40,12 @@ type SessionStore struct {
|
||||
// SaveSession takes a sessions.SessionState and stores the information from it
|
||||
// within Cookies set on the HTTP response writer
|
||||
func (s *SessionStore) SaveSession(rw http.ResponseWriter, req *http.Request, ss *sessions.SessionState) error {
|
||||
return fmt.Errorf("method not implemented")
|
||||
value, err := utils.CookieForSession(ss, s.CookieCipher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.setSessionCookie(rw, req, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadSession reads sessions.SessionState information from Cookies within the
|
||||
@ -77,6 +88,26 @@ func (s *SessionStore) ClearSession(rw http.ResponseWriter, req *http.Request) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// setSessionCookie adds the user's session cookie to the response
|
||||
func (s *SessionStore) setSessionCookie(rw http.ResponseWriter, req *http.Request, val string) {
|
||||
for _, c := range s.makeSessionCookie(req, val, s.CookieExpire, time.Now()) {
|
||||
http.SetCookie(rw, c)
|
||||
}
|
||||
}
|
||||
|
||||
// makeSessionCookie creates an http.Cookie containing the authenticated user's
|
||||
// authentication details
|
||||
func (s *SessionStore) makeSessionCookie(req *http.Request, value string, expiration time.Duration, now time.Time) []*http.Cookie {
|
||||
if value != "" {
|
||||
value = cookie.SignedValue(s.CookieSecret, s.CookieName, value, now)
|
||||
}
|
||||
c := s.makeCookie(req, s.CookieName, value, expiration)
|
||||
if len(c.Value) > 4096-len(s.CookieName) {
|
||||
return splitCookie(c)
|
||||
}
|
||||
return []*http.Cookie{c}
|
||||
}
|
||||
|
||||
func (s *SessionStore) makeCookie(req *http.Request, name string, value string, expiration time.Duration) *http.Cookie {
|
||||
return cookies.MakeCookie(
|
||||
req,
|
||||
@ -115,6 +146,33 @@ func NewCookieSessionStore(opts options.CookieStoreOptions, cookieOpts *options.
|
||||
}, nil
|
||||
}
|
||||
|
||||
// splitCookie reads the full cookie generated to store the session and splits
|
||||
// it into a slice of cookies which fit within the 4kb cookie limit indexing
|
||||
// the cookies from 0
|
||||
func splitCookie(c *http.Cookie) []*http.Cookie {
|
||||
if len(c.Value) < maxCookieLength {
|
||||
return []*http.Cookie{c}
|
||||
}
|
||||
cookies := []*http.Cookie{}
|
||||
valueBytes := []byte(c.Value)
|
||||
count := 0
|
||||
for len(valueBytes) > 0 {
|
||||
new := copyCookie(c)
|
||||
new.Name = fmt.Sprintf("%s_%d", c.Name, count)
|
||||
count++
|
||||
if len(valueBytes) < maxCookieLength {
|
||||
new.Value = string(valueBytes)
|
||||
valueBytes = []byte{}
|
||||
} else {
|
||||
newValue := valueBytes[:maxCookieLength]
|
||||
valueBytes = valueBytes[maxCookieLength:]
|
||||
new.Value = string(newValue)
|
||||
}
|
||||
cookies = append(cookies, new)
|
||||
}
|
||||
return cookies
|
||||
}
|
||||
|
||||
// loadCookie retreieves the sessions state cookie from the http request.
|
||||
// If a single cookie is present this will be returned, otherwise it attempts
|
||||
// to reconstruct a cookie split up by splitCookie
|
||||
|
@ -89,7 +89,7 @@ var _ = Describe("NewSessionStore", func() {
|
||||
})
|
||||
|
||||
It("sets a `set-cookie` header in the response", func() {
|
||||
Expect(response.Header().Get("Set-Cookie")).ToNot(BeEmpty())
|
||||
Expect(response.Header().Get("set-cookie")).ToNot(BeEmpty())
|
||||
})
|
||||
|
||||
CheckCookieOptions()
|
||||
@ -144,7 +144,7 @@ var _ = Describe("NewSessionStore", func() {
|
||||
})
|
||||
|
||||
It("sets a `set-cookie` header in the response", func() {
|
||||
Expect(response.Header().Get("Set-Cookie")).ToNot(BeEmpty())
|
||||
Expect(response.Header().Get("set-cookie")).ToNot(BeEmpty())
|
||||
})
|
||||
|
||||
CheckCookieOptions()
|
||||
@ -152,12 +152,23 @@ var _ = Describe("NewSessionStore", func() {
|
||||
|
||||
Context("when ClearSession is called", func() {
|
||||
BeforeEach(func() {
|
||||
cookie := cookies.MakeCookie(request,
|
||||
cookieOpts.CookieName,
|
||||
"foo",
|
||||
cookieOpts.CookiePath,
|
||||
cookieOpts.CookieDomain,
|
||||
cookieOpts.CookieHTTPOnly,
|
||||
cookieOpts.CookieSecure,
|
||||
cookieOpts.CookieExpire,
|
||||
time.Now(),
|
||||
)
|
||||
request.AddCookie(cookie)
|
||||
err := ss.ClearSession(response, request)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("sets a `set-cookie` header in the response", func() {
|
||||
Expect(response.Header().Get("set-cookie")).ToNot(BeEmpty())
|
||||
Expect(response.Header().Get("Set-Cookie")).ToNot(BeEmpty())
|
||||
})
|
||||
|
||||
CheckCookieOptions()
|
||||
@ -178,6 +189,15 @@ var _ = Describe("NewSessionStore", func() {
|
||||
CookieHTTPOnly: true,
|
||||
}
|
||||
|
||||
session = &sessionsapi.SessionState{
|
||||
AccessToken: "AccessToken",
|
||||
IDToken: "IDToken",
|
||||
ExpiresOn: time.Now().Add(1 * time.Hour),
|
||||
RefreshToken: "RefreshToken",
|
||||
Email: "john.doe@example.com",
|
||||
User: "john.doe",
|
||||
}
|
||||
|
||||
request = httptest.NewRequest("GET", "http://example.com/", nil)
|
||||
response = httptest.NewRecorder()
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user