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  }