summaryrefslogtreecommitdiffstats
path: root/modules/session/virtual.go
blob: 027960a2896968944b330d1210c3d05dc148290e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { col
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package session

import (
	"container/list"
	"encoding/json"
	"fmt"
	"sync"

	"gitea.com/macaron/session"
	couchbase "gitea.com/macaron/session/couchbase"
	memcache "gitea.com/macaron/session/memcache"
	mysql "gitea.com/macaron/session/mysql"
	nodb "gitea.com/macaron/session/nodb"
	postgres "gitea.com/macaron/session/postgres"
	redis "gitea.com/macaron/session/redis"
)

// VirtualSessionProvider represents a shadowed session provider implementation.
type VirtualSessionProvider struct {
	lock     sync.RWMutex
	provider session.Provider
}

// 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":
		o.provider = &MemProvider{list: list.New(), data: make(map[string]*list.Element)}
	case "file":
		o.provider = &session.FileProvider{}
	case "redis":
		o.provider = &redis.RedisProvider{}
	case "mysql":
		o.provider = &mysql.MysqlProvider{}
	case "postgres":
		o.provider = &postgres.PostgresProvider{}
	case "couchbase":
		o.provider = &couchbase.CouchbaseProvider{}
	case "memcache":
		o.provider = &memcache.MemcacheProvider{}
	case "nodb":
		o.provider = &nodb.NodbProvider{}
	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)
	}
	kv := make(map[interface{}]interface{})
	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
}

// Destroy deletes a session by session ID.
func (o *VirtualSessionProvider) Destroy(sid string) error {
	o.lock.Lock()
	defer o.lock.Unlock()
	return o.provider.Destroy(sid)
}

// 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 {
	p    *VirtualSessionProvider
	sid  string
	lock sync.RWMutex
	data map[interface{}]interface{}
}

// NewVirtualStore creates and returns a virtual session store.
func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore {
	return &VirtualStore{
		p:    p,
		sid:  sid,
		data: kv,
	}
}

// Set sets value to given key in session.
func (s *VirtualStore) Set(key, val interface{}) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.data[key] = val
	return nil
}

// Get gets value by given key in session.
func (s *VirtualStore) Get(key interface{}) interface{} {
	s.lock.RLock()
	defer s.lock.RUnlock()

	return s.data[key]
}

// Delete delete a key from session.
func (s *VirtualStore) Delete(key interface{}) error {
	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

		if realProvider.Exist(s.sid) {
			// 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
		}
		for key, value := range s.data {
			if err := realStore.Set(key, value); err != nil {
				return err
			}
		}
		return realStore.Release()
	}
	return nil
}

// Flush deletes all session data.
func (s *VirtualStore) Flush() error {
	s.lock.Lock()
	defer s.lock.Unlock()

	s.data = make(map[interface{}]interface{})
	return nil
}