sessions.go 3.63 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
	action   irma.Action
	token    string
	version  *irma.ProtocolVersion
	rrequest irma.RequestorRequest
	request  irma.SessionRequest
24

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

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

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

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

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

type memorySessionStore struct {
	sync.RWMutex
47
48
	conf *server.Configuration
	m    map[string]*session
49
50
}

Sietse Ringers's avatar
Sietse Ringers committed
51
const (
52
53
	maxSessionLifetime = 5 * time.Minute // After this a session is cancelled
	sessionChars       = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Sietse Ringers's avatar
Sietse Ringers committed
54
55
)

56
57
58
59
60
var (
	minProtocolVersion = irma.NewVersion(2, 4)
	maxProtocolVersion = irma.NewVersion(2, 4)
)

Sietse Ringers's avatar
Sietse Ringers committed
61
func init() {
Sietse Ringers's avatar
Sietse Ringers committed
62
	rand.Seed(time.Now().UnixNano())
Sietse Ringers's avatar
Sietse Ringers committed
63
64
}

65
66
67
68
69
70
71
72
73
74
75
76
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
}

77
78
func (s *memorySessionStore) update(session *session) {
	session.onUpdate()
79
80
}

Sietse Ringers's avatar
Sietse Ringers committed
81
82
83
84
85
86
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 {
87
		session.Lock()
88

89
		timeout := maxSessionLifetime
90
91
92
93
94
		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()) {
95
			if !session.status.Finished() {
96
				s.conf.Logger.WithFields(logrus.Fields{"session": session.token}).Infof("Session expired")
Sietse Ringers's avatar
Sietse Ringers committed
97
				session.markAlive()
Sietse Ringers's avatar
Sietse Ringers committed
98
				session.setStatus(server.StatusTimeout)
Sietse Ringers's avatar
Sietse Ringers committed
99
			} else {
100
				s.conf.Logger.WithFields(logrus.Fields{"session": session.token}).Infof("Deleting session")
Sietse Ringers's avatar
Sietse Ringers committed
101
102
				expired = append(expired, token)
			}
Sietse Ringers's avatar
Sietse Ringers committed
103
		}
104
		session.Unlock()
Sietse Ringers's avatar
Sietse Ringers committed
105
106
107
108
109
110
	}
	s.RUnlock()

	// Using a write lock, delete the expired sessions
	s.Lock()
	for _, token := range expired {
111
112
113
114
		session := s.m[token]
		if session.evtSource != nil {
			session.evtSource.Close()
		}
Sietse Ringers's avatar
Sietse Ringers committed
115
116
117
118
119
		delete(s.m, token)
	}
	s.Unlock()
}

120
121
var one *big.Int = big.NewInt(1)

122
func (s *Server) newSession(action irma.Action, request irma.RequestorRequest) *session {
123
	token := newSessionToken()
124
	ses := &session{
125
		action:     action,
126
127
		rrequest:   request,
		request:    request.SessionRequest(),
128
129
		lastActive: time.Now(),
		token:      token,
130
		status:     server.StatusInitialized,
131
		prevStatus: server.StatusInitialized,
132
133
		conf:       s.conf,
		sessions:   s.sessions,
Sietse Ringers's avatar
Sietse Ringers committed
134
		result: &server.SessionResult{
135
136
137
			Token:  token,
			Type:   action,
			Status: server.StatusInitialized,
138
		},
139
	}
140

141
	s.conf.Logger.WithFields(logrus.Fields{"session": ses.token}).Debug("New session started")
142
	nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
143
144
145
	ses.request.SetNonce(nonce)
	ses.request.SetContext(one)
	s.sessions.add(token, ses)
146

147
	return ses
148
149
150
151
152
153
154
155
156
}

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