sessions.go 4.01 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
46
47
48
}

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

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

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

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

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

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

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

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

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

92
func (s *memorySessionStore) deleteExpired() {
Sietse Ringers's avatar
Sietse Ringers committed
93
94
95
	// First check which sessions have expired
	// We don't need a write lock for this yet, so postpone that for actual deleting
	s.RLock()
96
97
	expired := make([]string, 0, len(s.requestor))
	for token, session := range s.requestor {
98
		session.Lock()
99

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

	// Using a write lock, delete the expired sessions
	s.Lock()
	for _, token := range expired {
122
		session := s.requestor[token]
123
124
125
		if session.evtSource != nil {
			session.evtSource.Close()
		}
126
		delete(s.client, session.clientToken)
127
		delete(s.requestor, token)
Sietse Ringers's avatar
Sietse Ringers committed
128
129
130
131
	}
	s.Unlock()
}

132
133
var one *big.Int = big.NewInt(1)

134
func (s *Server) newSession(action irma.Action, request irma.RequestorRequest) *session {
135
	token := newSessionToken()
136
137
	clientToken := newSessionToken()

138
	ses := &session{
139
140
141
142
143
144
145
146
147
148
		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
149
		result: &server.SessionResult{
150
151
152
			Token:  token,
			Type:   action,
			Status: server.StatusInitialized,
153
		},
154
	}
155

156
	s.conf.Logger.WithFields(logrus.Fields{"session": ses.token}).Debug("New session started")
157
	nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
158
159
	ses.request.SetNonce(nonce)
	ses.request.SetContext(one)
160
	s.sessions.add(ses)
161

162
	return ses
163
164
165
166
167
168
169
170
171
}

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