session.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. // Copyright 2013 Beego Authors
  2. // Copyright 2014 The Macaron Authors
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  5. // not use this file except in compliance with the License. You may obtain
  6. // a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. // License for the specific language governing permissions and limitations
  14. // under the License.
  15. // Package session a middleware that provides the session management of Macaron.
  16. package session
  17. import (
  18. "encoding/hex"
  19. "errors"
  20. "fmt"
  21. "net/http"
  22. "net/url"
  23. "strings"
  24. "time"
  25. "gopkg.in/macaron.v1"
  26. )
  27. const _VERSION = "0.4.0"
  28. func Version() string {
  29. return _VERSION
  30. }
  31. // RawStore is the interface that operates the session data.
  32. type RawStore interface {
  33. // Set sets value to given key in session.
  34. Set(interface{}, interface{}) error
  35. // Get gets value by given key in session.
  36. Get(interface{}) interface{}
  37. // Delete deletes a key from session.
  38. Delete(interface{}) error
  39. // ID returns current session ID.
  40. ID() string
  41. // Release releases session resource and save data to provider.
  42. Release() error
  43. // Flush deletes all session data.
  44. Flush() error
  45. }
  46. // Store is the interface that contains all data for one session process with specific ID.
  47. type Store interface {
  48. RawStore
  49. // Read returns raw session store by session ID.
  50. Read(string) (RawStore, error)
  51. // Destory deletes a session.
  52. Destory(*macaron.Context) error
  53. // RegenerateId regenerates a session store from old session ID to new one.
  54. RegenerateId(*macaron.Context) (RawStore, error)
  55. // Count counts and returns number of sessions.
  56. Count() int
  57. // GC calls GC to clean expired sessions.
  58. GC()
  59. }
  60. type store struct {
  61. RawStore
  62. *Manager
  63. }
  64. var _ Store = &store{}
  65. // Options represents a struct for specifying configuration options for the session middleware.
  66. type Options struct {
  67. // Name of provider. Default is "memory".
  68. Provider string
  69. // Provider configuration, it's corresponding to provider.
  70. ProviderConfig string
  71. // Cookie name to save session ID. Default is "MacaronSession".
  72. CookieName string
  73. // Cookie path to store. Default is "/".
  74. CookiePath string
  75. // GC interval time in seconds. Default is 3600.
  76. Gclifetime int64
  77. // Max life time in seconds. Default is whatever GC interval time is.
  78. Maxlifetime int64
  79. // Use HTTPS only. Default is false.
  80. Secure bool
  81. // Cookie life time. Default is 0.
  82. CookieLifeTime int
  83. // Cookie domain name. Default is empty.
  84. Domain string
  85. // Session ID length. Default is 16.
  86. IDLength int
  87. // Configuration section name. Default is "session".
  88. Section string
  89. }
  90. func prepareOptions(options []Options) Options {
  91. var opt Options
  92. if len(options) > 0 {
  93. opt = options[0]
  94. }
  95. if len(opt.Section) == 0 {
  96. opt.Section = "session"
  97. }
  98. sec := macaron.Config().Section(opt.Section)
  99. if len(opt.Provider) == 0 {
  100. opt.Provider = sec.Key("PROVIDER").MustString("memory")
  101. }
  102. if len(opt.ProviderConfig) == 0 {
  103. opt.ProviderConfig = sec.Key("PROVIDER_CONFIG").MustString("data/sessions")
  104. }
  105. if len(opt.CookieName) == 0 {
  106. opt.CookieName = sec.Key("COOKIE_NAME").MustString("MacaronSession")
  107. }
  108. if len(opt.CookiePath) == 0 {
  109. opt.CookiePath = sec.Key("COOKIE_PATH").MustString("/")
  110. }
  111. if opt.Gclifetime == 0 {
  112. opt.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(3600)
  113. }
  114. if opt.Maxlifetime == 0 {
  115. opt.Maxlifetime = sec.Key("MAX_LIFE_TIME").MustInt64(opt.Gclifetime)
  116. }
  117. if !opt.Secure {
  118. opt.Secure = sec.Key("SECURE").MustBool()
  119. }
  120. if opt.CookieLifeTime == 0 {
  121. opt.CookieLifeTime = sec.Key("COOKIE_LIFE_TIME").MustInt()
  122. }
  123. if len(opt.Domain) == 0 {
  124. opt.Domain = sec.Key("DOMAIN").String()
  125. }
  126. if opt.IDLength == 0 {
  127. opt.IDLength = sec.Key("ID_LENGTH").MustInt(16)
  128. }
  129. return opt
  130. }
  131. // Sessioner is a middleware that maps a session.SessionStore service into the Macaron handler chain.
  132. // An single variadic session.Options struct can be optionally provided to configure.
  133. func Sessioner(options ...Options) macaron.Handler {
  134. opt := prepareOptions(options)
  135. manager, err := NewManager(opt.Provider, opt)
  136. if err != nil {
  137. panic(err)
  138. }
  139. go manager.startGC()
  140. return func(ctx *macaron.Context) {
  141. sess, err := manager.Start(ctx)
  142. if err != nil {
  143. panic("session(start): " + err.Error())
  144. }
  145. // Get flash.
  146. vals, _ := url.ParseQuery(ctx.GetCookie("macaron_flash"))
  147. if len(vals) > 0 {
  148. f := &Flash{Values: vals}
  149. f.ErrorMsg = f.Get("error")
  150. f.SuccessMsg = f.Get("success")
  151. f.InfoMsg = f.Get("info")
  152. f.WarningMsg = f.Get("warning")
  153. ctx.Data["Flash"] = f
  154. ctx.SetCookie("macaron_flash", "", -1, opt.CookiePath)
  155. }
  156. f := &Flash{ctx, url.Values{}, "", "", "", ""}
  157. ctx.Resp.Before(func(macaron.ResponseWriter) {
  158. if flash := f.Encode(); len(flash) > 0 {
  159. ctx.SetCookie("macaron_flash", flash, 0, opt.CookiePath)
  160. }
  161. })
  162. ctx.Map(f)
  163. s := store{
  164. RawStore: sess,
  165. Manager: manager,
  166. }
  167. ctx.MapTo(s, (*Store)(nil))
  168. ctx.Next()
  169. if err = sess.Release(); err != nil {
  170. panic("session(release): " + err.Error())
  171. }
  172. }
  173. }
  174. // Provider is the interface that provides session manipulations.
  175. type Provider interface {
  176. // Init initializes session provider.
  177. Init(gclifetime int64, config string) error
  178. // Read returns raw session store by session ID.
  179. Read(sid string) (RawStore, error)
  180. // Exist returns true if session with given ID exists.
  181. Exist(sid string) bool
  182. // Destory deletes a session by session ID.
  183. Destory(sid string) error
  184. // Regenerate regenerates a session store from old session ID to new one.
  185. Regenerate(oldsid, sid string) (RawStore, error)
  186. // Count counts and returns number of sessions.
  187. Count() int
  188. // GC calls GC to clean expired sessions.
  189. GC()
  190. }
  191. var providers = make(map[string]Provider)
  192. // Register registers a provider.
  193. func Register(name string, provider Provider) {
  194. if provider == nil {
  195. panic("session: cannot register provider with nil value")
  196. }
  197. if _, dup := providers[name]; dup {
  198. panic(fmt.Errorf("session: cannot register provider '%s' twice", name))
  199. }
  200. providers[name] = provider
  201. }
  202. // _____
  203. // / \ _____ ____ _____ ____ ___________
  204. // / \ / \\__ \ / \\__ \ / ___\_/ __ \_ __ \
  205. // / Y \/ __ \| | \/ __ \_/ /_/ > ___/| | \/
  206. // \____|__ (____ /___| (____ /\___ / \___ >__|
  207. // \/ \/ \/ \//_____/ \/
  208. // Manager represents a struct that contains session provider and its configuration.
  209. type Manager struct {
  210. provider Provider
  211. opt Options
  212. }
  213. // NewManager creates and returns a new session manager by given provider name and configuration.
  214. // It panics when given provider isn't registered.
  215. func NewManager(name string, opt Options) (*Manager, error) {
  216. p, ok := providers[name]
  217. if !ok {
  218. return nil, fmt.Errorf("session: unknown provider '%s'(forgotten import?)", name)
  219. }
  220. return &Manager{p, opt}, p.Init(opt.Maxlifetime, opt.ProviderConfig)
  221. }
  222. // sessionID generates a new session ID with rand string, unix nano time, remote addr by hash function.
  223. func (m *Manager) sessionID() string {
  224. return hex.EncodeToString(generateRandomKey(m.opt.IDLength / 2))
  225. }
  226. // Start starts a session by generating new one
  227. // or retrieve existence one by reading session ID from HTTP request if it's valid.
  228. func (m *Manager) Start(ctx *macaron.Context) (RawStore, error) {
  229. sid := ctx.GetCookie(m.opt.CookieName)
  230. if len(sid) > 0 && m.provider.Exist(sid) {
  231. return m.provider.Read(sid)
  232. }
  233. sid = m.sessionID()
  234. sess, err := m.provider.Read(sid)
  235. if err != nil {
  236. return nil, err
  237. }
  238. cookie := &http.Cookie{
  239. Name: m.opt.CookieName,
  240. Value: sid,
  241. Path: m.opt.CookiePath,
  242. HttpOnly: true,
  243. Secure: m.opt.Secure,
  244. Domain: m.opt.Domain,
  245. }
  246. if m.opt.CookieLifeTime >= 0 {
  247. cookie.MaxAge = m.opt.CookieLifeTime
  248. }
  249. http.SetCookie(ctx.Resp, cookie)
  250. ctx.Req.AddCookie(cookie)
  251. return sess, nil
  252. }
  253. // Read returns raw session store by session ID.
  254. func (m *Manager) Read(sid string) (RawStore, error) {
  255. // No slashes or dots "./" should ever occur in the sid and to prevent session file forgery bug.
  256. // See https://github.com/gogs/gogs/issues/5469
  257. if strings.ContainsAny(sid, "./") {
  258. return nil, errors.New("invalid 'sid': " + sid)
  259. }
  260. return m.provider.Read(sid)
  261. }
  262. // Destory deletes a session by given ID.
  263. func (m *Manager) Destory(ctx *macaron.Context) error {
  264. sid := ctx.GetCookie(m.opt.CookieName)
  265. if len(sid) == 0 {
  266. return nil
  267. }
  268. if err := m.provider.Destory(sid); err != nil {
  269. return err
  270. }
  271. cookie := &http.Cookie{
  272. Name: m.opt.CookieName,
  273. Path: m.opt.CookiePath,
  274. HttpOnly: true,
  275. Expires: time.Now(),
  276. MaxAge: -1,
  277. }
  278. http.SetCookie(ctx.Resp, cookie)
  279. return nil
  280. }
  281. // RegenerateId regenerates a session store from old session ID to new one.
  282. func (m *Manager) RegenerateId(ctx *macaron.Context) (sess RawStore, err error) {
  283. sid := m.sessionID()
  284. oldsid := ctx.GetCookie(m.opt.CookieName)
  285. sess, err = m.provider.Regenerate(oldsid, sid)
  286. if err != nil {
  287. return nil, err
  288. }
  289. ck := &http.Cookie{
  290. Name: m.opt.CookieName,
  291. Value: sid,
  292. Path: m.opt.CookiePath,
  293. HttpOnly: true,
  294. Secure: m.opt.Secure,
  295. Domain: m.opt.Domain,
  296. }
  297. if m.opt.CookieLifeTime >= 0 {
  298. ck.MaxAge = m.opt.CookieLifeTime
  299. }
  300. http.SetCookie(ctx.Resp, ck)
  301. ctx.Req.AddCookie(ck)
  302. return sess, nil
  303. }
  304. // Count counts and returns number of sessions.
  305. func (m *Manager) Count() int {
  306. return m.provider.Count()
  307. }
  308. // GC starts GC job in a certain period.
  309. func (m *Manager) GC() {
  310. m.provider.GC()
  311. }
  312. // startGC starts GC job in a certain period.
  313. func (m *Manager) startGC() {
  314. m.GC()
  315. time.AfterFunc(time.Duration(m.opt.Gclifetime)*time.Second, func() { m.startGC() })
  316. }
  317. // SetSecure indicates whether to set cookie with HTTPS or not.
  318. func (m *Manager) SetSecure(secure bool) {
  319. m.opt.Secure = secure
  320. }
  321. // ___________.____ _____ _________ ___ ___
  322. // \_ _____/| | / _ \ / _____// | \
  323. // | __) | | / /_\ \ \_____ \/ ~ \
  324. // | \ | |___/ | \/ \ Y /
  325. // \___ / |_______ \____|__ /_______ /\___|_ /
  326. // \/ \/ \/ \/ \/
  327. type Flash struct {
  328. ctx *macaron.Context
  329. url.Values
  330. ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
  331. }
  332. func (f *Flash) set(name, msg string, current ...bool) {
  333. isShow := false
  334. if (len(current) == 0 && macaron.FlashNow) ||
  335. (len(current) > 0 && current[0]) {
  336. isShow = true
  337. }
  338. if isShow {
  339. f.ctx.Data["Flash"] = f
  340. } else {
  341. f.Set(name, msg)
  342. }
  343. }
  344. func (f *Flash) Error(msg string, current ...bool) {
  345. f.ErrorMsg = msg
  346. f.set("error", msg, current...)
  347. }
  348. func (f *Flash) Warning(msg string, current ...bool) {
  349. f.WarningMsg = msg
  350. f.set("warning", msg, current...)
  351. }
  352. func (f *Flash) Info(msg string, current ...bool) {
  353. f.InfoMsg = msg
  354. f.set("info", msg, current...)
  355. }
  356. func (f *Flash) Success(msg string, current ...bool) {
  357. f.SuccessMsg = msg
  358. f.set("success", msg, current...)
  359. }