Untitled diff
200 linhas
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,
			})
			})
		}
		}
	}
	}
}
}