From 9c58e168f84b982c0d55a040df6400a9ffb73d77 Mon Sep 17 00:00:00 2001 From: Dalton Hubble Date: Fri, 30 Dec 2022 20:03:11 -0800 Subject: [PATCH] Remove cookie Config from the Session struct * A Session should avoid having http.Cookie specific settings such as the cookie Config. Use the CookieStore's cookie Config when setting a new http.Cookie on a http.ResponseWriter * Stop exposing the cookie config of each session to callers, which would allow improper Config mutations after a cookie has been issued (confusing) * Reorganize cookie Config fields a bit --- CHANGES.md | 2 ++ cookie.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sessions.go | 23 +++------------------- store.go | 44 ++++------------------------------------- 4 files changed, 65 insertions(+), 60 deletions(-) create mode 100644 cookie.go diff --git a/CHANGES.md b/CHANGES.md index 4802d2c..5a86b60 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,8 @@ Notable changes between releases. ## Latest +* Remove cookie `Config` field from `Session` ([#17](https://github.com/dghubble/sessions/pull/17)) + ## v0.2.1 * Update minimum Go version from v1.17 to v1.18 ([#15](https://github.com/dghubble/sessions/pull/15)) diff --git a/cookie.go b/cookie.go new file mode 100644 index 0000000..98efb56 --- /dev/null +++ b/cookie.go @@ -0,0 +1,56 @@ +package sessions + +import ( + "net/http" + "time" +) + +// Config is the set of cookie properties. +type Config struct { + // cookie domain/path scope (leave zeroed for requested resource scope) + Path string + Domain string + // MaxAge=0 means no 'Max-Age' attribute specified. + // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'. + // MaxAge>0 means Max-Age attribute present and given in seconds. + MaxAge int + // cookie may only be transferred over HTTPS + Secure bool + // browser should prohibit non-HTTP (i.e. javascript) cookie access + HTTPOnly bool + // prohibit sending in cross-site requests with SameSiteLaxMode or SameSiteLaxMode + SameSite http.SameSite +} + +// newCookie returns a new http.Cookie with the given name, value, and +// properties from config. +func newCookie(name, value string, config *Config) *http.Cookie { + cookie := &http.Cookie{ + Name: name, + Value: value, + Path: config.Path, + Domain: config.Domain, + MaxAge: config.MaxAge, + HttpOnly: config.HTTPOnly, + Secure: config.Secure, + SameSite: config.SameSite, + } + // IE <9 does not understand MaxAge, set Expires based on MaxAge + if expires, present := cookieExpires(config.MaxAge); present { + cookie.Expires = expires + } + return cookie +} + +// cookieExpires takes the MaxAge number of seconds a Cookie should be valid +// and returns the Expires time.Time and whether the attribtue should be set. +// http://golang.org/src/net/http/cookie.go?s=618:801#L23 +func cookieExpires(maxAge int) (time.Time, bool) { + if maxAge > 0 { + d := time.Duration(maxAge) * time.Second + return time.Now().Add(d), true + } else if maxAge < 0 { + return time.Unix(1, 0), true // first second of the epoch + } + return time.Time{}, false +} diff --git a/sessions.go b/sessions.go index c902148..322195f 100644 --- a/sessions.go +++ b/sessions.go @@ -8,30 +8,13 @@ const ( defaultMaxAge = 3600 * 24 * 7 // 1 week ) -// Config is the set of cookie properties. -type Config struct { - // cookie domain/path scope (leave zeroed for requested resource scope) - Domain string - Path string - // MaxAge=0 means no 'Max-Age' attribute specified. - // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'. - // MaxAge>0 means Max-Age attribute present and given in seconds. - MaxAge int - // browser should prohibit non-HTTP (i.e. javascript) cookie access - HTTPOnly bool - // cookie may only be transferred over HTTPS - Secure bool - // prohibit sending in cross-site requests with SameSiteLaxMode or SameSiteLaxMode - SameSite http.SameSite -} - // Session represents Values state which a named bundle of maintained web state // stores web session state type Session struct { - name string // session cookie name - Config *Config // session cookie config - store Store // session store + name string // session cookie name Values map[string]interface{} + // convenience methods Save and Destroy use store + store Store } // NewSession returns a new Session. diff --git a/store.go b/store.go index b861378..82a83d7 100644 --- a/store.go +++ b/store.go @@ -1,9 +1,9 @@ package sessions import ( - "github.com/gorilla/securecookie" "net/http" - "time" + + "github.com/gorilla/securecookie" ) // Store is the interface for creating, reading, updating and destroying @@ -40,10 +40,7 @@ func NewCookieStore(keyPairs ...[]byte) *CookieStore { // New returns a new Session with the requested name and the store's config // value. func (s *CookieStore) New(name string) *Session { - session := NewSession(s, name) - config := *s.Config - session.Config = &config - return session + return NewSession(s, name) } // Get returns the named Session from the Request. Returns an error if the @@ -66,7 +63,7 @@ func (s *CookieStore) Save(w http.ResponseWriter, session *Session) error { if err != nil { return err } - http.SetCookie(w, newCookie(session.Name(), cookieValue, session.Config)) + http.SetCookie(w, newCookie(session.Name(), cookieValue, s.Config)) return nil } @@ -75,36 +72,3 @@ func (s *CookieStore) Save(w http.ResponseWriter, session *Session) error { func (s *CookieStore) Destroy(w http.ResponseWriter, name string) { http.SetCookie(w, newCookie(name, "", &Config{MaxAge: -1, Path: s.Config.Path})) } - -// newCookie returns a new http.Cookie with the given name, value, and -// properties from config. -func newCookie(name, value string, config *Config) *http.Cookie { - cookie := &http.Cookie{ - Name: name, - Value: value, - Domain: config.Domain, - Path: config.Path, - MaxAge: config.MaxAge, - HttpOnly: config.HTTPOnly, - Secure: config.Secure, - SameSite: config.SameSite, - } - // IE <9 does not understand MaxAge, set Expires based on MaxAge - if expires, present := cookieExpires(config.MaxAge); present { - cookie.Expires = expires - } - return cookie -} - -// cookieExpires takes the MaxAge number of seconds a Cookie should be valid -// and returns the Expires time.Time and whether the attribtue should be set. -// http://golang.org/src/net/http/cookie.go?s=618:801#L23 -func cookieExpires(maxAge int) (time.Time, bool) { - if maxAge > 0 { - d := time.Duration(maxAge) * time.Second - return time.Now().Add(d), true - } else if maxAge < 0 { - return time.Unix(1, 0), true // first second of the epoch - } - return time.Time{}, false -}