mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Fixed hashing of passwords, Closes #3
This commit is contained in:
parent
6d814af0cc
commit
3226a3a58e
2
grafana
2
grafana
@ -1 +1 @@
|
|||||||
Subproject commit d3e11cabd51d082244f83258b02fd635cc780c08
|
Subproject commit 164435f71d3462fa1719791e1c8eb9841374d299
|
@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||||
"github.com/torkelo/grafana-pro/pkg/utils"
|
"github.com/torkelo/grafana-pro/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetAccount(c *middleware.Context) {
|
func GetAccount(c *middleware.Context) {
|
||||||
@ -59,7 +59,7 @@ func GetOtherAccounts(c *middleware.Context) {
|
|||||||
err := bus.Dispatch(&query)
|
err := bus.Dispatch(&query)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(500, utils.DynMap{"message": err.Error()})
|
c.JSON(500, util.DynMap{"message": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||||
"github.com/torkelo/grafana-pro/pkg/utils"
|
"github.com/torkelo/grafana-pro/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetDashboard(c *middleware.Context) {
|
func GetDashboard(c *middleware.Context) {
|
||||||
@ -29,15 +26,13 @@ func DeleteDashboard(c *middleware.Context) {
|
|||||||
slug := c.Params(":slug")
|
slug := c.Params(":slug")
|
||||||
|
|
||||||
query := m.GetDashboardQuery{Slug: slug, AccountId: c.GetAccountId()}
|
query := m.GetDashboardQuery{Slug: slug, AccountId: c.GetAccountId()}
|
||||||
err := bus.Dispatch(&query)
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
if err != nil {
|
|
||||||
c.JsonApiErr(404, "Dashboard not found", nil)
|
c.JsonApiErr(404, "Dashboard not found", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.GetAccountId()}
|
cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.GetAccountId()}
|
||||||
err = bus.Dispatch(&cmd)
|
if err := bus.Dispatch(&cmd); err != nil {
|
||||||
if err != nil {
|
|
||||||
c.JsonApiErr(500, "Failed to delete dashboard", err)
|
c.JsonApiErr(500, "Failed to delete dashboard", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -47,41 +42,6 @@ func DeleteDashboard(c *middleware.Context) {
|
|||||||
c.JSON(200, resp)
|
c.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Search(c *middleware.Context) {
|
|
||||||
queryText := c.Query("q")
|
|
||||||
result := m.SearchResult{
|
|
||||||
Dashboards: []*m.DashboardSearchHit{},
|
|
||||||
Tags: []*m.DashboardTagCloudItem{},
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(queryText, "tags!:") {
|
|
||||||
query := m.GetDashboardTagsQuery{}
|
|
||||||
err := bus.Dispatch(&query)
|
|
||||||
if err != nil {
|
|
||||||
c.JsonApiErr(500, "Failed to get tags from database", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result.Tags = query.Result
|
|
||||||
result.TagsOnly = true
|
|
||||||
} else {
|
|
||||||
searchQueryRegEx, _ := regexp.Compile(`(tags:(\w*)\sAND\s)?(?:title:)?(.*)?`)
|
|
||||||
matches := searchQueryRegEx.FindStringSubmatch(queryText)
|
|
||||||
query := m.SearchDashboardsQuery{
|
|
||||||
Title: matches[3],
|
|
||||||
Tag: matches[2],
|
|
||||||
AccountId: c.GetAccountId(),
|
|
||||||
}
|
|
||||||
err := bus.Dispatch(&query)
|
|
||||||
if err != nil {
|
|
||||||
c.JsonApiErr(500, "Search failed", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result.Dashboards = query.Result
|
|
||||||
}
|
|
||||||
|
|
||||||
c.JSON(200, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PostDashboard(c *middleware.Context) {
|
func PostDashboard(c *middleware.Context) {
|
||||||
var cmd m.SaveDashboardCommand
|
var cmd m.SaveDashboardCommand
|
||||||
|
|
||||||
@ -102,5 +62,5 @@ func PostDashboard(c *middleware.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(200, utils.DynMap{"status": "success", "slug": cmd.Result.Slug})
|
c.JSON(200, util.DynMap{"status": "success", "slug": cmd.Result.Slug})
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||||
"github.com/torkelo/grafana-pro/pkg/utils"
|
"github.com/torkelo/grafana-pro/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy {
|
func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy {
|
||||||
@ -21,12 +21,12 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy
|
|||||||
reqQueryVals := req.URL.Query()
|
reqQueryVals := req.URL.Query()
|
||||||
|
|
||||||
if ds.Type == m.DS_INFLUXDB {
|
if ds.Type == m.DS_INFLUXDB {
|
||||||
req.URL.Path = utils.JoinUrlFragments(target.Path, "db/"+ds.Database+"/"+proxyPath)
|
req.URL.Path = util.JoinUrlFragments(target.Path, "db/"+ds.Database+"/"+proxyPath)
|
||||||
reqQueryVals.Add("u", ds.User)
|
reqQueryVals.Add("u", ds.User)
|
||||||
reqQueryVals.Add("p", ds.Password)
|
reqQueryVals.Add("p", ds.Password)
|
||||||
req.URL.RawQuery = reqQueryVals.Encode()
|
req.URL.RawQuery = reqQueryVals.Encode()
|
||||||
} else {
|
} else {
|
||||||
req.URL.Path = utils.JoinUrlFragments(target.Path, proxyPath)
|
req.URL.Path = util.JoinUrlFragments(target.Path, proxyPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/torkelo/grafana-pro/pkg/log"
|
"github.com/torkelo/grafana-pro/pkg/log"
|
||||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||||
"github.com/torkelo/grafana-pro/pkg/utils"
|
"github.com/torkelo/grafana-pro/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type loginJsonModel struct {
|
type loginJsonModel struct {
|
||||||
@ -19,7 +19,7 @@ func LoginPost(c *middleware.Context) {
|
|||||||
var loginModel loginJsonModel
|
var loginModel loginJsonModel
|
||||||
|
|
||||||
if !c.JsonBody(&loginModel) {
|
if !c.JsonBody(&loginModel) {
|
||||||
c.JSON(400, utils.DynMap{"status": "bad request"})
|
c.JSON(400, util.DynMap{"message": "bad request"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +33,8 @@ func LoginPost(c *middleware.Context) {
|
|||||||
|
|
||||||
account := userQuery.Result
|
account := userQuery.Result
|
||||||
|
|
||||||
if loginModel.Password != account.Password {
|
passwordHashed := util.EncodePassword(loginModel.Password, account.Salt)
|
||||||
|
if passwordHashed != account.Password {
|
||||||
c.JsonApiErr(401, "Invalid username or password", err)
|
c.JsonApiErr(401, "Invalid username or password", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -57,5 +58,5 @@ func loginUserWithAccount(account *m.Account, c *middleware.Context) {
|
|||||||
|
|
||||||
func LogoutPost(c *middleware.Context) {
|
func LogoutPost(c *middleware.Context) {
|
||||||
c.Session.Delete("accountId")
|
c.Session.Delete("accountId")
|
||||||
c.JSON(200, utils.DynMap{"status": "logged out"})
|
c.JSON(200, util.DynMap{"status": "logged out"})
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/torkelo/grafana-pro/pkg/bus"
|
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||||
|
"github.com/torkelo/grafana-pro/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateAccount(c *middleware.Context) {
|
func CreateAccount(c *middleware.Context) {
|
||||||
@ -15,9 +16,10 @@ func CreateAccount(c *middleware.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Login = cmd.Email
|
cmd.Login = cmd.Email
|
||||||
err := bus.Dispatch(&cmd)
|
cmd.Salt = util.GetRandomString(10)
|
||||||
|
cmd.Password = util.EncodePassword(cmd.Password, cmd.Salt)
|
||||||
|
|
||||||
if err != nil {
|
if err := bus.Dispatch(&cmd); err != nil {
|
||||||
c.JsonApiErr(500, "failed to create account", err)
|
c.JsonApiErr(500, "failed to create account", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,12 @@ import (
|
|||||||
|
|
||||||
"github.com/torkelo/grafana-pro/pkg/components/renderer"
|
"github.com/torkelo/grafana-pro/pkg/components/renderer"
|
||||||
"github.com/torkelo/grafana-pro/pkg/middleware"
|
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||||
"github.com/torkelo/grafana-pro/pkg/utils"
|
"github.com/torkelo/grafana-pro/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RenderToPng(c *middleware.Context) {
|
func RenderToPng(c *middleware.Context) {
|
||||||
accountId := c.GetAccountId()
|
accountId := c.GetAccountId()
|
||||||
queryReader := utils.NewUrlQueryReader(c.Req.URL)
|
queryReader := util.NewUrlQueryReader(c.Req.URL)
|
||||||
queryParams := "?render&accountId=" + strconv.FormatInt(accountId, 10) + "&" + c.Req.URL.RawQuery
|
queryParams := "?render&accountId=" + strconv.FormatInt(accountId, 10) + "&" + c.Req.URL.RawQuery
|
||||||
|
|
||||||
renderOpts := &renderer.RenderOpts{
|
renderOpts := &renderer.RenderOpts{
|
||||||
|
45
pkg/api/search.go
Normal file
45
pkg/api/search.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/torkelo/grafana-pro/pkg/bus"
|
||||||
|
"github.com/torkelo/grafana-pro/pkg/middleware"
|
||||||
|
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Search(c *middleware.Context) {
|
||||||
|
queryText := c.Query("q")
|
||||||
|
result := m.SearchResult{
|
||||||
|
Dashboards: []*m.DashboardSearchHit{},
|
||||||
|
Tags: []*m.DashboardTagCloudItem{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(queryText, "tags!:") {
|
||||||
|
query := m.GetDashboardTagsQuery{}
|
||||||
|
err := bus.Dispatch(&query)
|
||||||
|
if err != nil {
|
||||||
|
c.JsonApiErr(500, "Failed to get tags from database", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result.Tags = query.Result
|
||||||
|
result.TagsOnly = true
|
||||||
|
} else {
|
||||||
|
searchQueryRegEx, _ := regexp.Compile(`(tags:(\w*)\sAND\s)?(?:title:)?(.*)?`)
|
||||||
|
matches := searchQueryRegEx.FindStringSubmatch(queryText)
|
||||||
|
query := m.SearchDashboardsQuery{
|
||||||
|
Title: matches[3],
|
||||||
|
Tag: matches[2],
|
||||||
|
AccountId: c.GetAccountId(),
|
||||||
|
}
|
||||||
|
err := bus.Dispatch(&query)
|
||||||
|
if err != nil {
|
||||||
|
c.JsonApiErr(500, "Search failed", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result.Dashboards = query.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, result)
|
||||||
|
}
|
@ -18,6 +18,7 @@ type Account struct {
|
|||||||
FullName string
|
FullName string
|
||||||
Password string
|
Password string
|
||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
|
Rands string `xorm:"VARCHAR(10)"`
|
||||||
Salt string `xorm:"VARCHAR(10)"`
|
Salt string `xorm:"VARCHAR(10)"`
|
||||||
Company string
|
Company string
|
||||||
NextDashboardId int
|
NextDashboardId int
|
||||||
@ -55,6 +56,7 @@ type CreateAccountCommand struct {
|
|||||||
Password string `json:"password" binding:"required"`
|
Password string `json:"password" binding:"required"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Company string `json:"company"`
|
Company string `json:"company"`
|
||||||
|
Salt string `json:"-"`
|
||||||
|
|
||||||
Result Account `json:"-"`
|
Result Account `json:"-"`
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ func CreateAccount(cmd *m.CreateAccountCommand) error {
|
|||||||
Email: cmd.Email,
|
Email: cmd.Email,
|
||||||
Login: cmd.Login,
|
Login: cmd.Login,
|
||||||
Password: cmd.Password,
|
Password: cmd.Password,
|
||||||
|
Salt: cmd.Salt,
|
||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
Updated: time.Now(),
|
Updated: time.Now(),
|
||||||
}
|
}
|
||||||
|
67
pkg/util/encoding.go
Normal file
67
pkg/util/encoding.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// source: https://github.com/gogits/gogs/blob/9ee80e3e5426821f03a4e99fad34418f5c736413/modules/base/tool.go#L58
|
||||||
|
func GetRandomString(n int, alphabets ...byte) string {
|
||||||
|
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
var bytes = make([]byte, n)
|
||||||
|
rand.Read(bytes)
|
||||||
|
for i, b := range bytes {
|
||||||
|
if len(alphabets) == 0 {
|
||||||
|
bytes[i] = alphanum[b%byte(len(alphanum))]
|
||||||
|
} else {
|
||||||
|
bytes[i] = alphabets[b%byte(len(alphabets))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodePassword(password string, salt string) string {
|
||||||
|
newPasswd := PBKDF2([]byte(password), []byte(salt), 10000, 50, sha256.New)
|
||||||
|
return fmt.Sprintf("%x", newPasswd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto
|
||||||
|
func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
||||||
|
prf := hmac.New(h, password)
|
||||||
|
hashLen := prf.Size()
|
||||||
|
numBlocks := (keyLen + hashLen - 1) / hashLen
|
||||||
|
|
||||||
|
var buf [4]byte
|
||||||
|
dk := make([]byte, 0, numBlocks*hashLen)
|
||||||
|
U := make([]byte, hashLen)
|
||||||
|
for block := 1; block <= numBlocks; block++ {
|
||||||
|
// N.B.: || means concatenation, ^ means XOR
|
||||||
|
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
|
||||||
|
// U_1 = PRF(password, salt || uint(i))
|
||||||
|
prf.Reset()
|
||||||
|
prf.Write(salt)
|
||||||
|
buf[0] = byte(block >> 24)
|
||||||
|
buf[1] = byte(block >> 16)
|
||||||
|
buf[2] = byte(block >> 8)
|
||||||
|
buf[3] = byte(block)
|
||||||
|
prf.Write(buf[:4])
|
||||||
|
dk = prf.Sum(dk)
|
||||||
|
T := dk[len(dk)-hashLen:]
|
||||||
|
copy(U, T)
|
||||||
|
|
||||||
|
// U_n = PRF(password, U_(n-1))
|
||||||
|
for n := 2; n <= iter; n++ {
|
||||||
|
prf.Reset()
|
||||||
|
prf.Write(U)
|
||||||
|
U = U[:0]
|
||||||
|
U = prf.Sum(U)
|
||||||
|
for x := range U {
|
||||||
|
T[x] ^= U[x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dk[:keyLen]
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
package utils
|
package util
|
||||||
|
|
||||||
type DynMap map[string]interface{}
|
type DynMap map[string]interface{}
|
@ -1,4 +1,4 @@
|
|||||||
package utils
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
Loading…
Reference in New Issue
Block a user