diff --git a/httputil/session.go b/httputil/session.go deleted file mode 100644 index 5d2872eab361e583b2da660924e4930aa975178d..0000000000000000000000000000000000000000 --- a/httputil/session.go +++ /dev/null @@ -1,100 +0,0 @@ -package httputil - -import ( - "encoding/gob" - "log" - "net/http" - "time" - - "github.com/gorilla/sessions" -) - -// ExpiringSession is a session with server-side expiration check. -// Session data is saved in signed, encrypted cookies in the -// browser. We'd like these cookies to expire when a certain amount of -// time passes, or when the user closes the browser. We trust the -// browser for the latter, but we enforce time-based expiration on the -// server. -type ExpiringSession struct { - *sessions.Session -} - -// GetExpiringSession wraps a Session (obtained from 'store') with -// an ExpiringSession. If it's invalid or expired, a new empty Session -// will be created with an expiration time set using 'ttl'. -func GetExpiringSession(req *http.Request, store sessions.Store, key string, ttl time.Duration) (*ExpiringSession, error) { - now := time.Now() - - // An error here just means that we failed to decode the - // existing session for some reason. A new session will always - // be returned, so we just pass along the error to the caller - // (so it can be logged). - s, err := store.Get(req, key) - - // See if we have a valid session first. - if !s.IsNew { - if exp, ok := s.Values["_exp"].(time.Time); ok && now.Before(exp) { - return &ExpiringSession{Session: s}, err - } - // We can't call sessions.NewSession() because that - // won't register the session with the Registry, so it - // won't be sent with the response. Wipe the data - // instead. - for k := range s.Values { - delete(s.Values, k) - } - } - - // The session is either invalid or expired, create a new - // blank one containing no data. - expiry := now.Add(ttl) - s.Values["_exp"] = expiry - - return &ExpiringSession{Session: s}, err -} - -// Wrapper for an http.ResponseWriter that ensures all tracked -// sessions are saved before the request body is sent. -// -// We have to duplicate the logic to call WriteHeader on the first -// Write, otherwise the underlying ResponseWriter won't call our -// WriteHeader function but its own instead. -type sessionResponseWriter struct { - http.ResponseWriter - headerWritten bool - req *http.Request -} - -func (w *sessionResponseWriter) WriteHeader(statusCode int) { - if statusCode >= 200 && statusCode < 400 { - if err := sessions.Save(w.req, w.ResponseWriter); err != nil { - log.Printf("error saving sessions: %v", err) - } - } - w.ResponseWriter.WriteHeader(statusCode) - w.headerWritten = true -} - -func (w *sessionResponseWriter) Write(b []byte) (int, error) { - if !w.headerWritten { - w.WriteHeader(http.StatusOK) - } - return w.ResponseWriter.Write(b) -} - -// NewSessionResponseWriter returns a wrapped http.ResponseWriter that -// will always remember to save the Gorilla sessions before writing -// the response body. -func NewSessionResponseWriter(w http.ResponseWriter, req *http.Request) http.ResponseWriter { - return &sessionResponseWriter{ - ResponseWriter: w, - req: req, - } -} - -func init() { - // Register time.Time with encoding/gob, to ensure that the - // ExpiringSession timestamp can be serialized. - var t time.Time - gob.Register(t) -} diff --git a/httputil/session_test.go b/httputil/session_test.go deleted file mode 100644 index b46817117da6bab86633f1234336161eb813eae6..0000000000000000000000000000000000000000 --- a/httputil/session_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package httputil - -import ( - "encoding/gob" - "net/http" - "testing" - "time" - - "github.com/gorilla/sessions" -) - -type mySession struct { - Data string -} - -func init() { - gob.Register(&mySession{}) -} - -func TestExpiringSession(t *testing.T) { - store := sessions.NewCookieStore() - req, _ := http.NewRequest("GET", "http://localhost/", nil) - - httpsess, err := GetExpiringSession(req, store, "testkey", 60*time.Second) - if err != nil { - t.Errorf("store.Get error: %v", err) - } - - if _, ok := httpsess.Values["mykey"].(*mySession); ok { - t.Fatal("got a session without any data") - } -} diff --git a/server/http.go b/server/http.go index a8227fef28fd0a8c311ee7b908acad18ad741a63..d0ddb50baf40b0ed4bb88fb7e87d9c73a24dba80 100644 --- a/server/http.go +++ b/server/http.go @@ -24,8 +24,8 @@ import ( authclient "git.autistici.org/id/auth/client" ksclient "git.autistici.org/id/keystore/client" - "git.autistici.org/id/go-sso/httputil" "git.autistici.org/id/go-sso/server/device" + "git.autistici.org/id/go-sso/server/httputil" "git.autistici.org/id/go-sso/server/login" ) diff --git a/httputil/headers.go b/server/httputil/headers.go similarity index 100% rename from httputil/headers.go rename to server/httputil/headers.go diff --git a/httputil/renderer.go b/server/httputil/renderer.go similarity index 100% rename from httputil/renderer.go rename to server/httputil/renderer.go diff --git a/httputil/static.go b/server/httputil/static.go similarity index 100% rename from httputil/static.go rename to server/httputil/static.go diff --git a/server/login/login.go b/server/login/login.go index d936618500efb5fed3edef8d8b939ba7b9c6f6dd..c524acc87d4acb470b544e853d7d5f4f3de38b04 100644 --- a/server/login/login.go +++ b/server/login/login.go @@ -2,7 +2,6 @@ package login import ( "context" - "encoding/gob" "encoding/json" "errors" "fmt" @@ -13,16 +12,13 @@ import ( "git.autistici.org/id/auth" authclient "git.autistici.org/id/auth/client" - "github.com/gorilla/sessions" "github.com/tstranex/u2f" "go.opencensus.io/trace" - "git.autistici.org/id/go-sso/httputil" "git.autistici.org/id/go-sso/server/device" + "git.autistici.org/id/go-sso/server/httputil" ) -const loginSessionKey = "_auth" - const maxFailures = 5 type Auth struct { @@ -56,6 +52,9 @@ type loginSession struct { AuthResponse *auth.Response Redir string Failures int + + // Implementation detail of the session layer. + deleted bool } func (l *loginSession) Reset() { @@ -90,29 +89,8 @@ func (l *loginSession) Can2FA(method auth.TFAMethod) error { return nil } -func init() { - gob.Register(&loginSession{}) -} - -type loginSessionInt struct { - *loginSession - httpSession *httputil.ExpiringSession -} - -func (s *loginSessionInt) Delete(req *http.Request, w http.ResponseWriter) { - delete(s.httpSession.Values, "data") - s.httpSession.Options.MaxAge = -1 -} - -func newLoginSession(hs *httputil.ExpiringSession, s *loginSession) *loginSessionInt { - if s == nil { - s = new(loginSession) - hs.Values["data"] = s - } - return &loginSessionInt{ - loginSession: s, - httpSession: hs, - } +func (l *loginSession) Delete() { + l.deleted = true } type ctxKey int @@ -138,7 +116,7 @@ type LoginCallback func(context.Context, string, string, *auth.UserInfo) error // Login wraps an http.Handler with a login workflow. type Login struct { wrap http.Handler - sessionStore sessions.Store + sessionMgr *sessionManager sessionTTL time.Duration urlPrefix string renderer *httputil.Renderer @@ -152,21 +130,14 @@ type Login struct { // New returns a new Login wrapper. func New(wrap http.Handler, devMgr *device.Manager, authClient authclient.Client, authService, u2fAppID, urlPrefix, fallbackRedirect string, renderer *httputil.Renderer, callback LoginCallback, keyPairs [][]byte, sessionTTL time.Duration) *Login { - store := sessions.NewCookieStore(keyPairs...) - store.Options = &sessions.Options{ - HttpOnly: true, - Secure: true, - MaxAge: 0, - Path: urlPrefix + "/", - } - if sessionTTL == 0 { sessionTTL = 20 * time.Hour // default TTL. } + smgr := newSessionManager(urlPrefix+"/", keyPairs[0], keyPairs[1], sessionTTL) return &Login{ wrap: wrap, - sessionStore: store, + sessionMgr: smgr, sessionTTL: sessionTTL, urlPrefix: urlPrefix, renderer: renderer, @@ -183,58 +154,51 @@ func (l *Login) urlFor(path string) string { return l.urlPrefix + path } -func (l *Login) fetchOrInitSession(req *http.Request) *loginSessionInt { - httpSession, err := httputil.GetExpiringSession(req, l.sessionStore, loginSessionKey, l.sessionTTL) +func (l *Login) fetchOrInitSession(req *http.Request) *loginSession { + session, err := l.sessionMgr.getSession(req) if err != nil { - log.Printf("sessionStore.Get error: %v", err) - } - var session *loginSessionInt - if inner, ok := httpSession.Values["data"].(*loginSession); ok { - session = newLoginSession(httpSession, inner) - } else { - // Initialize a new session. - session = newLoginSession(httpSession, nil) + return new(loginSession) } return session } func (l *Login) ServeHTTP(w http.ResponseWriter, req *http.Request) { - sess := l.fetchOrInitSession(req) + session := l.fetchOrInitSession(req) - // This way we don't have to call sess.Save explicitly. - w = httputil.NewSessionResponseWriter(w, req) + // Ensure that the session is saved. + w = l.sessionMgr.newSessionResponseWriter(w, req, session) // A very simple router. switch req.URL.Path { case l.urlFor("/login"): - l.handleLogin(w, req, sess) + l.handleLogin(w, req, session) case l.urlFor("/login/u2f"): - l.handleLoginU2F(w, req, sess) + l.handleLoginU2F(w, req, session) case l.urlFor("/login/otp"): - l.handleLoginOTP(w, req, sess) + l.handleLoginOTP(w, req, session) default: // Wipe the session on logout, before passing through to the // wrapped handler. Note that the Auth object will still // contain valid data, but Authenticated will be set to false. if req.URL.Path == l.urlFor("/logout") { - log.Printf("logging out user %s", sess.Username) - sess.Authenticated = false - sess.Delete(req, w) - } else if !sess.Authenticated { + log.Printf("logging out user %s", session.Username) + session.Authenticated = false + session.Delete() + } else if !session.Authenticated { // Save the current URL in the session for later redirect. - sess.Redir = req.URL.String() + session.Redir = req.URL.String() http.Redirect(w, req, "/login", http.StatusFound) return } // Pass the AuthContext to the wrapped Handler via the // request context. - req = req.WithContext(withAuth(req.Context(), &sess.Auth)) + req = req.WithContext(withAuth(req.Context(), &session.Auth)) l.wrap.ServeHTTP(w, req) } } -func (l *Login) loginOk(w http.ResponseWriter, req *http.Request, sess *loginSessionInt, password string) { +func (l *Login) loginOk(w http.ResponseWriter, req *http.Request, sess *loginSession, password string) { if l.callback != nil { if err := l.callback(req.Context(), sess.Username, password, sess.UserInfo); err != nil { log.Printf("login callback error: %v", err) @@ -258,7 +222,7 @@ func (l *Login) loginOk(w http.ResponseWriter, req *http.Request, sess *loginSes http.Redirect(w, req, target, http.StatusFound) } -func (l *Login) handleLogin(w http.ResponseWriter, req *http.Request, sess *loginSessionInt) { +func (l *Login) handleLogin(w http.ResponseWriter, req *http.Request, sess *loginSession) { if req.Method != "GET" && req.Method != "POST" { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return @@ -311,7 +275,7 @@ func (l *Login) handleLogin(w http.ResponseWriter, req *http.Request, sess *logi l.renderer.Render(w, req, "login_password.html", env) } -func (l *Login) handleLoginOTP(w http.ResponseWriter, req *http.Request, sess *loginSessionInt) { +func (l *Login) handleLoginOTP(w http.ResponseWriter, req *http.Request, sess *loginSession) { if req.Method != "GET" && req.Method != "POST" { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return @@ -353,7 +317,7 @@ func (l *Login) handleLoginOTP(w http.ResponseWriter, req *http.Request, sess *l l.renderer.Render(w, req, "login_otp.html", env) } -func (l *Login) handleLoginU2F(w http.ResponseWriter, req *http.Request, sess *loginSessionInt) { +func (l *Login) handleLoginU2F(w http.ResponseWriter, req *http.Request, sess *loginSession) { if req.Method != "GET" && req.Method != "POST" { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return diff --git a/server/login/session.go b/server/login/session.go new file mode 100644 index 0000000000000000000000000000000000000000..fbaf8b80352dab68f561ea93f6d2b90b6642306b --- /dev/null +++ b/server/login/session.go @@ -0,0 +1,109 @@ +package login + +import ( + "encoding/gob" + "log" + "net/http" + "time" + + "github.com/gorilla/securecookie" +) + +func init() { + gob.Register(&loginSession{}) +} + +const sessionCookieName = "_sso_auth" + +type sessionManager struct { + sc *securecookie.SecureCookie + path string +} + +func newSessionManager(path string, authKey, encKey []byte, ttl time.Duration) *sessionManager { + sc := securecookie.New(authKey, encKey) + sc.MaxAge(int(ttl.Seconds())) + // The JSON encoder generates smaller data than gob in our case. + sc.SetSerializer(&securecookie.JSONEncoder{}) + return &sessionManager{ + sc: sc, + path: path, + } +} + +func (m *sessionManager) getSession(req *http.Request) (*loginSession, error) { + cookie, err := req.Cookie(sessionCookieName) + if err != nil { + return nil, err + } + var s loginSession + err = m.sc.Decode(sessionCookieName, cookie.Value, &s) + if err != nil { + return nil, err + } + return &s, nil +} + +func (m *sessionManager) setSession(w http.ResponseWriter, session *loginSession) (err error) { + var encoded string + if !session.deleted { + encoded, err = m.sc.Encode(sessionCookieName, session) + if err != nil { + return + } + } + cookie := &http.Cookie{ + Name: sessionCookieName, + Value: encoded, + Path: m.path, + Secure: true, + HttpOnly: true, + } + if session.deleted { + cookie.MaxAge = -1 + } + http.SetCookie(w, cookie) + return +} + +// Wrapper for an http.ResponseWriter that ensures all tracked +// sessions are saved before the request body is sent. +// +// We have to duplicate the logic to call WriteHeader on the first +// Write, otherwise the underlying ResponseWriter won't call our +// WriteHeader function but its own instead. +type sessionResponseWriter struct { + http.ResponseWriter + headerWritten bool + req *http.Request + mgr *sessionManager + session *loginSession +} + +func (w *sessionResponseWriter) WriteHeader(statusCode int) { + if statusCode >= 200 && statusCode < 400 { + if err := w.mgr.setSession(w, w.session); err != nil { + log.Printf("error saving sessions: %v", err) + } + } + w.ResponseWriter.WriteHeader(statusCode) + w.headerWritten = true +} + +func (w *sessionResponseWriter) Write(b []byte) (int, error) { + if !w.headerWritten { + w.WriteHeader(http.StatusOK) + } + return w.ResponseWriter.Write(b) +} + +// newSessionResponseWriter returns a wrapped http.ResponseWriter that +// will always save the session before writing the response body. +func (m *sessionManager) newSessionResponseWriter(w http.ResponseWriter, req *http.Request, session *loginSession) http.ResponseWriter { + return &sessionResponseWriter{ + ResponseWriter: w, + req: req, + mgr: m, + session: session, + } +} diff --git a/vendor/github.com/gorilla/sessions/AUTHORS b/vendor/github.com/gorilla/sessions/AUTHORS deleted file mode 100644 index 1e3e7acb629636728fb6f6f23bc6e5d16497db2a..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/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 <ahmadrezazibaei@hotmail.com> -Anton Lindström <lindztr@gmail.com> -Brian Jones <mojobojo@gmail.com> -Collin Stedman <kronion@users.noreply.github.com> -Deniz Eren <dee.116@gmail.com> -Dmitry Chestnykh <dmitry@codingrobots.com> -Dustin Oprea <myselfasunder@gmail.com> -Egon Elbre <egonelbre@gmail.com> -enumappstore <appstore@enumapps.com> -Geofrey Ernest <geofreyernest@live.com> -Google LLC (https://opensource.google.com/) -Jerry Saravia <SaraviaJ@gmail.com> -Jonathan Gillham <jonathan.gillham@gamil.com> -Justin Clift <justin@postgresql.org> -Justin Hellings <justin.hellings@gmail.com> -Kamil Kisiel <kamil@kamilkisiel.net> -Keiji Yoshida <yoshida.keiji.84@gmail.com> -kliron <kliron@gmail.com> -Kshitij Saraogi <KshitijSaraogi@gmail.com> -Lauris BH <lauris@nix.lv> -Lukas Rist <glaslos@gmail.com> -Mark Dain <ancarda@users.noreply.github.com> -Matt Ho <matt.ho@gmail.com> -Matt Silverlock <matt@eatsleeprepeat.net> -Mattias Wadman <mattias.wadman@gmail.com> -Michael Schuett <michaeljs1990@gmail.com> -Michael Stapelberg <stapelberg@users.noreply.github.com> -Mirco Zeiss <mirco.zeiss@gmail.com> -moraes <rodrigo.moraes@gmail.com> -nvcnvn <nguyen@open-vn.org> -pappz <zoltan.pmail@gmail.com> -Pontus Leitzler <leitzler@users.noreply.github.com> -QuaSoft <info@quasoft.net> -rcadena <robert.cadena@gmail.com> -rodrigo moraes <rodrigo.moraes@gmail.com> -Shawn Smith <shawnpsmith@gmail.com> -Taylor Hurt <taylor.a.hurt@gmail.com> -Tortuoise <sanyasinp@gmail.com> -Vitor De Mario <vitordemario@gmail.com> diff --git a/vendor/github.com/gorilla/sessions/LICENSE b/vendor/github.com/gorilla/sessions/LICENSE deleted file mode 100644 index 6903df6386e98928a3236b87c84b71260c2541a6..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -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 -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/sessions/README.md b/vendor/github.com/gorilla/sessions/README.md deleted file mode 100644 index d4d70e9d1cd7231a000fdf8893ef5930b2929889..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# sessions - -[](https://godoc.org/github.com/gorilla/sessions) [](https://travis-ci.org/gorilla/sessions) -[](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 - 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" - ) - - // 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([]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 - // 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 before we write to the response/return from the handler. - 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 -secret key used to authenticate the session. Inside the handler, we call -`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. - -More examples are available [on the Gorilla -website](https://www.gorillatoolkit.org/pkg/sessions). - -## 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 -- [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/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 -- [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/GoogleCloudPlatform/firestore-gorilla-sessions](https://github.com/GoogleCloudPlatform/firestore-gorilla-sessions) - Cloud Firestore - -## License - -BSD licensed. See the LICENSE file for details. diff --git a/vendor/github.com/gorilla/sessions/cookie.go b/vendor/github.com/gorilla/sessions/cookie.go deleted file mode 100644 index 1928b0471d9d259e6f18acf0ce96a8a42d78d306..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/cookie.go +++ /dev/null @@ -1,19 +0,0 @@ -// +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/vendor/github.com/gorilla/sessions/cookie_go111.go b/vendor/github.com/gorilla/sessions/cookie_go111.go deleted file mode 100644 index 173d1a3ed12bb983164d7f3705ec927885a06e83..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/cookie_go111.go +++ /dev/null @@ -1,20 +0,0 @@ -// +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/vendor/github.com/gorilla/sessions/doc.go b/vendor/github.com/gorilla/sessions/doc.go deleted file mode 100644 index f4673cccf559e6c34a4b13ae6eb0fe16737c7c41..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/doc.go +++ /dev/null @@ -1,207 +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 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: - - import ( - "net/http" - "github.com/gorilla/sessions" - ) - - // 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. - // 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) { - // 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) - return - } - - // Set some session values. - 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) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - } - -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. - -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. - -That's all you need to know for the basic usage. Let's take a look at other -options, starting with flash messages. - -Flash messages are session values that last until read. The term appeared with -Ruby On Rails a few years back. When we request a flash message, it is removed -from the session. To add a flash, call session.AddFlash(), and to get all -flashes, call session.Flashes(). Here is an example: - - func MyHandler(w http.ResponseWriter, r *http.Request) { - // Get a session. - session, err := store.Get(r, "session-name") - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Get the previous flashes, if any. - if flashes := session.Flashes(); len(flashes) > 0 { - // Use the flash values. - } else { - // Set a new flash. - session.AddFlash("Hello, flash messages world!") - } - 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, -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 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: - - func MyHandler(w http.ResponseWriter, r *http.Request) { - session, err := store.Get(r, "session-name") - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - 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 -configured and then all sessions saved using it will use that configuration. -We access session.Options or store.Options to set a new configuration. The -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, - 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 -it you just need to set multiple authentication and encryption keys, in pairs, -to be tested in order: - - var store = sessions.NewCookieStore( - []byte("new-authentication-key"), - []byte("new-encryption-key"), - []byte("old-authentication-key"), - []byte("old-encryption-key"), - ) - -New sessions will be saved using the first pair. Old sessions can still be -read because the first pair will fail, and the second will be tested. This -makes it easy to "rotate" secret keys and still be able to validate existing -sessions. Note: for all pairs the encryption key is optional; set it to nil -or omit it and and encryption won't be used. - -Multiple sessions can be used in the same request, even with different -session backends. When this happens, calling Save() on each session -individually would be cumbersome, so we have a way to save all sessions -at once: it's sessions.Save(). Here's an example: - - var store = sessions.NewCookieStore([]byte("something-very-secret")) - - func MyHandler(w http.ResponseWriter, r *http.Request) { - // Get a session and set a value. - session1, _ := store.Get(r, "session-one") - session1.Values["foo"] = "bar" - // Get another session and set another value. - session2, _ := store.Get(r, "session-two") - session2.Values[42] = 43 - // Save all sessions. - 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 -session to a common registry. Save() uses it to save all registered sessions. -*/ -package sessions diff --git a/vendor/github.com/gorilla/sessions/go.mod b/vendor/github.com/gorilla/sessions/go.mod deleted file mode 100644 index 9028bcf1c85e2483484fa781374774da85eeecc8..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/gorilla/sessions - -require github.com/gorilla/securecookie v1.1.1 diff --git a/vendor/github.com/gorilla/sessions/go.sum b/vendor/github.com/gorilla/sessions/go.sum deleted file mode 100644 index e6a7ed5f359b1341228321bd21eceea74152697d..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -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= diff --git a/vendor/github.com/gorilla/sessions/lex.go b/vendor/github.com/gorilla/sessions/lex.go deleted file mode 100644 index 4bbbe1096de07cb9b0593feda60d90faea5ab2ba..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/lex.go +++ /dev/null @@ -1,102 +0,0 @@ -// 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/vendor/github.com/gorilla/sessions/options.go b/vendor/github.com/gorilla/sessions/options.go deleted file mode 100644 index 38ba72fb6c2f3be835510c6bd6a066bb061d3dc2..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/options.go +++ /dev/null @@ -1,18 +0,0 @@ -// +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/vendor/github.com/gorilla/sessions/options_go111.go b/vendor/github.com/gorilla/sessions/options_go111.go deleted file mode 100644 index 388112aad1c25a35e8bdcaf205add57a405d0b19..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/options_go111.go +++ /dev/null @@ -1,22 +0,0 @@ -// +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/vendor/github.com/gorilla/sessions/sessions.go b/vendor/github.com/gorilla/sessions/sessions.go deleted file mode 100644 index c052b289112da473c649455e4232d6ee99beaefc..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/sessions.go +++ /dev/null @@ -1,218 +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 sessions - -import ( - "context" - "encoding/gob" - "fmt" - "net/http" - "time" -) - -// Default flashes key. -const flashesKey = "_flash" - -// Session -------------------------------------------------------------------- - -// 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, - Options: new(Options), - } -} - -// Session stores the values and optional configuration for a session. -type Session struct { - // 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 - store Store - name string -} - -// Flashes returns a slice of flash messages from the session. -// -// A single variadic argument is accepted, and it is optional: it defines -// the flash key. If not defined "_flash" is used by default. -func (s *Session) Flashes(vars ...string) []interface{} { - var flashes []interface{} - key := flashesKey - if len(vars) > 0 { - key = vars[0] - } - if v, ok := s.Values[key]; ok { - // Drop the flashes and return it. - delete(s.Values, key) - flashes = v.([]interface{}) - } - return flashes -} - -// AddFlash adds a flash message to the session. -// -// A single variadic argument is accepted, and it is optional: it defines -// the flash key. If not defined "_flash" is used by default. -func (s *Session) AddFlash(value interface{}, vars ...string) { - key := flashesKey - if len(vars) > 0 { - key = vars[0] - } - var flashes []interface{} - if v, ok := s.Values[key]; ok { - flashes = v.([]interface{}) - } - s.Values[key] = append(flashes, value) -} - -// Save is a convenience method to save this session. It is the same as calling -// 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) -} - -// Name returns the name used to register the session. -func (s *Session) Name() string { - return s.name -} - -// Store returns the session store used to register the session. -func (s *Session) Store() Store { - return s.store -} - -// Registry ------------------------------------------------------------------- - -// sessionInfo stores a session tracked by the registry. -type sessionInfo struct { - s *Session - e error -} - -// contextKey is the type used to store the registry in the context. -type contextKey int - -// registryKey is the key used to store the registry in the context. -const registryKey contextKey = 0 - -// GetRegistry returns a registry instance for the current request. -func GetRegistry(r *http.Request) *Registry { - var ctx = r.Context() - registry := ctx.Value(registryKey) - if registry != nil { - return registry.(*Registry) - } - newRegistry := &Registry{ - request: r, - sessions: make(map[string]sessionInfo), - } - *r = *r.WithContext(context.WithValue(ctx, registryKey, newRegistry)) - return newRegistry -} - -// Registry stores sessions used during a request. -type Registry struct { - request *http.Request - sessions map[string]sessionInfo -} - -// Get registers and returns a session for the given name and session store. -// -// 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, fmt.Errorf("sessions: invalid character in cookie name: %s", name) - } - if info, ok := s.sessions[name]; ok { - session, err = info.s, info.e - } else { - session, err = store.New(s.request, name) - session.name = name - s.sessions[name] = sessionInfo{s: session, e: err} - } - session.store = store - return -} - -// Save saves all sessions registered for the current request. -func (s *Registry) Save(w http.ResponseWriter) error { - var errMulti MultiError - for name, info := range s.sessions { - session := info.s - if session.store == nil { - errMulti = append(errMulti, fmt.Errorf( - "sessions: missing store for session %q", name)) - } else if err := session.store.Save(s.request, w, session); err != nil { - errMulti = append(errMulti, fmt.Errorf( - "sessions: error saving session %q -- %v", name, err)) - } - } - if errMulti != nil { - return errMulti - } - return nil -} - -// Helpers -------------------------------------------------------------------- - -func init() { - gob.Register([]interface{}{}) -} - -// Save saves all sessions used during the current request. -func Save(r *http.Request, w http.ResponseWriter) error { - return GetRegistry(r).Save(w) -} - -// NewCookie returns an http.Cookie with the options set. It also sets -// the Expires field calculated based on the MaxAge value, for Internet -// Explorer compatibility. -func NewCookie(name, value string, options *Options) *http.Cookie { - cookie := newCookieFromOptions(name, value, options) - if options.MaxAge > 0 { - d := time.Duration(options.MaxAge) * time.Second - cookie.Expires = time.Now().Add(d) - } else if options.MaxAge < 0 { - // Set it to the past to expire now. - cookie.Expires = time.Unix(1, 0) - } - return cookie -} - -// Error ---------------------------------------------------------------------- - -// MultiError stores multiple errors. -// -// Borrowed from the App Engine SDK. -type MultiError []error - -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) -} diff --git a/vendor/github.com/gorilla/sessions/store.go b/vendor/github.com/gorilla/sessions/store.go deleted file mode 100644 index bb7f9647d6d15c8c17a13d4031b36f42aab1143f..0000000000000000000000000000000000000000 --- a/vendor/github.com/gorilla/sessions/store.go +++ /dev/null @@ -1,292 +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 sessions - -import ( - "encoding/base32" - "io/ioutil" - "net/http" - "os" - "path/filepath" - "strings" - "sync" - - "github.com/gorilla/securecookie" -) - -// Store is an interface for custom session stores. -// -// 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 -} - -// CookieStore ---------------------------------------------------------------- - -// NewCookieStore returns a new CookieStore. -// -// Keys are defined in pairs to allow key rotation, but the common case is -// to set a single authentication key and optionally an encryption key. -// -// The first key in a pair is used for authentication and the second for -// encryption. The encryption key can be set to nil or omitted in the last -// pair, but the authentication key is required in all pairs. -// -// 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. -func NewCookieStore(keyPairs ...[]byte) *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. -type CookieStore struct { - Codecs []securecookie.Codec - Options *Options // default configuration -} - -// Get returns a session for the given name after adding it to the registry. -// -// It returns a new session if the sessions doesn't exist. Access IsNew on -// the session to check if it is an existing session or a new one. -// -// It returns a new session and an error if the session exists but could -// not be decoded. -func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) { - return GetRegistry(r).Get(s, name) -} - -// New returns a session for the given name without adding it to the registry. -// -// The difference between New() and Get() is that calling New() twice will -// decode the session data twice, while Get() registers and reuses the same -// decoded session after the first call. -func (s *CookieStore) New(r *http.Request, name string) (*Session, error) { - session := NewSession(s, name) - opts := *s.Options - session.Options = &opts - session.IsNew = true - var err error - if c, errCookie := r.Cookie(name); errCookie == nil { - err = securecookie.DecodeMulti(name, c.Value, &session.Values, - s.Codecs...) - if err == nil { - session.IsNew = false - } - } - return session, err -} - -// Save adds a single session to the response. -func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter, - session *Session) error { - encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, - s.Codecs...) - if err != nil { - return err - } - http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options)) - 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 - -// NewFilesystemStore returns a new FilesystemStore. -// -// The path argument is the directory where sessions will be saved. If empty -// it will use os.TempDir(). -// -// See NewCookieStore() for a description of the other parameters. -func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore { - if path == "" { - path = os.TempDir() - } - fs := &FilesystemStore{ - Codecs: securecookie.CodecsFromPairs(keyPairs...), - Options: &Options{ - Path: "/", - MaxAge: 86400 * 30, - }, - path: path, - } - - fs.MaxAge(fs.Options.MaxAge) - return fs -} - -// FilesystemStore stores sessions in the filesystem. -// -// It also serves as a reference for custom stores. -// -// This store is still experimental and not well tested. Feedback is welcome. -type FilesystemStore struct { - Codecs []securecookie.Codec - Options *Options // default configuration - 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(). -func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) { - return GetRegistry(r).Get(s, name) -} - -// New returns a session for the given name without adding it to the registry. -// -// See CookieStore.New(). -func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) { - session := NewSession(s, name) - opts := *s.Options - session.Options = &opts - session.IsNew = true - var err error - if c, errCookie := r.Cookie(name); errCookie == nil { - err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...) - if err == nil { - err = s.load(session) - if err == nil { - session.IsNew = false - } - } - } - return session, err -} - -// 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. - session.ID = strings.TrimRight( - base32.StdEncoding.EncodeToString( - securecookie.GenerateRandomKey(32)), "=") - } - if err := s.save(session); err != nil { - return err - } - encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, - s.Codecs...) - if err != nil { - return err - } - http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options)) - 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, - s.Codecs...) - if err != nil { - return err - } - filename := filepath.Join(s.path, "session_"+session.ID) - fileMutex.Lock() - defer fileMutex.Unlock() - 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 := filepath.Join(s.path, "session_"+session.ID) - fileMutex.RLock() - defer fileMutex.RUnlock() - fdata, err := ioutil.ReadFile(filename) - if err != nil { - return err - } - if err = securecookie.DecodeMulti(session.Name(), string(fdata), - &session.Values, s.Codecs...); err != nil { - return err - } - 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/vendor/vendor.json b/vendor/vendor.json index 02e412820d9f5bf6fa27aa7a57f88cb8babe59eb..106fa139c100e774ea5421110d946256dcbc59de 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -134,12 +134,6 @@ "revision": "61b4ad17eb88d0d1118560d1101176279be2bc88", "revisionTime": "2019-10-28T04:23:04Z" }, - { - "checksumSHA1": "cwc8hi1B7gYshKGFEEhrsvSUsxU=", - "path": "github.com/gorilla/sessions", - "revision": "400b592ab70b9f8ee876cf47bc6c794d255dd4aa", - "revisionTime": "2019-10-06T15:13:01Z" - }, { "checksumSHA1": "SGc5vSs9tXhrGJ5ncymDyMvTg24=", "path": "github.com/jonboulle/clockwork",