-
Notifications
You must be signed in to change notification settings - Fork 0
/
session.go
133 lines (98 loc) · 2.83 KB
/
session.go
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package fox
import (
"log"
"math/rand"
"os"
"time"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"github.com/robin-andreasson/fox/parser"
)
type SessionOptions struct {
Secret string // String used in session id hashing
TimeOut int // Milliseconds until session is expired in the session store, defaults to 24 hours
ClearProbability float64 // value between 0 - 100 that represents the chance of fox clearing expired sessions
Path string // path to the session store
Cookie CookieAttributes
init bool
}
var sessionOpt SessionOptions
const dbinit string = `
CREATE TABLE IF NOT EXISTS sessions (
sessID TEXT PRIMARY KEY,
payload TEXT,
timeout INT
);
CREATE INDEX IF NOT EXISTS "timeout_i" ON sessions ("timeout" ASC);
`
/*
Initialize Sessions
returns Session middleware
NOTE: sqlite3 is used as session store meaning that a gcc compiler is needed
*/
func Session(options SessionOptions) {
if options.Secret == "" {
log.Panic("zero value secret will lead to unsafe id hashing")
}
if options.TimeOut == 0 {
options.TimeOut = 1000 * 60 * 60 * 24
}
if options.ClearProbability < 0 || options.ClearProbability > 100 {
log.Panic("invalid value for ClearProbability, acceptable values are between 0 and 100")
}
if _, err := os.Stat(options.Path); os.IsNotExist(err) {
log.Panic("could not find target session store")
}
if extension, err := Ext(options.Path); err != nil || (extension != "db" && extension != "sql") {
log.Panic("invalid session store extension, sql or db is required")
}
if err := os.Truncate(options.Path, 0); err != nil {
log.Panic("could not clear session store before initialization")
}
db, err := sql.Open("sqlite3", options.Path)
if err != nil {
log.Panic("error opening session store")
}
defer db.Close()
if _, err := db.Exec(dbinit); err != nil {
log.Panic("error initializing table and timeout index")
}
sessionOpt = options
sessionOpt.ClearProbability = sessionOpt.ClearProbability / 100
sessionOpt.init = true
}
func handleSession(sessID string, c *Context) {
if !sessionOpt.init {
return
}
if sessID == "" {
return
}
db, err := sql.Open("sqlite3", sessionOpt.Path)
if err != nil {
c.Error = append(c.Error, err)
return
}
rand.Seed(time.Now().Unix())
if rand.Float64() <= sessionOpt.ClearProbability {
if _, err := db.Exec("DELETE FROM sessions WHERE timeout<=?", time.Now().UnixMilli()); err != nil {
c.Error = append(c.Error, err)
return
}
}
stmt, err := db.Prepare("SELECT payload FROM sessions WHERE sessID=?")
if err != nil {
c.Error = append(c.Error, err)
return
}
row := stmt.QueryRow(sessID)
payload := ""
if err := row.Scan(&payload); err != nil {
c.Error = append(c.Error, err)
return
}
if payload == "" {
return
}
parser.JSONUnmarshal(payload, &c.Session)
}