sessions.go 3.3 KB
Newer Older
1
package core
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
13
14
)

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
	rrequest irma.RequestorRequest
	request  irma.SessionRequest
22

Sietse Ringers's avatar
Sietse Ringers committed
23
	status     server.Status
24
25
	lastActive time.Time
	returned   bool
Sietse Ringers's avatar
Sietse Ringers committed
26
	result     *server.SessionResult
27
28
29
30
31
32
33

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

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

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

Sietse Ringers's avatar
Sietse Ringers committed
43
44
45
46
47
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
)

48
49
50
51
52
53
54
55
56
57
58
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
59
func init() {
Sietse Ringers's avatar
Sietse Ringers committed
60
	rand.Seed(time.Now().UnixNano())
Sietse Ringers's avatar
Sietse Ringers committed
61
62
63
	go sessions.deleteExpired()
}

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

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

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

		timeout := 5 * time.Minute
		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()) {
Sietse Ringers's avatar
Sietse Ringers committed
94
95
96
			if !session.finished() {
				conf.Logger.Infof("Session %s expired", token)
				session.markAlive()
Sietse Ringers's avatar
Sietse Ringers committed
97
				session.setStatus(server.StatusTimeout)
Sietse Ringers's avatar
Sietse Ringers committed
98
99
100
101
			} else {
				conf.Logger.Infof("Deleting %s", token)
				expired = append(expired, token)
			}
Sietse Ringers's avatar
Sietse Ringers committed
102
		}
103
		session.Unlock()
Sietse Ringers's avatar
Sietse Ringers committed
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
	}
	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()
	})
}

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

122
func newSession(action irma.Action, request irma.RequestorRequest) *session {
123
	token := newSessionToken()
124
	s := &session{
125
		action:     action,
126
127
		rrequest:   request,
		request:    request.SessionRequest(),
128
129
		lastActive: time.Now(),
		token:      token,
Sietse Ringers's avatar
Sietse Ringers committed
130
		result: &server.SessionResult{
131
			Token: token,
132
			Type:  action,
133
		},
134
	}
Sietse Ringers's avatar
Sietse Ringers committed
135
	s.setStatus(server.StatusInitialized)
136
	nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
137
138
	s.request.SetNonce(nonce)
	s.request.SetContext(one)
139
	sessions.add(token, s)
140
141
142
143
144
145
146
147
148
149
	return s
}

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