Commit 8e8f3c43 by Torkel Ödegaard

dashboard and folder search with permissions

parent b84fd3a7
...@@ -261,8 +261,6 @@ func (hs *HttpServer) registerRoutes() { ...@@ -261,8 +261,6 @@ func (hs *HttpServer) registerRoutes() {
dashboardRoute.Get("/tags", GetDashboardTags) dashboardRoute.Get("/tags", GetDashboardTags)
dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard)) dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard))
dashboardRoute.Get("/folders", wrap(GetFoldersForSignedInUser))
dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute RouteRegister) { dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute RouteRegister) {
dashIdRoute.Get("/versions", wrap(GetDashboardVersions)) dashIdRoute.Get("/versions", wrap(GetDashboardVersions))
dashIdRoute.Get("/versions/:id", wrap(GetDashboardVersion)) dashIdRoute.Get("/versions/:id", wrap(GetDashboardVersion))
......
...@@ -490,19 +490,3 @@ func GetDashboardTags(c *middleware.Context) { ...@@ -490,19 +490,3 @@ func GetDashboardTags(c *middleware.Context) {
c.JSON(200, query.Result) c.JSON(200, query.Result)
} }
func GetFoldersForSignedInUser(c *middleware.Context) Response {
title := c.Query("query")
query := m.GetFoldersForSignedInUserQuery{
OrgId: c.OrgId,
SignedInUser: c.SignedInUser,
Title: title,
}
err := bus.Dispatch(&query)
if err != nil {
return ApiError(500, "Failed to get folders from database", err)
}
return Json(200, query.Result)
}
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search" "github.com/grafana/grafana/pkg/services/search"
) )
...@@ -15,11 +16,16 @@ func Search(c *middleware.Context) { ...@@ -15,11 +16,16 @@ func Search(c *middleware.Context) {
starred := c.Query("starred") starred := c.Query("starred")
limit := c.QueryInt("limit") limit := c.QueryInt("limit")
dashboardType := c.Query("type") dashboardType := c.Query("type")
permission := models.PERMISSION_VIEW
if limit == 0 { if limit == 0 {
limit = 1000 limit = 1000
} }
if c.Query("permission") == "Edit" {
permission = models.PERMISSION_EDIT
}
dbids := make([]int64, 0) dbids := make([]int64, 0)
for _, id := range c.QueryStrings("dashboardIds") { for _, id := range c.QueryStrings("dashboardIds") {
dashboardId, err := strconv.ParseInt(id, 10, 64) dashboardId, err := strconv.ParseInt(id, 10, 64)
...@@ -46,6 +52,7 @@ func Search(c *middleware.Context) { ...@@ -46,6 +52,7 @@ func Search(c *middleware.Context) {
DashboardIds: dbids, DashboardIds: dbids,
Type: dashboardType, Type: dashboardType,
FolderIds: folderIds, FolderIds: folderIds,
Permission: permission,
} }
err := bus.Dispatch(&searchQuery) err := bus.Dispatch(&searchQuery)
......
...@@ -270,18 +270,6 @@ type GetDashboardsBySlugQuery struct { ...@@ -270,18 +270,6 @@ type GetDashboardsBySlugQuery struct {
Result []*Dashboard Result []*Dashboard
} }
type GetFoldersForSignedInUserQuery struct {
OrgId int64
SignedInUser *SignedInUser
Title string
Result []*DashboardFolder
}
type DashboardFolder struct {
Id int64 `json:"id"`
Title string `json:"title"`
}
type DashboardPermissionForUser struct { type DashboardPermissionForUser struct {
DashboardId int64 `json:"dashboardId"` DashboardId int64 `json:"dashboardId"`
Permission PermissionType `json:"permission"` Permission PermissionType `json:"permission"`
......
...@@ -21,6 +21,7 @@ func searchHandler(query *Query) error { ...@@ -21,6 +21,7 @@ func searchHandler(query *Query) error {
FolderIds: query.FolderIds, FolderIds: query.FolderIds,
Tags: query.Tags, Tags: query.Tags,
Limit: query.Limit, Limit: query.Limit,
Permission: query.Permission,
} }
if err := bus.Dispatch(&dashQuery); err != nil { if err := bus.Dispatch(&dashQuery); err != nil {
......
...@@ -52,6 +52,7 @@ type Query struct { ...@@ -52,6 +52,7 @@ type Query struct {
Type string Type string
DashboardIds []int64 DashboardIds []int64
FolderIds []int64 FolderIds []int64
Permission models.PermissionType
Result HitList Result HitList
} }
...@@ -66,7 +67,7 @@ type FindPersistedDashboardsQuery struct { ...@@ -66,7 +67,7 @@ type FindPersistedDashboardsQuery struct {
FolderIds []int64 FolderIds []int64
Tags []string Tags []string
Limit int Limit int
IsBrowse bool Permission models.PermissionType
Result HitList Result HitList
} }
package sqlstore package sqlstore
import ( import (
"fmt"
"strings" "strings"
"time" "time"
...@@ -21,7 +22,6 @@ func init() { ...@@ -21,7 +22,6 @@ func init() {
bus.AddHandler("sql", GetDashboardSlugById) bus.AddHandler("sql", GetDashboardSlugById)
bus.AddHandler("sql", GetDashboardUIDById) bus.AddHandler("sql", GetDashboardUIDById)
bus.AddHandler("sql", GetDashboardsByPluginId) bus.AddHandler("sql", GetDashboardsByPluginId)
bus.AddHandler("sql", GetFoldersForSignedInUser)
bus.AddHandler("sql", GetDashboardPermissionsForUser) bus.AddHandler("sql", GetDashboardPermissionsForUser)
bus.AddHandler("sql", GetDashboardsBySlug) bus.AddHandler("sql", GetDashboardsBySlug)
} }
...@@ -256,7 +256,7 @@ func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSear ...@@ -256,7 +256,7 @@ func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSear
limit = 1000 limit = 1000
} }
sb := NewSearchBuilder(query.SignedInUser, limit). sb := NewSearchBuilder(query.SignedInUser, limit, query.Permission).
WithTags(query.Tags). WithTags(query.Tags).
WithDashboardIdsIn(query.DashboardIds) WithDashboardIdsIn(query.DashboardIds)
...@@ -279,6 +279,7 @@ func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSear ...@@ -279,6 +279,7 @@ func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSear
var res []DashboardSearchProjection var res []DashboardSearchProjection
sql, params := sb.ToSql() sql, params := sb.ToSql()
fmt.Printf("%s, %v", sql, params)
sqlog.Info("sql", "sql", sql, "params", params) sqlog.Info("sql", "sql", sql, "params", params)
err := x.Sql(sql, params...).Find(&res) err := x.Sql(sql, params...).Find(&res)
if err != nil { if err != nil {
...@@ -358,54 +359,6 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error { ...@@ -358,54 +359,6 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
return err return err
} }
func GetFoldersForSignedInUser(query *m.GetFoldersForSignedInUserQuery) error {
query.Result = make([]*m.DashboardFolder, 0)
var err error
if query.SignedInUser.OrgRole == m.ROLE_ADMIN {
sql := `SELECT distinct d.id, d.title
FROM dashboard AS d WHERE d.is_folder = ? AND d.org_id = ?
ORDER BY d.title ASC`
err = x.Sql(sql, dialect.BooleanStr(true), query.OrgId).Find(&query.Result)
} else {
params := make([]interface{}, 0)
sql := `SELECT distinct d.id, d.title
FROM dashboard AS d
LEFT JOIN dashboard_acl AS da ON d.id = da.dashboard_id
LEFT JOIN team_member AS ugm ON ugm.team_id = da.team_id
LEFT JOIN org_user ou ON ou.role = da.role AND ou.user_id = ?
LEFT JOIN org_user ouRole ON ouRole.role = 'Editor' AND ouRole.user_id = ? AND ouRole.org_id = ?`
params = append(params, query.SignedInUser.UserId)
params = append(params, query.SignedInUser.UserId)
params = append(params, query.OrgId)
sql += ` WHERE
d.org_id = ? AND
d.is_folder = ? AND
(
(d.has_acl = ? AND da.permission > 1 AND (da.user_id = ? OR ugm.user_id = ? OR ou.id IS NOT NULL))
OR (d.has_acl = ? AND ouRole.id IS NOT NULL)
)`
params = append(params, query.OrgId)
params = append(params, dialect.BooleanStr(true))
params = append(params, dialect.BooleanStr(true))
params = append(params, query.SignedInUser.UserId)
params = append(params, query.SignedInUser.UserId)
params = append(params, dialect.BooleanStr(false))
if len(query.Title) > 0 {
sql += " AND d.title " + dialect.LikeStr() + " ?"
params = append(params, "%"+query.Title+"%")
}
sql += ` ORDER BY d.title ASC`
err = x.Sql(sql, params...).Find(&query.Result)
}
return err
}
func DeleteDashboard(cmd *m.DeleteDashboardCommand) error { func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
dashboard := m.Dashboard{Id: cmd.Id, OrgId: cmd.OrgId} dashboard := m.Dashboard{Id: cmd.Id, OrgId: cmd.OrgId}
......
...@@ -227,12 +227,14 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -227,12 +227,14 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("Admin users", func() { Convey("Admin users", func() {
Convey("Should have write access to all dashboard folders in their org", func() { Convey("Should have write access to all dashboard folders in their org", func() {
query := m.GetFoldersForSignedInUserQuery{ query := search.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
SignedInUser: &m.SignedInUser{UserId: adminUser.Id, OrgRole: m.ROLE_ADMIN}, SignedInUser: &m.SignedInUser{UserId: adminUser.Id, OrgRole: m.ROLE_ADMIN, OrgId: 1},
Permission: m.PERMISSION_VIEW,
Type: "dash-folder",
} }
err := GetFoldersForSignedInUser(&query) err := SearchDashboards(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2) So(len(query.Result), ShouldEqual, 2)
...@@ -260,13 +262,14 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -260,13 +262,14 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("Editor users", func() { Convey("Editor users", func() {
query := m.GetFoldersForSignedInUserQuery{ query := search.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
SignedInUser: &m.SignedInUser{UserId: editorUser.Id, OrgRole: m.ROLE_EDITOR}, SignedInUser: &m.SignedInUser{UserId: editorUser.Id, OrgRole: m.ROLE_EDITOR, OrgId: 1},
Permission: m.PERMISSION_EDIT,
} }
Convey("Should have write access to all dashboard folders with default ACL", func() { Convey("Should have write access to all dashboard folders with default ACL", func() {
err := GetFoldersForSignedInUser(&query) err := SearchDashboards(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2) So(len(query.Result), ShouldEqual, 2)
...@@ -295,7 +298,7 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -295,7 +298,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("Should have write access to one dashboard folder if default role changed to view for one folder", func() { Convey("Should have write access to one dashboard folder if default role changed to view for one folder", func() {
updateTestDashboardWithAcl(folder1.Id, editorUser.Id, m.PERMISSION_VIEW) updateTestDashboardWithAcl(folder1.Id, editorUser.Id, m.PERMISSION_VIEW)
err := GetFoldersForSignedInUser(&query) err := SearchDashboards(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1) So(len(query.Result), ShouldEqual, 1)
...@@ -305,13 +308,14 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -305,13 +308,14 @@ func TestDashboardFolderDataAccess(t *testing.T) {
}) })
Convey("Viewer users", func() { Convey("Viewer users", func() {
query := m.GetFoldersForSignedInUserQuery{ query := search.FindPersistedDashboardsQuery{
OrgId: 1, OrgId: 1,
SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgRole: m.ROLE_VIEWER}, SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgRole: m.ROLE_VIEWER, OrgId: 1},
Permission: m.PERMISSION_EDIT,
} }
Convey("Should have no write access to any dashboard folders with default ACL", func() { Convey("Should have no write access to any dashboard folders with default ACL", func() {
err := GetFoldersForSignedInUser(&query) err := SearchDashboards(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 0) So(len(query.Result), ShouldEqual, 0)
...@@ -338,7 +342,7 @@ func TestDashboardFolderDataAccess(t *testing.T) { ...@@ -338,7 +342,7 @@ func TestDashboardFolderDataAccess(t *testing.T) {
Convey("Should be able to get one dashboard folder if default role changed to edit for one folder", func() { Convey("Should be able to get one dashboard folder if default role changed to edit for one folder", func() {
updateTestDashboardWithAcl(folder1.Id, viewerUser.Id, m.PERMISSION_EDIT) updateTestDashboardWithAcl(folder1.Id, viewerUser.Id, m.PERMISSION_EDIT)
err := GetFoldersForSignedInUser(&query) err := SearchDashboards(&query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1) So(len(query.Result), ShouldEqual, 1)
......
...@@ -18,12 +18,14 @@ type SearchBuilder struct { ...@@ -18,12 +18,14 @@ type SearchBuilder struct {
whereTypeFolder bool whereTypeFolder bool
whereTypeDash bool whereTypeDash bool
whereFolderIds []int64 whereFolderIds []int64
permission m.PermissionType
} }
func NewSearchBuilder(signedInUser *m.SignedInUser, limit int) *SearchBuilder { func NewSearchBuilder(signedInUser *m.SignedInUser, limit int, permission m.PermissionType) *SearchBuilder {
searchBuilder := &SearchBuilder{ searchBuilder := &SearchBuilder{
signedInUser: signedInUser, signedInUser: signedInUser,
limit: limit, limit: limit,
permission: permission,
} }
return searchBuilder return searchBuilder
...@@ -174,7 +176,7 @@ func (sb *SearchBuilder) buildSearchWhereClause() { ...@@ -174,7 +176,7 @@ func (sb *SearchBuilder) buildSearchWhereClause() {
} }
} }
sb.writeDashboardPermissionFilter(sb.signedInUser, m.PERMISSION_VIEW) sb.writeDashboardPermissionFilter(sb.signedInUser, sb.permission)
if len(sb.whereTitle) > 0 { if len(sb.whereTitle) > 0 {
sb.sql.WriteString(" AND dashboard.title " + dialect.LikeStr() + " ?") sb.sql.WriteString(" AND dashboard.title " + dialect.LikeStr() + " ?")
......
...@@ -16,7 +16,8 @@ func TestSearchBuilder(t *testing.T) { ...@@ -16,7 +16,8 @@ func TestSearchBuilder(t *testing.T) {
OrgId: 1, OrgId: 1,
UserId: 1, UserId: 1,
} }
sb := NewSearchBuilder(signedInUser, 1000)
sb := NewSearchBuilder(signedInUser, 1000, m.PERMISSION_VIEW)
Convey("When building a normal search", func() { Convey("When building a normal search", func() {
sql, params := sb.IsStarred().WithTitle("test").ToSql() sql, params := sb.IsStarred().WithTitle("test").ToSql()
......
...@@ -12,7 +12,7 @@ type SqlBuilder struct { ...@@ -12,7 +12,7 @@ type SqlBuilder struct {
params []interface{} params []interface{}
} }
func (sb *SqlBuilder) writeDashboardPermissionFilter(user *m.SignedInUser, minPermission m.PermissionType) { func (sb *SqlBuilder) writeDashboardPermissionFilter(user *m.SignedInUser, permission m.PermissionType) {
if user.OrgRole == m.ROLE_ADMIN { if user.OrgRole == m.ROLE_ADMIN {
return return
...@@ -40,6 +40,6 @@ func (sb *SqlBuilder) writeDashboardPermissionFilter(user *m.SignedInUser, minPe ...@@ -40,6 +40,6 @@ func (sb *SqlBuilder) writeDashboardPermissionFilter(user *m.SignedInUser, minPe
) )
)`) )`)
sb.params = append(sb.params, user.OrgId, minPermission, user.UserId, user.UserId) sb.params = append(sb.params, user.OrgId, permission, user.UserId, user.UserId)
sb.params = append(sb.params, okRoles...) sb.params = append(sb.params, okRoles...)
} }
...@@ -30,7 +30,13 @@ export class FolderPickerCtrl { ...@@ -30,7 +30,13 @@ export class FolderPickerCtrl {
} }
getOptions(query) { getOptions(query) {
return this.backendSrv.get('api/dashboards/folders', { query: query }).then(result => { const params = {
query: query,
type: 'dash-folder',
permission: 'Edit',
};
return this.backendSrv.get('api/search', params).then(result => {
if ( if (
query === '' || query === '' ||
query.toLowerCase() === 'g' || query.toLowerCase() === 'g' ||
......
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