mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
A big refactoring for how sessions are handled, Api calls that authenticate with api key will no longer create a new session
This commit is contained in:
parent
00a713c42e
commit
c07d48d930
@ -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 {
|
||||
|
@ -1,6 +1,108 @@
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user