about summary refs log tree commit diff
path: root/vendor/github.com/gorilla/sessions/store.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gorilla/sessions/store.go')
-rw-r--r--vendor/github.com/gorilla/sessions/store.go298
1 files changed, 298 insertions, 0 deletions
diff --git a/vendor/github.com/gorilla/sessions/store.go b/vendor/github.com/gorilla/sessions/store.go
new file mode 100644
index 0000000..24db822
--- /dev/null
+++ b/vendor/github.com/gorilla/sessions/store.go
@@ -0,0 +1,298 @@
+// 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"
+	"net/http"
+	"os"
+	"path/filepath"
+	"sync"
+
+	"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.
+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,
+			SameSite: http.SameSiteNoneMode,
+			Secure:   true,
+		},
+	}
+
+	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
+}
+
+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
+// 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 && !os.IsNotExist(err) {
+			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 = base32RawStdEncoding.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, sessionFilePrefix+filepath.Base(session.ID))
+	fileMutex.Lock()
+	defer fileMutex.Unlock()
+	return os.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, sessionFilePrefix+filepath.Base(session.ID))
+	fileMutex.RLock()
+	defer fileMutex.RUnlock()
+	fdata, err := os.ReadFile(filepath.Clean(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, sessionFilePrefix+filepath.Base(session.ID))
+
+	fileMutex.RLock()
+	defer fileMutex.RUnlock()
+
+	err := os.Remove(filename)
+	return err
+}