Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / session_adapter_cookie.go
1 package revel
2
3 import (
4         "fmt"
5         "github.com/revel/revel/session"
6         "net/http"
7         "net/url"
8         "strconv"
9         "strings"
10         "time"
11 )
12
13
14 type (
15         // The session cookie engine
16         SessionCookieEngine struct {
17                 ExpireAfterDuration time.Duration
18         }
19 )
20
21 // A logger for the session engine
22 var sessionEngineLog = RevelLog.New("section", "session-engine")
23
24 // Create a new instance to test
25 func init() {
26         RegisterSessionEngine(initCookieEngine, "revel-cookie")
27 }
28
29 // For testing purposes this engine is used
30 func NewSessionCookieEngine() *SessionCookieEngine {
31         ce := &SessionCookieEngine{}
32         return ce
33 }
34
35 // Called when the the application starts, retrieves data from the app config so cannot be run until then
36 func initCookieEngine() SessionEngine {
37         ce := &SessionCookieEngine{}
38
39         var err error
40         if expiresString, ok := Config.String("session.expires"); !ok {
41                 ce.ExpireAfterDuration = 30 * 24 * time.Hour
42         } else if expiresString == session.SessionValueName {
43                 ce.ExpireAfterDuration = 0
44         } else if ce.ExpireAfterDuration, err = time.ParseDuration(expiresString); err != nil {
45                 panic(fmt.Errorf("session.expires invalid: %s", err))
46         }
47
48         return ce
49 }
50
51 // Decode the session information from the cookie retrieved from the controller request
52 func (cse *SessionCookieEngine) Decode(c *Controller) {
53         // Decode the session from a cookie.
54         c.Session = map[string]interface{}{}
55         sessionMap := c.Session
56         if cookie, err := c.Request.Cookie(CookiePrefix + session.SessionCookieSuffix); err != nil {
57                 return
58         } else {
59                 cse.DecodeCookie(cookie, sessionMap)
60                 c.Session = sessionMap
61         }
62 }
63
64 // Encode the session information to the cookie, set the cookie on the controller
65 func (cse *SessionCookieEngine) Encode(c *Controller) {
66
67         c.SetCookie(cse.GetCookie(c.Session))
68 }
69
70 // Exposed only for testing purposes
71 func (cse *SessionCookieEngine) DecodeCookie(cookie ServerCookie, s session.Session) {
72         // Decode the session from a cookie.
73         // Separate the data from the signature.
74         cookieValue := cookie.GetValue()
75         hyphen := strings.Index(cookieValue, "-")
76         if hyphen == -1 || hyphen >= len(cookieValue)-1 {
77                 return
78         }
79         sig, data := cookieValue[:hyphen], cookieValue[hyphen+1:]
80
81         // Verify the signature.
82         if !Verify(data, sig) {
83                 sessionEngineLog.Warn("Session cookie signature failed")
84                 return
85         }
86
87         // Parse the cookie into a temp map, and then load it into the session object
88         tempMap := map[string]string{}
89         ParseKeyValueCookie(data, func(key, val string) {
90                 tempMap[key] = val
91         })
92         s.Load(tempMap)
93
94         // Check timeout after unpacking values - if timeout missing (or removed) destroy all session
95         // objects
96         if s.SessionTimeoutExpiredOrMissing() {
97                 // If this fails we need to delete all the keys from the session
98                 for key := range s {
99                         delete(s, key)
100                 }
101         }
102 }
103
104 // Convert session to cookie
105 func (cse *SessionCookieEngine) GetCookie(s session.Session) *http.Cookie {
106         var sessionValue string
107         ts := s.GetExpiration(cse.ExpireAfterDuration)
108         if ts.IsZero() {
109                 s[session.TimestampKey] = session.SessionValueName
110         } else {
111                 s[session.TimestampKey] = strconv.FormatInt(ts.Unix(), 10)
112         }
113
114         // Convert the key to a string map
115         stringMap := s.Serialize()
116
117         for key, value := range stringMap {
118                 if strings.ContainsAny(key, ":\x00") {
119                         panic("Session keys may not have colons or null bytes")
120                 }
121                 if strings.Contains(value, "\x00") {
122                         panic("Session values may not have null bytes")
123                 }
124                 sessionValue += "\x00" + key + ":" + value + "\x00"
125         }
126
127         if len(sessionValue) > 1024*4 {
128                 sessionEngineLog.Error("SessionCookieEngine.Cookie, session data has exceeded 4k limit (%d) cookie data will not be reliable", "length", len(sessionValue))
129         }
130
131         sessionData := url.QueryEscape(sessionValue)
132         sessionCookie := &http.Cookie{
133                 Name:     CookiePrefix + session.SessionCookieSuffix,
134                 Value:    Sign(sessionData) + "-" + sessionData,
135                 Domain:   CookieDomain,
136                 Path:     "/",
137                 HttpOnly: true,
138                 Secure:   CookieSecure,
139                 Expires:  ts.UTC(),
140                 MaxAge:   int(cse.ExpireAfterDuration.Seconds()),
141         }
142         return sessionCookie
143
144 }