Commit d6341162 by Torkel Ödegaard

refactoring dashboad folder acl checks

parents b494fd76 d1e1c4be
......@@ -41,7 +41,7 @@ func GetDashboard(c *middleware.Context) Response {
return rsp
}
guardian := guardian.NewDashboardGuardian(dash, c.SignedInUser)
guardian := guardian.NewDashboardGuardian(dash.Id, c.OrgId, c.SignedInUser)
if canView, err := guardian.CanView(); err != nil {
return ApiError(500, "Error while checking dashboard permissions", err)
......
......@@ -48,11 +48,6 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/1/acl", "/api/dashboards/:id/acl", models.ROLE_EDITOR, func(sc *scenarioContext) {
mockResult = append(mockResult, &models.DashboardAclInfoDTO{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permissions: models.PERMISSION_EDIT})
bus.AddHandler("test2", func(query *models.GetAllowedDashboardsQuery) error {
query.Result = []int64{1}
return nil
})
Convey("Should be able to access ACL", func() {
sc.handlerFunc = GetDashboardAcl
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
......@@ -101,11 +96,6 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
Convey("When user is editor and not in the ACL", func() {
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/1/acl", "/api/dashboards/:id/acl", models.ROLE_EDITOR, func(sc *scenarioContext) {
bus.AddHandler("test2", func(query *models.GetAllowedDashboardsQuery) error {
query.Result = []int64{}
return nil
})
Convey("Should not be able to access ACL", func() {
sc.handlerFunc = GetDashboardAcl
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
......
......@@ -130,7 +130,7 @@ func GetPlaylistItems(c *middleware.Context) Response {
func GetPlaylistDashboards(c *middleware.Context) Response {
playlistId := c.ParamsInt64(":id")
playlists, err := LoadPlaylistDashboards(c.OrgId, c.UserId, playlistId)
playlists, err := LoadPlaylistDashboards(c.OrgId, c.SignedInUser, playlistId)
if err != nil {
return ApiError(500, "Could not load dashboards", err)
}
......
......@@ -34,18 +34,18 @@ func populateDashboardsById(dashboardByIds []int64, dashboardIdOrder map[int64]i
return result, nil
}
func populateDashboardsByTag(orgId, userId int64, dashboardByTag []string, dashboardTagOrder map[string]int) dtos.PlaylistDashboardsSlice {
func populateDashboardsByTag(orgId int64, signedInUser *m.SignedInUser, dashboardByTag []string, dashboardTagOrder map[string]int) dtos.PlaylistDashboardsSlice {
result := make(dtos.PlaylistDashboardsSlice, 0)
if len(dashboardByTag) > 0 {
for _, tag := range dashboardByTag {
searchQuery := search.Query{
Title: "",
Tags: []string{tag},
UserId: userId,
Limit: 100,
IsStarred: false,
OrgId: orgId,
Title: "",
Tags: []string{tag},
SignedInUser: signedInUser,
Limit: 100,
IsStarred: false,
OrgId: orgId,
}
if err := bus.Dispatch(&searchQuery); err == nil {
......@@ -64,7 +64,7 @@ func populateDashboardsByTag(orgId, userId int64, dashboardByTag []string, dashb
return result
}
func LoadPlaylistDashboards(orgId, userId, playlistId int64) (dtos.PlaylistDashboardsSlice, error) {
func LoadPlaylistDashboards(orgId int64, signedInUser *m.SignedInUser, playlistId int64) (dtos.PlaylistDashboardsSlice, error) {
playlistItems, _ := LoadPlaylistItems(playlistId)
dashboardByIds := make([]int64, 0)
......@@ -89,7 +89,7 @@ func LoadPlaylistDashboards(orgId, userId, playlistId int64) (dtos.PlaylistDashb
var k, _ = populateDashboardsById(dashboardByIds, dashboardIdOrder)
result = append(result, k...)
result = append(result, populateDashboardsByTag(orgId, userId, dashboardByTag, dashboardTagOrder)...)
result = append(result, populateDashboardsByTag(orgId, signedInUser, dashboardByTag, dashboardTagOrder)...)
sort.Sort(result)
return result, nil
......
......@@ -26,9 +26,9 @@ func Search(c *middleware.Context) {
mode = "list"
}
dbids := make([]int, 0)
dbids := make([]int64, 0)
for _, id := range c.QueryStrings("dashboardIds") {
dashboardId, err := strconv.Atoi(id)
dashboardId, err := strconv.ParseInt(id, 10, 64)
if err == nil {
dbids = append(dbids, dashboardId)
}
......@@ -37,7 +37,7 @@ func Search(c *middleware.Context) {
searchQuery := search.Query{
Title: query,
Tags: tags,
UserId: c.UserId,
SignedInUser: c.SignedInUser,
Limit: limit,
IsStarred: starred == "true",
OrgId: c.OrgId,
......
......@@ -88,7 +88,9 @@ type GetDashboardPermissionsQuery struct {
Result []*DashboardAclInfoDTO
}
type GetDashboardAclQuery struct {
// Returns dashboard acl list items and parent folder items
type GetInheritedDashboardAclQuery struct {
DashboardId int64
OrgId int64
Result []*DashboardAcl
}
......@@ -192,11 +192,3 @@ type GetDashboardSlugByIdQuery struct {
Id int64
Result string
}
type GetAllowedDashboardsQuery struct {
UserId int64
OrgId int64
DashList []int64
Result []int64
}
......@@ -6,50 +6,45 @@ import (
)
type DashboardGuardian struct {
user *m.SignedInUser
dashboard *m.Dashboard
acl []*m.DashboardAclInfoDTO
groups []*m.UserGroup
user *m.SignedInUser
dashId int64
orgId int64
acl []*m.DashboardAcl
groups []*m.UserGroup
}
func NewDashboardGuardian(dash *m.Dashboard, user *m.SignedInUser) *DashboardGuardian {
func NewDashboardGuardian(dashId int64, orgId int64, user *m.SignedInUser) *DashboardGuardian {
return &DashboardGuardian{
user: user,
dashboard: dash,
user: user,
dashId: dashId,
orgId: orgId,
}
}
func (g *DashboardGuardian) CanSave() (bool, error) {
if !g.dashboard.HasAcl {
return g.user.HasRole(m.ROLE_EDITOR), nil
}
return g.HasPermission(m.PERMISSION_EDIT)
return g.HasPermission(m.PERMISSION_EDIT, m.ROLE_EDITOR)
}
func (g *DashboardGuardian) CanEdit() (bool, error) {
if !g.dashboard.HasAcl {
return g.user.HasRole(m.ROLE_READ_ONLY_EDITOR), nil
}
return g.HasPermission(m.PERMISSION_READ_ONLY_EDIT)
return g.HasPermission(m.PERMISSION_READ_ONLY_EDIT, m.ROLE_READ_ONLY_EDITOR)
}
func (g *DashboardGuardian) CanView() (bool, error) {
if !g.dashboard.HasAcl {
return g.user.HasRole(m.ROLE_VIEWER), nil
}
return g.HasPermission(m.PERMISSION_VIEW)
return g.HasPermission(m.PERMISSION_VIEW, m.ROLE_VIEWER)
}
func (g *DashboardGuardian) HasPermission(permission m.PermissionType) (bool, error) {
userGroups, err := g.getUserGroups()
func (g *DashboardGuardian) HasPermission(permission m.PermissionType, fallbackRole m.RoleType) (bool, error) {
acl, err := g.getAcl()
if err != nil {
return false, err
}
acl, err := g.getAcl()
// if no acl use org role to determine permission
if len(acl) == 0 {
return g.user.HasRole(fallbackRole), nil
}
userGroups, err := g.getUserGroups()
if err != nil {
return false, err
}
......@@ -70,17 +65,12 @@ func (g *DashboardGuardian) HasPermission(permission m.PermissionType) (bool, er
}
// Returns dashboard acl
func (g *DashboardGuardian) getAcl() ([]*m.DashboardAclInfoDTO, error) {
func (g *DashboardGuardian) getAcl() ([]*m.DashboardAcl, error) {
if g.acl != nil {
return g.acl, nil
}
dashId := g.dashboard.Id
if g.dashboard.ParentId != 0 {
dashId = g.dashboard.ParentId
}
query := m.GetDashboardPermissionsQuery{DashboardId: dashId}
query := m.GetInheritedDashboardAclQuery{DashboardId: g.dashId, OrgId: g.orgId}
if err := bus.Dispatch(&query); err != nil {
return nil, err
}
......
......@@ -40,9 +40,8 @@ func searchHandler(query *Query) error {
dashQuery := FindPersistedDashboardsQuery{
Title: query.Title,
UserId: query.UserId,
SignedInUser: query.SignedInUser,
IsStarred: query.IsStarred,
OrgId: query.OrgId,
DashboardIds: query.DashboardIds,
Type: query.Type,
ParentId: query.FolderId,
......@@ -88,7 +87,7 @@ func searchHandler(query *Query) error {
}
// add isStarred info
if err := setIsStarredFlagOnSearchResults(query.UserId, hits); err != nil {
if err := setIsStarredFlagOnSearchResults(query.SignedInUser.UserId, hits); err != nil {
return err
}
......
......@@ -12,7 +12,7 @@ func TestSearch(t *testing.T) {
Convey("Given search query", t, func() {
jsonDashIndex = NewJsonDashIndex("../../../public/dashboards/")
query := Query{Limit: 2000}
query := Query{Limit: 2000, SignedInUser: &m.SignedInUser{IsGrafanaAdmin: true}}
bus.AddHandler("test", func(query *FindPersistedDashboardsQuery) error {
query.Result = HitList{
......
package search
import "strings"
import "github.com/grafana/grafana/pkg/models"
type HitType string
......@@ -43,11 +44,11 @@ type Query struct {
Title string
Tags []string
OrgId int64
UserId int64
SignedInUser *models.SignedInUser
Limit int
IsStarred bool
Type string
DashboardIds []int
DashboardIds []int64
FolderId int64
Mode string
......@@ -57,9 +58,9 @@ type Query struct {
type FindPersistedDashboardsQuery struct {
Title string
OrgId int64
UserId int64
SignedInUser *models.SignedInUser
IsStarred bool
DashboardIds []int
DashboardIds []int64
Type string
ParentId int64
Mode string
......
......@@ -148,12 +148,14 @@ func GetDashboard(query *m.GetDashboardQuery) error {
}
type DashboardSearchProjection struct {
Id int64
Title string
Slug string
Term string
IsFolder bool
ParentId int64
Id int64
Title string
Slug string
Term string
IsFolder bool
ParentId int64
FolderSlug string
FolderTitle string
}
func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error) {
......@@ -166,8 +168,11 @@ func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSear
dashboard.slug,
dashboard_tag.term,
dashboard.is_folder,
dashboard.parent_id
dashboard.parent_id,
f.slug as folder_slug,
f.title as folder_title
FROM dashboard
LEFT OUTER JOIN dashboard f on f.id = dashboard.parent_id
LEFT OUTER JOIN dashboard_tag on dashboard_tag.dashboard_id = dashboard.id`)
if query.IsStarred {
......@@ -175,12 +180,11 @@ func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSear
}
sql.WriteString(` WHERE dashboard.org_id=?`)
params = append(params, query.OrgId)
params = append(params, query.SignedInUser.OrgId)
if query.IsStarred {
sql.WriteString(` AND star.user_id=?`)
params = append(params, query.UserId)
params = append(params, query.SignedInUser.UserId)
}
if len(query.DashboardIds) > 0 {
......@@ -196,6 +200,23 @@ func findDashboards(query *search.FindPersistedDashboardsQuery) ([]DashboardSear
sql.WriteString(")")
}
if query.SignedInUser.OrgRole != m.ROLE_ADMIN {
allowedDashboardsSubQuery := ` AND (dashboard.has_acl = 0 OR dashboard.id in (
SELECT distinct d.id AS DashboardId
FROM dashboard AS d
LEFT JOIN dashboard AS df ON d.parent_id = df.id
LEFT JOIN dashboard_acl as dfa on d.parent_id = dfa.dashboard_id or d.id = dfa.dashboard_id
LEFT JOIN user_group_member as ugm on ugm.user_group_id = dfa.user_group_id
WHERE
d.has_acl = 1 and
(dfa.user_id = ? or ugm.user_id = ?)
and d.org_id = ?
))`
sql.WriteString(allowedDashboardsSubQuery)
params = append(params, query.SignedInUser.UserId, query.SignedInUser.UserId, query.SignedInUser.OrgId)
}
if len(query.Title) > 0 {
sql.WriteString(" AND dashboard.title " + dialect.LikeStr() + " ?")
params = append(params, "%"+query.Title+"%")
......@@ -250,26 +271,13 @@ func SearchDashboards(query *search.FindPersistedDashboardsQuery) error {
// appends parent folders for any hits to the search result
func appendDashboardFolders(res []DashboardSearchProjection) ([]DashboardSearchProjection, error) {
var dashboardFolderIds []int64
for _, item := range res {
if item.ParentId > 0 {
dashboardFolderIds = append(dashboardFolderIds, item.ParentId)
}
}
if len(dashboardFolderIds) > 0 {
folderQuery := &m.GetDashboardsQuery{DashboardIds: dashboardFolderIds}
err := GetDashboards(folderQuery)
if err != nil {
return nil, err
}
for _, folder := range folderQuery.Result {
res = append(res, DashboardSearchProjection{
Id: folder.Id,
Id: item.ParentId,
IsFolder: true,
Slug: folder.Slug,
Title: folder.Title,
Slug: item.FolderSlug,
Title: item.FolderTitle,
})
}
}
......@@ -371,6 +379,7 @@ func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {
"DELETE FROM dashboard WHERE id = ?",
"DELETE FROM playlist_item WHERE type = 'dashboard_by_id' AND value = ?",
"DELETE FROM dashboard_version WHERE dashboard_id = ?",
"DELETE FROM dashboard WHERE parent_id = ?",
}
for _, sql := range deletes {
......
......@@ -11,7 +11,7 @@ func init() {
bus.AddHandler("sql", AddOrUpdateDashboardPermission)
bus.AddHandler("sql", RemoveDashboardPermission)
bus.AddHandler("sql", GetDashboardPermissions)
bus.AddHandler("sql", GetDashboardAcl)
bus.AddHandler("sql", GetInheritedDashboardAcl)
}
func AddOrUpdateDashboardPermission(cmd *m.AddOrUpdateDashboardPermissionCommand) error {
......@@ -86,33 +86,31 @@ func RemoveDashboardPermission(cmd *m.RemoveDashboardPermissionCommand) error {
})
}
func GetDashboardAcl(query *m.GetDashboardAclQuery) error {
func GetInheritedDashboardAcl(query *m.GetInheritedDashboardAclQuery) error {
rawSQL := `SELECT
da.id,
da.org_id,
da.id,
da.dashboard_id,
da.user_id,
da.user_group_id,
da.permissions,
da.created,
da.updated,
FROM` + dialect.Quote("dashboard_acl") + ` as da
WHERE dashboard_id IN (
da.updated
FROM dashboard_acl as da
WHERE da.dashboard_id IN (
SELECT id FROM dashboard where id = ?
UNION
SELECT parent_id from dashboard where id = ?
)`
) AND org_id = ?`
query.Result = make([]*m.DashboardAcl, 0)
return x.SQL(rawSQL, query.DashboardId).Find(&query.Result)
return x.SQL(rawSQL, query.DashboardId, query.DashboardId, query.OrgId).Find(&query.Result)
}
func GetDashboardPermissions(query *m.GetDashboardPermissionsQuery) error {
rawSQL := `SELECT
da.id,
da.org_id,
da.id,
da.dashboard_id,
da.user_id,
da.user_group_id,
......
......@@ -25,6 +25,47 @@ func TestDashboardAclDataAccess(t *testing.T) {
So(err, ShouldEqual, m.ErrDashboardPermissionUserOrUserGroupEmpty)
})
Convey("Given dashboard folder permission", func() {
err := AddOrUpdateDashboardPermission(&m.AddOrUpdateDashboardPermissionCommand{
OrgId: 1,
UserId: currentUser.Id,
DashboardId: savedFolder.Id,
Permissions: m.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
Convey("When reading dashboard acl should include acl for parent folder", func() {
query := m.GetInheritedDashboardAclQuery{OrgId: 1, DashboardId: childDash.Id}
err := GetDashboardAcl(&query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1)
So(query.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
})
Convey("Given child dashboard permission", func() {
err := AddOrUpdateDashboardPermission(&m.AddOrUpdateDashboardPermissionCommand{
OrgId: 1,
UserId: currentUser.Id,
DashboardId: childDash.Id,
Permissions: m.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
Convey("When reading dashboard acl should include acl for parent folder and child", func() {
query := m.GetInheritedDashboardAclQuery{OrgId: 1, DashboardId: childDash.Id}
err := GetDashboardAcl(&query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
So(query.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
So(query.Result[1].DashboardId, ShouldEqual, childDash.Id)
})
})
})
Convey("Should be able to add dashboard permission", func() {
err := AddOrUpdateDashboardPermission(&m.AddOrUpdateDashboardPermissionCommand{
OrgId: 1,
......
......@@ -8,6 +8,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/setting"
)
func insertTestDashboard(title string, orgId int64, parentId int64, isFolder bool, tags ...interface{}) *m.Dashboard {
......@@ -113,9 +114,10 @@ func TestDashboardDataAccess(t *testing.T) {
Convey("Should be able to search for dashboard and return in folder hierarchy", func() {
query := search.FindPersistedDashboardsQuery{
Title: "test dash 23",
OrgId: 1,
Mode: "tree",
Title: "test dash 23",
OrgId: 1,
Mode: "tree",
SignedInUser: &m.SignedInUser{OrgId: 1},
}
err := SearchDashboards(&query)
......@@ -132,8 +134,9 @@ func TestDashboardDataAccess(t *testing.T) {
Convey("Should be able to search for dashboard folder", func() {
query := search.FindPersistedDashboardsQuery{
Title: "1 test dash folder",
OrgId: 1,
Title: "1 test dash folder",
OrgId: 1,
SignedInUser: &m.SignedInUser{OrgId: 1},
}
err := SearchDashboards(&query)
......@@ -146,8 +149,9 @@ func TestDashboardDataAccess(t *testing.T) {
Convey("Should be able to search for a dashboard folder's children", func() {
query := search.FindPersistedDashboardsQuery{
OrgId: 1,
ParentId: savedFolder.Id,
OrgId: 1,
ParentId: savedFolder.Id,
SignedInUser: &m.SignedInUser{OrgId: 1},
}
err := SearchDashboards(&query)
......@@ -161,9 +165,9 @@ func TestDashboardDataAccess(t *testing.T) {
Convey("Should be able to search for dashboard by dashboard ids", func() {
Convey("should be able to find two dashboards by id", func() {
query := search.FindPersistedDashboardsQuery{
DashboardIds: []int{2, 3},
OrgId: 1,
DashboardIds: []int64{2, 3},
Mode: "tree",
SignedInUser: &m.SignedInUser{OrgId: 1},
}
err := SearchDashboards(&query)
......@@ -180,8 +184,8 @@ func TestDashboardDataAccess(t *testing.T) {
Convey("DashboardIds that does not exists should not cause errors", func() {
query := search.FindPersistedDashboardsQuery{
DashboardIds: []int{1000},
OrgId: 1,
DashboardIds: []int64{1000},
SignedInUser: &m.SignedInUser{OrgId: 1},
}
err := SearchDashboards(&query)
......@@ -244,6 +248,23 @@ func TestDashboardDataAccess(t *testing.T) {
So(query.Result.ParentId, ShouldEqual, 0)
})
Convey("Should be able to delete a dashboard folder and its children", func() {
deleteCmd := &m.DeleteDashboardCommand{Id: savedFolder.Id}
err := DeleteDashboard(deleteCmd)
So(err, ShouldBeNil)
query := search.FindPersistedDashboardsQuery{
OrgId: 1,
ParentId: savedFolder.Id,
SignedInUser: &m.SignedInUser{},
}
err = SearchDashboards(&query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 0)
})
Convey("Should be able to get dashboard tags", func() {
query := m.GetDashboardTagsQuery{OrgId: 1}
......@@ -266,7 +287,7 @@ func TestDashboardDataAccess(t *testing.T) {
})
Convey("Should be able to search for starred dashboards", func() {
query := search.FindPersistedDashboardsQuery{OrgId: 1, UserId: 10, IsStarred: true}
query := search.FindPersistedDashboardsQuery{SignedInUser: &m.SignedInUser{UserId: 10, OrgId: 1}, IsStarred: true}
err := SearchDashboards(&query)
So(err, ShouldBeNil)
......@@ -275,5 +296,95 @@ func TestDashboardDataAccess(t *testing.T) {
})
})
})
Convey("Given one dashboard folder with two dashboard and one dashboard in the root folder", func() {
folder := insertTestDashboard("1 test dash folder", 1, 0, true, "prod", "webapp")
dashInRoot := insertTestDashboard("test dash 67", 1, 0, false, "prod", "webapp")
insertTestDashboard("test dash 23", 1, folder.Id, false, "prod", "webapp")
insertTestDashboard("test dash 45", 1, folder.Id, false, "prod")
currentUser := createUser("viewer", "Viewer", false)
Convey("and no acls are set", func() {
Convey("should return all dashboards", func() {
query := &search.FindPersistedDashboardsQuery{SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1}, OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}}
err := SearchDashboards(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
So(query.Result[0].Id, ShouldEqual, folder.Id)
So(query.Result[1].Id, ShouldEqual, dashInRoot.Id)
})
})
Convey("and acl is set for dashboard folder", func() {
var otherUser int64 = 999
updateTestDashboardWithAcl(folder.Id, otherUser, m.PERMISSION_EDIT)
Convey("should not return folder", func() {
query := &search.FindPersistedDashboardsQuery{SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1}, OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}}
err := SearchDashboards(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1)
So(query.Result[0].Id, ShouldEqual, dashInRoot.Id)
})
Convey("when the user is given permission", func() {
updateTestDashboardWithAcl(folder.Id, currentUser.Id, m.PERMISSION_EDIT)
Convey("should be able to access folder", func() {
query := &search.FindPersistedDashboardsQuery{SignedInUser: &m.SignedInUser{UserId: currentUser.Id, OrgId: 1}, OrgId: 1, DashboardIds: []int64{folder.Id, dashInRoot.Id}}
err := SearchDashboards(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
So(query.Result[0].Id, ShouldEqual, folder.Id)
So(query.Result[1].Id, ShouldEqual, dashInRoot.Id)
})
})
Convey("when the user is an admin", func() {
Convey("should be able to access folder", func() {
query := &search.FindPersistedDashboardsQuery{
SignedInUser: &m.SignedInUser{
UserId: currentUser.Id,
OrgId: 1,
OrgRole: m.ROLE_ADMIN,
},
OrgId: 1,
DashboardIds: []int64{folder.Id, dashInRoot.Id},
}
err := SearchDashboards(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
So(query.Result[0].Id, ShouldEqual, folder.Id)
So(query.Result[1].Id, ShouldEqual, dashInRoot.Id)
})
})
})
})
})
}
func createUser(name string, role string, isAdmin bool) m.User {
setting.AutoAssignOrg = true
setting.AutoAssignOrgRole = role
currentUserCmd := m.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin}
err := CreateUser(&currentUserCmd)
So(err, ShouldBeNil)
q1 := m.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}
GetUserOrgList(&q1)
So(q1.Result[0].Role, ShouldEqual, role)
return currentUserCmd.Result
}
func updateTestDashboardWithAcl(dashId int64, userId int64, permissions m.PermissionType) {
err := AddOrUpdateDashboardPermission(&m.AddOrUpdateDashboardPermissionCommand{
OrgId: 1,
UserId: userId,
DashboardId: dashId,
Permissions: permissions,
})
So(err, ShouldBeNil)
}
package sqlstore
import (
"fmt"
"strings"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
)
func init() {
bus.AddHandler("sql", GetAllowedDashboards)
}
func GetAllowedDashboards(query *m.GetAllowedDashboardsQuery) error {
dashboardIds := arrayToString(query.DashList, ",")
rawSQL := `select distinct d.id as DashboardId
from dashboard as d
left join dashboard as df on d.parent_id = df.id
left join dashboard_acl as dfa on d.parent_id = dfa.dashboard_id or d.id = dfa.dashboard_id
left join user_group_member as ugm on ugm.user_group_id = dfa.user_group_id
where (
(d.has_acl = 1 and (dfa.user_id = ? or ugm.user_id = ? or df.created_by = ? or (d.is_folder = 1 and d.created_by = ?)))
or d.has_acl = 0)
and d.org_id = ?`
rawSQL = fmt.Sprintf("%v and d.id in(%v)", rawSQL, dashboardIds)
query.Result = make([]int64, 0)
err := x.SQL(rawSQL, query.UserId, query.UserId, query.UserId, query.UserId, query.OrgId).Find(&query.Result)
if err != nil {
return err
}
return nil
}
func arrayToString(a []int64, delim string) string {
return strings.Trim(strings.Replace(fmt.Sprint(a), " ", delim, -1), "[]")
}
package sqlstore
import (
"testing"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
)
func TestGuardianDataAccess(t *testing.T) {
Convey("Testing DB", t, func() {
InitTestDB(t)
Convey("Given one dashboard folder with two dashboard and one dashboard in the root folder", func() {
folder := insertTestDashboard("1 test dash folder", 1, 0, true, "prod", "webapp")
dashInRoot := insertTestDashboard("test dash 67", 1, 0, false, "prod", "webapp")
insertTestDashboard("test dash 23", 1, folder.Id, false, "prod", "webapp")
insertTestDashboard("test dash 45", 1, folder.Id, false, "prod")
currentUser := createUser("viewer", "Viewer", false)
Convey("and no acls are set", func() {
Convey("should return all dashboards", func() {
query := &m.GetAllowedDashboardsQuery{UserId: currentUser.Id, OrgId: 1, DashList: []int64{folder.Id, dashInRoot.Id}}
err := GetAllowedDashboards(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
So(query.Result[0], ShouldEqual, folder.Id)
So(query.Result[1], ShouldEqual, dashInRoot.Id)
})
})
Convey("and acl is set for dashboard folder", func() {
var otherUser int64 = 999
updateTestDashboardWithAcl(folder.Id, otherUser, m.PERMISSION_EDIT)
Convey("should not return folder", func() {
query := &m.GetAllowedDashboardsQuery{UserId: currentUser.Id, OrgId: 1, DashList: []int64{folder.Id, dashInRoot.Id}}
err := GetAllowedDashboards(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 1)
So(query.Result[0], ShouldEqual, dashInRoot.Id)
})
Convey("when the user is given permission", func() {
updateTestDashboardWithAcl(folder.Id, currentUser.Id, m.PERMISSION_EDIT)
Convey("should folder", func() {
query := &m.GetAllowedDashboardsQuery{UserId: currentUser.Id, OrgId: 1, DashList: []int64{folder.Id, dashInRoot.Id}}
err := GetAllowedDashboards(query)
So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
So(query.Result[0], ShouldEqual, folder.Id)
So(query.Result[1], ShouldEqual, dashInRoot.Id)
})
})
})
})
})
}
func createUser(name string, role string, isAdmin bool) m.User {
setting.AutoAssignOrg = true
setting.AutoAssignOrgRole = role
currentUserCmd := m.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin}
err := CreateUser(&currentUserCmd)
So(err, ShouldBeNil)
q1 := m.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}
GetUserOrgList(&q1)
So(q1.Result[0].Role, ShouldEqual, role)
return currentUserCmd.Result
}
func updateTestDashboardWithAcl(dashId int64, userId int64, permission m.PermissionType) {
err := AddOrUpdateDashboardPermission(&m.AddOrUpdateDashboardPermissionCommand{
OrgId: 1,
UserId: userId,
DashboardId: dashId,
Permissions: permission,
})
So(err, ShouldBeNil)
}
......@@ -10,4 +10,5 @@ define([
'./prefs_control',
'./user_groups_ctrl',
'./user_group_details_ctrl',
'./create_user_group_modal',
], function () {});
///<reference path="../../headers/common.d.ts" />
import coreModule from 'app/core/core_module';
import appEvents from 'app/core/app_events';
import _ from 'lodash';
export class CreateUserGroupCtrl {
userGroupName = '';
/** @ngInject */
constructor(private backendSrv, private $scope, $sce, private $location) {
}
createUserGroup() {
this.backendSrv.post('/api/user-groups', {name: this.userGroupName}).then((result) => {
if (result.userGroupId) {
this.$location.path('/org/user-groups/edit/' + result.userGroupId);
}
this.dismiss();
});
}
dismiss() {
appEvents.emit('hide-modal');
}
}
export function createUserGroupModal() {
return {
restrict: 'E',
templateUrl: 'public/app/features/org/partials/create_user_group.html',
controller: CreateUserGroupCtrl,
bindToController: true,
controllerAs: 'ctrl',
};
}
coreModule.directive('createUserGroupModal', createUserGroupModal);
<div class="modal-body" ng-controller="UserGroupsCtrl">
<div class="modal-body">
<div class="modal-header">
<h2 class="modal-header-title">
Create User Group
<span class="p-l-1">Create User Group</span>
</h2>
<a class="modal-header-close" ng-click="dismiss();">
<a class="modal-header-close" ng-click="ctrl.dismiss();">
<i class="fa fa-remove"></i>
</a>
</div>
<div class="modal-content">
<form name="createUserGroupForm" class="gf-form-group">
<div class="gf-form-inline">
<div class="gf-form max-width-21">
<input type="text" class="gf-form-input" ng-model='ctrl.userGroupName' placeholder="Name"></input>
</div>
<div class="gf-form">
<button class="btn gf-form-btn btn-success" ng-click="ctrl.createUserGroup();dismiss();">Create</button>
</div>
</div>
<form name="ctrl.createUserGroupForm" class="gf-form-group" novalidate>
<div class="p-t-2">
<div class="gf-form-inline">
<div class="gf-form max-width-21">
<input type="text" class="gf-form-input" ng-model='ctrl.userGroupName' required give-focus="true" placeholder="Enter User Group Name"></input>
</div>
<div class="gf-form">
<button class="btn gf-form-btn btn-success" ng-click="ctrl.createUserGroup();ctrl.dismiss();">Create</button>
</div>
</div>
</div>
</form>
</div>
</div>
///<reference path="../../headers/common.d.ts" />
import coreModule from 'app/core/core_module';
import {appEvents} from 'app/core/core';
export class UserGroupsCtrl {
userGroups: any;
......@@ -10,7 +11,6 @@ export class UserGroupsCtrl {
totalPages: number;
showPaging = false;
query: any = '';
userGroupName: any = '';
navModel: any;
/** @ngInject */
......@@ -40,14 +40,6 @@ export class UserGroupsCtrl {
this.get();
}
createUserGroup() {
this.backendSrv.post('/api/user-groups', {name: this.userGroupName}).then((result) => {
if (result.userGroupId) {
this.$location.path('/org/user-groups/edit/' + result.userGroupId);
}
});
}
deleteUserGroup(userGroup) {
this.$scope.appEvent('confirm-modal', {
title: 'Delete',
......@@ -66,13 +58,9 @@ export class UserGroupsCtrl {
}
openUserGroupModal() {
var modalScope = this.$scope.$new();
modalScope.createUserGroup = this.createUserGroup.bind(this);
this.$scope.appEvent('show-modal', {
src: 'public/app/features/org/partials/create_user_group.html',
modalClass: 'modal--narrow',
scope: modalScope
appEvents.emit('show-modal', {
templateHtml: '<create-user-group-modal></create-user-group-modal>',
modalClass: 'modal--narrow'
});
}
}
......
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