Commit 97c13b77 by Daniel Lee

WIP: Add or update Dashboard ACL

SQL Integration Tests for the guardian class too.
parent 3785894b
package models
import "time"
type PermissionType int
const (
PERMISSION_EDIT PermissionType = 4
PERMISSION_READ_ONLY_EDIT PermissionType = 2
PERMISSION_VIEW PermissionType = 1
)
// Typed errors
// var (
// ErrDashboardPermissionAlreadyAdded = errors.New("A permission has ")
// )
// Dashboard ACL model
type DashboardAcl struct {
Id int64
OrgId int64
DashboardId int64
Created time.Time
Updated time.Time
UserId int64
UserGroupId int64
Permissions PermissionType
}
//
// COMMANDS
//
type AddOrUpdateDashboardPermissionCommand struct {
DashboardId int64 `json:"dashboardId" binding:"Required"`
OrgId int64 `json:"-"`
UserId int64 `json:"userId"`
UserGroupId int64 `json:"userGroupId"`
PermissionType PermissionType `json:"permissionType" binding:"Required"`
}
//
// QUERIES
//
type GetDashboardPermissionsQuery struct {
DashboardId int64 `json:"dashboardId" binding:"Required"`
Result []*DashboardAcl
}
...@@ -18,14 +18,6 @@ var ( ...@@ -18,14 +18,6 @@ var (
ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty") ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty")
) )
type PermissionType int
const (
PERMISSION_EDIT PermissionType = 4
PERMISSION_READ_ONLY_EDIT PermissionType = 2
PERMISSION_VIEW PermissionType = 1
)
type UpdatePluginDashboardError struct { type UpdatePluginDashboardError struct {
PluginId string PluginId string
} }
...@@ -57,6 +49,7 @@ type Dashboard struct { ...@@ -57,6 +49,7 @@ type Dashboard struct {
CreatedBy int64 CreatedBy int64
ParentId int64 ParentId int64
IsFolder bool IsFolder bool
HasAcl bool
Title string Title string
Data *simplejson.Json Data *simplejson.Json
......
package sqlstore
import (
"time"
"github.com/go-xorm/xorm"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
)
func init() {
bus.AddHandler("sql", AddOrUpdateDashboardPermission)
bus.AddHandler("sql", GetDashboardPermissions)
}
func AddOrUpdateDashboardPermission(cmd *m.AddOrUpdateDashboardPermissionCommand) error {
return inTransaction(func(sess *xorm.Session) error {
if res, err := sess.Query("SELECT 1 from dashboard_acl WHERE dashboard_id =? and (user_group_id=? or user_id=?)", cmd.DashboardId, cmd.UserGroupId, cmd.UserId); err != nil {
return err
} else if len(res) == 1 {
entity := m.DashboardAcl{
Permissions: cmd.PermissionType,
}
if _, err := sess.Cols("permissions").Where("dashboard_id =? and (user_group_id=? or user_id=?)", cmd.DashboardId, cmd.UserGroupId, cmd.UserId).Update(&entity); err != nil {
return err
}
return nil
}
entity := m.DashboardAcl{
OrgId: cmd.OrgId,
UserGroupId: cmd.UserGroupId,
UserId: cmd.UserId,
Created: time.Now(),
Updated: time.Now(),
DashboardId: cmd.DashboardId,
Permissions: cmd.PermissionType,
}
cols := []string{"org_id", "created", "updated", "dashboard_id", "permissions"}
if cmd.UserId != 0 {
cols = append(cols, "user_id")
}
if cmd.UserGroupId != 0 {
cols = append(cols, "user_group_id")
}
_, err := sess.Cols(cols...).Insert(&entity)
if err != nil {
return err
}
dashboard := m.Dashboard{
HasAcl: true,
}
if _, err := sess.Cols("has_acl").Where("id=? OR parent_id=?", cmd.DashboardId, cmd.DashboardId).Update(&dashboard); err != nil {
return err
}
return nil
})
}
func GetDashboardPermissions(query *m.GetDashboardPermissionsQuery) error {
sess := x.Where("dashboard_id=?", query.DashboardId)
query.Result = make([]*m.DashboardAcl, 0)
return sess.Find(&query.Result)
}
package sqlstore
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
m "github.com/grafana/grafana/pkg/models"
)
func TestDashboardAclDataAccess(t *testing.T) {
Convey("Testing DB", t, func() {
InitTestDB(t)
Convey("Given a dashboard folder", func() {
savedFolder := insertTestDashboard("1 test dash folder", 1, 0, true, "prod", "webapp")
childDash := insertTestDashboard("2 test dash", 1, savedFolder.Id, false, "prod", "webapp")
Convey("Should be able to add dashboard permission", func() {
err := AddOrUpdateDashboardPermission(&m.AddOrUpdateDashboardPermissionCommand{
OrgId: 1,
UserId: 1,
DashboardId: savedFolder.Id,
PermissionType: m.PERMISSION_EDIT,
})
So(err, ShouldBeNil)
q1 := &m.GetDashboardPermissionsQuery{DashboardId: savedFolder.Id}
err = GetDashboardPermissions(q1)
So(err, ShouldBeNil)
So(q1.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
So(q1.Result[0].Permissions, ShouldEqual, m.PERMISSION_EDIT)
So(q1.Result[0].UserId, ShouldEqual, 1)
Convey("Should update hasAcl field to true for dashboard folder and its children", func() {
q2 := &m.GetDashboardsQuery{DashboardIds: []int64{savedFolder.Id, childDash.Id}}
err := GetDashboards(q2)
So(err, ShouldBeNil)
So(q2.Result[0].HasAcl, ShouldBeTrue)
So(q2.Result[1].HasAcl, ShouldBeTrue)
})
Convey("Should be able to update an existing permission", func() {
err := AddOrUpdateDashboardPermission(&m.AddOrUpdateDashboardPermissionCommand{
OrgId: 1,
UserId: 1,
DashboardId: savedFolder.Id,
PermissionType: m.PERMISSION_READ_ONLY_EDIT,
})
So(err, ShouldBeNil)
q3 := &m.GetDashboardPermissionsQuery{DashboardId: savedFolder.Id}
err = GetDashboardPermissions(q3)
So(err, ShouldBeNil)
So(q3.Result[0].DashboardId, ShouldEqual, savedFolder.Id)
So(q3.Result[0].Permissions, ShouldEqual, m.PERMISSION_READ_ONLY_EDIT)
So(q3.Result[0].UserId, ShouldEqual, 1)
})
})
})
})
}
...@@ -15,6 +15,7 @@ func InitTestDB(t *testing.T) { ...@@ -15,6 +15,7 @@ func InitTestDB(t *testing.T) {
x, err := xorm.NewEngine(sqlutil.TestDB_Sqlite3.DriverName, sqlutil.TestDB_Sqlite3.ConnStr) x, err := xorm.NewEngine(sqlutil.TestDB_Sqlite3.DriverName, sqlutil.TestDB_Sqlite3.ConnStr)
//x, err := xorm.NewEngine(sqlutil.TestDB_Mysql.DriverName, sqlutil.TestDB_Mysql.ConnStr) //x, err := xorm.NewEngine(sqlutil.TestDB_Mysql.DriverName, sqlutil.TestDB_Mysql.ConnStr)
//x, err := xorm.NewEngine(sqlutil.TestDB_Postgres.DriverName, sqlutil.TestDB_Postgres.ConnStr) //x, err := xorm.NewEngine(sqlutil.TestDB_Postgres.DriverName, sqlutil.TestDB_Postgres.ConnStr)
// x.ShowSQL()
if err != nil { if err != nil {
t.Fatalf("Failed to init in memory sqllite3 db %v", err) t.Fatalf("Failed to init in memory sqllite3 db %v", err)
......
...@@ -30,7 +30,10 @@ where ( ...@@ -30,7 +30,10 @@ where (
query.Result = make([]int64, 0) query.Result = make([]int64, 0)
for _, dash := range res { for _, dash := range res {
id, _ := strconv.ParseInt(string(dash["DashboardId"]), 10, 64) id, err := strconv.ParseInt(string(dash["DashboardId"]), 10, 64)
if err != nil {
return err
}
query.Result = append(query.Result, id) query.Result = append(query.Result, id)
} }
......
...@@ -4,29 +4,71 @@ import ( ...@@ -4,29 +4,71 @@ import (
"testing" "testing"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey" . "github.com/smartystreets/goconvey/convey"
) )
func TestGuardianAccess(t *testing.T) { func TestGuardianDataAccess(t *testing.T) {
Convey("Testing DB", t, func() { Convey("Testing DB", t, func() {
InitTestDB(t) InitTestDB(t)
Convey("Given one dashboard folder with two dashboard and one dashboard in the root folder", func() { 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") folder := insertTestDashboard("1 test dash folder", 1, 0, true, "prod", "webapp")
// dashInFolder1 := insertTestDashboard("test dash 23", 1, folder.Id, false, "prod", "webapp") // insertTestDashboard("test dash 23", 1, folder.Id, false, "prod", "webapp")
// dashInFolder2 := insertTestDashboard("test dash 45", 1, folder.Id, false, "prod") // insertTestDashboard("test dash 45", 1, folder.Id, false, "prod")
dashInRoot := insertTestDashboard("test dash 67", 1, 0, false, "prod", "webapp") dashInRoot := insertTestDashboard("test dash 67", 1, 0, false, "prod", "webapp")
currentUser := createUser("viewer")
Convey("and no acls are set", func() { Convey("and no acls are set", func() {
Convey("should return all dashboards", func() { Convey("should return all dashboards", func() {
query := &m.GetAllowedDashboardsQuery{UserId: 1, OrgId: 1, DashList: []int64{folder.Id, dashInRoot.Id}} query := &m.GetAllowedDashboardsQuery{UserId: currentUser.Id, OrgId: 1, DashList: []int64{folder.Id, dashInRoot.Id}}
err := GetAllowedDashboards(query) err := GetAllowedDashboards(query)
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(len(query.Result), ShouldEqual, 2)
So(query.Result[0], ShouldEqual, folder.Id) So(query.Result[0], ShouldEqual, folder.Id)
So(query.Result[1], ShouldEqual, dashInRoot.Id) So(query.Result[1], ShouldEqual, dashInRoot.Id)
}) })
}) })
Convey("and acl is set for dashboard folder", func() {
Convey("should not return folder", func() {
var otherUser int64 = 999
updateTestDashboardWithAcl(folder.Id, otherUser, m.PERMISSION_EDIT)
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)
}) })
}) })
})
})
}
func createUser(name string) m.User {
setting.AutoAssignOrg = true
setting.AutoAssignOrgRole = "Viewer"
currentUserCmd := m.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: false}
err := CreateUser(&currentUserCmd)
So(err, ShouldBeNil)
q1 := m.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}
GetUserOrgList(&q1)
So(q1.Result[0].Role, ShouldEqual, "Viewer")
return currentUserCmd.Result
}
func updateTestDashboardWithAcl(dashId int64, userId int64, permissionType m.PermissionType) {
err := AddOrUpdateDashboardPermission(&m.AddOrUpdateDashboardPermissionCommand{
OrgId: 1,
UserId: userId,
DashboardId: dashId,
PermissionType: permissionType,
})
So(err, ShouldBeNil)
} }
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