session.go 11 KB

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