Untitled diff

Created Diff never expires
19 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
200 lines
26 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
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,
})
})
}
}
}
}
}
}