sessions.go 4.24 KB
Newer Older
1
package servercore
2
3

import (
Sietse Ringers's avatar
Sietse Ringers committed
4
	"crypto/rand"
5
	"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)
)

66
67
68
69
70
71
72
func (s *memorySessionStore) get(t string) *session {
	s.RLock()
	defer s.RUnlock()
	return s.requestor[t]
}

func (s *memorySessionStore) clientGet(t string) *session {
73
74
	s.RLock()
	defer s.RUnlock()
75
	return s.client[t]
76
77
}

78
func (s *memorySessionStore) add(session *session) {
79
80
	s.Lock()
	defer s.Unlock()
81
82
	s.requestor[session.token] = session
	s.client[session.clientToken] = session
83
84
}

85
86
func (s *memorySessionStore) update(session *session) {
	session.onUpdate()
87
88
}

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

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

107
		timeout := maxSessionLifetime
108
109
110
111
112
		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()) {
113
			if !session.status.Finished() {
114
				s.conf.Logger.WithFields(logrus.Fields{"session": session.token}).Infof("Session expired")
Sietse Ringers's avatar
Sietse Ringers committed
115
				session.markAlive()
Sietse Ringers's avatar
Sietse Ringers committed
116
				session.setStatus(server.StatusTimeout)
Sietse Ringers's avatar
Sietse Ringers committed
117
			} else {
118
				s.conf.Logger.WithFields(logrus.Fields{"session": session.token}).Infof("Deleting session")
Sietse Ringers's avatar
Sietse Ringers committed
119
120
				expired = append(expired, token)
			}
Sietse Ringers's avatar
Sietse Ringers committed
121
		}
122
		session.Unlock()
Sietse Ringers's avatar
Sietse Ringers committed
123
124
125
126
127
128
	}
	s.RUnlock()

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

139
140
var one *big.Int = big.NewInt(1)

141
func (s *Server) newSession(action irma.Action, request irma.RequestorRequest) *session {
142
	token := newSessionToken()
143
144
	clientToken := newSessionToken()

145
	ses := &session{
146
147
148
149
150
151
152
153
154
155
		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
156
		result: &server.SessionResult{
157
158
159
			Token:  token,
			Type:   action,
			Status: server.StatusInitialized,
160
		},
161
	}
162

163
	s.conf.Logger.WithFields(logrus.Fields{"session": ses.token}).Debug("New session started")
164
	nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
165
166
	ses.request.Base().Nonce = nonce
	ses.request.Base().Context = one
167
	s.sessions.add(ses)
168

169
	return ses
170
171
172
}

func newSessionToken() string {
Sietse Ringers's avatar
Sietse Ringers committed
173
174
175
176
177
178
179
180
181
	count := 20

	r := make([]byte, count)
	_, err := rand.Read(r)
	if err != nil {
		panic(err)
	}

	b := make([]byte, count)
182
	for i := range b {
Sietse Ringers's avatar
Sietse Ringers committed
183
		b[i] = sessionChars[r[i]%byte(len(sessionChars))]
184
185
186
	}
	return string(b)
}