You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gitea-fork-majority-judgment/vendor/github.com/lafriks/xormstore/xormstore.go

252 lines
6.6 KiB

/*
Package xormstore is a XORM backend for gorilla sessions
Simplest form:
store, err := xormstore.New(engine, []byte("secret-hash-key"))
All options:
store, err := xormstore.NewOptions(
engine, // *xorm.Engine
xormstore.Options{
TableName: "sessions", // "sessions" is default
SkipCreateTable: false, // false is default
},
[]byte("secret-hash-key"), // 32 or 64 bytes recommended, required
[]byte("secret-encyption-key")) // nil, 16, 24 or 32 bytes, optional
if err != nil {
// xormstore can not be initialized
}
// some more settings, see sessions.Options
store.SessionOpts.Secure = true
store.SessionOpts.HttpOnly = true
store.SessionOpts.MaxAge = 60 * 60 * 24 * 60
If you want periodic cleanup of expired sessions:
quit := make(chan struct{})
go store.PeriodicCleanup(1*time.Hour, quit)
For more information about the keys see https://github.com/gorilla/securecookie
For API to use in HTTP handlers see https://github.com/gorilla/sessions
*/
package xormstore
import (
"encoding/base32"
"net/http"
"strings"
"time"
"github.com/lafriks/xormstore/util"
"github.com/go-xorm/xorm"
"github.com/gorilla/context"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
)
const sessionIDLen = 32
const defaultTableName = "sessions"
const defaultMaxAge = 60 * 60 * 24 * 30 // 30 days
const defaultPath = "/"
// Options for xormstore
type Options struct {
TableName string
SkipCreateTable bool
}
// Store represent a xormstore
type Store struct {
e *xorm.Engine
opts Options
Codecs []securecookie.Codec
SessionOpts *sessions.Options
}
type xormSession struct {
ID string `xorm:"VARCHAR(100) PK NAME 'id'"`
Data string `xorm:"TEXT"`
CreatedUnix util.TimeStamp `xorm:"created"`
UpdatedUnix util.TimeStamp `xorm:"updated"`
ExpiresUnix util.TimeStamp `xorm:"INDEX"`
tableName string `xorm:"-"` // just to store table name for easier access
}
// Define a type for context keys so that they can't clash with anything else stored in context
type contextKey string
func (xs *xormSession) TableName() string {
return xs.tableName
}
// New creates a new xormstore session
func New(e *xorm.Engine, keyPairs ...[]byte) (*Store, error) {
return NewOptions(e, Options{}, keyPairs...)
}
// NewOptions creates a new xormstore session with options
func NewOptions(e *xorm.Engine, opts Options, keyPairs ...[]byte) (*Store, error) {
st := &Store{
e: e,
opts: opts,
Codecs: securecookie.CodecsFromPairs(keyPairs...),
SessionOpts: &sessions.Options{
Path: defaultPath,
MaxAge: defaultMaxAge,
},
}
if st.opts.TableName == "" {
st.opts.TableName = defaultTableName
}
if !st.opts.SkipCreateTable {
if err := st.e.Sync2(&xormSession{tableName: st.opts.TableName}); err != nil {
return nil, err
}
}
return st, nil
}
// Get returns a session for the given name after adding it to the registry.
func (st *Store) Get(r *http.Request, name string) (*sessions.Session, error) {
return sessions.GetRegistry(r).Get(st, name)
}
// New creates a session with name without adding it to the registry.
func (st *Store) New(r *http.Request, name string) (*sessions.Session, error) {
session := sessions.NewSession(st, name)
opts := *st.SessionOpts
session.Options = &opts
st.MaxAge(st.SessionOpts.MaxAge)
// try fetch from db if there is a cookie
if cookie, err := r.Cookie(name); err == nil {
if err := securecookie.DecodeMulti(name, cookie.Value, &session.ID, st.Codecs...); err != nil {
return session, nil
}
s := &xormSession{tableName: st.opts.TableName}
if has, err := st.e.Where("id = ? AND expires_unix >= ?", session.ID, util.TimeStampNow()).Get(s); !has || err != nil {
return session, nil
}
if err := securecookie.DecodeMulti(session.Name(), s.Data, &session.Values, st.Codecs...); err != nil {
return session, nil
}
context.Set(r, contextKey(name), s)
}
return session, nil
}
// Save session and set cookie header
func (st *Store) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
s, _ := context.Get(r, contextKey(session.Name())).(*xormSession)
// delete if max age is < 0
if session.Options.MaxAge < 0 {
if s != nil {
if _, err := st.e.Delete(&xormSession{
ID: session.ID,
tableName: st.opts.TableName,
}); err != nil {
return err
}
}
http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
return nil
}
data, err := securecookie.EncodeMulti(session.Name(), session.Values, st.Codecs...)
if err != nil {
return err
}
now := util.TimeStampNow()
expire := now.AddDuration(time.Second * time.Duration(session.Options.MaxAge))
if s == nil {
// generate random session ID key suitable for storage in the db
session.ID = strings.TrimRight(
base32.StdEncoding.EncodeToString(
securecookie.GenerateRandomKey(sessionIDLen)), "=")
s = &xormSession{
ID: session.ID,
Data: data,
CreatedUnix: now,
UpdatedUnix: now,
ExpiresUnix: expire,
tableName: st.opts.TableName,
}
if _, err := st.e.Insert(s); err != nil {
return err
}
context.Set(r, contextKey(session.Name()), s)
} else {
s.Data = data
s.UpdatedUnix = now
s.ExpiresUnix = expire
if _, err := st.e.ID(s.ID).Cols("data", "updated_unix", "expires_unix").Update(s); err != nil {
return err
}
}
// set session id cookie
id, err := securecookie.EncodeMulti(session.Name(), session.ID, st.Codecs...)
if err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), id, 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 (st *Store) MaxAge(age int) {
st.SessionOpts.MaxAge = age
for _, codec := range st.Codecs {
if sc, ok := codec.(*securecookie.SecureCookie); ok {
sc.MaxAge(age)
}
}
}
// 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 is 4096 (default for securecookie)
func (st *Store) MaxLength(l int) {
for _, c := range st.Codecs {
if codec, ok := c.(*securecookie.SecureCookie); ok {
codec.MaxLength(l)
}
}
}
// Cleanup deletes expired sessions
func (st *Store) Cleanup() {
st.e.Where("expires_unix < ?", util.TimeStampNow()).Delete(&xormSession{tableName: st.opts.TableName})
}
// PeriodicCleanup runs Cleanup every interval. Close quit channel to stop.
func (st *Store) PeriodicCleanup(interval time.Duration, quit <-chan struct{}) {
t := time.NewTicker(interval)
defer t.Stop()
for {
select {
case <-t.C:
st.Cleanup()
case <-quit:
return
}
}
}