Skip to content

Commit

Permalink
Remove cookie Config from the Session struct
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
dghubble committed Dec 31, 2022
1 parent 4fcd619 commit 9c58e16
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 60 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
56 changes: 56 additions & 0 deletions cookie.go
Original file line number Diff line number Diff line change
@@ -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
}
23 changes: 3 additions & 20 deletions sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
44 changes: 4 additions & 40 deletions store.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -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
}

0 comments on commit 9c58e16

Please sign in to comment.