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