diff options
Diffstat (limited to 'vendor/gitea.com/macaron/session/session.go')
-rw-r--r-- | vendor/gitea.com/macaron/session/session.go | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/vendor/gitea.com/macaron/session/session.go b/vendor/gitea.com/macaron/session/session.go new file mode 100644 index 0000000000..93f18342d0 --- /dev/null +++ b/vendor/gitea.com/macaron/session/session.go @@ -0,0 +1,393 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +// Package session a middleware that provides the session management of Macaron. +package session + +import ( + "encoding/hex" + "errors" + "fmt" + "net/http" + "net/url" + "time" + + "gitea.com/macaron/macaron" +) + +const _VERSION = "0.6.0" + +func Version() string { + return _VERSION +} + +// RawStore is the interface that operates the session data. +type RawStore interface { + // Set sets value to given key in session. + Set(interface{}, interface{}) error + // Get gets value by given key in session. + Get(interface{}) interface{} + // Delete deletes a key from session. + Delete(interface{}) error + // ID returns current session ID. + ID() string + // Release releases session resource and save data to provider. + Release() error + // Flush deletes all session data. + Flush() error +} + +// Store is the interface that contains all data for one session process with specific ID. +type Store interface { + RawStore + // Read returns raw session store by session ID. + Read(string) (RawStore, error) + // Destroy deletes a session. + Destroy(*macaron.Context) error + // RegenerateId regenerates a session store from old session ID to new one. + RegenerateId(*macaron.Context) (RawStore, error) + // Count counts and returns number of sessions. + Count() int + // GC calls GC to clean expired sessions. + GC() +} + +type store struct { + RawStore + *Manager +} + +var _ Store = &store{} + +// Options represents a struct for specifying configuration options for the session middleware. +type Options struct { + // Name of provider. Default is "memory". + Provider string + // Provider configuration, it's corresponding to provider. + ProviderConfig string + // Cookie name to save session ID. Default is "MacaronSession". + CookieName string + // Cookie path to store. Default is "/". + CookiePath string + // GC interval time in seconds. Default is 3600. + Gclifetime int64 + // Max life time in seconds. Default is whatever GC interval time is. + Maxlifetime int64 + // Use HTTPS only. Default is false. + Secure bool + // Cookie life time. Default is 0. + CookieLifeTime int + // Cookie domain name. Default is empty. + Domain string + // Session ID length. Default is 16. + IDLength int + // Configuration section name. Default is "session". + Section string + // Ignore release for websocket. Default is false. + IgnoreReleaseForWebSocket bool +} + +func prepareOptions(options []Options) Options { + var opt Options + if len(options) > 0 { + opt = options[0] + } + if len(opt.Section) == 0 { + opt.Section = "session" + } + sec := macaron.Config().Section(opt.Section) + + if len(opt.Provider) == 0 { + opt.Provider = sec.Key("PROVIDER").MustString("memory") + } + if len(opt.ProviderConfig) == 0 { + opt.ProviderConfig = sec.Key("PROVIDER_CONFIG").MustString("data/sessions") + } + if len(opt.CookieName) == 0 { + opt.CookieName = sec.Key("COOKIE_NAME").MustString("MacaronSession") + } + if len(opt.CookiePath) == 0 { + opt.CookiePath = sec.Key("COOKIE_PATH").MustString("/") + } + if opt.Gclifetime == 0 { + opt.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(3600) + } + if opt.Maxlifetime == 0 { + opt.Maxlifetime = sec.Key("MAX_LIFE_TIME").MustInt64(opt.Gclifetime) + } + if !opt.Secure { + opt.Secure = sec.Key("SECURE").MustBool() + } + if opt.CookieLifeTime == 0 { + opt.CookieLifeTime = sec.Key("COOKIE_LIFE_TIME").MustInt() + } + if len(opt.Domain) == 0 { + opt.Domain = sec.Key("DOMAIN").String() + } + if opt.IDLength == 0 { + opt.IDLength = sec.Key("ID_LENGTH").MustInt(16) + } + if !opt.IgnoreReleaseForWebSocket { + opt.IgnoreReleaseForWebSocket = sec.Key("IGNORE_RELEASE_FOR_WEBSOCKET").MustBool() + } + + return opt +} + +// Sessioner is a middleware that maps a session.SessionStore service into the Macaron handler chain. +// An single variadic session.Options struct can be optionally provided to configure. +func Sessioner(options ...Options) macaron.Handler { + opt := prepareOptions(options) + manager, err := NewManager(opt.Provider, opt) + if err != nil { + panic(err) + } + go manager.startGC() + + return func(ctx *macaron.Context) { + sess, err := manager.Start(ctx) + if err != nil { + panic("session(start): " + err.Error()) + } + + // Get flash. + vals, _ := url.ParseQuery(ctx.GetCookie("macaron_flash")) + if len(vals) > 0 { + f := &Flash{Values: vals} + f.ErrorMsg = f.Get("error") + f.SuccessMsg = f.Get("success") + f.InfoMsg = f.Get("info") + f.WarningMsg = f.Get("warning") + ctx.Data["Flash"] = f + ctx.SetCookie("macaron_flash", "", -1, opt.CookiePath) + } + + f := &Flash{ctx, url.Values{}, "", "", "", ""} + ctx.Resp.Before(func(macaron.ResponseWriter) { + if flash := f.Encode(); len(flash) > 0 { + ctx.SetCookie("macaron_flash", flash, 0, opt.CookiePath) + } + }) + + ctx.Map(f) + s := store{ + RawStore: sess, + Manager: manager, + } + + ctx.MapTo(s, (*Store)(nil)) + + ctx.Next() + + if manager.opt.IgnoreReleaseForWebSocket && ctx.Req.Header.Get("Upgrade") == "websocket" { + return + } + + if err = sess.Release(); err != nil { + panic("session(release): " + err.Error()) + } + } +} + +// Provider is the interface that provides session manipulations. +type Provider interface { + // Init initializes session provider. + Init(gclifetime int64, config string) error + // Read returns raw session store by session ID. + Read(sid string) (RawStore, error) + // Exist returns true if session with given ID exists. + Exist(sid string) bool + // Destroy deletes a session by session ID. + Destroy(sid string) error + // Regenerate regenerates a session store from old session ID to new one. + Regenerate(oldsid, sid string) (RawStore, error) + // Count counts and returns number of sessions. + Count() int + // GC calls GC to clean expired sessions. + GC() +} + +var providers = make(map[string]Provider) + +// Register registers a provider. +func Register(name string, provider Provider) { + if provider == nil { + panic("session: cannot register provider with nil value") + } + if _, dup := providers[name]; dup { + panic(fmt.Errorf("session: cannot register provider '%s' twice", name)) + } + providers[name] = provider +} + +// _____ +// / \ _____ ____ _____ ____ ___________ +// / \ / \\__ \ / \\__ \ / ___\_/ __ \_ __ \ +// / Y \/ __ \| | \/ __ \_/ /_/ > ___/| | \/ +// \____|__ (____ /___| (____ /\___ / \___ >__| +// \/ \/ \/ \//_____/ \/ + +// Manager represents a struct that contains session provider and its configuration. +type Manager struct { + provider Provider + opt Options +} + +// NewManager creates and returns a new session manager by given provider name and configuration. +// It panics when given provider isn't registered. +func NewManager(name string, opt Options) (*Manager, error) { + p, ok := providers[name] + if !ok { + return nil, fmt.Errorf("session: unknown provider '%s'(forgotten import?)", name) + } + return &Manager{p, opt}, p.Init(opt.Maxlifetime, opt.ProviderConfig) +} + +// sessionID generates a new session ID with rand string, unix nano time, remote addr by hash function. +func (m *Manager) sessionID() string { + return hex.EncodeToString(generateRandomKey(m.opt.IDLength / 2)) +} + +// validSessionID tests whether a provided session ID is a valid session ID. +func (m *Manager) validSessionID(sid string) (bool, error) { + if len(sid) != m.opt.IDLength { + return false, errors.New("invalid 'sid': " + sid) + } + + for i := range sid { + switch { + case '0' <= sid[i] && sid[i] <= '9': + case 'a' <= sid[i] && sid[i] <= 'f': + default: + return false, errors.New("invalid 'sid': " + sid) + } + } + return true, nil +} + +// Start starts a session by generating new one +// or retrieve existence one by reading session ID from HTTP request if it's valid. +func (m *Manager) Start(ctx *macaron.Context) (RawStore, error) { + sid := ctx.GetCookie(m.opt.CookieName) + valid, _ := m.validSessionID(sid) + if len(sid) > 0 && valid && m.provider.Exist(sid) { + return m.provider.Read(sid) + } + + sid = m.sessionID() + sess, err := m.provider.Read(sid) + if err != nil { + return nil, err + } + + cookie := &http.Cookie{ + Name: m.opt.CookieName, + Value: sid, + Path: m.opt.CookiePath, + HttpOnly: true, + Secure: m.opt.Secure, + Domain: m.opt.Domain, + } + if m.opt.CookieLifeTime >= 0 { + cookie.MaxAge = m.opt.CookieLifeTime + } + http.SetCookie(ctx.Resp, cookie) + ctx.Req.AddCookie(cookie) + return sess, nil +} + +// Read returns raw session store by session ID. +func (m *Manager) Read(sid string) (RawStore, error) { + // Ensure we're trying to read a valid session ID + if _, err := m.validSessionID(sid); err != nil { + return nil, err + } + + return m.provider.Read(sid) +} + +// Destroy deletes a session by given ID. +func (m *Manager) Destroy(ctx *macaron.Context) error { + sid := ctx.GetCookie(m.opt.CookieName) + if len(sid) == 0 { + return nil + } + + if _, err := m.validSessionID(sid); err != nil { + return err + } + + if err := m.provider.Destroy(sid); err != nil { + return err + } + cookie := &http.Cookie{ + Name: m.opt.CookieName, + Path: m.opt.CookiePath, + HttpOnly: true, + Expires: time.Now(), + MaxAge: -1, + } + http.SetCookie(ctx.Resp, cookie) + return nil +} + +// RegenerateId regenerates a session store from old session ID to new one. +func (m *Manager) RegenerateId(ctx *macaron.Context) (sess RawStore, err error) { + sid := m.sessionID() + oldsid := ctx.GetCookie(m.opt.CookieName) + _, err = m.validSessionID(oldsid) + if err != nil { + return nil, err + } + sess, err = m.provider.Regenerate(oldsid, sid) + if err != nil { + return nil, err + } + cookie := &http.Cookie{ + Name: m.opt.CookieName, + Value: sid, + Path: m.opt.CookiePath, + HttpOnly: true, + Secure: m.opt.Secure, + Domain: m.opt.Domain, + } + if m.opt.CookieLifeTime >= 0 { + cookie.MaxAge = m.opt.CookieLifeTime + } + http.SetCookie(ctx.Resp, cookie) + ctx.Req.AddCookie(cookie) + return sess, nil +} + +// Count counts and returns number of sessions. +func (m *Manager) Count() int { + return m.provider.Count() +} + +// GC starts GC job in a certain period. +func (m *Manager) GC() { + m.provider.GC() +} + +// startGC starts GC job in a certain period. +func (m *Manager) startGC() { + m.GC() + time.AfterFunc(time.Duration(m.opt.Gclifetime)*time.Second, func() { m.startGC() }) +} + +// SetSecure indicates whether to set cookie with HTTPS or not. +func (m *Manager) SetSecure(secure bool) { + m.opt.Secure = secure +} |