mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
HTTP API: grafana /render calls nows with api keys, Fixes #1649
This commit is contained in:
parent
f28af4f369
commit
059db533d5
@ -5,6 +5,7 @@
|
||||
- [Issue #1660](https://github.com/grafana/grafana/issues/1660). OAuth: Specify allowed email address domains for google or and github oauth logins
|
||||
|
||||
**Fixes**
|
||||
- [Issue #1649](https://github.com/grafana/grafana/issues/1649). HTTP API: grafana /render calls nows with api keys
|
||||
- [Issue #1667](https://github.com/grafana/grafana/issues/1667). Datasource proxy & session timeout fix (casued 401 Unauthorized error after a while)
|
||||
- [Issue #1707](https://github.com/grafana/grafana/issues/1707). Unsaved changes: Do not show for snapshots, scripted and file based dashboards
|
||||
- [Issue #1703](https://github.com/grafana/grafana/issues/1703). Unsaved changes: Do not show for users with role `Viewer`
|
||||
|
@ -13,6 +13,18 @@ import (
|
||||
func RenderToPng(c *middleware.Context) {
|
||||
queryReader := util.NewUrlQueryReader(c.Req.URL)
|
||||
queryParams := fmt.Sprintf("?%s", c.Req.URL.RawQuery)
|
||||
sessionId := c.Session.ID()
|
||||
|
||||
// Handle api calls authenticated without session
|
||||
if sessionId == "" && c.ApiKeyId != 0 {
|
||||
c.Session.Start(c)
|
||||
c.Session.Set(middleware.SESS_KEY_APIKEY, c.ApiKeyId)
|
||||
// release will make sure the new session is persisted before
|
||||
// we spin up phantomjs
|
||||
c.Session.Release()
|
||||
// cleanup session after render is complete
|
||||
defer func() { c.Session.Destory(c) }()
|
||||
}
|
||||
|
||||
renderOpts := &renderer.RenderOpts{
|
||||
Url: c.Params("*") + queryParams,
|
||||
|
@ -1,8 +1,6 @@
|
||||
package renderer
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -11,6 +9,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type RenderOpts struct {
|
||||
@ -24,7 +23,7 @@ func RenderToPng(params *RenderOpts) (string, error) {
|
||||
log.Info("PhantomRenderer::renderToPng url %v", params.Url)
|
||||
binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "phantomjs"))
|
||||
scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))
|
||||
pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, getHash(params.Url)))
|
||||
pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, util.GetRandomString(20)))
|
||||
pngPath = pngPath + ".png"
|
||||
|
||||
cmd := exec.Command(binPath, scriptPath, "url="+params.Url, "width="+params.Width,
|
||||
@ -64,9 +63,3 @@ func RenderToPng(params *RenderOpts) (string, error) {
|
||||
|
||||
return pngPath, nil
|
||||
}
|
||||
|
||||
func getHash(text string) string {
|
||||
hasher := md5.New()
|
||||
hasher.Write([]byte(text))
|
||||
return hex.EncodeToString(hasher.Sum(nil))
|
||||
}
|
||||
|
@ -34,8 +34,14 @@ func GetContextHandler() macaron.Handler {
|
||||
AllowAnonymous: false,
|
||||
}
|
||||
|
||||
// the order in which these are tested are important
|
||||
// look for api key in Authorization header first
|
||||
// then init session and look for userId in session
|
||||
// then look for api key in session (special case for render calls via api)
|
||||
// then test if anonymous access is enabled
|
||||
if initContextWithApiKey(ctx) ||
|
||||
initContextWithUserSessionCookie(ctx) ||
|
||||
initContextWithApiKeyFromSession(ctx) ||
|
||||
initContextWithAnonymousUser(ctx) {
|
||||
}
|
||||
|
||||
@ -77,7 +83,6 @@ func initContextWithUserSessionCookie(ctx *Context) bool {
|
||||
|
||||
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
|
||||
@ -114,8 +119,29 @@ func initContextWithApiKey(ctx *Context) bool {
|
||||
|
||||
ctx.IsSignedIn = true
|
||||
ctx.SignedInUser = &m.SignedInUser{}
|
||||
ctx.OrgRole = apikey.Role
|
||||
ctx.ApiKeyId = apikey.Id
|
||||
ctx.OrgId = apikey.OrgId
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix this
|
||||
// special case for panel render calls with api key
|
||||
func initContextWithApiKeyFromSession(ctx *Context) bool {
|
||||
keyId := ctx.Session.Get(SESS_KEY_APIKEY)
|
||||
if keyId == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
keyQuery := m.GetApiKeyByIdQuery{ApiKeyId: keyId.(int64)}
|
||||
if err := bus.Dispatch(&keyQuery); err != nil {
|
||||
log.Error(3, "Failed to get api key by id", err)
|
||||
return false
|
||||
} else {
|
||||
apikey := keyQuery.Result
|
||||
|
||||
ctx.IsSignedIn = true
|
||||
ctx.SignedInUser = &m.SignedInUser{}
|
||||
ctx.OrgRole = apikey.Role
|
||||
ctx.ApiKeyId = apikey.Id
|
||||
ctx.OrgId = apikey.OrgId
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SESS_KEY_USERID = "uid"
|
||||
SESS_KEY_FAVORITES = "favorites"
|
||||
SESS_KEY_USERID = "uid"
|
||||
SESS_KEY_APIKEY = "apikey_id" // used fror render requests with api keys
|
||||
)
|
||||
|
||||
var sessionManager *session.Manager
|
||||
@ -102,7 +102,10 @@ func (s *SessionWrapper) Release() error {
|
||||
|
||||
func (s *SessionWrapper) Destory(c *Context) error {
|
||||
if s.session != nil {
|
||||
return s.manager.Destory(c.Context)
|
||||
if err := s.manager.Destory(c.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
s.session = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -55,6 +55,11 @@ type GetApiKeyByNameQuery struct {
|
||||
Result *ApiKey
|
||||
}
|
||||
|
||||
type GetApiKeyByIdQuery struct {
|
||||
ApiKeyId int64
|
||||
Result *ApiKey
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// DTO & Projections
|
||||
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
func init() {
|
||||
bus.AddHandler("sql", GetApiKeys)
|
||||
bus.AddHandler("sql", GetApiKeyById)
|
||||
bus.AddHandler("sql", GetApiKeyByName)
|
||||
bus.AddHandler("sql", DeleteApiKey)
|
||||
bus.AddHandler("sql", AddApiKey)
|
||||
@ -49,6 +50,20 @@ func AddApiKey(cmd *m.AddApiKeyCommand) error {
|
||||
})
|
||||
}
|
||||
|
||||
func GetApiKeyById(query *m.GetApiKeyByIdQuery) error {
|
||||
var apikey m.ApiKey
|
||||
has, err := x.Id(query.ApiKeyId).Get(&apikey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has == false {
|
||||
return m.ErrInvalidApiKey
|
||||
}
|
||||
|
||||
query.Result = &apikey
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetApiKeyByName(query *m.GetApiKeyByNameQuery) error {
|
||||
var apikey m.ApiKey
|
||||
has, err := x.Where("org_id=? AND name=?", query.OrgId, query.KeyName).Get(&apikey)
|
||||
|
@ -259,6 +259,10 @@ func readSessionConfig() {
|
||||
if SessionOptions.Provider == "file" {
|
||||
os.MkdirAll(path.Dir(SessionOptions.ProviderConfig), os.ModePerm)
|
||||
}
|
||||
|
||||
if SessionOptions.CookiePath == "" {
|
||||
SessionOptions.CookiePath = "/"
|
||||
}
|
||||
}
|
||||
|
||||
var logLevels = map[string]string{
|
||||
|
Loading…
Reference in New Issue
Block a user