Commit 3226a3a5 by Torkel Ödegaard

Fixed hashing of passwords, Closes #3

parent 6d814af0
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
}
......
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{
......
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(),
}
......
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]
}
package utils
package util
type DynMap map[string]interface{}
package utils
package util
import (
"net/url"
......
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