From ee375ad9efa3c4d4eb23eb79dd8f083596460edb Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Mon, 8 Jul 2013 16:08:57 -0700 Subject: [PATCH 001/121] Copy store options to sessions instead of referencing them. Fixes GH-8 --- store.go | 6 ++++-- store_test.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 store_test.go diff --git a/store.go b/store.go index 8d00cb3..fe31c08 100644 --- a/store.go +++ b/store.go @@ -73,7 +73,8 @@ func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) { // decoded session after the first call. func (s *CookieStore) New(r *http.Request, name string) (*Session, error) { session := NewSession(s, name) - session.Options = &(*s.Options) + opts := *s.Options + session.Options = &opts session.IsNew = true var err error if c, errCookie := r.Cookie(name); errCookie == nil { @@ -148,7 +149,8 @@ func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) { // See CookieStore.New(). func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) { session := NewSession(s, name) - session.Options = &(*s.Options) + opts := *s.Options + session.Options = &opts session.IsNew = true var err error if c, errCookie := r.Cookie(name); errCookie == nil { diff --git a/store_test.go b/store_test.go new file mode 100644 index 0000000..77fea6b --- /dev/null +++ b/store_test.go @@ -0,0 +1,48 @@ +package sessions + +import ( + "net/http" + "testing" +) + +// Test for GH-8 for CookieStore +func TestGH8CookieStore(t *testing.T) { + originalPath := "/" + store := NewCookieStore() + store.Options.Path = originalPath + req, err := http.NewRequest("GET", "http://www.example.com", nil) + if err != nil { + t.Fatal("failed to create request", err) + } + + session, err := store.New(req, "hello") + if err != nil { + t.Fatal("failed to create session", err) + } + + store.Options.Path = "/foo" + if session.Options.Path != originalPath { + t.Fatalf("bad session path: got %q, want %q", session.Options.Path, originalPath) + } +} + +// Test for GH-8 for FilesystemStore +func TestGH8FilesystemStore(t *testing.T) { + originalPath := "/" + store := NewFilesystemStore("") + store.Options.Path = originalPath + req, err := http.NewRequest("GET", "http://www.example.com", nil) + if err != nil { + t.Fatal("failed to create request", err) + } + + session, err := store.New(req, "hello") + if err != nil { + t.Fatal("failed to create session", err) + } + + store.Options.Path = "/foo" + if session.Options.Path != originalPath { + t.Fatalf("bad session path: got %q, want %q", session.Options.Path, originalPath) + } +} From 8593e03f3101a7001cd6c76a6fb0f3c5cbf2724c Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 10 Jul 2013 11:08:01 -0700 Subject: [PATCH 002/121] Add couchbasestore to README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd92e08..4dea2ab 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,5 @@ Store Implementations --------------------- Other implementations of the sessions.Store interface: - * [redistore](https://github.com/boj/redistore) - store sessions in Redis + * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore/) - store sessions in Couchbase + * [github.com/boj/redistore](https://github.com/boj/redistore) - store sessions in Redis From 696523391f8287795f3d369d66eb83cd50381880 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 2 Aug 2013 14:44:22 +0800 Subject: [PATCH 003/121] Added examples for serialising custom types and handling errors from session.Save(). --- doc.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/doc.go b/doc.go index 1bf3b3c..24ba077 100644 --- a/doc.go +++ b/doc.go @@ -45,6 +45,9 @@ store.Get() to retrieve an existing session or a new one. Then we set some session values in session.Values, which is a map[interface{}]interface{}. And finally we call session.Save() to save the session in the response. +Note that in production code, we should check for errors when calling +session.Save(r, w), and either display an error message or otherwise handle it. + That's all you need to know for the basic usage. Let's take a look at other options, starting with flash messages. @@ -71,6 +74,36 @@ flashes, call session.Flashes(). Here is an example: Flash messages are useful to set information to be read after a redirection, like after form submissions. +There may also be cases where you want to store a complex datatype within a +session, such as a struct. Sessions are serialised using the encoding/gob package, +so it is easy to register new datatypes for storage in sessions: + + import( + "encoding/gob" + "github.com/gorilla/sessions" + ) + + type Person struct { + FirstName string + LastName string + Email string + Age int + } + + type M map[string]interface{} + + func init() { + + gob.Register(&Person{}) + gob.Register(&M{}) + } + +As it's not possible to pass a raw type as a parameter to a function, gob.Register() +relies on us passing it an empty pointer to the type as a parameter. In the example +above we've passed it a pointer to a struct and a pointer to a custom type +representing a map[string]interface. This will then allow us to serialise/deserialise +values of those types to and from our sessions. + By default, session cookies last for a month. This is probably too long for some cases, but it is easy to change this and other attributes during runtime. Sessions can be configured individually or the store can be From 81f1be5cc016add5e0b8a19c3159fbff96e07829 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Thu, 17 Oct 2013 09:55:59 -0700 Subject: [PATCH 004/121] Add MaxLength to FilesystemStore. --- store.go | 11 +++++++++++ store_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/store.go b/store.go index fe31c08..a5cb867 100644 --- a/store.go +++ b/store.go @@ -137,6 +137,17 @@ type FilesystemStore struct { path string } +// MaxLength restricts the maximum length of new sessions to l. +// If l is 0 there is no limit to the size of a session, use with caution. +// The default for a new FilesystemStore is 4096. +func (s *FilesystemStore) MaxLength(l int) { + for _, c := range s.Codecs { + if codec, ok := c.(*securecookie.SecureCookie); ok { + codec.MaxLength(l) + } + } +} + // Get returns a session for the given name after adding it to the registry. // // See CookieStore.Get(). diff --git a/store_test.go b/store_test.go index 77fea6b..022acba 100644 --- a/store_test.go +++ b/store_test.go @@ -1,7 +1,9 @@ package sessions import ( + "encoding/base64" "net/http" + "net/http/httptest" "testing" ) @@ -46,3 +48,26 @@ func TestGH8FilesystemStore(t *testing.T) { t.Fatalf("bad session path: got %q, want %q", session.Options.Path, originalPath) } } + +// Test for GH-2. +func TestGH2MaxLength(t *testing.T) { + store := NewFilesystemStore("", []byte("some key")) + req, err := http.NewRequest("GET", "http://www.example.com", nil) + if err != nil { + t.Fatal("failed to create request", err) + } + w := httptest.NewRecorder() + + session, err := store.New(req, "my session") + session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2)) + err = session.Save(req, w) + if err == nil { + t.Fatal("expected an error, got nil") + } + + store.MaxLength(4096 * 3) // A bit more than the value size to account for encoding overhead. + err = session.Save(req, w) + if err != nil { + t.Fatal("failed to Save:", err) + } +} From 43ff70ebcff1674fba9aff126178f93076b32718 Mon Sep 17 00:00:00 2001 From: Jonathan Gillham Date: Mon, 21 Oct 2013 13:35:37 +0100 Subject: [PATCH 005/121] Proposed change to Registry.Get function when CookieStore.New produces an error. --- sessions.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sessions.go b/sessions.go index 53111b3..72b6ecb 100644 --- a/sessions.go +++ b/sessions.go @@ -144,7 +144,9 @@ func (s *Registry) Get(store Store, name string) (session *Session, err error) { if info, ok := s.sessions[name]; ok { session, err = info.s, info.e } else { - session, err = store.New(s.request, name) + if session, err = store.New(s.request, name); err != nil { + return + } session.name = name s.sessions[name] = sessionInfo{s: session, e: err} } From b5d868122c68458b92558e9239d6225af9106b52 Mon Sep 17 00:00:00 2001 From: Jonathan Gillham Date: Fri, 25 Oct 2013 11:01:54 +0100 Subject: [PATCH 006/121] Revert "Proposed change to Registry.Get function when CookieStore.New produces" This reverts commit 43ff70ebcff1674fba9aff126178f93076b32718. --- sessions.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sessions.go b/sessions.go index 72b6ecb..53111b3 100644 --- a/sessions.go +++ b/sessions.go @@ -144,9 +144,7 @@ func (s *Registry) Get(store Store, name string) (session *Session, err error) { if info, ok := s.sessions[name]; ok { session, err = info.s, info.e } else { - if session, err = store.New(s.request, name); err != nil { - return - } + session, err = store.New(s.request, name) session.name = name s.sessions[name] = sessionInfo{s: session, e: err} } From cb4af09e63b4fa547369c43a1fa62c2c4ce5c2b7 Mon Sep 17 00:00:00 2001 From: Jonathan Gillham Date: Fri, 25 Oct 2013 11:15:33 +0100 Subject: [PATCH 007/121] Updated store documentation. --- store.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/store.go b/store.go index a5cb867..5b43bc8 100644 --- a/store.go +++ b/store.go @@ -16,6 +16,15 @@ import ( ) // Store is an interface for custom session stores. +// +// Get should return a cached session. +// New should create and return a new session. +// Save should persist session to the underlying store implementation. +// +// Note that New should never return a nul session, even in the case of an error +// if using the Registry infrastructure for caching of sessions in your store. +// +// See CookieStore and FilesystemStore for examples. type Store interface { Get(r *http.Request, name string) (*Session, error) New(r *http.Request, name string) (*Session, error) From 3cb09c3e9541606b194a45b02b85176b6e4635f4 Mon Sep 17 00:00:00 2001 From: Jonathan Gillham Date: Fri, 25 Oct 2013 20:28:06 +0100 Subject: [PATCH 008/121] Improved Store interface comments. --- store.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/store.go b/store.go index 5b43bc8..5eaea81 100644 --- a/store.go +++ b/store.go @@ -17,17 +17,18 @@ import ( // Store is an interface for custom session stores. // -// Get should return a cached session. -// New should create and return a new session. -// Save should persist session to the underlying store implementation. -// -// Note that New should never return a nul session, even in the case of an error -// if using the Registry infrastructure for caching of sessions in your store. -// // See CookieStore and FilesystemStore for examples. type Store interface { + // Get should return a cached session. Get(r *http.Request, name string) (*Session, error) + + // New should create and return a new session. + // + // Note that New should never return a nil session, even in the case of + // an error if using the Registry infrastructure to cache the session. New(r *http.Request, name string) (*Session, error) + + // Save should persist session to the underlying store implementation. Save(r *http.Request, w http.ResponseWriter, s *Session) error } From 1ace0650f7b7ebbf58eb835609698352efeb9cc9 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 30 Oct 2013 14:12:29 -0700 Subject: [PATCH 009/121] Add mysqlstore to README. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4dea2ab..4857acb 100644 --- a/README.md +++ b/README.md @@ -5,5 +5,6 @@ Store Implementations --------------------- Other implementations of the sessions.Store interface: - * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore/) - store sessions in Couchbase + * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - store sessions in Couchbase + * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - store sessions in MySQL * [github.com/boj/redistore](https://github.com/boj/redistore) - store sessions in Redis From ebb83c42456610126ae4eb748e895fcdb6064782 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Sat, 9 Nov 2013 09:44:54 -0800 Subject: [PATCH 010/121] Add link to gaesessions in README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4857acb..9661c66 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Store Implementations --------------------- Other implementations of the sessions.Store interface: - * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - store sessions in Couchbase - * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - store sessions in MySQL - * [github.com/boj/redistore](https://github.com/boj/redistore) - store sessions in Redis + * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase + * [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache or GAE + * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL + * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis From eca5ef69da5eb65faa4afeda2711ee462829e9cb Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Sat, 30 Nov 2013 13:26:13 -0800 Subject: [PATCH 011/121] Add travis.yml --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..70e012b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: go + +go: + - 1.0 + - 1.1 + - tip From d310efded77d581a7bb66175d4d651dd9991b9cc Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Sun, 1 Dec 2013 13:46:24 -0800 Subject: [PATCH 012/121] Fit vet nits. --- sessions_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sessions_test.go b/sessions_test.go index 3667d63..fcc69eb 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -109,7 +109,7 @@ func TestFlashes(t *testing.T) { hdr = rsp.Header() cookies, ok = hdr["Set-Cookie"] if !ok || len(cookies) != 1 { - t.Fatalf("No cookies. Header:", hdr) + t.Fatal("No cookies. Header:", hdr) } // Round 2 ---------------------------------------------------------------- @@ -168,7 +168,7 @@ func TestFlashes(t *testing.T) { hdr = rsp.Header() cookies, ok = hdr["Set-Cookie"] if !ok || len(cookies) != 1 { - t.Fatalf("No cookies. Header:", hdr) + t.Fatal("No cookies. Header:", hdr) } // Round 4 ---------------------------------------------------------------- From c9bf01c18818f7738e6b2670f1fd92f710ecb734 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 4 Dec 2013 23:16:17 -0800 Subject: [PATCH 013/121] Add go 1.2 to travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 70e012b..d87d465 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,5 @@ language: go go: - 1.0 - 1.1 + - 1.2 - tip From a8459038dc6d89fc6afb0e382f6ae0cf4f7d4f13 Mon Sep 17 00:00:00 2001 From: Mark Dain Date: Sat, 4 Jan 2014 16:40:48 +0000 Subject: [PATCH 014/121] Fixed Formatting Issue Go will refuse to compile unless the last key is on the same line as the closing brace: `non-declaration statement outside function body`. Additionally, I set the HttpOnly flag on. I think this is a good default for everybody as it increases security and Gorilla's cookies are actually encrypted so JavaScript access is unlikely to be needed. --- doc.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc.go b/doc.go index 24ba077..f9f80cb 100644 --- a/doc.go +++ b/doc.go @@ -113,9 +113,9 @@ fields are basically a subset of http.Cookie fields. Let's change the maximum age of a session to one week: session.Options = &sessions.Options{ - Path: "/", - MaxAge: 86400 * 7, - } + Path: "/", + MaxAge: 86400 * 7, + HttpOnly: true} Sometimes we may want to change authentication and/or encryption keys without breaking existing sessions. The CookieStore supports key rotation, and to use From 6e8111651587e2cf4de2570a5d3676a2397f82ea Mon Sep 17 00:00:00 2001 From: Mark Dain Date: Sun, 5 Jan 2014 10:10:22 +0000 Subject: [PATCH 015/121] Added trailing comma --- doc.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc.go b/doc.go index f9f80cb..c8eb758 100644 --- a/doc.go +++ b/doc.go @@ -115,7 +115,8 @@ maximum age of a session to one week: session.Options = &sessions.Options{ Path: "/", MaxAge: 86400 * 7, - HttpOnly: true} + HttpOnly: true, + } Sometimes we may want to change authentication and/or encryption keys without breaking existing sessions. The CookieStore supports key rotation, and to use From c1b041dc3f1a39dac3f0e6d97ee752163e776f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Lindstr=C3=B6m?= Date: Tue, 7 Jan 2014 18:43:15 +0100 Subject: [PATCH 016/121] add postgresql session store link in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9661c66..c3c1085 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,5 @@ Other implementations of the sessions.Store interface: * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase * [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache or GAE * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL + * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis From 44fb592008c2ecfbfc89725a8ae1638711dfa423 Mon Sep 17 00:00:00 2001 From: Mark Dain Date: Mon, 13 Jan 2014 00:12:44 +0000 Subject: [PATCH 017/121] Added note about memory leak (Fixes #15) This is something fairly important, it shouldn't be left out of the documentation. --- doc.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc.go b/doc.go index c8eb758..feeddd0 100644 --- a/doc.go +++ b/doc.go @@ -48,6 +48,12 @@ And finally we call session.Save() to save the session in the response. Note that in production code, we should check for errors when calling session.Save(r, w), and either display an error message or otherwise handle it. +Important Note: If you aren't using gorilla.Mux, you need to use +context.ClearHandler as your handler on ListenAndServe else you will leak memory! +The ClearHandler function is provided by the gorilla/context package. + + http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux)) + That's all you need to know for the basic usage. Let's take a look at other options, starting with flash messages. From 96a5fdda5c95c7dfbca04f1f643d82f85a7b456c Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Sun, 12 Jan 2014 21:06:25 -0800 Subject: [PATCH 018/121] Clarify docs a bit. --- doc.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc.go b/doc.go index feeddd0..7f8be22 100644 --- a/doc.go +++ b/doc.go @@ -48,12 +48,14 @@ And finally we call session.Save() to save the session in the response. Note that in production code, we should check for errors when calling session.Save(r, w), and either display an error message or otherwise handle it. -Important Note: If you aren't using gorilla.Mux, you need to use -context.ClearHandler as your handler on ListenAndServe else you will leak memory! -The ClearHandler function is provided by the gorilla/context package. +Important Note: If you aren't using gorilla/mux, you need to wrap your handlers +with context.ClearHandler as or else you will leak memory! An easy way to do this +is to wrap the top-level mux when calling http.ListenAndServe: http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux)) +The ClearHandler function is provided by the gorilla/context package. + That's all you need to know for the basic usage. Let's take a look at other options, starting with flash messages. From 9bf77df48f82ac7617808c6382fdb07ca16a1164 Mon Sep 17 00:00:00 2001 From: Brian Jones Date: Thu, 13 Feb 2014 22:36:33 +0900 Subject: [PATCH 019/121] Added RiakStore to implementation list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c3c1085..30d139a 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,4 @@ Other implementations of the sessions.Store interface: * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis + * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak From 05c7254e21c2ccc3ea0f35f3b902e762139300a5 Mon Sep 17 00:00:00 2001 From: nvcnvn Date: Wed, 5 Mar 2014 16:18:36 +0700 Subject: [PATCH 020/121] Add MongoStore to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 30d139a..2dba1d0 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,4 @@ Other implementations of the sessions.Store interface: * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak + * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB From c5bbe9d3d3c906d01de60189e5bfbba1d8164a80 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 5 Mar 2014 12:43:50 -0800 Subject: [PATCH 021/121] Update README.md Alphabetize --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2dba1d0..49122e5 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ Other implementations of the sessions.Store interface: * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase * [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache or GAE + * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak - * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB From 28f896870851935df0885fca3410114940d49e1b Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Fri, 16 May 2014 12:41:36 -0700 Subject: [PATCH 022/121] Add Memcache store implementation Fixes #28 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 49122e5..09a37a6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Store Implementations Other implementations of the sessions.Store interface: * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase - * [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache or GAE + * [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache + * [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache on GAE * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL From 43642f673fa7691e98095801179435bb3d9f84b5 Mon Sep 17 00:00:00 2001 From: Keiji Yoshida Date: Sat, 14 Jun 2014 04:04:41 +0900 Subject: [PATCH 023/121] Add github.com/yosssi/boltstore --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 09a37a6..27f1175 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,4 @@ Other implementations of the sessions.Store interface: * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak + * [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt From b5df0baf36ed4959cdfa3883153294207b288e10 Mon Sep 17 00:00:00 2001 From: Keiji Yoshida Date: Sat, 14 Jun 2014 04:27:47 +0900 Subject: [PATCH 024/121] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27f1175..a02df39 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Store Implementations --------------------- Other implementations of the sessions.Store interface: + * [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase * [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache * [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache on GAE @@ -13,4 +14,3 @@ Other implementations of the sessions.Store interface: * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak - * [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt From 4e0725cf9c768b11cc7f523a34fd2d40f96e56c6 Mon Sep 17 00:00:00 2001 From: Deniz Eren Date: Thu, 29 Jan 2015 18:17:56 +0000 Subject: [PATCH 025/121] Added DynamoStore to implementation list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a02df39..4a90fa3 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Other implementations of the sessions.Store interface: * [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase + * [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS * [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache * [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache on GAE * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB From 0f44a27391116bbb840afe7ee861d0f0d0302a1b Mon Sep 17 00:00:00 2001 From: rcadena Date: Wed, 11 Feb 2015 09:37:20 -0800 Subject: [PATCH 026/121] Added note about calling save before writing to response. Also removed calls to fmt.Fprintf in AddFlash example. If a user follows the old example then the session won't be stored. See: https://github.com/gorilla/sessions/issues/39 --- doc.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc.go b/doc.go index 7f8be22..c4012af 100644 --- a/doc.go +++ b/doc.go @@ -56,6 +56,9 @@ is to wrap the top-level mux when calling http.ListenAndServe: The ClearHandler function is provided by the gorilla/context package. +Also: Call Save before writing to the response, otherwise the session +cookie will not be sent to the client. + That's all you need to know for the basic usage. Let's take a look at other options, starting with flash messages. @@ -69,12 +72,10 @@ flashes, call session.Flashes(). Here is an example: session, _ := store.Get(r, "session-name") // Get the previously flashes, if any. if flashes := session.Flashes(); len(flashes) > 0 { - // Just print the flash values. - fmt.Fprint(w, "%v", flashes) + // Use the flash values. } else { // Set a new flash. session.AddFlash("Hello, flash messages world!") - fmt.Fprint(w, "No flashes found.") } session.Save(r, w) } From 2c775edb25fb0f91965caac0120a59ba80858c89 Mon Sep 17 00:00:00 2001 From: rcadena Date: Wed, 11 Feb 2015 09:47:56 -0800 Subject: [PATCH 027/121] Moved and tweaked text. --- doc.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc.go b/doc.go index c4012af..b56700f 100644 --- a/doc.go +++ b/doc.go @@ -48,6 +48,9 @@ And finally we call session.Save() to save the session in the response. Note that in production code, we should check for errors when calling session.Save(r, w), and either display an error message or otherwise handle it. +Save must be called before writing to the response, otherwise the session +cookie will not be sent to the client. + Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory! An easy way to do this is to wrap the top-level mux when calling http.ListenAndServe: @@ -56,9 +59,6 @@ is to wrap the top-level mux when calling http.ListenAndServe: The ClearHandler function is provided by the gorilla/context package. -Also: Call Save before writing to the response, otherwise the session -cookie will not be sent to the client. - That's all you need to know for the basic usage. Let's take a look at other options, starting with flash messages. From 39dd83d692d0cb984af851b38e89b0cd8ea302b6 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Mon, 23 Feb 2015 07:52:10 -0800 Subject: [PATCH 028/121] ensure FilesystemStore closes the file even on error. Fixes #41 --- store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store.go b/store.go index 5eaea81..b3461b0 100644 --- a/store.go +++ b/store.go @@ -222,10 +222,10 @@ func (s *FilesystemStore) save(session *Session) error { if err != nil { return err } + defer fp.Close() if _, err = fp.Write([]byte(encoded)); err != nil { return err } - fp.Close() return nil } From 2afdef550c7eed1c30b43cd48c8c9868ea66e1ed Mon Sep 17 00:00:00 2001 From: Michael Schuett Date: Sat, 28 Feb 2015 23:00:19 -0500 Subject: [PATCH 029/121] Update README.md I have created a SQLite driver which I find nice for developing locally with. It is just a slight modification to the already existing MySQL driver by srinathgs. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4a90fa3..40e44f7 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,4 @@ Other implementations of the sessions.Store interface: * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak + * [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite From 4b0af472301b1081c4821c65875c9f50824ebd3a Mon Sep 17 00:00:00 2001 From: Dmitry Chestnykh Date: Wed, 25 Mar 2015 22:35:41 +0100 Subject: [PATCH 030/121] Simplify FilesystemStore save and load. Use ioutil package to write and read files. --- store.go | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/store.go b/store.go index b3461b0..24b466f 100644 --- a/store.go +++ b/store.go @@ -6,7 +6,7 @@ package sessions import ( "encoding/base32" - "io" + "io/ioutil" "net/http" "os" "strings" @@ -218,38 +218,16 @@ func (s *FilesystemStore) save(session *Session) error { filename := s.path + "session_" + session.ID fileMutex.Lock() defer fileMutex.Unlock() - fp, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return err - } - defer fp.Close() - if _, err = fp.Write([]byte(encoded)); err != nil { - return err - } - return nil + return ioutil.WriteFile(filename, []byte(encoded), 0600) } // load reads a file and decodes its content into session.Values. func (s *FilesystemStore) load(session *Session) error { filename := s.path + "session_" + session.ID - fp, err := os.OpenFile(filename, os.O_RDONLY, 0400) + fdata, err := ioutil.ReadFile(filename) if err != nil { return err } - defer fp.Close() - var fdata []byte - buf := make([]byte, 128) - for { - var n int - n, err = fp.Read(buf[0:]) - fdata = append(fdata, buf[0:n]...) - if err != nil { - if err == io.EOF { - break - } - return err - } - } if err = securecookie.DecodeMulti(session.Name(), string(fdata), &session.Values, s.Codecs...); err != nil { return err From 9a1a995303506329f3fc682912d3baa7b937a724 Mon Sep 17 00:00:00 2001 From: Dmitry Chestnykh Date: Wed, 25 Mar 2015 22:40:48 +0100 Subject: [PATCH 031/121] Add missing fileMutex read locking. --- store.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/store.go b/store.go index 24b466f..13cd1db 100644 --- a/store.go +++ b/store.go @@ -224,6 +224,8 @@ func (s *FilesystemStore) save(session *Session) error { // load reads a file and decodes its content into session.Values. func (s *FilesystemStore) load(session *Session) error { filename := s.path + "session_" + session.ID + fileMutex.RLock() + defer fileMutex.RUnlock() fdata, err := ioutil.ReadFile(filename) if err != nil { return err From c9c3e432961bc1035d670dcf24f0c8763407e7f4 Mon Sep 17 00:00:00 2001 From: Brian Jones Date: Wed, 1 Apr 2015 18:20:06 +0900 Subject: [PATCH 032/121] rethinkstore link --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 40e44f7..5898bad 100644 --- a/README.md +++ b/README.md @@ -14,5 +14,6 @@ Other implementations of the sessions.Store interface: * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis + * [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak * [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite From 14656fba266a325f977b9473fe8d2cf176944674 Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Fri, 10 Apr 2015 10:44:37 +0300 Subject: [PATCH 033/121] Use correct path separator on Windows. --- store.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/store.go b/store.go index 13cd1db..2c0257f 100644 --- a/store.go +++ b/store.go @@ -9,6 +9,7 @@ import ( "io/ioutil" "net/http" "os" + "path/filepath" "strings" "sync" @@ -123,9 +124,6 @@ func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore { if path == "" { path = os.TempDir() } - if path[len(path)-1] != '/' { - path += "/" - } return &FilesystemStore{ Codecs: securecookie.CodecsFromPairs(keyPairs...), Options: &Options{ @@ -215,7 +213,7 @@ func (s *FilesystemStore) save(session *Session) error { if err != nil { return err } - filename := s.path + "session_" + session.ID + filename := filepath.Join(s.path, "session_"+session.ID) fileMutex.Lock() defer fileMutex.Unlock() return ioutil.WriteFile(filename, []byte(encoded), 0600) @@ -223,7 +221,7 @@ func (s *FilesystemStore) save(session *Session) error { // load reads a file and decodes its content into session.Values. func (s *FilesystemStore) load(session *Session) error { - filename := s.path + "session_" + session.ID + filename := filepath.Join(s.path, "session_"+session.ID) fileMutex.RLock() defer fileMutex.RUnlock() fdata, err := ioutil.ReadFile(filename) From 93a532e5acf5d76ade4f3d1ed78833edf7ecc2bf Mon Sep 17 00:00:00 2001 From: Jerry Saravia Date: Fri, 17 Apr 2015 10:02:49 -0400 Subject: [PATCH 034/121] Added arangodb session store --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5898bad..1ffa2cf 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Store Implementations --------------------- Other implementations of the sessions.Store interface: + * [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB * [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase * [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS From 286213d1c4d3c5f7ca906279577b126e58fb933c Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Thu, 6 Aug 2015 15:36:25 +0800 Subject: [PATCH 035/121] Fleshed out README with example from doc.go + build/godoc badges. --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1ffa2cf..34a8c11 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,56 @@ sessions ======== +[![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.png?branch=master)](https://travis-ci.org/gorilla/sessions) + +gorilla/sessions provides cookie and filesystem sessions and infrastructure for +custom session backends. + +The key features are: + +* Simple API: use it as an easy way to set signed (and optionally + encrypted) cookies. +* Built-in backends to store sessions in cookies or the filesystem. +* Flash messages: session values that last until read. +* Convenient way to switch session persistency (aka "remember me") and set + other attributes. +* Mechanism to rotate authentication and encryption keys. +* Multiple sessions per request, even using different backends. +* Interfaces and infrastructure for custom session backends: sessions from + different stores can be retrieved and batch-saved using a common API. + +Let's start with an example that shows the sessions API in a nutshell: + +```go + import ( + "net/http" + "github.com/gorilla/sessions" + ) + + var store = sessions.NewCookieStore([]byte("something-very-secret")) + + func MyHandler(w http.ResponseWriter, r *http.Request) { + // Get a session. We're ignoring the error resulted from decoding an + // existing session: Get() always returns a session, even if empty. + session, _ := store.Get(r, "session-name") + // Set some session values. + session.Values["foo"] = "bar" + session.Values[42] = 43 + // Save it. + session.Save(r, w) + } +``` + +First we initialize a session store calling NewCookieStore() and passing a +secret key used to authenticate the session. Inside the handler, we call +store.Get() to retrieve an existing session or a new one. Then we set some +session values in session.Values, which is a map[interface{}]interface{}. +And finally we call session.Save() to save the session in the response. + +More examples are available [on the Gorilla +website](http://www.gorillatoolkit.org/pkg/sessions). + +## Store Implementations -Store Implementations ---------------------- Other implementations of the sessions.Store interface: * [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB @@ -18,3 +66,7 @@ Other implementations of the sessions.Store interface: * [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak * [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite + + ## License + + BSD licensed. See the LICENSE file for details. From 460e18584b819b2ffc92706c71e9c4791aba65ea Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Thu, 6 Aug 2015 15:39:54 +0800 Subject: [PATCH 036/121] Improved docs re: calling Save before writing/returning. --- README.md | 2 +- doc.go | 2 +- sessions.go | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 34a8c11..9f2a2ac 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Let's start with an example that shows the sessions API in a nutshell: // Set some session values. session.Values["foo"] = "bar" session.Values[42] = 43 - // Save it. + // Save it before we write to the response/return from the handler. session.Save(r, w) } ``` diff --git a/doc.go b/doc.go index 7f8be22..7c09f8b 100644 --- a/doc.go +++ b/doc.go @@ -35,7 +35,7 @@ Let's start with an example that shows the sessions API in a nutshell: // Set some session values. session.Values["foo"] = "bar" session.Values[42] = 43 - // Save it. + // Save it before we write to the response/return from the handler. session.Save(r, w) } diff --git a/sessions.go b/sessions.go index 53111b3..d6bfb6e 100644 --- a/sessions.go +++ b/sessions.go @@ -88,7 +88,8 @@ func (s *Session) AddFlash(value interface{}, vars ...string) { } // Save is a convenience method to save this session. It is the same as calling -// store.Save(request, response, session) +// store.Save(request, response, session). You should call Save before writing to +// the response or returning from the handler. func (s *Session) Save(r *http.Request, w http.ResponseWriter) error { return s.store.Save(r, w, s) } From 67c3cbe3ac0f28c1688322d3942e68bf84061ac4 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Sat, 8 Aug 2015 20:04:39 +0800 Subject: [PATCH 037/121] Added example of retrieving from a session. --- doc.go | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/doc.go b/doc.go index 7c09f8b..c5a3274 100644 --- a/doc.go +++ b/doc.go @@ -31,7 +31,12 @@ Let's start with an example that shows the sessions API in a nutshell: func MyHandler(w http.ResponseWriter, r *http.Request) { // Get a session. We're ignoring the error resulted from decoding an // existing session: Get() always returns a session, even if empty. - session, _ := store.Get(r, "session-name") + session, err := store.Get(r, "session-name") + if err != nil { + http.Error(w, err.Error(), 500) + return + } + // Set some session values. session.Values["foo"] = "bar" session.Values[42] = 43 @@ -66,7 +71,12 @@ flashes, call session.Flashes(). Here is an example: func MyHandler(w http.ResponseWriter, r *http.Request) { // Get a session. - session, _ := store.Get(r, "session-name") + session, err := store.Get(r, "session-name") + if err != nil { + http.Error(w, err.Error(), 500) + return + } + // Get the previously flashes, if any. if flashes := session.Flashes(); len(flashes) > 0 { // Just print the flash values. @@ -112,6 +122,26 @@ above we've passed it a pointer to a struct and a pointer to a custom type representing a map[string]interface. This will then allow us to serialise/deserialise values of those types to and from our sessions. +Note that because session values are stored in a map[string]interface{}, there's +a need to type-assert data when retrieving it. We'll use the Person struct we registered above: + + func MyHandler(w http.ResponseWriter, r *http.Request) { + session, err := store.Get(r, "session-name") + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + // Retrieve our struct and type-assert it + val := session.Values["person"] + var person &Person{} + if person, ok := val.(*Person); !ok { + // Handle the case that it's not an expected type + } + + // Now we can use our person object + } + By default, session cookies last for a month. This is probably too long for some cases, but it is easy to change this and other attributes during runtime. Sessions can be configured individually or the store can be From ab250e0cde98b2d4030f0ac1879e7394c2e416a0 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Tue, 11 Aug 2015 20:01:05 +0800 Subject: [PATCH 038/121] Provides functionality to set the MaxAge on the underlying securecookie Codecs. - Addresses https://github.com/gorilla/sessions/issues/48 --- store.go | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/store.go b/store.go index 2c0257f..77ab0bf 100644 --- a/store.go +++ b/store.go @@ -51,13 +51,16 @@ type Store interface { // Use the convenience function securecookie.GenerateRandomKey() to create // strong keys. func NewCookieStore(keyPairs ...[]byte) *CookieStore { - return &CookieStore{ + cs := &CookieStore{ Codecs: securecookie.CodecsFromPairs(keyPairs...), Options: &Options{ Path: "/", MaxAge: 86400 * 30, }, } + + cs.MaxAge(cs.Options.MaxAge) + return cs } // CookieStore stores sessions using secure cookies. @@ -110,6 +113,20 @@ func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter, return nil } +// MaxAge sets the maximum age for the store and the underlying cookie +// implementation. Individual sessions can be deleted by setting Options.MaxAge +// = -1 for that session. +func (s *CookieStore) MaxAge(age int) { + s.Options.MaxAge = age + + // Set the maxAge for each securecookie instance. + for _, codec := range s.Codecs { + if sc, ok := codec.(*securecookie.SecureCookie); ok { + sc.MaxAge(age) + } + } +} + // FilesystemStore ------------------------------------------------------------ var fileMutex sync.RWMutex @@ -124,7 +141,7 @@ func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore { if path == "" { path = os.TempDir() } - return &FilesystemStore{ + fs := &FilesystemStore{ Codecs: securecookie.CodecsFromPairs(keyPairs...), Options: &Options{ Path: "/", @@ -132,6 +149,9 @@ func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore { }, path: path, } + + fs.MaxAge(fs.Options.MaxAge) + return fs } // FilesystemStore stores sessions in the filesystem. @@ -206,6 +226,20 @@ func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, return nil } +// MaxAge sets the maximum age for the store and the underlying cookie +// implementation. Individual sessions can be deleted by setting Options.MaxAge +// = -1 for that session. +func (s *FilesystemStore) MaxAge(age int) { + s.Options.MaxAge = age + + // Set the maxAge for each securecookie instance. + for _, codec := range s.Codecs { + if sc, ok := codec.(*securecookie.SecureCookie); ok { + sc.MaxAge(age) + } + } +} + // save writes encoded session.Values to a file. func (s *FilesystemStore) save(session *Session) error { encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, From c739570bf8879b63b55c74cd976a1130ae93dfa4 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Tue, 11 Aug 2015 20:09:55 +0800 Subject: [PATCH 039/121] Fixed typo in doc string. --- store.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/store.go b/store.go index 77ab0bf..e037e05 100644 --- a/store.go +++ b/store.go @@ -114,7 +114,7 @@ func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter, } // MaxAge sets the maximum age for the store and the underlying cookie -// implementation. Individual sessions can be deleted by setting Options.MaxAge +// implementation. Individual sessions can be deleted by setting Options.MaxAge // = -1 for that session. func (s *CookieStore) MaxAge(age int) { s.Options.MaxAge = age @@ -227,7 +227,7 @@ func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, } // MaxAge sets the maximum age for the store and the underlying cookie -// implementation. Individual sessions can be deleted by setting Options.MaxAge +// implementation. Individual sessions can be deleted by setting Options.MaxAge // = -1 for that session. func (s *FilesystemStore) MaxAge(age int) { s.Options.MaxAge = age From b6bcae186ac3099dba8fafce125dc930393f1e5e Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Wed, 19 Aug 2015 22:17:40 -0700 Subject: [PATCH 040/121] Update .travis.yml --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d87d465..f983b60 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: go +sudo: false go: - - 1.0 - - 1.1 - - 1.2 + - 1.3 + - 1.4 + - 1.5 - tip From 28bc6170a1cd767d14d2f4728cc6f8e2876e4d1f Mon Sep 17 00:00:00 2001 From: Mirco Zeiss Date: Fri, 2 Oct 2015 15:41:35 +0200 Subject: [PATCH 041/121] fix syntax error in docs for complex datatype --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 0da92a3..b03975f 100644 --- a/doc.go +++ b/doc.go @@ -135,7 +135,7 @@ a need to type-assert data when retrieving it. We'll use the Person struct we re // Retrieve our struct and type-assert it val := session.Values["person"] - var person &Person{} + var person = &Person{} if person, ok := val.(*Person); !ok { // Handle the case that it's not an expected type } From e62120438891836325329d6aa35c45680121ac2e Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Wed, 23 Dec 2015 07:57:00 +0800 Subject: [PATCH 042/121] [docs] Added mention of context.ClearHandler as per #59; formatting fixes. --- README.md | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9f2a2ac..c18798d 100644 --- a/README.md +++ b/README.md @@ -40,33 +40,39 @@ Let's start with an example that shows the sessions API in a nutshell: } ``` -First we initialize a session store calling NewCookieStore() and passing a +First we initialize a session store calling `NewCookieStore()` and passing a secret key used to authenticate the session. Inside the handler, we call -store.Get() to retrieve an existing session or a new one. Then we set some -session values in session.Values, which is a map[interface{}]interface{}. -And finally we call session.Save() to save the session in the response. +`store.Get()` to retrieve an existing session or a new one. Then we set some +session values in session.Values, which is a `map[interface{}]interface{}`. +And finally we call `session.Save()` to save the session in the response. + +Important Note: If you aren't using gorilla/mux, you need to wrap your handlers +with +[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler) +as or else you will leak memory! An easy way to do this is to wrap the top-level +mux when calling http.ListenAndServe: More examples are available [on the Gorilla website](http://www.gorillatoolkit.org/pkg/sessions). ## Store Implementations -Other implementations of the sessions.Store interface: +Other implementations of the `sessions.Store` interface: - * [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB - * [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt - * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase - * [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS - * [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache - * [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache on GAE - * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB - * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL - * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL - * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis - * [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB - * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak - * [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite +* [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB +* [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt +* [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase +* [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS +* [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache +* [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache on GAE +* [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB +* [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL +* [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL +* [github.com/boj/redistore](https://github.com/boj/redistore) - Redis +* [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB +* [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak +* [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite - ## License +## License - BSD licensed. See the LICENSE file for details. +BSD licensed. See the LICENSE file for details. From 59224c658b3432097bde66afb311b7c7175aed85 Mon Sep 17 00:00:00 2001 From: Mattias Wadman Date: Thu, 7 Jan 2016 00:28:49 +0100 Subject: [PATCH 043/121] Add https://github.com/wader/gormstore --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c18798d..2779904 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Other implementations of the `sessions.Store` interface: * [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak * [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite +* [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite) ## License From 5c34ad53e5366cb4d56292fdc6de1e9272828389 Mon Sep 17 00:00:00 2001 From: Shawn Smith Date: Thu, 21 Jan 2016 17:44:12 -0800 Subject: [PATCH 044/121] typo --- store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store.go b/store.go index e037e05..ba3b9e9 100644 --- a/store.go +++ b/store.go @@ -156,7 +156,7 @@ func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore { // FilesystemStore stores sessions in the filesystem. // -// It also serves as a referece for custom stores. +// It also serves as a reference for custom stores. // // This store is still experimental and not well tested. Feedback is welcome. type FilesystemStore struct { From eaa49efcba27548333e477b7da872da1afb98a58 Mon Sep 17 00:00:00 2001 From: kliron Date: Fri, 26 Feb 2016 20:30:07 +0100 Subject: [PATCH 045/121] store.Get() returns error if cookie name contains invalid characters --- lex.go | 105 +++++++++++++++++++++++++++++++++++++++++++++++ sessions.go | 4 ++ sessions_test.go | 4 ++ 3 files changed, 113 insertions(+) create mode 100644 lex.go diff --git a/lex.go b/lex.go new file mode 100644 index 0000000..269dd10 --- /dev/null +++ b/lex.go @@ -0,0 +1,105 @@ +// This file contains code adapted from the Go standard library +// https://github.com/golang/go/blob/39ad0fd0789872f9469167be7fe9578625ff246e/src/net/http/lex.go + +package sessions +import "strings" + + +var isTokenTable = [127]bool{ + '!': true, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '*': true, + '+': true, + '-': true, + '.': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'W': true, + 'V': true, + 'X': true, + 'Y': true, + 'Z': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '|': true, + '~': true, +} + + +func isToken(r rune) bool { + i := int(r) + return i < len(isTokenTable) && isTokenTable[i] +} + + +func isNotToken(r rune) bool { + return !isToken(r) +} + + +func isCookieNameValid(raw string) bool { + if raw == "" { + return false + } + return strings.IndexFunc(raw, isNotToken) < 0 +} diff --git a/sessions.go b/sessions.go index d6bfb6e..bfff1dc 100644 --- a/sessions.go +++ b/sessions.go @@ -11,6 +11,7 @@ import ( "time" "github.com/gorilla/context" + "errors" ) // Default flashes key. @@ -142,6 +143,9 @@ type Registry struct { // // It returns a new session if there are no sessions registered for the name. func (s *Registry) Get(store Store, name string) (session *Session, err error) { + if !isCookieNameValid(name) { + return nil, errors.New(fmt.Sprintf("invalid character in cookie name: %s", name)) + } if info, ok := s.sessions[name]; ok { session, err = info.s, info.e } else { diff --git a/sessions_test.go b/sessions_test.go index fcc69eb..a22e5d1 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -112,6 +112,10 @@ func TestFlashes(t *testing.T) { t.Fatal("No cookies. Header:", hdr) } + if _, err = store.Get(req, "session:key"); err.Error() != "invalid character in cookie name: session:key" { + t.Fatalf("Expected error due to invalid cookie name") + } + // Round 2 ---------------------------------------------------------------- req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) From 1ca3b496ce317563513cbd2e61eea99c2127c1e6 Mon Sep 17 00:00:00 2001 From: kliron Date: Fri, 26 Feb 2016 20:48:57 +0100 Subject: [PATCH 046/121] Using fmt.Errorf --- .gitignore | 27 +++++++++++++++++++++++++++ sessions.go | 3 +-- sessions.iml | 10 ++++++++++ sessions_test.go | 2 +- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 sessions.iml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f46d10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +### Go template +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +# Created by .ignore support plugin (hsz.mobi) diff --git a/sessions.go b/sessions.go index bfff1dc..a837c08 100644 --- a/sessions.go +++ b/sessions.go @@ -11,7 +11,6 @@ import ( "time" "github.com/gorilla/context" - "errors" ) // Default flashes key. @@ -144,7 +143,7 @@ type Registry struct { // It returns a new session if there are no sessions registered for the name. func (s *Registry) Get(store Store, name string) (session *Session, err error) { if !isCookieNameValid(name) { - return nil, errors.New(fmt.Sprintf("invalid character in cookie name: %s", name)) + return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name) } if info, ok := s.sessions[name]; ok { session, err = info.s, info.e diff --git a/sessions.iml b/sessions.iml new file mode 100644 index 0000000..e5e18e9 --- /dev/null +++ b/sessions.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/sessions_test.go b/sessions_test.go index a22e5d1..a84fb68 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -112,7 +112,7 @@ func TestFlashes(t *testing.T) { t.Fatal("No cookies. Header:", hdr) } - if _, err = store.Get(req, "session:key"); err.Error() != "invalid character in cookie name: session:key" { + if _, err = store.Get(req, "session:key"); err.Error() != "sessions: invalid character in cookie name: session:key" { t.Fatalf("Expected error due to invalid cookie name") } From e1724dc852b0ae28edf30adff9a237692345e340 Mon Sep 17 00:00:00 2001 From: kliron Date: Fri, 26 Feb 2016 20:57:55 +0100 Subject: [PATCH 047/121] Removed .iml file, fixed .gitignore --- .gitignore | 2 ++ sessions.iml | 10 ---------- 2 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 sessions.iml diff --git a/.gitignore b/.gitignore index 5f46d10..8d70a35 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ _testmain.go *.prof # Created by .ignore support plugin (hsz.mobi) +*.iml +.gitignore \ No newline at end of file diff --git a/sessions.iml b/sessions.iml deleted file mode 100644 index e5e18e9..0000000 --- a/sessions.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file From f95b0da202c16aa9fb9c3502fea3c9950b48f64d Mon Sep 17 00:00:00 2001 From: kliron Date: Fri, 26 Feb 2016 20:58:59 +0100 Subject: [PATCH 048/121] Removed .iml file, fixed .gitignore --- .gitignore | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 8d70a35..0000000 --- a/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -### Go template -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -# Created by .ignore support plugin (hsz.mobi) -*.iml -.gitignore \ No newline at end of file From 08065b2c88689cb8ac9055a8729fcc7e144e4081 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 26 Feb 2016 13:34:33 -0800 Subject: [PATCH 049/121] [refactor] gofmt on lex.go to fix import block. --- lex.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lex.go b/lex.go index 269dd10..4bbbe10 100644 --- a/lex.go +++ b/lex.go @@ -2,8 +2,8 @@ // https://github.com/golang/go/blob/39ad0fd0789872f9469167be7fe9578625ff246e/src/net/http/lex.go package sessions -import "strings" +import "strings" var isTokenTable = [127]bool{ '!': true, @@ -85,18 +85,15 @@ var isTokenTable = [127]bool{ '~': true, } - func isToken(r rune) bool { i := int(r) return i < len(isTokenTable) && isTokenTable[i] } - func isNotToken(r rune) bool { return !isToken(r) } - func isCookieNameValid(raw string) bool { if raw == "" { return false From a1216e0aed834aa9074063dd4085f5c3df8d1fab Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Sun, 10 Apr 2016 09:15:33 -0700 Subject: [PATCH 050/121] Use http.StatusInternalServerError instead of 500 (#74) [docs] Use http.StatusInternalServerError instead of 500 (#74) --- doc.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc.go b/doc.go index b03975f..0eea22c 100644 --- a/doc.go +++ b/doc.go @@ -33,7 +33,7 @@ Let's start with an example that shows the sessions API in a nutshell: // existing session: Get() always returns a session, even if empty. session, err := store.Get(r, "session-name") if err != nil { - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -76,7 +76,7 @@ flashes, call session.Flashes(). Here is an example: // Get a session. session, err := store.Get(r, "session-name") if err != nil { - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -129,7 +129,7 @@ a need to type-assert data when retrieving it. We'll use the Person struct we re func MyHandler(w http.ResponseWriter, r *http.Request) { session, err := store.Get(r, "session-name") if err != nil { - http.Error(w, err.Error(), 500) + http.Error(w, err.Error(), http.StatusInternalServerError) return } From a4e6dd28f9e9274729417506b9bd0a6c3edb91ce Mon Sep 17 00:00:00 2001 From: Justin Hellings Date: Wed, 18 May 2016 16:17:58 +0100 Subject: [PATCH 051/121] Clarified use of gob.Register() (#77) Clarified use of gob.Register() Proposing docs change after posting this support request: https://groups.google.com/forum/#!topic/gorilla-web/cPcbiqFC3GU --- doc.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc.go b/doc.go index 0eea22c..af8c89a 100644 --- a/doc.go +++ b/doc.go @@ -118,10 +118,10 @@ so it is easy to register new datatypes for storage in sessions: } As it's not possible to pass a raw type as a parameter to a function, gob.Register() -relies on us passing it an empty pointer to the type as a parameter. In the example -above we've passed it a pointer to a struct and a pointer to a custom type -representing a map[string]interface. This will then allow us to serialise/deserialise -values of those types to and from our sessions. +relies on us passing it a value of the desired type. In the example above we've passed +it a pointer to a struct and a pointer to a custom type representing a +map[string]interface. (We could have passed non-pointer values if we wished.) This will +then allow us to serialise/deserialise values of those types to and from our sessions. Note that because session values are stored in a map[string]interface{}, there's a need to type-assert data when retrieving it. We'll use the Person struct we registered above: From 867fb4d3daf83d6f7db1462e5f979a720d5fa38b Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Sat, 4 Jun 2016 09:41:22 -0700 Subject: [PATCH 052/121] [docs] Document Session.ID more clearly (#63) (#81) --- sessions.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sessions.go b/sessions.go index a837c08..fe0d2bc 100644 --- a/sessions.go +++ b/sessions.go @@ -45,7 +45,10 @@ func NewSession(store Store, name string) *Session { // Session stores the values and optional configuration for a session. type Session struct { - ID string + // The ID of the session, generated by stores. It should not be used for + // user data. + ID string + // Values contains the user-data for the session. Values map[interface{}]interface{} Options *Options IsNew bool From 56ba4b0a11da87516629a57408a5f7e4c8ea7b0b Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Sat, 4 Jun 2016 09:54:08 -0700 Subject: [PATCH 053/121] [ci] Update .travis.yml. (#82) --- .travis.yml | 23 ++++++++++++++++++----- doc.go | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index f983b60..db5e560 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,21 @@ language: go sudo: false -go: - - 1.3 - - 1.4 - - 1.5 - - tip +matrix: + include: + - go: 1.3 + - go: 1.4 + - go: 1.5 + - go: 1.6 + - go: tip + allow_failures: + - go: tip + +install: + - # skip + +script: + - go get -t -v ./... + - diff -u <(echo -n) <(gofmt -d .) + - go vet $(go list ./... | grep -v /vendor/) + - go test -v -race ./... diff --git a/doc.go b/doc.go index af8c89a..668e05e 100644 --- a/doc.go +++ b/doc.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. /* -Package gorilla/sessions provides cookie and filesystem sessions and +Package sessions provides cookie and filesystem sessions and infrastructure for custom session backends. The key features are: From 1bbba13ba476e132b5a0739b18f1653c3e57d3c1 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Tue, 16 Aug 2016 11:50:42 -0700 Subject: [PATCH 054/121] travis.yml: add go1.7 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index db5e560..db17dd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ matrix: - go: 1.4 - go: 1.5 - go: 1.6 + - go: 1.7 - go: tip allow_failures: - go: tip From 478cbfc73df023ca675fe89ea13a72b026fcced6 Mon Sep 17 00:00:00 2001 From: Dustin Oprea Date: Fri, 26 Aug 2016 09:13:12 -0400 Subject: [PATCH 055/121] Added goappenginesessioncascade to list of compatible stores. (#90) - Removed unmaintained AppEngine store. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2779904..12f6118 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Other implementations of the `sessions.Store` interface: * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase * [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS * [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache -* [github.com/hnakamur/gaesessions](https://github.com/hnakamur/gaesessions) - Memcache on GAE +* [github.com/dsoprea/goappenginesessioncascade](https://github.com/dsoprea/goappenginesessioncascade) - Memcache/Datastore/Context in AppEngine * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL From 7ab2742f1e374be6675e9a4eca54fe529221ae3a Mon Sep 17 00:00:00 2001 From: Tortuoise Date: Thu, 1 Sep 2016 07:18:54 +0100 Subject: [PATCH 056/121] Use httptest.ResponseRecorder (#92) * use net/http/httptest * go fmt --- sessions_test.go | 49 ++++-------------------------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/sessions_test.go b/sessions_test.go index a84fb68..c166b05 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -8,28 +8,13 @@ import ( "bytes" "encoding/gob" "net/http" + "net/http/httptest" "testing" ) -// ---------------------------------------------------------------------------- -// ResponseRecorder -// ---------------------------------------------------------------------------- -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// ResponseRecorder is an implementation of http.ResponseWriter that -// records its mutations for later inspection in tests. -type ResponseRecorder struct { - Code int // the HTTP response code from WriteHeader - HeaderMap http.Header // the HTTP response headers - Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to - Flushed bool -} - // NewRecorder returns an initialized ResponseRecorder. -func NewRecorder() *ResponseRecorder { - return &ResponseRecorder{ +func NewRecorder() *httptest.ResponseRecorder { + return &httptest.ResponseRecorder{ HeaderMap: make(http.Header), Body: new(bytes.Buffer), } @@ -39,32 +24,6 @@ func NewRecorder() *ResponseRecorder { // an explicit DefaultRemoteAddr isn't set on ResponseRecorder. const DefaultRemoteAddr = "1.2.3.4" -// Header returns the response headers. -func (rw *ResponseRecorder) Header() http.Header { - return rw.HeaderMap -} - -// Write always succeeds and writes to rw.Body, if not nil. -func (rw *ResponseRecorder) Write(buf []byte) (int, error) { - if rw.Body != nil { - rw.Body.Write(buf) - } - if rw.Code == 0 { - rw.Code = http.StatusOK - } - return len(buf), nil -} - -// WriteHeader sets rw.Code. -func (rw *ResponseRecorder) WriteHeader(code int) { - rw.Code = code -} - -// Flush sets rw.Flushed to true. -func (rw *ResponseRecorder) Flush() { - rw.Flushed = true -} - // ---------------------------------------------------------------------------- type FlashMessage struct { @@ -74,7 +33,7 @@ type FlashMessage struct { func TestFlashes(t *testing.T) { var req *http.Request - var rsp *ResponseRecorder + var rsp *httptest.ResponseRecorder var hdr http.Header var err error var ok bool From 57a8d1b542398b71f1aa1b06626467fa73c2ddc3 Mon Sep 17 00:00:00 2001 From: pappz Date: Sat, 17 Sep 2016 19:22:27 +0200 Subject: [PATCH 057/121] [feat] Delete store file when cookies max-age <= 0 (#93) * Delete store file in case if cookies max-age < 0 * Improve the file path definition in erase function * Protect the session file with mutex in delete func * Delete filesystem session if max-age is <= 0 * Add tests for filesystem store delete function * Extend the doc with the file session deletion. * format source code in store_test.go --- store.go | 25 +++++++++++++++++++++++++ store_test.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/store.go b/store.go index ba3b9e9..4ff6b6c 100644 --- a/store.go +++ b/store.go @@ -205,8 +205,22 @@ func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) { } // Save adds a single session to the response. +// +// If the Options.MaxAge of the session is <= 0 then the session file will be +// deleted from the store path. With this process it enforces the properly +// session cookie handling so no need to trust in the cookie management in the +// web browser. func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, session *Session) error { + // Delete if max-age is <= 0 + if session.Options.MaxAge <= 0 { + if err := s.erase(session); err != nil { + return err + } + http.SetCookie(w, NewCookie(session.Name(), "", session.Options)) + return nil + } + if session.ID == "" { // Because the ID is used in the filename, encode it to // use alphanumeric characters only. @@ -268,3 +282,14 @@ func (s *FilesystemStore) load(session *Session) error { } return nil } + +// delete session file +func (s *FilesystemStore) erase(session *Session) error { + filename := filepath.Join(s.path, "session_"+session.ID) + + fileMutex.RLock() + defer fileMutex.RUnlock() + + err := os.Remove(filename) + return err +} diff --git a/store_test.go b/store_test.go index 022acba..bfc53fa 100644 --- a/store_test.go +++ b/store_test.go @@ -71,3 +71,55 @@ func TestGH2MaxLength(t *testing.T) { t.Fatal("failed to Save:", err) } } + +// Test delete filesystem store with max-age: -1 +func TestGH8FilesystemStoreDelete(t *testing.T) { + store := NewFilesystemStore("", []byte("some key")) + req, err := http.NewRequest("GET", "http://www.example.com", nil) + if err != nil { + t.Fatal("failed to create request", err) + } + w := httptest.NewRecorder() + + session, err := store.New(req, "hello") + if err != nil { + t.Fatal("failed to create session", err) + } + + err = session.Save(req, w) + if err != nil { + t.Fatal("failed to save session", err) + } + + session.Options.MaxAge = -1 + err = session.Save(req, w) + if err != nil { + t.Fatal("failed to delete session", err) + } +} + +// Test delete filesystem store with max-age: 0 +func TestGH8FilesystemStoreDelete2(t *testing.T) { + store := NewFilesystemStore("", []byte("some key")) + req, err := http.NewRequest("GET", "http://www.example.com", nil) + if err != nil { + t.Fatal("failed to create request", err) + } + w := httptest.NewRecorder() + + session, err := store.New(req, "hello") + if err != nil { + t.Fatal("failed to create session", err) + } + + err = session.Save(req, w) + if err != nil { + t.Fatal("failed to save session", err) + } + + session.Options.MaxAge = 0 + err = session.Save(req, w) + if err != nil { + t.Fatal("failed to delete session", err) + } +} From ca9ada44574153444b00d3fd9c8559e4cc95f896 Mon Sep 17 00:00:00 2001 From: Dustin Oprea Date: Thu, 22 Sep 2016 10:58:04 -0400 Subject: [PATCH 058/121] [docs] Updated sessioncascade project URL. (#94) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12f6118..65e5e1b 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Other implementations of the `sessions.Store` interface: * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase * [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS * [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache -* [github.com/dsoprea/goappenginesessioncascade](https://github.com/dsoprea/goappenginesessioncascade) - Memcache/Datastore/Context in AppEngine +* [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL From 52389059572b4736b2b7d899b0242ecc449c8779 Mon Sep 17 00:00:00 2001 From: enumappstore Date: Tue, 29 Nov 2016 03:20:17 +0800 Subject: [PATCH 059/121] [docs] Add Store Implementations Project Link - MySQL Cluster (#101) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 65e5e1b..7ec1497 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Other implementations of the `sessions.Store` interface: * [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB * [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL +* [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster * [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL * [github.com/boj/redistore](https://github.com/boj/redistore) - Redis * [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB From 83c8db3bdc9be789e57e3756ffbcffd2d7d40176 Mon Sep 17 00:00:00 2001 From: Geofrey Ernest Date: Thu, 22 Dec 2016 04:33:42 +0300 Subject: [PATCH 060/121] Add link to qlstore (#102) This updates the README to add a `sessions.Store` implementation based on https://github.com/cznic/ql database --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7ec1497..5bb3107 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ Other implementations of the `sessions.Store` interface: * [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak * [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite * [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite) +* [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql ## License From 697f2d505ac4cbd7f453d5fc285582e2e48259e7 Mon Sep 17 00:00:00 2001 From: Vitor De Mario Date: Mon, 20 Feb 2017 17:39:20 -0300 Subject: [PATCH 061/121] Add missing example to README (#106) --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 5bb3107..1687f71 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,12 @@ with as or else you will leak memory! An easy way to do this is to wrap the top-level mux when calling http.ListenAndServe: +```go + http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux)) +``` + +The ClearHandler function is provided by the gorilla/context package. + More examples are available [on the Gorilla website](http://www.gorillatoolkit.org/pkg/sessions). From 803ac3201723cc018469e5db5dc341b501dd8b21 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Fri, 24 Feb 2017 11:39:18 -0800 Subject: [PATCH 062/121] README.md: Add sourcegraph badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1687f71..aeca2ad 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ sessions ======== [![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.png?branch=master)](https://travis-ci.org/gorilla/sessions) +[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) + gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. From ba78acc856fe7c79891d516131b3d903cc8d9367 Mon Sep 17 00:00:00 2001 From: Matt Ho Date: Mon, 6 Mar 2017 16:35:30 -0800 Subject: [PATCH 063/121] add dynamodb library using official aws libraries (#107) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aeca2ad..ebc60d0 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Other implementations of the `sessions.Store` interface: * [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt * [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase * [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS +* [github.com/savaki/dynastore](https://github.com/savaki/dynastore) - DynamoDB on AWS (Official AWS library) * [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache * [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine * [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB From 8b6b4cd75f07f7ee036eb37b8127bd40ab1efc49 Mon Sep 17 00:00:00 2001 From: Collin Stedman Date: Sat, 29 Apr 2017 15:22:41 -0400 Subject: [PATCH 064/121] Revise outdated comment (#113) I don't think this comment was very clear in the first place, but it seems irrelevant now because the error isn't being ignored anymore. --- doc.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc.go b/doc.go index 668e05e..591d932 100644 --- a/doc.go +++ b/doc.go @@ -29,8 +29,7 @@ Let's start with an example that shows the sessions API in a nutshell: var store = sessions.NewCookieStore([]byte("something-very-secret")) func MyHandler(w http.ResponseWriter, r *http.Request) { - // Get a session. We're ignoring the error resulted from decoding an - // existing session: Get() always returns a session, even if empty. + // Get a session. Get() always returns a session, even if empty. session, err := store.Get(r, "session-name") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) From b61c93cb7f67533c8bfbb5ea450efc07e5833c5e Mon Sep 17 00:00:00 2001 From: Justin Clift Date: Wed, 2 Aug 2017 15:17:18 +0100 Subject: [PATCH 065/121] [docs] Minor wording tweak for clarity (#120) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ebc60d0..10eb7f0 100644 --- a/README.md +++ b/README.md @@ -44,14 +44,14 @@ Let's start with an example that shows the sessions API in a nutshell: First we initialize a session store calling `NewCookieStore()` and passing a secret key used to authenticate the session. Inside the handler, we call -`store.Get()` to retrieve an existing session or a new one. Then we set some -session values in session.Values, which is a `map[interface{}]interface{}`. +`store.Get()` to retrieve an existing session or create a new one. Then we set +some session values in session.Values, which is a `map[interface{}]interface{}`. And finally we call `session.Save()` to save the session in the response. Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with [`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler) -as or else you will leak memory! An easy way to do this is to wrap the top-level +or else you will leak memory! An easy way to do this is to wrap the top-level mux when calling http.ListenAndServe: ```go From a3acf13e802c358d65f249324d14ed24aac11370 Mon Sep 17 00:00:00 2001 From: Pontus Leitzler Date: Sun, 8 Oct 2017 23:47:40 +0200 Subject: [PATCH 066/121] Add missing error check (#123) --- store_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/store_test.go b/store_test.go index bfc53fa..3561e5d 100644 --- a/store_test.go +++ b/store_test.go @@ -59,6 +59,10 @@ func TestGH2MaxLength(t *testing.T) { w := httptest.NewRecorder() session, err := store.New(req, "my session") + if err != nil { + t.Fatal("failed to create session", err) + } + session.Values["big"] = make([]byte, base64.StdEncoding.DecodedLen(4096*2)) err = session.Save(req, w) if err == nil { From fe21b6a095cd8a9d41cc7f412e13d35c130383f3 Mon Sep 17 00:00:00 2001 From: Taylor Hurt Date: Mon, 15 Jan 2018 12:38:07 -0500 Subject: [PATCH 067/121] Update doc.go (#127) --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 591d932..57a5291 100644 --- a/doc.go +++ b/doc.go @@ -79,7 +79,7 @@ flashes, call session.Flashes(). Here is an example: return } - // Get the previously flashes, if any. + // Get the previous flashes, if any. if flashes := session.Flashes(); len(flashes) > 0 { // Use the flash values. } else { From 41ee504a3bfe9b07b2d62ba5cc2e1314cabb4a63 Mon Sep 17 00:00:00 2001 From: QuaSoft Date: Tue, 6 Feb 2018 22:22:36 +0200 Subject: [PATCH 068/121] Add link to memstore implementation (#143) Adds a link to memstore, an in-memory implementation of `sessions.Store` for use in unit tests --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 10eb7f0..4b4f6ff 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Other implementations of the `sessions.Store` interface: * [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite * [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite) * [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql +* [github.com/quasoft/memstore](https://github.com/quasoft/memstore) - In-memory implementation for use in unit tests ## License From 6ba88b7f1c1e2c8298ec7fb2efda9ee411375c80 Mon Sep 17 00:00:00 2001 From: Ahmadreza Zibaei Date: Fri, 9 Feb 2018 22:52:18 +0330 Subject: [PATCH 069/121] Prevent panic in NewSession function (#140) * prevent panic in NewSession function * TestSessionCookieStore added * change test function name --- sessions.go | 7 ++++--- sessions_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/sessions.go b/sessions.go index fe0d2bc..344d48d 100644 --- a/sessions.go +++ b/sessions.go @@ -37,9 +37,10 @@ type Options struct { // NewSession is called by session stores to create a new session instance. func NewSession(store Store, name string) *Session { return &Session{ - Values: make(map[interface{}]interface{}), - store: store, - name: name, + Values: make(map[interface{}]interface{}), + store: store, + name: name, + Options: new(Options), } } diff --git a/sessions_test.go b/sessions_test.go index c166b05..81fbf9a 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -155,6 +155,31 @@ func TestFlashes(t *testing.T) { } } +func TestCookieStoreMapPanic(t *testing.T) { + defer func() { + err := recover() + if err != nil { + t.Fatal(err) + } + }() + + store := NewCookieStore([]byte("aaa0defe5d2839cbc46fc4f080cd7adc")) + req, err := http.NewRequest("GET", "http://www.example.com", nil) + if err != nil { + t.Fatal("failed to create request", err) + } + w := httptest.NewRecorder() + + session := NewSession(store, "hello") + + session.Values["data"] = "hello-world" + + err = session.Save(req, w) + if err != nil { + t.Fatal("failed to save session", err) + } +} + func init() { gob.Register(FlashMessage{}) } From 7087b4d669d1bc3da42fb4e2eda73ae139a24439 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Tue, 20 Feb 2018 12:10:45 -0800 Subject: [PATCH 070/121] Add go.mod file for vgo dependency management. (#145) --- go.mod | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bb9ad35 --- /dev/null +++ b/go.mod @@ -0,0 +1,6 @@ +module "github.com/gorilla/sessions" + +require ( + "github.com/gorilla/context" v1.1 + "github.com/gorilla/securecookie" v1.1 +) From 7910f5bb5ac86ab08f97d8bda39b476fc117b684 Mon Sep 17 00:00:00 2001 From: Kshitij Saraogi Date: Tue, 13 Mar 2018 00:15:12 +0530 Subject: [PATCH 071/121] Added description about Max-Age field in Options (#148) In this commit, the behavior of Max-Age field is described based on its different values. Fixes #131 --- sessions.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sessions.go b/sessions.go index 344d48d..9870e31 100644 --- a/sessions.go +++ b/sessions.go @@ -24,8 +24,9 @@ const flashesKey = "_flash" type Options struct { 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 no Max-Age attribute specified and the cookie will be + // deleted after the browser session ends. + // MaxAge<0 means delete cookie immediately. // MaxAge>0 means Max-Age attribute present and given in seconds. MaxAge int Secure bool From 92b749d9bb99c7953517267a3d4d58eff6b957f8 Mon Sep 17 00:00:00 2001 From: Lauris BH Date: Wed, 21 Mar 2018 18:38:25 +0200 Subject: [PATCH 072/121] Add link to XORM store implementation (#149) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4b4f6ff..db9192f 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Other implementations of the `sessions.Store` interface: * [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite) * [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql * [github.com/quasoft/memstore](https://github.com/quasoft/memstore) - In-memory implementation for use in unit tests +* [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) ## License From a2f2a3de9a4a575047f73e3e36bc85ecc3546391 Mon Sep 17 00:00:00 2001 From: Lukas Rist Date: Thu, 22 Mar 2018 00:38:55 +0800 Subject: [PATCH 073/121] replacing travis badge with scaling svg (#147) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db9192f..c9e0e92 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ sessions ======== -[![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.png?branch=master)](https://travis-ci.org/gorilla/sessions) +[![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.svg?branch=master)](https://travis-ci.org/gorilla/sessions) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) From 9ee0d62e031e098d6353a069417bd6be8015af45 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Mon, 14 May 2018 12:37:23 -0700 Subject: [PATCH 074/121] [build] Update deps to correct SemVer tags (#153) Fixes #152 --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index bb9ad35..44befd4 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module "github.com/gorilla/sessions" require ( - "github.com/gorilla/context" v1.1 - "github.com/gorilla/securecookie" v1.1 + "github.com/gorilla/context" v1.1.1 + "github.com/gorilla/securecookie" v1.1.1 ) From 03b6f63cc43ef9c7240a635a5e22b13180e822b8 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Wed, 6 Jun 2018 08:52:11 -0700 Subject: [PATCH 075/121] Add AUTHORS file; update LICENSE (#158) --- AUTHORS | 43 +++++++++++++++++++++++++++++++++++++++++++ LICENSE | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 AUTHORS diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..1e3e7ac --- /dev/null +++ b/AUTHORS @@ -0,0 +1,43 @@ +# This is the official list of gorilla/sessions authors for copyright purposes. +# +# Please keep the list sorted. + +Ahmadreza Zibaei +Anton Lindström +Brian Jones +Collin Stedman +Deniz Eren +Dmitry Chestnykh +Dustin Oprea +Egon Elbre +enumappstore +Geofrey Ernest +Google LLC (https://opensource.google.com/) +Jerry Saravia +Jonathan Gillham +Justin Clift +Justin Hellings +Kamil Kisiel +Keiji Yoshida +kliron +Kshitij Saraogi +Lauris BH +Lukas Rist +Mark Dain +Matt Ho +Matt Silverlock +Mattias Wadman +Michael Schuett +Michael Stapelberg +Mirco Zeiss +moraes +nvcnvn +pappz +Pontus Leitzler +QuaSoft +rcadena +rodrigo moraes +Shawn Smith +Taylor Hurt +Tortuoise +Vitor De Mario diff --git a/LICENSE b/LICENSE index 0e5fb87..6903df6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012 Rodrigo Moraes. All rights reserved. +Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are From f7981e83f813996d74eb2764486fea8525fb9562 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Mon, 3 Sep 2018 07:50:20 -0700 Subject: [PATCH 076/121] Create release-drafter.yml (#166) --- .github/release-drafter.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..12440bf --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,8 @@ +# Config for https://github.com/apps/release-drafter +template: | + + + + ### CHANGELOG + + $CHANGES From a12d85708f2f3efec3eb7650378623a5f191272a Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Mon, 3 Sep 2018 08:26:36 -0700 Subject: [PATCH 077/121] Update Travis CI to build against the latest Go (#167) * Update Travis CI to build against the latest Go * Update vet to only run on the latest Go version. --- .travis.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index db17dd3..899d4cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,17 @@ sudo: false matrix: include: - - go: 1.3 - - go: 1.4 - - go: 1.5 - - go: 1.6 - - go: 1.7 + - go: 1.3.x + - go: 1.4.x + - go: 1.5.x + - go: 1.6.x + - go: 1.7.x + - go: 1.8.x + - go: 1.9.x + - go: 1.10.x + - go: 1.11.x + - go: 1.x + env: LATEST=true - go: tip allow_failures: - go: tip @@ -18,5 +24,5 @@ install: script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d .) - - go vet $(go list ./... | grep -v /vendor/) + - if [[ "$LATEST" = true ]]; then go vet $(go list ./... | grep -v /vendor/); fi - go test -v -race ./... From 81547393f870a35be888759a606ba7bf71dbe5c7 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Mon, 3 Sep 2018 08:45:04 -0700 Subject: [PATCH 078/121] Adds support for SameSite cookie attribute (#165) --- options.go | 18 ++++++++++++++++++ options_go111.go | 22 ++++++++++++++++++++++ sessions.go | 17 ----------------- 3 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 options.go create mode 100644 options_go111.go diff --git a/options.go b/options.go new file mode 100644 index 0000000..38ba72f --- /dev/null +++ b/options.go @@ -0,0 +1,18 @@ +// +build !go1.11 + +package sessions + +// Options stores configuration for a session or session store. +// +// Fields are a subset of http.Cookie fields. +type Options struct { + Path string + Domain string + // MaxAge=0 means no Max-Age attribute specified and the cookie will be + // deleted after the browser session ends. + // MaxAge<0 means delete cookie immediately. + // MaxAge>0 means Max-Age attribute present and given in seconds. + MaxAge int + Secure bool + HttpOnly bool +} diff --git a/options_go111.go b/options_go111.go new file mode 100644 index 0000000..388112a --- /dev/null +++ b/options_go111.go @@ -0,0 +1,22 @@ +// +build go1.11 + +package sessions + +import "net/http" + +// Options stores configuration for a session or session store. +// +// Fields are a subset of http.Cookie fields. +type Options struct { + Path string + Domain string + // MaxAge=0 means no Max-Age attribute specified and the cookie will be + // deleted after the browser session ends. + // MaxAge<0 means delete cookie immediately. + // MaxAge>0 means Max-Age attribute present and given in seconds. + MaxAge int + Secure bool + HttpOnly bool + // Defaults to http.SameSiteDefaultMode + SameSite http.SameSite +} diff --git a/sessions.go b/sessions.go index 9870e31..2fcdf51 100644 --- a/sessions.go +++ b/sessions.go @@ -16,23 +16,6 @@ import ( // Default flashes key. const flashesKey = "_flash" -// Options -------------------------------------------------------------------- - -// Options stores configuration for a session or session store. -// -// Fields are a subset of http.Cookie fields. -type Options struct { - Path string - Domain string - // MaxAge=0 means no Max-Age attribute specified and the cookie will be - // deleted after the browser session ends. - // MaxAge<0 means delete cookie immediately. - // MaxAge>0 means Max-Age attribute present and given in seconds. - MaxAge int - Secure bool - HttpOnly bool -} - // Session -------------------------------------------------------------------- // NewSession is called by session stores to create a new session instance. From e736060e33a9e2f3a45e63f244efe405955c2126 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Thu, 13 Sep 2018 11:39:28 -0700 Subject: [PATCH 079/121] [docs] Improve advice around key generation & usage. (#168) --- README.md | 63 +++++++++++++++++++++++++++++-------------------------- doc.go | 6 +++++- store.go | 3 --- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index c9e0e92..12dd18a 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,22 @@ -sessions -======== +# sessions + [![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.svg?branch=master)](https://travis-ci.org/gorilla/sessions) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) - gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. The key features are: -* Simple API: use it as an easy way to set signed (and optionally +- Simple API: use it as an easy way to set signed (and optionally encrypted) cookies. -* Built-in backends to store sessions in cookies or the filesystem. -* Flash messages: session values that last until read. -* Convenient way to switch session persistency (aka "remember me") and set +- Built-in backends to store sessions in cookies or the filesystem. +- Flash messages: session values that last until read. +- Convenient way to switch session persistency (aka "remember me") and set other attributes. -* Mechanism to rotate authentication and encryption keys. -* Multiple sessions per request, even using different backends. -* Interfaces and infrastructure for custom session backends: sessions from +- Mechanism to rotate authentication and encryption keys. +- Multiple sessions per request, even using different backends. +- Interfaces and infrastructure for custom session backends: sessions from different stores can be retrieved and batch-saved using a common API. Let's start with an example that shows the sessions API in a nutshell: @@ -28,7 +27,11 @@ Let's start with an example that shows the sessions API in a nutshell: "github.com/gorilla/sessions" ) - var store = sessions.NewCookieStore([]byte("something-very-secret")) + // Note: Don't store your key in your source code. Pass it via an + // environmental variable, or flag (or both), and don't accidentally commit it + // alongside your code. Ensure your key is sufficiently random - i.e. use Go's + // crypto/rand or securecookie.GenerateRandomKey(32) and persist the result. + var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY")) func MyHandler(w http.ResponseWriter, r *http.Request) { // Get a session. We're ignoring the error resulted from decoding an @@ -67,25 +70,25 @@ website](http://www.gorillatoolkit.org/pkg/sessions). Other implementations of the `sessions.Store` interface: -* [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB -* [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt -* [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase -* [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS -* [github.com/savaki/dynastore](https://github.com/savaki/dynastore) - DynamoDB on AWS (Official AWS library) -* [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache -* [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine -* [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB -* [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL -* [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster -* [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL -* [github.com/boj/redistore](https://github.com/boj/redistore) - Redis -* [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB -* [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak -* [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite -* [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite) -* [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql -* [github.com/quasoft/memstore](https://github.com/quasoft/memstore) - In-memory implementation for use in unit tests -* [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) +- [github.com/starJammer/gorilla-sessions-arangodb](https://github.com/starJammer/gorilla-sessions-arangodb) - ArangoDB +- [github.com/yosssi/boltstore](https://github.com/yosssi/boltstore) - Bolt +- [github.com/srinathgs/couchbasestore](https://github.com/srinathgs/couchbasestore) - Couchbase +- [github.com/denizeren/dynamostore](https://github.com/denizeren/dynamostore) - Dynamodb on AWS +- [github.com/savaki/dynastore](https://github.com/savaki/dynastore) - DynamoDB on AWS (Official AWS library) +- [github.com/bradleypeabody/gorilla-sessions-memcache](https://github.com/bradleypeabody/gorilla-sessions-memcache) - Memcache +- [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine +- [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB +- [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL +- [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster +- [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL +- [github.com/boj/redistore](https://github.com/boj/redistore) - Redis +- [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB +- [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak +- [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite +- [github.com/wader/gormstore](https://github.com/wader/gormstore) - GORM (MySQL, PostgreSQL, SQLite) +- [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql +- [github.com/quasoft/memstore](https://github.com/quasoft/memstore) - In-memory implementation for use in unit tests +- [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) ## License diff --git a/doc.go b/doc.go index 57a5291..7db6729 100644 --- a/doc.go +++ b/doc.go @@ -26,7 +26,11 @@ Let's start with an example that shows the sessions API in a nutshell: "github.com/gorilla/sessions" ) - var store = sessions.NewCookieStore([]byte("something-very-secret")) + // Note: Don't store your key in your source code. Pass it via an + // environmental variable, or flag (or both), and don't accidentally commit it + // alongside your code. Ensure your key is sufficiently random - i.e. use Go's + // crypto/rand or securecookie.GenerateRandomKey(32) and persist the result. + var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY")) func MyHandler(w http.ResponseWriter, r *http.Request) { // Get a session. Get() always returns a session, even if empty. diff --git a/store.go b/store.go index 4ff6b6c..bb7f964 100644 --- a/store.go +++ b/store.go @@ -47,9 +47,6 @@ type Store interface { // It is recommended to use an authentication key with 32 or 64 bytes. // The encryption key, if set, must be either 16, 24, or 32 bytes to select // AES-128, AES-192, or AES-256 modes. -// -// Use the convenience function securecookie.GenerateRandomKey() to create -// strong keys. func NewCookieStore(keyPairs ...[]byte) *CookieStore { cs := &CookieStore{ Codecs: securecookie.CodecsFromPairs(keyPairs...), From 3ab3680f9972bff29b08f0905926046aa9d74338 Mon Sep 17 00:00:00 2001 From: Niels Widger Date: Fri, 28 Sep 2018 07:53:49 -0400 Subject: [PATCH 080/121] Set http.Cookie's SameSite field in NewCookie for Go 1.11 or later Set the returned http.Cookie's SameSite field to the value of the SameSite field in the Options struct passed into NewCookie. This fixes an oversight made in PR #165 which was made to address issue Add a newCookieFromOptions function which takes a name, value and Options struct and returns an http.Cookie. There are two newCookieFromOptions implementations, one for Go 1.11 and later which sets SameSite and one for earlier Go versions which does not. Add new tests TestNewCookieFromOptions and TestNewCookieFromOptionsSameSite which ensure that the values passed to newCookieFromOptions are properly set in the returned cookie. The test TestNewCookieFromOptionsSameSite only runs if running Go 1.11 and later. --- cookie.go | 19 ++++++++++++++ cookie_go111.go | 20 +++++++++++++++ cookie_go111_test.go | 32 +++++++++++++++++++++++ cookie_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++++ sessions.go | 10 +------- 5 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 cookie.go create mode 100644 cookie_go111.go create mode 100644 cookie_go111_test.go create mode 100644 cookie_test.go diff --git a/cookie.go b/cookie.go new file mode 100644 index 0000000..1928b04 --- /dev/null +++ b/cookie.go @@ -0,0 +1,19 @@ +// +build !go1.11 + +package sessions + +import "net/http" + +// newCookieFromOptions returns an http.Cookie with the options set. +func newCookieFromOptions(name, value string, options *Options) *http.Cookie { + return &http.Cookie{ + Name: name, + Value: value, + Path: options.Path, + Domain: options.Domain, + MaxAge: options.MaxAge, + Secure: options.Secure, + HttpOnly: options.HttpOnly, + } + +} diff --git a/cookie_go111.go b/cookie_go111.go new file mode 100644 index 0000000..173d1a3 --- /dev/null +++ b/cookie_go111.go @@ -0,0 +1,20 @@ +// +build go1.11 + +package sessions + +import "net/http" + +// newCookieFromOptions returns an http.Cookie with the options set. +func newCookieFromOptions(name, value string, options *Options) *http.Cookie { + return &http.Cookie{ + Name: name, + Value: value, + Path: options.Path, + Domain: options.Domain, + MaxAge: options.MaxAge, + Secure: options.Secure, + HttpOnly: options.HttpOnly, + SameSite: options.SameSite, + } + +} diff --git a/cookie_go111_test.go b/cookie_go111_test.go new file mode 100644 index 0000000..de3953f --- /dev/null +++ b/cookie_go111_test.go @@ -0,0 +1,32 @@ +// +build go1.11 + +package sessions + +import ( + "net/http" + "strconv" + "testing" +) + +// Test for setting SameSite field in new http.Cookie from name, value +// and options +func TestNewCookieFromOptionsSameSite(t *testing.T) { + tests := []struct { + sameSite http.SameSite + }{ + {http.SameSiteDefaultMode}, + {http.SameSiteLaxMode}, + {http.SameSiteStrictMode}, + } + for i, v := range tests { + t.Run(strconv.Itoa(i+1), func(t *testing.T) { + options := &Options{ + SameSite: v.sameSite, + } + cookie := newCookieFromOptions("", "", options) + if cookie.SameSite != v.sameSite { + t.Fatalf("bad cookie sameSite: got %v, want %v", cookie.SameSite, v.sameSite) + } + }) + } +} diff --git a/cookie_test.go b/cookie_test.go new file mode 100644 index 0000000..0e10ab3 --- /dev/null +++ b/cookie_test.go @@ -0,0 +1,60 @@ +package sessions + +import ( + "strconv" + "testing" +) + +// Test for creating new http.Cookie from name, value and options +func TestNewCookieFromOptions(t *testing.T) { + tests := []struct { + name string + value string + path string + domain string + maxAge int + secure bool + httpOnly bool + }{ + {"", "bar", "/foo/bar", "foo.example.com", 3600, true, true}, + {"foo", "", "/foo/bar", "foo.example.com", 3600, true, true}, + {"foo", "bar", "", "foo.example.com", 3600, true, true}, + {"foo", "bar", "/foo/bar", "", 3600, true, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 0, true, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 3600, false, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, false}, + } + for i, v := range tests { + t.Run(strconv.Itoa(i+1), func(t *testing.T) { + options := &Options{ + Path: v.path, + Domain: v.domain, + MaxAge: v.maxAge, + Secure: v.secure, + HttpOnly: v.httpOnly, + } + cookie := newCookieFromOptions(v.name, v.value, options) + if cookie.Name != v.name { + t.Fatalf("bad cookie name: got %q, want %q", cookie.Name, v.name) + } + if cookie.Value != v.value { + t.Fatalf("bad cookie value: got %q, want %q", cookie.Value, v.value) + } + if cookie.Path != v.path { + t.Fatalf("bad cookie path: got %q, want %q", cookie.Path, v.path) + } + if cookie.Domain != v.domain { + t.Fatalf("bad cookie domain: got %q, want %q", cookie.Domain, v.domain) + } + if cookie.MaxAge != v.maxAge { + t.Fatalf("bad cookie maxAge: got %q, want %q", cookie.MaxAge, v.maxAge) + } + if cookie.Secure != v.secure { + t.Fatalf("bad cookie secure: got %v, want %v", cookie.Secure, v.secure) + } + if cookie.HttpOnly != v.httpOnly { + t.Fatalf("bad cookie httpOnly: got %v, want %v", cookie.HttpOnly, v.httpOnly) + } + }) + } +} diff --git a/sessions.go b/sessions.go index 2fcdf51..a821d31 100644 --- a/sessions.go +++ b/sessions.go @@ -178,15 +178,7 @@ func Save(r *http.Request, w http.ResponseWriter) error { // the Expires field calculated based on the MaxAge value, for Internet // Explorer compatibility. func NewCookie(name, value string, options *Options) *http.Cookie { - cookie := &http.Cookie{ - Name: name, - Value: value, - Path: options.Path, - Domain: options.Domain, - MaxAge: options.MaxAge, - Secure: options.Secure, - HttpOnly: options.HttpOnly, - } + cookie := newCookieFromOptions(name, value, options) if options.MaxAge > 0 { d := time.Duration(options.MaxAge) * time.Second cookie.Expires = time.Now().Add(d) From f57b7e2d29c6211d16ffa52a0998272f75799030 Mon Sep 17 00:00:00 2001 From: Niels Widger Date: Fri, 28 Sep 2018 08:20:16 -0400 Subject: [PATCH 081/121] Don't use t.Run in tests, not supported in earlier Go versions This package is meant to work on Go versions going back to Go 1.3, which means tests can't use testing.T.Run which doesn't exists in Go 1.6 and earlier. --- cookie_go111_test.go | 17 +++++------- cookie_test.go | 61 +++++++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/cookie_go111_test.go b/cookie_go111_test.go index de3953f..1aac273 100644 --- a/cookie_go111_test.go +++ b/cookie_go111_test.go @@ -4,7 +4,6 @@ package sessions import ( "net/http" - "strconv" "testing" ) @@ -19,14 +18,12 @@ func TestNewCookieFromOptionsSameSite(t *testing.T) { {http.SameSiteStrictMode}, } for i, v := range tests { - t.Run(strconv.Itoa(i+1), func(t *testing.T) { - options := &Options{ - SameSite: v.sameSite, - } - cookie := newCookieFromOptions("", "", options) - if cookie.SameSite != v.sameSite { - t.Fatalf("bad cookie sameSite: got %v, want %v", cookie.SameSite, v.sameSite) - } - }) + options := &Options{ + SameSite: v.sameSite, + } + cookie := newCookieFromOptions("", "", options) + if cookie.SameSite != v.sameSite { + t.Fatalf("%v: bad cookie sameSite: got %v, want %v", i+1, cookie.SameSite, v.sameSite) + } } } diff --git a/cookie_test.go b/cookie_test.go index 0e10ab3..acb4efb 100644 --- a/cookie_test.go +++ b/cookie_test.go @@ -1,7 +1,6 @@ package sessions import ( - "strconv" "testing" ) @@ -25,36 +24,34 @@ func TestNewCookieFromOptions(t *testing.T) { {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, false}, } for i, v := range tests { - t.Run(strconv.Itoa(i+1), func(t *testing.T) { - options := &Options{ - Path: v.path, - Domain: v.domain, - MaxAge: v.maxAge, - Secure: v.secure, - HttpOnly: v.httpOnly, - } - cookie := newCookieFromOptions(v.name, v.value, options) - if cookie.Name != v.name { - t.Fatalf("bad cookie name: got %q, want %q", cookie.Name, v.name) - } - if cookie.Value != v.value { - t.Fatalf("bad cookie value: got %q, want %q", cookie.Value, v.value) - } - if cookie.Path != v.path { - t.Fatalf("bad cookie path: got %q, want %q", cookie.Path, v.path) - } - if cookie.Domain != v.domain { - t.Fatalf("bad cookie domain: got %q, want %q", cookie.Domain, v.domain) - } - if cookie.MaxAge != v.maxAge { - t.Fatalf("bad cookie maxAge: got %q, want %q", cookie.MaxAge, v.maxAge) - } - if cookie.Secure != v.secure { - t.Fatalf("bad cookie secure: got %v, want %v", cookie.Secure, v.secure) - } - if cookie.HttpOnly != v.httpOnly { - t.Fatalf("bad cookie httpOnly: got %v, want %v", cookie.HttpOnly, v.httpOnly) - } - }) + options := &Options{ + Path: v.path, + Domain: v.domain, + MaxAge: v.maxAge, + Secure: v.secure, + HttpOnly: v.httpOnly, + } + cookie := newCookieFromOptions(v.name, v.value, options) + if cookie.Name != v.name { + t.Fatalf("%v: bad cookie name: got %q, want %q", i+1, cookie.Name, v.name) + } + if cookie.Value != v.value { + t.Fatalf("%v: bad cookie value: got %q, want %q", i+1, cookie.Value, v.value) + } + if cookie.Path != v.path { + t.Fatalf("%v: bad cookie path: got %q, want %q", i+1, cookie.Path, v.path) + } + if cookie.Domain != v.domain { + t.Fatalf("%v: bad cookie domain: got %q, want %q", i+1, cookie.Domain, v.domain) + } + if cookie.MaxAge != v.maxAge { + t.Fatalf("%v: bad cookie maxAge: got %q, want %q", i+1, cookie.MaxAge, v.maxAge) + } + if cookie.Secure != v.secure { + t.Fatalf("%v: bad cookie secure: got %v, want %v", i+1, cookie.Secure, v.secure) + } + if cookie.HttpOnly != v.httpOnly { + t.Fatalf("%v: bad cookie httpOnly: got %v, want %v", i+1, cookie.HttpOnly, v.httpOnly) + } } } From 8619d3c3a2b9d5196aa3fa4ef3b980f13b4abce8 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Fri, 12 Oct 2018 08:33:09 -0700 Subject: [PATCH 082/121] README.md: Update site URL --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 12dd18a..22b13d9 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ And finally we call `session.Save()` to save the session in the response. Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with -[`context.ClearHandler`](http://www.gorillatoolkit.org/pkg/context#ClearHandler) +[`context.ClearHandler`](https://www.gorillatoolkit.org/pkg/context#ClearHandler) or else you will leak memory! An easy way to do this is to wrap the top-level mux when calling http.ListenAndServe: @@ -64,7 +64,7 @@ mux when calling http.ListenAndServe: The ClearHandler function is provided by the gorilla/context package. More examples are available [on the Gorilla -website](http://www.gorillatoolkit.org/pkg/sessions). +website](https://www.gorillatoolkit.org/pkg/sessions). ## Store Implementations From 68d1edeb366b66bdd5d714e32d7a065f74b03b9b Mon Sep 17 00:00:00 2001 From: Keegan Carruthers-Smith Date: Sun, 14 Oct 2018 10:38:51 +0200 Subject: [PATCH 083/121] Run go mod tidy Needing to quote the package strings is from an earlier version of vgo. --- go.mod | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 44befd4..ea28ffe 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ -module "github.com/gorilla/sessions" +module github.com/gorilla/sessions require ( - "github.com/gorilla/context" v1.1.1 - "github.com/gorilla/securecookie" v1.1.1 + github.com/gorilla/context v1.1.1 + github.com/gorilla/securecookie v1.1.1 ) From 4109461f01b3bac97fdddfe1bb49b4bd4feb1cd4 Mon Sep 17 00:00:00 2001 From: Nikhita Raghunath Date: Tue, 27 Nov 2018 18:02:35 +0530 Subject: [PATCH 084/121] [docs] Fix type in README example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22b13d9..00b43ca 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Let's start with an example that shows the sessions API in a nutshell: // environmental variable, or flag (or both), and don't accidentally commit it // alongside your code. Ensure your key is sufficiently random - i.e. use Go's // crypto/rand or securecookie.GenerateRandomKey(32) and persist the result. - var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY")) + var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) func MyHandler(w http.ResponseWriter, r *http.Request) { // Get a session. We're ignoring the error resulted from decoding an From b72c0abcab34f0952f00939c330c2effccf57423 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 7 Dec 2018 09:33:25 -0600 Subject: [PATCH 085/121] Add stalebot config --- .github/stale | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/stale diff --git a/.github/stale b/.github/stale new file mode 100644 index 0000000..f5c1622 --- /dev/null +++ b/.github/stale @@ -0,0 +1,11 @@ +daysUntilStale: 60 +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - v2 + - needs-review +staleLabel: stale +markComment: > + This issue has been automatically marked as stale because it hasn't seen + a recent update. It'll be automatically closed in a few days. +closeComment: false From 726776d48bfbb3961d6c7071d4dace34acc0ff28 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Sat, 8 Dec 2018 13:01:43 -0800 Subject: [PATCH 086/121] Update and rename stale to stale.yml (#177) --- .github/{stale => stale.yml} | 1 + 1 file changed, 1 insertion(+) rename .github/{stale => stale.yml} (94%) diff --git a/.github/stale b/.github/stale.yml similarity index 94% rename from .github/stale rename to .github/stale.yml index f5c1622..de8a678 100644 --- a/.github/stale +++ b/.github/stale.yml @@ -4,6 +4,7 @@ daysUntilClose: 7 exemptLabels: - v2 - needs-review + - work-required staleLabel: stale markComment: > This issue has been automatically marked as stale because it hasn't seen From 12bd4761fc66ac946e16fcc2a32b1e0b066f6177 Mon Sep 17 00:00:00 2001 From: secracon Date: Sat, 8 Dec 2018 22:45:19 +0100 Subject: [PATCH 087/121] Use golang context pkg instead of gorilla/context to fix memory leaks (#175) * - use golang context pkg instead of gorilla/context to fix memory leaks * - add test case for checking request context content upon shallow copy * - update docs, readme.md and travis.yml --- .travis.yml | 4 ---- README.md | 12 ------------ doc.go | 8 -------- sessions.go | 8 ++++---- sessions_test.go | 31 +++++++++++++++++++++++++++++++ 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index 899d4cb..85c28cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,6 @@ sudo: false matrix: include: - - go: 1.3.x - - go: 1.4.x - - go: 1.5.x - - go: 1.6.x - go: 1.7.x - go: 1.8.x - go: 1.9.x diff --git a/README.md b/README.md index 00b43ca..98c993d 100644 --- a/README.md +++ b/README.md @@ -51,18 +51,6 @@ secret key used to authenticate the session. Inside the handler, we call some session values in session.Values, which is a `map[interface{}]interface{}`. And finally we call `session.Save()` to save the session in the response. -Important Note: If you aren't using gorilla/mux, you need to wrap your handlers -with -[`context.ClearHandler`](https://www.gorillatoolkit.org/pkg/context#ClearHandler) -or else you will leak memory! An easy way to do this is to wrap the top-level -mux when calling http.ListenAndServe: - -```go - http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux)) -``` - -The ClearHandler function is provided by the gorilla/context package. - More examples are available [on the Gorilla website](https://www.gorillatoolkit.org/pkg/sessions). diff --git a/doc.go b/doc.go index 7db6729..64f858c 100644 --- a/doc.go +++ b/doc.go @@ -59,14 +59,6 @@ session.Save(r, w), and either display an error message or otherwise handle it. Save must be called before writing to the response, otherwise the session cookie will not be sent to the client. -Important Note: If you aren't using gorilla/mux, you need to wrap your handlers -with context.ClearHandler as or else you will leak memory! An easy way to do this -is to wrap the top-level mux when calling http.ListenAndServe: - - http.ListenAndServe(":8080", context.ClearHandler(http.DefaultServeMux)) - -The ClearHandler function is provided by the gorilla/context package. - That's all you need to know for the basic usage. Let's take a look at other options, starting with flash messages. diff --git a/sessions.go b/sessions.go index a821d31..c052b28 100644 --- a/sessions.go +++ b/sessions.go @@ -5,12 +5,11 @@ package sessions import ( + "context" "encoding/gob" "fmt" "net/http" "time" - - "github.com/gorilla/context" ) // Default flashes key. @@ -108,7 +107,8 @@ const registryKey contextKey = 0 // GetRegistry returns a registry instance for the current request. func GetRegistry(r *http.Request) *Registry { - registry := context.Get(r, registryKey) + var ctx = r.Context() + registry := ctx.Value(registryKey) if registry != nil { return registry.(*Registry) } @@ -116,7 +116,7 @@ func GetRegistry(r *http.Request) *Registry { request: r, sessions: make(map[string]sessionInfo), } - context.Set(r, registryKey, newRegistry) + *r = *r.WithContext(context.WithValue(ctx, registryKey, newRegistry)) return newRegistry } diff --git a/sessions_test.go b/sessions_test.go index 81fbf9a..a734f67 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -153,6 +153,37 @@ func TestFlashes(t *testing.T) { if custom.Type != 42 || custom.Message != "foo" { t.Errorf("Expected %#v, got %#v", FlashMessage{42, "foo"}, custom) } + + // Round 5 ---------------------------------------------------------------- + // Check if a request shallow copy resets the request context data store. + + req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) + + // Get a session. + if session, err = store.Get(req, "session-key"); err != nil { + t.Fatalf("Error getting session: %v", err) + } + + // Put a test value into the session data store. + session.Values["test"] = "test-value" + + // Create a shallow copy of the request. + req = req.WithContext(req.Context()) + + // Get the session again. + if session, err = store.Get(req, "session-key"); err != nil { + t.Fatalf("Error getting session: %v", err) + } + + // Check if the previous inserted value still exists. + if session.Values["test"] == nil { + t.Fatalf("Session test value is lost in the request context!") + } + + // Check if the previous inserted value has the same value. + if session.Values["test"] != "test-value" { + t.Fatalf("Session test value is changed in the request context!") + } } func TestCookieStoreMapPanic(t *testing.T) { From 56c33e9da484d617e23f395ecec61db32d99dcf3 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Wed, 26 Jun 2019 21:06:25 -0700 Subject: [PATCH 088/121] Create config.yml (#195) * Create config.yml * Update config.yml * Delete .travis.yml --- .circleci/config.yml | 65 ++++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 24 ---------------- 2 files changed, 65 insertions(+), 24 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 .travis.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..bd23335 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,65 @@ +version: 2.0 + +jobs: + # Base test configuration for Go library tests Each distinct version should + # inherit this base, and override (at least) the container image used. + "test": &test + docker: + - image: circleci/golang:latest + working_directory: /go/src/github.com/gorilla/sessions + steps: &steps + - checkout + - run: go version + - run: go get -t -v ./... + - run: diff -u <(echo -n) <(gofmt -d .) + - run: if [[ "$LATEST" = true ]]; then go vet -v .; fi + - run: go test -v -race ./... + + "latest": + <<: *test + environment: + LATEST: true + + + "1.12": + <<: *test + docker: + - image: circleci/golang:1.12 + + "1.11": + <<: *test + docker: + - image: circleci/golang:1.11 + + "1.10": + <<: *test + docker: + - image: circleci/golang:1.10 + + "1.9": + <<: *test + docker: + - image: circleci/golang:1.9 + + "1.8": + <<: *test + docker: + - image: circleci/golang:1.8 + + "1.7": + <<: *test + docker: + - image: circleci/golang:1.7 + + +workflows: + version: 2 + build: + jobs: + - "latest" + - "1.12" + - "1.11" + - "1.10" + - "1.9" + - "1.8" + - "1.7" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 85c28cd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: go -sudo: false - -matrix: - include: - - go: 1.7.x - - go: 1.8.x - - go: 1.9.x - - go: 1.10.x - - go: 1.11.x - - go: 1.x - env: LATEST=true - - go: tip - allow_failures: - - go: tip - -install: - - # skip - -script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d .) - - if [[ "$LATEST" = true ]]; then go vet $(go list ./... | grep -v /vendor/); fi - - go test -v -race ./... From 4355a998706e83fe1d71c31b07af94e34f68d74a Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Tue, 9 Jul 2019 09:04:13 -0500 Subject: [PATCH 089/121] Update go.mod: removes gorilla/context (#196) --- go.mod | 5 +---- go.sum | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 go.sum diff --git a/go.mod b/go.mod index ea28ffe..9028bcf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,3 @@ module github.com/gorilla/sessions -require ( - github.com/gorilla/context v1.1.1 - github.com/gorilla/securecookie v1.1.1 -) +require github.com/gorilla/securecookie v1.1.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e6a7ed5 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= From 9c6b24ee2dee928836dbf71f18bb0c7d0da4bf85 Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sat, 20 Jul 2019 22:21:36 +0200 Subject: [PATCH 090/121] Removed unused global var (#199) DefaultRemoteAddr seems to not be used (any longer?). --- sessions_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sessions_test.go b/sessions_test.go index a734f67..0b84fbc 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -20,10 +20,6 @@ func NewRecorder() *httptest.ResponseRecorder { } } -// DefaultRemoteAddr is the default remote address to return in RemoteAddr if -// an explicit DefaultRemoteAddr isn't set on ResponseRecorder. -const DefaultRemoteAddr = "1.2.3.4" - // ---------------------------------------------------------------------------- type FlashMessage struct { From daaabe7307937b2fde7a8fb84d689e6ec1aa7c09 Mon Sep 17 00:00:00 2001 From: Adam Jack Date: Tue, 17 Sep 2019 06:53:25 -0600 Subject: [PATCH 091/121] docs: added _ = to indicate there is a return from Save (#197) * Added _ = to indicate there is a return from Save This is because I copied the sample and failed to notice there was a return, which for me was failing, and lost a bunch of time to troubleshooting. * Added "err =" to session(s).Save() calls Indicated the need for error handling so developer who copy samples get the indication that error handling is required. ( I was learning/tinkering and copied a sample without error handling, failed to notice that Save returns an error in the documentation, and it took me a lot of time/troubleshooting to track down the problem.) * Highlighted possibility of Save() error. --- README.md | 6 +++++- doc.go | 19 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 98c993d..c730c05 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,11 @@ Let's start with an example that shows the sessions API in a nutshell: session.Values["foo"] = "bar" session.Values[42] = 43 // Save it before we write to the response/return from the handler. - session.Save(r, w) + err = session.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } ``` diff --git a/doc.go b/doc.go index 64f858c..f4673cc 100644 --- a/doc.go +++ b/doc.go @@ -30,6 +30,7 @@ Let's start with an example that shows the sessions API in a nutshell: // environmental variable, or flag (or both), and don't accidentally commit it // alongside your code. Ensure your key is sufficiently random - i.e. use Go's // crypto/rand or securecookie.GenerateRandomKey(32) and persist the result. + // Ensure SESSION_KEY exists in the environment, or sessions will fail. var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY")) func MyHandler(w http.ResponseWriter, r *http.Request) { @@ -44,7 +45,11 @@ Let's start with an example that shows the sessions API in a nutshell: session.Values["foo"] = "bar" session.Values[42] = 43 // Save it before we write to the response/return from the handler. - session.Save(r, w) + err = session.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } First we initialize a session store calling NewCookieStore() and passing a @@ -82,7 +87,11 @@ flashes, call session.Flashes(). Here is an example: // Set a new flash. session.AddFlash("Hello, flash messages world!") } - session.Save(r, w) + err = session.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } Flash messages are useful to set information to be read after a redirection, @@ -185,7 +194,11 @@ at once: it's sessions.Save(). Here's an example: session2, _ := store.Get(r, "session-two") session2.Values[42] = 43 // Save all sessions. - sessions.Save(r, w) + err = sessions.Save(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } This is possible because when we call Get() from a session store, it adds the From 36821f6da8fc2cb2d2b5c7a6ad481792be667489 Mon Sep 17 00:00:00 2001 From: Tyler Bui-Palsulich <26876514+tbpg@users.noreply.github.com> Date: Tue, 17 Sep 2019 08:54:14 -0400 Subject: [PATCH 092/121] docs: README.md: link Cloud Firestore implementation (#201) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c730c05..abc3809 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/gernest/qlstore](https://github.com/gernest/qlstore) - ql - [github.com/quasoft/memstore](https://github.com/quasoft/memstore) - In-memory implementation for use in unit tests - [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) +- [github.com/GoogleCloudPlatform/firestore-gorilla-sessions](https://github.com/GoogleCloudPlatform/firestore-gorilla-sessions) - Cloud Firestore ## License From 400b592ab70b9f8ee876cf47bc6c794d255dd4aa Mon Sep 17 00:00:00 2001 From: Ruben Cervilla <47088081+rbcervilla@users.noreply.github.com> Date: Sun, 6 Oct 2019 17:13:01 +0200 Subject: [PATCH 093/121] docs: Add new Redis store to README (#202) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index abc3809..d4d70e9 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster - [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL - [github.com/boj/redistore](https://github.com/boj/redistore) - Redis +- [github.com/rbcervilla/redisstore](https://github.com/rbcervilla/redisstore) - Redis (Single, Sentinel, Cluster) - [github.com/boj/rethinkstore](https://github.com/boj/rethinkstore) - RethinkDB - [github.com/boj/riakstore](https://github.com/boj/riakstore) - Riak - [github.com/michaeljs1990/sqlitestore](https://github.com/michaeljs1990/sqlitestore) - SQLite From 15ff3511704639ab26f7843f780c015f4bf49565 Mon Sep 17 00:00:00 2001 From: Waitire Colline Date: Sun, 5 Jan 2020 19:41:44 +0300 Subject: [PATCH 094/121] docs: fix CookieStore creation in doc.go (#206) --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index f4673cc..946bf5c 100644 --- a/doc.go +++ b/doc.go @@ -31,7 +31,7 @@ Let's start with an example that shows the sessions API in a nutshell: // alongside your code. Ensure your key is sufficiently random - i.e. use Go's // crypto/rand or securecookie.GenerateRandomKey(32) and persist the result. // Ensure SESSION_KEY exists in the environment, or sessions will fail. - var store = sessions.NewCookieStore(os.Getenv("SESSION_KEY")) + var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY"))) func MyHandler(w http.ResponseWriter, r *http.Request) { // Get a session. Get() always returns a session, even if empty. From 947297c38923a85d18d497ad4cadfa23688ed3d4 Mon Sep 17 00:00:00 2001 From: Stephen Afam-Osemene Date: Mon, 13 Jul 2020 06:47:38 +0200 Subject: [PATCH 095/121] Add link to implementation for CockroachDB (#219) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d4d70e9..a787813 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/quasoft/memstore](https://github.com/quasoft/memstore) - In-memory implementation for use in unit tests - [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) - [github.com/GoogleCloudPlatform/firestore-gorilla-sessions](https://github.com/GoogleCloudPlatform/firestore-gorilla-sessions) - Cloud Firestore +- [github.com/stephenafamo/crdbstore](https://github.com/stephenafamo/crdbstore) - CockroachDB ## License From 61fa50d034f99479a7de0d1c02c5e9dea5ad30cb Mon Sep 17 00:00:00 2001 From: James Cote <3276350+Coteh@users.noreply.github.com> Date: Wed, 19 Aug 2020 11:25:28 -0400 Subject: [PATCH 096/121] Fix typo in README example (#223) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a787813..a8fb98b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Let's start with an example that shows the sessions API in a nutshell: session.Values["foo"] = "bar" session.Values[42] = 43 // Save it before we write to the response/return from the handler. - err = session.Save(r, w) + err := session.Save(r, w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return From 9cb0b0a3e38d07f41143f03013eaf0a3243fb474 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Sat, 12 Sep 2020 12:25:02 -0700 Subject: [PATCH 097/121] build: use build matrix; drop Go <= 1.10 (#230) --- .circleci/config.yml | 119 ++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bd23335..1b545b7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,65 +1,70 @@ -version: 2.0 +version: 2.1 jobs: - # Base test configuration for Go library tests Each distinct version should - # inherit this base, and override (at least) the container image used. - "test": &test + "test": + parameters: + version: + type: string + default: "latest" + golint: + type: boolean + default: true + modules: + type: boolean + default: true + goproxy: + type: string + default: "" docker: - - image: circleci/golang:latest + - image: "circleci/golang:<< parameters.version >>" working_directory: /go/src/github.com/gorilla/sessions - steps: &steps - - checkout - - run: go version - - run: go get -t -v ./... - - run: diff -u <(echo -n) <(gofmt -d .) - - run: if [[ "$LATEST" = true ]]; then go vet -v .; fi - - run: go test -v -race ./... - - "latest": - <<: *test environment: - LATEST: true - - - "1.12": - <<: *test - docker: - - image: circleci/golang:1.12 - - "1.11": - <<: *test - docker: - - image: circleci/golang:1.11 - - "1.10": - <<: *test - docker: - - image: circleci/golang:1.10 - - "1.9": - <<: *test - docker: - - image: circleci/golang:1.9 - - "1.8": - <<: *test - docker: - - image: circleci/golang:1.8 - - "1.7": - <<: *test - docker: - - image: circleci/golang:1.7 - + GO111MODULE: "on" + GOPROXY: "<< parameters.goproxy >>" + steps: + - checkout + - run: + name: "Print the Go version" + command: > + go version + - run: + name: "Fetch dependencies" + command: > + if [[ << parameters.modules >> = true ]]; then + go mod download + export GO111MODULE=on + else + go get -v ./... + fi + # Only run gofmt, vet & lint against the latest Go version + - run: + name: "Run golint" + command: > + if [ << parameters.version >> = "latest" ] && [ << parameters.golint >> = true ]; then + go get -u golang.org/x/lint/golint + golint ./... + fi + - run: + name: "Run gofmt" + command: > + if [[ << parameters.version >> = "latest" ]]; then + diff -u <(echo -n) <(gofmt -d -e .) + fi + - run: + name: "Run go vet" + command: > + if [[ << parameters.version >> = "latest" ]]; then + go vet -v ./... + fi + - run: + name: "Run go test (+ race detector)" + command: > + go test -v -race ./... workflows: - version: 2 - build: + tests: jobs: - - "latest" - - "1.12" - - "1.11" - - "1.10" - - "1.9" - - "1.8" - - "1.7" + - test: + matrix: + parameters: + version: ["latest", "1.15", "1.14", "1.13", "1.12", "1.11"] From 0a84f353f00601bbc2fcf99e8da368ef368b4e51 Mon Sep 17 00:00:00 2001 From: Leung Yau Ming Date: Sat, 31 Jul 2021 21:13:50 +0800 Subject: [PATCH 098/121] refactor: use base32 encoder with no padding (#240) --- store.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/store.go b/store.go index bb7f964..eb45afb 100644 --- a/store.go +++ b/store.go @@ -10,7 +10,6 @@ import ( "net/http" "os" "path/filepath" - "strings" "sync" "github.com/gorilla/securecookie" @@ -201,6 +200,8 @@ func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) { return session, err } +var base32RawStdEncoding = base32.StdEncoding.WithPadding(base32.NoPadding) + // Save adds a single session to the response. // // If the Options.MaxAge of the session is <= 0 then the session file will be @@ -221,9 +222,8 @@ func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, if session.ID == "" { // Because the ID is used in the filename, encode it to // use alphanumeric characters only. - session.ID = strings.TrimRight( - base32.StdEncoding.EncodeToString( - securecookie.GenerateRandomKey(32)), "=") + session.ID = base32RawStdEncoding.EncodeToString( + securecookie.GenerateRandomKey(32)) } if err := s.save(session); err != nil { return err From d81e0696f6afcb11015b5f57b277994f501ec05f Mon Sep 17 00:00:00 2001 From: Ryuichiroh Ikeuchi <37844673+ryicoh@users.noreply.github.com> Date: Mon, 10 Jan 2022 00:14:04 +0900 Subject: [PATCH 099/121] docs: Add new TiKV store to README (#245) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a8fb98b..38c9f27 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/lafriks/xormstore](https://github.com/lafriks/xormstore) - XORM (MySQL, PostgreSQL, SQLite, Microsoft SQL Server, TiDB) - [github.com/GoogleCloudPlatform/firestore-gorilla-sessions](https://github.com/GoogleCloudPlatform/firestore-gorilla-sessions) - Cloud Firestore - [github.com/stephenafamo/crdbstore](https://github.com/stephenafamo/crdbstore) - CockroachDB +- [github.com/ryicoh/tikvstore](github.com/ryicoh/tikvstore) - TiKV ## License From a6a8e49c83a2dba9cf7f486feb2662d6439851f2 Mon Sep 17 00:00:00 2001 From: Marius Orcsik Date: Mon, 7 Feb 2022 12:04:23 +0100 Subject: [PATCH 100/121] Fix linting errors for go1.17 (#253) --- cookie.go | 1 + cookie_go111.go | 1 + cookie_go111_test.go | 1 + options.go | 1 + options_go111.go | 1 + 5 files changed, 5 insertions(+) diff --git a/cookie.go b/cookie.go index 1928b04..6612662 100644 --- a/cookie.go +++ b/cookie.go @@ -1,3 +1,4 @@ +//go:build !go1.11 // +build !go1.11 package sessions diff --git a/cookie_go111.go b/cookie_go111.go index 173d1a3..9b58828 100644 --- a/cookie_go111.go +++ b/cookie_go111.go @@ -1,3 +1,4 @@ +//go:build go1.11 // +build go1.11 package sessions diff --git a/cookie_go111_test.go b/cookie_go111_test.go index 1aac273..e6137c7 100644 --- a/cookie_go111_test.go +++ b/cookie_go111_test.go @@ -1,3 +1,4 @@ +//go:build go1.11 // +build go1.11 package sessions diff --git a/options.go b/options.go index 38ba72f..d33d076 100644 --- a/options.go +++ b/options.go @@ -1,3 +1,4 @@ +//go:build !go1.11 // +build !go1.11 package sessions diff --git a/options_go111.go b/options_go111.go index 388112a..af9cdf0 100644 --- a/options_go111.go +++ b/options_go111.go @@ -1,3 +1,4 @@ +//go:build go1.11 // +build go1.11 package sessions From 0e1d1d7c382124033b710ef1ef0993327195ed40 Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Fri, 9 Dec 2022 10:57:31 -0500 Subject: [PATCH 101/121] archive mode --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 38c9f27..b2b95ff 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,12 @@ [![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.svg?branch=master)](https://travis-ci.org/gorilla/sessions) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) +--- + +**The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit** + +--- + gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. From 7aa6ccf8b68628c0bf0404fc259d1a56340cb6ce Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sat, 15 Jul 2023 10:54:36 -0400 Subject: [PATCH 102/121] Update README.md Signed-off-by: Corey Daley --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index b2b95ff..c44bdd4 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,6 @@ [![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.svg?branch=master)](https://travis-ci.org/gorilla/sessions) [![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) ---- - -**The Gorilla project has been archived, and is no longer under active maintainenance. You can read more here: https://github.com/gorilla#gorilla-toolkit** - ---- gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. From b1daf6dac7416f09174c1a6778ce4ce6bbdf6564 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sun, 23 Jul 2023 16:32:59 -0400 Subject: [PATCH 103/121] Update go version, add tools for verification and testing (#263) Fixes # **Summary of Changes** 1. 2. 3. > PS: Make sure your PR includes/updates tests! If you need help with this part, just ask! --- .circleci/config.yml | 70 ------------------------------------ .editorconfig | 20 +++++++++++ .github/release-drafter.yml | 8 ----- .github/stale.yml | 12 ------- .github/workflows/issues.yml | 20 +++++++++++ .github/workflows/test.yml | 55 ++++++++++++++++++++++++++++ .gitignore | 1 + AUTHORS | 43 ---------------------- Makefile | 34 ++++++++++++++++++ README.md | 6 ++-- go.mod | 2 ++ sessions_test.go | 2 -- store.go | 5 ++- 13 files changed, 138 insertions(+), 140 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .editorconfig delete mode 100644 .github/release-drafter.yml delete mode 100644 .github/stale.yml create mode 100644 .github/workflows/issues.yml create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore delete mode 100644 AUTHORS create mode 100644 Makefile diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 1b545b7..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,70 +0,0 @@ -version: 2.1 - -jobs: - "test": - parameters: - version: - type: string - default: "latest" - golint: - type: boolean - default: true - modules: - type: boolean - default: true - goproxy: - type: string - default: "" - docker: - - image: "circleci/golang:<< parameters.version >>" - working_directory: /go/src/github.com/gorilla/sessions - environment: - GO111MODULE: "on" - GOPROXY: "<< parameters.goproxy >>" - steps: - - checkout - - run: - name: "Print the Go version" - command: > - go version - - run: - name: "Fetch dependencies" - command: > - if [[ << parameters.modules >> = true ]]; then - go mod download - export GO111MODULE=on - else - go get -v ./... - fi - # Only run gofmt, vet & lint against the latest Go version - - run: - name: "Run golint" - command: > - if [ << parameters.version >> = "latest" ] && [ << parameters.golint >> = true ]; then - go get -u golang.org/x/lint/golint - golint ./... - fi - - run: - name: "Run gofmt" - command: > - if [[ << parameters.version >> = "latest" ]]; then - diff -u <(echo -n) <(gofmt -d -e .) - fi - - run: - name: "Run go vet" - command: > - if [[ << parameters.version >> = "latest" ]]; then - go vet -v ./... - fi - - run: - name: "Run go test (+ race detector)" - command: > - go test -v -race ./... - -workflows: - tests: - jobs: - - test: - matrix: - parameters: - version: ["latest", "1.15", "1.14", "1.13", "1.12", "1.11"] diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2940ec9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +; https://editorconfig.org/ + +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] +indent_style = tab +indent_size = 4 + +[*.md] +indent_size = 4 +trim_trailing_whitespace = false + +eclint_indent_style = unset diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 12440bf..0000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Config for https://github.com/apps/release-drafter -template: | - - - - ### CHANGELOG - - $CHANGES diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index de8a678..0000000 --- a/.github/stale.yml +++ /dev/null @@ -1,12 +0,0 @@ -daysUntilStale: 60 -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - v2 - - needs-review - - work-required -staleLabel: stale -markComment: > - This issue has been automatically marked as stale because it hasn't seen - a recent update. It'll be automatically closed in a few days. -closeComment: false diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml new file mode 100644 index 0000000..5f56d21 --- /dev/null +++ b/.github/workflows/issues.yml @@ -0,0 +1,20 @@ +# Add all the issues created to the project. +name: Add issue or pull request to Project + +on: + issues: + types: + - opened + pull_request: + types: + - opened + +jobs: + add-to-project: + runs-on: ubuntu-latest + steps: + - name: Add issue to project + uses: actions/add-to-project@v0.5.0 + with: + project-url: https://github.com/orgs/gorilla/projects/4 + github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..f2e4b4d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,55 @@ +name: CI +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + +jobs: + verify-and-test: + strategy: + matrix: + go: ['1.19','1.20'] + os: [ubuntu-latest, macos-latest, windows-latest] + fail-fast: true + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GolangCI-Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.53 + args: --timeout=5m + + - name: Run GoSec + if: matrix.os == 'ubuntu-latest' + uses: securego/gosec@master + with: + args: ./... + + - name: Run GoVulnCheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: ${{ matrix.go }} + go-package: ./... + + - name: Run Tests + run: go test -race -cover -coverprofile=coverage -covermode=atomic -v ./... + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./coverage diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..84039fe --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +coverage.coverprofile diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 1e3e7ac..0000000 --- a/AUTHORS +++ /dev/null @@ -1,43 +0,0 @@ -# This is the official list of gorilla/sessions authors for copyright purposes. -# -# Please keep the list sorted. - -Ahmadreza Zibaei -Anton Lindström -Brian Jones -Collin Stedman -Deniz Eren -Dmitry Chestnykh -Dustin Oprea -Egon Elbre -enumappstore -Geofrey Ernest -Google LLC (https://opensource.google.com/) -Jerry Saravia -Jonathan Gillham -Justin Clift -Justin Hellings -Kamil Kisiel -Keiji Yoshida -kliron -Kshitij Saraogi -Lauris BH -Lukas Rist -Mark Dain -Matt Ho -Matt Silverlock -Mattias Wadman -Michael Schuett -Michael Stapelberg -Mirco Zeiss -moraes -nvcnvn -pappz -Pontus Leitzler -QuaSoft -rcadena -rodrigo moraes -Shawn Smith -Taylor Hurt -Tortuoise -Vitor De Mario diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ac37ffd --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '') +GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +GO_SEC=$(shell which gosec 2> /dev/null || echo '') +GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest + +GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '') +GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest + +.PHONY: golangci-lint +golangci-lint: + $(if $(GO_LINT), ,go install $(GO_LINT_URI)) + @echo "##### Running golangci-lint" + golangci-lint run -v + +.PHONY: gosec +gosec: + $(if $(GO_SEC), ,go install $(GO_SEC_URI)) + @echo "##### Running gosec" + gosec ./... + +.PHONY: govulncheck +govulncheck: + $(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI)) + @echo "##### Running govulncheck" + govulncheck ./... + +.PHONY: verify +verify: golangci-lint gosec govulncheck + +.PHONY: test +test: + @echo "##### Running tests" + go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./... diff --git a/README.md b/README.md index c44bdd4..1fb27b3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # sessions -[![GoDoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![Build Status](https://travis-ci.org/gorilla/sessions.svg?branch=master)](https://travis-ci.org/gorilla/sessions) -[![Sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) +![testing](https://github.com/gorilla/sessions/actions/workflows/test.yml/badge.svg) +[![codecov](https://codecov.io/github/gorilla/sessions/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/sessions) +[![godoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) +[![sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) gorilla/sessions provides cookie and filesystem sessions and infrastructure for diff --git a/go.mod b/go.mod index 9028bcf..9032b13 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/gorilla/sessions +go 1.19 + require github.com/gorilla/securecookie v1.1.1 diff --git a/sessions_test.go b/sessions_test.go index 0b84fbc..ddba006 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -75,7 +75,6 @@ func TestFlashes(t *testing.T) { req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) req.Header.Add("Cookie", cookies[0]) - rsp = NewRecorder() // Get a session. if session, err = store.Get(req, "session-key"); err != nil { t.Fatalf("Error getting session: %v", err) @@ -135,7 +134,6 @@ func TestFlashes(t *testing.T) { req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) req.Header.Add("Cookie", cookies[0]) - rsp = NewRecorder() // Get a session. if session, err = store.Get(req, "session-key"); err != nil { t.Fatalf("Error getting session: %v", err) diff --git a/store.go b/store.go index eb45afb..7b6c5ec 100644 --- a/store.go +++ b/store.go @@ -6,7 +6,6 @@ package sessions import ( "encoding/base32" - "io/ioutil" "net/http" "os" "path/filepath" @@ -261,7 +260,7 @@ func (s *FilesystemStore) save(session *Session) error { filename := filepath.Join(s.path, "session_"+session.ID) fileMutex.Lock() defer fileMutex.Unlock() - return ioutil.WriteFile(filename, []byte(encoded), 0600) + return os.WriteFile(filename, []byte(encoded), 0600) } // load reads a file and decodes its content into session.Values. @@ -269,7 +268,7 @@ func (s *FilesystemStore) load(session *Session) error { filename := filepath.Join(s.path, "session_"+session.ID) fileMutex.RLock() defer fileMutex.RUnlock() - fdata, err := ioutil.ReadFile(filename) + fdata, err := os.ReadFile(filepath.Clean(filename)) if err != nil { return err } From 070f6e732a4441f5301e1d44af1f73e68cab994d Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Tue, 25 Jul 2023 02:02:11 -0400 Subject: [PATCH 104/121] Update LICENSE (#264) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 6903df6..bb9d80b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved. +Copyright (c) 2023 The Gorilla Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are From 1a1e42e73b074121224de0e386b5291a97622a61 Mon Sep 17 00:00:00 2001 From: Apoorva Jagtap <35304110+apoorvajagtap@users.noreply.github.com> Date: Wed, 26 Jul 2023 13:12:22 +0530 Subject: [PATCH 105/121] Add gorilla logo to Readme (#265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1fb27b3..06119bb 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![godoc](https://godoc.org/github.com/gorilla/sessions?status.svg)](https://godoc.org/github.com/gorilla/sessions) [![sourcegraph](https://sourcegraph.com/github.com/gorilla/sessions/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/sessions?badge) +![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5) gorilla/sessions provides cookie and filesystem sessions and infrastructure for custom session backends. From 69327c514c1f898f7bec36af5ffe9f2f18f7c5ae Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Mon, 31 Jul 2023 03:43:45 -0400 Subject: [PATCH 106/121] Update issues.yml (#266) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing Signed-off-by: Corey Daley --- .github/workflows/issues.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index 5f56d21..8be6ced 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -1,13 +1,14 @@ -# Add all the issues created to the project. +# Add issues or pull-requests created to the project. name: Add issue or pull request to Project on: issues: types: - opened - pull_request: + pull_request_target: types: - opened + - reopened jobs: add-to-project: From dd83328c14c8fdd3ce533b409bc760166468ddea Mon Sep 17 00:00:00 2001 From: Marius Orcsik Date: Thu, 17 Aug 2023 19:05:40 +0200 Subject: [PATCH 107/121] =?UTF-8?q?Don't=20propagate=20"not=20exist"=20err?= =?UTF-8?q?or=20if=20trying=20to=20erase=20a=20session=20matchi=E2=80=A6?= =?UTF-8?q?=20(#252)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary of Changes Don't consider "oserror.ErrNotExist" as a failure when trying to erase a session which corresponds to a missing file. Hello, in the current implementation, if a request contains a session token that has been stored in a file that has been deleted and we're trying to erase it using the Options.MaxAge = -1 workaround, the action still fails, because the missing file error gets propagated higher in the stack. This small fix prevents this, and ensures that the session is regenerated. ___ This is a reopen of #237 which was closed by the stale bot. Co-authored-by: Corey Daley --- store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store.go b/store.go index 7b6c5ec..aea37e4 100644 --- a/store.go +++ b/store.go @@ -211,7 +211,7 @@ func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter, session *Session) error { // Delete if max-age is <= 0 if session.Options.MaxAge <= 0 { - if err := s.erase(session); err != nil { + if err := s.erase(session); err != nil && !os.IsNotExist(err) { return err } http.SetCookie(w, NewCookie(session.Name(), "", session.Options)) From 26d95ec758d6625a7e738960a9a0653a7fc71b66 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Wed, 18 Oct 2023 07:16:00 -0400 Subject: [PATCH 108/121] update GitHub workflows (#268) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [x] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [ ] Dependency Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [ ] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [x] `make verify` is passing - [x] `make test` is passing --- .github/workflows/issues.yml | 2 +- .github/workflows/security.yml | 37 ++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 26 +++--------------------- .github/workflows/verify.yml | 32 +++++++++++++++++++++++++++++ go.mod | 2 +- 5 files changed, 74 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/verify.yml diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index 8be6ced..768b05b 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -1,4 +1,4 @@ -# Add issues or pull-requests created to the project. +# Add all the issues created to the project. name: Add issue or pull request to Project on: diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..ff4a613 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,37 @@ +name: Security +on: + push: + branches: + - main + pull_request: + branches: + - main +permissions: + contents: read +jobs: + scan: + strategy: + matrix: + go: ['1.20','1.21'] + fail-fast: true + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GoSec + uses: securego/gosec@master + with: + args: -exclude-dir examples ./... + + - name: Run GoVulnCheck + uses: golang/govulncheck-action@v1 + with: + go-version-input: ${{ matrix.go }} + go-package: ./... diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f2e4b4d..50a3946 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: CI +name: Test on: push: branches: @@ -6,15 +6,13 @@ on: pull_request: branches: - main - permissions: contents: read - jobs: - verify-and-test: + unit: strategy: matrix: - go: ['1.19','1.20'] + go: ['1.20','1.21'] os: [ubuntu-latest, macos-latest, windows-latest] fail-fast: true runs-on: ${{ matrix.os }} @@ -28,24 +26,6 @@ jobs: go-version: ${{ matrix.go }} cache: false - - name: Run GolangCI-Lint - uses: golangci/golangci-lint-action@v3 - with: - version: v1.53 - args: --timeout=5m - - - name: Run GoSec - if: matrix.os == 'ubuntu-latest' - uses: securego/gosec@master - with: - args: ./... - - - name: Run GoVulnCheck - uses: golang/govulncheck-action@v1 - with: - go-version-input: ${{ matrix.go }} - go-package: ./... - - name: Run Tests run: go test -race -cover -coverprofile=coverage -covermode=atomic -v ./... diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml new file mode 100644 index 0000000..a3eb74b --- /dev/null +++ b/.github/workflows/verify.yml @@ -0,0 +1,32 @@ +name: Verify +on: + push: + branches: + - main + pull_request: + branches: + - main +permissions: + contents: read +jobs: + lint: + strategy: + matrix: + go: ['1.20','1.21'] + fail-fast: true + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup Go ${{ matrix.go }} + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + cache: false + + - name: Run GolangCI-Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.53 + args: --timeout=5m diff --git a/go.mod b/go.mod index 9032b13..9ba2c6f 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/gorilla/sessions -go 1.19 +go 1.20 require github.com/gorilla/securecookie v1.1.1 From 3eed1c4ffcde6f23b6f88068c63c1ef6190df331 Mon Sep 17 00:00:00 2001 From: Corey Daley Date: Sat, 4 Nov 2023 22:29:28 -0400 Subject: [PATCH 109/121] bump deps and add vendor dir (#269) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [x] Dependency Update ## Description ## Related Tickets & Documents - Related Issue # - Closes # ## Added/updated tests? - [x] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [x] `make verify` is passing - [x] `make test` is passing --- go.mod | 2 +- go.sum | 5 +- .../gorilla/securecookie/.editorconfig | 20 + .../gorilla/securecookie/.gitignore | 1 + .../github.com/gorilla/securecookie/LICENSE | 27 + .../github.com/gorilla/securecookie/Makefile | 39 ++ .../github.com/gorilla/securecookie/README.md | 144 ++++ vendor/github.com/gorilla/securecookie/doc.go | 61 ++ .../gorilla/securecookie/securecookie.go | 649 ++++++++++++++++++ vendor/modules.txt | 3 + 10 files changed, 948 insertions(+), 3 deletions(-) create mode 100644 vendor/github.com/gorilla/securecookie/.editorconfig create mode 100644 vendor/github.com/gorilla/securecookie/.gitignore create mode 100644 vendor/github.com/gorilla/securecookie/LICENSE create mode 100644 vendor/github.com/gorilla/securecookie/Makefile create mode 100644 vendor/github.com/gorilla/securecookie/README.md create mode 100644 vendor/github.com/gorilla/securecookie/doc.go create mode 100644 vendor/github.com/gorilla/securecookie/securecookie.go create mode 100644 vendor/modules.txt diff --git a/go.mod b/go.mod index 9ba2c6f..64cc6a3 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/gorilla/sessions go 1.20 -require github.com/gorilla/securecookie v1.1.1 +require github.com/gorilla/securecookie v1.1.2 diff --git a/go.sum b/go.sum index e6a7ed5..285ffee 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,3 @@ -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= diff --git a/vendor/github.com/gorilla/securecookie/.editorconfig b/vendor/github.com/gorilla/securecookie/.editorconfig new file mode 100644 index 0000000..2940ec9 --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/.editorconfig @@ -0,0 +1,20 @@ +; https://editorconfig.org/ + +root = true + +[*] +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[{Makefile,go.mod,go.sum,*.go,.gitmodules}] +indent_style = tab +indent_size = 4 + +[*.md] +indent_size = 4 +trim_trailing_whitespace = false + +eclint_indent_style = unset diff --git a/vendor/github.com/gorilla/securecookie/.gitignore b/vendor/github.com/gorilla/securecookie/.gitignore new file mode 100644 index 0000000..84039fe --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/.gitignore @@ -0,0 +1 @@ +coverage.coverprofile diff --git a/vendor/github.com/gorilla/securecookie/LICENSE b/vendor/github.com/gorilla/securecookie/LICENSE new file mode 100644 index 0000000..bb9d80b --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2023 The Gorilla Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/securecookie/Makefile b/vendor/github.com/gorilla/securecookie/Makefile new file mode 100644 index 0000000..2b9008a --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/Makefile @@ -0,0 +1,39 @@ +GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '') +GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +GO_SEC=$(shell which gosec 2> /dev/null || echo '') +GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest + +GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '') +GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest + +.PHONY: golangci-lint +golangci-lint: + $(if $(GO_LINT), ,go install $(GO_LINT_URI)) + @echo "##### Running golangci-lint" + golangci-lint run -v + +.PHONY: gosec +gosec: + $(if $(GO_SEC), ,go install $(GO_SEC_URI)) + @echo "##### Running gosec" + gosec ./... + +.PHONY: govulncheck +govulncheck: + $(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI)) + @echo "##### Running govulncheck" + govulncheck ./... + +.PHONY: verify +verify: golangci-lint gosec govulncheck + +.PHONY: test +test: + @echo "##### Running tests" + go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./... + +.PHONY: fuzz +fuzz: + @echo "##### Running fuzz tests" + go test -v -fuzz FuzzEncodeDecode -fuzztime 60s diff --git a/vendor/github.com/gorilla/securecookie/README.md b/vendor/github.com/gorilla/securecookie/README.md new file mode 100644 index 0000000..c3b9815 --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/README.md @@ -0,0 +1,144 @@ +# gorilla/securecookie + +![testing](https://github.com/gorilla/securecookie/actions/workflows/test.yml/badge.svg) +[![codecov](https://codecov.io/github/gorilla/securecookie/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/securecookie) +[![godoc](https://godoc.org/github.com/gorilla/securecookie?status.svg)](https://godoc.org/github.com/gorilla/securecookie) +[![sourcegraph](https://sourcegraph.com/github.com/gorilla/securecookie/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/securecookie?badge) + +![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5) + +securecookie encodes and decodes authenticated and optionally encrypted +cookie values. + +Secure cookies can't be forged, because their values are validated using HMAC. +When encrypted, the content is also inaccessible to malicious eyes. It is still +recommended that sensitive data not be stored in cookies, and that HTTPS be used +to prevent cookie [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). + +## Examples + +To use it, first create a new SecureCookie instance: + +```go +// Hash keys should be at least 32 bytes long +var hashKey = []byte("very-secret") +// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long. +// Shorter keys may weaken the encryption used. +var blockKey = []byte("a-lot-secret") +var s = securecookie.New(hashKey, blockKey) +``` + +The hashKey is required, used to authenticate the cookie value using HMAC. +It is recommended to use a key with 32 or 64 bytes. + +The blockKey is optional, used to encrypt the cookie value -- set it to nil +to not use encryption. If set, the length must correspond to the block size +of the encryption algorithm. For AES, used by default, valid lengths are +16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. + +Strong keys can be created using the convenience function +`GenerateRandomKey()`. Note that keys created using `GenerateRandomKey()` are not +automatically persisted. New keys will be created when the application is +restarted, and previously issued cookies will not be able to be decoded. + +Once a SecureCookie instance is set, use it to encode a cookie value: + +```go +func SetCookieHandler(w http.ResponseWriter, r *http.Request) { + value := map[string]string{ + "foo": "bar", + } + if encoded, err := s.Encode("cookie-name", value); err == nil { + cookie := &http.Cookie{ + Name: "cookie-name", + Value: encoded, + Path: "/", + Secure: true, + HttpOnly: true, + } + http.SetCookie(w, cookie) + } +} +``` + +Later, use the same SecureCookie instance to decode and validate a cookie +value: + +```go +func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { + if cookie, err := r.Cookie("cookie-name"); err == nil { + value := make(map[string]string) + if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil { + fmt.Fprintf(w, "The value of foo is %q", value["foo"]) + } + } +} +``` + +We stored a map[string]string, but secure cookies can hold any value that +can be encoded using `encoding/gob`. To store custom types, they must be +registered first using gob.Register(). For basic types this is not needed; +it works out of the box. An optional JSON encoder that uses `encoding/json` is +available for types compatible with JSON. + +### Key Rotation +Rotating keys is an important part of any security strategy. The `EncodeMulti` and +`DecodeMulti` functions allow for multiple keys to be rotated in and out. +For example, let's take a system that stores keys in a map: + +```go +// keys stored in a map will not be persisted between restarts +// a more persistent storage should be considered for production applications. +var cookies = map[string]*securecookie.SecureCookie{ + "previous": securecookie.New( + securecookie.GenerateRandomKey(64), + securecookie.GenerateRandomKey(32), + ), + "current": securecookie.New( + securecookie.GenerateRandomKey(64), + securecookie.GenerateRandomKey(32), + ), +} +``` + +Using the current key to encode new cookies: +```go +func SetCookieHandler(w http.ResponseWriter, r *http.Request) { + value := map[string]string{ + "foo": "bar", + } + if encoded, err := securecookie.EncodeMulti("cookie-name", value, cookies["current"]); err == nil { + cookie := &http.Cookie{ + Name: "cookie-name", + Value: encoded, + Path: "/", + } + http.SetCookie(w, cookie) + } +} +``` + +Later, decode cookies. Check against all valid keys: +```go +func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { + if cookie, err := r.Cookie("cookie-name"); err == nil { + value := make(map[string]string) + err = securecookie.DecodeMulti("cookie-name", cookie.Value, &value, cookies["current"], cookies["previous"]) + if err == nil { + fmt.Fprintf(w, "The value of foo is %q", value["foo"]) + } + } +} +``` + +Rotate the keys. This strategy allows previously issued cookies to be valid until the next rotation: +```go +func Rotate(newCookie *securecookie.SecureCookie) { + cookies["previous"] = cookies["current"] + cookies["current"] = newCookie +} +``` + +## License + +BSD licensed. See the LICENSE file for details. diff --git a/vendor/github.com/gorilla/securecookie/doc.go b/vendor/github.com/gorilla/securecookie/doc.go new file mode 100644 index 0000000..ae89408 --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/doc.go @@ -0,0 +1,61 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package securecookie encodes and decodes authenticated and optionally +encrypted cookie values. + +Secure cookies can't be forged, because their values are validated using HMAC. +When encrypted, the content is also inaccessible to malicious eyes. + +To use it, first create a new SecureCookie instance: + + var hashKey = []byte("very-secret") + var blockKey = []byte("a-lot-secret") + var s = securecookie.New(hashKey, blockKey) + +The hashKey is required, used to authenticate the cookie value using HMAC. +It is recommended to use a key with 32 or 64 bytes. + +The blockKey is optional, used to encrypt the cookie value -- set it to nil +to not use encryption. If set, the length must correspond to the block size +of the encryption algorithm. For AES, used by default, valid lengths are +16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. + +Strong keys can be created using the convenience function GenerateRandomKey(). + +Once a SecureCookie instance is set, use it to encode a cookie value: + + func SetCookieHandler(w http.ResponseWriter, r *http.Request) { + value := map[string]string{ + "foo": "bar", + } + if encoded, err := s.Encode("cookie-name", value); err == nil { + cookie := &http.Cookie{ + Name: "cookie-name", + Value: encoded, + Path: "/", + } + http.SetCookie(w, cookie) + } + } + +Later, use the same SecureCookie instance to decode and validate a cookie +value: + + func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { + if cookie, err := r.Cookie("cookie-name"); err == nil { + value := make(map[string]string) + if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil { + fmt.Fprintf(w, "The value of foo is %q", value["foo"]) + } + } + } + +We stored a map[string]string, but secure cookies can hold any value that +can be encoded using encoding/gob. To store custom types, they must be +registered first using gob.Register(). For basic types this is not needed; +it works out of the box. +*/ +package securecookie diff --git a/vendor/github.com/gorilla/securecookie/securecookie.go b/vendor/github.com/gorilla/securecookie/securecookie.go new file mode 100644 index 0000000..4d5ea86 --- /dev/null +++ b/vendor/github.com/gorilla/securecookie/securecookie.go @@ -0,0 +1,649 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package securecookie + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "crypto/subtle" + "encoding/base64" + "encoding/gob" + "encoding/json" + "fmt" + "hash" + "io" + "strconv" + "strings" + "time" +) + +// Error is the interface of all errors returned by functions in this library. +type Error interface { + error + + // IsUsage returns true for errors indicating the client code probably + // uses this library incorrectly. For example, the client may have + // failed to provide a valid hash key, or may have failed to configure + // the Serializer adequately for encoding value. + IsUsage() bool + + // IsDecode returns true for errors indicating that a cookie could not + // be decoded and validated. Since cookies are usually untrusted + // user-provided input, errors of this type should be expected. + // Usually, the proper action is simply to reject the request. + IsDecode() bool + + // IsInternal returns true for unexpected errors occurring in the + // securecookie implementation. + IsInternal() bool + + // Cause, if it returns a non-nil value, indicates that this error was + // propagated from some underlying library. If this method returns nil, + // this error was raised directly by this library. + // + // Cause is provided principally for debugging/logging purposes; it is + // rare that application logic should perform meaningfully different + // logic based on Cause. See, for example, the caveats described on + // (MultiError).Cause(). + Cause() error +} + +// errorType is a bitmask giving the error type(s) of an cookieError value. +type errorType int + +const ( + usageError = errorType(1 << iota) + decodeError + internalError +) + +type cookieError struct { + typ errorType + msg string + cause error +} + +func (e cookieError) IsUsage() bool { return (e.typ & usageError) != 0 } +func (e cookieError) IsDecode() bool { return (e.typ & decodeError) != 0 } +func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 } + +func (e cookieError) Cause() error { return e.cause } + +func (e cookieError) Error() string { + parts := []string{"securecookie: "} + if e.msg == "" { + parts = append(parts, "error") + } else { + parts = append(parts, e.msg) + } + if c := e.Cause(); c != nil { + parts = append(parts, " - caused by: ", c.Error()) + } + return strings.Join(parts, "") +} + +var ( + errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"} + + errNoCodecs = cookieError{typ: usageError, msg: "no codecs provided"} + errHashKeyNotSet = cookieError{typ: usageError, msg: "hash key is not set"} + errBlockKeyNotSet = cookieError{typ: usageError, msg: "block key is not set"} + errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"} + + errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"} + errTimestampInvalid = cookieError{typ: decodeError, msg: "invalid timestamp"} + errTimestampTooNew = cookieError{typ: decodeError, msg: "timestamp is too new"} + errTimestampExpired = cookieError{typ: decodeError, msg: "expired timestamp"} + errDecryptionFailed = cookieError{typ: decodeError, msg: "the value could not be decrypted"} + errValueNotByte = cookieError{typ: decodeError, msg: "value not a []byte."} + errValueNotBytePtr = cookieError{typ: decodeError, msg: "value not a pointer to []byte."} + + // ErrMacInvalid indicates that cookie decoding failed because the HMAC + // could not be extracted and verified. Direct use of this error + // variable is deprecated; it is public only for legacy compatibility, + // and may be privatized in the future, as it is rarely useful to + // distinguish between this error and other Error implementations. + ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"} +) + +// Codec defines an interface to encode and decode cookie values. +type Codec interface { + Encode(name string, value interface{}) (string, error) + Decode(name, value string, dst interface{}) error +} + +// New returns a new SecureCookie. +// +// hashKey is required, used to authenticate values using HMAC. Create it using +// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes. +// +// blockKey is optional, used to encrypt values. Create it using +// GenerateRandomKey(). The key length must correspond to the key size +// of the encryption algorithm. For AES, used by default, valid lengths are +// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. +// The default encoder used for cookie serialization is encoding/gob. +// +// Note that keys created using GenerateRandomKey() are not automatically +// persisted. New keys will be created when the application is restarted, and +// previously issued cookies will not be able to be decoded. +func New(hashKey, blockKey []byte) *SecureCookie { + s := &SecureCookie{ + hashKey: hashKey, + blockKey: blockKey, + hashFunc: sha256.New, + maxAge: 86400 * 30, + maxLength: 4096, + sz: GobEncoder{}, + } + if len(hashKey) == 0 { + s.err = errHashKeyNotSet + } + if blockKey != nil { + s.BlockFunc(aes.NewCipher) + } + return s +} + +// SecureCookie encodes and decodes authenticated and optionally encrypted +// cookie values. +type SecureCookie struct { + hashKey []byte + hashFunc func() hash.Hash + blockKey []byte + block cipher.Block + maxLength int + maxAge int64 + minAge int64 + err error + sz Serializer + // For testing purposes, the function that returns the current timestamp. + // If not set, it will use time.Now().UTC().Unix(). + timeFunc func() int64 +} + +// Serializer provides an interface for providing custom serializers for cookie +// values. +type Serializer interface { + Serialize(src interface{}) ([]byte, error) + Deserialize(src []byte, dst interface{}) error +} + +// GobEncoder encodes cookie values using encoding/gob. This is the simplest +// encoder and can handle complex types via gob.Register. +type GobEncoder struct{} + +// JSONEncoder encodes cookie values using encoding/json. Users who wish to +// encode complex types need to satisfy the json.Marshaller and +// json.Unmarshaller interfaces. +type JSONEncoder struct{} + +// NopEncoder does not encode cookie values, and instead simply accepts a []byte +// (as an interface{}) and returns a []byte. This is particularly useful when +// you encoding an object upstream and do not wish to re-encode it. +type NopEncoder struct{} + +// MaxLength restricts the maximum length, in bytes, for the cookie value. +// +// Default is 4096, which is the maximum value accepted by Internet Explorer. +func (s *SecureCookie) MaxLength(value int) *SecureCookie { + s.maxLength = value + return s +} + +// MaxAge restricts the maximum age, in seconds, for the cookie value. +// +// Default is 86400 * 30. Set it to 0 for no restriction. +func (s *SecureCookie) MaxAge(value int) *SecureCookie { + s.maxAge = int64(value) + return s +} + +// MinAge restricts the minimum age, in seconds, for the cookie value. +// +// Default is 0 (no restriction). +func (s *SecureCookie) MinAge(value int) *SecureCookie { + s.minAge = int64(value) + return s +} + +// HashFunc sets the hash function used to create HMAC. +// +// Default is crypto/sha256.New. +func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie { + s.hashFunc = f + return s +} + +// BlockFunc sets the encryption function used to create a cipher.Block. +// +// Default is crypto/aes.New. +func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie { + if s.blockKey == nil { + s.err = errBlockKeyNotSet + } else if block, err := f(s.blockKey); err == nil { + s.block = block + } else { + s.err = cookieError{cause: err, typ: usageError} + } + return s +} + +// Encoding sets the encoding/serialization method for cookies. +// +// Default is encoding/gob. To encode special structures using encoding/gob, +// they must be registered first using gob.Register(). +func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie { + s.sz = sz + + return s +} + +// Encode encodes a cookie value. +// +// It serializes, optionally encrypts, signs with a message authentication code, +// and finally encodes the value. +// +// The name argument is the cookie name. It is stored with the encoded value. +// The value argument is the value to be encoded. It can be any value that can +// be encoded using the currently selected serializer; see SetSerializer(). +// +// It is the client's responsibility to ensure that value, when encoded using +// the current serialization/encryption settings on s and then base64-encoded, +// is shorter than the maximum permissible length. +func (s *SecureCookie) Encode(name string, value interface{}) (string, error) { + if s.err != nil { + return "", s.err + } + if s.hashKey == nil { + s.err = errHashKeyNotSet + return "", s.err + } + var err error + var b []byte + // 1. Serialize. + if b, err = s.sz.Serialize(value); err != nil { + return "", cookieError{cause: err, typ: usageError} + } + // 2. Encrypt (optional). + if s.block != nil { + if b, err = encrypt(s.block, b); err != nil { + return "", cookieError{cause: err, typ: usageError} + } + } + b = encode(b) + // 3. Create MAC for "name|date|value". Extra pipe to be used later. + b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b)) + mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1]) + // Append mac, remove name. + b = append(b, mac...)[len(name)+1:] + // 4. Encode to base64. + b = encode(b) + // 5. Check length. + if s.maxLength != 0 && len(b) > s.maxLength { + return "", fmt.Errorf("%s: %d", errEncodedValueTooLong, len(b)) + } + // Done. + return string(b), nil +} + +// Decode decodes a cookie value. +// +// It decodes, verifies a message authentication code, optionally decrypts and +// finally deserializes the value. +// +// The name argument is the cookie name. It must be the same name used when +// it was stored. The value argument is the encoded cookie value. The dst +// argument is where the cookie will be decoded. It must be a pointer. +func (s *SecureCookie) Decode(name, value string, dst interface{}) error { + if s.err != nil { + return s.err + } + if s.hashKey == nil { + s.err = errHashKeyNotSet + return s.err + } + // 1. Check length. + if s.maxLength != 0 && len(value) > s.maxLength { + return fmt.Errorf("%s: %d", errValueToDecodeTooLong, len(value)) + } + // 2. Decode from base64. + b, err := decode([]byte(value)) + if err != nil { + return err + } + // 3. Verify MAC. Value is "date|value|mac". + parts := bytes.SplitN(b, []byte("|"), 3) + if len(parts) != 3 { + return ErrMacInvalid + } + h := hmac.New(s.hashFunc, s.hashKey) + b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...) + if err = verifyMac(h, b, parts[2]); err != nil { + return err + } + // 4. Verify date ranges. + var t1 int64 + if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil { + return errTimestampInvalid + } + t2 := s.timestamp() + if s.minAge != 0 && t1 > t2-s.minAge { + return errTimestampTooNew + } + if s.maxAge != 0 && t1 < t2-s.maxAge { + return errTimestampExpired + } + // 5. Decrypt (optional). + b, err = decode(parts[1]) + if err != nil { + return err + } + if s.block != nil { + if b, err = decrypt(s.block, b); err != nil { + return err + } + } + // 6. Deserialize. + if err = s.sz.Deserialize(b, dst); err != nil { + return cookieError{cause: err, typ: decodeError} + } + // Done. + return nil +} + +// timestamp returns the current timestamp, in seconds. +// +// For testing purposes, the function that generates the timestamp can be +// overridden. If not set, it will return time.Now().UTC().Unix(). +func (s *SecureCookie) timestamp() int64 { + if s.timeFunc == nil { + return time.Now().UTC().Unix() + } + return s.timeFunc() +} + +// Authentication ------------------------------------------------------------- + +// createMac creates a message authentication code (MAC). +func createMac(h hash.Hash, value []byte) []byte { + h.Write(value) + return h.Sum(nil) +} + +// verifyMac verifies that a message authentication code (MAC) is valid. +func verifyMac(h hash.Hash, value []byte, mac []byte) error { + mac2 := createMac(h, value) + // Check that both MACs are of equal length, as subtle.ConstantTimeCompare + // does not do this prior to Go 1.4. + if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 { + return nil + } + return ErrMacInvalid +} + +// Encryption ----------------------------------------------------------------- + +// encrypt encrypts a value using the given block in counter mode. +// +// A random initialization vector ( https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_(IV) ) with the length of the +// block size is prepended to the resulting ciphertext. +func encrypt(block cipher.Block, value []byte) ([]byte, error) { + iv := GenerateRandomKey(block.BlockSize()) + if iv == nil { + return nil, errGeneratingIV + } + // Encrypt it. + stream := cipher.NewCTR(block, iv) + stream.XORKeyStream(value, value) + // Return iv + ciphertext. + return append(iv, value...), nil +} + +// decrypt decrypts a value using the given block in counter mode. +// +// The value to be decrypted must be prepended by a initialization vector +// ( https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_(IV) ) with the length of the block size. +func decrypt(block cipher.Block, value []byte) ([]byte, error) { + size := block.BlockSize() + if len(value) > size { + // Extract iv. + iv := value[:size] + // Extract ciphertext. + value = value[size:] + // Decrypt it. + stream := cipher.NewCTR(block, iv) + stream.XORKeyStream(value, value) + return value, nil + } + return nil, errDecryptionFailed +} + +// Serialization -------------------------------------------------------------- + +// Serialize encodes a value using gob. +func (e GobEncoder) Serialize(src interface{}) ([]byte, error) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + if err := enc.Encode(src); err != nil { + return nil, cookieError{cause: err, typ: usageError} + } + return buf.Bytes(), nil +} + +// Deserialize decodes a value using gob. +func (e GobEncoder) Deserialize(src []byte, dst interface{}) error { + dec := gob.NewDecoder(bytes.NewBuffer(src)) + if err := dec.Decode(dst); err != nil { + return cookieError{cause: err, typ: decodeError} + } + return nil +} + +// Serialize encodes a value using encoding/json. +func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) { + buf := new(bytes.Buffer) + enc := json.NewEncoder(buf) + if err := enc.Encode(src); err != nil { + return nil, cookieError{cause: err, typ: usageError} + } + return buf.Bytes(), nil +} + +// Deserialize decodes a value using encoding/json. +func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error { + dec := json.NewDecoder(bytes.NewReader(src)) + if err := dec.Decode(dst); err != nil { + return cookieError{cause: err, typ: decodeError} + } + return nil +} + +// Serialize passes a []byte through as-is. +func (e NopEncoder) Serialize(src interface{}) ([]byte, error) { + if b, ok := src.([]byte); ok { + return b, nil + } + + return nil, errValueNotByte +} + +// Deserialize passes a []byte through as-is. +func (e NopEncoder) Deserialize(src []byte, dst interface{}) error { + if dat, ok := dst.(*[]byte); ok { + *dat = src + return nil + } + return errValueNotBytePtr +} + +// Encoding ------------------------------------------------------------------- + +// encode encodes a value using base64. +func encode(value []byte) []byte { + encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value))) + base64.URLEncoding.Encode(encoded, value) + return encoded +} + +// decode decodes a cookie using base64. +func decode(value []byte) ([]byte, error) { + decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value))) + b, err := base64.URLEncoding.Decode(decoded, value) + if err != nil { + return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"} + } + return decoded[:b], nil +} + +// Helpers -------------------------------------------------------------------- + +// GenerateRandomKey creates a random key with the given length in bytes. +// On failure, returns nil. +// +// Note that keys created using `GenerateRandomKey()` are not automatically +// persisted. New keys will be created when the application is restarted, and +// previously issued cookies will not be able to be decoded. +// +// Callers should explicitly check for the possibility of a nil return, treat +// it as a failure of the system random number generator, and not continue. +func GenerateRandomKey(length int) []byte { + k := make([]byte, length) + if _, err := io.ReadFull(rand.Reader, k); err != nil { + return nil + } + return k +} + +// CodecsFromPairs returns a slice of SecureCookie instances. +// +// It is a convenience function to create a list of codecs for key rotation. Note +// that the generated Codecs will have the default options applied: callers +// should iterate over each Codec and type-assert the underlying *SecureCookie to +// change these. +// +// Example: +// +// codecs := securecookie.CodecsFromPairs( +// []byte("new-hash-key"), +// []byte("new-block-key"), +// []byte("old-hash-key"), +// []byte("old-block-key"), +// ) +// +// // Modify each instance. +// for _, s := range codecs { +// if cookie, ok := s.(*securecookie.SecureCookie); ok { +// cookie.MaxAge(86400 * 7) +// cookie.SetSerializer(securecookie.JSONEncoder{}) +// cookie.HashFunc(sha512.New512_256) +// } +// } +func CodecsFromPairs(keyPairs ...[]byte) []Codec { + codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2) + for i := 0; i < len(keyPairs); i += 2 { + var blockKey []byte + if i+1 < len(keyPairs) { + blockKey = keyPairs[i+1] + } + codecs[i/2] = New(keyPairs[i], blockKey) + } + return codecs +} + +// EncodeMulti encodes a cookie value using a group of codecs. +// +// The codecs are tried in order. Multiple codecs are accepted to allow +// key rotation. +// +// On error, may return a MultiError. +func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) { + if len(codecs) == 0 { + return "", errNoCodecs + } + + var errors MultiError + for _, codec := range codecs { + encoded, err := codec.Encode(name, value) + if err == nil { + return encoded, nil + } + errors = append(errors, err) + } + return "", errors +} + +// DecodeMulti decodes a cookie value using a group of codecs. +// +// The codecs are tried in order. Multiple codecs are accepted to allow +// key rotation. +// +// On error, may return a MultiError. +func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error { + if len(codecs) == 0 { + return errNoCodecs + } + + var errors MultiError + for _, codec := range codecs { + err := codec.Decode(name, value, dst) + if err == nil { + return nil + } + errors = append(errors, err) + } + return errors +} + +// MultiError groups multiple errors. +type MultiError []error + +func (m MultiError) IsUsage() bool { return m.any(func(e Error) bool { return e.IsUsage() }) } +func (m MultiError) IsDecode() bool { return m.any(func(e Error) bool { return e.IsDecode() }) } +func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) } + +// Cause returns nil for MultiError; there is no unique underlying cause in the +// general case. +// +// Note: we could conceivably return a non-nil Cause only when there is exactly +// one child error with a Cause. However, it would be brittle for client code +// to rely on the arity of causes inside a MultiError, so we have opted not to +// provide this functionality. Clients which really wish to access the Causes +// of the underlying errors are free to iterate through the errors themselves. +func (m MultiError) Cause() error { return nil } + +func (m MultiError) Error() string { + s, n := "", 0 + for _, e := range m { + if e != nil { + if n == 0 { + s = e.Error() + } + n++ + } + } + switch n { + case 0: + return "(0 errors)" + case 1: + return s + case 2: + return s + " (and 1 other error)" + } + return fmt.Sprintf("%s (and %d other errors)", s, n-1) +} + +// any returns true if any element of m is an Error for which pred returns true. +func (m MultiError) any(pred func(Error) bool) bool { + for _, e := range m { + if ourErr, ok := e.(Error); ok && pred(ourErr) { + return true + } + } + return false +} diff --git a/vendor/modules.txt b/vendor/modules.txt new file mode 100644 index 0000000..6224b61 --- /dev/null +++ b/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/gorilla/securecookie v1.1.2 +## explicit; go 1.20 +github.com/gorilla/securecookie From e308bfd8bdcba01bc953589c6da7e5fd24fabda7 Mon Sep 17 00:00:00 2001 From: Joe <875022+moloch--@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:06:43 -0700 Subject: [PATCH 110/121] Fix path traversal (#274) ## What type of PR is this? (check all applicable) - [ ] Refactor - [ ] Feature - [x] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [ ] Dependency Update ## Added/updated tests? - [ ] Yes - [x] No, and this is why: _no additional tests needed, small fix_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [x] `make test` is passing ``` PASS coverage: 78.1% of statements ok github.com/gorilla/sessions 1.155s coverage: 78.1% of statements ``` --- store.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/store.go b/store.go index aea37e4..68d4ce5 100644 --- a/store.go +++ b/store.go @@ -14,6 +14,11 @@ import ( "github.com/gorilla/securecookie" ) +const ( + // File name prefix for session files. + sessionFilePrefix = "session_" +) + // Store is an interface for custom session stores. // // See CookieStore and FilesystemStore for examples. @@ -257,7 +262,7 @@ func (s *FilesystemStore) save(session *Session) error { if err != nil { return err } - filename := filepath.Join(s.path, "session_"+session.ID) + filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) fileMutex.Lock() defer fileMutex.Unlock() return os.WriteFile(filename, []byte(encoded), 0600) @@ -265,7 +270,7 @@ func (s *FilesystemStore) save(session *Session) error { // load reads a file and decodes its content into session.Values. func (s *FilesystemStore) load(session *Session) error { - filename := filepath.Join(s.path, "session_"+session.ID) + filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) fileMutex.RLock() defer fileMutex.RUnlock() fdata, err := os.ReadFile(filepath.Clean(filename)) @@ -281,7 +286,7 @@ func (s *FilesystemStore) load(session *Session) error { // delete session file func (s *FilesystemStore) erase(session *Session) error { - filename := filepath.Join(s.path, "session_"+session.ID) + filename := filepath.Join(s.path, sessionFilePrefix+filepath.Base(session.ID)) fileMutex.RLock() defer fileMutex.RUnlock() From bdabf0ac29ab2354c0674cb0dcd3a4f31f53589d Mon Sep 17 00:00:00 2001 From: kashishbehl <104421875+kashishbehl@users.noreply.github.com> Date: Sat, 4 May 2024 21:31:06 +0530 Subject: [PATCH 111/121] #272: feat: Add support for paritioned attribute in cookies as per chrome 3rd party cookie phaseout (#273) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What type of PR is this? (check all applicable) - [ ] Refactor - [x] Feature - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update - [ ] Go Version Update - [ ] Dependency Update ## Description The PR contains the change to add Partitioned attribute in the cookies. As chrome will be deprecating support for 3rd Party cookies, we need to add support for CHIPS to make cookies partitioned to the website. ## Related Tickets & Documents - Related Issue # - Closes #272 ## Added/updated tests? - [x] Yes - [ ] No, and this is why: _please replace this line with details on why tests have not been included_ - [ ] I need help with writing tests ## Run verifications and test - [ ] `make verify` is passing - [ ] `make test` is passing --- cookie.go | 1 + cookie_go111.go | 1 + cookie_test.go | 20 ++++++++++++------- options.go | 1 + options_go111.go | 1 + .../github.com/gorilla/securecookie/README.md | 1 + 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cookie.go b/cookie.go index 6612662..fa70e7c 100644 --- a/cookie.go +++ b/cookie.go @@ -15,6 +15,7 @@ func newCookieFromOptions(name, value string, options *Options) *http.Cookie { MaxAge: options.MaxAge, Secure: options.Secure, HttpOnly: options.HttpOnly, + Partitioned: options.Partitioned, } } diff --git a/cookie_go111.go b/cookie_go111.go index 9b58828..d5e9e62 100644 --- a/cookie_go111.go +++ b/cookie_go111.go @@ -16,6 +16,7 @@ func newCookieFromOptions(name, value string, options *Options) *http.Cookie { Secure: options.Secure, HttpOnly: options.HttpOnly, SameSite: options.SameSite, + Partitioned: options.Partitioned, } } diff --git a/cookie_test.go b/cookie_test.go index acb4efb..8e02fbc 100644 --- a/cookie_test.go +++ b/cookie_test.go @@ -14,14 +14,16 @@ func TestNewCookieFromOptions(t *testing.T) { maxAge int secure bool httpOnly bool + partitioned bool }{ - {"", "bar", "/foo/bar", "foo.example.com", 3600, true, true}, - {"foo", "", "/foo/bar", "foo.example.com", 3600, true, true}, - {"foo", "bar", "", "foo.example.com", 3600, true, true}, - {"foo", "bar", "/foo/bar", "", 3600, true, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 0, true, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 3600, false, true}, - {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, false}, + {"", "bar", "/foo/bar", "foo.example.com", 3600, true, true, true}, + {"foo", "", "/foo/bar", "foo.example.com", 3600, true, true, true}, + {"foo", "bar", "", "foo.example.com", 3600, true, true, true}, + {"foo", "bar", "/foo/bar", "", 3600, true, true, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 0, true, true, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 3600, false, true, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, false, true}, + {"foo", "bar", "/foo/bar", "foo.example.com", 3600, true, true, false}, } for i, v := range tests { options := &Options{ @@ -30,6 +32,7 @@ func TestNewCookieFromOptions(t *testing.T) { MaxAge: v.maxAge, Secure: v.secure, HttpOnly: v.httpOnly, + Partitioned: v.partitioned, } cookie := newCookieFromOptions(v.name, v.value, options) if cookie.Name != v.name { @@ -53,5 +56,8 @@ func TestNewCookieFromOptions(t *testing.T) { if cookie.HttpOnly != v.httpOnly { t.Fatalf("%v: bad cookie httpOnly: got %v, want %v", i+1, cookie.HttpOnly, v.httpOnly) } + if cookie.Partitioned != v.partitioned { + t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) + } } } diff --git a/options.go b/options.go index d33d076..ec07068 100644 --- a/options.go +++ b/options.go @@ -16,4 +16,5 @@ type Options struct { MaxAge int Secure bool HttpOnly bool + Partitioned bool } diff --git a/options_go111.go b/options_go111.go index af9cdf0..3214990 100644 --- a/options_go111.go +++ b/options_go111.go @@ -18,6 +18,7 @@ type Options struct { MaxAge int Secure bool HttpOnly bool + Partitioned bool // Defaults to http.SameSiteDefaultMode SameSite http.SameSite } diff --git a/vendor/github.com/gorilla/securecookie/README.md b/vendor/github.com/gorilla/securecookie/README.md index c3b9815..62e4ec7 100644 --- a/vendor/github.com/gorilla/securecookie/README.md +++ b/vendor/github.com/gorilla/securecookie/README.md @@ -55,6 +55,7 @@ func SetCookieHandler(w http.ResponseWriter, r *http.Request) { Path: "/", Secure: true, HttpOnly: true, + Partitioned: true, } http.SetCookie(w, cookie) } From ef99c782e9aae430b326a614151c983dcd7c2c1d Mon Sep 17 00:00:00 2001 From: Bharat Rajani Date: Sat, 15 Jun 2024 07:59:33 +0530 Subject: [PATCH 112/121] fix(cookie): Add default samesite (#276) Sets the SameSite cookie attribute to None in the Set-Cookie header. The SameSite=None value provides a reasonable balance between security and usability for websites. This also requires setting Secure=True by default. Reference: https://owasp.org/www-community/SameSite Related Tickets & Documents - Related Issue # https://github.com/gorilla/sessions/issues/256 - Closes https://github.com/gorilla/sessions/issues/256 --- cookie_go111_test.go | 1 + sessions_test.go | 9 +++++++++ store.go | 6 ++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cookie_go111_test.go b/cookie_go111_test.go index e6137c7..2fad2e7 100644 --- a/cookie_go111_test.go +++ b/cookie_go111_test.go @@ -17,6 +17,7 @@ func TestNewCookieFromOptionsSameSite(t *testing.T) { {http.SameSiteDefaultMode}, {http.SameSiteLaxMode}, {http.SameSiteStrictMode}, + {http.SameSiteNoneMode}, } for i, v := range tests { options := &Options{ diff --git a/sessions_test.go b/sessions_test.go index ddba006..9476c22 100644 --- a/sessions_test.go +++ b/sessions_test.go @@ -9,6 +9,7 @@ import ( "encoding/gob" "net/http" "net/http/httptest" + "strings" "testing" ) @@ -39,6 +40,10 @@ func TestFlashes(t *testing.T) { store := NewCookieStore([]byte("secret-key")) + if store.Options.SameSite != http.SameSiteNoneMode { + t.Fatalf("cookie store error: default same site is not set to None") + } + // Round 1 ---------------------------------------------------------------- req, _ = http.NewRequest("GET", "http://localhost:8080/", nil) @@ -67,6 +72,10 @@ func TestFlashes(t *testing.T) { t.Fatal("No cookies. Header:", hdr) } + if !strings.Contains(cookies[0], "SameSite=None") || !strings.Contains(cookies[0], "Secure") { + t.Fatal("Set-Cookie does not contains SameSite=None with Secure, cookie string:", cookies[0]) + } + if _, err = store.Get(req, "session:key"); err.Error() != "sessions: invalid character in cookie name: session:key" { t.Fatalf("Expected error due to invalid cookie name") } diff --git a/store.go b/store.go index 68d4ce5..24db822 100644 --- a/store.go +++ b/store.go @@ -54,8 +54,10 @@ func NewCookieStore(keyPairs ...[]byte) *CookieStore { cs := &CookieStore{ Codecs: securecookie.CodecsFromPairs(keyPairs...), Options: &Options{ - Path: "/", - MaxAge: 86400 * 30, + Path: "/", + MaxAge: 86400 * 30, + SameSite: http.SameSiteNoneMode, + Secure: true, }, } From c373b3e334dc26e3e513168292d784d7773f7d27 Mon Sep 17 00:00:00 2001 From: Muhammed Bacalan Date: Sat, 15 Jun 2024 04:32:13 +0200 Subject: [PATCH 113/121] Fix gorillatoolkit link in README.md (#278) Fix "more examples" link not taking you to where more examples are at Related Tickets & Documents: - Closes #246 Signed-off-by: Muhammed Bacalan --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 06119bb..3aef6a4 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,7 @@ secret key used to authenticate the session. Inside the handler, we call some session values in session.Values, which is a `map[interface{}]interface{}`. And finally we call `session.Save()` to save the session in the response. -More examples are available [on the Gorilla -website](https://www.gorillatoolkit.org/pkg/sessions). +More examples are available at [package documentation](https://pkg.go.dev/github.com/gorilla/sessions). ## Store Implementations From 8e2d54718e949e895e6a0268e9d4df396b3fbe06 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sat, 15 Jun 2024 02:39:54 +0000 Subject: [PATCH 114/121] chore(go): Remove vendored dependencies --- .../gorilla/securecookie/.editorconfig | 20 - .../gorilla/securecookie/.gitignore | 1 - .../github.com/gorilla/securecookie/LICENSE | 27 - .../github.com/gorilla/securecookie/Makefile | 39 -- .../github.com/gorilla/securecookie/README.md | 145 ---- vendor/github.com/gorilla/securecookie/doc.go | 61 -- .../gorilla/securecookie/securecookie.go | 649 ------------------ vendor/modules.txt | 3 - 8 files changed, 945 deletions(-) delete mode 100644 vendor/github.com/gorilla/securecookie/.editorconfig delete mode 100644 vendor/github.com/gorilla/securecookie/.gitignore delete mode 100644 vendor/github.com/gorilla/securecookie/LICENSE delete mode 100644 vendor/github.com/gorilla/securecookie/Makefile delete mode 100644 vendor/github.com/gorilla/securecookie/README.md delete mode 100644 vendor/github.com/gorilla/securecookie/doc.go delete mode 100644 vendor/github.com/gorilla/securecookie/securecookie.go delete mode 100644 vendor/modules.txt diff --git a/vendor/github.com/gorilla/securecookie/.editorconfig b/vendor/github.com/gorilla/securecookie/.editorconfig deleted file mode 100644 index 2940ec9..0000000 --- a/vendor/github.com/gorilla/securecookie/.editorconfig +++ /dev/null @@ -1,20 +0,0 @@ -; https://editorconfig.org/ - -root = true - -[*] -insert_final_newline = true -charset = utf-8 -trim_trailing_whitespace = true -indent_style = space -indent_size = 2 - -[{Makefile,go.mod,go.sum,*.go,.gitmodules}] -indent_style = tab -indent_size = 4 - -[*.md] -indent_size = 4 -trim_trailing_whitespace = false - -eclint_indent_style = unset diff --git a/vendor/github.com/gorilla/securecookie/.gitignore b/vendor/github.com/gorilla/securecookie/.gitignore deleted file mode 100644 index 84039fe..0000000 --- a/vendor/github.com/gorilla/securecookie/.gitignore +++ /dev/null @@ -1 +0,0 @@ -coverage.coverprofile diff --git a/vendor/github.com/gorilla/securecookie/LICENSE b/vendor/github.com/gorilla/securecookie/LICENSE deleted file mode 100644 index bb9d80b..0000000 --- a/vendor/github.com/gorilla/securecookie/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2023 The Gorilla Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gorilla/securecookie/Makefile b/vendor/github.com/gorilla/securecookie/Makefile deleted file mode 100644 index 2b9008a..0000000 --- a/vendor/github.com/gorilla/securecookie/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '') -GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest - -GO_SEC=$(shell which gosec 2> /dev/null || echo '') -GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest - -GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '') -GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest - -.PHONY: golangci-lint -golangci-lint: - $(if $(GO_LINT), ,go install $(GO_LINT_URI)) - @echo "##### Running golangci-lint" - golangci-lint run -v - -.PHONY: gosec -gosec: - $(if $(GO_SEC), ,go install $(GO_SEC_URI)) - @echo "##### Running gosec" - gosec ./... - -.PHONY: govulncheck -govulncheck: - $(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI)) - @echo "##### Running govulncheck" - govulncheck ./... - -.PHONY: verify -verify: golangci-lint gosec govulncheck - -.PHONY: test -test: - @echo "##### Running tests" - go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./... - -.PHONY: fuzz -fuzz: - @echo "##### Running fuzz tests" - go test -v -fuzz FuzzEncodeDecode -fuzztime 60s diff --git a/vendor/github.com/gorilla/securecookie/README.md b/vendor/github.com/gorilla/securecookie/README.md deleted file mode 100644 index 62e4ec7..0000000 --- a/vendor/github.com/gorilla/securecookie/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# gorilla/securecookie - -![testing](https://github.com/gorilla/securecookie/actions/workflows/test.yml/badge.svg) -[![codecov](https://codecov.io/github/gorilla/securecookie/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/securecookie) -[![godoc](https://godoc.org/github.com/gorilla/securecookie?status.svg)](https://godoc.org/github.com/gorilla/securecookie) -[![sourcegraph](https://sourcegraph.com/github.com/gorilla/securecookie/-/badge.svg)](https://sourcegraph.com/github.com/gorilla/securecookie?badge) - -![Gorilla Logo](https://github.com/gorilla/.github/assets/53367916/d92caabf-98e0-473e-bfbf-ab554ba435e5) - -securecookie encodes and decodes authenticated and optionally encrypted -cookie values. - -Secure cookies can't be forged, because their values are validated using HMAC. -When encrypted, the content is also inaccessible to malicious eyes. It is still -recommended that sensitive data not be stored in cookies, and that HTTPS be used -to prevent cookie [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). - -## Examples - -To use it, first create a new SecureCookie instance: - -```go -// Hash keys should be at least 32 bytes long -var hashKey = []byte("very-secret") -// Block keys should be 16 bytes (AES-128) or 32 bytes (AES-256) long. -// Shorter keys may weaken the encryption used. -var blockKey = []byte("a-lot-secret") -var s = securecookie.New(hashKey, blockKey) -``` - -The hashKey is required, used to authenticate the cookie value using HMAC. -It is recommended to use a key with 32 or 64 bytes. - -The blockKey is optional, used to encrypt the cookie value -- set it to nil -to not use encryption. If set, the length must correspond to the block size -of the encryption algorithm. For AES, used by default, valid lengths are -16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. - -Strong keys can be created using the convenience function -`GenerateRandomKey()`. Note that keys created using `GenerateRandomKey()` are not -automatically persisted. New keys will be created when the application is -restarted, and previously issued cookies will not be able to be decoded. - -Once a SecureCookie instance is set, use it to encode a cookie value: - -```go -func SetCookieHandler(w http.ResponseWriter, r *http.Request) { - value := map[string]string{ - "foo": "bar", - } - if encoded, err := s.Encode("cookie-name", value); err == nil { - cookie := &http.Cookie{ - Name: "cookie-name", - Value: encoded, - Path: "/", - Secure: true, - HttpOnly: true, - Partitioned: true, - } - http.SetCookie(w, cookie) - } -} -``` - -Later, use the same SecureCookie instance to decode and validate a cookie -value: - -```go -func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { - if cookie, err := r.Cookie("cookie-name"); err == nil { - value := make(map[string]string) - if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil { - fmt.Fprintf(w, "The value of foo is %q", value["foo"]) - } - } -} -``` - -We stored a map[string]string, but secure cookies can hold any value that -can be encoded using `encoding/gob`. To store custom types, they must be -registered first using gob.Register(). For basic types this is not needed; -it works out of the box. An optional JSON encoder that uses `encoding/json` is -available for types compatible with JSON. - -### Key Rotation -Rotating keys is an important part of any security strategy. The `EncodeMulti` and -`DecodeMulti` functions allow for multiple keys to be rotated in and out. -For example, let's take a system that stores keys in a map: - -```go -// keys stored in a map will not be persisted between restarts -// a more persistent storage should be considered for production applications. -var cookies = map[string]*securecookie.SecureCookie{ - "previous": securecookie.New( - securecookie.GenerateRandomKey(64), - securecookie.GenerateRandomKey(32), - ), - "current": securecookie.New( - securecookie.GenerateRandomKey(64), - securecookie.GenerateRandomKey(32), - ), -} -``` - -Using the current key to encode new cookies: -```go -func SetCookieHandler(w http.ResponseWriter, r *http.Request) { - value := map[string]string{ - "foo": "bar", - } - if encoded, err := securecookie.EncodeMulti("cookie-name", value, cookies["current"]); err == nil { - cookie := &http.Cookie{ - Name: "cookie-name", - Value: encoded, - Path: "/", - } - http.SetCookie(w, cookie) - } -} -``` - -Later, decode cookies. Check against all valid keys: -```go -func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { - if cookie, err := r.Cookie("cookie-name"); err == nil { - value := make(map[string]string) - err = securecookie.DecodeMulti("cookie-name", cookie.Value, &value, cookies["current"], cookies["previous"]) - if err == nil { - fmt.Fprintf(w, "The value of foo is %q", value["foo"]) - } - } -} -``` - -Rotate the keys. This strategy allows previously issued cookies to be valid until the next rotation: -```go -func Rotate(newCookie *securecookie.SecureCookie) { - cookies["previous"] = cookies["current"] - cookies["current"] = newCookie -} -``` - -## License - -BSD licensed. See the LICENSE file for details. diff --git a/vendor/github.com/gorilla/securecookie/doc.go b/vendor/github.com/gorilla/securecookie/doc.go deleted file mode 100644 index ae89408..0000000 --- a/vendor/github.com/gorilla/securecookie/doc.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package securecookie encodes and decodes authenticated and optionally -encrypted cookie values. - -Secure cookies can't be forged, because their values are validated using HMAC. -When encrypted, the content is also inaccessible to malicious eyes. - -To use it, first create a new SecureCookie instance: - - var hashKey = []byte("very-secret") - var blockKey = []byte("a-lot-secret") - var s = securecookie.New(hashKey, blockKey) - -The hashKey is required, used to authenticate the cookie value using HMAC. -It is recommended to use a key with 32 or 64 bytes. - -The blockKey is optional, used to encrypt the cookie value -- set it to nil -to not use encryption. If set, the length must correspond to the block size -of the encryption algorithm. For AES, used by default, valid lengths are -16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. - -Strong keys can be created using the convenience function GenerateRandomKey(). - -Once a SecureCookie instance is set, use it to encode a cookie value: - - func SetCookieHandler(w http.ResponseWriter, r *http.Request) { - value := map[string]string{ - "foo": "bar", - } - if encoded, err := s.Encode("cookie-name", value); err == nil { - cookie := &http.Cookie{ - Name: "cookie-name", - Value: encoded, - Path: "/", - } - http.SetCookie(w, cookie) - } - } - -Later, use the same SecureCookie instance to decode and validate a cookie -value: - - func ReadCookieHandler(w http.ResponseWriter, r *http.Request) { - if cookie, err := r.Cookie("cookie-name"); err == nil { - value := make(map[string]string) - if err = s2.Decode("cookie-name", cookie.Value, &value); err == nil { - fmt.Fprintf(w, "The value of foo is %q", value["foo"]) - } - } - } - -We stored a map[string]string, but secure cookies can hold any value that -can be encoded using encoding/gob. To store custom types, they must be -registered first using gob.Register(). For basic types this is not needed; -it works out of the box. -*/ -package securecookie diff --git a/vendor/github.com/gorilla/securecookie/securecookie.go b/vendor/github.com/gorilla/securecookie/securecookie.go deleted file mode 100644 index 4d5ea86..0000000 --- a/vendor/github.com/gorilla/securecookie/securecookie.go +++ /dev/null @@ -1,649 +0,0 @@ -// Copyright 2012 The Gorilla Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package securecookie - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/hmac" - "crypto/rand" - "crypto/sha256" - "crypto/subtle" - "encoding/base64" - "encoding/gob" - "encoding/json" - "fmt" - "hash" - "io" - "strconv" - "strings" - "time" -) - -// Error is the interface of all errors returned by functions in this library. -type Error interface { - error - - // IsUsage returns true for errors indicating the client code probably - // uses this library incorrectly. For example, the client may have - // failed to provide a valid hash key, or may have failed to configure - // the Serializer adequately for encoding value. - IsUsage() bool - - // IsDecode returns true for errors indicating that a cookie could not - // be decoded and validated. Since cookies are usually untrusted - // user-provided input, errors of this type should be expected. - // Usually, the proper action is simply to reject the request. - IsDecode() bool - - // IsInternal returns true for unexpected errors occurring in the - // securecookie implementation. - IsInternal() bool - - // Cause, if it returns a non-nil value, indicates that this error was - // propagated from some underlying library. If this method returns nil, - // this error was raised directly by this library. - // - // Cause is provided principally for debugging/logging purposes; it is - // rare that application logic should perform meaningfully different - // logic based on Cause. See, for example, the caveats described on - // (MultiError).Cause(). - Cause() error -} - -// errorType is a bitmask giving the error type(s) of an cookieError value. -type errorType int - -const ( - usageError = errorType(1 << iota) - decodeError - internalError -) - -type cookieError struct { - typ errorType - msg string - cause error -} - -func (e cookieError) IsUsage() bool { return (e.typ & usageError) != 0 } -func (e cookieError) IsDecode() bool { return (e.typ & decodeError) != 0 } -func (e cookieError) IsInternal() bool { return (e.typ & internalError) != 0 } - -func (e cookieError) Cause() error { return e.cause } - -func (e cookieError) Error() string { - parts := []string{"securecookie: "} - if e.msg == "" { - parts = append(parts, "error") - } else { - parts = append(parts, e.msg) - } - if c := e.Cause(); c != nil { - parts = append(parts, " - caused by: ", c.Error()) - } - return strings.Join(parts, "") -} - -var ( - errGeneratingIV = cookieError{typ: internalError, msg: "failed to generate random iv"} - - errNoCodecs = cookieError{typ: usageError, msg: "no codecs provided"} - errHashKeyNotSet = cookieError{typ: usageError, msg: "hash key is not set"} - errBlockKeyNotSet = cookieError{typ: usageError, msg: "block key is not set"} - errEncodedValueTooLong = cookieError{typ: usageError, msg: "the value is too long"} - - errValueToDecodeTooLong = cookieError{typ: decodeError, msg: "the value is too long"} - errTimestampInvalid = cookieError{typ: decodeError, msg: "invalid timestamp"} - errTimestampTooNew = cookieError{typ: decodeError, msg: "timestamp is too new"} - errTimestampExpired = cookieError{typ: decodeError, msg: "expired timestamp"} - errDecryptionFailed = cookieError{typ: decodeError, msg: "the value could not be decrypted"} - errValueNotByte = cookieError{typ: decodeError, msg: "value not a []byte."} - errValueNotBytePtr = cookieError{typ: decodeError, msg: "value not a pointer to []byte."} - - // ErrMacInvalid indicates that cookie decoding failed because the HMAC - // could not be extracted and verified. Direct use of this error - // variable is deprecated; it is public only for legacy compatibility, - // and may be privatized in the future, as it is rarely useful to - // distinguish between this error and other Error implementations. - ErrMacInvalid = cookieError{typ: decodeError, msg: "the value is not valid"} -) - -// Codec defines an interface to encode and decode cookie values. -type Codec interface { - Encode(name string, value interface{}) (string, error) - Decode(name, value string, dst interface{}) error -} - -// New returns a new SecureCookie. -// -// hashKey is required, used to authenticate values using HMAC. Create it using -// GenerateRandomKey(). It is recommended to use a key with 32 or 64 bytes. -// -// blockKey is optional, used to encrypt values. Create it using -// GenerateRandomKey(). The key length must correspond to the key size -// of the encryption algorithm. For AES, used by default, valid lengths are -// 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256. -// The default encoder used for cookie serialization is encoding/gob. -// -// Note that keys created using GenerateRandomKey() are not automatically -// persisted. New keys will be created when the application is restarted, and -// previously issued cookies will not be able to be decoded. -func New(hashKey, blockKey []byte) *SecureCookie { - s := &SecureCookie{ - hashKey: hashKey, - blockKey: blockKey, - hashFunc: sha256.New, - maxAge: 86400 * 30, - maxLength: 4096, - sz: GobEncoder{}, - } - if len(hashKey) == 0 { - s.err = errHashKeyNotSet - } - if blockKey != nil { - s.BlockFunc(aes.NewCipher) - } - return s -} - -// SecureCookie encodes and decodes authenticated and optionally encrypted -// cookie values. -type SecureCookie struct { - hashKey []byte - hashFunc func() hash.Hash - blockKey []byte - block cipher.Block - maxLength int - maxAge int64 - minAge int64 - err error - sz Serializer - // For testing purposes, the function that returns the current timestamp. - // If not set, it will use time.Now().UTC().Unix(). - timeFunc func() int64 -} - -// Serializer provides an interface for providing custom serializers for cookie -// values. -type Serializer interface { - Serialize(src interface{}) ([]byte, error) - Deserialize(src []byte, dst interface{}) error -} - -// GobEncoder encodes cookie values using encoding/gob. This is the simplest -// encoder and can handle complex types via gob.Register. -type GobEncoder struct{} - -// JSONEncoder encodes cookie values using encoding/json. Users who wish to -// encode complex types need to satisfy the json.Marshaller and -// json.Unmarshaller interfaces. -type JSONEncoder struct{} - -// NopEncoder does not encode cookie values, and instead simply accepts a []byte -// (as an interface{}) and returns a []byte. This is particularly useful when -// you encoding an object upstream and do not wish to re-encode it. -type NopEncoder struct{} - -// MaxLength restricts the maximum length, in bytes, for the cookie value. -// -// Default is 4096, which is the maximum value accepted by Internet Explorer. -func (s *SecureCookie) MaxLength(value int) *SecureCookie { - s.maxLength = value - return s -} - -// MaxAge restricts the maximum age, in seconds, for the cookie value. -// -// Default is 86400 * 30. Set it to 0 for no restriction. -func (s *SecureCookie) MaxAge(value int) *SecureCookie { - s.maxAge = int64(value) - return s -} - -// MinAge restricts the minimum age, in seconds, for the cookie value. -// -// Default is 0 (no restriction). -func (s *SecureCookie) MinAge(value int) *SecureCookie { - s.minAge = int64(value) - return s -} - -// HashFunc sets the hash function used to create HMAC. -// -// Default is crypto/sha256.New. -func (s *SecureCookie) HashFunc(f func() hash.Hash) *SecureCookie { - s.hashFunc = f - return s -} - -// BlockFunc sets the encryption function used to create a cipher.Block. -// -// Default is crypto/aes.New. -func (s *SecureCookie) BlockFunc(f func([]byte) (cipher.Block, error)) *SecureCookie { - if s.blockKey == nil { - s.err = errBlockKeyNotSet - } else if block, err := f(s.blockKey); err == nil { - s.block = block - } else { - s.err = cookieError{cause: err, typ: usageError} - } - return s -} - -// Encoding sets the encoding/serialization method for cookies. -// -// Default is encoding/gob. To encode special structures using encoding/gob, -// they must be registered first using gob.Register(). -func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie { - s.sz = sz - - return s -} - -// Encode encodes a cookie value. -// -// It serializes, optionally encrypts, signs with a message authentication code, -// and finally encodes the value. -// -// The name argument is the cookie name. It is stored with the encoded value. -// The value argument is the value to be encoded. It can be any value that can -// be encoded using the currently selected serializer; see SetSerializer(). -// -// It is the client's responsibility to ensure that value, when encoded using -// the current serialization/encryption settings on s and then base64-encoded, -// is shorter than the maximum permissible length. -func (s *SecureCookie) Encode(name string, value interface{}) (string, error) { - if s.err != nil { - return "", s.err - } - if s.hashKey == nil { - s.err = errHashKeyNotSet - return "", s.err - } - var err error - var b []byte - // 1. Serialize. - if b, err = s.sz.Serialize(value); err != nil { - return "", cookieError{cause: err, typ: usageError} - } - // 2. Encrypt (optional). - if s.block != nil { - if b, err = encrypt(s.block, b); err != nil { - return "", cookieError{cause: err, typ: usageError} - } - } - b = encode(b) - // 3. Create MAC for "name|date|value". Extra pipe to be used later. - b = []byte(fmt.Sprintf("%s|%d|%s|", name, s.timestamp(), b)) - mac := createMac(hmac.New(s.hashFunc, s.hashKey), b[:len(b)-1]) - // Append mac, remove name. - b = append(b, mac...)[len(name)+1:] - // 4. Encode to base64. - b = encode(b) - // 5. Check length. - if s.maxLength != 0 && len(b) > s.maxLength { - return "", fmt.Errorf("%s: %d", errEncodedValueTooLong, len(b)) - } - // Done. - return string(b), nil -} - -// Decode decodes a cookie value. -// -// It decodes, verifies a message authentication code, optionally decrypts and -// finally deserializes the value. -// -// The name argument is the cookie name. It must be the same name used when -// it was stored. The value argument is the encoded cookie value. The dst -// argument is where the cookie will be decoded. It must be a pointer. -func (s *SecureCookie) Decode(name, value string, dst interface{}) error { - if s.err != nil { - return s.err - } - if s.hashKey == nil { - s.err = errHashKeyNotSet - return s.err - } - // 1. Check length. - if s.maxLength != 0 && len(value) > s.maxLength { - return fmt.Errorf("%s: %d", errValueToDecodeTooLong, len(value)) - } - // 2. Decode from base64. - b, err := decode([]byte(value)) - if err != nil { - return err - } - // 3. Verify MAC. Value is "date|value|mac". - parts := bytes.SplitN(b, []byte("|"), 3) - if len(parts) != 3 { - return ErrMacInvalid - } - h := hmac.New(s.hashFunc, s.hashKey) - b = append([]byte(name+"|"), b[:len(b)-len(parts[2])-1]...) - if err = verifyMac(h, b, parts[2]); err != nil { - return err - } - // 4. Verify date ranges. - var t1 int64 - if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil { - return errTimestampInvalid - } - t2 := s.timestamp() - if s.minAge != 0 && t1 > t2-s.minAge { - return errTimestampTooNew - } - if s.maxAge != 0 && t1 < t2-s.maxAge { - return errTimestampExpired - } - // 5. Decrypt (optional). - b, err = decode(parts[1]) - if err != nil { - return err - } - if s.block != nil { - if b, err = decrypt(s.block, b); err != nil { - return err - } - } - // 6. Deserialize. - if err = s.sz.Deserialize(b, dst); err != nil { - return cookieError{cause: err, typ: decodeError} - } - // Done. - return nil -} - -// timestamp returns the current timestamp, in seconds. -// -// For testing purposes, the function that generates the timestamp can be -// overridden. If not set, it will return time.Now().UTC().Unix(). -func (s *SecureCookie) timestamp() int64 { - if s.timeFunc == nil { - return time.Now().UTC().Unix() - } - return s.timeFunc() -} - -// Authentication ------------------------------------------------------------- - -// createMac creates a message authentication code (MAC). -func createMac(h hash.Hash, value []byte) []byte { - h.Write(value) - return h.Sum(nil) -} - -// verifyMac verifies that a message authentication code (MAC) is valid. -func verifyMac(h hash.Hash, value []byte, mac []byte) error { - mac2 := createMac(h, value) - // Check that both MACs are of equal length, as subtle.ConstantTimeCompare - // does not do this prior to Go 1.4. - if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 { - return nil - } - return ErrMacInvalid -} - -// Encryption ----------------------------------------------------------------- - -// encrypt encrypts a value using the given block in counter mode. -// -// A random initialization vector ( https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_(IV) ) with the length of the -// block size is prepended to the resulting ciphertext. -func encrypt(block cipher.Block, value []byte) ([]byte, error) { - iv := GenerateRandomKey(block.BlockSize()) - if iv == nil { - return nil, errGeneratingIV - } - // Encrypt it. - stream := cipher.NewCTR(block, iv) - stream.XORKeyStream(value, value) - // Return iv + ciphertext. - return append(iv, value...), nil -} - -// decrypt decrypts a value using the given block in counter mode. -// -// The value to be decrypted must be prepended by a initialization vector -// ( https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Initialization_vector_(IV) ) with the length of the block size. -func decrypt(block cipher.Block, value []byte) ([]byte, error) { - size := block.BlockSize() - if len(value) > size { - // Extract iv. - iv := value[:size] - // Extract ciphertext. - value = value[size:] - // Decrypt it. - stream := cipher.NewCTR(block, iv) - stream.XORKeyStream(value, value) - return value, nil - } - return nil, errDecryptionFailed -} - -// Serialization -------------------------------------------------------------- - -// Serialize encodes a value using gob. -func (e GobEncoder) Serialize(src interface{}) ([]byte, error) { - buf := new(bytes.Buffer) - enc := gob.NewEncoder(buf) - if err := enc.Encode(src); err != nil { - return nil, cookieError{cause: err, typ: usageError} - } - return buf.Bytes(), nil -} - -// Deserialize decodes a value using gob. -func (e GobEncoder) Deserialize(src []byte, dst interface{}) error { - dec := gob.NewDecoder(bytes.NewBuffer(src)) - if err := dec.Decode(dst); err != nil { - return cookieError{cause: err, typ: decodeError} - } - return nil -} - -// Serialize encodes a value using encoding/json. -func (e JSONEncoder) Serialize(src interface{}) ([]byte, error) { - buf := new(bytes.Buffer) - enc := json.NewEncoder(buf) - if err := enc.Encode(src); err != nil { - return nil, cookieError{cause: err, typ: usageError} - } - return buf.Bytes(), nil -} - -// Deserialize decodes a value using encoding/json. -func (e JSONEncoder) Deserialize(src []byte, dst interface{}) error { - dec := json.NewDecoder(bytes.NewReader(src)) - if err := dec.Decode(dst); err != nil { - return cookieError{cause: err, typ: decodeError} - } - return nil -} - -// Serialize passes a []byte through as-is. -func (e NopEncoder) Serialize(src interface{}) ([]byte, error) { - if b, ok := src.([]byte); ok { - return b, nil - } - - return nil, errValueNotByte -} - -// Deserialize passes a []byte through as-is. -func (e NopEncoder) Deserialize(src []byte, dst interface{}) error { - if dat, ok := dst.(*[]byte); ok { - *dat = src - return nil - } - return errValueNotBytePtr -} - -// Encoding ------------------------------------------------------------------- - -// encode encodes a value using base64. -func encode(value []byte) []byte { - encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value))) - base64.URLEncoding.Encode(encoded, value) - return encoded -} - -// decode decodes a cookie using base64. -func decode(value []byte) ([]byte, error) { - decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value))) - b, err := base64.URLEncoding.Decode(decoded, value) - if err != nil { - return nil, cookieError{cause: err, typ: decodeError, msg: "base64 decode failed"} - } - return decoded[:b], nil -} - -// Helpers -------------------------------------------------------------------- - -// GenerateRandomKey creates a random key with the given length in bytes. -// On failure, returns nil. -// -// Note that keys created using `GenerateRandomKey()` are not automatically -// persisted. New keys will be created when the application is restarted, and -// previously issued cookies will not be able to be decoded. -// -// Callers should explicitly check for the possibility of a nil return, treat -// it as a failure of the system random number generator, and not continue. -func GenerateRandomKey(length int) []byte { - k := make([]byte, length) - if _, err := io.ReadFull(rand.Reader, k); err != nil { - return nil - } - return k -} - -// CodecsFromPairs returns a slice of SecureCookie instances. -// -// It is a convenience function to create a list of codecs for key rotation. Note -// that the generated Codecs will have the default options applied: callers -// should iterate over each Codec and type-assert the underlying *SecureCookie to -// change these. -// -// Example: -// -// codecs := securecookie.CodecsFromPairs( -// []byte("new-hash-key"), -// []byte("new-block-key"), -// []byte("old-hash-key"), -// []byte("old-block-key"), -// ) -// -// // Modify each instance. -// for _, s := range codecs { -// if cookie, ok := s.(*securecookie.SecureCookie); ok { -// cookie.MaxAge(86400 * 7) -// cookie.SetSerializer(securecookie.JSONEncoder{}) -// cookie.HashFunc(sha512.New512_256) -// } -// } -func CodecsFromPairs(keyPairs ...[]byte) []Codec { - codecs := make([]Codec, len(keyPairs)/2+len(keyPairs)%2) - for i := 0; i < len(keyPairs); i += 2 { - var blockKey []byte - if i+1 < len(keyPairs) { - blockKey = keyPairs[i+1] - } - codecs[i/2] = New(keyPairs[i], blockKey) - } - return codecs -} - -// EncodeMulti encodes a cookie value using a group of codecs. -// -// The codecs are tried in order. Multiple codecs are accepted to allow -// key rotation. -// -// On error, may return a MultiError. -func EncodeMulti(name string, value interface{}, codecs ...Codec) (string, error) { - if len(codecs) == 0 { - return "", errNoCodecs - } - - var errors MultiError - for _, codec := range codecs { - encoded, err := codec.Encode(name, value) - if err == nil { - return encoded, nil - } - errors = append(errors, err) - } - return "", errors -} - -// DecodeMulti decodes a cookie value using a group of codecs. -// -// The codecs are tried in order. Multiple codecs are accepted to allow -// key rotation. -// -// On error, may return a MultiError. -func DecodeMulti(name string, value string, dst interface{}, codecs ...Codec) error { - if len(codecs) == 0 { - return errNoCodecs - } - - var errors MultiError - for _, codec := range codecs { - err := codec.Decode(name, value, dst) - if err == nil { - return nil - } - errors = append(errors, err) - } - return errors -} - -// MultiError groups multiple errors. -type MultiError []error - -func (m MultiError) IsUsage() bool { return m.any(func(e Error) bool { return e.IsUsage() }) } -func (m MultiError) IsDecode() bool { return m.any(func(e Error) bool { return e.IsDecode() }) } -func (m MultiError) IsInternal() bool { return m.any(func(e Error) bool { return e.IsInternal() }) } - -// Cause returns nil for MultiError; there is no unique underlying cause in the -// general case. -// -// Note: we could conceivably return a non-nil Cause only when there is exactly -// one child error with a Cause. However, it would be brittle for client code -// to rely on the arity of causes inside a MultiError, so we have opted not to -// provide this functionality. Clients which really wish to access the Causes -// of the underlying errors are free to iterate through the errors themselves. -func (m MultiError) Cause() error { return nil } - -func (m MultiError) Error() string { - s, n := "", 0 - for _, e := range m { - if e != nil { - if n == 0 { - s = e.Error() - } - n++ - } - } - switch n { - case 0: - return "(0 errors)" - case 1: - return s - case 2: - return s + " (and 1 other error)" - } - return fmt.Sprintf("%s (and %d other errors)", s, n-1) -} - -// any returns true if any element of m is an Error for which pred returns true. -func (m MultiError) any(pred func(Error) bool) bool { - for _, e := range m { - if ourErr, ok := e.(Error); ok && pred(ourErr) { - return true - } - } - return false -} diff --git a/vendor/modules.txt b/vendor/modules.txt deleted file mode 100644 index 6224b61..0000000 --- a/vendor/modules.txt +++ /dev/null @@ -1,3 +0,0 @@ -# github.com/gorilla/securecookie v1.1.2 -## explicit; go 1.20 -github.com/gorilla/securecookie From ff5660f3c355c08371621b642d76c7a4c88836c6 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sat, 15 Jun 2024 02:52:55 +0000 Subject: [PATCH 115/121] chore(go): Add warning about main branch Main branch is in a state of flux because of the partitioned cookie attribute which won't land in go until version 1.23. Added a warning to this affect as well as upped the version of go in the go.mod to 1.23 to indicate this change. --- README.md | 5 ++++- go.mod | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3aef6a4..184e7df 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -# sessions +# Gorilla Sessions + +> [!CAUTION] +> The main branch of this respository is in flux and being prepared for the [Go 1.23 release](https://github.com/golang/go/milestone/212). Please use released versions as per the release page. ![testing](https://github.com/gorilla/sessions/actions/workflows/test.yml/badge.svg) [![codecov](https://codecov.io/github/gorilla/sessions/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/sessions) diff --git a/go.mod b/go.mod index 64cc6a3..25a82cc 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ module github.com/gorilla/sessions -go 1.20 +go 1.23 require github.com/gorilla/securecookie v1.1.2 From 7a8159ef2d1bc1afea2b9fbb52e947ded867cf0c Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sat, 15 Jun 2024 02:54:42 +0000 Subject: [PATCH 116/121] chore(go): Remove go version 1.11 support --- cookie_go111.go | 22 ---------------------- cookie_go111_test.go | 31 ------------------------------- cookie_test.go | 30 +++++++++++++++--------------- options_go111.go | 24 ------------------------ 4 files changed, 15 insertions(+), 92 deletions(-) delete mode 100644 cookie_go111.go delete mode 100644 cookie_go111_test.go delete mode 100644 options_go111.go diff --git a/cookie_go111.go b/cookie_go111.go deleted file mode 100644 index d5e9e62..0000000 --- a/cookie_go111.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build go1.11 -// +build go1.11 - -package sessions - -import "net/http" - -// newCookieFromOptions returns an http.Cookie with the options set. -func newCookieFromOptions(name, value string, options *Options) *http.Cookie { - return &http.Cookie{ - Name: name, - Value: value, - Path: options.Path, - Domain: options.Domain, - MaxAge: options.MaxAge, - Secure: options.Secure, - HttpOnly: options.HttpOnly, - SameSite: options.SameSite, - Partitioned: options.Partitioned, - } - -} diff --git a/cookie_go111_test.go b/cookie_go111_test.go deleted file mode 100644 index 2fad2e7..0000000 --- a/cookie_go111_test.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build go1.11 -// +build go1.11 - -package sessions - -import ( - "net/http" - "testing" -) - -// Test for setting SameSite field in new http.Cookie from name, value -// and options -func TestNewCookieFromOptionsSameSite(t *testing.T) { - tests := []struct { - sameSite http.SameSite - }{ - {http.SameSiteDefaultMode}, - {http.SameSiteLaxMode}, - {http.SameSiteStrictMode}, - {http.SameSiteNoneMode}, - } - for i, v := range tests { - options := &Options{ - SameSite: v.sameSite, - } - cookie := newCookieFromOptions("", "", options) - if cookie.SameSite != v.sameSite { - t.Fatalf("%v: bad cookie sameSite: got %v, want %v", i+1, cookie.SameSite, v.sameSite) - } - } -} diff --git a/cookie_test.go b/cookie_test.go index 8e02fbc..de1ef44 100644 --- a/cookie_test.go +++ b/cookie_test.go @@ -7,13 +7,13 @@ import ( // Test for creating new http.Cookie from name, value and options func TestNewCookieFromOptions(t *testing.T) { tests := []struct { - name string - value string - path string - domain string - maxAge int - secure bool - httpOnly bool + name string + value string + path string + domain string + maxAge int + secure bool + httpOnly bool partitioned bool }{ {"", "bar", "/foo/bar", "foo.example.com", 3600, true, true, true}, @@ -27,11 +27,11 @@ func TestNewCookieFromOptions(t *testing.T) { } for i, v := range tests { options := &Options{ - Path: v.path, - Domain: v.domain, - MaxAge: v.maxAge, - Secure: v.secure, - HttpOnly: v.httpOnly, + Path: v.path, + Domain: v.domain, + MaxAge: v.maxAge, + Secure: v.secure, + HttpOnly: v.httpOnly, Partitioned: v.partitioned, } cookie := newCookieFromOptions(v.name, v.value, options) @@ -56,8 +56,8 @@ func TestNewCookieFromOptions(t *testing.T) { if cookie.HttpOnly != v.httpOnly { t.Fatalf("%v: bad cookie httpOnly: got %v, want %v", i+1, cookie.HttpOnly, v.httpOnly) } - if cookie.Partitioned != v.partitioned { - t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) - } + // if cookie.Partitioned != v.partitioned { + // t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) + // } } } diff --git a/options_go111.go b/options_go111.go deleted file mode 100644 index 3214990..0000000 --- a/options_go111.go +++ /dev/null @@ -1,24 +0,0 @@ -//go:build go1.11 -// +build go1.11 - -package sessions - -import "net/http" - -// Options stores configuration for a session or session store. -// -// Fields are a subset of http.Cookie fields. -type Options struct { - Path string - Domain string - // MaxAge=0 means no Max-Age attribute specified and the cookie will be - // deleted after the browser session ends. - // MaxAge<0 means delete cookie immediately. - // MaxAge>0 means Max-Age attribute present and given in seconds. - MaxAge int - Secure bool - HttpOnly bool - Partitioned bool - // Defaults to http.SameSiteDefaultMode - SameSite http.SameSite -} From 466d29e7f343560836292b7c922e1385e908ef95 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Sat, 15 Jun 2024 02:58:08 +0000 Subject: [PATCH 117/121] chore: Update readme and copyrights --- LICENSE | 2 +- cookie.go | 19 ++++++++++--------- cookie_test.go | 10 +++++++--- options.go | 5 +++-- store_test.go | 4 ++++ 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/LICENSE b/LICENSE index bb9d80b..7fa9009 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2023 The Gorilla Authors. All rights reserved. +Copyright (c) 2024 The Gorilla Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/cookie.go b/cookie.go index fa70e7c..28cd31d 100644 --- a/cookie.go +++ b/cookie.go @@ -1,5 +1,6 @@ -//go:build !go1.11 -// +build !go1.11 +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. package sessions @@ -8,13 +9,13 @@ import "net/http" // newCookieFromOptions returns an http.Cookie with the options set. func newCookieFromOptions(name, value string, options *Options) *http.Cookie { return &http.Cookie{ - Name: name, - Value: value, - Path: options.Path, - Domain: options.Domain, - MaxAge: options.MaxAge, - Secure: options.Secure, - HttpOnly: options.HttpOnly, + Name: name, + Value: value, + Path: options.Path, + Domain: options.Domain, + MaxAge: options.MaxAge, + Secure: options.Secure, + HttpOnly: options.HttpOnly, Partitioned: options.Partitioned, } diff --git a/cookie_test.go b/cookie_test.go index de1ef44..de530ca 100644 --- a/cookie_test.go +++ b/cookie_test.go @@ -1,3 +1,7 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package sessions import ( @@ -56,8 +60,8 @@ func TestNewCookieFromOptions(t *testing.T) { if cookie.HttpOnly != v.httpOnly { t.Fatalf("%v: bad cookie httpOnly: got %v, want %v", i+1, cookie.HttpOnly, v.httpOnly) } - // if cookie.Partitioned != v.partitioned { - // t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) - // } + if cookie.Partitioned != v.partitioned { + t.Fatalf("%v: bad cookie partitioned: got %v, want %v", i+1, cookie.Partitioned, v.partitioned) + } } } diff --git a/options.go b/options.go index ec07068..32e64d8 100644 --- a/options.go +++ b/options.go @@ -1,5 +1,6 @@ -//go:build !go1.11 -// +build !go1.11 +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. package sessions diff --git a/store_test.go b/store_test.go index 3561e5d..09580bd 100644 --- a/store_test.go +++ b/store_test.go @@ -1,3 +1,7 @@ +// Copyright 2012 The Gorilla Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package sessions import ( From a56e60c14e37beb3f690c14311c8ad7be7580bb5 Mon Sep 17 00:00:00 2001 From: "./daniele" <47982731+danielepintore@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:38:06 +0200 Subject: [PATCH 118/121] Add mysql store to the readme (#279) Add MySQL store to README Signed-off-by: ./daniele <47982731+danielepintore@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 184e7df..540b63d 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Other implementations of the `sessions.Store` interface: - [github.com/dsoprea/go-appengine-sessioncascade](https://github.com/dsoprea/go-appengine-sessioncascade) - Memcache/Datastore/Context in AppEngine - [github.com/kidstuff/mongostore](https://github.com/kidstuff/mongostore) - MongoDB - [github.com/srinathgs/mysqlstore](https://github.com/srinathgs/mysqlstore) - MySQL +- [github.com/danielepintore/gorilla-sessions-mysql](https://github.com/danielepintore/gorilla-sessions-mysql) - MySQL - [github.com/EnumApps/clustersqlstore](https://github.com/EnumApps/clustersqlstore) - MySQL Cluster - [github.com/antonlindstrom/pgstore](https://github.com/antonlindstrom/pgstore) - PostgreSQL - [github.com/boj/redistore](https://github.com/boj/redistore) - Redis From 6eef180e176e826b77f4d2f5f8e1cf855a87db02 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Tue, 20 Aug 2024 14:01:07 +0000 Subject: [PATCH 119/121] fix: Missing SameSite attribute on options --- cookie.go | 1 + options.go | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cookie.go b/cookie.go index 28cd31d..fd6f48c 100644 --- a/cookie.go +++ b/cookie.go @@ -17,6 +17,7 @@ func newCookieFromOptions(name, value string, options *Options) *http.Cookie { Secure: options.Secure, HttpOnly: options.HttpOnly, Partitioned: options.Partitioned, + SameSite: options.SameSite, } } diff --git a/options.go b/options.go index 32e64d8..6ed7934 100644 --- a/options.go +++ b/options.go @@ -4,6 +4,8 @@ package sessions +import "net/http" + // Options stores configuration for a session or session store. // // Fields are a subset of http.Cookie fields. @@ -14,8 +16,9 @@ type Options struct { // deleted after the browser session ends. // MaxAge<0 means delete cookie immediately. // MaxAge>0 means Max-Age attribute present and given in seconds. - MaxAge int - Secure bool - HttpOnly bool + MaxAge int + Secure bool + HttpOnly bool Partitioned bool + SameSite http.SameSite } From e2083f956282b2e627d9734f4ebbd51fa0339f09 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Tue, 20 Aug 2024 14:05:02 +0000 Subject: [PATCH 120/121] chore: update to go 1.23 for workflows --- .github/workflows/security.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/verify.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index ff4a613..19a6c1c 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -12,7 +12,7 @@ jobs: scan: strategy: matrix: - go: ['1.20','1.21'] + go: ['1.23'] fail-fast: true runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 50a3946..c9987eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: unit: strategy: matrix: - go: ['1.20','1.21'] + go: ['1.23'] os: [ubuntu-latest, macos-latest, windows-latest] fail-fast: true runs-on: ${{ matrix.os }} diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index a3eb74b..3471511 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -12,7 +12,7 @@ jobs: lint: strategy: matrix: - go: ['1.20','1.21'] + go: ['1.23'] fail-fast: true runs-on: ubuntu-latest steps: @@ -28,5 +28,5 @@ jobs: - name: Run GolangCI-Lint uses: golangci/golangci-lint-action@v3 with: - version: v1.53 + version: v1.60.1 args: --timeout=5m From bb4cd60c952a9ce48ea0dc6cc7b282ff79c38263 Mon Sep 17 00:00:00 2001 From: Daniel Holmes Date: Tue, 20 Aug 2024 14:07:20 +0000 Subject: [PATCH 121/121] chore: Update readme to relect go 1.23 release --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 540b63d..d2cbea6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Gorilla Sessions -> [!CAUTION] -> The main branch of this respository is in flux and being prepared for the [Go 1.23 release](https://github.com/golang/go/milestone/212). Please use released versions as per the release page. +> [!IMPORTANT] +> The latest version of this repository requires go 1.23 because of the new partitioned attribute. The last version that is compatible with older versions of go is v1.3.0. ![testing](https://github.com/gorilla/sessions/actions/workflows/test.yml/badge.svg) [![codecov](https://codecov.io/github/gorilla/sessions/branch/main/graph/badge.svg)](https://codecov.io/github/gorilla/sessions)