sessions.go 2.96 KB
Newer Older
1
2
3
4
5
6
package backend

import (
	"math/big"
	"math/rand"
	"sync"
Sietse Ringers's avatar
Sietse Ringers committed
7
	"time"
8
9
10
11
12
13
14

	"github.com/mhe/gabi"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/irmaserver"
)

type session struct {
Sietse Ringers's avatar
Sietse Ringers committed
15
16
	sync.Mutex

17
18
19
20
21
	action  irma.Action
	token   string
	version *irma.ProtocolVersion
	request irma.SessionRequest

22
	status     irmaserver.Status
23
24
25
	lastActive time.Time
	returned   bool
	result     *irmaserver.SessionResult
26
27
28
29
30
31
32

	kssProofs map[irma.SchemeManagerIdentifier]*gabi.ProofP
}

type sessionStore interface {
	get(token string) *session
	add(token string, session *session)
33
	update(token string, session *session)
Sietse Ringers's avatar
Sietse Ringers committed
34
	deleteExpired()
35
36
37
38
39
40
41
}

type memorySessionStore struct {
	sync.RWMutex
	m map[string]*session
}

Sietse Ringers's avatar
Sietse Ringers committed
42
43
44
45
46
const (
	maxSessionLifetime = 5 * time.Minute  // After this a session is cancelled
	expiryTicker       = 10 * time.Second // Every so often we check if any session has expired
)

47
48
49
50
51
52
53
54
55
56
57
const sessionChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

var (
	minProtocolVersion = irma.NewVersion(2, 4)
	maxProtocolVersion = irma.NewVersion(2, 4)

	sessions sessionStore = &memorySessionStore{
		m: make(map[string]*session),
	}
)

Sietse Ringers's avatar
Sietse Ringers committed
58
59
60
61
func init() {
	go sessions.deleteExpired()
}

62
63
64
65
66
67
68
69
70
71
72
73
func (s *memorySessionStore) get(token string) *session {
	s.RLock()
	defer s.RUnlock()
	return s.m[token]
}

func (s *memorySessionStore) add(token string, session *session) {
	s.Lock()
	defer s.Unlock()
	s.m[token] = session
}

74
75
76
77
func (s *memorySessionStore) update(token string, session *session) {
	// nop
}

Sietse Ringers's avatar
Sietse Ringers committed
78
79
80
81
82
83
func (s memorySessionStore) deleteExpired() {
	// First check which sessions have expired
	// We don't need a write lock for this yet, so postpone that for actual deleting
	s.RLock()
	expired := make([]string, 0, len(s.m))
	for token, session := range s.m {
84
		session.Lock()
85
		if session.lastActive.Add(5 * time.Minute).Before(time.Now()) {
Sietse Ringers's avatar
Sietse Ringers committed
86
87
88
89
90
91
92
93
			if !session.finished() {
				conf.Logger.Infof("Session %s expired", token)
				session.markAlive()
				session.setStatus(irmaserver.StatusTimeout)
			} else {
				conf.Logger.Infof("Deleting %s", token)
				expired = append(expired, token)
			}
Sietse Ringers's avatar
Sietse Ringers committed
94
		}
95
		session.Unlock()
Sietse Ringers's avatar
Sietse Ringers committed
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
	}
	s.RUnlock()

	// Using a write lock, delete the expired sessions
	s.Lock()
	for _, token := range expired {
		delete(s.m, token)
	}
	s.Unlock()

	// Schedule next run
	time.AfterFunc(expiryTicker, func() {
		s.deleteExpired()
	})
}

112
113
114
var one *big.Int = big.NewInt(1)

func newSession(action irma.Action, request irma.SessionRequest) *session {
115
	token := newSessionToken()
116
	s := &session{
117
118
119
120
121
122
123
		action:     action,
		request:    request,
		lastActive: time.Now(),
		token:      token,
		result: &irmaserver.SessionResult{
			Token: token,
		},
124
	}
125
	s.setStatus(irmaserver.StatusInitialized)
126
127
128
	nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
	request.SetNonce(nonce)
	request.SetContext(one)
129
	sessions.add(token, s)
130
131
132
133
134
135
136
137
138
139
	return s
}

func newSessionToken() string {
	b := make([]byte, 20)
	for i := range b {
		b[i] = sessionChars[rand.Int63()%int64(len(sessionChars))]
	}
	return string(b)
}