2019-04-20 09:44:50 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2019-04-20 09:44:50 +03:00
package session
import (
"fmt"
"sync"
2021-07-24 19:03:58 +03:00
"code.gitea.io/gitea/modules/json"
2021-01-26 18:36:53 +03:00
"gitea.com/go-chi/session"
couchbase "gitea.com/go-chi/session/couchbase"
memcache "gitea.com/go-chi/session/memcache"
mysql "gitea.com/go-chi/session/mysql"
postgres "gitea.com/go-chi/session/postgres"
2019-04-20 09:44:50 +03:00
)
// VirtualSessionProvider represents a shadowed session provider implementation.
type VirtualSessionProvider struct {
2019-06-12 22:41:28 +03:00
lock sync . RWMutex
provider session . Provider
2019-04-20 09:44:50 +03:00
}
// Init initializes the cookie session provider with given root path.
func ( o * VirtualSessionProvider ) Init ( gclifetime int64 , config string ) error {
var opts session . Options
if err := json . Unmarshal ( [ ] byte ( config ) , & opts ) ; err != nil {
return err
}
// Note that these options are unprepared so we can't just use NewManager here.
// Nor can we access the provider map in session.
// So we will just have to do this by hand.
// This is only slightly more wrong than modules/setting/session.go:23
switch opts . Provider {
case "memory" :
2020-09-03 00:10:35 +03:00
o . provider = & session . MemProvider { }
2019-04-20 09:44:50 +03:00
case "file" :
o . provider = & session . FileProvider { }
case "redis" :
2020-09-28 00:09:46 +03:00
o . provider = & RedisProvider { }
2021-02-15 08:33:31 +03:00
case "db" :
o . provider = & DBProvider { }
2019-04-20 09:44:50 +03:00
case "mysql" :
o . provider = & mysql . MysqlProvider { }
case "postgres" :
o . provider = & postgres . PostgresProvider { }
case "couchbase" :
o . provider = & couchbase . CouchbaseProvider { }
case "memcache" :
o . provider = & memcache . MemcacheProvider { }
default :
return fmt . Errorf ( "VirtualSessionProvider: Unknown Provider: %s" , opts . Provider )
}
return o . provider . Init ( gclifetime , opts . ProviderConfig )
}
// Read returns raw session store by session ID.
func ( o * VirtualSessionProvider ) Read ( sid string ) ( session . RawStore , error ) {
o . lock . RLock ( )
defer o . lock . RUnlock ( )
if o . provider . Exist ( sid ) {
return o . provider . Read ( sid )
}
2023-07-04 21:36:08 +03:00
kv := make ( map [ any ] any )
2019-04-20 09:44:50 +03:00
kv [ "_old_uid" ] = "0"
return NewVirtualStore ( o , sid , kv ) , nil
}
// Exist returns true if session with given ID exists.
func ( o * VirtualSessionProvider ) Exist ( sid string ) bool {
return true
}
2019-08-23 19:40:30 +03:00
// Destroy deletes a session by session ID.
func ( o * VirtualSessionProvider ) Destroy ( sid string ) error {
2019-04-20 09:44:50 +03:00
o . lock . Lock ( )
defer o . lock . Unlock ( )
2019-08-23 19:40:30 +03:00
return o . provider . Destroy ( sid )
2019-04-20 09:44:50 +03:00
}
// Regenerate regenerates a session store from old session ID to new one.
func ( o * VirtualSessionProvider ) Regenerate ( oldsid , sid string ) ( session . RawStore , error ) {
o . lock . Lock ( )
defer o . lock . Unlock ( )
return o . provider . Regenerate ( oldsid , sid )
}
// Count counts and returns number of sessions.
func ( o * VirtualSessionProvider ) Count ( ) int {
o . lock . RLock ( )
defer o . lock . RUnlock ( )
return o . provider . Count ( )
}
// GC calls GC to clean expired sessions.
func ( o * VirtualSessionProvider ) GC ( ) {
o . provider . GC ( )
}
func init ( ) {
session . Register ( "VirtualSession" , & VirtualSessionProvider { } )
}
// VirtualStore represents a virtual session store implementation.
type VirtualStore struct {
2020-05-24 02:59:04 +03:00
p * VirtualSessionProvider
sid string
lock sync . RWMutex
2023-07-04 21:36:08 +03:00
data map [ any ] any
2020-05-24 02:59:04 +03:00
released bool
2019-04-20 09:44:50 +03:00
}
// NewVirtualStore creates and returns a virtual session store.
2023-07-04 21:36:08 +03:00
func NewVirtualStore ( p * VirtualSessionProvider , sid string , kv map [ any ] any ) * VirtualStore {
2019-04-20 09:44:50 +03:00
return & VirtualStore {
p : p ,
sid : sid ,
data : kv ,
}
}
// Set sets value to given key in session.
2023-07-04 21:36:08 +03:00
func ( s * VirtualStore ) Set ( key , val any ) error {
2019-04-20 09:44:50 +03:00
s . lock . Lock ( )
defer s . lock . Unlock ( )
s . data [ key ] = val
return nil
}
// Get gets value by given key in session.
2023-07-04 21:36:08 +03:00
func ( s * VirtualStore ) Get ( key any ) any {
2019-04-20 09:44:50 +03:00
s . lock . RLock ( )
defer s . lock . RUnlock ( )
return s . data [ key ]
}
// Delete delete a key from session.
2023-07-04 21:36:08 +03:00
func ( s * VirtualStore ) Delete ( key any ) error {
2019-04-20 09:44:50 +03:00
s . lock . Lock ( )
defer s . lock . Unlock ( )
delete ( s . data , key )
return nil
}
// ID returns current session ID.
func ( s * VirtualStore ) ID ( ) string {
return s . sid
}
// Release releases resource and save data to provider.
func ( s * VirtualStore ) Release ( ) error {
s . lock . Lock ( )
defer s . lock . Unlock ( )
// Now need to lock the provider
s . p . lock . Lock ( )
defer s . p . lock . Unlock ( )
if oldUID , ok := s . data [ "_old_uid" ] ; ( ok && ( oldUID != "0" || len ( s . data ) > 1 ) ) || ( ! ok && len ( s . data ) > 0 ) {
// Now ensure that we don't exist!
realProvider := s . p . provider
2020-05-24 02:59:04 +03:00
if ! s . released && realProvider . Exist ( s . sid ) {
2019-04-20 09:44:50 +03:00
// This is an error!
return fmt . Errorf ( "new sid '%s' already exists" , s . sid )
}
realStore , err := realProvider . Read ( s . sid )
if err != nil {
return err
}
2020-05-24 02:59:04 +03:00
if err := realStore . Flush ( ) ; err != nil {
return err
}
2019-04-20 09:44:50 +03:00
for key , value := range s . data {
if err := realStore . Set ( key , value ) ; err != nil {
return err
}
}
2020-05-24 02:59:04 +03:00
err = realStore . Release ( )
if err == nil {
s . released = true
}
return err
2019-04-20 09:44:50 +03:00
}
return nil
}
// Flush deletes all session data.
func ( s * VirtualStore ) Flush ( ) error {
s . lock . Lock ( )
defer s . lock . Unlock ( )
2023-07-04 21:36:08 +03:00
s . data = make ( map [ any ] any )
2019-04-20 09:44:50 +03:00
return nil
}