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.

192 lines
5.0 KiB

  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package session
  5. import (
  6. "container/list"
  7. "encoding/json"
  8. "fmt"
  9. "sync"
  10. "gitea.com/macaron/session"
  11. couchbase "gitea.com/macaron/session/couchbase"
  12. memcache "gitea.com/macaron/session/memcache"
  13. mysql "gitea.com/macaron/session/mysql"
  14. nodb "gitea.com/macaron/session/nodb"
  15. postgres "gitea.com/macaron/session/postgres"
  16. redis "gitea.com/macaron/session/redis"
  17. )
  18. // VirtualSessionProvider represents a shadowed session provider implementation.
  19. type VirtualSessionProvider struct {
  20. lock sync.RWMutex
  21. provider session.Provider
  22. }
  23. // Init initializes the cookie session provider with given root path.
  24. func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error {
  25. var opts session.Options
  26. if err := json.Unmarshal([]byte(config), &opts); err != nil {
  27. return err
  28. }
  29. // Note that these options are unprepared so we can't just use NewManager here.
  30. // Nor can we access the provider map in session.
  31. // So we will just have to do this by hand.
  32. // This is only slightly more wrong than modules/setting/session.go:23
  33. switch opts.Provider {
  34. case "memory":
  35. o.provider = &MemProvider{list: list.New(), data: make(map[string]*list.Element)}
  36. case "file":
  37. o.provider = &session.FileProvider{}
  38. case "redis":
  39. o.provider = &redis.RedisProvider{}
  40. case "mysql":
  41. o.provider = &mysql.MysqlProvider{}
  42. case "postgres":
  43. o.provider = &postgres.PostgresProvider{}
  44. case "couchbase":
  45. o.provider = &couchbase.CouchbaseProvider{}
  46. case "memcache":
  47. o.provider = &memcache.MemcacheProvider{}
  48. case "nodb":
  49. o.provider = &nodb.NodbProvider{}
  50. default:
  51. return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider)
  52. }
  53. return o.provider.Init(gclifetime, opts.ProviderConfig)
  54. }
  55. // Read returns raw session store by session ID.
  56. func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
  57. o.lock.RLock()
  58. defer o.lock.RUnlock()
  59. if o.provider.Exist(sid) {
  60. return o.provider.Read(sid)
  61. }
  62. kv := make(map[interface{}]interface{})
  63. kv["_old_uid"] = "0"
  64. return NewVirtualStore(o, sid, kv), nil
  65. }
  66. // Exist returns true if session with given ID exists.
  67. func (o *VirtualSessionProvider) Exist(sid string) bool {
  68. return true
  69. }
  70. // Destroy deletes a session by session ID.
  71. func (o *VirtualSessionProvider) Destroy(sid string) error {
  72. o.lock.Lock()
  73. defer o.lock.Unlock()
  74. return o.provider.Destroy(sid)
  75. }
  76. // Regenerate regenerates a session store from old session ID to new one.
  77. func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
  78. o.lock.Lock()
  79. defer o.lock.Unlock()
  80. return o.provider.Regenerate(oldsid, sid)
  81. }
  82. // Count counts and returns number of sessions.
  83. func (o *VirtualSessionProvider) Count() int {
  84. o.lock.RLock()
  85. defer o.lock.RUnlock()
  86. return o.provider.Count()
  87. }
  88. // GC calls GC to clean expired sessions.
  89. func (o *VirtualSessionProvider) GC() {
  90. o.provider.GC()
  91. }
  92. func init() {
  93. session.Register("VirtualSession", &VirtualSessionProvider{})
  94. }
  95. // VirtualStore represents a virtual session store implementation.
  96. type VirtualStore struct {
  97. p *VirtualSessionProvider
  98. sid string
  99. lock sync.RWMutex
  100. data map[interface{}]interface{}
  101. }
  102. // NewVirtualStore creates and returns a virtual session store.
  103. func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore {
  104. return &VirtualStore{
  105. p: p,
  106. sid: sid,
  107. data: kv,
  108. }
  109. }
  110. // Set sets value to given key in session.
  111. func (s *VirtualStore) Set(key, val interface{}) error {
  112. s.lock.Lock()
  113. defer s.lock.Unlock()
  114. s.data[key] = val
  115. return nil
  116. }
  117. // Get gets value by given key in session.
  118. func (s *VirtualStore) Get(key interface{}) interface{} {
  119. s.lock.RLock()
  120. defer s.lock.RUnlock()
  121. return s.data[key]
  122. }
  123. // Delete delete a key from session.
  124. func (s *VirtualStore) Delete(key interface{}) error {
  125. s.lock.Lock()
  126. defer s.lock.Unlock()
  127. delete(s.data, key)
  128. return nil
  129. }
  130. // ID returns current session ID.
  131. func (s *VirtualStore) ID() string {
  132. return s.sid
  133. }
  134. // Release releases resource and save data to provider.
  135. func (s *VirtualStore) Release() error {
  136. s.lock.Lock()
  137. defer s.lock.Unlock()
  138. // Now need to lock the provider
  139. s.p.lock.Lock()
  140. defer s.p.lock.Unlock()
  141. if oldUID, ok := s.data["_old_uid"]; (ok && (oldUID != "0" || len(s.data) > 1)) || (!ok && len(s.data) > 0) {
  142. // Now ensure that we don't exist!
  143. realProvider := s.p.provider
  144. if realProvider.Exist(s.sid) {
  145. // This is an error!
  146. return fmt.Errorf("new sid '%s' already exists", s.sid)
  147. }
  148. realStore, err := realProvider.Read(s.sid)
  149. if err != nil {
  150. return err
  151. }
  152. for key, value := range s.data {
  153. if err := realStore.Set(key, value); err != nil {
  154. return err
  155. }
  156. }
  157. return realStore.Release()
  158. }
  159. return nil
  160. }
  161. // Flush deletes all session data.
  162. func (s *VirtualStore) Flush() error {
  163. s.lock.Lock()
  164. defer s.lock.Unlock()
  165. s.data = make(map[interface{}]interface{})
  166. return nil
  167. }