-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsessions.go
More file actions
113 lines (93 loc) · 2.31 KB
/
sessions.go
File metadata and controls
113 lines (93 loc) · 2.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//go:build !wasm
package user
import (
"time"
"github.com/tinywasm/orm"
"github.com/tinywasm/unixid"
)
// RotateSession atomically deletes the old session and creates a new one
// with the same userID, updated IP/UserAgent, and a fresh TTL.
// Prevents session fixation attacks when called post-login.
func (m *Module) RotateSession(oldID, ip, userAgent string) (Session, error) {
oldSess, err := m.GetSession(oldID)
if err != nil {
return Session{}, err
}
err = m.DeleteSession(oldID)
if err != nil {
return Session{}, err
}
return m.CreateSession(oldSess.UserID, ip, userAgent)
}
func (m *Module) CreateSession(userID, ip, userAgent string) (Session, error) {
u, err := unixid.NewUnixID()
if err != nil {
return Session{}, err
}
ttl := m.config.TokenTTL
if ttl == 0 {
ttl = 86400
}
now := time.Now().Unix()
sess := Session{
ID: u.GetNewID(),
UserID: userID,
ExpiresAt: now + int64(ttl),
IP: ip,
UserAgent: userAgent,
CreatedAt: now,
}
if err := m.db.Create(&sess); err != nil {
return Session{}, err
}
m.cache.set(sess.ID, sess)
return sess, nil
}
func (m *Module) GetSession(id string) (Session, error) {
if s, ok := m.cache.get(id); ok {
if s.ExpiresAt < time.Now().Unix() {
m.cache.delete(id)
return Session{}, ErrSessionExpired
}
return s, nil
}
qb := m.db.Query(&Session{}).Where(Session_.ID).Eq(id)
results, err := ReadAllSession(qb)
if err != nil {
return Session{}, err
}
if len(results) == 0 {
return Session{}, ErrNotFound
}
s := *results[0]
if s.ExpiresAt < time.Now().Unix() {
return Session{}, ErrSessionExpired
}
m.cache.set(s.ID, s)
return s, nil
}
func (m *Module) DeleteSession(id string) error {
m.cache.delete(id)
qb := m.db.Query(&Session{}).Where(Session_.ID).Eq(id)
results, err := ReadAllSession(qb)
if err == nil && len(results) > 0 {
return m.db.Delete(results[0], orm.Eq(Session_.ID, results[0].ID))
}
return err
}
func (m *Module) PurgeExpiredSessions() error {
now := time.Now().Unix()
m.cache.mu.Lock()
for k, v := range m.cache.items {
if v.ExpiresAt < now {
delete(m.cache.items, k)
}
}
m.cache.mu.Unlock()
qb := m.db.Query(&Session{}).Where(Session_.ExpiresAt).Lt(now)
sessions, _ := ReadAllSession(qb)
for _, s := range sessions {
m.db.Delete(s, orm.Eq(Session_.ID, s.ID))
}
return nil
}