Untitled diff

Created Diff never expires
22 removals
200 lines
29 additions
207 lines
package app
package app
import (
import (
"crypto/rand"
"crypto/rand"
"encoding/hex"
"encoding/hex"
"fmt"
"fmt"
"net/http"
"net/http"
"strings"
"strings"
"time"
"time"
"github.com/garyburd/redigo/redis"
"github.com/revel/revel"
"github.com/robfig/revel"
"menteslibres.net/gosexy/redis"
)
)
const (
const (
SESSION_ID_KEY = "_ID"
SESSION_ID_KEY = "_ID"
CSRF_TOKEN_KEY = "_CSRF"
CSRF_TOKEN_KEY = "_CSRF"
CSRF_COOKIE = "_CSRF_TOKEN"
CSRF_COOKIE = "_CSRF_TOKEN"
CSRF_HEADER = "X-CSRF-Token"
CSRF_HEADER = "X-CSRF-Token"
)
)
var (
var (
expireAfterDuration time.Duration
expireAfterDuration time.Duration
csrfEnabled bool
csrfEnabled bool
)
)
func init() {
func init() {
// Set expireAfterDuration, default to 30 days if no value in config
// Set expireAfterDuration, default to 30 days if no value in config
revel.OnAppStart(func() {
revel.OnAppStart(func() {
var err error
var err error
if expiresString, ok := revel.Config.String("session.expires"); !ok {
if expiresString, ok := revel.Config.String("session.expires"); !ok {
expireAfterDuration = 30 * 24 * time.Hour
expireAfterDuration = 30 * 24 * time.Hour
} else if expiresString == "session" {
} else if expiresString == "session" {
expireAfterDuration = 0
expireAfterDuration = 0
} else if expireAfterDuration, err = time.ParseDuration(expiresString); err != nil {
} else if expireAfterDuration, err = time.ParseDuration(expiresString); err != nil {
panic(fmt.Errorf("session.expires invalid: %s", err))
panic(fmt.Errorf("session.expires invalid: %s", err))
}
}
csrfEnabled = revel.Config.BoolDefault("session.csrf", false)
csrfEnabled = revel.Config.BoolDefault("session.csrf", false)
})
})
}
}
// Saves the session into redis
// Saves the session into redis
// Returns an http.Cookie containing the signed session token or nil on error
// Returns an http.Cookie containing the signed session token or nil on error
func saveSession(s revel.Session) *http.Cookie {
func saveSession(s revel.Session) *http.Cookie {
if len(s) == 0 {
if len(s) == 0 {
return nil
return nil
}
}
var sessionValue string
var sessionValue string
sessionToken := s.Id()
sessionToken := s.Id()
if _, ok := s[CSRF_TOKEN_KEY]; !ok && csrfEnabled {
if _, ok := s[CSRF_TOKEN_KEY]; !ok && csrfEnabled {
buf := make([]byte, 32)
buf := make([]byte, 32)
_, err := rand.Read(buf)
_, err := rand.Read(buf)
if err == nil {
if err == nil {
s[CSRF_TOKEN_KEY] = hex.EncodeToString(buf)
s[CSRF_TOKEN_KEY] = hex.EncodeToString(buf)
} else {
} else {
revel.ERROR.Println(err)
revel.ERROR.Println(err)
}
}
}
}
for key, value := range s {
for key, value := range s {
if key == SESSION_ID_KEY {
if key == SESSION_ID_KEY {
continue
continue
}
}
if strings.ContainsAny(key, ":\x00") {
if strings.ContainsAny(key, ":\x00") {
panic("Session keys may not have colons or null bytes")
panic("Session keys may not have colons or null bytes")
}
}
if strings.Contains(value, "\x00") {
if strings.Contains(value, "\x00") {
panic("Session values may not have null bytes")
panic("Session values may not have null bytes")
}
}
sessionValue += "\x00" + key + ":" + value + "\x00"
sessionValue += "\x00" + key + ":" + value + "\x00"
}
}
redisConn := Redis.Get()
rc := redis.New()
defer redisConn.Close()
rc.Connect("127.0.0.1", 6379)
defer rc.Quit()
params := []interface{}{"session:" + sessionToken, sessionValue}
key := "session:" + sessionToken
var err error
if expireAfterDuration != 0 {
if expireAfterDuration != 0 {
params = append(params, "EX", int(expireAfterDuration/time.Second))
_, err = rc.SetEx(key, int64(expireAfterDuration/time.Second), sessionValue)
} else {
_, err = rc.Set(key, sessionValue)
}
}
_, err := redisConn.Do("SET", params...)
if err != nil {
if err != nil {
revel.ERROR.Println(err)
revel.ERROR.Println(err)
return nil
return nil
}
}
cookie := &http.Cookie{
cookie := &http.Cookie{
Name: revel.CookiePrefix + "_SESSION",
Name: revel.CookiePrefix + "_SESSION",
Value: revel.Sign(sessionToken) + "-" + sessionToken,
Value: revel.Sign(sessionToken) + "-" + sessionToken,
Path: "/",
Path: "/",
HttpOnly: revel.CookieHttpOnly,
HttpOnly: revel.CookieHttpOnly,
Secure: revel.CookieSecure,
Secure: revel.CookieSecure,
}
}
if expireAfterDuration != 0 {
if expireAfterDuration != 0 {
cookie.Expires = time.Now().Add(expireAfterDuration).UTC()
cookie.Expires = time.Now().Add(expireAfterDuration).UTC()
} else {
} else {
cookie.Expires = time.Time{}.UTC()
cookie.Expires = time.Time{}.UTC()
}
}
return cookie
return cookie
}
}
// Returns a Session from redis with the session id pulled from a signed cookie.
// Returns a Session from redis with the session id pulled from a signed cookie.
// Returns a blank session if the session has expired or there was an error
// Returns a blank session if the session has expired or there was an error
func restoreSession(req *http.Request) revel.Session {
func restoreSession(req *http.Request) revel.Session {
session := make(revel.Session)
session := make(revel.Session)
cookie, err := req.Cookie(revel.CookiePrefix + "_SESSION")
cookie, err := req.Cookie(revel.CookiePrefix + "_SESSION")
if err != nil {
if err != nil {
return session
return session
}
}
// Separate the token from the signature.
// Separate the token from the signature.
hyphen := strings.Index(cookie.Value, "-")
hyphen := strings.Index(cookie.Value, "-")
if hyphen == -1 || hyphen >= len(cookie.Value)-1 {
if hyphen == -1 || hyphen >= len(cookie.Value)-1 {
return session
return session
}
}
sig, token := cookie.Value[:hyphen], cookie.Value[hyphen+1:]
sig, token := cookie.Value[:hyphen], cookie.Value[hyphen+1:]
// Verify the signature.
// Verify the signature.
if !revel.Verify(token, sig) {
if !revel.Verify(token, sig) {
revel.INFO.Println("Session cookie signature failed")
revel.INFO.Println("Session cookie signature failed")
return session
return session
}
}
redisConn := Redis.Get()
rc := redis.New()
defer redisConn.Close()
rc.Connect("127.0.0.1", 6379)
defer rc.Quit()
data, err := redis.String(redisConn.Do("GET", "session:"+token))
data, err := rc.Get("session:" + token)
if err != nil {
if err != nil {
revel.ERROR.Println(err)
revel.ERROR.Println(err)
return session
return session
}
}
revel.ParseKeyValueCookie(data, func(key, val string) {
revel.ParseKeyValueCookie(data, func(key, val string) {
session[key] = val
session[key] = val
})
})
session[SESSION_ID_KEY] = token
session[SESSION_ID_KEY] = token
if csrfToken, ok := session[CSRF_TOKEN_KEY]; ok && csrfEnabled {
if csrfToken, ok := session[CSRF_TOKEN_KEY]; ok && csrfEnabled {
csrfCookie, err := req.Cookie(revel.CookiePrefix + CSRF_COOKIE)
csrfCookie, err := req.Cookie(revel.CookiePrefix + CSRF_COOKIE)
if err != nil || csrfCookie.Value != csrfToken {
if err != nil || csrfCookie.Value != csrfToken {
err = RevokeSession(&session)
err = RevokeSession(&session)
if err != nil {
if err != nil {
revel.ERROR.Println(err)
revel.ERROR.Println(err)
}
}
return session
return session
}
}
if req.Method != "GET" && req.Method != "HEAD" {
if req.Method != "GET" && req.Method != "HEAD" {
csrfHeader := req.Header.Get(CSRF_HEADER)
csrfHeader := req.Header.Get(CSRF_HEADER)
if csrfHeader != csrfToken {
if csrfHeader != csrfToken {
err = RevokeSession(&session)
err = RevokeSession(&session)
if err != nil {
if err != nil {
revel.ERROR.Println(err)
revel.ERROR.Println(err)
}
}
return session
return session
}
}
}
}
}
}
return session
return session
}
}
func RevokeSession(s *revel.Session) error {
func RevokeSession(s *revel.Session) error {
token, ok := (*s)[SESSION_ID_KEY]
token, ok := (*s)[SESSION_ID_KEY]
*s = make(revel.Session)
*s = make(revel.Session)
if !ok {
if !ok {
return nil
return nil
}
}
revel.INFO.Println("Revoking session:", token)
revel.INFO.Println("Revoking session:", token)
redisConn := Redis.Get()
rc := redis.New()
defer redisConn.Close()
rc.Connect("127.0.0.1", 6379)
defer rc.Quit()
_, err := redis.Int(redisConn.Do("DEL", "session:"+token))
_, err := rc.Del("session:" + token)
return err
return err
}
}
func SessionFilter(c *revel.Controller, fc []revel.Filter) {
func SessionFilter(c *revel.Controller, fc []revel.Filter) {
c.Session = restoreSession(c.Request.Request)
c.Session = restoreSession(c.Request.Request)
// Make session vars available in templates as {{.session.xyz}}
// Make session vars available in templates as {{.session.xyz}}
c.RenderArgs["session"] = c.Session
c.RenderArgs["session"] = c.Session
fc[0](c, fc[1:])
fc[0](c, fc[1:])
// Store the session (and sign it) if it isn't empty.
// Store the session (and sign it) if it isn't empty.
cookie := saveSession(c.Session)
cookie := saveSession(c.Session)
if cookie != nil {
if cookie != nil {
c.SetCookie(cookie)
c.SetCookie(cookie)
if csrfEnabled {
if csrfEnabled {
c.SetCookie(&http.Cookie{
c.SetCookie(&http.Cookie{
Name: revel.CookiePrefix + CSRF_COOKIE,
Name: revel.CookiePrefix + CSRF_COOKIE,
Value: c.Session[CSRF_TOKEN_KEY],
Value: c.Session[CSRF_TOKEN_KEY],
Path: "/",
Path: "/",
Expires: cookie.Expires,
Expires: cookie.Expires,
})
})
}
}
}
}
}
}