Просмотр исходного кода

WIP: Add or update Dashboard ACL

SQL Integration Tests for the guardian class too.
Daniel Lee 8 лет назад
Родитель
Сommit
97c13b77bf

+ 51 - 0
pkg/models/dashboard_acl.go

@@ -0,0 +1,51 @@
+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
+}

+ 1 - 8
pkg/models/dashboards.go

@@ -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 {
 	CreatedBy int64
 	CreatedBy int64
 	ParentId  int64
 	ParentId  int64
 	IsFolder  bool
 	IsFolder  bool
+	HasAcl    bool
 
 
 	Title string
 	Title string
 	Data  *simplejson.Json
 	Data  *simplejson.Json

+ 71 - 0
pkg/services/sqlstore/dashboard_acl.go

@@ -0,0 +1,71 @@
+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)
+}

+ 62 - 0
pkg/services/sqlstore/dashboard_acl_test.go

@@ -0,0 +1,62 @@
+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)
+
+				})
+			})
+		})
+	})
+}

+ 1 - 0
pkg/services/sqlstore/datasource_test.go

@@ -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)

+ 4 - 1
pkg/services/sqlstore/guardian.go

@@ -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)
 	}
 	}
 
 

+ 46 - 4
pkg/services/sqlstore/guardian_test.go

@@ -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")
-			// dashInFolder2 := insertTestDashboard("test dash 45", 1, folder.Id, false, "prod")
+			// insertTestDashboard("test dash 23", 1, folder.Id, false, "prod", "webapp")
+			// 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)
+}