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/middleware"
|
||||
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) {
|
||||
@ -59,7 +59,7 @@ func GetOtherAccounts(c *middleware.Context) {
|
||||
err := bus.Dispatch(&query)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(500, utils.DynMap{"message": err.Error()})
|
||||
c.JSON(500, util.DynMap{"message": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,10 @@
|
||||
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"
|
||||
"github.com/torkelo/grafana-pro/pkg/utils"
|
||||
"github.com/torkelo/grafana-pro/pkg/util"
|
||||
)
|
||||
|
||||
func GetDashboard(c *middleware.Context) {
|
||||
@ -29,15 +26,13 @@ func DeleteDashboard(c *middleware.Context) {
|
||||
slug := c.Params(":slug")
|
||||
|
||||
query := m.GetDashboardQuery{Slug: slug, AccountId: c.GetAccountId()}
|
||||
err := bus.Dispatch(&query)
|
||||
if err != nil {
|
||||
if err := bus.Dispatch(&query); err != nil {
|
||||
c.JsonApiErr(404, "Dashboard not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.GetAccountId()}
|
||||
err = bus.Dispatch(&cmd)
|
||||
if err != nil {
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
c.JsonApiErr(500, "Failed to delete dashboard", err)
|
||||
return
|
||||
}
|
||||
@ -47,41 +42,6 @@ func DeleteDashboard(c *middleware.Context) {
|
||||
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) {
|
||||
var cmd m.SaveDashboardCommand
|
||||
|
||||
@ -102,5 +62,5 @@ func PostDashboard(c *middleware.Context) {
|
||||
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/middleware"
|
||||
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 {
|
||||
@ -21,12 +21,12 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy
|
||||
reqQueryVals := req.URL.Query()
|
||||
|
||||
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("p", ds.Password)
|
||||
req.URL.RawQuery = reqQueryVals.Encode()
|
||||
} 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/middleware"
|
||||
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 {
|
||||
@ -19,7 +19,7 @@ func LoginPost(c *middleware.Context) {
|
||||
var loginModel loginJsonModel
|
||||
|
||||
if !c.JsonBody(&loginModel) {
|
||||
c.JSON(400, utils.DynMap{"status": "bad request"})
|
||||
c.JSON(400, util.DynMap{"message": "bad request"})
|
||||
return
|
||||
}
|
||||
|
||||
@ -33,7 +33,8 @@ func LoginPost(c *middleware.Context) {
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
@ -57,5 +58,5 @@ func loginUserWithAccount(account *m.Account, c *middleware.Context) {
|
||||
|
||||
func LogoutPost(c *middleware.Context) {
|
||||
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/middleware"
|
||||
m "github.com/torkelo/grafana-pro/pkg/models"
|
||||
"github.com/torkelo/grafana-pro/pkg/util"
|
||||
)
|
||||
|
||||
func CreateAccount(c *middleware.Context) {
|
||||
@ -15,9 +16,10 @@ func CreateAccount(c *middleware.Context) {
|
||||
}
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
@ -6,12 +6,12 @@ import (
|
||||
|
||||
"github.com/torkelo/grafana-pro/pkg/components/renderer"
|
||||
"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) {
|
||||
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
|
||||
|
||||
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
|
||||
Password string
|
||||
IsAdmin bool
|
||||
Rands string `xorm:"VARCHAR(10)"`
|
||||
Salt string `xorm:"VARCHAR(10)"`
|
||||
Company string
|
||||
NextDashboardId int
|
||||
@ -55,6 +56,7 @@ type CreateAccountCommand struct {
|
||||
Password string `json:"password" binding:"required"`
|
||||
Name string `json:"name"`
|
||||
Company string `json:"company"`
|
||||
Salt string `json:"-"`
|
||||
|
||||
Result Account `json:"-"`
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ func CreateAccount(cmd *m.CreateAccountCommand) error {
|
||||
Email: cmd.Email,
|
||||
Login: cmd.Login,
|
||||
Password: cmd.Password,
|
||||
Salt: cmd.Salt,
|
||||
Created: 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{}
|
@ -1,4 +1,4 @@
|
||||
package utils
|
||||
package util
|
||||
|
||||
import (
|
||||
"net/url"
|
Loading…
Reference in New Issue
Block a user