Commit c07d48d9 by Torkel Ödegaard

A big refactoring for how sessions are handled, Api calls that authenticate with…

A big refactoring for how sessions are handled, Api calls that authenticate with api key will no longer create a new session
parent 00a713c4
......@@ -139,6 +139,6 @@ func loginUserWithUser(user *m.User, c *middleware.Context) {
func Logout(c *middleware.Context) {
c.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl+"/")
c.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl+"/")
c.Session.Destory(c.Context)
c.Session.Destory(c)
c.Redirect(setting.AppSubUrl + "/login")
}
......@@ -14,10 +14,6 @@ import (
"github.com/Unknwon/macaron"
"github.com/codegangsta/cli"
"github.com/macaron-contrib/session"
_ "github.com/macaron-contrib/session/mysql"
_ "github.com/macaron-contrib/session/postgres"
_ "github.com/macaron-contrib/session/redis"
"github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/api/static"
......@@ -54,8 +50,6 @@ func newMacaron() *macaron.Macaron {
mapStatic(m, "img", "img")
mapStatic(m, "fonts", "fonts")
m.Use(session.Sessioner(setting.SessionOptions))
m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: path.Join(setting.StaticRootPath, "views"),
IndentJSON: macaron.Env != macaron.PROD,
......@@ -63,6 +57,8 @@ func newMacaron() *macaron.Macaron {
}))
m.Use(middleware.GetContextHandler())
m.Use(middleware.Sessioner(setting.SessionOptions))
return m
}
......
......@@ -5,7 +5,6 @@ import (
"strings"
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/session"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/apikeygen"
......@@ -19,78 +18,111 @@ type Context struct {
*macaron.Context
*m.SignedInUser
Session session.Store
Session SessionStore
IsSignedIn bool
AllowAnonymous bool
}
func GetContextHandler() macaron.Handler {
return func(c *macaron.Context, sess session.Store) {
return func(c *macaron.Context) {
ctx := &Context{
Context: c,
Session: sess,
SignedInUser: &m.SignedInUser{},
Session: GetSession(),
IsSignedIn: false,
AllowAnonymous: false,
}
// try get account id from request
if userId := getRequestUserId(ctx); userId != 0 {
query := m.GetSignedInUserQuery{UserId: userId}
if err := bus.Dispatch(&query); err != nil {
log.Error(3, "Failed to get user by id, %v, %v", userId, err)
} else {
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
}
} else if keyString := getApiKey(ctx); keyString != "" {
// base64 decode key
decoded, err := apikeygen.Decode(keyString)
if err != nil {
ctx.JsonApiErr(401, "Invalid API key", err)
return
}
// fetch key
keyQuery := m.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId}
if err := bus.Dispatch(&keyQuery); err != nil {
ctx.JsonApiErr(401, "Invalid API key", err)
return
} else {
apikey := keyQuery.Result
// validate api key
if !apikeygen.IsValid(decoded, apikey.Key) {
ctx.JsonApiErr(401, "Invalid API key", err)
return
}
ctx.IsSignedIn = true
ctx.SignedInUser = &m.SignedInUser{}
// TODO: fix this
ctx.OrgRole = apikey.Role
ctx.ApiKeyId = apikey.Id
ctx.OrgId = apikey.OrgId
}
} else if setting.AnonymousEnabled {
orgQuery := m.GetOrgByNameQuery{Name: setting.AnonymousOrgName}
if err := bus.Dispatch(&orgQuery); err != nil {
log.Error(3, "Anonymous access organization error: '%s': %s", setting.AnonymousOrgName, err)
} else {
ctx.IsSignedIn = false
ctx.AllowAnonymous = true
ctx.SignedInUser = &m.SignedInUser{}
ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole)
ctx.OrgId = orgQuery.Result.Id
ctx.OrgName = orgQuery.Result.Name
}
if initContextWithApiKey(ctx) ||
initContextWithUserSessionCookie(ctx) ||
initContextWithAnonymousUser(ctx) {
}
c.Map(ctx)
}
}
func initContextWithAnonymousUser(ctx *Context) bool {
if !setting.AnonymousEnabled {
return false
}
orgQuery := m.GetOrgByNameQuery{Name: setting.AnonymousOrgName}
if err := bus.Dispatch(&orgQuery); err != nil {
log.Error(3, "Anonymous access organization error: '%s': %s", setting.AnonymousOrgName, err)
return false
} else {
ctx.IsSignedIn = false
ctx.AllowAnonymous = true
ctx.SignedInUser = &m.SignedInUser{}
ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole)
ctx.OrgId = orgQuery.Result.Id
ctx.OrgName = orgQuery.Result.Name
return true
}
}
func initContextWithUserSessionCookie(ctx *Context) bool {
// initialize session
if err := ctx.Session.Start(ctx); err != nil {
log.Error(3, "Failed to start session", err)
return false
}
var userId int64
if userId = getRequestUserId(ctx); userId == 0 {
return false
}
query := m.GetSignedInUserQuery{UserId: userId}
if err := bus.Dispatch(&query); err != nil {
log.Error(3, "Failed to get user by id, %v, %v", userId, err)
return false
} else {
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
return true
}
}
func initContextWithApiKey(ctx *Context) bool {
var keyString string
if keyString = getApiKey(ctx); keyString == "" {
return false
}
// base64 decode key
decoded, err := apikeygen.Decode(keyString)
if err != nil {
ctx.JsonApiErr(401, "Invalid API key", err)
return true
}
// fetch key
keyQuery := m.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId}
if err := bus.Dispatch(&keyQuery); err != nil {
ctx.JsonApiErr(401, "Invalid API key", err)
return true
} else {
apikey := keyQuery.Result
// validate api key
if !apikeygen.IsValid(decoded, apikey.Key) {
ctx.JsonApiErr(401, "Invalid API key", err)
return true
}
ctx.IsSignedIn = true
ctx.SignedInUser = &m.SignedInUser{}
// TODO: fix this
ctx.OrgRole = apikey.Role
ctx.ApiKeyId = apikey.Id
ctx.OrgId = apikey.OrgId
return true
}
}
// Handle handles and logs error by given status.
func (ctx *Context) Handle(status int, title string, err error) {
if err != nil {
......
package middleware
import (
"time"
"github.com/Unknwon/macaron"
"github.com/macaron-contrib/session"
_ "github.com/macaron-contrib/session/mysql"
_ "github.com/macaron-contrib/session/postgres"
_ "github.com/macaron-contrib/session/redis"
)
const (
SESS_KEY_USERID = "uid"
SESS_KEY_FAVORITES = "favorites"
)
var sessionManager *session.Manager
var sessionOptions session.Options
func startSessionGC() {
sessionManager.GC()
time.AfterFunc(time.Duration(sessionOptions.Gclifetime)*time.Second, startSessionGC)
}
func Sessioner(options session.Options) macaron.Handler {
var err error
sessionOptions = options
sessionManager, err = session.NewManager(options.Provider, options)
if err != nil {
panic(err)
}
go startSessionGC()
return func(ctx *Context) {
ctx.Next()
if err = ctx.Session.Release(); err != nil {
panic("session(release): " + err.Error())
}
}
}
func GetSession() SessionStore {
return &SessionWrapper{manager: sessionManager}
}
type SessionStore interface {
// Set sets value to given key in session.
Set(interface{}, interface{}) error
// Get gets value by given key in session.
Get(interface{}) interface{}
// ID returns current session ID.
ID() string
// Release releases session resource and save data to provider.
Release() error
// Destory deletes a session.
Destory(*Context) error
// init
Start(*Context) error
}
type SessionWrapper struct {
session session.RawStore
manager *session.Manager
}
func (s *SessionWrapper) Start(c *Context) error {
var err error
s.session, err = s.manager.Start(c.Context)
return err
}
func (s *SessionWrapper) Set(k interface{}, v interface{}) error {
if s.session != nil {
return s.session.Set(k, v)
}
return nil
}
func (s *SessionWrapper) Get(k interface{}) interface{} {
if s.session != nil {
return s.session.Get(k)
}
return nil
}
func (s *SessionWrapper) ID() string {
if s.session != nil {
return s.session.ID()
}
return ""
}
func (s *SessionWrapper) Release() error {
if s.session != nil {
return s.session.Release()
}
return nil
}
func (s *SessionWrapper) Destory(c *Context) error {
if s.session != nil {
return s.manager.Destory(c.Context)
}
return nil
}
......@@ -254,6 +254,7 @@ func readSessionConfig() {
SessionOptions.Secure = sec.Key("cookie_secure").MustBool()
SessionOptions.Gclifetime = Cfg.Section("session").Key("gc_interval_time").MustInt64(86400)
SessionOptions.Maxlifetime = Cfg.Section("session").Key("session_life_time").MustInt64(86400)
SessionOptions.IDLength = 16
if SessionOptions.Provider == "file" {
os.MkdirAll(path.Dir(SessionOptions.ProviderConfig), os.ModePerm)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment