From 94a54248c1892433a62898949538816dcb16b49a Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 19 Jan 2018 17:41:09 +0100 Subject: [PATCH] dashfolders: show folders use can save to in picker Instead of returning all folders a user has some sort of access to, this change creates a new end point that returns folders the user has write access to. This new endpoint is used in the folder picker --- pkg/api/api.go | 2 + pkg/api/dashboard.go | 16 ++++ pkg/models/dashboards.go | 12 +++ pkg/services/sqlstore/dashboard.go | 46 +++++++++++ pkg/services/sqlstore/dashboard_test.go | 76 +++++++++++++++++++ .../dashboard/folder_picker/folder_picker.ts | 7 +- 6 files changed, 153 insertions(+), 6 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 563e0c224f3..086d7345483 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -252,6 +252,8 @@ func (hs *HttpServer) registerRoutes() { dashboardRoute.Get("/tags", GetDashboardTags) dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard)) + dashboardRoute.Get("/folders", wrap(GetFoldersForSignedInUser)) + dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute RouteRegister) { dashIdRoute.Get("/versions", wrap(GetDashboardVersions)) dashIdRoute.Get("/versions/:id", wrap(GetDashboardVersion)) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index a72fc0fafbc..210b6fd1d55 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -438,3 +438,19 @@ func GetDashboardTags(c *middleware.Context) { 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) +} diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 091f27ec413..dccd93707a5 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -209,3 +209,15 @@ type GetDashboardSlugByIdQuery struct { Id int64 Result string } + +type GetFoldersForSignedInUserQuery struct { + OrgId int64 + SignedInUser *SignedInUser + Title string + Result []*DashboardFolder +} + +type DashboardFolder struct { + Id int64 `json:"id"` + Title string `json:"title"` +} diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 0b6b60a5e11..f4cdab22e89 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -18,6 +18,7 @@ func init() { bus.AddHandler("sql", GetDashboardTags) bus.AddHandler("sql", GetDashboardSlugById) bus.AddHandler("sql", GetDashboardsByPluginId) + bus.AddHandler("sql", GetFoldersForSignedInUser) } func SaveDashboard(cmd *m.SaveDashboardCommand) error { @@ -291,6 +292,51 @@ func GetDashboardTags(query *m.GetDashboardTagsQuery) error { 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 = ? + ORDER BY d.title ASC` + + err = x.Sql(sql, dialect.BooleanStr(true)).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 = ?` + params = append(params, query.SignedInUser.UserId) + params = append(params, query.SignedInUser.UserId) + + sql += `WHERE + d.org_id = ? AND + d.is_folder = 1 AND + ( + (d.has_acl = 1 AND da.permission > 1 AND (da.user_id = ? OR ugm.user_id = ? OR ou.id IS NOT NULL)) + OR (d.has_acl = 0 AND ouRole.id IS NOT NULL) + )` + params = append(params, query.OrgId) + params = append(params, query.SignedInUser.UserId) + params = append(params, query.SignedInUser.UserId) + + 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 { return inTransaction(func(sess *DBSession) error { dashboard := m.Dashboard{Id: cmd.Id, OrgId: cmd.OrgId} diff --git a/pkg/services/sqlstore/dashboard_test.go b/pkg/services/sqlstore/dashboard_test.go index a552bd0546a..3b1e05d3772 100644 --- a/pkg/services/sqlstore/dashboard_test.go +++ b/pkg/services/sqlstore/dashboard_test.go @@ -459,6 +459,82 @@ func TestDashboardDataAccess(t *testing.T) { }) }) + Convey("Given two dashboard folders", func() { + + folder1 := insertTestDashboard("1 test dash folder", 1, 0, true, "prod") + folder2 := insertTestDashboard("2 test dash folder", 1, 0, true, "prod") + + adminUser := createUser("admin", "Admin", true) + editorUser := createUser("editor", "Editor", false) + viewerUser := createUser("viewer", "Viewer", false) + + Convey("Admin users", func() { + Convey("Should have write access to all dashboard folders", func() { + query := m.GetFoldersForSignedInUserQuery{ + OrgId: 1, + SignedInUser: &m.SignedInUser{UserId: adminUser.Id, OrgRole: m.ROLE_ADMIN}, + } + + err := GetFoldersForSignedInUser(&query) + So(err, ShouldBeNil) + + So(len(query.Result), ShouldEqual, 2) + So(query.Result[0].Id, ShouldEqual, folder1.Id) + So(query.Result[1].Id, ShouldEqual, folder2.Id) + }) + }) + + Convey("Editor users", func() { + query := m.GetFoldersForSignedInUserQuery{ + OrgId: 1, + SignedInUser: &m.SignedInUser{UserId: editorUser.Id, OrgRole: m.ROLE_EDITOR}, + } + + Convey("Should have write access to all dashboard folders with default ACL", func() { + err := GetFoldersForSignedInUser(&query) + So(err, ShouldBeNil) + + So(len(query.Result), ShouldEqual, 2) + So(query.Result[0].Id, ShouldEqual, folder1.Id) + So(query.Result[1].Id, ShouldEqual, folder2.Id) + }) + + 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) + + err := GetFoldersForSignedInUser(&query) + So(err, ShouldBeNil) + + So(len(query.Result), ShouldEqual, 1) + So(query.Result[0].Id, ShouldEqual, folder2.Id) + }) + }) + + Convey("Viewer users", func() { + query := m.GetFoldersForSignedInUserQuery{ + OrgId: 1, + SignedInUser: &m.SignedInUser{UserId: viewerUser.Id, OrgRole: m.ROLE_VIEWER}, + } + + Convey("Should have no write access to any dashboard folders with default ACL", func() { + err := GetFoldersForSignedInUser(&query) + So(err, ShouldBeNil) + + So(len(query.Result), ShouldEqual, 0) + }) + + 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) + + err := GetFoldersForSignedInUser(&query) + So(err, ShouldBeNil) + + So(len(query.Result), ShouldEqual, 1) + So(query.Result[0].Id, ShouldEqual, folder1.Id) + }) + }) + }) + Convey("Given a plugin with imported dashboards", func() { pluginId := "test-app" diff --git a/public/app/features/dashboard/folder_picker/folder_picker.ts b/public/app/features/dashboard/folder_picker/folder_picker.ts index 9fb9f8ef379..05356c139cf 100644 --- a/public/app/features/dashboard/folder_picker/folder_picker.ts +++ b/public/app/features/dashboard/folder_picker/folder_picker.ts @@ -30,12 +30,7 @@ export class FolderPickerCtrl { } getOptions(query) { - var params = { - query: query, - type: 'dash-folder', - }; - - return this.backendSrv.search(params).then(result => { + return this.backendSrv.get('api/dashboards/folders', { query: query }).then(result => { if ( query === '' || query.toLowerCase() === 'r' ||