github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/sessionstore/kv.go (about)

     1  package sessionstore
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base32"
     6  	"encoding/gob"
     7  	"github.com/cloudreve/Cloudreve/v3/pkg/cache"
     8  	"github.com/gorilla/securecookie"
     9  	"github.com/gorilla/sessions"
    10  	"net/http"
    11  	"strings"
    12  )
    13  
    14  type kvStore struct {
    15  	Codecs        []securecookie.Codec
    16  	Options       *sessions.Options
    17  	DefaultMaxAge int
    18  
    19  	prefix     string
    20  	serializer SessionSerializer
    21  	store      cache.Driver
    22  }
    23  
    24  func newKvStore(prefix string, store cache.Driver, keyPairs ...[]byte) *kvStore {
    25  	return &kvStore{
    26  		prefix:        prefix,
    27  		store:         store,
    28  		DefaultMaxAge: 60 * 20,
    29  		serializer:    GobSerializer{},
    30  		Codecs:        securecookie.CodecsFromPairs(keyPairs...),
    31  		Options: &sessions.Options{
    32  			Path:   "/",
    33  			MaxAge: 86400 * 30,
    34  		},
    35  	}
    36  }
    37  
    38  // Get returns a session for the given name after adding it to the registry.
    39  //
    40  // It returns a new session if the sessions doesn't exist. Access IsNew on
    41  // the session to check if it is an existing session or a new one.
    42  //
    43  // It returns a new session and an error if the session exists but could
    44  // not be decoded.
    45  func (s *kvStore) Get(r *http.Request, name string) (*sessions.Session, error) {
    46  	return sessions.GetRegistry(r).Get(s, name)
    47  }
    48  
    49  // New returns a session for the given name without adding it to the registry.
    50  //
    51  // The difference between New() and Get() is that calling New() twice will
    52  // decode the session data twice, while Get() registers and reuses the same
    53  // decoded session after the first call.
    54  func (s *kvStore) New(r *http.Request, name string) (*sessions.Session, error) {
    55  	var (
    56  		err error
    57  	)
    58  	session := sessions.NewSession(s, name)
    59  	// make a copy
    60  	options := *s.Options
    61  	session.Options = &options
    62  	session.IsNew = true
    63  	if c, errCookie := r.Cookie(name); errCookie == nil {
    64  		err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
    65  		if err == nil {
    66  			res, ok := s.store.Get(s.prefix + session.ID)
    67  			if ok {
    68  				err = s.serializer.Deserialize(res.([]byte), session)
    69  			}
    70  
    71  			session.IsNew = !(err == nil && ok) // not new if no error and data available
    72  		}
    73  	}
    74  	return session, err
    75  }
    76  func (s *kvStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
    77  	// Marked for deletion.
    78  	if session.Options.MaxAge <= 0 {
    79  		if err := s.store.Delete([]string{session.ID}, s.prefix); err != nil {
    80  			return err
    81  		}
    82  		http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
    83  	} else {
    84  		// Build an alphanumeric key for the redis store.
    85  		if session.ID == "" {
    86  			session.ID = strings.TrimRight(base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)), "=")
    87  		}
    88  
    89  		b, err := s.serializer.Serialize(session)
    90  		if err != nil {
    91  			return err
    92  		}
    93  
    94  		age := session.Options.MaxAge
    95  		if age == 0 {
    96  			age = s.DefaultMaxAge
    97  		}
    98  
    99  		if err := s.store.Set(s.prefix+session.ID, b, age); err != nil {
   100  			return err
   101  		}
   102  
   103  		encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, s.Codecs...)
   104  		if err != nil {
   105  			return err
   106  		}
   107  		http.SetCookie(w, sessions.NewCookie(session.Name(), encoded, session.Options))
   108  	}
   109  	return nil
   110  }
   111  
   112  // SessionSerializer provides an interface hook for alternative serializers
   113  type SessionSerializer interface {
   114  	Deserialize(d []byte, ss *sessions.Session) error
   115  	Serialize(ss *sessions.Session) ([]byte, error)
   116  }
   117  
   118  // GobSerializer uses gob package to encode the session map
   119  type GobSerializer struct{}
   120  
   121  // Serialize using gob
   122  func (s GobSerializer) Serialize(ss *sessions.Session) ([]byte, error) {
   123  	buf := new(bytes.Buffer)
   124  	enc := gob.NewEncoder(buf)
   125  	err := enc.Encode(ss.Values)
   126  	if err == nil {
   127  		return buf.Bytes(), nil
   128  	}
   129  	return nil, err
   130  }
   131  
   132  // Deserialize back to map[interface{}]interface{}
   133  func (s GobSerializer) Deserialize(d []byte, ss *sessions.Session) error {
   134  	dec := gob.NewDecoder(bytes.NewBuffer(d))
   135  	return dec.Decode(&ss.Values)
   136  }