forked from birkirb/revel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
session_adapter_cookie.go
145 lines (123 loc) · 3.92 KB
/
session_adapter_cookie.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
134
135
136
137
138
139
140
141
142
143
144
145
package revel
import (
"fmt"
"github.com/revel/revel/session"
"net/http"
"net/url"
"strconv"
"strings"
"time"
)
type (
// The session cookie engine
SessionCookieEngine struct {
ExpireAfterDuration time.Duration
}
)
// A logger for the session engine
var sessionEngineLog = RevelLog.New("section", "session-engine")
// Create a new instance to test
func init() {
RegisterSessionEngine(initCookieEngine, "revel-cookie")
}
// For testing purposes this engine is used
func NewSessionCookieEngine() *SessionCookieEngine {
ce := &SessionCookieEngine{}
return ce
}
// Called when the the application starts, retrieves data from the app config so cannot be run until then
func initCookieEngine() SessionEngine {
ce := &SessionCookieEngine{}
var err error
if expiresString, ok := Config.String("session.expires"); !ok {
ce.ExpireAfterDuration = 30 * 24 * time.Hour
} else if expiresString == session.SessionValueName {
ce.ExpireAfterDuration = 0
} else if ce.ExpireAfterDuration, err = time.ParseDuration(expiresString); err != nil {
panic(fmt.Errorf("session.expires invalid: %s", err))
}
return ce
}
// Decode the session information from the cookie retrieved from the controller request
func (cse *SessionCookieEngine) Decode(c *Controller) {
// Decode the session from a cookie.
c.Session = map[string]interface{}{}
sessionMap := c.Session
if cookie, err := c.Request.Cookie(CookiePrefix + session.SessionCookieSuffix); err != nil {
return
} else {
cse.DecodeCookie(cookie, sessionMap)
c.Session = sessionMap
}
}
// Encode the session information to the cookie, set the cookie on the controller
func (cse *SessionCookieEngine) Encode(c *Controller) {
c.SetCookie(cse.GetCookie(c.Session))
}
// Exposed only for testing purposes
func (cse *SessionCookieEngine) DecodeCookie(cookie ServerCookie, s session.Session) {
// Decode the session from a cookie.
// Separate the data from the signature.
cookieValue := cookie.GetValue()
hyphen := strings.Index(cookieValue, "-")
if hyphen == -1 || hyphen >= len(cookieValue)-1 {
return
}
sig, data := cookieValue[:hyphen], cookieValue[hyphen+1:]
// Verify the signature.
if !Verify(data, sig) {
sessionEngineLog.Warn("Session cookie signature failed")
return
}
// Parse the cookie into a temp map, and then load it into the session object
tempMap := map[string]string{}
ParseKeyValueCookie(data, func(key, val string) {
tempMap[key] = val
})
s.Load(tempMap)
// Check timeout after unpacking values - if timeout missing (or removed) destroy all session
// objects
if s.SessionTimeoutExpiredOrMissing() {
// If this fails we need to delete all the keys from the session
for key := range s {
delete(s, key)
}
}
}
// Convert session to cookie
func (cse *SessionCookieEngine) GetCookie(s session.Session) *http.Cookie {
var sessionValue string
ts := s.GetExpiration(cse.ExpireAfterDuration)
if ts.IsZero() {
s[session.TimestampKey] = session.SessionValueName
} else {
s[session.TimestampKey] = strconv.FormatInt(ts.Unix(), 10)
}
// Convert the key to a string map
stringMap := s.Serialize()
for key, value := range stringMap {
if strings.ContainsAny(key, ":\x00") {
panic("Session keys may not have colons or null bytes")
}
if strings.Contains(value, "\x00") {
panic("Session values may not have null bytes")
}
sessionValue += "\x00" + key + ":" + value + "\x00"
}
if len(sessionValue) > 1024*4 {
sessionEngineLog.Error("SessionCookieEngine.Cookie, session data has exceeded 4k limit (%d) cookie data will not be reliable", "length", len(sessionValue))
}
sessionData := url.QueryEscape(sessionValue)
sessionCookie := &http.Cookie{
Name: CookiePrefix + session.SessionCookieSuffix,
Value: Sign(sessionData) + "-" + sessionData,
Domain: CookieDomain,
Path: "/",
HttpOnly: true,
Secure: CookieSecure,
SameSite: CookieSameSite,
Expires: ts.UTC(),
MaxAge: int(cse.ExpireAfterDuration.Seconds()),
}
return sessionCookie
}