sessions.go 4.19 KB
Newer Older
1
package servercore
2
3
4
5

import (
	"math/rand"
	"sync"
Sietse Ringers's avatar
Sietse Ringers committed
6
	"time"
7

8
9
	"github.com/privacybydesign/gabi"
	"github.com/privacybydesign/gabi/big"
10
	"github.com/privacybydesign/irmago"
Sietse Ringers's avatar
Sietse Ringers committed
11
	"github.com/privacybydesign/irmago/server"
12
	"github.com/sirupsen/logrus"
13
	"gopkg.in/antage/eventsource.v1"
14
15
16
)

type session struct {
Sietse Ringers's avatar
Sietse Ringers committed
17
18
	sync.Mutex

19
20
21
22
23
24
	action      irma.Action
	token       string
	clientToken string
	version     *irma.ProtocolVersion
	rrequest    irma.RequestorRequest
	request     irma.SessionRequest
25

26
27
28
	status     server.Status
	prevStatus server.Status
	evtSource  eventsource.EventSource
29

30
	lastActive time.Time
Sietse Ringers's avatar
Sietse Ringers committed
31
	result     *server.SessionResult
32
33

	kssProofs map[irma.SchemeManagerIdentifier]*gabi.ProofP
34
35
36

	conf     *server.Configuration
	sessions sessionStore
37
38
39
40
}

type sessionStore interface {
	get(token string) *session
41
42
	clientGet(token string) *session
	add(session *session)
43
	update(session *session)
Sietse Ringers's avatar
Sietse Ringers committed
44
	deleteExpired()
45
	stop()
46
47
48
49
}

type memorySessionStore struct {
	sync.RWMutex
50
	conf *server.Configuration
51
52
53

	requestor map[string]*session
	client    map[string]*session
54
55
}

Sietse Ringers's avatar
Sietse Ringers committed
56
const (
57
58
	maxSessionLifetime = 5 * time.Minute // After this a session is cancelled
	sessionChars       = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Sietse Ringers's avatar
Sietse Ringers committed
59
60
)

61
62
63
64
65
var (
	minProtocolVersion = irma.NewVersion(2, 4)
	maxProtocolVersion = irma.NewVersion(2, 4)
)

Sietse Ringers's avatar
Sietse Ringers committed
66
func init() {
Sietse Ringers's avatar
Sietse Ringers committed
67
	rand.Seed(time.Now().UnixNano())
Sietse Ringers's avatar
Sietse Ringers committed
68
69
}

70
71
72
73
74
75
76
func (s *memorySessionStore) get(t string) *session {
	s.RLock()
	defer s.RUnlock()
	return s.requestor[t]
}

func (s *memorySessionStore) clientGet(t string) *session {
77
78
	s.RLock()
	defer s.RUnlock()
79
	return s.client[t]
80
81
}

82
func (s *memorySessionStore) add(session *session) {
83
84
	s.Lock()
	defer s.Unlock()
85
86
	s.requestor[session.token] = session
	s.client[session.clientToken] = session
87
88
}

89
90
func (s *memorySessionStore) update(session *session) {
	session.onUpdate()
91
92
}

93
94
95
96
97
98
99
100
101
102
func (s *memorySessionStore) stop() {
	s.Lock()
	defer s.Unlock()
	for _, session := range s.requestor {
		if session.evtSource != nil {
			session.evtSource.Close()
		}
	}
}

103
func (s *memorySessionStore) deleteExpired() {
Sietse Ringers's avatar
Sietse Ringers committed
104
105
106
	// First check which sessions have expired
	// We don't need a write lock for this yet, so postpone that for actual deleting
	s.RLock()
107
108
	expired := make([]string, 0, len(s.requestor))
	for token, session := range s.requestor {
109
		session.Lock()
110

111
		timeout := maxSessionLifetime
112
113
114
115
116
		if session.status == server.StatusInitialized && session.rrequest.Base().ClientTimeout != 0 {
			timeout = time.Duration(session.rrequest.Base().ClientTimeout) * time.Second
		}

		if session.lastActive.Add(timeout).Before(time.Now()) {
117
			if !session.status.Finished() {
118
				s.conf.Logger.WithFields(logrus.Fields{"session": session.token}).Infof("Session expired")
Sietse Ringers's avatar
Sietse Ringers committed
119
				session.markAlive()
Sietse Ringers's avatar
Sietse Ringers committed
120
				session.setStatus(server.StatusTimeout)
Sietse Ringers's avatar
Sietse Ringers committed
121
			} else {
122
				s.conf.Logger.WithFields(logrus.Fields{"session": session.token}).Infof("Deleting session")
Sietse Ringers's avatar
Sietse Ringers committed
123
124
				expired = append(expired, token)
			}
Sietse Ringers's avatar
Sietse Ringers committed
125
		}
126
		session.Unlock()
Sietse Ringers's avatar
Sietse Ringers committed
127
128
129
130
131
132
	}
	s.RUnlock()

	// Using a write lock, delete the expired sessions
	s.Lock()
	for _, token := range expired {
133
		session := s.requestor[token]
134
135
136
		if session.evtSource != nil {
			session.evtSource.Close()
		}
137
		delete(s.client, session.clientToken)
138
		delete(s.requestor, token)
Sietse Ringers's avatar
Sietse Ringers committed
139
140
141
142
	}
	s.Unlock()
}

143
144
var one *big.Int = big.NewInt(1)

145
func (s *Server) newSession(action irma.Action, request irma.RequestorRequest) *session {
146
	token := newSessionToken()
147
148
	clientToken := newSessionToken()

149
	ses := &session{
150
151
152
153
154
155
156
157
158
159
		action:      action,
		rrequest:    request,
		request:     request.SessionRequest(),
		lastActive:  time.Now(),
		token:       token,
		clientToken: clientToken,
		status:      server.StatusInitialized,
		prevStatus:  server.StatusInitialized,
		conf:        s.conf,
		sessions:    s.sessions,
Sietse Ringers's avatar
Sietse Ringers committed
160
		result: &server.SessionResult{
161
162
163
			Token:  token,
			Type:   action,
			Status: server.StatusInitialized,
164
		},
165
	}
166

167
	s.conf.Logger.WithFields(logrus.Fields{"session": ses.token}).Debug("New session started")
168
	nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
169
170
	ses.request.SetNonce(nonce)
	ses.request.SetContext(one)
171
	s.sessions.add(ses)
172

173
	return ses
174
175
176
177
178
179
180
181
182
}

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