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.

399 lines
11 KiB

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