Big refactoring for context.User, and how current user info is fetching, now included collaborator role

This commit is contained in:
Torkel Ödegaard
2015-01-16 14:32:18 +01:00
parent 52992928d5
commit 22156fe309
17 changed files with 169 additions and 105 deletions

Submodule grafana updated: aaa717aac2...9637efd5ee

View File

@@ -7,7 +7,7 @@ import (
)
func GetAccount(c *middleware.Context) {
query := m.GetAccountInfoQuery{Id: c.UserAccount.Id}
query := m.GetAccountInfoQuery{Id: c.AccountId}
err := bus.Dispatch(&query)
if err != nil {
@@ -26,7 +26,7 @@ func UpdateAccount(c *middleware.Context) {
return
}
cmd.AccountId = c.UserAccount.Id
cmd.AccountId = c.AccountId
if err := bus.Dispatch(&cmd); err != nil {
c.JsonApiErr(400, "Failed to update account", nil)
@@ -37,7 +37,7 @@ func UpdateAccount(c *middleware.Context) {
}
func GetOtherAccounts(c *middleware.Context) {
query := m.GetOtherAccountsQuery{AccountId: c.UserAccount.Id}
query := m.GetOtherAccountsQuery{AccountId: c.AccountId}
err := bus.Dispatch(&query)
if err != nil {
@@ -46,13 +46,13 @@ func GetOtherAccounts(c *middleware.Context) {
}
result := append(query.Result, &m.OtherAccountDTO{
AccountId: c.UserAccount.Id,
Role: "owner",
Email: c.UserAccount.Email,
AccountId: c.AccountId,
Role: m.ROLE_OWNER,
Email: c.UserEmail,
})
for _, ac := range result {
if ac.AccountId == c.UserAccount.UsingAccountId {
if ac.AccountId == c.UsingAccountId {
ac.IsUsing = true
break
}
@@ -85,13 +85,13 @@ func validateUsingAccount(accountId int64, otherId int64) bool {
func SetUsingAccount(c *middleware.Context) {
usingAccountId := c.ParamsInt64(":id")
if !validateUsingAccount(c.UserAccount.Id, usingAccountId) {
if !validateUsingAccount(c.AccountId, usingAccountId) {
c.JsonApiErr(401, "Not a valid account", nil)
return
}
cmd := m.SetUsingAccountCommand{
AccountId: c.UserAccount.Id,
AccountId: c.AccountId,
UsingAccountId: usingAccountId,
}

View File

@@ -12,7 +12,7 @@ import (
// Register adds http routes
func Register(r *macaron.Macaron) {
reqSignedIn := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true})
reqAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqAdmin: true})
reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
bind := binding.Bind
// not logged in views
@@ -63,7 +63,7 @@ func Register(r *macaron.Macaron) {
// Dashboard
r.Group("/dashboard", func() {
r.Combo("/:slug").Get(GetDashboard).Delete(DeleteDashboard)
r.Post("/", PostDashboard)
r.Post("/", bind(m.SaveDashboardCommand{}), PostDashboard)
})
// Search
r.Get("/search/", Search)
@@ -74,7 +74,7 @@ func Register(r *macaron.Macaron) {
// admin api
r.Group("/api/admin", func() {
r.Get("/accounts", AdminSearchAccounts)
}, reqAdmin)
}, reqGrafanaAdmin)
// rendering
r.Get("/render/*", reqSignedIn, RenderToPng)
@@ -88,7 +88,21 @@ func setIndexViewData(c *middleware.Context) error {
return err
}
c.Data["User"] = dtos.NewCurrentUser(c.UserAccount)
currentUser := &dtos.CurrentUser{}
if c.IsSignedIn {
currentUser = &dtos.CurrentUser{
Login: c.UserLogin,
Email: c.UserEmail,
Name: c.UserName,
UsingAccountName: c.UsingAccountName,
GravatarUrl: dtos.GetGravatarUrl(c.UserEmail),
IsGrafanaAdmin: c.IsGrafanaAdmin,
Role: c.UserRole,
}
}
c.Data["User"] = currentUser
c.Data["Settings"] = settings
c.Data["AppUrl"] = setting.AppUrl
c.Data["AppSubUrl"] = setting.AppSubUrl

View File

@@ -21,12 +21,12 @@ func AddCollaborator(c *middleware.Context, cmd m.AddCollaboratorCommand) {
accountToAdd := userQuery.Result
if accountToAdd.Id == c.UserAccount.Id {
if accountToAdd.Id == c.AccountId {
c.JsonApiErr(400, "Cannot add yourself as collaborator", nil)
return
}
cmd.AccountId = c.UserAccount.Id
cmd.AccountId = c.AccountId
cmd.CollaboratorId = accountToAdd.Id
err = bus.Dispatch(&cmd)
@@ -39,7 +39,7 @@ func AddCollaborator(c *middleware.Context, cmd m.AddCollaboratorCommand) {
}
func GetCollaborators(c *middleware.Context) {
query := m.GetCollaboratorsQuery{AccountId: c.UserAccount.Id}
query := m.GetCollaboratorsQuery{AccountId: c.AccountId}
if err := bus.Dispatch(&query); err != nil {
c.JsonApiErr(500, "Failed to get collaborators", err)
return
@@ -51,7 +51,7 @@ func GetCollaborators(c *middleware.Context) {
func RemoveCollaborator(c *middleware.Context) {
collaboratorId := c.ParamsInt64(":id")
cmd := m.RemoveCollaboratorCommand{AccountId: c.UserAccount.Id, CollaboratorId: collaboratorId}
cmd := m.RemoveCollaboratorCommand{AccountId: c.AccountId, CollaboratorId: collaboratorId}
if err := bus.Dispatch(&cmd); err != nil {
c.JsonApiErr(500, "Failed to remove collaborator", err)

View File

@@ -10,7 +10,7 @@ import (
func GetDashboard(c *middleware.Context) {
slug := c.Params(":slug")
query := m.GetDashboardQuery{Slug: slug, AccountId: c.GetAccountId()}
query := m.GetDashboardQuery{Slug: slug, AccountId: c.UsingAccountId}
err := bus.Dispatch(&query)
if err != nil {
c.JsonApiErr(404, "Dashboard not found", nil)
@@ -25,13 +25,13 @@ func GetDashboard(c *middleware.Context) {
func DeleteDashboard(c *middleware.Context) {
slug := c.Params(":slug")
query := m.GetDashboardQuery{Slug: slug, AccountId: c.GetAccountId()}
query := m.GetDashboardQuery{Slug: slug, AccountId: c.UsingAccountId}
if err := bus.Dispatch(&query); err != nil {
c.JsonApiErr(404, "Dashboard not found", nil)
return
}
cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.GetAccountId()}
cmd := m.DeleteDashboardCommand{Slug: slug, AccountId: c.UsingAccountId}
if err := bus.Dispatch(&cmd); err != nil {
c.JsonApiErr(500, "Failed to delete dashboard", err)
return
@@ -42,15 +42,8 @@ func DeleteDashboard(c *middleware.Context) {
c.JSON(200, resp)
}
func PostDashboard(c *middleware.Context) {
var cmd m.SaveDashboardCommand
if !c.JsonBody(&cmd) {
c.JsonApiErr(400, "bad request", nil)
return
}
cmd.AccountId = c.GetAccountId()
func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) {
cmd.AccountId = c.UsingAccountId
err := bus.Dispatch(&cmd)
if err != nil {

View File

@@ -39,7 +39,7 @@ func ProxyDataSourceRequest(c *middleware.Context) {
query := m.GetDataSourceByIdQuery{
Id: id,
AccountId: c.GetAccountId(),
AccountId: c.UsingAccountId,
}
err := bus.Dispatch(&query)

View File

@@ -8,7 +8,7 @@ import (
)
func GetDataSources(c *middleware.Context) {
query := m.GetDataSourcesQuery{AccountId: c.Account.Id}
query := m.GetDataSourcesQuery{AccountId: c.UsingAccountId}
err := bus.Dispatch(&query)
if err != nil {
@@ -44,7 +44,7 @@ func DeleteDataSource(c *middleware.Context) {
return
}
cmd := &m.DeleteDataSourceCommand{Id: id, AccountId: c.UserAccount.Id}
cmd := &m.DeleteDataSourceCommand{Id: id, AccountId: c.UsingAccountId}
err := bus.Dispatch(cmd)
if err != nil {
@@ -63,7 +63,7 @@ func AddDataSource(c *middleware.Context) {
return
}
cmd.AccountId = c.Account.Id
cmd.AccountId = c.UsingAccountId
if err := bus.Dispatch(&cmd); err != nil {
c.JsonApiErr(500, "Failed to add datasource", err)
@@ -81,7 +81,7 @@ func UpdateDataSource(c *middleware.Context) {
return
}
cmd.AccountId = c.Account.Id
cmd.AccountId = c.UsingAccountId
err := bus.Dispatch(&cmd)
if err != nil {

View File

@@ -5,7 +5,7 @@ import (
"fmt"
"strings"
"github.com/torkelo/grafana-pro/pkg/models"
m "github.com/torkelo/grafana-pro/pkg/models"
)
type LoginResult struct {
@@ -14,24 +14,27 @@ type LoginResult struct {
}
type CurrentUser struct {
Login string `json:"login"`
Email string `json:"email"`
IsAdmin bool `json:"isAdmin"`
GravatarUrl string `json:"gravatarUrl"`
Login string `json:"login"`
Email string `json:"email"`
Role m.RoleType `json:"role"`
Name string `json:"name"`
UsingAccountName string `json:"usingAccountName"`
IsGrafanaAdmin bool `json:"isGrafanaAdmin"`
GravatarUrl string `json:"gravatarUrl"`
}
type DataSource struct {
Id int64 `json:"id"`
AccountId int64 `json:"accountId"`
Name string `json:"name"`
Type models.DsType `json:"type"`
Access models.DsAccess `json:"access"`
Url string `json:"url"`
Password string `json:"password"`
User string `json:"user"`
Database string `json:"database"`
BasicAuth bool `json:"basicAuth"`
IsDefault bool `json:"isDefault"`
Id int64 `json:"id"`
AccountId int64 `json:"accountId"`
Name string `json:"name"`
Type m.DsType `json:"type"`
Access m.DsAccess `json:"access"`
Url string `json:"url"`
Password string `json:"password"`
User string `json:"user"`
Database string `json:"database"`
BasicAuth bool `json:"basicAuth"`
IsDefault bool `json:"isDefault"`
}
type MetricQueryResultDto struct {
@@ -43,18 +46,11 @@ type MetricQueryResultDataDto struct {
DataPoints [][2]float64 `json:"datapoints"`
}
func NewCurrentUser(account *models.Account) *CurrentUser {
model := &CurrentUser{}
if account != nil {
model.Login = account.Login
model.Email = account.Email
model.GravatarUrl = getGravatarUrl(account.Email)
model.IsAdmin = account.IsAdmin
func GetGravatarUrl(text string) string {
if text == "" {
return ""
}
return model
}
func getGravatarUrl(text string) string {
hasher := md5.New()
hasher.Write([]byte(strings.ToLower(text)))
return fmt.Sprintf("https://secure.gravatar.com/avatar/%x?s=90&default=mm", hasher.Sum(nil))

View File

@@ -12,8 +12,8 @@ import (
func getFrontendSettings(c *middleware.Context) (map[string]interface{}, error) {
accountDataSources := make([]*m.DataSource, 0)
if c.Account != nil {
query := m.GetDataSourcesQuery{AccountId: c.Account.Id}
if c.IsSignedIn {
query := m.GetDataSourcesQuery{AccountId: c.UsingAccountId}
err := bus.Dispatch(&query)
if err != nil {

View File

@@ -10,7 +10,7 @@ import (
)
func RenderToPng(c *middleware.Context) {
accountId := c.GetAccountId()
accountId := c.UsingAccountId
queryReader := util.NewUrlQueryReader(c.Req.URL)
queryParams := "?render&accountId=" + strconv.FormatInt(accountId, 10) + "&" + c.Req.URL.RawQuery

View File

@@ -31,7 +31,7 @@ func Search(c *middleware.Context) {
query := m.SearchDashboardsQuery{
Title: matches[3],
Tag: matches[2],
AccountId: c.GetAccountId(),
AccountId: c.UsingAccountId,
}
err := bus.Dispatch(&query)
if err != nil {

View File

@@ -8,7 +8,7 @@ import (
)
func GetTokens(c *middleware.Context) {
query := m.GetTokensQuery{AccountId: c.UserAccount.Id}
query := m.GetTokensQuery{AccountId: c.AccountId}
err := bus.Dispatch(&query)
if err != nil {
@@ -30,7 +30,7 @@ func GetTokens(c *middleware.Context) {
func DeleteToken(c *middleware.Context) {
id := c.ParamsInt64(":id")
cmd := &m.DeleteTokenCommand{Id: id, AccountId: c.UserAccount.Id}
cmd := &m.DeleteTokenCommand{Id: id, AccountId: c.AccountId}
err := bus.Dispatch(cmd)
if err != nil {
@@ -47,7 +47,7 @@ func AddToken(c *middleware.Context, cmd m.AddTokenCommand) {
return
}
cmd.AccountId = c.UserAccount.Id
cmd.AccountId = c.AccountId
cmd.Token = util.GetRandomString(64)
if err := bus.Dispatch(&cmd); err != nil {
@@ -71,7 +71,7 @@ func UpdateToken(c *middleware.Context, cmd m.UpdateTokenCommand) {
return
}
cmd.AccountId = c.UserAccount.Id
cmd.AccountId = c.AccountId
err := bus.Dispatch(&cmd)
if err != nil {

View File

@@ -13,8 +13,8 @@ import (
)
type AuthOptions struct {
ReqAdmin bool
ReqSignedIn bool
ReqGrafanaAdmin bool
ReqSignedIn bool
}
func getRequestAccountId(c *Context) (int64, error) {
@@ -68,7 +68,7 @@ func Auth(options *AuthOptions) macaron.Handler {
return
}
if !c.IsAdmin && options.ReqAdmin {
if !c.IsGrafanaAdmin && options.ReqGrafanaAdmin {
authDenied(c)
return
}

View File

@@ -16,17 +16,11 @@ import (
type Context struct {
*macaron.Context
*m.SignInUser
Session session.Store
IsSignedIn bool
IsAdmin bool
Account *m.Account
UserAccount *m.Account
}
func (c *Context) GetAccountId() int64 {
return c.Account.Id
}
func GetContextHandler() macaron.Handler {
@@ -38,27 +32,15 @@ func GetContextHandler() macaron.Handler {
// try get account id from request
if accountId, err := getRequestAccountId(ctx); err == nil {
// fetch user
userQuery := m.GetAccountByIdQuery{Id: accountId}
if err := bus.Dispatch(&userQuery); err != nil {
query := m.GetSignedInUserQuery{AccountId: accountId}
if err := bus.Dispatch(&query); err != nil {
log.Error(3, "Failed to get user by id, %v, %v", accountId, err)
} else {
// fetch using account
ctx.UserAccount = userQuery.Result
usingQuery := m.GetAccountByIdQuery{Id: ctx.UserAccount.UsingAccountId}
if err := bus.Dispatch(&usingQuery); err != nil {
log.Error(3, "Faild to get account's using account, account: %v, usingAccountId: %v, err:%v", accountId, ctx.UserAccount.Id, err)
} else {
ctx.Account = usingQuery.Result
}
ctx.IsSignedIn = true
ctx.SignInUser = query.Result
}
}
if ctx.Account != nil {
ctx.IsSignedIn = true
ctx.IsAdmin = ctx.Account.IsAdmin
}
c.Map(ctx)
}
}

View File

@@ -85,14 +85,29 @@ type SearchAccountsQuery struct {
Result []*AccountSearchHitDTO
}
type GetSignedInUserQuery struct {
AccountId int64
Result *SignInUser
}
// ------------------------
// DTO & Projections
type SignInUser struct {
AccountId int64
UsingAccountId int64
UsingAccountName string
UserRole RoleType
UserLogin string
UserName string
UserEmail string
IsGrafanaAdmin bool
}
type OtherAccountDTO struct {
AccountId int64 `json:"accountId"`
Email string `json:"email"`
Role string `json:"role"`
IsUsing bool `json:"isUsing"`
AccountId int64 `json:"accountId"`
Email string `json:"email"`
Role RoleType `json:"role"`
IsUsing bool `json:"isUsing"`
}
type AccountSearchHitDTO struct {

View File

@@ -20,6 +20,7 @@ func init() {
bus.AddHandler("sql", GetAccountByToken)
bus.AddHandler("sql", SearchAccounts)
bus.AddHandler("sql", UpdateAccount)
bus.AddHandler("sql", GetSignedInUser)
}
func CreateAccount(cmd *m.CreateAccountCommand) error {
@@ -27,6 +28,7 @@ func CreateAccount(cmd *m.CreateAccountCommand) error {
account := m.Account{
Email: cmd.Email,
Name: cmd.Name,
Login: cmd.Login,
Password: cmd.Password,
Salt: cmd.Salt,
@@ -180,5 +182,38 @@ func SearchAccounts(query *m.SearchAccountsQuery) error {
sess.Cols("id", "email", "name", "login", "is_admin")
err := sess.Find(&query.Result)
return err
}
func GetSignedInUser(query *m.GetSignedInUserQuery) error {
var rawSql = `SELECT
userAccount.id as account_id,
userAccount.is_admin as is_grafana_admin,
userAccount.email as user_email,
userAccount.name as user_name,
userAccount.login as user_login,
usingAccount.id as using_account_id,
usingAccount.name as using_account_name,
collaborator.role as user_role
FROM account as userAccount
LEFT OUTER JOIN account as usingAccount on usingAccount.id = userAccount.using_account_id
LEFT OUTER JOIN collaborator on collaborator.account_id = usingAccount.id AND collaborator.collaborator_id = userAccount.id
WHERE userAccount.id=?`
var user m.SignInUser
sess := x.Table("account")
has, err := sess.Sql(rawSql, query.AccountId).Get(&user)
if err != nil {
return err
} else if !has {
return m.ErrAccountNotFound
}
if user.UsingAccountId == 0 || user.UsingAccountId == user.AccountId {
user.UsingAccountId = query.AccountId
user.UsingAccountName = user.UserName
user.UserRole = m.ROLE_OWNER
}
query.Result = &user
return err
}

View File

@@ -14,8 +14,8 @@ func TestAccountDataAccess(t *testing.T) {
InitTestDB(t)
Convey("Given two saved accounts", func() {
ac1cmd := m.CreateAccountCommand{Login: "ac1", Email: "ac1@test.com"}
ac2cmd := m.CreateAccountCommand{Login: "ac2", Email: "ac2@test.com"}
ac1cmd := m.CreateAccountCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
ac2cmd := m.CreateAccountCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true}
err := CreateAccount(&ac1cmd)
err = CreateAccount(&ac2cmd)
@@ -42,7 +42,7 @@ func TestAccountDataAccess(t *testing.T) {
So(query.Result[1].Email, ShouldEqual, "ac2@test.com")
})
Convey("Can add collaborator", func() {
Convey("Given an added collaborator", func() {
cmd := m.AddCollaboratorCommand{
AccountId: ac1.Id,
CollaboratorId: ac2.Id,
@@ -50,10 +50,25 @@ func TestAccountDataAccess(t *testing.T) {
}
err := AddCollaborator(&cmd)
Convey("Saved without error", func() {
Convey("Should have been saved without error", func() {
So(err, ShouldBeNil)
})
Convey("Can get logged in user projection", func() {
query := m.GetSignedInUserQuery{AccountId: ac2.Id}
err := GetSignedInUser(&query)
So(err, ShouldBeNil)
So(query.Result.AccountId, ShouldEqual, ac2.Id)
So(query.Result.UserEmail, ShouldEqual, "ac2@test.com")
So(query.Result.UserName, ShouldEqual, "ac2 name")
So(query.Result.UserLogin, ShouldEqual, "ac2")
So(query.Result.UserRole, ShouldEqual, "Owner")
So(query.Result.UsingAccountName, ShouldEqual, "ac2 name")
So(query.Result.UsingAccountId, ShouldEqual, ac2.Id)
So(query.Result.IsGrafanaAdmin, ShouldBeTrue)
})
Convey("Can get other accounts", func() {
query := m.GetOtherAccountsQuery{AccountId: ac2.Id}
err := GetOtherAccounts(&query)
@@ -66,6 +81,20 @@ func TestAccountDataAccess(t *testing.T) {
cmd := m.SetUsingAccountCommand{AccountId: ac2.Id, UsingAccountId: ac1.Id}
err := SetUsingAccount(&cmd)
So(err, ShouldBeNil)
Convey("Logged in user query should return correct using account info", func() {
query := m.GetSignedInUserQuery{AccountId: ac2.Id}
err := GetSignedInUser(&query)
So(err, ShouldBeNil)
So(query.Result.AccountId, ShouldEqual, ac2.Id)
So(query.Result.UserEmail, ShouldEqual, "ac2@test.com")
So(query.Result.UserName, ShouldEqual, "ac2 name")
So(query.Result.UserLogin, ShouldEqual, "ac2")
So(query.Result.UsingAccountName, ShouldEqual, "ac1 name")
So(query.Result.UsingAccountId, ShouldEqual, ac1.Id)
So(query.Result.UserRole, ShouldEqual, "Viewer")
})
})
})
})